qpdf_ruby 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ #pragma once
2
+ #define POINTERHOLDER_TRANSITION 1
3
+
4
+ #include <qpdf/QPDF.hh>
5
+
6
+ #include <map>
7
+ #include <optional>
8
+ #include <string>
9
+
10
+ // Fixes for the PDF/UA-1 gaps of Chromium's tagged PDF (print-to-PDF with generateTaggedPDF): it
11
+ // writes no /Artifact sections, no LBody, no link descriptions, no XMP identification, and uses
12
+ // PDF 2.0 structure types without a RoleMap. Each step only adds what is missing and leaves
13
+ // everything else in place.
14
+ namespace qpdf_ruby::pdfua {
15
+
16
+ // Painting operators outside any marked content, per kind - what PDF/UA-1 7.1-3 rejects.
17
+ struct UntaggedCounts {
18
+ long paths = 0;
19
+ long texts = 0;
20
+ long xobjects = 0;
21
+ long shadings = 0;
22
+ long inline_images = 0;
23
+ long total() const { return paths + texts + xobjects + shadings + inline_images; }
24
+ };
25
+
26
+ // Wraps every painting operation outside marked content in /Artifact BMC ... EMC: paths (with
27
+ // their construction operators), text shows, XObjects, shadings, inline images. Content inside
28
+ // marked content is never touched, and neither is a Form XObject that carries marked content of
29
+ // its own (wrapping it would nest tagged content in an artifact, 7.1-2). Returns the counts.
30
+ // Covers page content and the Form XObjects it draws - not annotation appearance streams (/AP),
31
+ // tiling-pattern content or Type 3 glyph procedures, which Chromium does not emit today.
32
+ UntaggedCounts mark_untagged_content_as_artifacts(QPDF& pdf);
33
+
34
+ // The same walk without changing anything.
35
+ UntaggedCounts count_untagged_content(QPDF& pdf);
36
+
37
+ // Chromium tags CSS backgrounds and borders as content of the element they belong to - a Table's
38
+ // cell backgrounds, an <article>'s card - and a grouping element (Document, Part, Art, Sect, Div,
39
+ // BlockQuote, TOC, TOCI, Index, the table and list containers; after the RoleMap) must not hold
40
+ // content of its own (PAC: "Content in an inadmissible location", "Invalid use of a TR"). Content
41
+ // held directly by such an element that paints only shapes - no text, XObject or inline image, no
42
+ // nested tagged content - becomes /Artifact BMC ... EMC, and leaves the element's kids and the
43
+ // ParentTree. Content with text stays tagged. Returns how many pieces were turned into artifacts.
44
+ long artifact_tagged_decorations(QPDF& pdf);
45
+
46
+ // Gives each Link annotation without /Contents a description (7.18.1-2, 7.18.5-2): the text given
47
+ // for its URI in `texts`, else its structure element's /Alt or /ActualText, else the URI itself
48
+ // (a mailto: address without the scheme). An internal link uses the text given for "#<name>" of
49
+ // its named destination (a name in the catalog's /Dests, a string in the /Names /Dests name tree),
50
+ // else "Page N". Returns how many were described.
51
+ long describe_links(QPDF& pdf, std::map<std::string, std::string> const& texts);
52
+
53
+ // Moves an LI's content other than Lbl/LBody into a new LBody (7.2-20), structure elements as well
54
+ // as marked-content references, with the ParentTree entries of the moved content updated.
55
+ // Returns how many LBody elements were created.
56
+ long wrap_list_bodies(QPDF& pdf);
57
+
58
+ // Marked content and annotations whose ParentTree entry does not name the structure element that
59
+ // holds them - MCIDs looked up in the stream they are numbered in (a reference's /Stm, else the
60
+ // page), annotations through their /StructParent. Read-only; 0 for a consistent tree.
61
+ long count_parent_tree_mismatches(QPDF& pdf);
62
+
63
+ // Maps PDF 2.0 structure types Chromium emits (Aside, Strong, Em, ...) to PDF 1.7 standard types
64
+ // in the RoleMap, unless the RoleMap already maps them (7.1-5). Returns how many were added.
65
+ long map_nonstandard_roles(QPDF& pdf);
66
+
67
+ // Chromium tags an HTML <figure> as a Figure without alternative text around the image's own
68
+ // Figure (which carries the alt text) and its Caption (7.3-1). Such a grouping Figure - no /Alt or
69
+ // /ActualText of its own, but a Figure below it - becomes a Div; nothing is lost, the caption stays
70
+ // readable. Returns how many were retagged.
71
+ long retag_grouping_figures(QPDF& pdf);
72
+
73
+ // Figures that still have neither /Alt nor /ActualText - only a person can write those (7.3-1).
74
+ long count_figures_without_alt(QPDF& pdf);
75
+
76
+ // Identifies the file as PDF/UA-1 in its XMP metadata (7.1-8) and sets DisplayDocTitle and
77
+ // Marked. An existing metadata stream keeps everything it has: pdfuaid:part and dc:title are each
78
+ // added only if missing (read with namespace scoping, element or attribute form; comments and CDATA
79
+ // do not count); an existing dc:title is never replaced. Without a stream, a new one is written.
80
+ // The title is `title` if given (it also becomes the information dictionary's /Title), else that
81
+ // /Title. Never throws for odd input: without a structure tree only the title and DisplayDocTitle
82
+ // are set (Marked and pdfuaid would claim a tagged file), and an existing packet that is not
83
+ // well-formed XML with an rdf:RDF element is left alone - apply() reports both as
84
+ // unidentified_reason. Returns true if anything changed.
85
+ bool add_pdfua_identification(QPDF& pdf, std::optional<std::string> const& title);
86
+
87
+ struct Report {
88
+ long decorations = 0;
89
+ UntaggedCounts artifacts;
90
+ long links = 0;
91
+ long list_bodies = 0;
92
+ long roles = 0;
93
+ long figure_groups = 0;
94
+ long figures_without_alt = 0;
95
+ bool identified = false;
96
+ std::optional<std::string> unidentified_reason; // why the file is not identified, if it is not
97
+ };
98
+
99
+ // All of the above, in the order that keeps each step's input intact.
100
+ Report apply(QPDF& pdf, std::map<std::string, std::string> const& link_texts, std::optional<std::string> const& title);
101
+
102
+ } // namespace qpdf_ruby::pdfua