Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
write.cc
Go to the documentation of this file.
1// Copyright 2018 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#include "samples/helpers/write.h"
6
7#include <limits.h>
8
9#include <sstream>
10#include <string>
11#include <utility>
12#include <vector>
13
14#include "public/cpp/fpdf_scopers.h"
15#include "public/fpdf_annot.h"
16#include "public/fpdf_attachment.h"
17#include "public/fpdf_edit.h"
18#include "public/fpdf_thumbnail.h"
19#include "testing/fx_string_testhelpers.h"
20#include "testing/image_diff/image_diff_png.h"
21#include "third_party/base/notreached.h"
22
23#ifdef PDF_ENABLE_SKIA
24#include "third_party/skia/include/core/SkPicture.h" // nogncheck
25#include "third_party/skia/include/core/SkSerialProcs.h" // nogncheck
26#include "third_party/skia/include/core/SkStream.h" // nogncheck
27#include "third_party/skia/include/encode/SkPngEncoder.h" // nogncheck
28#endif
29
30namespace {
31
32bool CheckDimensions(int stride, int width, int height) {
33 if (stride < 0 || width < 0 || height < 0) {
34 return false;
35 }
36 if (height > 0 && stride > INT_MAX / height) {
37 return false;
38 }
39 return true;
40}
41
42const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
43 if (subtype == FPDF_ANNOT_TEXT) {
44 return "Text";
45 }
46 if (subtype == FPDF_ANNOT_LINK) {
47 return "Link";
48 }
49 if (subtype == FPDF_ANNOT_FREETEXT) {
50 return "FreeText";
51 }
52 if (subtype == FPDF_ANNOT_LINE) {
53 return "Line";
54 }
55 if (subtype == FPDF_ANNOT_SQUARE) {
56 return "Square";
57 }
58 if (subtype == FPDF_ANNOT_CIRCLE) {
59 return "Circle";
60 }
61 if (subtype == FPDF_ANNOT_POLYGON) {
62 return "Polygon";
63 }
64 if (subtype == FPDF_ANNOT_POLYLINE) {
65 return "PolyLine";
66 }
67 if (subtype == FPDF_ANNOT_HIGHLIGHT) {
68 return "Highlight";
69 }
70 if (subtype == FPDF_ANNOT_UNDERLINE) {
71 return "Underline";
72 }
73 if (subtype == FPDF_ANNOT_SQUIGGLY) {
74 return "Squiggly";
75 }
76 if (subtype == FPDF_ANNOT_STRIKEOUT) {
77 return "StrikeOut";
78 }
79 if (subtype == FPDF_ANNOT_STAMP) {
80 return "Stamp";
81 }
82 if (subtype == FPDF_ANNOT_CARET) {
83 return "Caret";
84 }
85 if (subtype == FPDF_ANNOT_INK) {
86 return "Ink";
87 }
88 if (subtype == FPDF_ANNOT_POPUP) {
89 return "Popup";
90 }
91 if (subtype == FPDF_ANNOT_FILEATTACHMENT) {
92 return "FileAttachment";
93 }
94 if (subtype == FPDF_ANNOT_SOUND) {
95 return "Sound";
96 }
97 if (subtype == FPDF_ANNOT_MOVIE) {
98 return "Movie";
99 }
100 if (subtype == FPDF_ANNOT_WIDGET) {
101 return "Widget";
102 }
103 if (subtype == FPDF_ANNOT_SCREEN) {
104 return "Screen";
105 }
106 if (subtype == FPDF_ANNOT_PRINTERMARK) {
107 return "PrinterMark";
108 }
109 if (subtype == FPDF_ANNOT_TRAPNET) {
110 return "TrapNet";
111 }
112 if (subtype == FPDF_ANNOT_WATERMARK) {
113 return "Watermark";
114 }
115 if (subtype == FPDF_ANNOT_THREED) {
116 return "3D";
117 }
118 if (subtype == FPDF_ANNOT_RICHMEDIA) {
119 return "RichMedia";
120 }
121 if (subtype == FPDF_ANNOT_XFAWIDGET) {
122 return "XFAWidget";
123 }
124 NOTREACHED_NORETURN();
125}
126
127void AppendFlagString(const char* flag, std::string* output) {
128 if (!output->empty()) {
129 *output += ", ";
130 }
131 *output += flag;
132}
133
134std::string AnnotFlagsToString(int flags) {
135 std::string str;
136 if (flags & FPDF_ANNOT_FLAG_INVISIBLE) {
137 AppendFlagString("Invisible", &str);
138 }
139 if (flags & FPDF_ANNOT_FLAG_HIDDEN) {
140 AppendFlagString("Hidden", &str);
141 }
142 if (flags & FPDF_ANNOT_FLAG_PRINT) {
143 AppendFlagString("Print", &str);
144 }
145 if (flags & FPDF_ANNOT_FLAG_NOZOOM) {
146 AppendFlagString("NoZoom", &str);
147 }
148 if (flags & FPDF_ANNOT_FLAG_NOROTATE) {
149 AppendFlagString("NoRotate", &str);
150 }
151 if (flags & FPDF_ANNOT_FLAG_NOVIEW) {
152 AppendFlagString("NoView", &str);
153 }
154 if (flags & FPDF_ANNOT_FLAG_READONLY) {
155 AppendFlagString("ReadOnly", &str);
156 }
157 if (flags & FPDF_ANNOT_FLAG_LOCKED) {
158 AppendFlagString("Locked", &str);
159 }
160 if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW) {
161 AppendFlagString("ToggleNoView", &str);
162 }
163 return str;
164}
165
166const char* PageObjectTypeToCString(int type) {
167 if (type == FPDF_PAGEOBJ_TEXT) {
168 return "Text";
169 }
170 if (type == FPDF_PAGEOBJ_PATH) {
171 return "Path";
172 }
173 if (type == FPDF_PAGEOBJ_IMAGE) {
174 return "Image";
175 }
176 if (type == FPDF_PAGEOBJ_SHADING) {
177 return "Shading";
178 }
179 if (type == FPDF_PAGEOBJ_FORM) {
180 return "Form";
181 }
182 NOTREACHED_NORETURN();
183}
184
185std::vector<uint8_t> EncodePng(pdfium::span<const uint8_t> input,
186 int width,
187 int height,
188 int stride,
189 int format) {
190 std::vector<uint8_t> png;
191 switch (format) {
193 break;
194 case FPDFBitmap_Gray:
195 png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
196 break;
197 case FPDFBitmap_BGR:
198 png = image_diff_png::EncodeBGRPNG(input, width, height, stride);
199 break;
200 case FPDFBitmap_BGRx:
201 png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
202 /*discard_transparency=*/true);
203 break;
204 case FPDFBitmap_BGRA:
205 png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
206 /*discard_transparency=*/false);
207 break;
208 default:
209 NOTREACHED_NORETURN();
210 }
211 return png;
212}
213
214#ifdef _WIN32
215int CALLBACK EnhMetaFileProc(HDC hdc,
216 HANDLETABLE* handle_table,
217 const ENHMETARECORD* record,
218 int objects_count,
219 LPARAM param) {
220 std::vector<const ENHMETARECORD*>& items =
221 *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
222 items.push_back(record);
223 return 1;
224}
225#endif // _WIN32
226
227std::string GeneratePageOutputFilename(const char* pdf_name,
228 int page_num,
229 const char* extension) {
230 std::ostringstream stream;
231 stream << pdf_name << "." << page_num << "." << extension;
232 std::string filename = stream.str();
233 if (filename.size() >= 256) {
234 fprintf(stderr, "Filename %s is too long\n", filename.c_str());
235 return std::string();
236 }
237
238 return filename;
239}
240
241std::string GenerateImageOutputFilename(const char* pdf_name,
242 int page_num,
243 int image_num,
244 const char* extension) {
245 std::ostringstream stream;
246 stream << pdf_name << "." << page_num << "." << image_num << "." << extension;
247 std::string filename = stream.str();
248 if (filename.size() >= 256) {
249 fprintf(stderr, "Filename %s for saving image is too long.\n",
250 filename.c_str());
251 return std::string();
252 }
253
254 return filename;
255}
256
257} // namespace
258
259std::string WritePpm(const char* pdf_name,
260 int num,
261 void* buffer_void,
262 int stride,
263 int width,
264 int height) {
265 if (!CheckDimensions(stride, width, height)) {
266 return "";
267 }
268
269 int out_len = width * height;
270 if (out_len > INT_MAX / 3) {
271 return "";
272 }
273
274 out_len *= 3;
275
276 std::string filename = GeneratePageOutputFilename(pdf_name, num, "ppm");
277 if (filename.empty()) {
278 return std::string();
279 }
280 FILE* fp = fopen(filename.c_str(), "wb");
281 if (!fp) {
282 return std::string();
283 }
284
285 fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
286 // Source data is B, G, R, unused.
287 // Dest data is R, G, B.
288 const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buffer_void);
289 std::vector<uint8_t> result(out_len);
290 for (int h = 0; h < height; ++h) {
291 const uint8_t* src_line = buffer + (stride * h);
292 uint8_t* dest_line = result.data() + (width * h * 3);
293 for (int w = 0; w < width; ++w) {
294 // R
295 dest_line[w * 3] = src_line[(w * 4) + 2];
296 // G
297 dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
298 // B
299 dest_line[(w * 3) + 2] = src_line[w * 4];
300 }
301 }
302 if (fwrite(result.data(), out_len, 1, fp) != 1) {
303 fprintf(stderr, "Failed to write to %s\n", filename.c_str());
304 }
305
306 fclose(fp);
307 return filename;
308}
309
310void WriteText(FPDF_TEXTPAGE textpage, const char* pdf_name, int num) {
311 std::string filename = GeneratePageOutputFilename(pdf_name, num, "txt");
312 if (filename.empty()) {
313 return;
314 }
315 FILE* fp = fopen(filename.c_str(), "w");
316 if (!fp) {
317 fprintf(stderr, "Failed to open %s for output\n", filename.c_str());
318 return;
319 }
320
321 // Output in UTF32-LE.
322 uint32_t bom = 0x0000FEFF;
323 if (fwrite(&bom, sizeof(bom), 1, fp) != 1) {
324 fprintf(stderr, "Failed to write to %s\n", filename.c_str());
325 (void)fclose(fp);
326 return;
327 }
328
329 for (int i = 0; i < FPDFText_CountChars(textpage); i++) {
330 uint32_t c = FPDFText_GetUnicode(textpage, i);
331 if (fwrite(&c, sizeof(c), 1, fp) != 1) {
332 fprintf(stderr, "Failed to write to %s\n", filename.c_str());
333 break;
334 }
335 }
336 (void)fclose(fp);
337}
338
339void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
340 // Open the output text file.
341 std::string filename = GeneratePageOutputFilename(pdf_name, num, "annot.txt");
342 if (filename.empty()) {
343 return;
344 }
345 FILE* fp = fopen(filename.c_str(), "w");
346 if (!fp) {
347 fprintf(stderr, "Failed to open %s for output\n", filename.c_str());
348 return;
349 }
350
351 int annot_count = FPDFPage_GetAnnotCount(page);
352 fprintf(fp, "Number of annotations: %d\n\n", annot_count);
353
354 // Iterate through all annotations on this page.
355 for (int i = 0; i < annot_count; ++i) {
356 // Retrieve the annotation object and its subtype.
357 fprintf(fp, "Annotation #%d:\n", i + 1);
358 ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
359 if (!annot) {
360 fprintf(fp, "Failed to retrieve annotation!\n\n");
361 continue;
362 }
363
364 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot.get());
365 fprintf(fp, "Subtype: %s\n", AnnotSubtypeToCString(subtype));
366
367 // Retrieve the annotation flags.
368 fprintf(fp, "Flags set: %s\n",
369 AnnotFlagsToString(FPDFAnnot_GetFlags(annot.get())).c_str());
370
371 // Retrieve the annotation's object count and object types.
372 const int obj_count = FPDFAnnot_GetObjectCount(annot.get());
373 fprintf(fp, "Number of objects: %d\n", obj_count);
374 if (obj_count > 0) {
375 fprintf(fp, "Object types: ");
376 for (int j = 0; j < obj_count; ++j) {
377 const char* type = PageObjectTypeToCString(
378 FPDFPageObj_GetType(FPDFAnnot_GetObject(annot.get(), j)));
379 fprintf(fp, "%s ", type);
380 }
381 fprintf(fp, "\n");
382 }
383
384 // Retrieve the annotation's color and interior color.
385 unsigned int R;
386 unsigned int G;
387 unsigned int B;
388 unsigned int A;
389 if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R, &G, &B,
390 &A)) {
391 fprintf(fp, "Color in RGBA: %d %d %d %d\n", R, G, B, A);
392 } else {
393 fprintf(fp, "Failed to retrieve color.\n");
394 }
395 if (FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_InteriorColor, &R,
396 &G, &B, &A)) {
397 fprintf(fp, "Interior color in RGBA: %d %d %d %d\n", R, G, B, A);
398 } else {
399 fprintf(fp, "Failed to retrieve interior color.\n");
400 }
401
402 // Retrieve the annotation's contents and author.
403 static constexpr char kContentsKey[] = "Contents";
404 static constexpr char kAuthorKey[] = "T";
405 unsigned long length_bytes =
406 FPDFAnnot_GetStringValue(annot.get(), kContentsKey, nullptr, 0);
407 std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
408 FPDFAnnot_GetStringValue(annot.get(), kContentsKey, buf.data(),
409 length_bytes);
410 fprintf(fp, "Content: %ls\n", GetPlatformWString(buf.data()).c_str());
411 length_bytes =
412 FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0);
413 buf = GetFPDFWideStringBuffer(length_bytes);
414 FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(), length_bytes);
415 fprintf(fp, "Author: %ls\n", GetPlatformWString(buf.data()).c_str());
416
417 // Retrieve the annotation's quadpoints if it is a markup annotation.
418 if (FPDFAnnot_HasAttachmentPoints(annot.get())) {
419 size_t qp_count = FPDFAnnot_CountAttachmentPoints(annot.get());
420 fprintf(fp, "Number of quadpoints sets: %zu\n", qp_count);
421
422 // Iterate through all quadpoints of the current annotation
423 for (size_t j = 0; j < qp_count; ++j) {
424 FS_QUADPOINTSF quadpoints;
425 if (FPDFAnnot_GetAttachmentPoints(annot.get(), j, &quadpoints)) {
426 fprintf(fp,
427 "Quadpoints set #%zu: (%.3f, %.3f), (%.3f, %.3f), "
428 "(%.3f, %.3f), (%.3f, %.3f)\n",
429 j + 1, quadpoints.x1, quadpoints.y1, quadpoints.x2,
430 quadpoints.y2, quadpoints.x3, quadpoints.y3, quadpoints.x4,
431 quadpoints.y4);
432 } else {
433 fprintf(fp, "Failed to retrieve quadpoints set #%zu.\n", j + 1);
434 }
435 }
436 }
437
438 // Retrieve the annotation's rectangle coordinates.
439 FS_RECTF rect;
440 if (FPDFAnnot_GetRect(annot.get(), &rect)) {
441 fprintf(fp, "Rectangle: l - %.3f, b - %.3f, r - %.3f, t - %.3f\n\n",
442 rect.left, rect.bottom, rect.right, rect.top);
443 } else {
444 fprintf(fp, "Failed to retrieve annotation rectangle.\n");
445 }
446 }
447
448 (void)fclose(fp);
449}
450
451std::string WritePng(const char* pdf_name,
452 int num,
453 void* buffer,
454 int stride,
455 int width,
456 int height) {
457 if (!CheckDimensions(stride, width, height)) {
458 return "";
459 }
460
461 auto input = pdfium::make_span(static_cast<uint8_t*>(buffer),
462 static_cast<size_t>(stride) * height);
463 std::vector<uint8_t> png_encoding =
464 EncodePng(input, width, height, stride, FPDFBitmap_BGRA);
465 if (png_encoding.empty()) {
466 fprintf(stderr, "Failed to convert bitmap to PNG\n");
467 return "";
468 }
469
470 std::string filename = GeneratePageOutputFilename(pdf_name, num, "png");
471 if (filename.empty()) {
472 return std::string();
473 }
474 FILE* fp = fopen(filename.c_str(), "wb");
475 if (!fp) {
476 fprintf(stderr, "Failed to open %s for output\n", filename.c_str());
477 return std::string();
478 }
479
480 size_t bytes_written =
481 fwrite(&png_encoding.front(), 1, png_encoding.size(), fp);
482 if (bytes_written != png_encoding.size()) {
483 fprintf(stderr, "Failed to write to %s\n", filename.c_str());
484 }
485
486 (void)fclose(fp);
487 return filename;
488}
489
490#ifdef _WIN32
491std::string WriteBmp(const char* pdf_name,
492 int num,
493 void* buffer,
494 int stride,
495 int width,
496 int height) {
497 if (!CheckDimensions(stride, width, height)) {
498 return std::string();
499 }
500
501 int out_len = stride * height;
502 if (out_len > INT_MAX / 3) {
503 return std::string();
504 }
505
506 std::string filename = GeneratePageOutputFilename(pdf_name, num, "bmp");
507 if (filename.empty()) {
508 return std::string();
509 }
510 FILE* fp = fopen(filename.c_str(), "wb");
511 if (!fp) {
512 return std::string();
513 }
514
515 BITMAPINFO bmi = {};
516 bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
517 bmi.bmiHeader.biWidth = width;
518 bmi.bmiHeader.biHeight = -height; // top-down image
519 bmi.bmiHeader.biPlanes = 1;
520 bmi.bmiHeader.biBitCount = 32;
521 bmi.bmiHeader.biCompression = BI_RGB;
522 bmi.bmiHeader.biSizeImage = 0;
523
524 BITMAPFILEHEADER file_header = {};
525 file_header.bfType = 0x4d42;
526 file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
527 file_header.bfOffBits = file_header.bfSize - out_len;
528
529 if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 ||
530 fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 ||
531 fwrite(buffer, out_len, 1, fp) != 1) {
532 fprintf(stderr, "Failed to write to %s\n", filename.c_str());
533 }
534 fclose(fp);
535 return filename;
536}
537
538void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
539 std::string filename = GeneratePageOutputFilename(pdf_name, num, "emf");
540 if (filename.empty()) {
541 return;
542 }
543
544 HDC dc = CreateEnhMetaFileA(nullptr, filename.c_str(), nullptr, nullptr);
545
546 int width = static_cast<int>(FPDF_GetPageWidthF(page));
547 int height = static_cast<int>(FPDF_GetPageHeightF(page));
548 HRGN rgn = CreateRectRgn(0, 0, width, height);
549 SelectClipRgn(dc, rgn);
550 DeleteObject(rgn);
551
552 SelectObject(dc, GetStockObject(NULL_PEN));
553 SelectObject(dc, GetStockObject(WHITE_BRUSH));
554 // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
555 Rectangle(dc, 0, 0, width + 1, height + 1);
556
557 FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
558
559 DeleteEnhMetaFile(CloseEnhMetaFile(dc));
560}
561
562void WritePS(FPDF_PAGE page, const char* pdf_name, int num) {
563 std::string filename = GeneratePageOutputFilename(pdf_name, num, "ps");
564 if (filename.empty()) {
565 return;
566 }
567 FILE* fp = fopen(filename.c_str(), "wb");
568 if (!fp) {
569 return;
570 }
571
572 HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
573
574 int width = static_cast<int>(FPDF_GetPageWidthF(page));
575 int height = static_cast<int>(FPDF_GetPageHeightF(page));
576 FPDF_RenderPage(dc, page, 0, 0, width, height, 0, FPDF_ANNOT | FPDF_PRINTING);
577
578 HENHMETAFILE emf = CloseEnhMetaFile(dc);
579 std::vector<const ENHMETARECORD*> items;
580 EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
581 for (const ENHMETARECORD* record : items) {
582 if (record->iType != EMR_GDICOMMENT) {
583 continue;
584 }
585
586 const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
587 const char* data = reinterpret_cast<const char*>(comment->Data);
588 uint16_t size = *reinterpret_cast<const uint16_t*>(data);
589 if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) {
590 fprintf(stderr, "Failed to write to %s\n", filename.c_str());
591 break;
592 }
593 }
594 fclose(fp);
595 DeleteEnhMetaFile(emf);
596}
597#endif // _WIN32
598
599#ifdef PDF_ENABLE_SKIA
600std::unique_ptr<SkWStream> WriteToSkWStream(const std::string& pdf_name,
601 int num,
602 const std::string& extension) {
603 std::string discarded_filename;
604 return WriteToSkWStream(pdf_name, num, extension, discarded_filename);
605}
606
607std::unique_ptr<SkWStream> WriteToSkWStream(const std::string& pdf_name,
608 int num,
609 const std::string& extension,
610 std::string& filename) {
611 filename =
612 GeneratePageOutputFilename(pdf_name.c_str(), num, extension.c_str());
613 if (filename.empty()) {
614 return nullptr;
615 }
616
617 auto stream = std::make_unique<SkFILEWStream>(filename.c_str());
618 if (!stream->isValid()) {
619 return nullptr;
620 }
621
622 return stream;
623}
624
625std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture) {
626 std::string filename;
627 std::unique_ptr<SkWStream> stream =
628 WriteToSkWStream(pdf_name, num, "skp", filename);
629 if (!stream) {
630 return "";
631 }
632 SkSerialProcs procs;
633 procs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
634 return SkPngEncoder::Encode(nullptr, img, SkPngEncoder::Options{});
635 };
636
637 picture.serialize(stream.get(), &procs);
638 return filename;
639}
640#endif // PDF_ENABLE_SKIA
641
643
644bool GetThumbnailFilename(char* name_buf,
645 size_t name_buf_size,
646 const char* pdf_name,
647 int page_num,
648 ThumbnailDecodeType decode_type) {
649 const char* format;
650 switch (decode_type) {
652 format = "%s.thumbnail.%d.png";
653 break;
655 format = "%s.thumbnail.decoded.%d.bin";
656 break;
658 format = "%s.thumbnail.raw.%d.bin";
659 break;
660 }
661
662 int chars_formatted =
663 snprintf(name_buf, name_buf_size, format, pdf_name, page_num);
664 if (chars_formatted < 0 ||
665 static_cast<size_t>(chars_formatted) >= name_buf_size) {
666 fprintf(stderr, "Filename %s for saving is too long.\n", name_buf);
667 return false;
668 }
669
670 return true;
671}
672
673void WriteBufferToFile(const void* buf,
674 size_t buflen,
675 const char* filename,
676 const char* filetype) {
677 FILE* fp = fopen(filename, "wb");
678 if (!fp) {
679 fprintf(stderr, "Failed to open %s for saving %s.", filename, filetype);
680 return;
681 }
682
683 size_t bytes_written = fwrite(buf, 1, buflen, fp);
684 if (bytes_written == buflen) {
685 fprintf(stderr, "Successfully wrote %s %s.\n", filetype, filename);
686 } else {
687 fprintf(stderr, "Failed to write to %s.\n", filename);
688 }
689 fclose(fp);
690}
691
692std::vector<uint8_t> EncodeBitmapToPng(ScopedFPDFBitmap bitmap) {
693 std::vector<uint8_t> png_encoding;
694 int format = FPDFBitmap_GetFormat(bitmap.get());
695 if (format == FPDFBitmap_Unknown) {
696 return png_encoding;
697 }
698
699 int width = FPDFBitmap_GetWidth(bitmap.get());
700 int height = FPDFBitmap_GetHeight(bitmap.get());
701 int stride = FPDFBitmap_GetStride(bitmap.get());
702 if (!CheckDimensions(stride, width, height)) {
703 return png_encoding;
704 }
705
706 auto input = pdfium::make_span(
707 static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap.get())),
708 static_cast<size_t>(stride) * height);
709
710 png_encoding = EncodePng(input, width, height, stride, format);
711 return png_encoding;
712}
713
714void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name) {
715 for (int i = 0; i < FPDFDoc_GetAttachmentCount(doc); ++i) {
716 FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc, i);
717
718 // Retrieve the attachment file name.
719 std::string attachment_name;
720 unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0);
721 if (length_bytes) {
722 std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
723 unsigned long actual_length_bytes =
724 FPDFAttachment_GetName(attachment, buf.data(), length_bytes);
725 if (actual_length_bytes == length_bytes) {
726 attachment_name = GetPlatformString(buf.data());
727 }
728 }
729 if (attachment_name.empty()) {
730 fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
731 continue;
732 }
733
734 // Calculate the full attachment file name.
735 char save_name[256];
736 int chars_formatted =
737 snprintf(save_name, sizeof(save_name), "%s.attachment.%s", name.c_str(),
738 attachment_name.c_str());
739 if (chars_formatted < 0 ||
740 static_cast<size_t>(chars_formatted) >= sizeof(save_name)) {
741 fprintf(stderr, "Filename %s is too long.\n", save_name);
742 continue;
743 }
744
745 // Retrieve the attachment.
746 if (!FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)) {
747 fprintf(stderr, "Failed to retrieve attachment \"%s\".\n",
748 attachment_name.c_str());
749 continue;
750 }
751
752 std::vector<char> data_buf(length_bytes);
753 if (length_bytes) {
754 unsigned long actual_length_bytes;
755 if (!FPDFAttachment_GetFile(attachment, data_buf.data(), length_bytes,
756 &actual_length_bytes)) {
757 fprintf(stderr, "Failed to retrieve attachment \"%s\".\n",
758 attachment_name.c_str());
759 continue;
760 }
761 }
762
763 // Write the attachment file. Since a PDF document could have 0-byte files
764 // as attachments, we should allow saving the 0-byte attachments to files.
765 WriteBufferToFile(data_buf.data(), length_bytes, save_name, "attachment");
766 }
767}
768
769void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) {
770 for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
771 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
773 continue;
774 }
775
776 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
777 if (!bitmap) {
778 fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
779 i + 1, page_num + 1);
780 continue;
781 }
782
783 std::string filename =
784 GenerateImageOutputFilename(pdf_name, page_num, i, "png");
785 if (filename.empty()) {
786 continue;
787 }
788
789 std::vector<uint8_t> png_encoding = EncodeBitmapToPng(std::move(bitmap));
790 if (png_encoding.empty()) {
791 fprintf(stderr,
792 "Failed to convert image object #%d, on page #%d to png.\n",
793 i + 1, page_num + 1);
794 continue;
795 }
796
797 WriteBufferToFile(&png_encoding.front(), png_encoding.size(),
798 filename.c_str(), "image");
799 }
800}
801
802void WriteRenderedImages(FPDF_DOCUMENT doc,
803 FPDF_PAGE page,
804 const char* pdf_name,
805 int page_num) {
806 for (int i = 0; i < FPDFPage_CountObjects(page); ++i) {
807 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i);
809 continue;
810 }
811
812 ScopedFPDFBitmap bitmap(FPDFImageObj_GetRenderedBitmap(doc, page, obj));
813 if (!bitmap) {
814 fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n",
815 i + 1, page_num + 1);
816 continue;
817 }
818
819 std::string filename =
820 GenerateImageOutputFilename(pdf_name, page_num, i, "png");
821 if (filename.empty()) {
822 continue;
823 }
824
825 std::vector<uint8_t> png_encoding = EncodeBitmapToPng(std::move(bitmap));
826 if (png_encoding.empty()) {
827 fprintf(stderr,
828 "Failed to convert image object #%d, on page #%d to png.\n",
829 i + 1, page_num + 1);
830 continue;
831 }
832
833 WriteBufferToFile(&png_encoding.front(), png_encoding.size(),
834 filename.c_str(), "image");
835 }
836}
837
838void WriteDecodedThumbnailStream(FPDF_PAGE page,
839 const char* pdf_name,
840 int page_num) {
841 char filename[256];
842 if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
843 ThumbnailDecodeType::kDecodedStream)) {
844 return;
845 }
846
847 unsigned long decoded_data_size =
849
850 // Only continue if there actually is a thumbnail for this page
851 if (decoded_data_size == 0) {
852 fprintf(stderr, "Failed to get decoded thumbnail for page #%d.\n",
853 page_num + 1);
854 return;
855 }
856
857 std::vector<uint8_t> thumb_buf(decoded_data_size);
858 if (FPDFPage_GetDecodedThumbnailData(
859 page, thumb_buf.data(), decoded_data_size) != decoded_data_size) {
860 fprintf(stderr, "Failed to get decoded thumbnail data for %s.\n", filename);
861 return;
862 }
863
864 WriteBufferToFile(thumb_buf.data(), decoded_data_size, filename,
865 "decoded thumbnail");
866}
867
868void WriteRawThumbnailStream(FPDF_PAGE page,
869 const char* pdf_name,
870 int page_num) {
871 char filename[256];
872 if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
873 ThumbnailDecodeType::kRawStream)) {
874 return;
875 }
876
877 unsigned long raw_data_size = FPDFPage_GetRawThumbnailData(page, nullptr, 0u);
878
879 // Only continue if there actually is a thumbnail for this page
880 if (raw_data_size == 0) {
881 fprintf(stderr, "Failed to get raw thumbnail data for page #%d.\n",
882 page_num + 1);
883 return;
884 }
885
886 std::vector<uint8_t> thumb_buf(raw_data_size);
887 if (FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), raw_data_size) !=
888 raw_data_size) {
889 fprintf(stderr, "Failed to get raw thumbnail data for %s.\n", filename);
890 return;
891 }
892
893 WriteBufferToFile(thumb_buf.data(), raw_data_size, filename, "raw thumbnail");
894}
895
896void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num) {
897 char filename[256];
898 if (!GetThumbnailFilename(filename, sizeof(filename), pdf_name, page_num,
899 ThumbnailDecodeType::kBitmap)) {
900 return;
901 }
902
903 ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page));
904 if (!thumb_bitmap) {
905 fprintf(stderr, "Thumbnail of page #%d has an empty bitmap.\n",
906 page_num + 1);
907 return;
908 }
909
910 std::vector<uint8_t> png_encoding =
911 EncodeBitmapToPng(std::move(thumb_bitmap));
912 if (png_encoding.empty()) {
913 fprintf(stderr, "Failed to convert thumbnail of page #%d to png.\n",
914 page_num + 1);
915 return;
916 }
917
918 WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename,
919 "thumbnail");
920}
#define FPDF_ANNOT_FILEATTACHMENT
Definition fpdf_annot.h:37
#define FPDF_ANNOT_RICHMEDIA
Definition fpdf_annot.h:46
#define FPDF_ANNOT_STAMP
Definition fpdf_annot.h:33
#define FPDF_ANNOT_FLAG_INVISIBLE
Definition fpdf_annot.h:52
#define FPDF_ANNOT_MOVIE
Definition fpdf_annot.h:39
#define FPDF_ANNOT_FLAG_NOROTATE
Definition fpdf_annot.h:56
#define FPDF_ANNOT_FLAG_NOZOOM
Definition fpdf_annot.h:55
#define FPDF_ANNOT_FLAG_HIDDEN
Definition fpdf_annot.h:53
#define FPDF_ANNOT_FLAG_PRINT
Definition fpdf_annot.h:54
#define FPDF_ANNOT_THREED
Definition fpdf_annot.h:45
#define FPDF_ANNOT_STRIKEOUT
Definition fpdf_annot.h:32
#define FPDF_ANNOT_UNDERLINE
Definition fpdf_annot.h:30
#define FPDF_ANNOT_LINE
Definition fpdf_annot.h:24
#define FPDF_ANNOT_SCREEN
Definition fpdf_annot.h:41
#define FPDF_ANNOT_FLAG_READONLY
Definition fpdf_annot.h:58
FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page)
#define FPDF_ANNOT_WATERMARK
Definition fpdf_annot.h:44
#define FPDF_ANNOT_XFAWIDGET
Definition fpdf_annot.h:47
#define FPDF_ANNOT_WIDGET
Definition fpdf_annot.h:40
#define FPDF_ANNOT_HIGHLIGHT
Definition fpdf_annot.h:29
#define FPDF_ANNOT_TRAPNET
Definition fpdf_annot.h:43
#define FPDF_ANNOT_SQUIGGLY
Definition fpdf_annot.h:31
#define FPDF_ANNOT_SOUND
Definition fpdf_annot.h:38
#define FPDF_ANNOT_INK
Definition fpdf_annot.h:35
#define FPDF_ANNOT_FLAG_LOCKED
Definition fpdf_annot.h:59
#define FPDF_ANNOT_SQUARE
Definition fpdf_annot.h:25
#define FPDF_ANNOT_FREETEXT
Definition fpdf_annot.h:23
#define FPDF_ANNOT_FLAG_NOVIEW
Definition fpdf_annot.h:57
#define FPDF_ANNOT_POLYGON
Definition fpdf_annot.h:27
#define FPDF_ANNOT_CARET
Definition fpdf_annot.h:34
#define FPDF_ANNOT_POPUP
Definition fpdf_annot.h:36
@ FPDFANNOT_COLORTYPE_InteriorColor
Definition fpdf_annot.h:97
@ FPDFANNOT_COLORTYPE_Color
Definition fpdf_annot.h:96
#define FPDF_ANNOT_FLAG_TOGGLENOVIEW
Definition fpdf_annot.h:60
#define FPDF_ANNOT_CIRCLE
Definition fpdf_annot.h:26
#define FPDF_ANNOT_TEXT
Definition fpdf_annot.h:21
#define FPDF_ANNOT_PRINTERMARK
Definition fpdf_annot.h:42
#define FPDF_ANNOT_LINK
Definition fpdf_annot.h:22
#define FPDF_ANNOT_POLYLINE
Definition fpdf_annot.h:28
FPDF_EXPORT int FPDF_CALLCONV FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document)
FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAttachment_GetName(FPDF_ATTACHMENT attachment, FPDF_WCHAR *buffer, unsigned long buflen)
FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index)
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment, void *buffer, unsigned long buflen, unsigned long *out_buflen)
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page, int index)
FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object)
#define FPDF_PAGEOBJ_PATH
Definition fpdf_edit.h:40
#define FPDF_PAGEOBJ_TEXT
Definition fpdf_edit.h:39
#define FPDF_PAGEOBJ_SHADING
Definition fpdf_edit.h:42
#define FPDF_PAGEOBJ_IMAGE
Definition fpdf_edit.h:41
#define FPDF_PAGEOBJ_FORM
Definition fpdf_edit.h:43
FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page)
FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page)
Definition fpdf_text.cpp:59
FPDF_EXPORT unsigned int FPDF_CALLCONV FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index)
Definition fpdf_text.cpp:65
FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFPage_GetRawThumbnailData(FPDF_PAGE page, void *buffer, unsigned long buflen)
FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFPage_GetDecodedThumbnailData(FPDF_PAGE page, void *buffer, unsigned long buflen)
#define FPDFBitmap_BGR
Definition fpdfview.h:1100
#define FPDFBitmap_BGRx
Definition fpdfview.h:1102
#define FPDFBitmap_BGRA
Definition fpdfview.h:1104
#define FPDFBitmap_Unknown
Definition fpdfview.h:1096
#define FPDFBitmap_Gray
Definition fpdfview.h:1098
bool GetThumbnailFilename(char *name_buf, size_t name_buf_size, const char *pdf_name, int page_num, ThumbnailDecodeType decode_type)
Definition write.cc:644
void WriteDecodedThumbnailStream(FPDF_PAGE page, const char *pdf_name, int page_num)
Definition write.cc:838
void WriteRenderedImages(FPDF_DOCUMENT doc, FPDF_PAGE page, const char *pdf_name, int page_num)
Definition write.cc:802
void WriteBufferToFile(const void *buf, size_t buflen, const char *filename, const char *filetype)
Definition write.cc:673
ThumbnailDecodeType
Definition write.cc:642
void WriteRawThumbnailStream(FPDF_PAGE page, const char *pdf_name, int page_num)
Definition write.cc:868
std::vector< uint8_t > EncodeBitmapToPng(ScopedFPDFBitmap bitmap)
Definition write.cc:692
void WriteAnnot(FPDF_PAGE page, const char *pdf_name, int num)
Definition write.cc:339
void WriteImages(FPDF_PAGE page, const char *pdf_name, int page_num)
Definition write.cc:769
std::string WritePng(const char *pdf_name, int num, void *buffer, int stride, int width, int height)
Definition write.cc:451
void WriteThumbnail(FPDF_PAGE page, const char *pdf_name, int page_num)
Definition write.cc:896
void WriteText(FPDF_TEXTPAGE textpage, const char *pdf_name, int num)
Definition write.cc:310
std::string WritePpm(const char *pdf_name, int num, void *buffer_void, int stride, int width, int height)
Definition write.cc:259
void WriteAttachments(FPDF_DOCUMENT doc, const std::string &name)
Definition write.cc:714