blob: 19f15d1399f0fb6e551cb71667e4c66a942292ef [file] [log] [blame]
edisonn@google.comcf2cfa12013-08-21 16:31:37 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
edisonn@google.com3aa35552013-08-14 18:26:20 +00008#include "SkPdfNativeDoc.h"
edisonn@google.com3aac1f92013-07-02 22:42:53 +00009
edisonn@google.com571c70b2013-07-10 17:09:50 +000010#include <stdio.h>
11#include <string.h>
12#include <sys/types.h>
13#include <sys/stat.h>
edisonn@google.com3aac1f92013-07-02 22:42:53 +000014
edisonn@google.comc8fda9d2013-10-09 20:23:12 +000015#include "SkPdfMapper_autogen.h"
16#include "SkPdfNativeObject.h"
17#include "SkPdfNativeTokenizer.h"
18#include "SkPdfReporter.h"
19#include "SkStream.h"
20
edisonn@google.com33f11b62013-08-14 21:35:27 +000021// TODO(edisonn): for some reason on mac these files are found here, but are found from headers
22//#include "SkPdfFileTrailerDictionary_autogen.h"
23//#include "SkPdfCatalogDictionary_autogen.h"
24//#include "SkPdfPageObjectDictionary_autogen.h"
25//#include "SkPdfPageTreeNodeDictionary_autogen.h"
26#include "SkPdfHeaders_autogen.h"
27
edisonn@google.coma3356fc2013-07-10 18:20:06 +000028static long getFileSize(const char* filename)
edisonn@google.com571c70b2013-07-10 17:09:50 +000029{
30 struct stat stat_buf;
31 int rc = stat(filename, &stat_buf);
edisonn@google.coma3356fc2013-07-10 18:20:06 +000032 return rc == 0 ? (long)stat_buf.st_size : -1;
edisonn@google.com3aac1f92013-07-02 22:42:53 +000033}
34
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000035static const unsigned char* lineHome(const unsigned char* start, const unsigned char* current) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000036 while (current > start && !isPdfEOL(*(current - 1))) {
37 current--;
38 }
39 return current;
40}
41
edisonn@google.comc8fda9d2013-10-09 20:23:12 +000042static const unsigned char* previousLineHome(const unsigned char* start,
43 const unsigned char* current) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000044 if (current > start && isPdfEOL(*(current - 1))) {
45 current--;
46 }
47
48 // allows CR+LF, LF+CR but not two CR+CR or LF+LF
49 if (current > start && isPdfEOL(*(current - 1)) && *current != *(current - 1)) {
50 current--;
51 }
52
53 while (current > start && !isPdfEOL(*(current - 1))) {
54 current--;
55 }
56
57 return current;
58}
59
edisonn@google.com2ccc3af2013-07-23 17:43:18 +000060static const unsigned char* ignoreLine(const unsigned char* current, const unsigned char* end) {
edisonn@google.com571c70b2013-07-10 17:09:50 +000061 while (current < end && !isPdfEOL(*current)) {
62 current++;
63 }
64 current++;
65 if (current < end && isPdfEOL(*current) && *current != *(current - 1)) {
66 current++;
67 }
68 return current;
69}
70
edisonn@google.com3aa35552013-08-14 18:26:20 +000071SkPdfNativeDoc* gDoc = NULL;
edisonn@google.com571c70b2013-07-10 17:09:50 +000072
edisonn@google.com3aa35552013-08-14 18:26:20 +000073SkPdfNativeDoc::SkPdfNativeDoc(SkStream* stream)
edisonn@google.com147adb12013-07-24 15:56:19 +000074 : fAllocator(new SkPdfAllocator())
75 , fFileContent(NULL)
76 , fContentLength(0)
77 , fRootCatalogRef(NULL)
78 , fRootCatalog(NULL) {
79 size_t size = stream->getLength();
80 void* ptr = sk_malloc_throw(size);
81 stream->read(ptr, size);
82
83 init(ptr, size);
84}
85
edisonn@google.com3aa35552013-08-14 18:26:20 +000086SkPdfNativeDoc::SkPdfNativeDoc(const char* path)
edisonn@google.com432640a2013-07-10 22:53:40 +000087 : fAllocator(new SkPdfAllocator())
edisonn@google.com147adb12013-07-24 15:56:19 +000088 , fFileContent(NULL)
89 , fContentLength(0)
edisonn@google.com432640a2013-07-10 22:53:40 +000090 , fRootCatalogRef(NULL)
91 , fRootCatalog(NULL) {
edisonn@google.com222382b2013-07-10 22:33:10 +000092 gDoc = this;
edisonn@google.com571c70b2013-07-10 17:09:50 +000093 FILE* file = fopen(path, "r");
edisonn@google.come57c62d2013-08-07 18:04:15 +000094 // TODO(edisonn): put this in a function that can return NULL
95 if (file) {
96 size_t size = getFileSize(path);
97 void* content = sk_malloc_throw(size);
98 bool ok = (0 != fread(content, size, 1, file));
99 fclose(file);
100 if (!ok) {
101 sk_free(content);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000102 SkPdfReport(kFatalError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue,
103 "could not read file", NULL, NULL);
edisonn@google.come57c62d2013-08-07 18:04:15 +0000104 // 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) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000121 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue,
122 "Could not find startxref", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000123 }
124
125 long xrefByteOffset = atol((const char*)xrefByteOffsetLine);
126
127 bool storeCatalog = true;
128 while (xrefByteOffset >= 0) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000129 const unsigned char* trailerStart = readCrossReferenceSection(fFileContent + xrefByteOffset,
130 xrefstartKeywordLine);
edisonn@google.com24cdf132013-07-30 16:06:12 +0000131 xrefByteOffset = -1;
132 if (trailerStart < xrefstartKeywordLine) {
133 readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog, &xrefByteOffset, false);
134 storeCatalog = false;
135 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000136 }
137
138 // TODO(edisonn): warn/error expect fObjects[fRefCatalogId].fGeneration == fRefCatalogGeneration
139 // TODO(edisonn): security, verify that SkPdfCatalogDictionary is indeed using mapper
edisonn@google.com571c70b2013-07-10 17:09:50 +0000140
edisonn@google.com432640a2013-07-10 22:53:40 +0000141 if (fRootCatalogRef) {
142 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
edisonn@google.com8bad7372013-07-10 23:36:56 +0000143 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
144 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
145 if (tree && tree->isDictionary() && tree->valid()) {
146 fillPages(tree);
147 }
148 }
edisonn@google.com432640a2013-07-10 22:53:40 +0000149 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000150
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000151 if (pages() == 0) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000152 // TODO(edisonn): probably it would be better to return NULL and make a clean document.
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000153 loadWithoutXRef();
154 }
155
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000156 // TODO(edisonn): corrupted pdf, read it from beginning and rebuild
157 // (xref, trailer, or just read all objects)
edisonn@google.com571c70b2013-07-10 17:09:50 +0000158}
159
edisonn@google.com3aa35552013-08-14 18:26:20 +0000160void SkPdfNativeDoc::loadWithoutXRef() {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000161 const unsigned char* current = fFileContent;
162 const unsigned char* end = fFileContent + fContentLength;
163
164 // TODO(edisonn): read pdf version
165 current = ignoreLine(current, end);
166
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000167 current = skipPdfWhiteSpaces(current, end);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000168 while (current < end) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000169 SkPdfNativeObject token;
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000170 current = nextObject(current, end, &token, NULL, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000171 if (token.isInteger()) {
172 int id = (int)token.intValue();
173
174 token.reset();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000175 current = nextObject(current, end, &token, NULL, NULL);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000176 // TODO(edisonn): generation ignored for now (used in pdfs with updates)
177 // int generation = (int)token.intValue();
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000178
179 token.reset();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000180 current = nextObject(current, end, &token, NULL, NULL);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000181 // TODO(edisonn): keywork must be "obj". Add ability to report error instead ignoring.
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000182 if (!token.isKeyword("obj")) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000183 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue,
184 "Could not find obj", NULL, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000185 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.com598cf5d2013-10-09 15:13:19 +0000195 current = nextObject(current, end, obj, fAllocator, this);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000196
197 fObjects[id].fResolvedReference = obj;
198 fObjects[id].fObj = obj;
edisonn@google.comaf54a512013-09-13 19:33:42 +0000199 fObjects[id].fIsReferenceResolved = true;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000200 } else if (token.isKeyword("trailer")) {
201 long dummy;
202 current = readTrailer(current, end, true, &dummy, true);
203 } else if (token.isKeyword("startxref")) {
204 token.reset();
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000205 current = nextObject(current, end, &token, NULL, NULL); // ignore startxref
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000206 }
207
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000208 current = skipPdfWhiteSpaces(current, end);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000209 }
210
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000211 // TODO(edisonn): quick hack, detect root catalog. When we implement linearized support we
212 // might not need it.
edisonn@google.com4f898b72013-08-07 21:11:57 +0000213 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
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000223 if (fRootCatalogRef) {
224 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
225 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
226 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
227 if (tree && tree->isDictionary() && tree->valid()) {
228 fillPages(tree);
229 }
230 }
231 }
232
edisonn@google.com4f898b72013-08-07 21:11:57 +0000233
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000234}
235
edisonn@google.com3aa35552013-08-14 18:26:20 +0000236SkPdfNativeDoc::~SkPdfNativeDoc() {
edisonn@google.com147adb12013-07-24 15:56:19 +0000237 sk_free((void*)fFileContent);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000238 delete fAllocator;
239}
240
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000241const unsigned char* SkPdfNativeDoc::readCrossReferenceSection(const unsigned char* xrefStart,
242 const unsigned char* trailerEnd) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000243 SkPdfNativeObject xref;
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000244 const unsigned char* current = nextObject(xrefStart, trailerEnd, &xref, NULL, NULL);
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000245
246 if (!xref.isKeyword("xref")) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000247 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue, "Could not find sref",
248 NULL, NULL);
edisonn@google.com2273f9b2013-08-06 21:48:44 +0000249 return trailerEnd;
250 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000251
edisonn@google.com3aa35552013-08-14 18:26:20 +0000252 SkPdfNativeObject token;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000253 while (current < trailerEnd) {
254 token.reset();
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000255 const unsigned char* previous = current;
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000256 current = nextObject(current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000257 if (!token.isInteger()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000258 SkPdfReport(kInfo_SkPdfIssueSeverity, kNoIssue_SkPdfIssue,
259 "Done readCrossReferenceSection", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000260 return previous;
261 }
262
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000263 int startId = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000264 token.reset();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000265 current = nextObject(current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000266
267 if (!token.isInteger()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000268 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readCrossReferenceSection",
269 &token, SkPdfNativeObject::kInteger_PdfObjectType, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000270 return current;
271 }
272
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000273 int entries = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000274
275 for (int i = 0; i < entries; i++) {
276 token.reset();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000277 current = nextObject(current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000278 if (!token.isInteger()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000279 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
280 "readCrossReferenceSection",
281 &token, SkPdfNativeObject::kInteger_PdfObjectType, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000282 return current;
283 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000284 int offset = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000285
286 token.reset();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000287 current = nextObject(current, trailerEnd, &token, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000288 if (!token.isInteger()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000289 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
290 "readCrossReferenceSection",
291 &token, SkPdfNativeObject::kInteger_PdfObjectType, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000292 return current;
293 }
edisonn@google.coma3356fc2013-07-10 18:20:06 +0000294 int generation = (int)token.intValue();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000295
296 token.reset();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000297 current = nextObject(current, trailerEnd, &token, NULL, NULL);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000298 if (!token.isKeyword() || token.lenstr() != 1 ||
299 (*token.c_str() != 'f' && *token.c_str() != 'n')) {
300 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
301 "readCrossReferenceSection: f or n expected",
302 &token, SkPdfNativeObject::kKeyword_PdfObjectType, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000303 return current;
304 }
305
306 addCrossSectionInfo(startId + i, generation, offset, *token.c_str() == 'f');
307 }
308 }
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000309 SkPdfReport(kInfo_SkPdfIssueSeverity, kNoIssue_SkPdfIssue,
310 "Unexpected end of readCrossReferenceSection", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000311 return current;
312}
313
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000314const unsigned char* SkPdfNativeDoc::readTrailer(const unsigned char* trailerStart,
315 const unsigned char* trailerEnd,
316 bool storeCatalog, long* prev, bool skipKeyword) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000317 *prev = -1;
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000318
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000319 const unsigned char* current = trailerStart;
320 if (!skipKeyword) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000321 SkPdfNativeObject trailerKeyword;
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000322 // Use null allocator, and let it just fail if memory, it should not crash.
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000323 current = nextObject(current, trailerEnd, &trailerKeyword, NULL, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000324
325 if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.lenstr() ||
326 strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000327 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
328 "readTrailer: trailer keyword expected",
329 &trailerKeyword,
330 SkPdfNativeObject::kKeyword_PdfObjectType, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000331 return current;
332 }
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000333 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000334
edisonn@google.com3aa35552013-08-14 18:26:20 +0000335 SkPdfNativeObject token;
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000336 current = nextObject(current, trailerEnd, &token, fAllocator, NULL);
edisonn@google.com432640a2013-07-10 22:53:40 +0000337 if (!token.isDictionary()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000338 return current;
edisonn@google.com432640a2013-07-10 22:53:40 +0000339 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000340 SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token;
edisonn@google.com432640a2013-07-10 22:53:40 +0000341 if (!trailer->valid()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000342 return current;
edisonn@google.com432640a2013-07-10 22:53:40 +0000343 }
edisonn@google.com571c70b2013-07-10 17:09:50 +0000344
345 if (storeCatalog) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000346 SkPdfNativeObject* ref = trailer->Root(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000347 if (ref == NULL || !ref->isReference()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000348 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
349 "readTrailer: unexpected root reference",
350 ref, SkPdfNativeObject::kReference_PdfObjectType, NULL);
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000351 return current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000352 }
353 fRootCatalogRef = ref;
354 }
355
356 if (trailer->has_Prev()) {
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000357 *prev = (long)trailer->Prev(NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000358 }
359
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000360 return current;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000361}
362
edisonn@google.com3aa35552013-08-14 18:26:20 +0000363void SkPdfNativeDoc::addCrossSectionInfo(int id, int generation, int offset, bool isFreed) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000364 // TODO(edisonn): security here, verify id
edisonn@google.com571c70b2013-07-10 17:09:50 +0000365 while (fObjects.count() < id + 1) {
366 reset(fObjects.append());
367 }
368
369 fObjects[id].fOffset = offset;
370 fObjects[id].fObj = NULL;
edisonn@google.com4ef4bed2013-07-29 22:14:45 +0000371 fObjects[id].fResolvedReference = NULL;
edisonn@google.comf68aed32013-08-22 15:37:21 +0000372 fObjects[id].fIsReferenceResolved = false;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000373}
374
edisonn@google.com3aa35552013-08-14 18:26:20 +0000375SkPdfNativeObject* SkPdfNativeDoc::readObject(int id/*, int expectedGeneration*/) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000376 long startOffset = fObjects[id].fOffset;
377 //long endOffset = fObjects[id].fOffsetEnd;
378 // TODO(edisonn): use hinted endOffset
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000379 const unsigned char* current = fFileContent + startOffset;
380 const unsigned char* end = fFileContent + fContentLength;
edisonn@google.com571c70b2013-07-10 17:09:50 +0000381
edisonn@google.com33f11b62013-08-14 21:35:27 +0000382 SkPdfNativeTokenizer tokenizer(current, end - current, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000383
edisonn@google.com3aa35552013-08-14 18:26:20 +0000384 SkPdfNativeObject idObj;
385 SkPdfNativeObject generationObj;
386 SkPdfNativeObject objKeyword;
387 SkPdfNativeObject* dict = fAllocator->allocObject();
edisonn@google.com571c70b2013-07-10 17:09:50 +0000388
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000389 current = nextObject(current, end, &idObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000390 if (current >= end) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000391 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue, "reading id",
392 NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000393 return NULL;
394 }
395
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000396 current = nextObject(current, end, &generationObj, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000397 if (current >= end) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000398 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue,
399 "reading generation", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000400 return NULL;
401 }
402
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000403 current = nextObject(current, end, &objKeyword, NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000404 if (current >= end) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000405 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue,
406 "reading keyword obj", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000407 return NULL;
408 }
409
edisonn@google.comaf54a512013-09-13 19:33:42 +0000410 if (!idObj.isInteger() || id != idObj.intValue()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000411 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readObject: unexpected id",
412 &idObj, SkPdfNativeObject::kInteger_PdfObjectType, NULL);
edisonn@google.comaf54a512013-09-13 19:33:42 +0000413 }
414
415 // TODO(edisonn): verify that the generation is the right one
416 if (!generationObj.isInteger() /* || generation != generationObj.intValue()*/) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000417 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
418 "readObject: unexpected generation",
419 &generationObj, SkPdfNativeObject::kInteger_PdfObjectType, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000420 }
421
422 if (!objKeyword.isKeyword() || strcmp(objKeyword.c_str(), "obj") != 0) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000423 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
424 "readObject: unexpected obj keyword",
425 &objKeyword, SkPdfNativeObject::kKeyword_PdfObjectType, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000426 }
427
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000428 current = nextObject(current, end, dict, fAllocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000429
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000430 // TODO(edisonn): report warning/error - verify that the last token is endobj
edisonn@google.com571c70b2013-07-10 17:09:50 +0000431
432 return dict;
433}
434
edisonn@google.com3aa35552013-08-14 18:26:20 +0000435void SkPdfNativeDoc::fillPages(SkPdfPageTreeNodeDictionary* tree) {
edisonn@google.comb0145ce2013-08-05 16:23:23 +0000436 SkPdfArray* kids = tree->Kids(this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000437 if (kids == NULL) {
438 *fPages.append() = (SkPdfPageObjectDictionary*)tree;
439 return;
440 }
441
442 int cnt = kids->size();
443 for (int i = 0; i < cnt; i++) {
edisonn@google.com3aa35552013-08-14 18:26:20 +0000444 SkPdfNativeObject* obj = resolveReference(kids->objAtAIndex(i));
445 if (fMapper->mapPageObjectDictionary(obj) != kPageObjectDictionary_SkPdfNativeObjectType) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000446 *fPages.append() = (SkPdfPageObjectDictionary*)obj;
447 } else {
448 // TODO(edisonn): verify that it is a page tree indeed
449 fillPages((SkPdfPageTreeNodeDictionary*)obj);
450 }
451 }
452}
453
edisonn@google.com3aa35552013-08-14 18:26:20 +0000454int SkPdfNativeDoc::pages() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000455 return fPages.count();
456}
457
edisonn@google.com3aa35552013-08-14 18:26:20 +0000458SkPdfPageObjectDictionary* SkPdfNativeDoc::page(int page) {
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000459 SkASSERT(page >= 0 && page < fPages.count());
460 return fPages[page];
461}
462
463
edisonn@google.com3aa35552013-08-14 18:26:20 +0000464SkPdfResourceDictionary* SkPdfNativeDoc::pageResources(int page) {
edisonn@google.com88fc03d2013-07-30 13:34:10 +0000465 SkASSERT(page >= 0 && page < fPages.count());
edisonn@google.com571c70b2013-07-10 17:09:50 +0000466 return fPages[page]->Resources(this);
467}
468
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000469// TODO(edisonn): Partial implemented.
470// Move the logics directly in the code generator for inheritable and default values?
edisonn@google.com3aa35552013-08-14 18:26:20 +0000471SkRect SkPdfNativeDoc::MediaBox(int page) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000472 SkPdfPageObjectDictionary* current = fPages[page];
473 while (!current->has_MediaBox() && current->has_Parent()) {
474 current = (SkPdfPageObjectDictionary*)current->Parent(this);
475 }
476 if (current) {
477 return current->MediaBox(this);
478 }
479 return SkRect::MakeEmpty();
480}
481
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000482SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfPage(int page, SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000483 if (fPages[page]->isContentsAStream(this)) {
edisonn@google.com2ccc3af2013-07-23 17:43:18 +0000484 return tokenizerOfStream(fPages[page]->getContentsAsStream(this), allocator);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000485 } else {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000486 // TODO(edisonn): NYI, we need to concatenate all streams in the array or
487 // make the tokenizer smart so we don't allocate new memory.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000488 return NULL;
489 }
490}
491
edisonn@google.com3aa35552013-08-14 18:26:20 +0000492SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfStream(SkPdfNativeObject* stream,
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000493 SkPdfAllocator* allocator) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000494 if (stream == NULL) {
495 return NULL;
496 }
497
edisonn@google.com33f11b62013-08-14 21:35:27 +0000498 return new SkPdfNativeTokenizer(stream, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000499}
500
edisonn@google.com3aa35552013-08-14 18:26:20 +0000501SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfBuffer(const unsigned char* buffer, size_t len,
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000502 SkPdfAllocator* allocator) {
edisonn@google.com33f11b62013-08-14 21:35:27 +0000503 return new SkPdfNativeTokenizer(buffer, len, allocator, this);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000504}
505
edisonn@google.com3aa35552013-08-14 18:26:20 +0000506size_t SkPdfNativeDoc::objects() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000507 return fObjects.count();
508}
509
edisonn@google.com3aa35552013-08-14 18:26:20 +0000510SkPdfNativeObject* SkPdfNativeDoc::object(int i) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000511 SkASSERT(!(i < 0 || i > fObjects.count()));
512
513 if (i < 0 || i > fObjects.count()) {
514 return NULL;
515 }
516
517 if (fObjects[i].fObj == NULL) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000518 fObjects[i].fObj = readObject(i);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000519 // TODO(edisonn): For perf, when we read the cross reference sections, we should take
520 // advantage of the boundaries of known objects, to minimize the risk of just parsing a bad
521 // stream, and fail quickly, in case we default to sequential stream read.
edisonn@google.com571c70b2013-07-10 17:09:50 +0000522 }
523
524 return fObjects[i].fObj;
525}
526
edisonn@google.com3aa35552013-08-14 18:26:20 +0000527const SkPdfMapper* SkPdfNativeDoc::mapper() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000528 return fMapper;
529}
530
edisonn@google.com3aa35552013-08-14 18:26:20 +0000531SkPdfReal* SkPdfNativeDoc::createReal(double value) const {
532 SkPdfNativeObject* obj = fAllocator->allocObject();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000533 SkPdfNativeObject::makeReal(value, obj);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000534 TRACK_OBJECT_SRC(obj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000535 return (SkPdfReal*)obj;
536}
537
edisonn@google.com3aa35552013-08-14 18:26:20 +0000538SkPdfInteger* SkPdfNativeDoc::createInteger(int value) const {
539 SkPdfNativeObject* obj = fAllocator->allocObject();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000540 SkPdfNativeObject::makeInteger(value, obj);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000541 TRACK_OBJECT_SRC(obj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000542 return (SkPdfInteger*)obj;
543}
544
edisonn@google.com3aa35552013-08-14 18:26:20 +0000545SkPdfString* SkPdfNativeDoc::createString(const unsigned char* sz, size_t len) const {
546 SkPdfNativeObject* obj = fAllocator->allocObject();
edisonn@google.com598cf5d2013-10-09 15:13:19 +0000547 SkPdfNativeObject::makeString(sz, len, obj);
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000548 TRACK_OBJECT_SRC(obj);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000549 return (SkPdfString*)obj;
550}
551
edisonn@google.com3aa35552013-08-14 18:26:20 +0000552SkPdfAllocator* SkPdfNativeDoc::allocator() const {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000553 return fAllocator;
554}
555
edisonn@google.com3aa35552013-08-14 18:26:20 +0000556SkPdfNativeObject* SkPdfNativeDoc::resolveReference(SkPdfNativeObject* ref) {
edisonn@google.com571c70b2013-07-10 17:09:50 +0000557 if (ref && ref->isReference()) {
558 int id = ref->referenceId();
559 // TODO(edisonn): generation/updates not supported now
560 //int gen = ref->referenceGeneration();
561
edisonn@google.com641cce92013-07-30 12:09:14 +0000562 // TODO(edisonn): verify id and gen expected
563 if (id < 0 || id >= fObjects.count()) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000564 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue,
565 "resolve reference id out of bounds", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000566 return NULL;
567 }
568
edisonn@google.comf68aed32013-08-22 15:37:21 +0000569 if (fObjects[id].fIsReferenceResolved) {
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000570 SkPdfReportIf(!fObjects[id].fResolvedReference, kIgnoreError_SkPdfIssueSeverity,
571 kBadReference_SkPdfIssue, "ref is NULL", NULL, NULL);
edisonn@google.com571c70b2013-07-10 17:09:50 +0000572 return fObjects[id].fResolvedReference;
573 }
574
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000575 // TODO(edisonn): there are pdfs in the crashing suite that cause a stack overflow
576 // here unless we check for resolved reference on next line.
577 // Determine if the pdf is corrupted, or we have a bug here.
edisonn@google.comf68aed32013-08-22 15:37:21 +0000578
edisonn@google.comc8fda9d2013-10-09 20:23:12 +0000579 // Avoids recursive calls
edisonn@google.comf68aed32013-08-22 15:37:21 +0000580 fObjects[id].fIsReferenceResolved = true;
581
edisonn@google.com571c70b2013-07-10 17:09:50 +0000582 if (fObjects[id].fObj == NULL) {
583 fObjects[id].fObj = readObject(id);
584 }
585
586 if (fObjects[id].fResolvedReference == NULL) {
587 if (!fObjects[id].fObj->isReference()) {
588 fObjects[id].fResolvedReference = fObjects[id].fObj;
589 } else {
590 fObjects[id].fResolvedReference = resolveReference(fObjects[id].fObj);
591 }
592 }
593
594 return fObjects[id].fResolvedReference;
595 }
edisonn@google.com276fed92013-08-01 21:20:47 +0000596
edisonn@google.com3aa35552013-08-14 18:26:20 +0000597 return (SkPdfNativeObject*)ref;
edisonn@google.com3aac1f92013-07-02 22:42:53 +0000598}
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000599
edisonn@google.com3aa35552013-08-14 18:26:20 +0000600size_t SkPdfNativeDoc::bytesUsed() const {
edisonn@google.coma5aaa792013-07-11 12:27:21 +0000601 return fAllocator->bytesUsed() +
602 fContentLength +
603 fObjects.count() * sizeof(PublicObjectEntry) +
604 fPages.count() * sizeof(SkPdfPageObjectDictionary*) +
605 sizeof(*this);
606}