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

Side by Side Diff: src/pdf/SkPDFDocument.cpp

Issue 1034583002: SkPDF refactor skpdfdocument (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: SkToS32() 2015-03-24 (Tuesday) 16:54:14 EDT Created 5 years, 9 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
« no previous file with comments | « src/pdf/SkPDFDocument.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* 2 /*
3 * Copyright 2011 Google Inc. 3 * Copyright 2011 Google Inc.
4 * 4 *
5 * Use of this source code is governed by a BSD-style license that can be 5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file. 6 * found in the LICENSE file.
7 */ 7 */
8 8
9 9
10 #include "SkPDFCatalog.h" 10 #include "SkPDFCatalog.h"
11 #include "SkPDFDevice.h" 11 #include "SkPDFDevice.h"
12 #include "SkPDFDocument.h" 12 #include "SkPDFDocument.h"
13 #include "SkPDFFont.h" 13 #include "SkPDFFont.h"
14 #include "SkPDFPage.h" 14 #include "SkPDFPage.h"
15 #include "SkPDFTypes.h" 15 #include "SkPDFTypes.h"
16 #include "SkStream.h" 16 #include "SkStream.h"
17 17
18 static void addResourcesToCatalog(bool firstPage,
19 SkTSet<SkPDFObject*>* resourceSet,
20 SkPDFCatalog* catalog) {
21 for (int i = 0; i < resourceSet->count(); i++) {
22 catalog->addObject((*resourceSet)[i], firstPage);
23 }
24 }
25 18
26 static void perform_font_subsetting(SkPDFCatalog* catalog, 19 static void perform_font_subsetting(SkPDFCatalog* catalog,
27 const SkTDArray<SkPDFPage*>& pages, 20 const SkTDArray<SkPDFPage*>& pages,
28 SkTDArray<SkPDFObject*>* substitutes) { 21 SkTDArray<SkPDFObject*>* substitutes) {
29 SkASSERT(catalog); 22 SkASSERT(catalog);
30 SkASSERT(substitutes); 23 SkASSERT(substitutes);
31 24
32 SkPDFGlyphSetMap usage; 25 SkPDFGlyphSetMap usage;
33 for (int i = 0; i < pages.count(); ++i) { 26 for (int i = 0; i < pages.count(); ++i) {
34 usage.merge(pages[i]->getFontGlyphUsage()); 27 usage.merge(pages[i]->getFontGlyphUsage());
35 } 28 }
36 SkPDFGlyphSetMap::F2BIter iterator(usage); 29 SkPDFGlyphSetMap::F2BIter iterator(usage);
37 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); 30 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
38 while (entry) { 31 while (entry) {
39 SkPDFFont* subsetFont = 32 SkPDFFont* subsetFont =
40 entry->fFont->getFontSubset(entry->fGlyphSet); 33 entry->fFont->getFontSubset(entry->fGlyphSet);
41 if (subsetFont) { 34 if (subsetFont) {
42 catalog->setSubstitute(entry->fFont, subsetFont); 35 catalog->setSubstitute(entry->fFont, subsetFont);
43 substitutes->push(subsetFont); // Transfer ownership to substitutes 36 substitutes->push(subsetFont); // Transfer ownership to substitutes
44 } 37 }
45 entry = iterator.next(); 38 entry = iterator.next();
46 } 39 }
47 } 40 }
48 41
49 SkPDFDocument::SkPDFDocument() 42 SkPDFDocument::SkPDFDocument() {}
50 : fXRefFileOffset(0), 43
51 fTrailerDict(NULL) { 44 SkPDFDocument::~SkPDFDocument() { fPageDevices.unrefAll(); }
52 fCatalog.reset(SkNEW(SkPDFCatalog)); 45
53 fDocCatalog = SkNEW_ARGS(SkPDFDict, ("Catalog")); 46 static void emit_pdf_header(SkWStream* stream) {
54 fCatalog->addObject(fDocCatalog, true); 47 stream->writeText("%PDF-1.4\n%");
55 fFirstPageResources = NULL; 48 // The PDF spec recommends including a comment with four bytes, all
56 fOtherPageResources = NULL; 49 // with their high bits set. This is "Skia" with the high bits set.
50 stream->write32(0xD3EBE9E1);
51 stream->writeText("\n");
57 } 52 }
58 53
59 SkPDFDocument::~SkPDFDocument() { 54 static void emit_pdf_footer(SkWStream* stream,
60 fPages.safeUnrefAll(); 55 SkPDFCatalog* catalog,
56 SkPDFObject* docCatalog,
57 int64_t objCount,
58 int32_t xRefFileOffset) {
59 SkPDFDict trailerDict;
60 // TODO(vandebo): Linearized format will take a Prev entry too.
61 // TODO(vandebo): PDF/A requires an ID entry.
62 trailerDict.insertInt("Size", int(objCount));
63 trailerDict.insert("Root", new SkPDFObjRef(docCatalog))->unref();
64
65 stream->writeText("trailer\n");
66 trailerDict.emitObject(stream, catalog);
67 stream->writeText("\nstartxref\n");
68 stream->writeBigDecAsText(xRefFileOffset);
69 stream->writeText("\n%%EOF");
70 }
71
72 bool SkPDFDocument::emitPDF(SkWStream* stream) {
73 // SkTDArray<SkPDFDevice*> fPageDevices;
74 if (fPageDevices.isEmpty()) {
75 return false;
76 }
77 SkTDArray<SkPDFPage*> pages;
78 for (int i = 0; i < fPageDevices.count(); i++) {
79 // Reference from new passed to pages.
80 pages.push(SkNEW_ARGS(SkPDFPage, (fPageDevices[i])));
81 }
82 SkPDFCatalog catalog;
83
84 SkTDArray<SkPDFDict*> pageTree;
85 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));
86 SkTSet<SkPDFObject*> firstPageResources;
87 SkTSet<SkPDFObject*> otherPageResources;
88 SkTDArray<SkPDFObject*> substitutes;
89 catalog.addObject(docCatalog.get(), true);
90
91 SkPDFDict* pageTreeRoot;
92 SkPDFPage::GeneratePageTree(pages, &catalog, &pageTree, &pageTreeRoot);
93 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
94
95 /* TODO(vandebo): output intent
96 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
97 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref();
98 outputIntent->insert("OutputConditionIdentifier",
99 new SkPDFString("sRGB"))->unref();
100 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray;
101 intentArray->append(outputIntent.get());
102 docCatalog->insert("OutputIntent", intentArray.get());
103 */
104
105 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
106
107 bool firstPage = true;
108 /* The references returned in newResources are transfered to
109 * firstPageResources or otherPageResources depending on firstPage and
110 * knownResources doesn't have a reference but just relies on the other
111 * two sets to maintain a reference.
112 */
113 SkTSet<SkPDFObject*> knownResources;
114
115 // mergeInto returns the number of duplicates.
116 // If there are duplicates, there is a bug and we mess ref counting.
117 SkDEBUGCODE(int duplicates = ) knownResources.mergeInto(firstPageResources);
118 SkASSERT(duplicates == 0);
119
120 for (int i = 0; i < pages.count(); i++) {
121 if (i == 1) {
122 firstPage = false;
123 SkDEBUGCODE(duplicates = )
124 knownResources.mergeInto(otherPageResources);
125 }
126 SkTSet<SkPDFObject*> newResources;
127 pages[i]->finalizePage(&catalog, firstPage, knownResources,
128 &newResources);
129 for (int j = 0; j < newResources.count(); j++) {
130 catalog.addObject(newResources[i], firstPage);
131 }
132 if (firstPage) {
133 SkDEBUGCODE(duplicates = )
134 firstPageResources.mergeInto(newResources);
135 } else {
136 SkDEBUGCODE(duplicates = )
137 otherPageResources.mergeInto(newResources);
138 }
139 SkASSERT(duplicates == 0);
140
141 SkDEBUGCODE(duplicates = ) knownResources.mergeInto(newResources);
142 SkASSERT(duplicates == 0);
143
144 pages[i]->appendDestinations(dests);
145 }
146
147 if (dests->size() > 0) {
148 SkPDFDict* raw_dests = dests.get();
149 firstPageResources.add(dests.detach()); // Transfer ownership.
150 catalog.addObject(raw_dests, true /* onFirstPage */);
151 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests)))
152 ->unref();
153 }
154
155 // Build font subsetting info before proceeding.
156 perform_font_subsetting(&catalog, pages, &substitutes);
157
158 SkTSet<SkPDFObject*> resourceSet;
159 if (resourceSet.add(docCatalog.get())) {
160 docCatalog->addResources(&resourceSet, &catalog);
161 }
162 size_t baseOffset = SkToOffT(stream->bytesWritten());
163 emit_pdf_header(stream);
164 for (int i = 0; i < resourceSet.count(); ++i) {
165 SkPDFObject* object = resourceSet[i];
166 catalog.setFileOffset(object,
167 SkToOffT(stream->bytesWritten() - baseOffset));
168 SkASSERT(object == catalog.getSubstituteObject(object));
169 stream->writeDecAsText(catalog.getObjectNumber(object));
170 stream->writeText(" 0 obj\n"); // Generation number is always 0.
171 object->emitObject(stream, &catalog);
172 stream->writeText("\nendobj\n");
173 }
174 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
175 int64_t objCount = catalog.emitXrefTable(stream, pages.count() > 1);
176
177 emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount,
178 xRefFileOffset);
61 179
62 // The page tree has both child and parent pointers, so it creates a 180 // The page tree has both child and parent pointers, so it creates a
63 // reference cycle. We must clear that cycle to properly reclaim memory. 181 // reference cycle. We must clear that cycle to properly reclaim memory.
64 for (int i = 0; i < fPageTree.count(); i++) { 182 for (int i = 0; i < pageTree.count(); i++) {
65 fPageTree[i]->clear(); 183 pageTree[i]->clear();
66 } 184 }
67 fPageTree.safeUnrefAll(); 185 pageTree.safeUnrefAll();
186 pages.unrefAll();
68 187
69 if (fFirstPageResources) { 188 firstPageResources.safeUnrefAll();
70 fFirstPageResources->safeUnrefAll(); 189 otherPageResources.safeUnrefAll();
71 }
72 if (fOtherPageResources) {
73 fOtherPageResources->safeUnrefAll();
74 }
75 190
76 fSubstitutes.safeUnrefAll(); 191 substitutes.unrefAll();
77 192 docCatalog.reset(NULL);
78 fDocCatalog->unref();
79 SkSafeUnref(fTrailerDict);
80 SkDELETE(fFirstPageResources);
81 SkDELETE(fOtherPageResources);
82 }
83
84 bool SkPDFDocument::emitPDF(SkWStream* stream) {
85 if (fPages.isEmpty()) {
86 return false;
87 }
88 for (int i = 0; i < fPages.count(); i++) {
89 if (fPages[i] == NULL) {
90 return false;
91 }
92 }
93
94 fFirstPageResources = SkNEW(SkTSet<SkPDFObject*>);
95 fOtherPageResources = SkNEW(SkTSet<SkPDFObject*>);
96
97 // We haven't emitted the document before if fPageTree is empty.
98 if (fPageTree.isEmpty()) {
99 SkPDFDict* pageTreeRoot;
100 SkPDFPage::GeneratePageTree(fPages, fCatalog.get(), &fPageTree,
101 &pageTreeRoot);
102 fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
103
104 /* TODO(vandebo): output intent
105 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
106 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref();
107 outputIntent->insert("OutputConditionIdentifier",
108 new SkPDFString("sRGB"))->unref();
109 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray;
110 intentArray->append(outputIntent.get());
111 fDocCatalog->insert("OutputIntent", intentArray.get());
112 */
113
114 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
115
116 bool firstPage = true;
117 /* The references returned in newResources are transfered to
118 * fFirstPageResources or fOtherPageResources depending on firstPage and
119 * knownResources doesn't have a reference but just relies on the other
120 * two sets to maintain a reference.
121 */
122 SkTSet<SkPDFObject*> knownResources;
123
124 // mergeInto returns the number of duplicates.
125 // If there are duplicates, there is a bug and we mess ref counting.
126 SkDEBUGCODE(int duplicates =) knownResources.mergeInto(*fFirstPageResour ces);
127 SkASSERT(duplicates == 0);
128
129 for (int i = 0; i < fPages.count(); i++) {
130 if (i == 1) {
131 firstPage = false;
132 SkDEBUGCODE(duplicates =) knownResources.mergeInto(*fOtherPageRe sources);
133 }
134 SkTSet<SkPDFObject*> newResources;
135 fPages[i]->finalizePage(
136 fCatalog.get(), firstPage, knownResources, &newResources);
137 addResourcesToCatalog(firstPage, &newResources, fCatalog.get());
138 if (firstPage) {
139 SkDEBUGCODE(duplicates =) fFirstPageResources->mergeInto(newReso urces);
140 } else {
141 SkDEBUGCODE(duplicates =) fOtherPageResources->mergeInto(newReso urces);
142 }
143 SkASSERT(duplicates == 0);
144
145 SkDEBUGCODE(duplicates =) knownResources.mergeInto(newResources);
146 SkASSERT(duplicates == 0);
147
148 fPages[i]->appendDestinations(dests);
149 }
150
151 if (dests->size() > 0) {
152 SkPDFDict* raw_dests = dests.get();
153 fFirstPageResources->add(dests.detach()); // Transfer ownership.
154 fCatalog->addObject(raw_dests, true /* onFirstPage */);
155 fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests)))-> unref();
156 }
157
158 // Build font subsetting info before proceeding.
159 perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
160 }
161
162 SkTSet<SkPDFObject*> resourceSet;
163 if (resourceSet.add(fDocCatalog)) {
164 fDocCatalog->addResources(&resourceSet, fCatalog);
165 }
166 off_t baseOffset = SkToOffT(stream->bytesWritten());
167 emitHeader(stream);
168 for (int i = 0; i < resourceSet.count(); ++i) {
169 SkPDFObject* object = resourceSet[i];
170 fCatalog->setFileOffset(object,
171 SkToOffT(stream->bytesWritten()) - baseOffset);
172 SkASSERT(object == fCatalog->getSubstituteObject(object));
173 stream->writeDecAsText(fCatalog->getObjectNumber(object));
174 stream->writeText(" 0 obj\n"); // Generation number is always 0.
175 object->emitObject(stream, fCatalog);
176 stream->writeText("\nendobj\n");
177 }
178 fXRefFileOffset = SkToOffT(stream->bytesWritten()) - baseOffset;
179 int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1);
180 emitFooter(stream, objCount);
181 return true; 193 return true;
182 } 194 }
183 195
184 // TODO(halcanary): remove this method, since it is unused.
185 bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) {
186 if (!fPageTree.isEmpty()) {
187 return false;
188 }
189
190 pageNumber--;
191 SkASSERT(pageNumber >= 0);
192
193 if (pageNumber >= fPages.count()) {
194 int oldSize = fPages.count();
195 fPages.setCount(pageNumber + 1);
196 for (int i = oldSize; i <= pageNumber; i++) {
197 fPages[i] = NULL;
198 }
199 }
200
201 SkPDFPage* page = new SkPDFPage(pdfDevice);
202 SkSafeUnref(fPages[pageNumber]);
203 fPages[pageNumber] = page; // Reference from new passed to fPages.
204 return true;
205 }
206
207 bool SkPDFDocument::appendPage(SkPDFDevice* pdfDevice) {
208 if (!fPageTree.isEmpty()) {
209 return false;
210 }
211
212 SkPDFPage* page = new SkPDFPage(pdfDevice);
213 fPages.push(page); // Reference from new passed to fPages.
214 return true;
215 }
216
217 // Deprecated.
218 // TODO(halcanary): remove
219 void SkPDFDocument::getCountOfFontTypes(
220 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const {
221 sk_bzero(counts, sizeof(int) *
222 (SkAdvancedTypefaceMetrics::kOther_Font + 2));
223 SkTDArray<SkFontID> seenFonts;
224 int notEmbeddable = 0;
225
226 for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) {
227 const SkTDArray<SkPDFFont*>& fontResources =
228 fPages[pageNumber]->getFontResources();
229 for (int font = 0; font < fontResources.count(); font++) {
230 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
231 if (seenFonts.find(fontID) == -1) {
232 counts[fontResources[font]->getType()]++;
233 seenFonts.push(fontID);
234 if (!fontResources[font]->canEmbed()) {
235 notEmbeddable++;
236 }
237 }
238 }
239 }
240 counts[SkAdvancedTypefaceMetrics::kOther_Font + 1] = notEmbeddable;
241 }
242
243 // TODO(halcanary): expose notEmbeddableCount in SkDocument 196 // TODO(halcanary): expose notEmbeddableCount in SkDocument
244 void SkPDFDocument::getCountOfFontTypes( 197 void SkPDFDocument::getCountOfFontTypes(
245 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], 198 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
246 int* notSubsettableCount, 199 int* notSubsettableCount,
247 int* notEmbeddableCount) const { 200 int* notEmbeddableCount) const {
248 sk_bzero(counts, sizeof(int) * 201 sk_bzero(counts, sizeof(int) *
249 (SkAdvancedTypefaceMetrics::kOther_Font + 1)); 202 (SkAdvancedTypefaceMetrics::kOther_Font + 1));
250 SkTDArray<SkFontID> seenFonts; 203 SkTDArray<SkFontID> seenFonts;
251 int notSubsettable = 0; 204 int notSubsettable = 0;
252 int notEmbeddable = 0; 205 int notEmbeddable = 0;
253 206
254 for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) { 207 for (int pageNumber = 0; pageNumber < fPageDevices.count(); pageNumber++) {
255 const SkTDArray<SkPDFFont*>& fontResources = 208 const SkTDArray<SkPDFFont*>& fontResources =
256 fPages[pageNumber]->getFontResources(); 209 fPageDevices[pageNumber]->getFontResources();
257 for (int font = 0; font < fontResources.count(); font++) { 210 for (int font = 0; font < fontResources.count(); font++) {
258 SkFontID fontID = fontResources[font]->typeface()->uniqueID(); 211 SkFontID fontID = fontResources[font]->typeface()->uniqueID();
259 if (seenFonts.find(fontID) == -1) { 212 if (seenFonts.find(fontID) == -1) {
260 counts[fontResources[font]->getType()]++; 213 counts[fontResources[font]->getType()]++;
261 seenFonts.push(fontID); 214 seenFonts.push(fontID);
262 if (!fontResources[font]->canSubset()) { 215 if (!fontResources[font]->canSubset()) {
263 notSubsettable++; 216 notSubsettable++;
264 } 217 }
265 if (!fontResources[font]->canEmbed()) { 218 if (!fontResources[font]->canEmbed()) {
266 notEmbeddable++; 219 notEmbeddable++;
267 } 220 }
268 } 221 }
269 } 222 }
270 } 223 }
271 if (notSubsettableCount) { 224 if (notSubsettableCount) {
272 *notSubsettableCount = notSubsettable; 225 *notSubsettableCount = notSubsettable;
273 226
274 } 227 }
275 if (notEmbeddableCount) { 228 if (notEmbeddableCount) {
276 *notEmbeddableCount = notEmbeddable; 229 *notEmbeddableCount = notEmbeddable;
277 } 230 }
278 } 231 }
279
280 void SkPDFDocument::emitHeader(SkWStream* stream) {
281 stream->writeText("%PDF-1.4\n%");
282 // The PDF spec recommends including a comment with four bytes, all
283 // with their high bits set. This is "Skia" with the high bits set.
284 stream->write32(0xD3EBE9E1);
285 stream->writeText("\n");
286 }
287
288 //TODO(halcanary): remove this function
289 size_t SkPDFDocument::headerSize() {
290 SkDynamicMemoryWStream buffer;
291 emitHeader(&buffer);
292 return buffer.getOffset();
293 }
294
295 void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) {
296 if (NULL == fTrailerDict) {
297 fTrailerDict = SkNEW(SkPDFDict);
298
299 // TODO(vandebo): Linearized format will take a Prev entry too.
300 // TODO(vandebo): PDF/A requires an ID entry.
301 fTrailerDict->insertInt("Size", int(objCount));
302 fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref();
303 }
304
305 stream->writeText("trailer\n");
306 fTrailerDict->emitObject(stream, fCatalog.get());
307 stream->writeText("\nstartxref\n");
308 stream->writeBigDecAsText(fXRefFileOffset);
309 stream->writeText("\n%%EOF");
310 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFDocument.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698