// Copyright 2018 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "samples/pdfium_test_dump_helper.h" #include #include #include #include #include #include #include #include "public/cpp/fpdf_scopers.h" #include "public/fpdf_doc.h" #include "public/fpdf_transformpage.h" #include "testing/fx_string_testhelpers.h" using GetBoxInfoFunc = std::function; namespace { 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 DumpBoxInfo(GetBoxInfoFunc func, const char* box_type, FPDF_PAGE page, int page_idx) { FS_RECTF rect; bool ret = func(page, &rect.left, &rect.bottom, &rect.right, &rect.top); if (!ret) { printf("Page %d: No %s.\n", page_idx, box_type); return; } printf("Page %d: %s: %0.2f %0.2f %0.2f %0.2f\n", page_idx, box_type, rect.left, rect.bottom, rect.right, rect.top); } void DumpStructureElementAttributes(FPDF_STRUCTELEMENT_ATTR attr, int indent) { static const size_t kBufSize = 1024; int count = FPDF_StructElement_Attr_GetCount(attr); for (int i = 0; i < count; i++) { char name[kBufSize] = {}; unsigned long len = ULONG_MAX; if (!FPDF_StructElement_Attr_GetName(attr, i, name, sizeof(name), &len)) { printf("%*s FPDF_StructElement_Attr_GetName failed for %d\n", indent, "", i); continue; } FPDF_OBJECT_TYPE type = FPDF_StructElement_Attr_GetType(attr, name); if (type == FPDF_OBJECT_BOOLEAN) { int value; if (!FPDF_StructElement_Attr_GetBooleanValue(attr, name, &value)) { printf("%*s %s: Failed FPDF_StructElement_Attr_GetBooleanValue\n", indent, "", name); continue; } printf("%*s %s: %d\n", indent, "", name, value); } else if (type == FPDF_OBJECT_NUMBER) { float value; if (!FPDF_StructElement_Attr_GetNumberValue(attr, name, &value)) { printf("%*s %s: Failed FPDF_StructElement_Attr_GetNumberValue\n", indent, "", name); continue; } printf("%*s %s: %f\n", indent, "", name, value); } else if (type == FPDF_OBJECT_STRING || type == FPDF_OBJECT_NAME) { unsigned short buffer[kBufSize] = {}; if (!FPDF_StructElement_Attr_GetStringValue(attr, name, buffer, sizeof(buffer), &len)) { printf("%*s %s: Failed FPDF_StructElement_Attr_GetStringValue\n", indent, "", name); continue; } printf("%*s %s: %ls\n", indent, "", name, ConvertToWString(buffer, len).c_str()); } else if (type == FPDF_OBJECT_UNKNOWN) { printf("%*s %s: FPDF_OBJECT_UNKNOWN\n", indent, "", name); } else { printf("%*s %s: NOT_YET_IMPLEMENTED: %d\n", indent, "", name, type); } } } } // namespace 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); if (len > 0) printf("%*s S: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); int attr_count = FPDF_StructElement_GetAttributeCount(child); for (int i = 0; i < attr_count; i++) { FPDF_STRUCTELEMENT_ATTR child_attr = FPDF_StructElement_GetAttributeAtIndex(child, i); if (!child_attr) continue; printf("%*s A[%d]:\n", indent * 2, "", i); DumpStructureElementAttributes(child_attr, indent * 2 + 2); } memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetActualText(child, buf, kBufSize); if (len > 0) { printf("%*s ActualText: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); } memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetAltText(child, buf, kBufSize); if (len > 0) { printf("%*s AltText: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); } memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetID(child, buf, kBufSize); if (len > 0) printf("%*s ID: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetLang(child, buf, kBufSize); if (len > 0) { printf("%*s Lang: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); } int mcid = FPDF_StructElement_GetMarkedContentID(child); if (mcid != -1) printf("%*s MCID: %d\n", indent * 2, "", mcid); FPDF_STRUCTELEMENT parent = FPDF_StructElement_GetParent(child); if (parent) { memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetID(parent, buf, kBufSize); if (len > 0) { printf("%*s Parent ID: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); } } memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetTitle(child, buf, kBufSize); if (len > 0) { printf("%*s Title: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); } memset(buf, 0, sizeof(buf)); len = FPDF_StructElement_GetObjType(child, buf, kBufSize); if (len > 0) { printf("%*s Type: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); } 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 DumpPageInfo(FPDF_PAGE page, int page_idx) { DumpBoxInfo(&FPDFPage_GetMediaBox, "MediaBox", page, page_idx); DumpBoxInfo(&FPDFPage_GetCropBox, "CropBox", page, page_idx); DumpBoxInfo(&FPDFPage_GetBleedBox, "BleedBox", page, page_idx); DumpBoxInfo(&FPDFPage_GetTrimBox, "TrimBox", page, page_idx); DumpBoxInfo(&FPDFPage_GetArtBox, "ArtBox", page, page_idx); } void DumpPageStructure(FPDF_PAGE page, int page_idx) { ScopedFPDFStructTree 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) { static constexpr const char* kMetaTags[] = { "Title", "Author", "Subject", "Keywords", "Creator", "Producer", "CreationDate", "ModDate"}; for (const char* meta_tag : kMetaTags) { 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(meta_buffer); printf("%-12s = %ls (%lu bytes)\n", meta_tag, GetPlatformWString(meta_string).c_str(), len); } }