blob: 8892ee2643c6236c24ed34b0c19cb2fdae1fd6dc [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.com147adb12013-07-24 15:56:19 +000093 size_t size = getFileSize(path);
94 void* content = sk_malloc_throw(size);
95 bool ok = (0 != fread(content, size, 1, file));
edisonn@google.com571c70b2013-07-10 17:09:50 +000096 fclose(file);
97 file = NULL;
98
edisonn@google.com620edc52013-07-18 13:03:03 +000099 if (!ok) {
edisonn@google.com147adb12013-07-24 15:56:19 +0000100 sk_free(content);
edisonn@google.com620edc52013-07-18 13:03:03 +0000101 // TODO(edisonn): report read error
edisonn@google.com147adb12013-07-24 15:56:19 +0000102 // TODO(edisonn): not nice to return like this from constructor, create a static
103 // function that can report NULL for failures.
edisonn@google.com620edc52013-07-18 13:03:03 +0000104 return; // Doc will have 0 pages
105 }
106
edisonn@google.com147adb12013-07-24 15:56:19 +0000107 init(content, size);
108}
109
110void SkNativeParsedPDF::init(const void* bytes, size_t length) {
111 fFileContent = (const unsigned char*)bytes;
112 fContentLength = length;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000113 const unsigned char* eofLine = lineHome(fFileContent, fFileContent + fContentLength - 1);
114 const unsigned char* xrefByteOffsetLine = previousLineHome(fFileContent, eofLine);
115 const unsigned char* xrefstartKeywordLine = previousLineHome(fFileContent, xrefByteOffsetLine);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000116
117 if (strcmp((char*)xrefstartKeywordLine, "startxref") != 0) {
118 // TODO(edisonn): report/issue
119 }
120
121 long xrefByteOffset = atol((const char*)xrefByteOffsetLine);
122
123 bool storeCatalog = true;
124 while (xrefByteOffset >= 0) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000125 const unsigned char* trailerStart = readCrossReferenceSection(fFileContent + xrefByteOffset, xrefstartKeywordLine);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000126 xrefByteOffset = readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog);
127 storeCatalog = false;
128 }
129
130 // TODO(edisonn): warn/error expect fObjects[fRefCatalogId].fGeneration == fRefCatalogGeneration
131 // TODO(edisonn): security, verify that SkPdfCatalogDictionary is indeed using mapper
132 // load catalog
edisonn@google.com571c70b2013-07-10 17:09:50 +0000133
edisonn@google.com432640a2013-07-10 22:53:40 +0000134 if (fRootCatalogRef) {
135 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
edisonn@google.com8bad7372013-07-10 23:36:56 +0000136 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
137 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
138 if (tree && tree->isDictionary() && tree->valid()) {
139 fillPages(tree);
140 }
141 }
edisonn@google.com432640a2013-07-10 22:53:40 +0000142 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000143
edisonn@google.com8bad7372013-07-10 23:36:56 +0000144 // TODO(edisonn): corrupted pdf, read it from beginning and rebuild (xref, trailer, or just reall all objects)
145 // 0 pages
146
edisonn@google.com571c70b2013-07-10 17:09:50 +0000147 // now actually read all objects if we want, or do it lazyly
148 // and resolve references?... or not ...
149}
150
151// TODO(edisonn): NYI
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000152SkNativeParsedPDF::~SkNativeParsedPDF() {
edisonn@google.com147adb12013-07-24 15:56:19 +0000153 sk_free((void*)fFileContent);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000154 delete fAllocator;
155}
156
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000157const unsigned char* SkNativeParsedPDF::readCrossReferenceSection(const unsigned char* xrefStart, const unsigned char* trailerEnd) {
158 const unsigned char* current = ignoreLine(xrefStart, trailerEnd); // TODO(edisonn): verify next keyord is "xref", use nextObject here
edisonn@google.com571c70b2013-07-10 17:09:50 +0000159
160 SkPdfObject token;
161 while (current < trailerEnd) {
162 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000163 const unsigned char* previous = current;
164 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000165 if (!token.isInteger()) {
166 return previous;
167 }
168
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000169 int startId = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000170 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000171 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000172
173 if (!token.isInteger()) {
174 // TODO(edisonn): report/warning
175 return current;
176 }
177
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000178 int entries = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000179
180 for (int i = 0; i < entries; i++) {
181 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000182 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000183 if (!token.isInteger()) {
184 // TODO(edisonn): report/warning
185 return current;
186 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000187 int offset = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000188
189 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000190 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000191 if (!token.isInteger()) {
192 // TODO(edisonn): report/warning
193 return current;
194 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000195 int generation = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000196
197 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000198 current = nextObject(0, current, trailerEnd, &token, NULL, NULL);
edisonn@google.come878e722013-07-29 19:10:58 +0000199 if (!token.isKeyword() || token.lenstr() != 1 || (*token.c_str() != 'f' && *token.c_str() != 'n')) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000200 // TODO(edisonn): report/warning
201 return current;
202 }
203
204 addCrossSectionInfo(startId + i, generation, offset, *token.c_str() == 'f');
205 }
206 }
207 // TODO(edisonn): it should never get here? there is no trailer?
208 return current;
209}
210
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000211long SkNativeParsedPDF::readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog) {
212 SkPdfObject trailerKeyword;
213 // TODO(edisonn): use null allocator, and let it just fail if memory
214 // needs allocated (but no crash)!
215 const unsigned char* current =
edisonn@google.com1f080162013-07-23 21:05:49 +0000216 nextObject(0, trailerStart, trailerEnd, &trailerKeyword, NULL, NULL);
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000217
edisonn@google.come878e722013-07-29 19:10:58 +0000218 if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.lenstr() ||
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000219 strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
220 // TODO(edisonn): report warning, rebuild trailer from objects.
221 return -1;
222 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000223
224 SkPdfObject token;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000225 current = nextObject(0, current, trailerEnd, &token, fAllocator, NULL);
edisonn@google.com432640a2013-07-10 22:53:40 +0000226 if (!token.isDictionary()) {
227 return -1;
228 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000229 SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token;
edisonn@google.com432640a2013-07-10 22:53:40 +0000230 if (!trailer->valid()) {
231 return -1;
232 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000233
234 if (storeCatalog) {
235 const SkPdfObject* ref = trailer->Root(NULL);
236 if (ref == NULL || !ref->isReference()) {
237 // TODO(edisonn): oops, we have to fix the corrup pdf file
238 return -1;
239 }
240 fRootCatalogRef = ref;
241 }
242
243 if (trailer->has_Prev()) {
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000244 return (long)trailer->Prev(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000245 }
246
247 return -1;
248}
249
250void SkNativeParsedPDF::addCrossSectionInfo(int id, int generation, int offset, bool isFreed) {
251 // TODO(edisonn): security here
252 while (fObjects.count() < id + 1) {
253 reset(fObjects.append());
254 }
255
256 fObjects[id].fOffset = offset;
257 fObjects[id].fObj = NULL;
258}
259
edisonn@google.com951d6532013-07-10 23:17:31 +0000260SkPdfObject* SkNativeParsedPDF::readObject(int id/*, int expectedGeneration*/) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000261 long startOffset = fObjects[id].fOffset;
262 //long endOffset = fObjects[id].fOffsetEnd;
263 // TODO(edisonn): use hinted endOffset
264 // TODO(edisonn): current implementation will result in a lot of memory usage
265 // to decrease memory usage, we wither need to be smart and know where objects end, and we will
266 // alocate only the chancks needed, or the tokenizer will not make copies, but then it needs to
267 // cache the results so it does not go twice on the same buffer
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000268 const unsigned char* current = fFileContent + startOffset;
269 const unsigned char* end = fFileContent + fContentLength;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000270
edisonn@google.com951d6532013-07-10 23:17:31 +0000271 SkPdfNativeTokenizer tokenizer(current, end - current, fMapper, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000272
273 SkPdfObject idObj;
274 SkPdfObject generationObj;
275 SkPdfObject objKeyword;
276 SkPdfObject* dict = fAllocator->allocObject();
277
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000278 current = nextObject(0, current, end, &idObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000279 if (current >= end) {
280 // TODO(edisonn): report warning/error
281 return NULL;
282 }
283
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000284 current = nextObject(0, current, end, &generationObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000285 if (current >= end) {
286 // TODO(edisonn): report warning/error
287 return NULL;
288 }
289
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000290 current = nextObject(0, current, end, &objKeyword, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000291 if (current >= end) {
292 // TODO(edisonn): report warning/error
293 return NULL;
294 }
295
296 if (!idObj.isInteger() || !generationObj.isInteger() || id != idObj.intValue()/* || generation != generationObj.intValue()*/) {
297 // TODO(edisonn): report warning/error
298 }
299
300 if (!objKeyword.isKeyword() || strcmp(objKeyword.c_str(), "obj") != 0) {
301 // TODO(edisonn): report warning/error
302 }
303
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000304 current = nextObject(1, current, end, dict, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000305
306 // TODO(edisonn): report warning/error - verify last token is endobj
307
308 return dict;
309}
310
311void SkNativeParsedPDF::fillPages(SkPdfPageTreeNodeDictionary* tree) {
312 const SkPdfArray* kids = tree->Kids(this);
313 if (kids == NULL) {
314 *fPages.append() = (SkPdfPageObjectDictionary*)tree;
315 return;
316 }
317
318 int cnt = kids->size();
319 for (int i = 0; i < cnt; i++) {
320 const SkPdfObject* obj = resolveReference(kids->objAtAIndex(i));
321 if (fMapper->mapPageObjectDictionary(obj) != kPageObjectDictionary_SkPdfObjectType) {
322 *fPages.append() = (SkPdfPageObjectDictionary*)obj;
323 } else {
324 // TODO(edisonn): verify that it is a page tree indeed
325 fillPages((SkPdfPageTreeNodeDictionary*)obj);
326 }
327 }
328}
329
330int SkNativeParsedPDF::pages() const {
331 return fPages.count();
332}
333
334SkPdfResourceDictionary* SkNativeParsedPDF::pageResources(int page) {
335 return fPages[page]->Resources(this);
336}
337
338// 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 +0000339SkRect SkNativeParsedPDF::MediaBox(int page) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000340 SkPdfPageObjectDictionary* current = fPages[page];
341 while (!current->has_MediaBox() && current->has_Parent()) {
342 current = (SkPdfPageObjectDictionary*)current->Parent(this);
343 }
344 if (current) {
345 return current->MediaBox(this);
346 }
347 return SkRect::MakeEmpty();
348}
349
350// TODO(edisonn): stream or array ... ? for now only array
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000351SkPdfNativeTokenizer* SkNativeParsedPDF::tokenizerOfPage(int page,
352 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000353 if (fPages[page]->isContentsAStream(this)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000354 return tokenizerOfStream(fPages[page]->getContentsAsStream(this), allocator);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000355 } else {
356 // TODO(edisonn): NYI, we need to concatenate all streams in the array or make the tokenizer smart
357 // so we don't allocate new memory
358 return NULL;
359 }
360}
361
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000362SkPdfNativeTokenizer* SkNativeParsedPDF::tokenizerOfStream(SkPdfObject* stream,
363 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000364 if (stream == NULL) {
365 return NULL;
366 }
367
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000368 return new SkPdfNativeTokenizer(stream, fMapper, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000369}
370
371// TODO(edisonn): NYI
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000372SkPdfNativeTokenizer* SkNativeParsedPDF::tokenizerOfBuffer(const unsigned char* buffer, size_t len,
373 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000374 // warning does not track two calls in the same buffer! the buffer is updated!
375 // make a clean copy if needed!
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000376 return new SkPdfNativeTokenizer(buffer, len, fMapper, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000377}
378
379size_t SkNativeParsedPDF::objects() const {
380 return fObjects.count();
381}
382
383SkPdfObject* SkNativeParsedPDF::object(int i) {
384 SkASSERT(!(i < 0 || i > fObjects.count()));
385
386 if (i < 0 || i > fObjects.count()) {
387 return NULL;
388 }
389
390 if (fObjects[i].fObj == NULL) {
391 // TODO(edisonn): when we read the cross reference sections, store the start of the next object
392 // and fill fOffsetEnd
393 fObjects[i].fObj = readObject(i);
394 }
395
396 return fObjects[i].fObj;
397}
398
399const SkPdfMapper* SkNativeParsedPDF::mapper() const {
400 return fMapper;
401}
402
403SkPdfReal* SkNativeParsedPDF::createReal(double value) const {
404 SkPdfObject* obj = fAllocator->allocObject();
405 SkPdfObject::makeReal(value, obj);
406 return (SkPdfReal*)obj;
407}
408
409SkPdfInteger* SkNativeParsedPDF::createInteger(int value) const {
410 SkPdfObject* obj = fAllocator->allocObject();
411 SkPdfObject::makeInteger(value, obj);
412 return (SkPdfInteger*)obj;
413}
414
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000415SkPdfString* SkNativeParsedPDF::createString(const unsigned char* sz, size_t len) const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000416 SkPdfObject* obj = fAllocator->allocObject();
417 SkPdfObject::makeString(sz, len, obj);
418 return (SkPdfString*)obj;
419}
420
edisonn@google.com571c70b2013-07-10 17:09:50 +0000421SkPdfAllocator* SkNativeParsedPDF::allocator() const {
422 return fAllocator;
423}
424
edisonn@google.com571c70b2013-07-10 17:09:50 +0000425// TODO(edisonn): fix infinite loop if ref to itself!
426// TODO(edisonn): perf, fix refs at load, and resolve will simply return fResolvedReference?
edisonn@google.com951d6532013-07-10 23:17:31 +0000427SkPdfObject* SkNativeParsedPDF::resolveReference(const SkPdfObject* ref) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000428 if (ref && ref->isReference()) {
429 int id = ref->referenceId();
430 // TODO(edisonn): generation/updates not supported now
431 //int gen = ref->referenceGeneration();
432
433 SkASSERT(!(id < 0 || id > fObjects.count()));
434
435 if (id < 0 || id > fObjects.count()) {
436 return NULL;
437 }
438
439 // TODO(edisonn): verify id and gen expected
440
441 if (fObjects[id].fResolvedReference != NULL) {
442 return fObjects[id].fResolvedReference;
443 }
444
445 if (fObjects[id].fObj == NULL) {
446 fObjects[id].fObj = readObject(id);
447 }
448
449 if (fObjects[id].fResolvedReference == NULL) {
450 if (!fObjects[id].fObj->isReference()) {
451 fObjects[id].fResolvedReference = fObjects[id].fObj;
452 } else {
453 fObjects[id].fResolvedReference = resolveReference(fObjects[id].fObj);
454 }
455 }
456
457 return fObjects[id].fResolvedReference;
458 }
459 // TODO(edisonn): fix the mess with const, probably we need to remove it pretty much everywhere
460 return (SkPdfObject*)ref;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000461}
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000462
edisonn@google.com7b328fd2013-07-11 12:53:06 +0000463size_t SkNativeParsedPDF::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000464 return fAllocator->bytesUsed() +
465 fContentLength +
466 fObjects.count() * sizeof(PublicObjectEntry) +
467 fPages.count() * sizeof(SkPdfPageObjectDictionary*) +
468 sizeof(*this);
469}