gfx/skia/trunk/src/pdf/SkPDFDocument.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial