diff options
Diffstat (limited to 'samples/pdfium_test.cc')
-rw-r--r-- | samples/pdfium_test.cc | 784 |
1 files changed, 5 insertions, 779 deletions
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc index c02629732b..b0268d0196 100644 --- a/samples/pdfium_test.cc +++ b/samples/pdfium_test.cc @@ -7,13 +7,11 @@ #include <stdlib.h> #include <string.h> -#include <bitset> #include <iterator> #include <map> #include <memory> #include <sstream> #include <string> -#include <utility> #include <vector> #if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_ @@ -31,7 +29,9 @@ #include "public/fpdf_structtree.h" #include "public/fpdf_text.h" #include "public/fpdfview.h" -#include "testing/image_diff/image_diff_png.h" +#include "samples/pdfium_test_dump_helper.h" +#include "samples/pdfium_test_event_helper.h" +#include "samples/pdfium_test_write_helper.h" #include "testing/test_support.h" #include "third_party/base/logging.h" @@ -50,11 +50,6 @@ #include "v8/include/v8.h" #endif // PDF_ENABLE_V8 -#ifdef PDF_ENABLE_SKIA -#include "third_party/skia/include/core/SkPictureRecorder.h" -#include "third_party/skia/include/core/SkStream.h" -#endif - #ifdef _WIN32 #define access _access #define snprintf _snprintf @@ -133,14 +128,6 @@ FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo( return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info); } -bool CheckDimensions(int stride, int width, int height) { - if (stride < 0 || width < 0 || height < 0) - return false; - if (height > 0 && width > INT_MAX / height) - return false; - return true; -} - void OutputMD5Hash(const char* file_name, const char* buffer, int len) { // Get the MD5 hash and write it to stdout. std::string hash = @@ -148,472 +135,6 @@ void OutputMD5Hash(const char* file_name, const char* buffer, int len) { printf("MD5:%s:%s\n", file_name, hash.c_str()); } -std::string WritePpm(const char* pdf_name, - int num, - const void* buffer_void, - int stride, - int width, - int height) { - const auto* buffer = reinterpret_cast<const char*>(buffer_void); - - if (!CheckDimensions(stride, width, height)) - return ""; - - int out_len = width * height; - if (out_len > INT_MAX / 3) - return ""; - out_len *= 3; - - char filename[256]; - snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num); - FILE* fp = fopen(filename, "wb"); - if (!fp) - return ""; - fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height); - // Source data is B, G, R, unused. - // Dest data is R, G, B. - std::vector<char> result(out_len); - for (int h = 0; h < height; ++h) { - const char* src_line = buffer + (stride * h); - char* dest_line = result.data() + (width * h * 3); - for (int w = 0; w < width; ++w) { - // R - dest_line[w * 3] = src_line[(w * 4) + 2]; - // G - dest_line[(w * 3) + 1] = src_line[(w * 4) + 1]; - // B - dest_line[(w * 3) + 2] = src_line[w * 4]; - } - } - if (fwrite(result.data(), out_len, 1, fp) != 1) - fprintf(stderr, "Failed to write to %s\n", filename); - fclose(fp); - return std::string(filename); -} - -void WriteText(FPDF_PAGE page, const char* pdf_name, int num) { - char filename[256]; - int chars_formatted = - snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num); - if (chars_formatted < 0 || - static_cast<size_t>(chars_formatted) >= sizeof(filename)) { - fprintf(stderr, "Filename %s is too long\n", filename); - return; - } - - FILE* fp = fopen(filename, "w"); - if (!fp) { - fprintf(stderr, "Failed to open %s for output\n", filename); - return; - } - - // Output in UTF32-LE. - uint32_t bom = 0x0000FEFF; - if (fwrite(&bom, sizeof(bom), 1, fp) != 1) { - fprintf(stderr, "Failed to write to %s\n", filename); - (void)fclose(fp); - return; - } - - std::unique_ptr<void, FPDFTextPageDeleter> textpage(FPDFText_LoadPage(page)); - for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) { - uint32_t c = FPDFText_GetUnicode(textpage.get(), i); - if (fwrite(&c, sizeof(c), 1, fp) != 1) { - fprintf(stderr, "Failed to write to %s\n", filename); - break; - } - } - (void)fclose(fp); -} - -const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) { - if (subtype == FPDF_ANNOT_TEXT) - return "Text"; - if (subtype == FPDF_ANNOT_LINK) - return "Link"; - if (subtype == FPDF_ANNOT_FREETEXT) - return "FreeText"; - if (subtype == FPDF_ANNOT_LINE) - return "Line"; - if (subtype == FPDF_ANNOT_SQUARE) - return "Square"; - if (subtype == FPDF_ANNOT_CIRCLE) - return "Circle"; - if (subtype == FPDF_ANNOT_POLYGON) - return "Polygon"; - if (subtype == FPDF_ANNOT_POLYLINE) - return "PolyLine"; - if (subtype == FPDF_ANNOT_HIGHLIGHT) - return "Highlight"; - if (subtype == FPDF_ANNOT_UNDERLINE) - return "Underline"; - if (subtype == FPDF_ANNOT_SQUIGGLY) - return "Squiggly"; - if (subtype == FPDF_ANNOT_STRIKEOUT) - return "StrikeOut"; - if (subtype == FPDF_ANNOT_STAMP) - return "Stamp"; - if (subtype == FPDF_ANNOT_CARET) - return "Caret"; - if (subtype == FPDF_ANNOT_INK) - return "Ink"; - if (subtype == FPDF_ANNOT_POPUP) - return "Popup"; - if (subtype == FPDF_ANNOT_FILEATTACHMENT) - return "FileAttachment"; - if (subtype == FPDF_ANNOT_SOUND) - return "Sound"; - if (subtype == FPDF_ANNOT_MOVIE) - return "Movie"; - if (subtype == FPDF_ANNOT_WIDGET) - return "Widget"; - if (subtype == FPDF_ANNOT_SCREEN) - return "Screen"; - if (subtype == FPDF_ANNOT_PRINTERMARK) - return "PrinterMark"; - if (subtype == FPDF_ANNOT_TRAPNET) - return "TrapNet"; - if (subtype == FPDF_ANNOT_WATERMARK) - return "Watermark"; - if (subtype == FPDF_ANNOT_THREED) - return "3D"; - if (subtype == FPDF_ANNOT_RICHMEDIA) - return "RichMedia"; - if (subtype == FPDF_ANNOT_XFAWIDGET) - return "XFAWidget"; - NOTREACHED(); - return ""; -} - -void AppendFlagString(const char* flag, std::string* output) { - if (!output->empty()) - *output += ", "; - *output += flag; -} - -std::string AnnotFlagsToString(int flags) { - std::string str; - if (flags & FPDF_ANNOT_FLAG_INVISIBLE) - AppendFlagString("Invisible", &str); - if (flags & FPDF_ANNOT_FLAG_HIDDEN) - AppendFlagString("Hidden", &str); - if (flags & FPDF_ANNOT_FLAG_PRINT) - AppendFlagString("Print", &str); - if (flags & FPDF_ANNOT_FLAG_NOZOOM) - AppendFlagString("NoZoom", &str); - if (flags & FPDF_ANNOT_FLAG_NOROTATE) - AppendFlagString("NoRotate", &str); - if (flags & FPDF_ANNOT_FLAG_NOVIEW) - AppendFlagString("NoView", &str); - if (flags & FPDF_ANNOT_FLAG_READONLY) - AppendFlagString("ReadOnly", &str); - if (flags & FPDF_ANNOT_FLAG_LOCKED) - AppendFlagString("Locked", &str); - if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW) - AppendFlagString("ToggleNoView", &str); - return str; -} - -const char* PageObjectTypeToCString(int type) { - if (type == FPDF_PAGEOBJ_TEXT) - return "Text"; - if (type == FPDF_PAGEOBJ_PATH) - return "Path"; - if (type == FPDF_PAGEOBJ_IMAGE) - return "Image"; - if (type == FPDF_PAGEOBJ_SHADING) - return "Shading"; - if (type == FPDF_PAGEOBJ_FORM) - return "Form"; - NOTREACHED(); - return ""; -} - -void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) { - // Open the output text file. - char filename[256]; - int chars_formatted = - snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num); - if (chars_formatted < 0 || - static_cast<size_t>(chars_formatted) >= sizeof(filename)) { - fprintf(stderr, "Filename %s is too long\n", filename); - return; - } - FILE* fp = fopen(filename, "w"); - if (!fp) { - fprintf(stderr, "Failed to open %s for output\n", filename); - return; - } - - int annot_count = FPDFPage_GetAnnotCount(page); - fprintf(fp, "Number of annotations: %d\n\n", annot_count); - - // Iterate through all annotations on this page. - for (int i = 0; i < annot_count; ++i) { - // Retrieve the annotation object and its subtype. - fprintf(fp, "Annotation #%d:\n", i + 1); - std::unique_ptr<void, FPDFAnnotationDeleter> annot( - FPDFPage_GetAnnot(page, i)); - if (!annot) { - fprintf(fp, "Failed to retrieve annotation!\n\n"); - continue; - } - FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot.get()); - fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype)); - - // Retrieve the annotation flags. - fprintf(fp, "Flags set: %s\n", - AnnotFlagsToString(FPDFAnnot_GetFlags(annot.get())).c_str()); - - // Retrieve the annotation's object count and object types. - const int obj_count = FPDFAnnot_GetObjectCount(annot.get()); - fprintf(fp, "Number of objects: %d\n", obj_count); - if (obj_count > 0) { - fprintf(fp, "Object types: "); - for (int j = 0; j < obj_count; ++j) { - const char* type = PageObjectTypeToCString( - FPDFPageObj_GetType(FPDFAnnot_GetObject(annot.get(), j))); - fprintf(fp, "%s ", type); - } - fprintf(fp, "\n"); - } - - // Retrieve the annotation's color and interior color. - unsigned int R; - unsigned int G; - unsigned int B; - unsigned int A; - if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R, &G, &B, - &A)) { - fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A); - } else { - fprintf(fp, "Failed to retrieve color.\n"); - } - if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_InteriorColor, &R, - &G, &B, &A)) { - fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A); - } else { - fprintf(fp, "Failed to retrieve interior color.\n"); - } - - // Retrieve the annotation's contents and author. - static constexpr char kContentsKey[] = "Contents"; - static constexpr char kAuthorKey[] = "T"; - unsigned long len = - FPDFAnnot_GetStringValue(annot.get(), kContentsKey, nullptr, 0); - std::vector<char> buf(len); - FPDFAnnot_GetStringValue(annot.get(), kContentsKey, buf.data(), len); - fprintf(fp, "Content: %ls\n", - GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data())) - .c_str()); - len = FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0); - buf.clear(); - buf.resize(len); - FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(), len); - fprintf(fp, "Author: %ls\n", - GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data())) - .c_str()); - - // Retrieve the annotation's quadpoints if it is a markup annotation. - if (FPDFAnnot_HasAttachmentPoints(annot.get())) { - FS_QUADPOINTSF quadpoints; - if (FPDFAnnot_GetAttachmentPoints(annot.get(), &quadpoints)) { - fprintf(fp, - "Quadpoints: (%.3f, %.3f), (%.3f, %.3f), (%.3f, %.3f), (%.3f, " - "%.3f)\n", - quadpoints.x1, quadpoints.y1, quadpoints.x2, quadpoints.y2, - quadpoints.x3, quadpoints.y3, quadpoints.x4, quadpoints.y4); - } else { - fprintf(fp, "Failed to retrieve quadpoints.\n"); - } - } - - // Retrieve the annotation's rectangle coordinates. - FS_RECTF rect; - if (FPDFAnnot_GetRect(annot.get(), &rect)) { - fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n", - rect.left, rect.bottom, rect.right, rect.top); - } else { - fprintf(fp, "Failed to retrieve annotation rectangle.\n"); - } - } - - (void)fclose(fp); -} - -std::string WritePng(const char* pdf_name, - int num, - const void* buffer_void, - int stride, - int width, - int height) { - if (!CheckDimensions(stride, width, height)) - return ""; - - std::vector<unsigned char> png_encoding; - const auto* buffer = static_cast<const unsigned char*>(buffer_void); - if (!image_diff_png::EncodeBGRAPNG( - buffer, width, height, stride, false, &png_encoding)) { - fprintf(stderr, "Failed to convert bitmap to PNG\n"); - return ""; - } - - char filename[256]; - int chars_formatted = snprintf( - filename, sizeof(filename), "%s.%d.png", pdf_name, num); - if (chars_formatted < 0 || - static_cast<size_t>(chars_formatted) >= sizeof(filename)) { - fprintf(stderr, "Filename %s is too long\n", filename); - return ""; - } - - FILE* fp = fopen(filename, "wb"); - if (!fp) { - fprintf(stderr, "Failed to open %s for output\n", filename); - return ""; - } - - size_t bytes_written = fwrite( - &png_encoding.front(), 1, png_encoding.size(), fp); - if (bytes_written != png_encoding.size()) - fprintf(stderr, "Failed to write to %s\n", filename); - - (void)fclose(fp); - return std::string(filename); -} - -#ifdef _WIN32 -std::string WriteBmp(const char* pdf_name, - int num, - const void* buffer, - int stride, - int width, - int height) { - if (!CheckDimensions(stride, width, height)) - return ""; - - int out_len = stride * height; - if (out_len > INT_MAX / 3) - return ""; - - char filename[256]; - snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num); - FILE* fp = fopen(filename, "wb"); - if (!fp) - return ""; - - BITMAPINFO bmi = {}; - bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD); - bmi.bmiHeader.biWidth = width; - bmi.bmiHeader.biHeight = -height; // top-down image - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = 0; - - BITMAPFILEHEADER file_header = {}; - file_header.bfType = 0x4d42; - file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len; - file_header.bfOffBits = file_header.bfSize - out_len; - - if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 || - fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 || - fwrite(buffer, out_len, 1, fp) != 1) { - fprintf(stderr, "Failed to write to %s\n", filename); - } - fclose(fp); - return std::string(filename); -} - -void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) { - char filename[256]; - snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num); - - HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr); - - int width = static_cast<int>(FPDF_GetPageWidth(page)); - int height = static_cast<int>(FPDF_GetPageHeight(page)); - HRGN rgn = CreateRectRgn(0, 0, width, height); - SelectClipRgn(dc, rgn); - DeleteObject(rgn); - - SelectObject(dc, GetStockObject(NULL_PEN)); - SelectObject(dc, GetStockObject(WHITE_BRUSH)); - // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less. - Rectangle(dc, 0, 0, width + 1, height + 1); - - FPDF_RenderPage(dc, page, 0, 0, width, height, 0, - FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH); - - DeleteEnhMetaFile(CloseEnhMetaFile(dc)); -} - -int CALLBACK EnhMetaFileProc(HDC hdc, - HANDLETABLE* handle_table, - const ENHMETARECORD* record, - int objects_count, - LPARAM param) { - std::vector<const ENHMETARECORD*>& items = - *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param); - items.push_back(record); - return 1; -} - -void WritePS(FPDF_PAGE page, const char* pdf_name, int num) { - char filename[256]; - snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num); - FILE* fp = fopen(filename, "wb"); - if (!fp) - return; - - HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr); - - int width = static_cast<int>(FPDF_GetPageWidth(page)); - int height = static_cast<int>(FPDF_GetPageHeight(page)); - FPDF_RenderPage(dc, page, 0, 0, width, height, 0, - FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH); - - HENHMETAFILE emf = CloseEnhMetaFile(dc); - std::vector<const ENHMETARECORD*> items; - EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr); - for (const ENHMETARECORD* record : items) { - if (record->iType != EMR_GDICOMMENT) - continue; - - const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record); - const char* data = reinterpret_cast<const char*>(comment->Data); - uint16_t size = *reinterpret_cast<const uint16_t*>(data); - if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) { - fprintf(stderr, "Failed to write to %s\n", filename); - break; - } - } - fclose(fp); - DeleteEnhMetaFile(emf); -} -#endif // _WIN32 - -#ifdef PDF_ENABLE_SKIA -std::string WriteSkp(const char* pdf_name, - int num, - SkPictureRecorder* recorder) { - char filename[256]; - int chars_formatted = - snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num); - - if (chars_formatted < 0 || - static_cast<size_t>(chars_formatted) >= sizeof(filename)) { - fprintf(stderr, "Filename %s is too long\n", filename); - return ""; - } - - sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture()); - SkFILEWStream wStream(filename); - picture->serialize(&wStream); - return std::string(filename); -} -#endif - // These example JS platform callback handlers are entirely optional, // and exist here to show the flow of information from a document back // to the embedder. @@ -901,82 +422,6 @@ FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) { void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {} -void SendPageEvents(FPDF_FORMHANDLE form, - FPDF_PAGE page, - const std::string& events) { - auto lines = StringSplit(events, '\n'); - for (auto line : lines) { - auto command = StringSplit(line, '#'); - if (command[0].empty()) - continue; - auto tokens = StringSplit(command[0], ','); - if (tokens[0] == "charcode") { - if (tokens.size() == 2) { - int keycode = atoi(tokens[1].c_str()); - FORM_OnChar(form, page, keycode, 0); - } else { - fprintf(stderr, "charcode: bad args\n"); - } - } else if (tokens[0] == "keycode") { - if (tokens.size() == 2) { - int keycode = atoi(tokens[1].c_str()); - FORM_OnKeyDown(form, page, keycode, 0); - FORM_OnKeyUp(form, page, keycode, 0); - } else { - fprintf(stderr, "keycode: bad args\n"); - } - } else if (tokens[0] == "mousedown") { - if (tokens.size() == 4) { - int x = atoi(tokens[2].c_str()); - int y = atoi(tokens[3].c_str()); - if (tokens[1] == "left") - FORM_OnLButtonDown(form, page, 0, x, y); -#ifdef PDF_ENABLE_XFA - else if (tokens[1] == "right") - FORM_OnRButtonDown(form, page, 0, x, y); -#endif - else - fprintf(stderr, "mousedown: bad button name\n"); - } else { - fprintf(stderr, "mousedown: bad args\n"); - } - } else if (tokens[0] == "mouseup") { - if (tokens.size() == 4) { - int x = atoi(tokens[2].c_str()); - int y = atoi(tokens[3].c_str()); - if (tokens[1] == "left") - FORM_OnLButtonUp(form, page, 0, x, y); -#ifdef PDF_ENABLE_XFA - else if (tokens[1] == "right") - FORM_OnRButtonUp(form, page, 0, x, y); -#endif - else - fprintf(stderr, "mouseup: bad button name\n"); - } else { - fprintf(stderr, "mouseup: bad args\n"); - } - } else if (tokens[0] == "mousemove") { - if (tokens.size() == 3) { - int x = atoi(tokens[1].c_str()); - int y = atoi(tokens[2].c_str()); - FORM_OnMouseMove(form, page, 0, x, y); - } else { - fprintf(stderr, "mousemove: bad args\n"); - } - } else if (tokens[0] == "focus") { - if (tokens.size() == 3) { - int x = atoi(tokens[1].c_str()); - int y = atoi(tokens[2].c_str()); - FORM_OnFocus(form, page, 0, x, y); - } else { - fprintf(stderr, "focus: bad args\n"); - } - } else { - fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str()); - } - } -} - FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param, FPDF_DOCUMENT doc, int index) { @@ -1001,225 +446,6 @@ FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param, return page_ptr; } -std::wstring ConvertToWString(const unsigned short* buf, - unsigned long buf_size) { - std::wstring result; - result.reserve(buf_size); - std::copy(buf, buf + buf_size, std::back_inserter(result)); - return result; -} - -void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) { - static const size_t kBufSize = 1024; - unsigned short buf[kBufSize]; - unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize); - printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str()); - - memset(buf, 0, sizeof(buf)); - len = FPDF_StructElement_GetTitle(child, buf, kBufSize); - if (len > 0) - printf(": '%ls'", ConvertToWString(buf, len).c_str()); - - memset(buf, 0, sizeof(buf)); - len = FPDF_StructElement_GetAltText(child, buf, kBufSize); - if (len > 0) - printf(" (%ls)", ConvertToWString(buf, len).c_str()); - printf("\n"); - - for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) { - FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i); - // If the child is not an Element then this will return null. This can - // happen if the element is things like an object reference or a stream. - if (!sub_child) - continue; - - DumpChildStructure(sub_child, indent + 1); - } -} - -void DumpPageStructure(FPDF_PAGE page, const int page_idx) { - std::unique_ptr<void, FPDFStructTreeDeleter> tree( - FPDF_StructTree_GetForPage(page)); - if (!tree) { - fprintf(stderr, "Failed to load struct tree for page %d\n", page_idx); - return; - } - - printf("Structure Tree for Page %d\n", page_idx); - for (int i = 0; i < FPDF_StructTree_CountChildren(tree.get()); ++i) { - FPDF_STRUCTELEMENT child = FPDF_StructTree_GetChildAtIndex(tree.get(), i); - if (!child) { - fprintf(stderr, "Failed to load child %d for page %d\n", i, page_idx); - continue; - } - DumpChildStructure(child, 0); - } - printf("\n\n"); -} - -void DumpMetaData(FPDF_DOCUMENT doc) { - constexpr const char* meta_tags[] = {"Title", "Author", "Subject", - "Keywords", "Creator", "Producer", - "CreationDate", "ModDate"}; - for (const char* meta_tag : meta_tags) { - char meta_buffer[4096]; - unsigned long len = - FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer)); - if (!len) - continue; - - auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer); - printf("%-12s = %ls (%lu bytes)\n", meta_tag, - GetPlatformWString(meta_string).c_str(), len); - } -} - -void SaveAttachments(FPDF_DOCUMENT doc, const std::string& name) { - for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) { - FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i); - - // Retrieve the attachment file name. - std::string attachment_name; - unsigned long len = FPDFAttachment_GetName(attachment, nullptr, 0); - if (len) { - std::vector<char> buf(len); - unsigned long actual_len = - FPDFAttachment_GetName(attachment, buf.data(), len); - if (actual_len == len) { - attachment_name = - GetPlatformString(reinterpret_cast<unsigned short*>(buf.data())); - } - } - if (attachment_name.empty()) { - fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1); - continue; - } - - // Calculate the full attachment file name. - char save_name[256]; - int chars_formatted = - snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(), - attachment_name.c_str()); - if (chars_formatted < 0 || - static_cast<size_t>(chars_formatted) >= sizeof(save_name)) { - fprintf(stderr, "Filename %s is too long\n", save_name); - continue; - } - - // Retrieve the attachment. - len = FPDFAttachment_GetFile(attachment, nullptr, 0); - std::vector<char> data_buf(len); - if (len) { - unsigned long actual_len = - FPDFAttachment_GetFile(attachment, data_buf.data(), len); - if (actual_len != len) - data_buf.clear(); - } - if (data_buf.empty()) { - fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str()); - continue; - } - - // Write the attachment file. - FILE* fp = fopen(save_name, "wb"); - if (!fp) { - fprintf(stderr, "Failed to open %s for saving attachment.\n", save_name); - continue; - } - - size_t written_len = fwrite(data_buf.data(), 1, len, fp); - if (written_len == len) { - fprintf(stderr, "Saved attachment \"%s\" as: %s.\n", - attachment_name.c_str(), save_name); - } else { - fprintf(stderr, "Failed to write to %s\n", save_name); - } - fclose(fp); - } -} - -void SaveImages(FPDF_PAGE page, const char* pdf_name, int page_num) { - for (int i = 0; i < FPDFPage_CountObjects(page); ++i) { - FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i); - if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE) - continue; - - std::unique_ptr<void, FPDFBitmapDeleter> bitmap( - FPDFImageObj_GetBitmap(obj)); - if (!bitmap) { - fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n", - i + 1, page_num + 1); - continue; - } - - int format = FPDFBitmap_GetFormat(bitmap.get()); - if (format == FPDFBitmap_Unknown) { - fprintf(stderr, - "Image object #%d on page #%d has a bitmap of unknown format.\n", - i + 1, page_num + 1); - continue; - } - - std::vector<unsigned char> png_encoding; - const unsigned char* buffer = - static_cast<const unsigned char*>(FPDFBitmap_GetBuffer(bitmap.get())); - int width = FPDFBitmap_GetWidth(bitmap.get()); - int height = FPDFBitmap_GetHeight(bitmap.get()); - int stride = FPDFBitmap_GetStride(bitmap.get()); - bool ret = false; - switch (format) { - case FPDFBitmap_Gray: - ret = image_diff_png::EncodeGrayPNG(buffer, width, height, stride, - &png_encoding); - break; - case FPDFBitmap_BGR: - ret = image_diff_png::EncodeBGRPNG(buffer, width, height, stride, - &png_encoding); - break; - case FPDFBitmap_BGRx: - ret = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride, true, - &png_encoding); - break; - case FPDFBitmap_BGRA: - ret = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride, - false, &png_encoding); - break; - default: - NOTREACHED(); - } - if (!ret) { - fprintf(stderr, - "Failed to convert image object #%d on page #%d to png.\n", i + 1, - page_num + 1); - continue; - } - - char filename[256]; - int chars_formatted = snprintf(filename, sizeof(filename), "%s.%d.%d.png", - pdf_name, page_num, i); - if (chars_formatted < 0 || - static_cast<size_t>(chars_formatted) >= sizeof(filename)) { - fprintf(stderr, "Filename %s for saving image is too long\n", filename); - continue; - } - - FILE* fp = fopen(filename, "wb"); - if (!fp) { - fprintf(stderr, "Failed to open %s for saving image.\n", filename); - continue; - } - - size_t bytes_written = - fwrite(&png_encoding.front(), 1, png_encoding.size(), fp); - if (bytes_written != png_encoding.size()) - fprintf(stderr, "Failed to write to %s.\n", filename); - else - fprintf(stderr, "Successfully wrote embedded image %s.\n", filename); - - (void)fclose(fp); - } -} - // Note, for a client using progressive rendering you'd want to determine if you // need the rendering to pause instead of always saying |true|. This is for // testing to force the renderer to break whenever possible. @@ -1240,7 +466,7 @@ bool RenderPage(const std::string& name, if (options.send_events) SendPageEvents(form, page, events); if (options.save_images) - SaveImages(page, name.c_str(), page_index); + WriteImages(page, name.c_str(), page_index); if (options.output_format == OUTPUT_STRUCTURE) { DumpPageStructure(page, page_index); return true; @@ -1412,7 +638,7 @@ void RenderPdf(const std::string& name, DumpMetaData(doc.get()); if (options.save_attachments) - SaveAttachments(doc.get(), name); + WriteAttachments(doc.get(), name); IPDF_JSPLATFORM platform_callbacks = {}; platform_callbacks.version = 3; |