• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef TESTING_EMBEDDER_TEST_H_
6 #define TESTING_EMBEDDER_TEST_H_
7 
8 #include <stdint.h>
9 
10 #include <fstream>
11 #include <map>
12 #include <memory>
13 #include <string>
14 #include <vector>
15 
16 #include "build/build_config.h"
17 #include "core/fxcrt/span.h"
18 #include "core/fxcrt/unowned_ptr.h"
19 #include "public/cpp/fpdf_scopers.h"
20 #include "public/fpdf_dataavail.h"
21 #include "public/fpdf_ext.h"
22 #include "public/fpdf_formfill.h"
23 #include "public/fpdf_save.h"
24 #include "public/fpdfview.h"
25 #include "testing/fake_file_access.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 
28 class TestLoader;
29 
30 // The loading time of the CFGAS_FontMgr is linear in the number of times it is
31 // loaded. So, if a test suite has a lot of tests that need a font manager they
32 // can end up executing very, very slowly.
33 
34 // This class is used to load a PDF document, and then run programatic
35 // API tests against it.
36 class EmbedderTest : public ::testing::Test,
37                      public UNSUPPORT_INFO,
38                      public IPDF_JSPLATFORM,
39                      public FPDF_FORMFILLINFO,
40                      public FPDF_FILEWRITE {
41  public:
42   enum class LinearizeOption { kDefaultLinearize, kMustLinearize };
43   enum class JavaScriptOption { kDisableJavaScript, kEnableJavaScript };
44 
45   class Delegate {
46    public:
47     virtual ~Delegate() = default;
48 
49     // Equivalent to UNSUPPORT_INFO::FSDK_UnSupport_Handler().
UnsupportedHandler(int type)50     virtual void UnsupportedHandler(int type) {}
51 
52     // Equivalent to IPDF_JSPLATFORM::app_alert().
Alert(FPDF_WIDESTRING message,FPDF_WIDESTRING title,int type,int icon)53     virtual int Alert(FPDF_WIDESTRING message,
54                       FPDF_WIDESTRING title,
55                       int type,
56                       int icon) {
57       return 0;
58     }
59 
60     // Equivalent to FPDF_FORMFILLINFO::FFI_SetTimer().
SetTimer(int msecs,TimerCallback fn)61     virtual int SetTimer(int msecs, TimerCallback fn) { return 0; }
62 
63     // Equivalent to FPDF_FORMFILLINFO::FFI_KillTimer().
KillTimer(int id)64     virtual void KillTimer(int id) {}
65 
66     // Equivalent to FPDF_FORMFILLINFO::FFI_GetPage().
67     virtual FPDF_PAGE GetPage(FPDF_FORMFILLINFO* info,
68                               FPDF_DOCUMENT document,
69                               int page_index);
70 
71     // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction().
DoURIAction(FPDF_BYTESTRING uri)72     virtual void DoURIAction(FPDF_BYTESTRING uri) {}
73 
74     // Equivalent to FPDF_FORMFILLINFO::FFI_DoGoToAction().
DoGoToAction(FPDF_FORMFILLINFO * info,int page_index,int zoom_mode,float * pos_arry,int array_size)75     virtual void DoGoToAction(FPDF_FORMFILLINFO* info,
76                               int page_index,
77                               int zoom_mode,
78                               float* pos_arry,
79                               int array_size) {}
80 
81     // Equivalent to FPDF_FORMFILLINFO::FFI_OnFocusChange().
OnFocusChange(FPDF_FORMFILLINFO * info,FPDF_ANNOTATION annot,int page_index)82     virtual void OnFocusChange(FPDF_FORMFILLINFO* info,
83                                FPDF_ANNOTATION annot,
84                                int page_index) {}
85 
86     // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIActionWithKeyboardModifier().
DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO * info,FPDF_BYTESTRING uri,int modifiers)87     virtual void DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO* info,
88                                                  FPDF_BYTESTRING uri,
89                                                  int modifiers) {}
90   };
91 
92   class ScopedEmbedderTestPage {
93    public:
94     ScopedEmbedderTestPage();
95     ScopedEmbedderTestPage(EmbedderTest* test, int page_index);
96     ScopedEmbedderTestPage(const ScopedEmbedderTestPage&) = delete;
97     ScopedEmbedderTestPage& operator=(const ScopedEmbedderTestPage&) = delete;
98     ScopedEmbedderTestPage(ScopedEmbedderTestPage&&) noexcept;
99     ScopedEmbedderTestPage& operator=(ScopedEmbedderTestPage&&) noexcept;
100     ~ScopedEmbedderTestPage();
101 
get()102     FPDF_PAGE get() { return page_; }
103 
104     explicit operator bool() const { return !!page_; }
105 
106    private:
107     UnownedPtr<EmbedderTest> test_;
108     FPDF_PAGE page_;
109   };
110 
111   EmbedderTest();
112   ~EmbedderTest() override;
113 
114   void SetUp() override;
115   void TearDown() override;
116 
GetDelegate()117   Delegate* GetDelegate() { return delegate_; }
SetDelegate(Delegate * delegate)118   void SetDelegate(Delegate* delegate) {
119     delegate_ = delegate ? delegate : default_delegate_.get();
120   }
121 
SetFormFillInfoVersion(int form_fill_info_version)122   void SetFormFillInfoVersion(int form_fill_info_version) {
123     form_fill_info_version_ = form_fill_info_version;
124   }
125 
126   void SetDocumentFromAvail();
document()127   FPDF_DOCUMENT document() const { return document_.get(); }
saved_document()128   FPDF_DOCUMENT saved_document() const { return saved_document_.get(); }
form_handle()129   FPDF_FORMHANDLE form_handle() const { return form_handle_.get(); }
saved_form_handle()130   FPDF_FORMHANDLE saved_form_handle() const { return saved_form_handle_.get(); }
131 
132   // Wrapper for FPDFAvail_Create() to set `avail_`.
133   void CreateAvail(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file);
avail()134   FPDF_AVAIL avail() { return avail_.get(); }
135 
136   // Create an empty document, and its form fill environment.
137   void CreateEmptyDocument();
138 
139   // Create an empty document without a form fill environment.
140   void CreateEmptyDocumentWithoutFormFillEnvironment();
141 
142   // Open the document specified by |filename|, and create its form fill
143   // environment, or return false on failure. The |filename| is relative to
144   // the test data directory where we store all the test files. |password| can
145   // be nullptr if the file is not password protected. If |javascript_opts|
146   // is kDisableJavascript, then the document will be given stubs in place
147   // of the real JS engine.
148   virtual bool OpenDocumentWithOptions(const std::string& filename,
149                                        const char* password,
150                                        LinearizeOption linearize_option,
151                                        JavaScriptOption javascript_option);
152 
153   // Variants provided for convenience.
154   bool OpenDocument(const std::string& filename);
155   bool OpenDocumentLinearized(const std::string& filename);
156   bool OpenDocumentWithPassword(const std::string& filename,
157                                 const char* password);
158   bool OpenDocumentWithoutJavaScript(const std::string& filename);
159 
160   // Close the document from a previous OpenDocument() call. This happens
161   // automatically at tear-down, and is usually not explicitly required,
162   // unless testing multiple documents or duplicate destruction.
163   void CloseDocument();
164 
165   // Perform JavaScript actions that are to run at document open time.
166   void DoOpenActions();
167 
168   // Determine the page numbers present in the document.
169   int GetFirstPageNum();
170   int GetPageCount();
171 
172   // Load a specific page of the open document with a given non-negative
173   // `page_index`. On success, fire form events for the page and return a
174   // ScopedEmbedderTestPage with the page handle. On failure, return an empty
175   // ScopedEmbedderTestPage.
176   // The caller needs to let the ScopedEmbedderTestPage go out of scope to
177   // properly unload the page, and must do so before the page's document and
178   // `this` get destroyed.
179   // The caller cannot call this for a `page_index` if it already obtained and
180   // holds the page handle for that page.
181   ScopedEmbedderTestPage LoadScopedPage(int page_index);
182 
183   // Prefer LoadScopedPage() above.
184   //
185   // Load a specific page of the open document with a given non-negative
186   // `page_index`. On success, fire form events for the page and return a page
187   // handle. On failure, return nullptr.
188   // The caller does not own the returned page handle, but must call
189   // UnloadPage() on it when done.
190   // The caller cannot call this for a `page_index` if it already obtained and
191   // holds the page handle for that page.
192   FPDF_PAGE LoadPage(int page_index);
193 
194   // Same as LoadPage(), but does not fire form events.
195   FPDF_PAGE LoadPageNoEvents(int page_index);
196 
197   // Fire form unload events and release the resources for a |page| obtained
198   // from LoadPage(). Further use of |page| is prohibited after calling this.
199   void UnloadPage(FPDF_PAGE page);
200 
201   // Same as UnloadPage(), but does not fire form events.
202   void UnloadPageNoEvents(FPDF_PAGE page);
203 
204   // Apply standard highlighting color/alpha to forms.
205   void SetInitialFormFieldHighlight(FPDF_FORMHANDLE form);
206 
207   // RenderLoadedPageWithFlags() with no flags.
208   ScopedFPDFBitmap RenderLoadedPage(FPDF_PAGE page);
209 
210   // Convert |page| loaded via LoadPage() into a bitmap with the specified page
211   // rendering |flags|.
212   //
213   // See public/fpdfview.h for a list of page rendering flags.
214   ScopedFPDFBitmap RenderLoadedPageWithFlags(FPDF_PAGE page, int flags);
215 
216   // RenderSavedPageWithFlags() with no flags.
217   ScopedFPDFBitmap RenderSavedPage(FPDF_PAGE page);
218 
219   // Convert |page| loaded via LoadSavedPage() into a bitmap with the specified
220   // page rendering |flags|.
221   //
222   // See public/fpdfview.h for a list of page rendering flags.
223   ScopedFPDFBitmap RenderSavedPageWithFlags(FPDF_PAGE page, int flags);
224 
225   // Convert |page| into a bitmap with the specified page rendering |flags|.
226   // The form handle associated with |page| should be passed in via |handle|.
227   // If |handle| is nullptr, then forms on the page will not be rendered.
228   //
229   // See public/fpdfview.h for a list of page rendering flags.
230   // If none of the above Render methods are appropriate, then use this one.
231   static ScopedFPDFBitmap RenderPageWithFlags(FPDF_PAGE page,
232                                               FPDF_FORMHANDLE handle,
233                                               int flags);
234 
235   // Simplified form of RenderPageWithFlags() with no handle and no flags.
236   static ScopedFPDFBitmap RenderPage(FPDF_PAGE page);
237 
238 #if BUILDFLAG(IS_WIN)
239   // Convert |page| into EMF with the specified page rendering |flags|.
240   static std::vector<uint8_t> RenderPageWithFlagsToEmf(FPDF_PAGE page,
241                                                        int flags);
242 
243   // Get the PostScript data from |emf_data|.
244   static std::string GetPostScriptFromEmf(pdfium::span<const uint8_t> emf_data);
245 #endif  // BUILDFLAG(IS_WIN)
246 
247   // Return bytes for each of the FPDFBitmap_* format types.
248   static int BytesPerPixelForFormat(int format);
249 
250  protected:
251   using PageNumberToHandleMap = std::map<int, FPDF_PAGE>;
252 
253   bool OpenDocumentHelper(const char* password,
254                           LinearizeOption linearize_option,
255                           JavaScriptOption javascript_option,
256                           FakeFileAccess* network_simulator,
257                           ScopedFPDFDocument* document,
258                           ScopedFPDFAvail* avail,
259                           ScopedFPDFFormHandle* form_handle);
260 
261   FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc,
262                                            JavaScriptOption javascript_option);
263 
264   // Return the hash of only the pixels in |bitmap|. i.e. Excluding the gap, if
265   // any, at the end of a row where the stride is larger than width * bpp.
266   static std::string HashBitmap(FPDF_BITMAP bitmap);
267 
268   // For debugging purposes.
269   // Write |bitmap| as a PNG to |filename|.
270   static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename);
271 
272   // Check |bitmap| to make sure it has the right dimensions and content.
273   static void CompareBitmap(FPDF_BITMAP bitmap,
274                             int expected_width,
275                             int expected_height,
276                             const char* expected_md5sum);
277 
ClearString()278   void ClearString() { data_string_.clear(); }
GetString()279   const std::string& GetString() const { return data_string_; }
280 
281   static int GetBlockFromString(void* param,
282                                 unsigned long pos,
283                                 unsigned char* buf,
284                                 unsigned long size);
285 
286   // See comments in the respective non-Saved versions of these methods.
287   FPDF_DOCUMENT OpenSavedDocument();
288   FPDF_DOCUMENT OpenSavedDocumentWithPassword(const char* password);
289   void CloseSavedDocument();
290   FPDF_PAGE LoadSavedPage(int page_index);
291   void CloseSavedPage(FPDF_PAGE page);
292 
293   void VerifySavedRendering(FPDF_PAGE page,
294                             int width,
295                             int height,
296                             const char* md5);
297   void VerifySavedDocument(int width, int height, const char* md5);
298 
299   void SetWholeFileAvailable();
300 
301 #ifndef NDEBUG
302   // For debugging purposes.
303   // While open, write any data that gets passed to WriteBlockCallback() to
304   // |filename|. This is typically used to capture data from FPDF_SaveAsCopy()
305   // calls.
306   void OpenPDFFileForWrite(const std::string& filename);
307   void ClosePDFFileForWrite();
308 #endif
309 
310  private:
311   static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite,
312                                 const void* data,
313                                 unsigned long size);
314 
315   // Helper method for the methods below.
316   static int GetPageNumberForPage(const PageNumberToHandleMap& page_map,
317                                   FPDF_PAGE page);
318   // Find |page| inside |page_map_| and return the associated page number, or -1
319   // if |page| cannot be found.
320   int GetPageNumberForLoadedPage(FPDF_PAGE page) const;
321 
322   // Same as GetPageNumberForLoadedPage(), but with |saved_page_map_|.
323   int GetPageNumberForSavedPage(FPDF_PAGE page) const;
324 
325   void UnloadPageCommon(FPDF_PAGE page, bool do_events);
326   FPDF_PAGE LoadPageCommon(int page_index, bool do_events);
327 
328   std::unique_ptr<Delegate> default_delegate_;
329   Delegate* delegate_;
330 
331 #ifdef PDF_ENABLE_XFA
332   int form_fill_info_version_ = 2;
333 #else   // PDF_ENABLE_XFA
334   int form_fill_info_version_ = 1;
335 #endif  // PDF_ENABLE_XFA
336 
337   // must outlive `loader_`.
338   std::vector<uint8_t> file_contents_;
339   std::unique_ptr<TestLoader> loader_;
340   FPDF_FILEACCESS file_access_;                       // must outlive `avail_`.
341   std::unique_ptr<FakeFileAccess> fake_file_access_;  // must outlive `avail_`.
342   ScopedFPDFAvail avail_;
343   ScopedFPDFDocument document_;
344   ScopedFPDFFormHandle form_handle_;
345   PageNumberToHandleMap page_map_;
346 
347   FPDF_FILEACCESS saved_file_access_;  // must outlive `saved_avail_`.
348   // must outlive `saved_avail_`.
349   std::unique_ptr<FakeFileAccess> saved_fake_file_access_;
350   ScopedFPDFAvail saved_avail_;
351   ScopedFPDFDocument saved_document_;
352   ScopedFPDFFormHandle saved_form_handle_;
353   PageNumberToHandleMap saved_page_map_;
354 
355   std::string data_string_;
356   std::string saved_document_file_data_;
357   std::ofstream filestream_;
358 };
359 
360 #endif  // TESTING_EMBEDDER_TEST_H_
361