qpdf_ruby 0.1.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +70 -5
- data/README.md +95 -54
- data/ext/qpdf_ruby/document_handle.cpp +1 -1
- data/ext/qpdf_ruby/figure_node.cpp +112 -87
- data/ext/qpdf_ruby/mcid_bounds.cpp +317 -0
- data/ext/qpdf_ruby/mcid_bounds.hpp +33 -0
- data/ext/qpdf_ruby/pdf_struct_walker.cpp +11 -18
- data/ext/qpdf_ruby/pdf_struct_walker.hpp +5 -3
- data/ext/qpdf_ruby/pdfua.cpp +1005 -0
- data/ext/qpdf_ruby/pdfua.hpp +118 -0
- data/ext/qpdf_ruby/qpdf_ruby.cpp +424 -212
- data/ext/qpdf_ruby/qpdf_ruby.hpp +1 -4
- data/ext/qpdf_ruby/ruby_guard.hpp +71 -0
- data/ext/qpdf_ruby/stroke_bounds.cpp +155 -0
- data/ext/qpdf_ruby/stroke_bounds.hpp +49 -0
- data/ext/qpdf_ruby/xmp_scan.cpp +329 -0
- data/ext/qpdf_ruby/xmp_scan.hpp +49 -0
- data/lib/qpdf_ruby/version.rb +1 -1
- data/lib/qpdf_ruby.rb +3 -2
- data/sig/qpdf_ruby.rbs +82 -1
- metadata +11 -12
- data/.clang-format +0 -4
- data/.rspec +0 -3
- data/.rubocop.yml +0 -88
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/Rakefile +0 -25
- data/cliff.toml +0 -138
- data/docker/Dockerfile +0 -46
- data/ext/qpdf_ruby/pdf_image_mapper.cpp +0 -294
- data/ext/qpdf_ruby/pdf_image_mapper.hpp +0 -68
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
// Fixes for the PDF/UA-1 gaps of Chromium's tagged PDF (print-to-PDF with generateTaggedPDF): it
|
|
12
|
+
// writes no /Artifact sections, no LBody, no link descriptions, no XMP identification, and uses
|
|
13
|
+
// PDF 2.0 structure types without a RoleMap. Each step only adds what is missing and leaves
|
|
14
|
+
// everything else in place.
|
|
15
|
+
namespace qpdf_ruby::pdfua {
|
|
16
|
+
|
|
17
|
+
// Painting operators outside any marked content, per kind - what PDF/UA-1 7.1-3 rejects.
|
|
18
|
+
struct UntaggedCounts {
|
|
19
|
+
long paths = 0;
|
|
20
|
+
long texts = 0;
|
|
21
|
+
long xobjects = 0;
|
|
22
|
+
long shadings = 0;
|
|
23
|
+
long inline_images = 0;
|
|
24
|
+
long total() const { return paths + texts + xobjects + shadings + inline_images; }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Wraps every painting operation outside marked content in /Artifact BMC ... EMC: paths (with
|
|
28
|
+
// their construction operators), text shows, XObjects, shadings, inline images. Content inside
|
|
29
|
+
// marked content is never touched, and neither is a Form XObject that carries marked content of
|
|
30
|
+
// its own (wrapping it would nest tagged content in an artifact, 7.1-2). Returns the counts.
|
|
31
|
+
// Covers page content and the Form XObjects it draws - not annotation appearance streams (/AP),
|
|
32
|
+
// tiling-pattern content or Type 3 glyph procedures, which Chromium does not emit today.
|
|
33
|
+
UntaggedCounts mark_untagged_content_as_artifacts(QPDF& pdf);
|
|
34
|
+
|
|
35
|
+
// The same walk without changing anything.
|
|
36
|
+
UntaggedCounts count_untagged_content(QPDF& pdf);
|
|
37
|
+
|
|
38
|
+
// Chromium tags CSS backgrounds and borders as content of the element they belong to - a Table's
|
|
39
|
+
// cell backgrounds, an <article>'s card - and a grouping element (Document, Part, Art, Sect, Div,
|
|
40
|
+
// BlockQuote, TOC, TOCI, Index, the table and list containers; after the RoleMap) must not hold
|
|
41
|
+
// content of its own (PAC: "Content in an inadmissible location", "Invalid use of a TR"). Content
|
|
42
|
+
// held directly by such an element that paints only shapes - no text, XObject or inline image, no
|
|
43
|
+
// nested tagged content - becomes /Artifact BMC ... EMC, and leaves the element's kids and the
|
|
44
|
+
// ParentTree. Content with text stays tagged. Returns how many pieces were turned into artifacts.
|
|
45
|
+
long artifact_tagged_decorations(QPDF& pdf);
|
|
46
|
+
|
|
47
|
+
// Gives each Link annotation without /Contents a description (7.18.1-2, 7.18.5-2): the text given
|
|
48
|
+
// for its URI in `texts`, else its structure element's /Alt or /ActualText, else the URI itself
|
|
49
|
+
// (a mailto: address without the scheme). An internal link uses the text given for "#<name>" of
|
|
50
|
+
// its named destination (a name in the catalog's /Dests, a string in the /Names /Dests name tree),
|
|
51
|
+
// else "Page N". Returns how many were described.
|
|
52
|
+
long describe_links(QPDF& pdf, std::map<std::string, std::string> const& texts);
|
|
53
|
+
|
|
54
|
+
// Moves an LI's content other than Lbl/LBody into a new LBody (7.2-20), structure elements as well
|
|
55
|
+
// as marked-content references, with the ParentTree entries of the moved content updated.
|
|
56
|
+
// Returns how many LBody elements were created.
|
|
57
|
+
long wrap_list_bodies(QPDF& pdf);
|
|
58
|
+
|
|
59
|
+
// Marked content and annotations whose ParentTree entry does not name the structure element that
|
|
60
|
+
// holds them - MCIDs looked up in the stream they are numbered in (a reference's /Stm, else the
|
|
61
|
+
// page), annotations through their /StructParent. Read-only; 0 for a consistent tree.
|
|
62
|
+
long count_parent_tree_mismatches(QPDF& pdf);
|
|
63
|
+
|
|
64
|
+
// Maps PDF 2.0 structure types Chromium emits (Aside, Strong, Em, ...) to PDF 1.7 standard types
|
|
65
|
+
// in the RoleMap, unless the RoleMap already maps them (7.1-5). Returns how many were added.
|
|
66
|
+
long map_nonstandard_roles(QPDF& pdf);
|
|
67
|
+
|
|
68
|
+
// Chromium tags an HTML <figure> as a Figure without alternative text around the image's own
|
|
69
|
+
// Figure (which carries the alt text) and its Caption (7.3-1). Such a grouping Figure - no /Alt or
|
|
70
|
+
// /ActualText of its own, but a Figure below it - becomes a Div; nothing is lost, the caption stays
|
|
71
|
+
// readable. Returns how many were retagged.
|
|
72
|
+
long retag_grouping_figures(QPDF& pdf);
|
|
73
|
+
|
|
74
|
+
// Figures that still have neither /Alt nor /ActualText - only a person can write those (7.3-1).
|
|
75
|
+
long count_figures_without_alt(QPDF& pdf);
|
|
76
|
+
|
|
77
|
+
// Identifies the file as PDF/UA-1 in its XMP metadata (7.1-8) and sets DisplayDocTitle and
|
|
78
|
+
// Marked. An existing metadata stream keeps everything it has: pdfuaid:part and dc:title are each
|
|
79
|
+
// added only if missing (read with namespace scoping, element or attribute form; comments and CDATA
|
|
80
|
+
// do not count); an existing dc:title is never replaced. Without a stream, a new one is written.
|
|
81
|
+
// The title is `title` if given (it also becomes the information dictionary's /Title), else that
|
|
82
|
+
// /Title. Never throws for odd input: without a structure tree only the title and DisplayDocTitle
|
|
83
|
+
// are set (Marked and pdfuaid would claim a tagged file), and an existing packet that is not
|
|
84
|
+
// well-formed XML with an rdf:RDF element is left alone - apply() reports both as
|
|
85
|
+
// unidentified_reason. Returns true if anything changed.
|
|
86
|
+
bool add_pdfua_identification(QPDF& pdf, std::optional<std::string> const& title);
|
|
87
|
+
|
|
88
|
+
// Who made the file: the information dictionary's /Creator and /Producer, and their XMP twins
|
|
89
|
+
// xmp:CreatorTool and pdf:Producer, kept in agreement. A value that is not given, or empty, is left
|
|
90
|
+
// alone. The information dictionary is always set. In XMP a property that already holds exactly
|
|
91
|
+
// that value once stays; every other occurrence of it is removed and the value written in a new
|
|
92
|
+
// rdf:Description - a packet that is not well-formed XML with an rdf:RDF element is left alone and
|
|
93
|
+
// xmp_error says so. Without a packet a new one is written. The values must be valid XML text
|
|
94
|
+
// (xmp::valid_text) - the Ruby binding checks that.
|
|
95
|
+
struct DocumentInfoResult {
|
|
96
|
+
bool changed = false;
|
|
97
|
+
std::optional<std::string> xmp_error; // why the XMP packet was not updated, if it was not
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
DocumentInfoResult set_document_info(QPDF& pdf, std::optional<std::string> const& creator,
|
|
101
|
+
std::optional<std::string> const& producer);
|
|
102
|
+
|
|
103
|
+
struct Report {
|
|
104
|
+
long decorations = 0;
|
|
105
|
+
UntaggedCounts artifacts;
|
|
106
|
+
long links = 0;
|
|
107
|
+
long list_bodies = 0;
|
|
108
|
+
long roles = 0;
|
|
109
|
+
long figure_groups = 0;
|
|
110
|
+
long figures_without_alt = 0;
|
|
111
|
+
bool identified = false;
|
|
112
|
+
std::optional<std::string> unidentified_reason; // why the file is not identified, if it is not
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// All of the above, in the order that keeps each step's input intact.
|
|
116
|
+
Report apply(QPDF& pdf, std::map<std::string, std::string> const& link_texts, std::optional<std::string> const& title);
|
|
117
|
+
|
|
118
|
+
} // namespace qpdf_ruby::pdfua
|