Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(364)

Side by Side Diff: experimental/PdfViewer/pdfparser/native/SkPdfNativeDoc.cpp

Issue 26613006: code cleanup (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright 2013 Google Inc. 2 * Copyright 2013 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkPdfNativeDoc.h" 8 #include "SkPdfNativeDoc.h"
9 #include "SkPdfNativeTokenizer.h"
10 #include "SkPdfNativeObject.h"
11 #include "SkPdfReporter.h"
12 9
13 #include <stdio.h> 10 #include <stdio.h>
14 #include <string.h> 11 #include <string.h>
15 #include <sys/types.h> 12 #include <sys/types.h>
16 #include <sys/stat.h> 13 #include <sys/stat.h>
17 14
15 #include "SkPdfMapper_autogen.h"
16 #include "SkPdfNativeObject.h"
17 #include "SkPdfNativeTokenizer.h"
18 #include "SkPdfReporter.h"
19 #include "SkStream.h"
20
18 // TODO(edisonn): for some reason on mac these files are found here, but are fou nd from headers 21 // TODO(edisonn): for some reason on mac these files are found here, but are fou nd from headers
19 //#include "SkPdfFileTrailerDictionary_autogen.h" 22 //#include "SkPdfFileTrailerDictionary_autogen.h"
20 //#include "SkPdfCatalogDictionary_autogen.h" 23 //#include "SkPdfCatalogDictionary_autogen.h"
21 //#include "SkPdfPageObjectDictionary_autogen.h" 24 //#include "SkPdfPageObjectDictionary_autogen.h"
22 //#include "SkPdfPageTreeNodeDictionary_autogen.h" 25 //#include "SkPdfPageTreeNodeDictionary_autogen.h"
23 #include "SkPdfHeaders_autogen.h" 26 #include "SkPdfHeaders_autogen.h"
24 27
25 #include "SkPdfMapper_autogen.h"
26
27 #include "SkStream.h"
28
29
30 static long getFileSize(const char* filename) 28 static long getFileSize(const char* filename)
31 { 29 {
32 struct stat stat_buf; 30 struct stat stat_buf;
33 int rc = stat(filename, &stat_buf); 31 int rc = stat(filename, &stat_buf);
34 return rc == 0 ? (long)stat_buf.st_size : -1; 32 return rc == 0 ? (long)stat_buf.st_size : -1;
35 } 33 }
36 34
37 static const unsigned char* lineHome(const unsigned char* start, const unsigned char* current) { 35 static const unsigned char* lineHome(const unsigned char* start, const unsigned char* current) {
38 while (current > start && !isPdfEOL(*(current - 1))) { 36 while (current > start && !isPdfEOL(*(current - 1))) {
39 current--; 37 current--;
40 } 38 }
41 return current; 39 return current;
42 } 40 }
43 41
44 static const unsigned char* previousLineHome(const unsigned char* start, const u nsigned char* current) { 42 static const unsigned char* previousLineHome(const unsigned char* start,
43 const unsigned char* current) {
45 if (current > start && isPdfEOL(*(current - 1))) { 44 if (current > start && isPdfEOL(*(current - 1))) {
46 current--; 45 current--;
47 } 46 }
48 47
49 // allows CR+LF, LF+CR but not two CR+CR or LF+LF 48 // allows CR+LF, LF+CR but not two CR+CR or LF+LF
50 if (current > start && isPdfEOL(*(current - 1)) && *current != *(current - 1 )) { 49 if (current > start && isPdfEOL(*(current - 1)) && *current != *(current - 1 )) {
51 current--; 50 current--;
52 } 51 }
53 52
54 while (current > start && !isPdfEOL(*(current - 1))) { 53 while (current > start && !isPdfEOL(*(current - 1))) {
55 current--; 54 current--;
56 } 55 }
57 56
58 return current; 57 return current;
59 } 58 }
60 59
61 static const unsigned char* ignoreLine(const unsigned char* current, const unsig ned char* end) { 60 static const unsigned char* ignoreLine(const unsigned char* current, const unsig ned char* end) {
62 while (current < end && !isPdfEOL(*current)) { 61 while (current < end && !isPdfEOL(*current)) {
63 current++; 62 current++;
64 } 63 }
65 current++; 64 current++;
66 if (current < end && isPdfEOL(*current) && *current != *(current - 1)) { 65 if (current < end && isPdfEOL(*current) && *current != *(current - 1)) {
67 current++; 66 current++;
68 } 67 }
69 return current; 68 return current;
70 } 69 }
71 70
72 SkPdfNativeDoc* gDoc = NULL; 71 SkPdfNativeDoc* gDoc = NULL;
73 72
74 // TODO(edisonn): NYI
75 // TODO(edisonn): 3 constructuctors from URL, from stream, from file ...
76 // TODO(edisonn): write one that accepts errors in the file and ignores/fixis th em
77 // TODO(edisonn): testing:
78 // 1) run on a lot of file
79 // 2) recoverable corupt file: remove endobj, endsteam, remove other keywords, u se other white spaces, insert comments randomly, ...
80 // 3) irrecoverable corrupt file
81
82 SkPdfNativeDoc::SkPdfNativeDoc(SkStream* stream) 73 SkPdfNativeDoc::SkPdfNativeDoc(SkStream* stream)
83 : fAllocator(new SkPdfAllocator()) 74 : fAllocator(new SkPdfAllocator())
84 , fFileContent(NULL) 75 , fFileContent(NULL)
85 , fContentLength(0) 76 , fContentLength(0)
86 , fRootCatalogRef(NULL) 77 , fRootCatalogRef(NULL)
87 , fRootCatalog(NULL) { 78 , fRootCatalog(NULL) {
88 size_t size = stream->getLength(); 79 size_t size = stream->getLength();
89 void* ptr = sk_malloc_throw(size); 80 void* ptr = sk_malloc_throw(size);
90 stream->read(ptr, size); 81 stream->read(ptr, size);
91 82
92 init(ptr, size); 83 init(ptr, size);
93 } 84 }
94 85
95 SkPdfNativeDoc::SkPdfNativeDoc(const char* path) 86 SkPdfNativeDoc::SkPdfNativeDoc(const char* path)
96 : fAllocator(new SkPdfAllocator()) 87 : fAllocator(new SkPdfAllocator())
97 , fFileContent(NULL) 88 , fFileContent(NULL)
98 , fContentLength(0) 89 , fContentLength(0)
99 , fRootCatalogRef(NULL) 90 , fRootCatalogRef(NULL)
100 , fRootCatalog(NULL) { 91 , fRootCatalog(NULL) {
101 gDoc = this; 92 gDoc = this;
102 FILE* file = fopen(path, "r"); 93 FILE* file = fopen(path, "r");
103 // TODO(edisonn): put this in a function that can return NULL 94 // TODO(edisonn): put this in a function that can return NULL
104 if (file) { 95 if (file) {
105 size_t size = getFileSize(path); 96 size_t size = getFileSize(path);
106 void* content = sk_malloc_throw(size); 97 void* content = sk_malloc_throw(size);
107 bool ok = (0 != fread(content, size, 1, file)); 98 bool ok = (0 != fread(content, size, 1, file));
108 fclose(file); 99 fclose(file);
109 if (!ok) { 100 if (!ok) {
110 sk_free(content); 101 sk_free(content);
111 SkPdfReport(kFatalError_SkPdfIssueSeverity, kReadStreamError_SkPdfIs sue, "could not read file", NULL, NULL); 102 SkPdfReport(kFatalError_SkPdfIssueSeverity, kReadStreamError_SkPdfIs sue,
103 "could not read file", NULL, NULL);
112 // TODO(edisonn): not nice to return like this from constructor, cre ate a static 104 // TODO(edisonn): not nice to return like this from constructor, cre ate a static
113 // function that can report NULL for failures. 105 // function that can report NULL for failures.
114 return; // Doc will have 0 pages 106 return; // Doc will have 0 pages
115 } 107 }
116 108
117 init(content, size); 109 init(content, size);
118 } 110 }
119 } 111 }
120 112
121 void SkPdfNativeDoc::init(const void* bytes, size_t length) { 113 void SkPdfNativeDoc::init(const void* bytes, size_t length) {
122 fFileContent = (const unsigned char*)bytes; 114 fFileContent = (const unsigned char*)bytes;
123 fContentLength = length; 115 fContentLength = length;
124 const unsigned char* eofLine = lineHome(fFileContent, fFileContent + fConten tLength - 1); 116 const unsigned char* eofLine = lineHome(fFileContent, fFileContent + fConten tLength - 1);
125 const unsigned char* xrefByteOffsetLine = previousLineHome(fFileContent, eof Line); 117 const unsigned char* xrefByteOffsetLine = previousLineHome(fFileContent, eof Line);
126 const unsigned char* xrefstartKeywordLine = previousLineHome(fFileContent, x refByteOffsetLine); 118 const unsigned char* xrefstartKeywordLine = previousLineHome(fFileContent, x refByteOffsetLine);
127 119
128 if (strcmp((char*)xrefstartKeywordLine, "startxref") != 0) { 120 if (strcmp((char*)xrefstartKeywordLine, "startxref") != 0) {
129 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue, "Coul d not find startxref", NULL, NULL); 121 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue,
122 "Could not find startxref", NULL, NULL);
130 } 123 }
131 124
132 long xrefByteOffset = atol((const char*)xrefByteOffsetLine); 125 long xrefByteOffset = atol((const char*)xrefByteOffsetLine);
133 126
134 bool storeCatalog = true; 127 bool storeCatalog = true;
135 while (xrefByteOffset >= 0) { 128 while (xrefByteOffset >= 0) {
136 const unsigned char* trailerStart = readCrossReferenceSection(fFileConte nt + xrefByteOffset, xrefstartKeywordLine); 129 const unsigned char* trailerStart = readCrossReferenceSection(fFileConte nt + xrefByteOffset,
130 xrefstartK eywordLine);
137 xrefByteOffset = -1; 131 xrefByteOffset = -1;
138 if (trailerStart < xrefstartKeywordLine) { 132 if (trailerStart < xrefstartKeywordLine) {
139 readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog, &xrefB yteOffset, false); 133 readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog, &xrefB yteOffset, false);
140 storeCatalog = false; 134 storeCatalog = false;
141 } 135 }
142 } 136 }
143 137
144 // TODO(edisonn): warn/error expect fObjects[fRefCatalogId].fGeneration == f RefCatalogGeneration 138 // TODO(edisonn): warn/error expect fObjects[fRefCatalogId].fGeneration == f RefCatalogGeneration
145 // TODO(edisonn): security, verify that SkPdfCatalogDictionary is indeed usi ng mapper 139 // TODO(edisonn): security, verify that SkPdfCatalogDictionary is indeed usi ng mapper
146 // load catalog
147 140
148 if (fRootCatalogRef) { 141 if (fRootCatalogRef) {
149 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef ); 142 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef );
150 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) { 143 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
151 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this); 144 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
152 if (tree && tree->isDictionary() && tree->valid()) { 145 if (tree && tree->isDictionary() && tree->valid()) {
153 fillPages(tree); 146 fillPages(tree);
154 } 147 }
155 } 148 }
156 } 149 }
157 150
158 // TODO(edisonn): clean up this doc, or better, let the caller call again an d build a new doc
159 // caller should be a static function.
160 if (pages() == 0) { 151 if (pages() == 0) {
152 // TODO(edisonn): probably it would be better to return NULL and make a clean document.
161 loadWithoutXRef(); 153 loadWithoutXRef();
162 } 154 }
163 155
164 // TODO(edisonn): corrupted pdf, read it from beginning and rebuild (xref, t railer, or just reall all objects) 156 // TODO(edisonn): corrupted pdf, read it from beginning and rebuild
165 // 0 pages 157 // (xref, trailer, or just read all objects)
166
167 // now actually read all objects if we want, or do it lazyly
168 // and resolve references?... or not ...
169 } 158 }
170 159
171 void SkPdfNativeDoc::loadWithoutXRef() { 160 void SkPdfNativeDoc::loadWithoutXRef() {
172 const unsigned char* current = fFileContent; 161 const unsigned char* current = fFileContent;
173 const unsigned char* end = fFileContent + fContentLength; 162 const unsigned char* end = fFileContent + fContentLength;
174 163
175 // TODO(edisonn): read pdf version 164 // TODO(edisonn): read pdf version
176 current = ignoreLine(current, end); 165 current = ignoreLine(current, end);
177 166
178 current = skipPdfWhiteSpaces(current, end); 167 current = skipPdfWhiteSpaces(current, end);
179 while (current < end) { 168 while (current < end) {
180 SkPdfNativeObject token; 169 SkPdfNativeObject token;
181 current = nextObject(current, end, &token, NULL, NULL); 170 current = nextObject(current, end, &token, NULL, NULL);
182 if (token.isInteger()) { 171 if (token.isInteger()) {
183 int id = (int)token.intValue(); 172 int id = (int)token.intValue();
184 173
185 token.reset(); 174 token.reset();
186 current = nextObject(current, end, &token, NULL, NULL); 175 current = nextObject(current, end, &token, NULL, NULL);
187 // int generation = (int)token.intValue(); // TODO(edisonn): ignore d for now 176 // TODO(edisonn): generation ignored for now (used in pdfs with upda tes)
177 // int generation = (int)token.intValue();
188 178
189 token.reset(); 179 token.reset();
190 current = nextObject(current, end, &token, NULL, NULL); 180 current = nextObject(current, end, &token, NULL, NULL);
191 // TODO(edisonn): must be obj, return error if not? ignore ? 181 // TODO(edisonn): keywork must be "obj". Add ability to report error instead ignoring.
192 if (!token.isKeyword("obj")) { 182 if (!token.isKeyword("obj")) {
193 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssu e, "Could not find obj", NULL, NULL); 183 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssu e,
184 "Could not find obj", NULL, NULL);
194 continue; 185 continue;
195 } 186 }
196 187
197 while (fObjects.count() < id + 1) { 188 while (fObjects.count() < id + 1) {
198 reset(fObjects.append()); 189 reset(fObjects.append());
199 } 190 }
200 191
201 fObjects[id].fOffset = current - fFileContent; 192 fObjects[id].fOffset = current - fFileContent;
202 193
203 SkPdfNativeObject* obj = fAllocator->allocObject(); 194 SkPdfNativeObject* obj = fAllocator->allocObject();
204 current = nextObject(current, end, obj, fAllocator, this); 195 current = nextObject(current, end, obj, fAllocator, this);
205 196
206 fObjects[id].fResolvedReference = obj; 197 fObjects[id].fResolvedReference = obj;
207 fObjects[id].fObj = obj; 198 fObjects[id].fObj = obj;
208 fObjects[id].fIsReferenceResolved = true; 199 fObjects[id].fIsReferenceResolved = true;
209
210
211 // set objects
212 } else if (token.isKeyword("trailer")) { 200 } else if (token.isKeyword("trailer")) {
213 long dummy; 201 long dummy;
214 current = readTrailer(current, end, true, &dummy, true); 202 current = readTrailer(current, end, true, &dummy, true);
215 } else if (token.isKeyword("startxref")) { 203 } else if (token.isKeyword("startxref")) {
216 token.reset(); 204 token.reset();
217 current = nextObject(current, end, &token, NULL, NULL); // ignore 205 current = nextObject(current, end, &token, NULL, NULL); // ignore s tartxref
218 } 206 }
219 207
220 current = skipPdfWhiteSpaces(current, end); 208 current = skipPdfWhiteSpaces(current, end);
221 } 209 }
222 210
223 // TODO(edisonn): hack, detect root catalog - we need to implement liniarize d support, and remove this hack. 211 // TODO(edisonn): quick hack, detect root catalog. When we implement lineari zed support we
212 // might not need it.
224 if (!fRootCatalogRef) { 213 if (!fRootCatalogRef) {
225 for (unsigned int i = 0 ; i < objects(); i++) { 214 for (unsigned int i = 0 ; i < objects(); i++) {
226 SkPdfNativeObject* obj = object(i); 215 SkPdfNativeObject* obj = object(i);
227 SkPdfNativeObject* root = (obj && obj->isDictionary()) ? obj->get("R oot") : NULL; 216 SkPdfNativeObject* root = (obj && obj->isDictionary()) ? obj->get("R oot") : NULL;
228 if (root && root->isReference()) { 217 if (root && root->isReference()) {
229 fRootCatalogRef = root; 218 fRootCatalogRef = root;
230 } 219 }
231 } 220 }
232 } 221 }
233 222
234 if (fRootCatalogRef) { 223 if (fRootCatalogRef) {
235 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef ); 224 fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef );
236 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) { 225 if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
237 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this); 226 SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
238 if (tree && tree->isDictionary() && tree->valid()) { 227 if (tree && tree->isDictionary() && tree->valid()) {
239 fillPages(tree); 228 fillPages(tree);
240 } 229 }
241 } 230 }
242 } 231 }
243 232
244 233
245 } 234 }
246 235
247 // TODO(edisonn): NYI
248 SkPdfNativeDoc::~SkPdfNativeDoc() { 236 SkPdfNativeDoc::~SkPdfNativeDoc() {
249 sk_free((void*)fFileContent); 237 sk_free((void*)fFileContent);
250 delete fAllocator; 238 delete fAllocator;
251 } 239 }
252 240
253 const unsigned char* SkPdfNativeDoc::readCrossReferenceSection(const unsigned ch ar* xrefStart, const unsigned char* trailerEnd) { 241 const unsigned char* SkPdfNativeDoc::readCrossReferenceSection(const unsigned ch ar* xrefStart,
242 const unsigned ch ar* trailerEnd) {
254 SkPdfNativeObject xref; 243 SkPdfNativeObject xref;
255 const unsigned char* current = nextObject(xrefStart, trailerEnd, &xref, NULL , NULL); 244 const unsigned char* current = nextObject(xrefStart, trailerEnd, &xref, NULL , NULL);
256 245
257 if (!xref.isKeyword("xref")) { 246 if (!xref.isKeyword("xref")) {
258 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue, "Coul d not find sref", NULL, NULL); 247 SkPdfReport(kWarning_SkPdfIssueSeverity, kMissingToken_SkPdfIssue, "Coul d not find sref",
248 NULL, NULL);
259 return trailerEnd; 249 return trailerEnd;
260 } 250 }
261 251
262 SkPdfNativeObject token; 252 SkPdfNativeObject token;
263 while (current < trailerEnd) { 253 while (current < trailerEnd) {
264 token.reset(); 254 token.reset();
265 const unsigned char* previous = current; 255 const unsigned char* previous = current;
266 current = nextObject(current, trailerEnd, &token, NULL, NULL); 256 current = nextObject(current, trailerEnd, &token, NULL, NULL);
267 if (!token.isInteger()) { 257 if (!token.isInteger()) {
268 SkPdfReport(kInfo_SkPdfIssueSeverity, kNoIssue_SkPdfIssue, "Done rea dCrossReferenceSection", NULL, NULL); 258 SkPdfReport(kInfo_SkPdfIssueSeverity, kNoIssue_SkPdfIssue,
259 "Done readCrossReferenceSection", NULL, NULL);
269 return previous; 260 return previous;
270 } 261 }
271 262
272 int startId = (int)token.intValue(); 263 int startId = (int)token.intValue();
273 token.reset(); 264 token.reset();
274 current = nextObject(current, trailerEnd, &token, NULL, NULL); 265 current = nextObject(current, trailerEnd, &token, NULL, NULL);
275 266
276 if (!token.isInteger()) { 267 if (!token.isInteger()) {
277 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readCros sReferenceSection", &token, SkPdfNativeObject::kInteger_PdfObjectType, NULL); 268 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readCros sReferenceSection",
269 &token, SkPdfNativeObject::kInteger_PdfObj ectType, NULL);
278 return current; 270 return current;
279 } 271 }
280 272
281 int entries = (int)token.intValue(); 273 int entries = (int)token.intValue();
282 274
283 for (int i = 0; i < entries; i++) { 275 for (int i = 0; i < entries; i++) {
284 token.reset(); 276 token.reset();
285 current = nextObject(current, trailerEnd, &token, NULL, NULL); 277 current = nextObject(current, trailerEnd, &token, NULL, NULL);
286 if (!token.isInteger()) { 278 if (!token.isInteger()) {
287 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "read CrossReferenceSection", &token, SkPdfNativeObject::kInteger_PdfObjectType, NULL) ; 279 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
280 "readCrossReferenceSection",
281 &token, SkPdfNativeObject::kInteger_Pd fObjectType, NULL);
288 return current; 282 return current;
289 } 283 }
290 int offset = (int)token.intValue(); 284 int offset = (int)token.intValue();
291 285
292 token.reset(); 286 token.reset();
293 current = nextObject(current, trailerEnd, &token, NULL, NULL); 287 current = nextObject(current, trailerEnd, &token, NULL, NULL);
294 if (!token.isInteger()) { 288 if (!token.isInteger()) {
295 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "read CrossReferenceSection", &token, SkPdfNativeObject::kInteger_PdfObjectType, NULL) ; 289 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
290 "readCrossReferenceSection",
291 &token, SkPdfNativeObject::kInteger_Pd fObjectType, NULL);
296 return current; 292 return current;
297 } 293 }
298 int generation = (int)token.intValue(); 294 int generation = (int)token.intValue();
299 295
300 token.reset(); 296 token.reset();
301 current = nextObject(current, trailerEnd, &token, NULL, NULL); 297 current = nextObject(current, trailerEnd, &token, NULL, NULL);
302 if (!token.isKeyword() || token.lenstr() != 1 || (*token.c_str() != 'f' && *token.c_str() != 'n')) { 298 if (!token.isKeyword() || token.lenstr() != 1 ||
303 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "read CrossReferenceSection: f or n expected", &token, SkPdfNativeObject::kKeyword_Pdf ObjectType, NULL); 299 (*token.c_str() != 'f' && *token.c_str() != 'n')) {
300 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
301 "readCrossReferenceSection: f or n exp ected",
302 &token, SkPdfNativeObject::kKeyword_Pd fObjectType, NULL);
304 return current; 303 return current;
305 } 304 }
306 305
307 addCrossSectionInfo(startId + i, generation, offset, *token.c_str() == 'f'); 306 addCrossSectionInfo(startId + i, generation, offset, *token.c_str() == 'f');
308 } 307 }
309 } 308 }
310 SkPdfReport(kInfo_SkPdfIssueSeverity, kNoIssue_SkPdfIssue, "Unexpected end o f readCrossReferenceSection", NULL, NULL); 309 SkPdfReport(kInfo_SkPdfIssueSeverity, kNoIssue_SkPdfIssue,
310 "Unexpected end of readCrossReferenceSection", NULL, NULL);
311 return current; 311 return current;
312 } 312 }
313 313
314 const unsigned char* SkPdfNativeDoc::readTrailer(const unsigned char* trailerSta rt, const unsigned char* trailerEnd, bool storeCatalog, long* prev, bool skipKey word) { 314 const unsigned char* SkPdfNativeDoc::readTrailer(const unsigned char* trailerSta rt,
315 const unsigned char* trailerEnd ,
316 bool storeCatalog, long* prev, bool skipKeyword) {
315 *prev = -1; 317 *prev = -1;
316 318
317 const unsigned char* current = trailerStart; 319 const unsigned char* current = trailerStart;
318 if (!skipKeyword) { 320 if (!skipKeyword) {
319 SkPdfNativeObject trailerKeyword; 321 SkPdfNativeObject trailerKeyword;
320 // TODO(edisonn): use null allocator, and let it just fail if memory 322 // Use null allocator, and let it just fail if memory, it should not cra sh.
321 // needs allocated (but no crash)!
322 current = nextObject(current, trailerEnd, &trailerKeyword, NULL, NULL); 323 current = nextObject(current, trailerEnd, &trailerKeyword, NULL, NULL);
323 324
324 if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.l enstr() || 325 if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.l enstr() ||
325 strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) { 326 strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
326 // TODO(edisonn): report warning, rebuild trailer from objects. 327 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
327 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readTrai ler: trailer keyword expected", &trailerKeyword, SkPdfNativeObject::kKeyword_Pdf ObjectType, NULL); 328 "readTrailer: trailer keyword expected",
329 &trailerKeyword,
330 SkPdfNativeObject::kKeyword_PdfObjectType, NULL);
328 return current; 331 return current;
329 } 332 }
330 } 333 }
331 334
332 SkPdfNativeObject token; 335 SkPdfNativeObject token;
333 current = nextObject(current, trailerEnd, &token, fAllocator, NULL); 336 current = nextObject(current, trailerEnd, &token, fAllocator, NULL);
334 if (!token.isDictionary()) { 337 if (!token.isDictionary()) {
335 return current; 338 return current;
336 } 339 }
337 SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token; 340 SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token;
338 if (!trailer->valid()) { 341 if (!trailer->valid()) {
339 return current; 342 return current;
340 } 343 }
341 344
342 if (storeCatalog) { 345 if (storeCatalog) {
343 SkPdfNativeObject* ref = trailer->Root(NULL); 346 SkPdfNativeObject* ref = trailer->Root(NULL);
344 if (ref == NULL || !ref->isReference()) { 347 if (ref == NULL || !ref->isReference()) {
345 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readTrai ler: unexpected root reference", ref, SkPdfNativeObject::kReference_PdfObjectTyp e, NULL); 348 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
349 "readTrailer: unexpected root reference",
350 ref, SkPdfNativeObject::kReference_PdfObje ctType, NULL);
346 return current; 351 return current;
347 } 352 }
348 fRootCatalogRef = ref; 353 fRootCatalogRef = ref;
349 } 354 }
350 355
351 if (trailer->has_Prev()) { 356 if (trailer->has_Prev()) {
352 *prev = (long)trailer->Prev(NULL); 357 *prev = (long)trailer->Prev(NULL);
353 } 358 }
354 359
355 return current; 360 return current;
356 } 361 }
357 362
358 void SkPdfNativeDoc::addCrossSectionInfo(int id, int generation, int offset, boo l isFreed) { 363 void SkPdfNativeDoc::addCrossSectionInfo(int id, int generation, int offset, boo l isFreed) {
359 // TODO(edisonn): security here 364 // TODO(edisonn): security here, verify id
360 while (fObjects.count() < id + 1) { 365 while (fObjects.count() < id + 1) {
361 reset(fObjects.append()); 366 reset(fObjects.append());
362 } 367 }
363 368
364 fObjects[id].fOffset = offset; 369 fObjects[id].fOffset = offset;
365 fObjects[id].fObj = NULL; 370 fObjects[id].fObj = NULL;
366 fObjects[id].fResolvedReference = NULL; 371 fObjects[id].fResolvedReference = NULL;
367 fObjects[id].fIsReferenceResolved = false; 372 fObjects[id].fIsReferenceResolved = false;
368 } 373 }
369 374
370 SkPdfNativeObject* SkPdfNativeDoc::readObject(int id/*, int expectedGeneration*/ ) { 375 SkPdfNativeObject* SkPdfNativeDoc::readObject(int id/*, int expectedGeneration*/ ) {
371 long startOffset = fObjects[id].fOffset; 376 long startOffset = fObjects[id].fOffset;
372 //long endOffset = fObjects[id].fOffsetEnd; 377 //long endOffset = fObjects[id].fOffsetEnd;
373 // TODO(edisonn): use hinted endOffset 378 // TODO(edisonn): use hinted endOffset
374 // TODO(edisonn): current implementation will result in a lot of memory usag e
375 // to decrease memory usage, we wither need to be smart and know where objec ts end, and we will
376 // alocate only the chancks needed, or the tokenizer will not make copies, b ut then it needs to
377 // cache the results so it does not go twice on the same buffer
378 const unsigned char* current = fFileContent + startOffset; 379 const unsigned char* current = fFileContent + startOffset;
379 const unsigned char* end = fFileContent + fContentLength; 380 const unsigned char* end = fFileContent + fContentLength;
380 381
381 SkPdfNativeTokenizer tokenizer(current, end - current, fAllocator, this); 382 SkPdfNativeTokenizer tokenizer(current, end - current, fAllocator, this);
382 383
383 SkPdfNativeObject idObj; 384 SkPdfNativeObject idObj;
384 SkPdfNativeObject generationObj; 385 SkPdfNativeObject generationObj;
385 SkPdfNativeObject objKeyword; 386 SkPdfNativeObject objKeyword;
386 SkPdfNativeObject* dict = fAllocator->allocObject(); 387 SkPdfNativeObject* dict = fAllocator->allocObject();
387 388
388 current = nextObject(current, end, &idObj, NULL, NULL); 389 current = nextObject(current, end, &idObj, NULL, NULL);
389 if (current >= end) { 390 if (current >= end) {
390 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue , "reading id", NULL, NULL); 391 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue , "reading id",
392 NULL, NULL);
391 return NULL; 393 return NULL;
392 } 394 }
393 395
394 current = nextObject(current, end, &generationObj, NULL, NULL); 396 current = nextObject(current, end, &generationObj, NULL, NULL);
395 if (current >= end) { 397 if (current >= end) {
396 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue , "reading generation", NULL, NULL); 398 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue ,
399 "reading generation", NULL, NULL);
397 return NULL; 400 return NULL;
398 } 401 }
399 402
400 current = nextObject(current, end, &objKeyword, NULL, NULL); 403 current = nextObject(current, end, &objKeyword, NULL, NULL);
401 if (current >= end) { 404 if (current >= end) {
402 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue , "reading keyword obj", NULL, NULL); 405 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfIssue ,
406 "reading keyword obj", NULL, NULL);
403 return NULL; 407 return NULL;
404 } 408 }
405 409
406 if (!idObj.isInteger() || id != idObj.intValue()) { 410 if (!idObj.isInteger() || id != idObj.intValue()) {
407 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readObject: unexpected id", &idObj, SkPdfNativeObject::kInteger_PdfObjectType, NULL); 411 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readObject: unexpected id",
412 &idObj, SkPdfNativeObject::kInteger_PdfObjectT ype, NULL);
408 } 413 }
409 414
410 // TODO(edisonn): verify that the generation is the right one 415 // TODO(edisonn): verify that the generation is the right one
411 if (!generationObj.isInteger() /* || generation != generationObj.intValue()* /) { 416 if (!generationObj.isInteger() /* || generation != generationObj.intValue()* /) {
412 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readObject: unexpected generation", &generationObj, SkPdfNativeObject::kInteger_PdfObjectTyp e, NULL); 417 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
418 "readObject: unexpected generation",
419 &generationObj, SkPdfNativeObject::kInteger_Pd fObjectType, NULL);
413 } 420 }
414 421
415 if (!objKeyword.isKeyword() || strcmp(objKeyword.c_str(), "obj") != 0) { 422 if (!objKeyword.isKeyword() || strcmp(objKeyword.c_str(), "obj") != 0) {
416 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity, "readObject: unexpected obj keyword", &objKeyword, SkPdfNativeObject::kKeyword_PdfObjectType, NULL); 423 SkPdfReportUnexpectedType(kIgnoreError_SkPdfIssueSeverity,
424 "readObject: unexpected obj keyword",
425 &objKeyword, SkPdfNativeObject::kKeyword_PdfOb jectType, NULL);
417 } 426 }
418 427
419 current = nextObject(current, end, dict, fAllocator, this); 428 current = nextObject(current, end, dict, fAllocator, this);
420 429
421 // TODO(edisonn): report warning/error - verify last token is endobj 430 // TODO(edisonn): report warning/error - verify that the last token is endob j
422 431
423 return dict; 432 return dict;
424 } 433 }
425 434
426 void SkPdfNativeDoc::fillPages(SkPdfPageTreeNodeDictionary* tree) { 435 void SkPdfNativeDoc::fillPages(SkPdfPageTreeNodeDictionary* tree) {
427 SkPdfArray* kids = tree->Kids(this); 436 SkPdfArray* kids = tree->Kids(this);
428 if (kids == NULL) { 437 if (kids == NULL) {
429 *fPages.append() = (SkPdfPageObjectDictionary*)tree; 438 *fPages.append() = (SkPdfPageObjectDictionary*)tree;
430 return; 439 return;
431 } 440 }
(...skipping 18 matching lines...) Expand all
450 SkASSERT(page >= 0 && page < fPages.count()); 459 SkASSERT(page >= 0 && page < fPages.count());
451 return fPages[page]; 460 return fPages[page];
452 } 461 }
453 462
454 463
455 SkPdfResourceDictionary* SkPdfNativeDoc::pageResources(int page) { 464 SkPdfResourceDictionary* SkPdfNativeDoc::pageResources(int page) {
456 SkASSERT(page >= 0 && page < fPages.count()); 465 SkASSERT(page >= 0 && page < fPages.count());
457 return fPages[page]->Resources(this); 466 return fPages[page]->Resources(this);
458 } 467 }
459 468
460 // TODO(edisonn): Partial implemented. Move the logics directly in the code gene rator for inheritable and default value? 469 // TODO(edisonn): Partial implemented.
470 // Move the logics directly in the code generator for inheritable and default va lues?
461 SkRect SkPdfNativeDoc::MediaBox(int page) { 471 SkRect SkPdfNativeDoc::MediaBox(int page) {
462 SkPdfPageObjectDictionary* current = fPages[page]; 472 SkPdfPageObjectDictionary* current = fPages[page];
463 while (!current->has_MediaBox() && current->has_Parent()) { 473 while (!current->has_MediaBox() && current->has_Parent()) {
464 current = (SkPdfPageObjectDictionary*)current->Parent(this); 474 current = (SkPdfPageObjectDictionary*)current->Parent(this);
465 } 475 }
466 if (current) { 476 if (current) {
467 return current->MediaBox(this); 477 return current->MediaBox(this);
468 } 478 }
469 return SkRect::MakeEmpty(); 479 return SkRect::MakeEmpty();
470 } 480 }
471 481
472 // TODO(edisonn): stream or array ... ? for now only array 482 SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfPage(int page, SkPdfAllocator* allocator) {
473 SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfPage(int page,
474 SkPdfAllocator* allocat or) {
475 if (fPages[page]->isContentsAStream(this)) { 483 if (fPages[page]->isContentsAStream(this)) {
476 return tokenizerOfStream(fPages[page]->getContentsAsStream(this), alloca tor); 484 return tokenizerOfStream(fPages[page]->getContentsAsStream(this), alloca tor);
477 } else { 485 } else {
478 // TODO(edisonn): NYI, we need to concatenate all streams in the array o r make the tokenizer smart 486 // TODO(edisonn): NYI, we need to concatenate all streams in the array o r
479 // so we don't allocate new memory 487 // make the tokenizer smart so we don't allocate new memory.
480 return NULL; 488 return NULL;
481 } 489 }
482 } 490 }
483 491
484 SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfStream(SkPdfNativeObject* strea m, 492 SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfStream(SkPdfNativeObject* strea m,
485 SkPdfAllocator* alloc ator) { 493 SkPdfAllocator* allocato r) {
486 if (stream == NULL) { 494 if (stream == NULL) {
487 return NULL; 495 return NULL;
488 } 496 }
489 497
490 return new SkPdfNativeTokenizer(stream, allocator, this); 498 return new SkPdfNativeTokenizer(stream, allocator, this);
491 } 499 }
492 500
493 // TODO(edisonn): NYI
494 SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfBuffer(const unsigned char* buf fer, size_t len, 501 SkPdfNativeTokenizer* SkPdfNativeDoc::tokenizerOfBuffer(const unsigned char* buf fer, size_t len,
495 SkPdfAllocator* alloc ator) { 502 SkPdfAllocator* allocato r) {
496 // warning does not track two calls in the same buffer! the buffer is update d!
497 // make a clean copy if needed!
498 return new SkPdfNativeTokenizer(buffer, len, allocator, this); 503 return new SkPdfNativeTokenizer(buffer, len, allocator, this);
499 } 504 }
500 505
501 size_t SkPdfNativeDoc::objects() const { 506 size_t SkPdfNativeDoc::objects() const {
502 return fObjects.count(); 507 return fObjects.count();
503 } 508 }
504 509
505 SkPdfNativeObject* SkPdfNativeDoc::object(int i) { 510 SkPdfNativeObject* SkPdfNativeDoc::object(int i) {
506 SkASSERT(!(i < 0 || i > fObjects.count())); 511 SkASSERT(!(i < 0 || i > fObjects.count()));
507 512
508 if (i < 0 || i > fObjects.count()) { 513 if (i < 0 || i > fObjects.count()) {
509 return NULL; 514 return NULL;
510 } 515 }
511 516
512 if (fObjects[i].fObj == NULL) { 517 if (fObjects[i].fObj == NULL) {
513 // TODO(edisonn): when we read the cross reference sections, store the s tart of the next object
514 // and fill fOffsetEnd
515 fObjects[i].fObj = readObject(i); 518 fObjects[i].fObj = readObject(i);
519 // TODO(edisonn): For perf, when we read the cross reference sections, w e 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 rea d.
516 } 522 }
517 523
518 return fObjects[i].fObj; 524 return fObjects[i].fObj;
519 } 525 }
520 526
521 const SkPdfMapper* SkPdfNativeDoc::mapper() const { 527 const SkPdfMapper* SkPdfNativeDoc::mapper() const {
522 return fMapper; 528 return fMapper;
523 } 529 }
524 530
525 SkPdfReal* SkPdfNativeDoc::createReal(double value) const { 531 SkPdfReal* SkPdfNativeDoc::createReal(double value) const {
526 SkPdfNativeObject* obj = fAllocator->allocObject(); 532 SkPdfNativeObject* obj = fAllocator->allocObject();
527 SkPdfNativeObject::makeReal(value, obj); 533 SkPdfNativeObject::makeReal(value, obj);
528 // TODO(edisonn): TRACK_FROM_CODE(obj); 534 TRACK_OBJECT_SRC(obj);
529 return (SkPdfReal*)obj; 535 return (SkPdfReal*)obj;
530 } 536 }
531 537
532 SkPdfInteger* SkPdfNativeDoc::createInteger(int value) const { 538 SkPdfInteger* SkPdfNativeDoc::createInteger(int value) const {
533 SkPdfNativeObject* obj = fAllocator->allocObject(); 539 SkPdfNativeObject* obj = fAllocator->allocObject();
534 SkPdfNativeObject::makeInteger(value, obj); 540 SkPdfNativeObject::makeInteger(value, obj);
535 // TODO(edisonn): TRACK_FROM_CODE(obj); 541 TRACK_OBJECT_SRC(obj);
536 return (SkPdfInteger*)obj; 542 return (SkPdfInteger*)obj;
537 } 543 }
538 544
539 SkPdfString* SkPdfNativeDoc::createString(const unsigned char* sz, size_t len) c onst { 545 SkPdfString* SkPdfNativeDoc::createString(const unsigned char* sz, size_t len) c onst {
540 SkPdfNativeObject* obj = fAllocator->allocObject(); 546 SkPdfNativeObject* obj = fAllocator->allocObject();
541 SkPdfNativeObject::makeString(sz, len, obj); 547 SkPdfNativeObject::makeString(sz, len, obj);
542 // TODO(edisonn): TRACK_FROM_CODE(obj); 548 TRACK_OBJECT_SRC(obj);
543 return (SkPdfString*)obj; 549 return (SkPdfString*)obj;
544 } 550 }
545 551
546 SkPdfAllocator* SkPdfNativeDoc::allocator() const { 552 SkPdfAllocator* SkPdfNativeDoc::allocator() const {
547 return fAllocator; 553 return fAllocator;
548 } 554 }
549 555
550 // TODO(edisonn): fix infinite loop if ref to itself!
551 // TODO(edisonn): perf, fix refs at load, and resolve will simply return fResolv edReference?
552 SkPdfNativeObject* SkPdfNativeDoc::resolveReference(SkPdfNativeObject* ref) { 556 SkPdfNativeObject* SkPdfNativeDoc::resolveReference(SkPdfNativeObject* ref) {
553 if (ref && ref->isReference()) { 557 if (ref && ref->isReference()) {
554 int id = ref->referenceId(); 558 int id = ref->referenceId();
555 // TODO(edisonn): generation/updates not supported now 559 // TODO(edisonn): generation/updates not supported now
556 //int gen = ref->referenceGeneration(); 560 //int gen = ref->referenceGeneration();
557 561
558 // TODO(edisonn): verify id and gen expected 562 // TODO(edisonn): verify id and gen expected
559 if (id < 0 || id >= fObjects.count()) { 563 if (id < 0 || id >= fObjects.count()) {
560 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfI ssue, "resolve reference id out of bounds", NULL, NULL); 564 SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kReadStreamError_SkPdfI ssue,
565 "resolve reference id out of bounds", NULL, NULL);
561 return NULL; 566 return NULL;
562 } 567 }
563 568
564 if (fObjects[id].fIsReferenceResolved) { 569 if (fObjects[id].fIsReferenceResolved) {
565 570 SkPdfReportIf(!fObjects[id].fResolvedReference, kIgnoreError_SkPdfIs sueSeverity,
566 #ifdef PDF_TRACE 571 kBadReference_SkPdfIssue, "ref is NULL", NULL, NULL);
567 printf("\nresolve(%s) = %s\n", ref->toString(0).c_str(), fObjects[id ].fResolvedReference->toString(0, ref->toString().size() + 13).c_str());
568 #endif
569
570 SkPdfReportIf(!fObjects[id].fResolvedReference, kIgnoreError_SkPdfIs sueSeverity, kBadReference_SkPdfIssue, "ref is NULL", NULL, NULL);
571 return fObjects[id].fResolvedReference; 572 return fObjects[id].fResolvedReference;
572 } 573 }
573 574
574 // TODO(edisonn): there are pdfs in the crashing suite that cause a stac k overflow here unless we check for resolved reference on next line 575 // TODO(edisonn): there are pdfs in the crashing suite that cause a stac k overflow
575 // determine if the pdf is corrupted, or we have a bug here 576 // here unless we check for resolved reference on next line.
577 // Determine if the pdf is corrupted, or we have a bug here.
576 578
577 // avoids recursive calls 579 // Avoids recursive calls
578 fObjects[id].fIsReferenceResolved = true; 580 fObjects[id].fIsReferenceResolved = true;
579 581
580 if (fObjects[id].fObj == NULL) { 582 if (fObjects[id].fObj == NULL) {
581 fObjects[id].fObj = readObject(id); 583 fObjects[id].fObj = readObject(id);
582 } 584 }
583 585
584 if (fObjects[id].fResolvedReference == NULL) { 586 if (fObjects[id].fResolvedReference == NULL) {
585 if (!fObjects[id].fObj->isReference()) { 587 if (!fObjects[id].fObj->isReference()) {
586 fObjects[id].fResolvedReference = fObjects[id].fObj; 588 fObjects[id].fResolvedReference = fObjects[id].fObj;
587 } else { 589 } else {
588 fObjects[id].fResolvedReference = resolveReference(fObjects[id]. fObj); 590 fObjects[id].fResolvedReference = resolveReference(fObjects[id]. fObj);
589 } 591 }
590 } 592 }
591 593
592 #ifdef PDF_TRACE
593 printf("\nresolve(%s) = %s\n", ref->toString(0).c_str(), fObjects[id].fR esolvedReference->toString(0, ref->toString().size() + 13).c_str());
594 #endif
595 return fObjects[id].fResolvedReference; 594 return fObjects[id].fResolvedReference;
596 } 595 }
597 596
598 // TODO(edisonn): fix the mess with const, probably we need to remove it pre tty much everywhere
599 return (SkPdfNativeObject*)ref; 597 return (SkPdfNativeObject*)ref;
600 } 598 }
601 599
602 size_t SkPdfNativeDoc::bytesUsed() const { 600 size_t SkPdfNativeDoc::bytesUsed() const {
603 return fAllocator->bytesUsed() + 601 return fAllocator->bytesUsed() +
604 fContentLength + 602 fContentLength +
605 fObjects.count() * sizeof(PublicObjectEntry) + 603 fObjects.count() * sizeof(PublicObjectEntry) +
606 fPages.count() * sizeof(SkPdfPageObjectDictionary*) + 604 fPages.count() * sizeof(SkPdfPageObjectDictionary*) +
607 sizeof(*this); 605 sizeof(*this);
608 } 606 }
OLDNEW
« no previous file with comments | « experimental/PdfViewer/pdfparser/native/SkPdfNativeDoc.h ('k') | experimental/PdfViewer/pdfparser/native/SkPdfNativeObject.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698