| OLD | NEW |
| 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 | 18 |
| 19 static void perform_font_subsetting(SkPDFCatalog* catalog, | 19 static void perform_font_subsetting(SkPDFCatalog* catalog, |
| 20 const SkTDArray<SkPDFPage*>& pages, | 20 const SkTDArray<SkPDFPage*>& pages) { |
| 21 SkTDArray<SkPDFObject*>* substitutes) { | |
| 22 SkASSERT(catalog); | 21 SkASSERT(catalog); |
| 23 SkASSERT(substitutes); | |
| 24 | 22 |
| 25 SkPDFGlyphSetMap usage; | 23 SkPDFGlyphSetMap usage; |
| 26 for (int i = 0; i < pages.count(); ++i) { | 24 for (int i = 0; i < pages.count(); ++i) { |
| 27 usage.merge(pages[i]->getFontGlyphUsage()); | 25 usage.merge(pages[i]->getFontGlyphUsage()); |
| 28 } | 26 } |
| 29 SkPDFGlyphSetMap::F2BIter iterator(usage); | 27 SkPDFGlyphSetMap::F2BIter iterator(usage); |
| 30 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); | 28 const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next(); |
| 31 while (entry) { | 29 while (entry) { |
| 32 SkPDFFont* subsetFont = | 30 SkAutoTUnref<SkPDFFont> subsetFont( |
| 33 entry->fFont->getFontSubset(entry->fGlyphSet); | 31 entry->fFont->getFontSubset(entry->fGlyphSet)); |
| 34 if (subsetFont) { | 32 if (subsetFont) { |
| 35 catalog->setSubstitute(entry->fFont, subsetFont); | 33 catalog->setSubstitute(entry->fFont, subsetFont.get()); |
| 36 substitutes->push(subsetFont); // Transfer ownership to substitutes | |
| 37 } | 34 } |
| 38 entry = iterator.next(); | 35 entry = iterator.next(); |
| 39 } | 36 } |
| 40 } | 37 } |
| 41 | 38 |
| 42 static void emit_pdf_header(SkWStream* stream) { | 39 static void emit_pdf_header(SkWStream* stream) { |
| 43 stream->writeText("%PDF-1.4\n%"); | 40 stream->writeText("%PDF-1.4\n%"); |
| 44 // The PDF spec recommends including a comment with four bytes, all | 41 // The PDF spec recommends including a comment with four bytes, all |
| 45 // with their high bits set. This is "Skia" with the high bits set. | 42 // with their high bits set. This is "Skia" with the high bits set. |
| 46 stream->write32(0xD3EBE9E1); | 43 stream->write32(0xD3EBE9E1); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 65 stream->writeText("\n%%EOF"); | 62 stream->writeText("\n%%EOF"); |
| 66 } | 63 } |
| 67 | 64 |
| 68 bool SkPDFDocument::EmitPDF(const SkTDArray<const SkPDFDevice*>& pageDevices, | 65 bool SkPDFDocument::EmitPDF(const SkTDArray<const SkPDFDevice*>& pageDevices, |
| 69 SkWStream* stream) { | 66 SkWStream* stream) { |
| 70 if (pageDevices.isEmpty()) { | 67 if (pageDevices.isEmpty()) { |
| 71 return false; | 68 return false; |
| 72 } | 69 } |
| 73 | 70 |
| 74 SkTDArray<SkPDFPage*> pages; | 71 SkTDArray<SkPDFPage*> pages; |
| 72 SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); |
| 73 |
| 75 for (int i = 0; i < pageDevices.count(); i++) { | 74 for (int i = 0; i < pageDevices.count(); i++) { |
| 76 SkASSERT(pageDevices[i]); | 75 SkASSERT(pageDevices[i]); |
| 77 SkASSERT(i == 0 || | 76 SkASSERT(i == 0 || |
| 78 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); | 77 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); |
| 79 // Reference from new passed to pages. | 78 // Reference from new passed to pages. |
| 80 pages.push(SkNEW_ARGS(SkPDFPage, (pageDevices[i]))); | 79 SkAutoTUnref<SkPDFPage> page(SkNEW_ARGS(SkPDFPage, (pageDevices[i]))); |
| 80 page->finalizePage(); |
| 81 page->appendDestinations(dests); |
| 82 pages.push(page.detach()); |
| 81 } | 83 } |
| 82 SkPDFCatalog catalog; | 84 SkPDFCatalog catalog; |
| 83 | 85 |
| 84 SkTDArray<SkPDFDict*> pageTree; | 86 SkTDArray<SkPDFDict*> pageTree; |
| 85 SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); | 87 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 | 88 |
| 91 SkPDFDict* pageTreeRoot; | 89 SkPDFDict* pageTreeRoot; |
| 92 SkPDFPage::GeneratePageTree(pages, &catalog, &pageTree, &pageTreeRoot); | 90 SkPDFPage::GeneratePageTree(pages, &pageTree, &pageTreeRoot); |
| 91 |
| 93 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); | 92 docCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); |
| 94 | 93 |
| 95 /* TODO(vandebo): output intent | 94 /* TODO(vandebo): output intent |
| 96 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); | 95 SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); |
| 97 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); | 96 outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); |
| 98 outputIntent->insert("OutputConditionIdentifier", | 97 outputIntent->insert("OutputConditionIdentifier", |
| 99 new SkPDFString("sRGB"))->unref(); | 98 new SkPDFString("sRGB"))->unref(); |
| 100 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; | 99 SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray; |
| 101 intentArray->append(outputIntent.get()); | 100 intentArray->append(outputIntent.get()); |
| 102 docCatalog->insert("OutputIntent", intentArray.get()); | 101 docCatalog->insert("OutputIntent", intentArray.get()); |
| 103 */ | 102 */ |
| 104 | 103 |
| 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) { | 104 if (dests->size() > 0) { |
| 148 SkPDFDict* raw_dests = dests.get(); | 105 docCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (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(); | 106 ->unref(); |
| 153 } | 107 } |
| 154 | 108 |
| 155 // Build font subsetting info before proceeding. | 109 // Build font subsetting info before proceeding. |
| 156 perform_font_subsetting(&catalog, pages, &substitutes); | 110 perform_font_subsetting(&catalog, pages); |
| 157 | 111 |
| 158 SkTSet<SkPDFObject*> resourceSet; | 112 SkTSet<SkPDFObject*> resourceSet; |
| 159 if (resourceSet.add(docCatalog.get())) { | 113 if (resourceSet.add(docCatalog.get())) { |
| 160 docCatalog->addResources(&resourceSet, &catalog); | 114 docCatalog->addResources(&resourceSet, &catalog); |
| 161 } | 115 } |
| 116 for (int i = 0; i < resourceSet.count(); ++i) { |
| 117 SkAssertResult(catalog.addObject(resourceSet[i])); |
| 118 } |
| 119 |
| 162 size_t baseOffset = SkToOffT(stream->bytesWritten()); | 120 size_t baseOffset = SkToOffT(stream->bytesWritten()); |
| 163 emit_pdf_header(stream); | 121 emit_pdf_header(stream); |
| 122 SkTDArray<int32_t> offsets; |
| 164 for (int i = 0; i < resourceSet.count(); ++i) { | 123 for (int i = 0; i < resourceSet.count(); ++i) { |
| 165 SkPDFObject* object = resourceSet[i]; | 124 SkPDFObject* object = resourceSet[i]; |
| 166 catalog.setFileOffset(object, | 125 offsets.push(SkToS32(stream->bytesWritten() - baseOffset)); |
| 167 SkToOffT(stream->bytesWritten() - baseOffset)); | |
| 168 SkASSERT(object == catalog.getSubstituteObject(object)); | 126 SkASSERT(object == catalog.getSubstituteObject(object)); |
| 169 stream->writeDecAsText(catalog.getObjectNumber(object)); | 127 SkASSERT(catalog.getObjectNumber(object) == i + 1); |
| 128 stream->writeDecAsText(i + 1); |
| 170 stream->writeText(" 0 obj\n"); // Generation number is always 0. | 129 stream->writeText(" 0 obj\n"); // Generation number is always 0. |
| 171 object->emitObject(stream, &catalog); | 130 object->emitObject(stream, &catalog); |
| 172 stream->writeText("\nendobj\n"); | 131 stream->writeText("\nendobj\n"); |
| 173 } | 132 } |
| 174 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); | 133 int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); |
| 175 int64_t objCount = catalog.emitXrefTable(stream, pages.count() > 1); | |
| 176 | 134 |
| 135 int32_t objCount = SkToS32(offsets.count() + 1); |
| 136 |
| 137 stream->writeText("xref\n0 "); |
| 138 stream->writeDecAsText(objCount + 1); |
| 139 stream->writeText("\n0000000000 65535 f \n"); |
| 140 for (int i = 0; i < offsets.count(); i++) { |
| 141 SkASSERT(offsets[i] > 0); |
| 142 stream->writeBigDecAsText(offsets[i], 10); |
| 143 stream->writeText(" 00000 n \n"); |
| 144 } |
| 177 emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount, | 145 emit_pdf_footer(stream, &catalog, docCatalog.get(), objCount, |
| 178 xRefFileOffset); | 146 xRefFileOffset); |
| 179 | 147 |
| 180 // The page tree has both child and parent pointers, so it creates a | 148 // The page tree has both child and parent pointers, so it creates a |
| 181 // reference cycle. We must clear that cycle to properly reclaim memory. | 149 // reference cycle. We must clear that cycle to properly reclaim memory. |
| 182 for (int i = 0; i < pageTree.count(); i++) { | 150 for (int i = 0; i < pageTree.count(); i++) { |
| 183 pageTree[i]->clear(); | 151 pageTree[i]->clear(); |
| 184 } | 152 } |
| 185 pageTree.safeUnrefAll(); | 153 pageTree.safeUnrefAll(); |
| 186 pages.unrefAll(); | 154 pages.unrefAll(); |
| 187 | |
| 188 firstPageResources.safeUnrefAll(); | |
| 189 otherPageResources.safeUnrefAll(); | |
| 190 | |
| 191 substitutes.unrefAll(); | |
| 192 docCatalog.reset(NULL); | |
| 193 return true; | 155 return true; |
| 194 } | 156 } |
| 195 | 157 |
| 196 // TODO(halcanary): expose notEmbeddableCount in SkDocument | 158 // TODO(halcanary): expose notEmbeddableCount in SkDocument |
| 197 void SkPDFDocument::GetCountOfFontTypes( | 159 void SkPDFDocument::GetCountOfFontTypes( |
| 198 const SkTDArray<SkPDFDevice*>& pageDevices, | 160 const SkTDArray<SkPDFDevice*>& pageDevices, |
| 199 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], | 161 int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], |
| 200 int* notSubsettableCount, | 162 int* notSubsettableCount, |
| 201 int* notEmbeddableCount) { | 163 int* notEmbeddableCount) { |
| 202 sk_bzero(counts, sizeof(int) * | 164 sk_bzero(counts, sizeof(int) * |
| (...skipping 20 matching lines...) Expand all Loading... |
| 223 } | 185 } |
| 224 } | 186 } |
| 225 if (notSubsettableCount) { | 187 if (notSubsettableCount) { |
| 226 *notSubsettableCount = notSubsettable; | 188 *notSubsettableCount = notSubsettable; |
| 227 | 189 |
| 228 } | 190 } |
| 229 if (notEmbeddableCount) { | 191 if (notEmbeddableCount) { |
| 230 *notEmbeddableCount = notEmbeddable; | 192 *notEmbeddableCount = notEmbeddable; |
| 231 } | 193 } |
| 232 } | 194 } |
| OLD | NEW |