blob: c0156cf875abdf1885999760efbe0f1e755cdf2f [file] [log] [blame]
edisonn@google.com3aa35552013-08-14 18:26:20 +00001#include "SkPdfNativeDoc.h"
edisonn@google.com571c70b2013-07-10 17:09:50 +00002#include "SkPdfNativeTokenizer.h"
edisonn@google.com3aa35552013-08-14 18:26:20 +00003#include "SkPdfNativeObject.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +00004
edisonn@google.com571c70b2013-07-10 17:09:50 +00005#include <stdio.h>
6#include <string.h>
7#include <sys/types.h>
8#include <sys/stat.h>
edisonn@google.com3aac1f92013-07-02 22:42:53 +00009
edisonn@google.com33f11b62013-08-14 21:35:27 +000010// TODO(edisonn): for some reason on mac these files are found here, but are found from headers
11//#include "SkPdfFileTrailerDictionary_autogen.h"
12//#include "SkPdfCatalogDictionary_autogen.h"
13//#include "SkPdfPageObjectDictionary_autogen.h"
14//#include "SkPdfPageTreeNodeDictionary_autogen.h"
15#include "SkPdfHeaders_autogen.h"
16
edisonn@google.com571c70b2013-07-10 17:09:50 +000017#include "SkPdfMapper_autogen.h"
18
edisonn@google.com147adb12013-07-24 15:56:19 +000019#include "SkStream.h"
edisonn@google.com571c70b2013-07-10 17:09:50 +000020
21
edisonn@google.coma3356fc2013-07-10 18:20:06 +000022static long getFileSize(const char* filename)
edisonn@google.com571c70b2013-07-10 17:09:50 +000023{
24 struct stat stat_buf;
25 int rc = stat(filename, &stat_buf);
edisonn@google.coma3356fc2013-07-10 18:20:06 +000026 return rc == 0 ? (long)stat_buf.st_size : -1;
edisonn@google.com3aac1f92013-07-02 22:42:53 +000027}
28
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000029static const unsigned char* lineHome(const unsigned char* start, const unsigned char* current) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000030 while (current > start && !isPdfEOL(*(current - 1))) {
31 current--;
32 }
33 return current;
34}
35
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000036static const unsigned char* previousLineHome(const unsigned char* start, const unsigned char* current) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000037 if (current > start && isPdfEOL(*(current - 1))) {
38 current--;
39 }
40
41 // allows CR+LF, LF+CR but not two CR+CR or LF+LF
42 if (current > start && isPdfEOL(*(current - 1)) && *current != *(current - 1)) {
43 current--;
44 }
45
46 while (current > start && !isPdfEOL(*(current - 1))) {
47 current--;
48 }
49
50 return current;
51}
52
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000053static const unsigned char* ignoreLine(const unsigned char* current, const unsigned char* end) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000054 while (current < end && !isPdfEOL(*current)) {
55 current++;
56 }
57 current++;
58 if (current < end && isPdfEOL(*current) && *current != *(current - 1)) {
59 current++;
60 }
61 return current;
62}
63
edisonn@google.com3aa35552013-08-14 18:26:20 +000064SkPdfNativeDoc* gDoc = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +000065
66// TODO(edisonn): NYI
67// TODO(edisonn): 3 constructuctors from URL, from stream, from file ...
68// TODO(edisonn): write one that accepts errors in the file and ignores/fixis them
69// TODO(edisonn): testing:
70// 1) run on a lot of file
71// 2) recoverable corupt file: remove endobj, endsteam, remove other keywords, use other white spaces, insert comments randomly, ...
72// 3) irrecoverable corrupt file
edisonn@google.com147adb12013-07-24 15:56:19 +000073
edisonn@google.com3aa35552013-08-14 18:26:20 +000074SkPdfNativeDoc::SkPdfNativeDoc(SkStream* stream)
edisonn@google.com147adb12013-07-24 15:56:19 +000075 : fAllocator(new SkPdfAllocator())
76 , fFileContent(NULL)
77 , fContentLength(0)
78 , fRootCatalogRef(NULL)
79 , fRootCatalog(NULL) {
80 size_t size = stream->getLength();
81 void* ptr = sk_malloc_throw(size);
82 stream->read(ptr, size);
83
84 init(ptr, size);
85}
86
edisonn@google.com3aa35552013-08-14 18:26:20 +000087SkPdfNativeDoc::SkPdfNativeDoc(const char* path)
edisonn@google.com432640a2013-07-10 22:53:40 +000088 : fAllocator(new SkPdfAllocator())
edisonn@google.com147adb12013-07-24 15:56:19 +000089 , fFileContent(NULL)
90 , fContentLength(0)
edisonn@google.com432640a2013-07-10 22:53:40 +000091 , fRootCatalogRef(NULL)
92 , fRootCatalog(NULL) {
edisonn@google.com222382b2013-07-10 22:33:10 +000093 gDoc = this;
edisonn@google.com571c70b2013-07-10 17:09:50 +000094 FILE* file = fopen(path, "r");
edisonn@google.come57c62d2013-08-07 18:04:15 +000095 // TODO(edisonn): put this in a function that can return NULL
96 if (file) {
97 size_t size = getFileSize(path);
98 void* content = sk_malloc_throw(size);
99 bool ok = (0 != fread(content, size, 1, file));
100 fclose(file);
101 if (!ok) {
102 sk_free(content);
103 // TODO(edisonn): report read error
104 // TODO(edisonn): not nice to return like this from constructor, create a static
105 // function that can report NULL for failures.
106 return; // Doc will have 0 pages
107 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000108
edisonn@google.come57c62d2013-08-07 18:04:15 +0000109 init(content, size);
edisonn@google.com620edc52013-07-18 13:03:03 +0000110 }
edisonn@google.com147adb12013-07-24 15:56:19 +0000111}
112
edisonn@google.com3aa35552013-08-14 18:26:20 +0000113void SkPdfNativeDoc::init(const void* bytes, size_t length) {
edisonn@google.com147adb12013-07-24 15:56:19 +0000114 fFileContent = (const unsigned char*)bytes;
115 fContentLength = length;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000116 const unsigned char* eofLine = lineHome(fFileContent, fFileContent + fContentLength - 1);
117 const unsigned char* xrefByteOffsetLine = previousLineHome(fFileContent, eofLine);
118 const unsigned char* xrefstartKeywordLine = previousLineHome(fFileContent, xrefByteOffsetLine);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000119
120 if (strcmp((char*)xrefstartKeywordLine, "startxref") != 0) {
121 // TODO(edisonn): report/issue
122 }
123
124 long xrefByteOffset = atol((const char*)xrefByteOffsetLine);
125
126 bool storeCatalog = true;
127 while (xrefByteOffset >= 0) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000128 const unsigned char* trailerStart = readCrossReferenceSection(fFileContent + xrefByteOffset, xrefstartKeywordLine);
edisonn@google.com24cdf132013-07-30 16:06:12 +0000129 xrefByteOffset = -1;
130 if (trailerStart < xrefstartKeywordLine) {
131 readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog, &xrefByteOffset, false);
132 storeCatalog = false;
133 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000134 }
135
136 // TODO(edisonn): warn/error expect fObjects[fRefCatalogId].fGeneration == fRefCatalogGeneration
137 // TODO(edisonn): security, verify that SkPdfCatalogDictionary is indeed using mapper
138 // load catalog
edisonn@google.com571c70b2013-07-10 17:09:50 +0000139
edisonn@google.com432640a2013-07-10 22:53:40 +0000140 if (fRootCatalogRef) {
141 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
edisonn@google.com8bad7372013-07-10 23:36:56 +0000142 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
143 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
144 if (tree && tree->isDictionary() && tree->valid()) {
145 fillPages(tree);
146 }
147 }
edisonn@google.com432640a2013-07-10 22:53:40 +0000148 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000149
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000150 // TODO(edisonn): clean up this doc, or better, let the caller call again and build a new doc
151 // caller should be a static function.
152 if (pages() == 0) {
153 loadWithoutXRef();
154 }
155
edisonn@google.com8bad7372013-07-10 23:36:56 +0000156 // TODO(edisonn): corrupted pdf, read it from beginning and rebuild (xref, trailer, or just reall all objects)
157 // 0 pages
158
edisonn@google.com571c70b2013-07-10 17:09:50 +0000159 // now actually read all objects if we want, or do it lazyly
160 // and resolve references?... or not ...
161}
162
edisonn@google.com3aa35552013-08-14 18:26:20 +0000163void SkPdfNativeDoc::loadWithoutXRef() {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000164 const unsigned char* current = fFileContent;
165 const unsigned char* end = fFileContent + fContentLength;
166
167 // TODO(edisonn): read pdf version
168 current = ignoreLine(current, end);
169
170 current = skipPdfWhiteSpaces(0, current, end);
171 while (current < end) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000172 SkPdfNativeObject token;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000173 current = nextObject(0, current, end, &token, NULL, NULL);
174 if (token.isInteger()) {
175 int id = (int)token.intValue();
176
177 token.reset();
178 current = nextObject(0, current, end, &token, NULL, NULL);
179 // int generation = (int)token.intValue(); // TODO(edisonn): ignored for now
180
181 token.reset();
182 current = nextObject(0, current, end, &token, NULL, NULL);
183 // TODO(edisonn): must be obj, return error if not? ignore ?
184 if (!token.isKeyword("obj")) {
185 continue;
186 }
187
188 while (fObjects.count() < id + 1) {
189 reset(fObjects.append());
190 }
191
192 fObjects[id].fOffset = current - fFileContent;
193
edisonn@google.com3aa35552013-08-14 18:26:20 +0000194 SkPdfNativeObject* obj = fAllocator->allocObject();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000195 current = nextObject(0, current, end, obj, fAllocator, this);
196
197 fObjects[id].fResolvedReference = obj;
198 fObjects[id].fObj = obj;
199
200 // set objects
201 } else if (token.isKeyword("trailer")) {
202 long dummy;
203 current = readTrailer(current, end, true, &dummy, true);
204 } else if (token.isKeyword("startxref")) {
205 token.reset();
206 current = nextObject(0, current, end, &token, NULL, NULL); // ignore
207 }
208
209 current = skipPdfWhiteSpaces(0, current, end);
210 }
211
edisonn@google.com4f898b72013-08-07 21:11:57 +0000212 // TODO(edisonn): hack, detect root catalog - we need to implement liniarized support, and remove this hack.
213 if (!fRootCatalogRef) {
214 for (unsigned int i = 0 ; i < objects(); i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000215 SkPdfNativeObject* obj = object(i);
216 SkPdfNativeObject* root = (obj && obj->isDictionary()) ? obj->get("Root") : NULL;
edisonn@google.com4f898b72013-08-07 21:11:57 +0000217 if (root && root->isReference()) {
218 fRootCatalogRef = root;
219 }
220 }
221 }
222
223
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000224 if (fRootCatalogRef) {
225 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
226 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
227 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
228 if (tree && tree->isDictionary() && tree->valid()) {
229 fillPages(tree);
230 }
231 }
232 }
233
edisonn@google.com4f898b72013-08-07 21:11:57 +0000234
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000235}
236
edisonn@google.com571c70b2013-07-10 17:09:50 +0000237// TODO(edisonn): NYI
edisonn@google.com3aa35552013-08-14 18:26:20 +0000238SkPdfNativeDoc::~SkPdfNativeDoc() {
edisonn@google.com147adb12013-07-24 15:56:19 +0000239 sk_free((void*)fFileContent);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000240 delete fAllocator;
241}
242
edisonn@google.com3aa35552013-08-14 18:26:20 +0000243const unsigned char* SkPdfNativeDoc::readCrossReferenceSection(const unsigned char* xrefStart, const unsigned char* trailerEnd) {
244 SkPdfNativeObject xref;
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000245 const unsigned char* current = nextObject(0, xrefStart, trailerEnd, &xref, NULL, NULL);
246
247 if (!xref.isKeyword("xref")) {
248 return trailerEnd;
249 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000250
edisonn@google.com3aa35552013-08-14 18:26:20 +0000251 SkPdfNativeObject token;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000252 while (current < trailerEnd) {
253 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000254 const unsigned char* previous = current;
255 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000256 if (!token.isInteger()) {
257 return previous;
258 }
259
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000260 int startId = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000261 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000262 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000263
264 if (!token.isInteger()) {
265 // TODO(edisonn): report/warning
266 return current;
267 }
268
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000269 int entries = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000270
271 for (int i = 0; i < entries; i++) {
272 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000273 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000274 if (!token.isInteger()) {
275 // TODO(edisonn): report/warning
276 return current;
277 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000278 int offset = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000279
280 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000281 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000282 if (!token.isInteger()) {
283 // TODO(edisonn): report/warning
284 return current;
285 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000286 int generation = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000287
288 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000289 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.come878e722013-07-29 19:10:58 +0000290 if (!token.isKeyword() || token.lenstr() != 1 || (*token.c_str() != 'f' && *token.c_str() != 'n')) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000291 // TODO(edisonn): report/warning
292 return current;
293 }
294
295 addCrossSectionInfo(startId + i, generation, offset, *token.c_str() == 'f');
296 }
297 }
298 // TODO(edisonn): it should never get here? there is no trailer?
299 return current;
300}
301
edisonn@google.com3aa35552013-08-14 18:26:20 +0000302const unsigned char* SkPdfNativeDoc::readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog, long* prev, bool skipKeyword) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000303 *prev = -1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000304
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000305 const unsigned char* current = trailerStart;
306 if (!skipKeyword) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000307 SkPdfNativeObject trailerKeyword;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000308 // TODO(edisonn): use null allocator, and let it just fail if memory
309 // needs allocated (but no crash)!
310 current = nextObject(0, current, trailerEnd, &trailerKeyword, NULL, NULL);
311
312 if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.lenstr() ||
313 strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
314 // TODO(edisonn): report warning, rebuild trailer from objects.
315 return current;
316 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000317 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000318
edisonn@google.com3aa35552013-08-14 18:26:20 +0000319 SkPdfNativeObject token;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000320 current = nextObject(0, current, trailerEnd, &token, fAllocator, NULL);
edisonn@google.com432640a2013-07-10 22:53:40 +0000321 if (!token.isDictionary()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000322 return current;
edisonn@google.com432640a2013-07-10 22:53:40 +0000323 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000324 SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token;
edisonn@google.com432640a2013-07-10 22:53:40 +0000325 if (!trailer->valid()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000326 return current;
edisonn@google.com432640a2013-07-10 22:53:40 +0000327 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000328
329 if (storeCatalog) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000330 SkPdfNativeObject* ref = trailer->Root(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000331 if (ref == NULL || !ref->isReference()) {
332 // TODO(edisonn): oops, we have to fix the corrup pdf file
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000333 return current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000334 }
335 fRootCatalogRef = ref;
336 }
337
338 if (trailer->has_Prev()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000339 *prev = (long)trailer->Prev(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000340 }
341
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000342 return current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000343}
344
edisonn@google.com3aa35552013-08-14 18:26:20 +0000345void SkPdfNativeDoc::addCrossSectionInfo(int id, int generation, int offset, bool isFreed) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000346 // TODO(edisonn): security here
347 while (fObjects.count() < id + 1) {
348 reset(fObjects.append());
349 }
350
351 fObjects[id].fOffset = offset;
352 fObjects[id].fObj = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000353 fObjects[id].fResolvedReference = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000354}
355
edisonn@google.com3aa35552013-08-14 18:26:20 +0000356SkPdfNativeObject* SkPdfNativeDoc::readObject(int id/*, int expectedGeneration*/) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000357 long startOffset = fObjects[id].fOffset;
358 //long endOffset = fObjects[id].fOffsetEnd;
359 // TODO(edisonn): use hinted endOffset
360 // TODO(edisonn): current implementation will result in a lot of memory usage
361 // to decrease memory usage, we wither need to be smart and know where objects end, and we will
362 // alocate only the chancks needed, or the tokenizer will not make copies, but then it needs to
363 // cache the results so it does not go twice on the same buffer
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000364 const unsigned char* current = fFileContent + startOffset;
365 const unsigned char* end = fFileContent + fContentLength;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000366
edisonn@google.com33f11b62013-08-14 21:35:27 +0000367 SkPdfNativeTokenizer tokenizer(current, end - current, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000368
edisonn@google.com3aa35552013-08-14 18:26:20 +0000369 SkPdfNativeObject idObj;
370 SkPdfNativeObject generationObj;
371 SkPdfNativeObject objKeyword;
372 SkPdfNativeObject* dict = fAllocator->allocObject();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000373
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000374 current = nextObject(0, current, end, &idObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000375 if (current >= end) {
376 // TODO(edisonn): report warning/error
377 return NULL;
378 }
379
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000380 current = nextObject(0, current, end, &generationObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000381 if (current >= end) {
382 // TODO(edisonn): report warning/error
383 return NULL;
384 }
385
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000386 current = nextObject(0, current, end, &objKeyword, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000387 if (current >= end) {
388 // TODO(edisonn): report warning/error
389 return NULL;
390 }
391
392 if (!idObj.isInteger() || !generationObj.isInteger() || id != idObj.intValue()/* || generation != generationObj.intValue()*/) {
393 // TODO(edisonn): report warning/error
394 }
395
396 if (!objKeyword.isKeyword() || strcmp(objKeyword.c_str(), "obj") != 0) {
397 // TODO(edisonn): report warning/error
398 }
399
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000400 current = nextObject(1, current, end, dict, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000401
402 // TODO(edisonn): report warning/error - verify last token is endobj
403
404 return dict;
405}
406
edisonn@google.com3aa35552013-08-14 18:26:20 +0000407void SkPdfNativeDoc::fillPages(SkPdfPageTreeNodeDictionary* tree) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000408 SkPdfArray* kids = tree->Kids(this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000409 if (kids == NULL) {
410 *fPages.append() = (SkPdfPageObjectDictionary*)tree;
411 return;
412 }
413
414 int cnt = kids->size();
415 for (int i = 0; i < cnt; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000416 SkPdfNativeObject* obj = resolveReference(kids->objAtAIndex(i));
417 if (fMapper->mapPageObjectDictionary(obj) != kPageObjectDictionary_SkPdfNativeObjectType) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000418 *fPages.append() = (SkPdfPageObjectDictionary*)obj;
419 } else {
420 // TODO(edisonn): verify that it is a page tree indeed
421 fillPages((SkPdfPageTreeNodeDictionary*)obj);
422 }
423 }
424}
425
edisonn@google.com3aa35552013-08-14 18:26:20 +0000426int SkPdfNativeDoc::pages() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000427 return fPages.count();
428}
429
edisonn@google.com3aa35552013-08-14 18:26:20 +0000430SkPdfPageObjectDictionary* SkPdfNativeDoc::page(int page) {
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000431 SkASSERT(page >= 0 && page < fPages.count());
432 return fPages[page];
433}
434
435
edisonn@google.com3aa35552013-08-14 18:26:20 +0000436SkPdfResourceDictionary* SkPdfNativeDoc::pageResources(int page) {
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000437 SkASSERT(page >= 0 && page < fPages.count());
edisonn@google.com571c70b2013-07-10 17:09:50 +0000438 return fPages[page]->Resources(this);
439}
440
441// TODO(edisonn): Partial implemented. Move the logics directly in the code generator for inheritable and default value?
edisonn@google.com3aa35552013-08-14 18:26:20 +0000442SkRect SkPdfNativeDoc::MediaBox(int page) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000443 SkPdfPageObjectDictionary* current = fPages[page];
444 while (!current->has_MediaBox() && current->has_Parent()) {
445 current = (SkPdfPageObjectDictionary*)current->Parent(this);
446 }
447 if (current) {
448 return current->MediaBox(this);
449 }
450 return SkRect::MakeEmpty();
451}
452
453// TODO(edisonn): stream or array ... ? for now only array
edisonn@google.com3aa35552013-08-14 18:26:20 +0000454SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfPage(int page,
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000455 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000456 if (fPages[page]->isContentsAStream(this)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000457 return tokenizerOfStream(fPages[page]->getContentsAsStream(this), allocator);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000458 } else {
459 // TODO(edisonn): NYI, we need to concatenate all streams in the array or make the tokenizer smart
460 // so we don't allocate new memory
461 return NULL;
462 }
463}
464
edisonn@google.com3aa35552013-08-14 18:26:20 +0000465SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfStream(SkPdfNativeObject* stream,
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000466 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000467 if (stream == NULL) {
468 return NULL;
469 }
470
edisonn@google.com33f11b62013-08-14 21:35:27 +0000471 return new SkPdfNativeTokenizer(stream, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000472}
473
474// TODO(edisonn): NYI
edisonn@google.com3aa35552013-08-14 18:26:20 +0000475SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfBuffer(const unsigned char* buffer, size_t len,
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000476 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000477 // warning does not track two calls in the same buffer! the buffer is updated!
478 // make a clean copy if needed!
edisonn@google.com33f11b62013-08-14 21:35:27 +0000479 return new SkPdfNativeTokenizer(buffer, len, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000480}
481
edisonn@google.com3aa35552013-08-14 18:26:20 +0000482size_t SkPdfNativeDoc::objects() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000483 return fObjects.count();
484}
485
edisonn@google.com3aa35552013-08-14 18:26:20 +0000486SkPdfNativeObject* SkPdfNativeDoc::object(int i) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000487 SkASSERT(!(i < 0 || i > fObjects.count()));
488
489 if (i < 0 || i > fObjects.count()) {
490 return NULL;
491 }
492
493 if (fObjects[i].fObj == NULL) {
494 // TODO(edisonn): when we read the cross reference sections, store the start of the next object
495 // and fill fOffsetEnd
496 fObjects[i].fObj = readObject(i);
497 }
498
499 return fObjects[i].fObj;
500}
501
edisonn@google.com3aa35552013-08-14 18:26:20 +0000502const SkPdfMapper* SkPdfNativeDoc::mapper() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000503 return fMapper;
504}
505
edisonn@google.com3aa35552013-08-14 18:26:20 +0000506SkPdfReal* SkPdfNativeDoc::createReal(double value) const {
507 SkPdfNativeObject* obj = fAllocator->allocObject();
508 SkPdfNativeObject::makeReal(value, obj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000509 return (SkPdfReal*)obj;
510}
511
edisonn@google.com3aa35552013-08-14 18:26:20 +0000512SkPdfInteger* SkPdfNativeDoc::createInteger(int value) const {
513 SkPdfNativeObject* obj = fAllocator->allocObject();
514 SkPdfNativeObject::makeInteger(value, obj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000515 return (SkPdfInteger*)obj;
516}
517
edisonn@google.com3aa35552013-08-14 18:26:20 +0000518SkPdfString* SkPdfNativeDoc::createString(const unsigned char* sz, size_t len) const {
519 SkPdfNativeObject* obj = fAllocator->allocObject();
520 SkPdfNativeObject::makeString(sz, len, obj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000521 return (SkPdfString*)obj;
522}
523
edisonn@google.com3aa35552013-08-14 18:26:20 +0000524SkPdfAllocator* SkPdfNativeDoc::allocator() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000525 return fAllocator;
526}
527
edisonn@google.com571c70b2013-07-10 17:09:50 +0000528// TODO(edisonn): fix infinite loop if ref to itself!
529// TODO(edisonn): perf, fix refs at load, and resolve will simply return fResolvedReference?
edisonn@google.com3aa35552013-08-14 18:26:20 +0000530SkPdfNativeObject* SkPdfNativeDoc::resolveReference(SkPdfNativeObject* ref) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000531 if (ref && ref->isReference()) {
532 int id = ref->referenceId();
533 // TODO(edisonn): generation/updates not supported now
534 //int gen = ref->referenceGeneration();
535
edisonn@google.com641cce92013-07-30 12:09:14 +0000536 // TODO(edisonn): verify id and gen expected
537 if (id < 0 || id >= fObjects.count()) {
538 // TODO(edisonn): report error/warning
edisonn@google.com571c70b2013-07-10 17:09:50 +0000539 return NULL;
540 }
541
edisonn@google.com571c70b2013-07-10 17:09:50 +0000542 if (fObjects[id].fResolvedReference != NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +0000543
544#ifdef PDF_TRACE
545 printf("\nresolve(%s) = %s\n", ref->toString(0).c_str(), fObjects[id].fResolvedReference->toString(0, ref->toString().size() + 13).c_str());
546#endif
547
edisonn@google.com571c70b2013-07-10 17:09:50 +0000548 return fObjects[id].fResolvedReference;
549 }
550
551 if (fObjects[id].fObj == NULL) {
552 fObjects[id].fObj = readObject(id);
553 }
554
555 if (fObjects[id].fResolvedReference == NULL) {
556 if (!fObjects[id].fObj->isReference()) {
557 fObjects[id].fResolvedReference = fObjects[id].fObj;
558 } else {
559 fObjects[id].fResolvedReference = resolveReference(fObjects[id].fObj);
560 }
561 }
562
edisonn@google.com276fed92013-08-01 21:20:47 +0000563#ifdef PDF_TRACE
564 printf("\nresolve(%s) = %s\n", ref->toString(0).c_str(), fObjects[id].fResolvedReference->toString(0, ref->toString().size() + 13).c_str());
565#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +0000566 return fObjects[id].fResolvedReference;
567 }
edisonn@google.com276fed92013-08-01 21:20:47 +0000568
edisonn@google.com571c70b2013-07-10 17:09:50 +0000569 // TODO(edisonn): fix the mess with const, probably we need to remove it pretty much everywhere
edisonn@google.com3aa35552013-08-14 18:26:20 +0000570 return (SkPdfNativeObject*)ref;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000571}
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000572
edisonn@google.com3aa35552013-08-14 18:26:20 +0000573size_t SkPdfNativeDoc::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000574 return fAllocator->bytesUsed() +
575 fContentLength +
576 fObjects.count() * sizeof(PublicObjectEntry) +
577 fPages.count() * sizeof(SkPdfPageObjectDictionary*) +
578 sizeof(*this);
579}