DEPS include_rules no longer allows core/ -> fpsdfsk/ inclusion.
[pdfium.git] / fpdfsdk / src / fpdfview.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "../../core/include/fxcodec/fx_codec.h"
8 #include "../../core/include/fxcrt/fx_safe_types.h"
9 #include "../../public/fpdf_ext.h"
10 #include "../../public/fpdf_progressive.h"
11 #include "../../public/fpdfview.h"
12 #include "../../third_party/base/nonstd_unique_ptr.h"
13 #include "../../third_party/base/numerics/safe_conversions_impl.h"
14 #include "../include/fsdk_define.h"
15 #include "../include/fsdk_mgr.h"
16 #include "../include/fsdk_rendercontext.h"
17 #include "../include/javascript/IJavaScript.h"
18
19 CPDF_Document* CPDFDocumentFromFPDFDocument(FPDF_DOCUMENT doc) {
20   return static_cast<CPDF_Document*>(doc);
21 }
22
23 CPDF_Page* CPDFPageFromFPDFPage(FPDF_PAGE page) {
24   return static_cast<CPDF_Page*>(page);
25 }
26
27 CPDF_CustomAccess::CPDF_CustomAccess(FPDF_FILEACCESS* pFileAccess) {
28   if (pFileAccess)
29     m_FileAccess = *pFileAccess;
30 }
31
32 FX_BOOL CPDF_CustomAccess::ReadBlock(void* buffer,
33                                      FX_FILESIZE offset,
34                                      size_t size) {
35   if (offset < 0) {
36     return FALSE;
37   }
38   FX_SAFE_FILESIZE newPos =
39       pdfium::base::checked_cast<FX_FILESIZE, size_t>(size);
40   newPos += offset;
41   if (!newPos.IsValid() || newPos.ValueOrDie() > m_FileAccess.m_FileLen) {
42     return FALSE;
43   }
44   return m_FileAccess.m_GetBlock(m_FileAccess.m_Param, offset, (uint8_t*)buffer,
45                                  size);
46 }
47
48 // 0 bit: FPDF_POLICY_MACHINETIME_ACCESS
49 static FX_DWORD foxit_sandbox_policy = 0xFFFFFFFF;
50
51 void FSDK_SetSandBoxPolicy(FPDF_DWORD policy, FPDF_BOOL enable) {
52   switch (policy) {
53     case FPDF_POLICY_MACHINETIME_ACCESS: {
54       if (enable)
55         foxit_sandbox_policy |= 0x01;
56       else
57         foxit_sandbox_policy &= 0xFFFFFFFE;
58     } break;
59     default:
60       break;
61   }
62 }
63
64 FPDF_BOOL FSDK_IsSandBoxPolicyEnabled(FPDF_DWORD policy) {
65   switch (policy) {
66     case FPDF_POLICY_MACHINETIME_ACCESS:
67       return (foxit_sandbox_policy & 0x01) ? TRUE : FALSE;
68     default:
69       return FALSE;
70   }
71 }
72
73 CCodec_ModuleMgr* g_pCodecModule = nullptr;
74
75 DLLEXPORT void STDCALL FPDF_InitLibrary() {
76   FPDF_InitLibraryWithConfig(nullptr);
77 }
78
79 DLLEXPORT void STDCALL FPDF_InitLibraryWithConfig(
80     const FPDF_LIBRARY_CONFIG* cfg) {
81   g_pCodecModule = new CCodec_ModuleMgr();
82
83   CFX_GEModule::Create(cfg ? cfg->m_pUserFontPaths : nullptr);
84   CFX_GEModule::Get()->SetCodecModule(g_pCodecModule);
85
86   CPDF_ModuleMgr::Create();
87   CPDF_ModuleMgr::Get()->SetCodecModule(g_pCodecModule);
88   CPDF_ModuleMgr::Get()->InitPageModule();
89   CPDF_ModuleMgr::Get()->InitRenderModule();
90   CPDF_ModuleMgr* pModuleMgr = CPDF_ModuleMgr::Get();
91   if (pModuleMgr) {
92     pModuleMgr->LoadEmbeddedGB1CMaps();
93     pModuleMgr->LoadEmbeddedJapan1CMaps();
94     pModuleMgr->LoadEmbeddedCNS1CMaps();
95     pModuleMgr->LoadEmbeddedKorea1CMaps();
96   }
97   if (cfg && cfg->version >= 2)
98     IJS_Runtime::Initialize(cfg->m_v8EmbedderSlot, cfg->m_pIsolate);
99 }
100
101 DLLEXPORT void STDCALL FPDF_DestroyLibrary() {
102   CPDF_ModuleMgr::Destroy();
103   CFX_GEModule::Destroy();
104
105   delete g_pCodecModule;
106   g_pCodecModule = nullptr;
107 }
108
109 #ifndef _WIN32
110 int g_LastError;
111 void SetLastError(int err) {
112   g_LastError = err;
113 }
114
115 int GetLastError() {
116   return g_LastError;
117 }
118 #endif
119
120 void ProcessParseError(FX_DWORD err_code) {
121   // Translate FPDFAPI error code to FPDFVIEW error code
122   switch (err_code) {
123     case PDFPARSE_ERROR_FILE:
124       err_code = FPDF_ERR_FILE;
125       break;
126     case PDFPARSE_ERROR_FORMAT:
127       err_code = FPDF_ERR_FORMAT;
128       break;
129     case PDFPARSE_ERROR_PASSWORD:
130       err_code = FPDF_ERR_PASSWORD;
131       break;
132     case PDFPARSE_ERROR_HANDLER:
133       err_code = FPDF_ERR_SECURITY;
134       break;
135   }
136   SetLastError(err_code);
137 }
138
139 DLLEXPORT void STDCALL FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
140                                              FPDF_BOOL enable) {
141   return FSDK_SetSandBoxPolicy(policy, enable);
142 }
143
144 DLLEXPORT FPDF_DOCUMENT STDCALL FPDF_LoadDocument(FPDF_STRING file_path,
145                                                   FPDF_BYTESTRING password) {
146   // NOTE: the creation of the file needs to be by the embedder on the
147   // other side of this API.
148   IFX_FileRead* pFileAccess = FX_CreateFileRead((const FX_CHAR*)file_path);
149   if (!pFileAccess) {
150     return nullptr;
151   }
152
153   CPDF_Parser* pParser = new CPDF_Parser;
154   pParser->SetPassword(password);
155
156   FX_DWORD err_code = pParser->StartParse(pFileAccess);
157   if (err_code) {
158     delete pParser;
159     ProcessParseError(err_code);
160     return NULL;
161   }
162   return pParser->GetDocument();
163 }
164
165 class CMemFile final : public IFX_FileRead {
166  public:
167   CMemFile(uint8_t* pBuf, FX_FILESIZE size) : m_pBuf(pBuf), m_size(size) {}
168
169   virtual void Release() { delete this; }
170   virtual FX_FILESIZE GetSize() { return m_size; }
171   virtual FX_BOOL ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) {
172     if (offset < 0) {
173       return FALSE;
174     }
175     FX_SAFE_FILESIZE newPos =
176         pdfium::base::checked_cast<FX_FILESIZE, size_t>(size);
177     newPos += offset;
178     if (!newPos.IsValid() || newPos.ValueOrDie() > (FX_DWORD)m_size) {
179       return FALSE;
180     }
181     FXSYS_memcpy(buffer, m_pBuf + offset, size);
182     return TRUE;
183   }
184
185  private:
186   ~CMemFile() override {}
187
188   uint8_t* m_pBuf;
189   FX_FILESIZE m_size;
190 };
191 DLLEXPORT FPDF_DOCUMENT STDCALL FPDF_LoadMemDocument(const void* data_buf,
192                                                      int size,
193                                                      FPDF_BYTESTRING password) {
194   CPDF_Parser* pParser = new CPDF_Parser;
195   pParser->SetPassword(password);
196   CMemFile* pMemFile = new CMemFile((uint8_t*)data_buf, size);
197   FX_DWORD err_code = pParser->StartParse(pMemFile);
198   if (err_code) {
199     delete pParser;
200     ProcessParseError(err_code);
201     return NULL;
202   }
203   CPDF_Document* pDoc = NULL;
204   pDoc = pParser ? pParser->GetDocument() : NULL;
205   CheckUnSupportError(pDoc, err_code);
206   return pParser->GetDocument();
207 }
208
209 DLLEXPORT FPDF_DOCUMENT STDCALL
210 FPDF_LoadCustomDocument(FPDF_FILEACCESS* pFileAccess,
211                         FPDF_BYTESTRING password) {
212   CPDF_Parser* pParser = new CPDF_Parser;
213   pParser->SetPassword(password);
214   CPDF_CustomAccess* pFile = new CPDF_CustomAccess(pFileAccess);
215   FX_DWORD err_code = pParser->StartParse(pFile);
216   if (err_code) {
217     delete pParser;
218     ProcessParseError(err_code);
219     return NULL;
220   }
221   CPDF_Document* pDoc = NULL;
222   pDoc = pParser ? pParser->GetDocument() : NULL;
223   CheckUnSupportError(pDoc, err_code);
224   return pParser->GetDocument();
225 }
226
227 DLLEXPORT FPDF_BOOL STDCALL FPDF_GetFileVersion(FPDF_DOCUMENT doc,
228                                                 int* fileVersion) {
229   if (!fileVersion)
230     return FALSE;
231
232   *fileVersion = 0;
233   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(doc);
234   if (!pDoc)
235     return FALSE;
236
237   CPDF_Parser* pParser = (CPDF_Parser*)pDoc->GetParser();
238   if (!pParser)
239     return FALSE;
240
241   *fileVersion = pParser->GetFileVersion();
242   return TRUE;
243 }
244
245 // jabdelmalek: changed return type from FX_DWORD to build on Linux (and match
246 // header).
247 DLLEXPORT unsigned long STDCALL FPDF_GetDocPermissions(FPDF_DOCUMENT document) {
248   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
249   if (!pDoc)
250     return 0;
251
252   CPDF_Parser* pParser = (CPDF_Parser*)pDoc->GetParser();
253   CPDF_Dictionary* pDict = pParser->GetEncryptDict();
254   return pDict ? pDict->GetInteger("P") : (FX_DWORD)-1;
255 }
256
257 DLLEXPORT int STDCALL FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document) {
258   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
259   if (!pDoc)
260     return -1;
261
262   CPDF_Parser* pParser = (CPDF_Parser*)pDoc->GetParser();
263   CPDF_Dictionary* pDict = pParser->GetEncryptDict();
264   return pDict ? pDict->GetInteger("R") : -1;
265 }
266
267 DLLEXPORT int STDCALL FPDF_GetPageCount(FPDF_DOCUMENT document) {
268   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
269   return pDoc ? pDoc->GetPageCount() : 0;
270 }
271
272 DLLEXPORT FPDF_PAGE STDCALL FPDF_LoadPage(FPDF_DOCUMENT document,
273                                           int page_index) {
274   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
275   if (!pDoc)
276     return nullptr;
277
278   if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
279     return nullptr;
280
281   CPDF_Dictionary* pDict = pDoc->GetPage(page_index);
282   if (pDict == NULL)
283     return NULL;
284   CPDF_Page* pPage = new CPDF_Page;
285   pPage->Load(pDoc, pDict);
286   pPage->ParseContent();
287   return pPage;
288 }
289
290 DLLEXPORT double STDCALL FPDF_GetPageWidth(FPDF_PAGE page) {
291   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
292   return pPage ? pPage->GetPageWidth() : 0.0;
293 }
294
295 DLLEXPORT double STDCALL FPDF_GetPageHeight(FPDF_PAGE page) {
296   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
297   return pPage ? pPage->GetPageHeight() : 0.0;
298 }
299
300 void DropContext(void* data) {
301   delete (CRenderContext*)data;
302 }
303
304 #if defined(_DEBUG) || defined(DEBUG)
305 #define DEBUG_TRACE
306 #endif
307
308 #if defined(_WIN32)
309 DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc,
310                                        FPDF_PAGE page,
311                                        int start_x,
312                                        int start_y,
313                                        int size_x,
314                                        int size_y,
315                                        int rotate,
316                                        int flags) {
317   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
318   if (!pPage)
319     return;
320
321   CRenderContext* pContext = new CRenderContext;
322   pPage->SetPrivateData((void*)1, pContext, DropContext);
323
324 #ifndef _WIN32_WCE
325   CFX_DIBitmap* pBitmap = NULL;
326   FX_BOOL bBackgroundAlphaNeeded = FALSE;
327   bBackgroundAlphaNeeded = pPage->BackgroundAlphaNeeded();
328   if (bBackgroundAlphaNeeded) {
329     pBitmap = new CFX_DIBitmap;
330     pBitmap->Create(size_x, size_y, FXDIB_Argb);
331     pBitmap->Clear(0x00ffffff);
332 #ifdef _SKIA_SUPPORT_
333     pContext->m_pDevice = new CFX_SkiaDevice;
334     ((CFX_SkiaDevice*)pContext->m_pDevice)->Attach((CFX_DIBitmap*)pBitmap);
335 #else
336     pContext->m_pDevice = new CFX_FxgeDevice;
337     ((CFX_FxgeDevice*)pContext->m_pDevice)->Attach((CFX_DIBitmap*)pBitmap);
338 #endif
339   } else
340     pContext->m_pDevice = new CFX_WindowsDevice(dc);
341
342   FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
343                          rotate, flags, TRUE, NULL);
344
345   if (bBackgroundAlphaNeeded) {
346     if (pBitmap) {
347       CFX_WindowsDevice WinDC(dc);
348
349       if (WinDC.GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
350         CFX_DIBitmap* pDst = new CFX_DIBitmap;
351         int pitch = pBitmap->GetPitch();
352         pDst->Create(size_x, size_y, FXDIB_Rgb32);
353         FXSYS_memset(pDst->GetBuffer(), -1, pitch * size_y);
354         pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
355                               FXDIB_BLEND_NORMAL, NULL, FALSE, NULL);
356         WinDC.StretchDIBits(pDst, 0, 0, size_x, size_y);
357         delete pDst;
358       } else
359         WinDC.SetDIBits(pBitmap, 0, 0);
360     }
361   }
362 #else
363   // get clip region
364   RECT rect, cliprect;
365   rect.left = start_x;
366   rect.top = start_y;
367   rect.right = start_x + size_x;
368   rect.bottom = start_y + size_y;
369   GetClipBox(dc, &cliprect);
370   IntersectRect(&rect, &rect, &cliprect);
371   int width = rect.right - rect.left;
372   int height = rect.bottom - rect.top;
373
374 #ifdef DEBUG_TRACE
375   {
376     char str[128];
377     memset(str, 0, sizeof(str));
378     FXSYS_snprintf(str, sizeof(str) - 1, "Rendering DIB %d x %d", width,
379                    height);
380     CPDF_ModuleMgr::Get()->ReportError(999, str);
381   }
382 #endif
383
384   // Create a DIB section
385   LPVOID pBuffer;
386   BITMAPINFOHEADER bmih;
387   FXSYS_memset(&bmih, 0, sizeof bmih);
388   bmih.biSize = sizeof bmih;
389   bmih.biBitCount = 24;
390   bmih.biHeight = -height;
391   bmih.biPlanes = 1;
392   bmih.biWidth = width;
393   pContext->m_hBitmap = CreateDIBSection(dc, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
394                                          &pBuffer, NULL, 0);
395   if (pContext->m_hBitmap == NULL) {
396 #if defined(DEBUG) || defined(_DEBUG)
397     char str[128];
398     memset(str, 0, sizeof(str));
399     FXSYS_snprintf(str, sizeof(str) - 1,
400                    "Error CreateDIBSection: %d x %d, error code = %d", width,
401                    height, GetLastError());
402     CPDF_ModuleMgr::Get()->ReportError(FPDFERR_OUT_OF_MEMORY, str);
403 #else
404     CPDF_ModuleMgr::Get()->ReportError(FPDFERR_OUT_OF_MEMORY, NULL);
405 #endif
406   }
407   FXSYS_memset(pBuffer, 0xff, height * ((width * 3 + 3) / 4 * 4));
408
409 #ifdef DEBUG_TRACE
410   { CPDF_ModuleMgr::Get()->ReportError(999, "DIBSection created"); }
411 #endif
412
413   // Create a device with this external buffer
414   pContext->m_pBitmap = new CFX_DIBitmap;
415   pContext->m_pBitmap->Create(width, height, FXDIB_Rgb, (uint8_t*)pBuffer);
416   pContext->m_pDevice = new CPDF_FxgeDevice;
417   ((CPDF_FxgeDevice*)pContext->m_pDevice)->Attach(pContext->m_pBitmap);
418
419 #ifdef DEBUG_TRACE
420   CPDF_ModuleMgr::Get()->ReportError(999, "Ready for PDF rendering");
421 #endif
422
423   // output to bitmap device
424   FPDF_RenderPage_Retail(pContext, page, start_x - rect.left,
425                          start_y - rect.top, size_x, size_y, rotate, flags);
426
427 #ifdef DEBUG_TRACE
428   CPDF_ModuleMgr::Get()->ReportError(999, "Finished PDF rendering");
429 #endif
430
431   // Now output to real device
432   HDC hMemDC = CreateCompatibleDC(dc);
433   if (hMemDC == NULL) {
434 #if defined(DEBUG) || defined(_DEBUG)
435     char str[128];
436     memset(str, 0, sizeof(str));
437     FXSYS_snprintf(str, sizeof(str) - 1,
438                    "Error CreateCompatibleDC. Error code = %d", GetLastError());
439     CPDF_ModuleMgr::Get()->ReportError(FPDFERR_OUT_OF_MEMORY, str);
440 #else
441     CPDF_ModuleMgr::Get()->ReportError(FPDFERR_OUT_OF_MEMORY, NULL);
442 #endif
443   }
444
445   HGDIOBJ hOldBitmap = SelectObject(hMemDC, pContext->m_hBitmap);
446
447 #ifdef DEBUG_TRACE
448   CPDF_ModuleMgr::Get()->ReportError(999, "Ready for screen rendering");
449 #endif
450
451   BitBlt(dc, rect.left, rect.top, width, height, hMemDC, 0, 0, SRCCOPY);
452   SelectObject(hMemDC, hOldBitmap);
453   DeleteDC(hMemDC);
454
455 #ifdef DEBUG_TRACE
456   CPDF_ModuleMgr::Get()->ReportError(999, "Finished screen rendering");
457 #endif
458
459 #endif
460   if (bBackgroundAlphaNeeded) {
461     delete pBitmap;
462     pBitmap = NULL;
463   }
464   delete pContext;
465   pPage->RemovePrivateData((void*)1);
466 }
467 #endif
468
469 DLLEXPORT void STDCALL FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,
470                                              FPDF_PAGE page,
471                                              int start_x,
472                                              int start_y,
473                                              int size_x,
474                                              int size_y,
475                                              int rotate,
476                                              int flags) {
477   if (!bitmap)
478     return;
479   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
480   if (!pPage)
481     return;
482   CRenderContext* pContext = new CRenderContext;
483   pPage->SetPrivateData((void*)1, pContext, DropContext);
484 #ifdef _SKIA_SUPPORT_
485   pContext->m_pDevice = new CFX_SkiaDevice;
486
487   if (flags & FPDF_REVERSE_BYTE_ORDER)
488     ((CFX_SkiaDevice*)pContext->m_pDevice)
489         ->Attach((CFX_DIBitmap*)bitmap, 0, TRUE);
490   else
491     ((CFX_SkiaDevice*)pContext->m_pDevice)->Attach((CFX_DIBitmap*)bitmap);
492 #else
493   pContext->m_pDevice = new CFX_FxgeDevice;
494
495   if (flags & FPDF_REVERSE_BYTE_ORDER)
496     ((CFX_FxgeDevice*)pContext->m_pDevice)
497         ->Attach((CFX_DIBitmap*)bitmap, 0, TRUE);
498   else
499     ((CFX_FxgeDevice*)pContext->m_pDevice)->Attach((CFX_DIBitmap*)bitmap);
500 #endif
501
502   FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y,
503                          rotate, flags, TRUE, NULL);
504
505   delete pContext;
506   pPage->RemovePrivateData((void*)1);
507 }
508
509 DLLEXPORT void STDCALL FPDF_ClosePage(FPDF_PAGE page) {
510   if (!page)
511     return;
512   CPDFSDK_PageView* pPageView =
513       (CPDFSDK_PageView*)(((CPDF_Page*)page))->GetPrivateData((void*)page);
514   if (pPageView && pPageView->IsLocked()) {
515     pPageView->TakeOverPage();
516     return;
517   }
518   delete (CPDF_Page*)page;
519 }
520
521 DLLEXPORT void STDCALL FPDF_CloseDocument(FPDF_DOCUMENT document) {
522   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
523   if (!pDoc)
524     return;
525
526   CPDF_Parser* pParser = (CPDF_Parser*)pDoc->GetParser();
527   if (!pParser) {
528     delete pDoc;
529     return;
530   }
531   delete pParser;
532 }
533
534 DLLEXPORT unsigned long STDCALL FPDF_GetLastError() {
535   return GetLastError();
536 }
537
538 DLLEXPORT void STDCALL FPDF_DeviceToPage(FPDF_PAGE page,
539                                          int start_x,
540                                          int start_y,
541                                          int size_x,
542                                          int size_y,
543                                          int rotate,
544                                          int device_x,
545                                          int device_y,
546                                          double* page_x,
547                                          double* page_y) {
548   if (page == NULL || page_x == NULL || page_y == NULL)
549     return;
550   CPDF_Page* pPage = (CPDF_Page*)page;
551
552   CPDF_Matrix page2device;
553   pPage->GetDisplayMatrix(page2device, start_x, start_y, size_x, size_y,
554                           rotate);
555   CPDF_Matrix device2page;
556   device2page.SetReverse(page2device);
557
558   FX_FLOAT page_x_f, page_y_f;
559   device2page.Transform((FX_FLOAT)(device_x), (FX_FLOAT)(device_y), page_x_f,
560                         page_y_f);
561
562   *page_x = (page_x_f);
563   *page_y = (page_y_f);
564 }
565
566 DLLEXPORT void STDCALL FPDF_PageToDevice(FPDF_PAGE page,
567                                          int start_x,
568                                          int start_y,
569                                          int size_x,
570                                          int size_y,
571                                          int rotate,
572                                          double page_x,
573                                          double page_y,
574                                          int* device_x,
575                                          int* device_y) {
576   if (!device_x || !device_y)
577     return;
578   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
579   if (!pPage)
580     return;
581   CPDF_Matrix page2device;
582   pPage->GetDisplayMatrix(page2device, start_x, start_y, size_x, size_y,
583                           rotate);
584
585   FX_FLOAT device_x_f, device_y_f;
586   page2device.Transform(((FX_FLOAT)page_x), ((FX_FLOAT)page_y), device_x_f,
587                         device_y_f);
588
589   *device_x = FXSYS_round(device_x_f);
590   *device_y = FXSYS_round(device_y_f);
591 }
592
593 DLLEXPORT FPDF_BITMAP STDCALL FPDFBitmap_Create(int width,
594                                                 int height,
595                                                 int alpha) {
596   nonstd::unique_ptr<CFX_DIBitmap> pBitmap(new CFX_DIBitmap);
597   if (!pBitmap->Create(width, height, alpha ? FXDIB_Argb : FXDIB_Rgb32)) {
598     return NULL;
599   }
600   return pBitmap.release();
601 }
602
603 DLLEXPORT FPDF_BITMAP STDCALL FPDFBitmap_CreateEx(int width,
604                                                   int height,
605                                                   int format,
606                                                   void* first_scan,
607                                                   int stride) {
608   FXDIB_Format fx_format;
609   switch (format) {
610     case FPDFBitmap_Gray:
611       fx_format = FXDIB_8bppRgb;
612       break;
613     case FPDFBitmap_BGR:
614       fx_format = FXDIB_Rgb;
615       break;
616     case FPDFBitmap_BGRx:
617       fx_format = FXDIB_Rgb32;
618       break;
619     case FPDFBitmap_BGRA:
620       fx_format = FXDIB_Argb;
621       break;
622     default:
623       return NULL;
624   }
625   CFX_DIBitmap* pBitmap = new CFX_DIBitmap;
626   pBitmap->Create(width, height, fx_format, (uint8_t*)first_scan, stride);
627   return pBitmap;
628 }
629
630 DLLEXPORT void STDCALL FPDFBitmap_FillRect(FPDF_BITMAP bitmap,
631                                            int left,
632                                            int top,
633                                            int width,
634                                            int height,
635                                            FPDF_DWORD color) {
636   if (bitmap == NULL)
637     return;
638 #ifdef _SKIA_SUPPORT_
639   CFX_SkiaDevice device;
640 #else
641   CFX_FxgeDevice device;
642 #endif
643   device.Attach((CFX_DIBitmap*)bitmap);
644   if (!((CFX_DIBitmap*)bitmap)->HasAlpha())
645     color |= 0xFF000000;
646   FX_RECT rect(left, top, left + width, top + height);
647   device.FillRect(&rect, color);
648 }
649
650 DLLEXPORT void* STDCALL FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
651   if (bitmap == NULL)
652     return NULL;
653   return ((CFX_DIBitmap*)bitmap)->GetBuffer();
654 }
655
656 DLLEXPORT int STDCALL FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
657   if (bitmap == NULL)
658     return 0;
659   return ((CFX_DIBitmap*)bitmap)->GetWidth();
660 }
661
662 DLLEXPORT int STDCALL FPDFBitmap_GetHeight(FPDF_BITMAP bitmap) {
663   if (bitmap == NULL)
664     return 0;
665   return ((CFX_DIBitmap*)bitmap)->GetHeight();
666 }
667
668 DLLEXPORT int STDCALL FPDFBitmap_GetStride(FPDF_BITMAP bitmap) {
669   if (bitmap == NULL)
670     return 0;
671   return ((CFX_DIBitmap*)bitmap)->GetPitch();
672 }
673
674 DLLEXPORT void STDCALL FPDFBitmap_Destroy(FPDF_BITMAP bitmap) {
675   delete (CFX_DIBitmap*)bitmap;
676 }
677
678 void FPDF_RenderPage_Retail(CRenderContext* pContext,
679                             FPDF_PAGE page,
680                             int start_x,
681                             int start_y,
682                             int size_x,
683                             int size_y,
684                             int rotate,
685                             int flags,
686                             FX_BOOL bNeedToRestore,
687                             IFSDK_PAUSE_Adapter* pause) {
688   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
689   if (!pPage)
690     return;
691
692   if (!pContext->m_pOptions)
693     pContext->m_pOptions = new CPDF_RenderOptions;
694
695   if (flags & FPDF_LCD_TEXT)
696     pContext->m_pOptions->m_Flags |= RENDER_CLEARTYPE;
697   else
698     pContext->m_pOptions->m_Flags &= ~RENDER_CLEARTYPE;
699   if (flags & FPDF_NO_NATIVETEXT)
700     pContext->m_pOptions->m_Flags |= RENDER_NO_NATIVETEXT;
701   if (flags & FPDF_RENDER_LIMITEDIMAGECACHE)
702     pContext->m_pOptions->m_Flags |= RENDER_LIMITEDIMAGECACHE;
703   if (flags & FPDF_RENDER_FORCEHALFTONE)
704     pContext->m_pOptions->m_Flags |= RENDER_FORCE_HALFTONE;
705   if (flags & FPDF_RENDER_NO_SMOOTHTEXT)
706     pContext->m_pOptions->m_Flags |= RENDER_NOTEXTSMOOTH;
707   if (flags & FPDF_RENDER_NO_SMOOTHIMAGE)
708     pContext->m_pOptions->m_Flags |= RENDER_NOIMAGESMOOTH;
709   if (flags & FPDF_RENDER_NO_SMOOTHPATH)
710     pContext->m_pOptions->m_Flags |= RENDER_NOPATHSMOOTH;
711   // Grayscale output
712   if (flags & FPDF_GRAYSCALE) {
713     pContext->m_pOptions->m_ColorMode = RENDER_COLOR_GRAY;
714     pContext->m_pOptions->m_ForeColor = 0;
715     pContext->m_pOptions->m_BackColor = 0xffffff;
716   }
717   const CPDF_OCContext::UsageType usage =
718       (flags & FPDF_PRINTING) ? CPDF_OCContext::Print : CPDF_OCContext::View;
719   pContext->m_pOptions->m_AddFlags = flags >> 8;
720   pContext->m_pOptions->m_pOCContext =
721       new CPDF_OCContext(pPage->m_pDocument, usage);
722
723   CFX_AffineMatrix matrix;
724   pPage->GetDisplayMatrix(matrix, start_x, start_y, size_x, size_y, rotate);
725
726   FX_RECT clip;
727   clip.left = start_x;
728   clip.right = start_x + size_x;
729   clip.top = start_y;
730   clip.bottom = start_y + size_y;
731   pContext->m_pDevice->SaveState();
732   pContext->m_pDevice->SetClip_Rect(&clip);
733
734   pContext->m_pContext = new CPDF_RenderContext;
735   pContext->m_pContext->Create(pPage);
736   pContext->m_pContext->AppendObjectList(pPage, &matrix);
737
738   if (flags & FPDF_ANNOT) {
739     pContext->m_pAnnots = new CPDF_AnnotList(pPage);
740     FX_BOOL bPrinting = pContext->m_pDevice->GetDeviceClass() != FXDC_DISPLAY;
741     pContext->m_pAnnots->DisplayAnnots(pPage, pContext->m_pContext, bPrinting,
742                                        &matrix, TRUE, NULL);
743   }
744
745   pContext->m_pRenderer = new CPDF_ProgressiveRenderer(
746       pContext->m_pContext, pContext->m_pDevice, pContext->m_pOptions);
747   pContext->m_pRenderer->Start(pause);
748   if (bNeedToRestore)
749     pContext->m_pDevice->RestoreState();
750 }
751
752 DLLEXPORT int STDCALL FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
753                                               int page_index,
754                                               double* width,
755                                               double* height) {
756   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
757   if (!pDoc)
758     return FALSE;
759
760   CPDF_Dictionary* pDict = pDoc->GetPage(page_index);
761   if (!pDict)
762     return FALSE;
763
764   CPDF_Page page;
765   page.Load(pDoc, pDict);
766   *width = page.GetPageWidth();
767   *height = page.GetPageHeight();
768
769   return TRUE;
770 }
771
772 DLLEXPORT FPDF_BOOL STDCALL
773 FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document) {
774   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
775   if (!pDoc)
776     return TRUE;
777   CPDF_ViewerPreferences viewRef(pDoc);
778   return viewRef.PrintScaling();
779 }
780
781 DLLEXPORT int STDCALL FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document) {
782   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
783   if (!pDoc)
784     return 1;
785   CPDF_ViewerPreferences viewRef(pDoc);
786   return viewRef.NumCopies();
787 }
788
789 DLLEXPORT FPDF_PAGERANGE STDCALL
790 FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) {
791   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
792   if (!pDoc)
793     return NULL;
794   CPDF_ViewerPreferences viewRef(pDoc);
795   return viewRef.PrintPageRange();
796 }
797
798 DLLEXPORT FPDF_DUPLEXTYPE STDCALL
799 FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document) {
800   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
801   if (!pDoc)
802     return DuplexUndefined;
803   CPDF_ViewerPreferences viewRef(pDoc);
804   CFX_ByteString duplex = viewRef.Duplex();
805   if (FX_BSTRC("Simplex") == duplex)
806     return Simplex;
807   if (FX_BSTRC("DuplexFlipShortEdge") == duplex)
808     return DuplexFlipShortEdge;
809   if (FX_BSTRC("DuplexFlipLongEdge") == duplex)
810     return DuplexFlipLongEdge;
811   return DuplexUndefined;
812 }
813
814 DLLEXPORT FPDF_DWORD STDCALL FPDF_CountNamedDests(FPDF_DOCUMENT document) {
815   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
816   if (!pDoc)
817     return 0;
818
819   CPDF_Dictionary* pRoot = pDoc->GetRoot();
820   if (!pRoot)
821     return 0;
822
823   CPDF_NameTree nameTree(pDoc, FX_BSTRC("Dests"));
824   int count = nameTree.GetCount();
825   CPDF_Dictionary* pDest = pRoot->GetDict(FX_BSTRC("Dests"));
826   if (pDest)
827     count += pDest->GetCount();
828   return count;
829 }
830
831 DLLEXPORT FPDF_DEST STDCALL FPDF_GetNamedDestByName(FPDF_DOCUMENT document,
832                                                     FPDF_BYTESTRING name) {
833   if (!name || name[0] == 0)
834     return nullptr;
835
836   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
837   if (!pDoc)
838     return nullptr;
839
840   CPDF_NameTree name_tree(pDoc, FX_BSTRC("Dests"));
841   return name_tree.LookupNamedDest(pDoc, name);
842 }
843
844 DLLEXPORT FPDF_DEST STDCALL FPDF_GetNamedDest(FPDF_DOCUMENT document,
845                                               int index,
846                                               void* buffer,
847                                               long* buflen) {
848   if (!buffer)
849     *buflen = 0;
850   if (!document || index < 0)
851     return NULL;
852   CPDF_Document* pDoc = (CPDF_Document*)document;
853
854   CPDF_Dictionary* pRoot = pDoc->GetRoot();
855   if (!pRoot)
856     return NULL;
857
858   CPDF_Object* pDestObj = NULL;
859   CFX_ByteString bsName;
860   CPDF_NameTree nameTree(pDoc, FX_BSTRC("Dests"));
861   int count = nameTree.GetCount();
862   if (index >= count) {
863     CPDF_Dictionary* pDest = pRoot->GetDict(FX_BSTRC("Dests"));
864     if (!pDest)
865       return NULL;
866     if (index >= count + pDest->GetCount())
867       return NULL;
868     index -= count;
869     FX_POSITION pos = pDest->GetStartPos();
870     int i = 0;
871     while (pos) {
872       pDestObj = pDest->GetNextElement(pos, bsName);
873       if (!pDestObj)
874         continue;
875       if (i == index)
876         break;
877       i++;
878     }
879   } else {
880     pDestObj = nameTree.LookupValue(index, bsName);
881   }
882   if (!pDestObj)
883     return NULL;
884   if (pDestObj->GetType() == PDFOBJ_DICTIONARY) {
885     pDestObj = ((CPDF_Dictionary*)pDestObj)->GetArray(FX_BSTRC("D"));
886     if (!pDestObj)
887       return NULL;
888   }
889   if (pDestObj->GetType() != PDFOBJ_ARRAY)
890     return NULL;
891   CFX_WideString wsName = PDF_DecodeText(bsName);
892   CFX_ByteString utf16Name = wsName.UTF16LE_Encode();
893   unsigned int len = utf16Name.GetLength();
894   if (!buffer) {
895     *buflen = len;
896   } else if (*buflen >= len) {
897     memcpy(buffer, utf16Name.c_str(), len);
898     *buflen = len;
899   } else {
900     *buflen = -1;
901   }
902   return (FPDF_DEST)pDestObj;
903 }