1 #include "qpdf_xobject.h"
2 //#include <qpdf/Types.h>
3 #include <qpdf/QPDF.hh>
4 #include <qpdf/Pl_Discard.hh>
5 #include <qpdf/Pl_Count.hh>
6 #include <qpdf/Pl_Concatenate.hh>
7 #include "qpdf_tools.h"
8 #include "qpdf_pdftopdf.h"
9
10 // TODO: need to remove Struct Parent stuff (or fix)
11
12 // NOTE: use /TrimBox to position content inside Nup cell, /BleedBox to clip against
13
14 class CombineFromContents_Provider : public QPDFObjectHandle::StreamDataProvider {
15 public:
16 CombineFromContents_Provider(const std::vector<QPDFObjectHandle> &contents);
17
18 void provideStreamData(int objid, int generation, Pipeline* pipeline);
19 private:
20 std::vector<QPDFObjectHandle> contents;
21 };
22
CombineFromContents_Provider(const std::vector<QPDFObjectHandle> & contents)23 CombineFromContents_Provider::CombineFromContents_Provider(const std::vector<QPDFObjectHandle> &contents)
24 : contents(contents)
25 {
26 }
27
provideStreamData(int objid,int generation,Pipeline * pipeline)28 void CombineFromContents_Provider::provideStreamData(int objid, int generation, Pipeline* pipeline)
29 {
30 Pl_Concatenate concat("concat", pipeline);
31 const int clen=contents.size();
32 for (int iA=0;iA<clen;iA++) {
33 contents[iA].pipeStreamData(&concat, true, false, false);
34 }
35 concat.manualFinish();
36 }
37
38 /*
39 To convert a page to an XObject there are several keys to consider:
40
41 /Type /Page -> /Type /XObject (/Type optional for XObject)
42 -> /Subtype /Form
43 -> [/FormType 1] (optional)
44 /Parent ? ? R -> remove
45 /Resources dict -> copy
46 /MediaBox rect [/CropBox /BleedBox /TrimBox /ArtBox]
47 -> /BBox (use TrimBox [+ Bleed consideration?], with fallback to /MediaBox)
48 note that /BBox is in *Form Space*, see /Matrix!
49 [/BoxColorInfo dict] (used for guidelines that may be shown by viewer)
50 -> ignore/remove
51 [/Contents asfd] -> concatenate into stream data of the XObject (page is a dict, XObject a stream)
52
53 [/Rotate 90] ... must be handled (either use CTM where XObject is /used/ -- or set /Matrix)
54 [/UserUnit] (PDF 1.6) -> to /Matrix ? -- it MUST be handled.
55
56 [/Group dict] -> copy
57 [/Thumb stream] -> remove, not needed any more / would have to be regenerated (combined)
58 [/B] article beads -- ignore for now
59 [/Dur] -> remove (transition duration)
60 [/Trans] -> remove (transitions)
61 [/AA] -> remove (additional-actions)
62
63 [/Metadata] what shall we do?? (kill: we can't combine XML)
64 [/PieceInfo] -> remove, we can't combine private app data (?)
65 [/LastModified date] (opt except /PieceInfo) -> see there
66
67 [/PZ] -> remove, can't combine/keep (preferred zoom level)
68 [/SeparationInfo] -> remove, no way to keep this (needed for separation)
69
70 [/ID] related to web capture -- ignore/kill?
71 [/StructParents] (opt except pdf contains "structural content items")
72 -> copy (is this correct?)
73
74 [/Annots] annotations -- ignore for now
75 [/Tabs] tab order for annotations (/R row, /C column, /S structure order) -- see /Annots
76
77 [/TemplateInstantiated] (reqd, if page was created from named page obj, 1.5) -- ? just ignore?
78 [/PresSteps] -> remove (sub-page navigation for presentations) [no subpage navigation for printing / nup]
79 [/VP] viewport rects -- ignore/drop or recalculate into new page
80
81 */
makeXObject(QPDF * pdf,QPDFObjectHandle page)82 QPDFObjectHandle makeXObject(QPDF *pdf,QPDFObjectHandle page)
83 {
84 page.assertPageObject();
85
86 QPDFObjectHandle ret=QPDFObjectHandle::newStream(pdf);
87 QPDFObjectHandle dict=ret.getDict();
88
89 dict.replaceKey("/Type",QPDFObjectHandle::newName("/XObject")); // optional
90 dict.replaceKey("/Subtype",QPDFObjectHandle::newName("/Form")); // required
91 // dict.replaceKey("/FormType",QPDFObjectHandle::newInteger(1)); // optional
92
93 QPDFObjectHandle box=getTrimBox(page); // already in "form space"
94 dict.replaceKey("/BBox",box); // reqd
95
96 // [/Matrix .] ... default is [1 0 0 1 0 0]; we incorporate /UserUnit and /Rotate here
97 Matrix mtx;
98 if (page.hasKey("/UserUnit")) {
99 mtx.scale(page.getKey("/UserUnit").getNumericValue());
100 }
101
102 // transform, so that bbox is [0 0 w h] (in outer space, but after UserUnit)
103 Rotation rot=getRotate(page);
104
105 // calculate rotation effect on [0 0 w h]
106 PageRect bbox=getBoxAsRect(box),tmp;
107 tmp.left=0;
108 tmp.bottom=0;
109 tmp.right=0;
110 tmp.top=0;
111 tmp.rotate_move(rot,bbox.width,bbox.height);
112 // tmp.rotate_move moves the bbox; we must achieve this move with the matrix.
113 mtx.translate(tmp.left,tmp.bottom); // 1. move origin to end up at left,bottom after rotation
114
115 mtx.rotate(rot); // 2. rotate coordinates according to /Rotate
116 mtx.translate(-bbox.left,-bbox.bottom); // 3. move origin from 0,0 to "form space"
117
118 dict.replaceKey("/Matrix",mtx.get());
119
120 dict.replaceKey("/Resources",page.getKey("/Resources"));
121 if (page.hasKey("/Group")) {
122 dict.replaceKey("/Group",page.getKey("/Group")); // (transparency); opt, copy if there
123 }
124
125 // ?? /StructParents ... can basically copy from page, but would need fixup in Structure Tree
126 // FIXME: remove (globally) Tagged spec (/MarkInfo), and Structure Tree
127
128 // Note: [/Name] (reqd. only in 1.0 -- but there we even can't use our normal img/patter procedures)
129
130 // none:
131 // QPDFObjectHandle filter=QPDFObjectHandle::newArray();
132 // QPDFObjectHandle decode_parms=QPDFObjectHandle::newArray();
133 // null leads to use of "default filters" from qpdf's settings
134 QPDFObjectHandle filter=QPDFObjectHandle::newNull();
135 QPDFObjectHandle decode_parms=QPDFObjectHandle::newNull();
136
137 std::vector<QPDFObjectHandle> contents=page.getPageContents(); // (will assertPageObject)
138
139 auto ph=PointerHolder<QPDFObjectHandle::StreamDataProvider>(new CombineFromContents_Provider(contents));
140 ret.replaceStreamData(ph,filter,decode_parms);
141
142 return ret;
143 }
144
145 /*
146 we will have to fix up the structure tree (e.g. /K in element), when copying /StructParents;
147 (there is /Pg, which has to point to the containing page, /Stm when it's not part of the page's content stream
148 i.e. when it is in our XObject!; then there is /StmOwn ...)
149 when not copying, we have to remove the structure tree completely (also /MarkInfo dict)
150 Still this might not be sufficient(?), as there are probably BDC and EMC operators in the stream.
151 */
152
153 /* /XObject /Form has
154 [/Type /XObject]
155 /Subtype /Form
156 [/FormType 1]
157 /BBox rect from crop box, or recalculate
158 [/Matrix .] ... default is [1 0 0 1 0 0] --- we have to incorporate /UserUnit here?!
159 [/Resources dict] from page.
160 [/Group dict] used for transparency -- can copy from page
161 [/Ref dict] not needed; for external reference
162 [/Metadata] not, as long we can not combine.
163 [/PieceInfo] can copy, but not combine
164 [/LastModified date] copy if /PieceInfo there
165 [/StructParent] . don't want to be one ... have to read more spec
166 [/StructParents] . copy from page!
167 [/OPI] no opi version. don't set
168 [/OC] is this optional content? NO! not needed.
169 [/Name] (only reqd. in 1.0 -- but there we even can't use our normal img/patter procedures)
170 */
171