blob: 2e7e670ce93b09afc436ca4662a2694ead86ec1b [file] [log] [blame]
edisonn@google.com3aac1f92013-07-02 22:42:53 +00001#include "SkNativeParsedPDF.h"
edisonn@google.com571c70b2013-07-10 17:09:50 +00002#include "SkPdfNativeTokenizer.h"
3#include "SkPdfBasics.h"
edisonn@google.com571c70b2013-07-10 17:09:50 +00004#include "SkPdfObject.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +00005
edisonn@google.com571c70b2013-07-10 17:09:50 +00006#include <stdio.h>
7#include <string.h>
8#include <sys/types.h>
9#include <sys/stat.h>
edisonn@google.com3aac1f92013-07-02 22:42:53 +000010
edisonn@google.com571c70b2013-07-10 17:09:50 +000011#include "SkPdfFileTrailerDictionary_autogen.h"
12#include "SkPdfCatalogDictionary_autogen.h"
13#include "SkPdfPageObjectDictionary_autogen.h"
14#include "SkPdfPageTreeNodeDictionary_autogen.h"
15#include "SkPdfMapper_autogen.h"
16
edisonn@google.com147adb12013-07-24 15:56:19 +000017#include "SkStream.h"
edisonn@google.com571c70b2013-07-10 17:09:50 +000018
19
edisonn@google.coma3356fc2013-07-10 18:20:06 +000020static long getFileSize(const char* filename)
edisonn@google.com571c70b2013-07-10 17:09:50 +000021{
22 struct stat stat_buf;
23 int rc = stat(filename, &stat_buf);
edisonn@google.coma3356fc2013-07-10 18:20:06 +000024 return rc == 0 ? (long)stat_buf.st_size : -1;
edisonn@google.com3aac1f92013-07-02 22:42:53 +000025}
26
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000027static const unsigned char* lineHome(const unsigned char* start, const unsigned char* current) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000028 while (current > start && !isPdfEOL(*(current - 1))) {
29 current--;
30 }
31 return current;
32}
33
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000034static const unsigned char* previousLineHome(const unsigned char* start, const unsigned char* current) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000035 if (current > start && isPdfEOL(*(current - 1))) {
36 current--;
37 }
38
39 // allows CR+LF, LF+CR but not two CR+CR or LF+LF
40 if (current > start && isPdfEOL(*(current - 1)) && *current != *(current - 1)) {
41 current--;
42 }
43
44 while (current > start && !isPdfEOL(*(current - 1))) {
45 current--;
46 }
47
48 return current;
49}
50
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000051static const unsigned char* ignoreLine(const unsigned char* current, const unsigned char* end) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000052 while (current < end && !isPdfEOL(*current)) {
53 current++;
54 }
55 current++;
56 if (current < end && isPdfEOL(*current) && *current != *(current - 1)) {
57 current++;
58 }
59 return current;
60}
61
edisonn@google.com222382b2013-07-10 22:33:10 +000062SkNativeParsedPDF* gDoc = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +000063
64// TODO(edisonn): NYI
65// TODO(edisonn): 3 constructuctors from URL, from stream, from file ...
66// TODO(edisonn): write one that accepts errors in the file and ignores/fixis them
67// TODO(edisonn): testing:
68// 1) run on a lot of file
69// 2) recoverable corupt file: remove endobj, endsteam, remove other keywords, use other white spaces, insert comments randomly, ...
70// 3) irrecoverable corrupt file
edisonn@google.com147adb12013-07-24 15:56:19 +000071
72SkNativeParsedPDF::SkNativeParsedPDF(SkStream* stream)
73 : fAllocator(new SkPdfAllocator())
74 , fFileContent(NULL)
75 , fContentLength(0)
76 , fRootCatalogRef(NULL)
77 , fRootCatalog(NULL) {
78 size_t size = stream->getLength();
79 void* ptr = sk_malloc_throw(size);
80 stream->read(ptr, size);
81
82 init(ptr, size);
83}
84
edisonn@google.com432640a2013-07-10 22:53:40 +000085SkNativeParsedPDF::SkNativeParsedPDF(const char* path)
86 : fAllocator(new SkPdfAllocator())
edisonn@google.com147adb12013-07-24 15:56:19 +000087 , fFileContent(NULL)
88 , fContentLength(0)
edisonn@google.com432640a2013-07-10 22:53:40 +000089 , fRootCatalogRef(NULL)
90 , fRootCatalog(NULL) {
edisonn@google.com222382b2013-07-10 22:33:10 +000091 gDoc = this;
edisonn@google.com571c70b2013-07-10 17:09:50 +000092 FILE* file = fopen(path, "r");
edisonn@google.come57c62d2013-08-07 18:04:15 +000093 // TODO(edisonn): put this in a function that can return NULL
94 if (file) {
95 size_t size = getFileSize(path);
96 void* content = sk_malloc_throw(size);
97 bool ok = (0 != fread(content, size, 1, file));
98 fclose(file);
99 if (!ok) {
100 sk_free(content);
101 // TODO(edisonn): report read error
102 // TODO(edisonn): not nice to return like this from constructor, create a static
103 // function that can report NULL for failures.
104 return; // Doc will have 0 pages
105 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000106
edisonn@google.come57c62d2013-08-07 18:04:15 +0000107 init(content, size);
edisonn@google.com620edc52013-07-18 13:03:03 +0000108 }
edisonn@google.com147adb12013-07-24 15:56:19 +0000109}
110
111void SkNativeParsedPDF::init(const void* bytes, size_t length) {
112 fFileContent = (const unsigned char*)bytes;
113 fContentLength = length;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000114 const unsigned char* eofLine = lineHome(fFileContent, fFileContent + fContentLength - 1);
115 const unsigned char* xrefByteOffsetLine = previousLineHome(fFileContent, eofLine);
116 const unsigned char* xrefstartKeywordLine = previousLineHome(fFileContent, xrefByteOffsetLine);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000117
118 if (strcmp((char*)xrefstartKeywordLine, "startxref") != 0) {
119 // TODO(edisonn): report/issue
120 }
121
122 long xrefByteOffset = atol((const char*)xrefByteOffsetLine);
123
124 bool storeCatalog = true;
125 while (xrefByteOffset >= 0) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000126 const unsigned char* trailerStart = readCrossReferenceSection(fFileContent + xrefByteOffset, xrefstartKeywordLine);
edisonn@google.com24cdf132013-07-30 16:06:12 +0000127 xrefByteOffset = -1;
128 if (trailerStart < xrefstartKeywordLine) {
129 readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog, &xrefByteOffset, false);
130 storeCatalog = false;
131 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000132 }
133
134 // TODO(edisonn): warn/error expect fObjects[fRefCatalogId].fGeneration == fRefCatalogGeneration
135 // TODO(edisonn): security, verify that SkPdfCatalogDictionary is indeed using mapper
136 // load catalog
edisonn@google.com571c70b2013-07-10 17:09:50 +0000137
edisonn@google.com432640a2013-07-10 22:53:40 +0000138 if (fRootCatalogRef) {
139 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
edisonn@google.com8bad7372013-07-10 23:36:56 +0000140 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
141 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
142 if (tree && tree->isDictionary() && tree->valid()) {
143 fillPages(tree);
144 }
145 }
edisonn@google.com432640a2013-07-10 22:53:40 +0000146 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000147
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000148 // TODO(edisonn): clean up this doc, or better, let the caller call again and build a new doc
149 // caller should be a static function.
150 if (pages() == 0) {
151 loadWithoutXRef();
152 }
153
edisonn@google.com8bad7372013-07-10 23:36:56 +0000154 // TODO(edisonn): corrupted pdf, read it from beginning and rebuild (xref, trailer, or just reall all objects)
155 // 0 pages
156
edisonn@google.com571c70b2013-07-10 17:09:50 +0000157 // now actually read all objects if we want, or do it lazyly
158 // and resolve references?... or not ...
159}
160
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000161void SkNativeParsedPDF::loadWithoutXRef() {
162 const unsigned char* current = fFileContent;
163 const unsigned char* end = fFileContent + fContentLength;
164
165 // TODO(edisonn): read pdf version
166 current = ignoreLine(current, end);
167
168 current = skipPdfWhiteSpaces(0, current, end);
169 while (current < end) {
170 SkPdfObject token;
171 current = nextObject(0, current, end, &token, NULL, NULL);
172 if (token.isInteger()) {
173 int id = (int)token.intValue();
174
175 token.reset();
176 current = nextObject(0, current, end, &token, NULL, NULL);
177 // int generation = (int)token.intValue(); // TODO(edisonn): ignored for now
178
179 token.reset();
180 current = nextObject(0, current, end, &token, NULL, NULL);
181 // TODO(edisonn): must be obj, return error if not? ignore ?
182 if (!token.isKeyword("obj")) {
183 continue;
184 }
185
186 while (fObjects.count() < id + 1) {
187 reset(fObjects.append());
188 }
189
190 fObjects[id].fOffset = current - fFileContent;
191
192 SkPdfObject* obj = fAllocator->allocObject();
193 current = nextObject(0, current, end, obj, fAllocator, this);
194
195 fObjects[id].fResolvedReference = obj;
196 fObjects[id].fObj = obj;
197
198 // set objects
199 } else if (token.isKeyword("trailer")) {
200 long dummy;
201 current = readTrailer(current, end, true, &dummy, true);
202 } else if (token.isKeyword("startxref")) {
203 token.reset();
204 current = nextObject(0, current, end, &token, NULL, NULL); // ignore
205 }
206
207 current = skipPdfWhiteSpaces(0, current, end);
208 }
209
210 if (fRootCatalogRef) {
211 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
212 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
213 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
214 if (tree && tree->isDictionary() && tree->valid()) {
215 fillPages(tree);
216 }
217 }
218 }
219
220}
221
edisonn@google.com571c70b2013-07-10 17:09:50 +0000222// TODO(edisonn): NYI
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000223SkNativeParsedPDF::~SkNativeParsedPDF() {
edisonn@google.com147adb12013-07-24 15:56:19 +0000224 sk_free((void*)fFileContent);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000225 delete fAllocator;
226}
227
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000228const unsigned char* SkNativeParsedPDF::readCrossReferenceSection(const unsigned char* xrefStart, const unsigned char* trailerEnd) {
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000229 SkPdfObject xref;
230 const unsigned char* current = nextObject(0, xrefStart, trailerEnd, &xref, NULL, NULL);
231
232 if (!xref.isKeyword("xref")) {
233 return trailerEnd;
234 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000235
236 SkPdfObject token;
237 while (current < trailerEnd) {
238 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000239 const unsigned char* previous = current;
240 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000241 if (!token.isInteger()) {
242 return previous;
243 }
244
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000245 int startId = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000246 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000247 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000248
249 if (!token.isInteger()) {
250 // TODO(edisonn): report/warning
251 return current;
252 }
253
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000254 int entries = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000255
256 for (int i = 0; i < entries; i++) {
257 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000258 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000259 if (!token.isInteger()) {
260 // TODO(edisonn): report/warning
261 return current;
262 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000263 int offset = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000264
265 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000266 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000267 if (!token.isInteger()) {
268 // TODO(edisonn): report/warning
269 return current;
270 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000271 int generation = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000272
273 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000274 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.come878e722013-07-29 19:10:58 +0000275 if (!token.isKeyword() || token.lenstr() != 1 || (*token.c_str() != 'f' && *token.c_str() != 'n')) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000276 // TODO(edisonn): report/warning
277 return current;
278 }
279
280 addCrossSectionInfo(startId + i, generation, offset, *token.c_str() == 'f');
281 }
282 }
283 // TODO(edisonn): it should never get here? there is no trailer?
284 return current;
285}
286
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000287const unsigned char* SkNativeParsedPDF::readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog, long* prev, bool skipKeyword) {
288 *prev = -1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000289
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000290 const unsigned char* current = trailerStart;
291 if (!skipKeyword) {
292 SkPdfObject trailerKeyword;
293 // TODO(edisonn): use null allocator, and let it just fail if memory
294 // needs allocated (but no crash)!
295 current = nextObject(0, current, trailerEnd, &trailerKeyword, NULL, NULL);
296
297 if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.lenstr() ||
298 strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
299 // TODO(edisonn): report warning, rebuild trailer from objects.
300 return current;
301 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000302 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000303
304 SkPdfObject token;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000305 current = nextObject(0, current, trailerEnd, &token, fAllocator, NULL);
edisonn@google.com432640a2013-07-10 22:53:40 +0000306 if (!token.isDictionary()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000307 return current;
edisonn@google.com432640a2013-07-10 22:53:40 +0000308 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000309 SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token;
edisonn@google.com432640a2013-07-10 22:53:40 +0000310 if (!trailer->valid()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000311 return current;
edisonn@google.com432640a2013-07-10 22:53:40 +0000312 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000313
314 if (storeCatalog) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000315 SkPdfObject* ref = trailer->Root(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000316 if (ref == NULL || !ref->isReference()) {
317 // TODO(edisonn): oops, we have to fix the corrup pdf file
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000318 return current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000319 }
320 fRootCatalogRef = ref;
321 }
322
323 if (trailer->has_Prev()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000324 *prev = (long)trailer->Prev(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000325 }
326
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000327 return current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000328}
329
330void SkNativeParsedPDF::addCrossSectionInfo(int id, int generation, int offset, bool isFreed) {
331 // TODO(edisonn): security here
332 while (fObjects.count() < id + 1) {
333 reset(fObjects.append());
334 }
335
336 fObjects[id].fOffset = offset;
337 fObjects[id].fObj = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000338 fObjects[id].fResolvedReference = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000339}
340
edisonn@google.com951d6532013-07-10 23:17:31 +0000341SkPdfObject* SkNativeParsedPDF::readObject(int id/*, int expectedGeneration*/) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000342 long startOffset = fObjects[id].fOffset;
343 //long endOffset = fObjects[id].fOffsetEnd;
344 // TODO(edisonn): use hinted endOffset
345 // TODO(edisonn): current implementation will result in a lot of memory usage
346 // to decrease memory usage, we wither need to be smart and know where objects end, and we will
347 // alocate only the chancks needed, or the tokenizer will not make copies, but then it needs to
348 // cache the results so it does not go twice on the same buffer
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000349 const unsigned char* current = fFileContent + startOffset;
350 const unsigned char* end = fFileContent + fContentLength;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000351
edisonn@google.com951d6532013-07-10 23:17:31 +0000352 SkPdfNativeTokenizer tokenizer(current, end - current, fMapper, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000353
354 SkPdfObject idObj;
355 SkPdfObject generationObj;
356 SkPdfObject objKeyword;
357 SkPdfObject* dict = fAllocator->allocObject();
358
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000359 current = nextObject(0, current, end, &idObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000360 if (current >= end) {
361 // TODO(edisonn): report warning/error
362 return NULL;
363 }
364
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000365 current = nextObject(0, current, end, &generationObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000366 if (current >= end) {
367 // TODO(edisonn): report warning/error
368 return NULL;
369 }
370
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000371 current = nextObject(0, current, end, &objKeyword, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000372 if (current >= end) {
373 // TODO(edisonn): report warning/error
374 return NULL;
375 }
376
377 if (!idObj.isInteger() || !generationObj.isInteger() || id != idObj.intValue()/* || generation != generationObj.intValue()*/) {
378 // TODO(edisonn): report warning/error
379 }
380
381 if (!objKeyword.isKeyword() || strcmp(objKeyword.c_str(), "obj") != 0) {
382 // TODO(edisonn): report warning/error
383 }
384
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000385 current = nextObject(1, current, end, dict, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000386
387 // TODO(edisonn): report warning/error - verify last token is endobj
388
389 return dict;
390}
391
392void SkNativeParsedPDF::fillPages(SkPdfPageTreeNodeDictionary* tree) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000393 SkPdfArray* kids = tree->Kids(this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000394 if (kids == NULL) {
395 *fPages.append() = (SkPdfPageObjectDictionary*)tree;
396 return;
397 }
398
399 int cnt = kids->size();
400 for (int i = 0; i < cnt; i++) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000401 SkPdfObject* obj = resolveReference(kids->objAtAIndex(i));
edisonn@google.com571c70b2013-07-10 17:09:50 +0000402 if (fMapper->mapPageObjectDictionary(obj) != kPageObjectDictionary_SkPdfObjectType) {
403 *fPages.append() = (SkPdfPageObjectDictionary*)obj;
404 } else {
405 // TODO(edisonn): verify that it is a page tree indeed
406 fillPages((SkPdfPageTreeNodeDictionary*)obj);
407 }
408 }
409}
410
411int SkNativeParsedPDF::pages() const {
412 return fPages.count();
413}
414
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000415SkPdfPageObjectDictionary* SkNativeParsedPDF::page(int page) {
416 SkASSERT(page >= 0 && page < fPages.count());
417 return fPages[page];
418}
419
420
edisonn@google.com571c70b2013-07-10 17:09:50 +0000421SkPdfResourceDictionary* SkNativeParsedPDF::pageResources(int page) {
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000422 SkASSERT(page >= 0 && page < fPages.count());
edisonn@google.com571c70b2013-07-10 17:09:50 +0000423 return fPages[page]->Resources(this);
424}
425
426// TODO(edisonn): Partial implemented. Move the logics directly in the code generator for inheritable and default value?
edisonn@google.com951d6532013-07-10 23:17:31 +0000427SkRect SkNativeParsedPDF::MediaBox(int page) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000428 SkPdfPageObjectDictionary* current = fPages[page];
429 while (!current->has_MediaBox() && current->has_Parent()) {
430 current = (SkPdfPageObjectDictionary*)current->Parent(this);
431 }
432 if (current) {
433 return current->MediaBox(this);
434 }
435 return SkRect::MakeEmpty();
436}
437
438// TODO(edisonn): stream or array ... ? for now only array
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000439SkPdfNativeTokenizer* SkNativeParsedPDF::tokenizerOfPage(int page,
440 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000441 if (fPages[page]->isContentsAStream(this)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000442 return tokenizerOfStream(fPages[page]->getContentsAsStream(this), allocator);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000443 } else {
444 // TODO(edisonn): NYI, we need to concatenate all streams in the array or make the tokenizer smart
445 // so we don't allocate new memory
446 return NULL;
447 }
448}
449
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000450SkPdfNativeTokenizer* SkNativeParsedPDF::tokenizerOfStream(SkPdfObject* stream,
451 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000452 if (stream == NULL) {
453 return NULL;
454 }
455
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000456 return new SkPdfNativeTokenizer(stream, fMapper, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000457}
458
459// TODO(edisonn): NYI
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000460SkPdfNativeTokenizer* SkNativeParsedPDF::tokenizerOfBuffer(const unsigned char* buffer, size_t len,
461 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000462 // warning does not track two calls in the same buffer! the buffer is updated!
463 // make a clean copy if needed!
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000464 return new SkPdfNativeTokenizer(buffer, len, fMapper, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000465}
466
467size_t SkNativeParsedPDF::objects() const {
468 return fObjects.count();
469}
470
471SkPdfObject* SkNativeParsedPDF::object(int i) {
472 SkASSERT(!(i < 0 || i > fObjects.count()));
473
474 if (i < 0 || i > fObjects.count()) {
475 return NULL;
476 }
477
478 if (fObjects[i].fObj == NULL) {
479 // TODO(edisonn): when we read the cross reference sections, store the start of the next object
480 // and fill fOffsetEnd
481 fObjects[i].fObj = readObject(i);
482 }
483
484 return fObjects[i].fObj;
485}
486
487const SkPdfMapper* SkNativeParsedPDF::mapper() const {
488 return fMapper;
489}
490
491SkPdfReal* SkNativeParsedPDF::createReal(double value) const {
492 SkPdfObject* obj = fAllocator->allocObject();
493 SkPdfObject::makeReal(value, obj);
494 return (SkPdfReal*)obj;
495}
496
497SkPdfInteger* SkNativeParsedPDF::createInteger(int value) const {
498 SkPdfObject* obj = fAllocator->allocObject();
499 SkPdfObject::makeInteger(value, obj);
500 return (SkPdfInteger*)obj;
501}
502
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000503SkPdfString* SkNativeParsedPDF::createString(const unsigned char* sz, size_t len) const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000504 SkPdfObject* obj = fAllocator->allocObject();
505 SkPdfObject::makeString(sz, len, obj);
506 return (SkPdfString*)obj;
507}
508
edisonn@google.com571c70b2013-07-10 17:09:50 +0000509SkPdfAllocator* SkNativeParsedPDF::allocator() const {
510 return fAllocator;
511}
512
edisonn@google.com571c70b2013-07-10 17:09:50 +0000513// TODO(edisonn): fix infinite loop if ref to itself!
514// TODO(edisonn): perf, fix refs at load, and resolve will simply return fResolvedReference?
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000515SkPdfObject* SkNativeParsedPDF::resolveReference(SkPdfObject* ref) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000516 if (ref && ref->isReference()) {
517 int id = ref->referenceId();
518 // TODO(edisonn): generation/updates not supported now
519 //int gen = ref->referenceGeneration();
520
edisonn@google.com641cce92013-07-30 12:09:14 +0000521 // TODO(edisonn): verify id and gen expected
522 if (id < 0 || id >= fObjects.count()) {
523 // TODO(edisonn): report error/warning
edisonn@google.com571c70b2013-07-10 17:09:50 +0000524 return NULL;
525 }
526
edisonn@google.com571c70b2013-07-10 17:09:50 +0000527 if (fObjects[id].fResolvedReference != NULL) {
edisonn@google.com276fed92013-08-01 21:20:47 +0000528
529#ifdef PDF_TRACE
530 printf("\nresolve(%s) = %s\n", ref->toString(0).c_str(), fObjects[id].fResolvedReference->toString(0, ref->toString().size() + 13).c_str());
531#endif
532
edisonn@google.com571c70b2013-07-10 17:09:50 +0000533 return fObjects[id].fResolvedReference;
534 }
535
536 if (fObjects[id].fObj == NULL) {
537 fObjects[id].fObj = readObject(id);
538 }
539
540 if (fObjects[id].fResolvedReference == NULL) {
541 if (!fObjects[id].fObj->isReference()) {
542 fObjects[id].fResolvedReference = fObjects[id].fObj;
543 } else {
544 fObjects[id].fResolvedReference = resolveReference(fObjects[id].fObj);
545 }
546 }
547
edisonn@google.com276fed92013-08-01 21:20:47 +0000548#ifdef PDF_TRACE
549 printf("\nresolve(%s) = %s\n", ref->toString(0).c_str(), fObjects[id].fResolvedReference->toString(0, ref->toString().size() + 13).c_str());
550#endif
edisonn@google.com571c70b2013-07-10 17:09:50 +0000551 return fObjects[id].fResolvedReference;
552 }
edisonn@google.com276fed92013-08-01 21:20:47 +0000553
554
555
edisonn@google.com571c70b2013-07-10 17:09:50 +0000556 // TODO(edisonn): fix the mess with const, probably we need to remove it pretty much everywhere
557 return (SkPdfObject*)ref;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000558}
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000559
edisonn@google.com7b328fd2013-07-11 12:53:06 +0000560size_t SkNativeParsedPDF::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000561 return fAllocator->bytesUsed() +
562 fContentLength +
563 fObjects.count() * sizeof(PublicObjectEntry) +
564 fPages.count() * sizeof(SkPdfPageObjectDictionary*) +
565 sizeof(*this);
566}