• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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