1/*
2 * Copyright 2011 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
8#include "SkDocument.h"
9#include "SkPDFCanon.h"
10#include "SkPDFDevice.h"
11#include "SkPDFFont.h"
12#include "SkPDFStream.h"
13#include "SkPDFTypes.h"
14#include "SkStream.h"
15
16static void emit_pdf_header(SkWStream* stream) {
17    stream->writeText("%PDF-1.4\n%");
18    // The PDF spec recommends including a comment with four bytes, all
19    // with their high bits set.  This is "Skia" with the high bits set.
20    stream->write32(0xD3EBE9E1);
21    stream->writeText("\n");
22}
23
24static void emit_pdf_footer(SkWStream* stream,
25                            const SkPDFObjNumMap& objNumMap,
26                            const SkPDFSubstituteMap& substitutes,
27                            SkPDFObject* docCatalog,
28                            int64_t objCount,
29                            int32_t xRefFileOffset) {
30    SkPDFDict trailerDict;
31    // TODO(vandebo): Linearized format will take a Prev entry too.
32    // TODO(vandebo): PDF/A requires an ID entry.
33    trailerDict.insertInt("Size", int(objCount));
34    trailerDict.insertObjRef("Root", SkRef(docCatalog));
35
36    stream->writeText("trailer\n");
37    trailerDict.emitObject(stream, objNumMap, substitutes);
38    stream->writeText("\nstartxref\n");
39    stream->writeBigDecAsText(xRefFileOffset);
40    stream->writeText("\n%%EOF");
41}
42
43static void perform_font_subsetting(
44        const SkTDArray<const SkPDFDevice*>& pageDevices,
45        SkPDFSubstituteMap* substituteMap) {
46    SkASSERT(substituteMap);
47
48    SkPDFGlyphSetMap usage;
49    for (int i = 0; i < pageDevices.count(); ++i) {
50        usage.merge(pageDevices[i]->getFontGlyphUsage());
51    }
52    SkPDFGlyphSetMap::F2BIter iterator(usage);
53    const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
54    while (entry) {
55        SkAutoTUnref<SkPDFFont> subsetFont(
56                entry->fFont->getFontSubset(entry->fGlyphSet));
57        if (subsetFont) {
58            substituteMap->setSubstitute(entry->fFont, subsetFont.get());
59        }
60        entry = iterator.next();
61    }
62}
63
64static SkPDFObject* create_pdf_page_content(const SkPDFDevice* pageDevice) {
65    SkAutoTDelete<SkStreamAsset> content(pageDevice->content());
66    return SkNEW_ARGS(SkPDFStream, (content.get()));
67}
68
69static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) {
70    SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page")));
71    page->insertObject("Resources", pageDevice->createResourceDict());
72    page->insertObject("MediaBox", pageDevice->copyMediaBox());
73    if (SkPDFArray* annots = pageDevice->getAnnotations()) {
74        SkASSERT(annots->size() > 0);
75        page->insertObject("Annots", SkRef(annots));
76    }
77    page->insertObjRef("Contents", create_pdf_page_content(pageDevice));
78    return page.detach();
79}
80
81static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages,
82                               SkTDArray<SkPDFDict*>* pageTree,
83                               SkPDFDict** rootNode) {
84    // PDF wants a tree describing all the pages in the document.  We arbitrary
85    // choose 8 (kNodeSize) as the number of allowed children.  The internal
86    // nodes have type "Pages" with an array of children, a parent pointer, and
87    // the number of leaves below the node as "Count."  The leaves are passed
88    // into the method, have type "Page" and need a parent pointer. This method
89    // builds the tree bottom up, skipping internal nodes that would have only
90    // one child.
91    static const int kNodeSize = 8;
92
93    // curNodes takes a reference to its items, which it passes to pageTree.
94    SkTDArray<SkPDFDict*> curNodes;
95    curNodes.setReserve(pages.count());
96    for (int i = 0; i < pages.count(); i++) {
97        SkSafeRef(pages[i]);
98        curNodes.push(pages[i]);
99    }
100
101    // nextRoundNodes passes its references to nodes on to curNodes.
102    SkTDArray<SkPDFDict*> nextRoundNodes;
103    nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
104
105    int treeCapacity = kNodeSize;
106    do {
107        for (int i = 0; i < curNodes.count(); ) {
108            if (i > 0 && i + 1 == curNodes.count()) {
109                nextRoundNodes.push(curNodes[i]);
110                break;
111            }
112
113            SkAutoTUnref<SkPDFDict> newNode(new SkPDFDict("Pages"));
114            SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
115            kids->reserve(kNodeSize);
116
117            int count = 0;
118            for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
119                curNodes[i]->insertObjRef("Parent", SkRef(newNode.get()));
120                kids->appendObjRef(SkRef(curNodes[i]));
121
122                // TODO(vandebo): put the objects in strict access order.
123                // Probably doesn't matter because they are so small.
124                if (curNodes[i] != pages[0]) {
125                    pageTree->push(curNodes[i]);  // Transfer reference.
126                } else {
127                    SkSafeUnref(curNodes[i]);
128                }
129            }
130
131            // treeCapacity is the number of leaf nodes possible for the
132            // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
133            // It is hard to count the number of leaf nodes in the current
134            // subtree. However, by construction, we know that unless it's the
135            // last subtree for the current depth, the leaf count will be
136            // treeCapacity, otherwise it's what ever is left over after
137            // consuming treeCapacity chunks.
138            int pageCount = treeCapacity;
139            if (i == curNodes.count()) {
140                pageCount = ((pages.count() - 1) % treeCapacity) + 1;
141            }
142            newNode->insertInt("Count", pageCount);
143            newNode->insertObject("Kids", kids.detach());
144            nextRoundNodes.push(newNode.detach());  // Transfer reference.
145        }
146
147        curNodes = nextRoundNodes;
148        nextRoundNodes.rewind();
149        treeCapacity *= kNodeSize;
150    } while (curNodes.count() > 1);
151
152    pageTree->push(curNodes[0]);  // Transfer reference.
153    if (rootNode) {
154        *rootNode = curNodes[0];
155    }
156}
157
158static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
159                              SkWStream* stream) {
160    if (pageDevices.isEmpty()) {
161        return false;
162    }
163
164    SkTDArray<SkPDFDict*> pages;
165    SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
166
167    for (int i = 0; i < pageDevices.count(); i++) {
168        SkASSERT(pageDevices[i]);
169        SkASSERT(i == 0 ||
170                 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
171        SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i]));
172        pageDevices[i]->appendDestinations(dests, page.get());
173        pages.push(page.detach());
174    }
175
176    SkTDArray<SkPDFDict*> pageTree;
177    SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));
178
179    SkPDFDict* pageTreeRoot;
180    generate_page_tree(pages, &pageTree, &pageTreeRoot);
181    docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot));
182
183    if (dests->size() > 0) {
184        docCatalog->insertObjRef("Dests", dests.detach());
185    }
186
187    /* TODO(vandebo): output intent
188    SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
189    outputIntent->insertName("S", "GTS_PDFA1");
190    outputIntent->insertString("OutputConditionIdentifier", "sRGB");
191    SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray);
192    intentArray->appendObject(SkRef(outputIntent.get()));
193    docCatalog->insertObject("OutputIntent", intentArray.detach());
194    */
195
196    // Build font subsetting info before proceeding.
197    SkPDFSubstituteMap substitutes;
198    perform_font_subsetting(pageDevices, &substitutes);
199
200    SkPDFObjNumMap objNumMap;
201    if (objNumMap.addObject(docCatalog.get())) {
202        docCatalog->addResources(&objNumMap, substitutes);
203    }
204    size_t baseOffset = SkToOffT(stream->bytesWritten());
205    emit_pdf_header(stream);
206    SkTDArray<int32_t> offsets;
207    for (int i = 0; i < objNumMap.objects().count(); ++i) {
208        SkPDFObject* object = objNumMap.objects()[i];
209        offsets.push(SkToS32(stream->bytesWritten() - baseOffset));
210        SkASSERT(object == substitutes.getSubstitute(object));
211        SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
212        stream->writeDecAsText(i + 1);
213        stream->writeText(" 0 obj\n");  // Generation number is always 0.
214        object->emitObject(stream, objNumMap, substitutes);
215        stream->writeText("\nendobj\n");
216    }
217    int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
218
219    // Include the zeroth object in the count.
220    int32_t objCount = SkToS32(offsets.count() + 1);
221
222    stream->writeText("xref\n0 ");
223    stream->writeDecAsText(objCount);
224    stream->writeText("\n0000000000 65535 f \n");
225    for (int i = 0; i < offsets.count(); i++) {
226        SkASSERT(offsets[i] > 0);
227        stream->writeBigDecAsText(offsets[i], 10);
228        stream->writeText(" 00000 n \n");
229    }
230    emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
231                    xRefFileOffset);
232
233    // The page tree has both child and parent pointers, so it creates a
234    // reference cycle.  We must clear that cycle to properly reclaim memory.
235    for (int i = 0; i < pageTree.count(); i++) {
236        pageTree[i]->clear();
237    }
238    pageTree.safeUnrefAll();
239    pages.unrefAll();
240    return true;
241}
242
243#if 0
244// TODO(halcanary): expose notEmbeddableCount in SkDocument
245void GetCountOfFontTypes(
246        const SkTDArray<SkPDFDevice*>& pageDevices,
247        int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
248        int* notSubsettableCount,
249        int* notEmbeddableCount) {
250    sk_bzero(counts, sizeof(int) *
251                     (SkAdvancedTypefaceMetrics::kOther_Font + 1));
252    SkTDArray<SkFontID> seenFonts;
253    int notSubsettable = 0;
254    int notEmbeddable = 0;
255
256    for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
257        const SkTDArray<SkPDFFont*>& fontResources =
258                pageDevices[pageNumber]->getFontResources();
259        for (int font = 0; font < fontResources.count(); font++) {
260            SkFontID fontID = fontResources[font]->typeface()->uniqueID();
261            if (seenFonts.find(fontID) == -1) {
262                counts[fontResources[font]->getType()]++;
263                seenFonts.push(fontID);
264                if (!fontResources[font]->canSubset()) {
265                    notSubsettable++;
266                }
267                if (!fontResources[font]->canEmbed()) {
268                    notEmbeddable++;
269                }
270            }
271        }
272    }
273    if (notSubsettableCount) {
274        *notSubsettableCount = notSubsettable;
275
276    }
277    if (notEmbeddableCount) {
278        *notEmbeddableCount = notEmbeddable;
279    }
280}
281#endif
282////////////////////////////////////////////////////////////////////////////////
283
284namespace {
285class SkDocument_PDF : public SkDocument {
286public:
287    SkDocument_PDF(SkWStream* stream,
288                   void (*doneProc)(SkWStream*, bool),
289                   SkScalar rasterDpi)
290        : SkDocument(stream, doneProc)
291        , fRasterDpi(rasterDpi) {}
292
293    virtual ~SkDocument_PDF() {
294        // subclasses must call close() in their destructors
295        this->close();
296    }
297
298protected:
299    SkCanvas* onBeginPage(SkScalar width, SkScalar height,
300                          const SkRect& trimBox) override {
301        SkASSERT(!fCanvas.get());
302
303        SkISize pageSize = SkISize::Make(
304                SkScalarRoundToInt(width), SkScalarRoundToInt(height));
305        SkAutoTUnref<SkPDFDevice> device(
306                SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
307        fCanvas.reset(SkNEW_ARGS(SkCanvas, (device.get())));
308        fPageDevices.push(device.detach());
309        fCanvas->clipRect(trimBox);
310        fCanvas->translate(trimBox.x(), trimBox.y());
311        return fCanvas.get();
312    }
313
314    void onEndPage() override {
315        SkASSERT(fCanvas.get());
316        fCanvas->flush();
317        fCanvas.reset(NULL);
318    }
319
320    bool onClose(SkWStream* stream) override {
321        SkASSERT(!fCanvas.get());
322
323        bool success = emit_pdf_document(fPageDevices, stream);
324        fPageDevices.unrefAll();
325        fCanon.reset();
326        return success;
327    }
328
329    void onAbort() override {
330        fPageDevices.unrefAll();
331        fCanon.reset();
332    }
333
334private:
335    SkPDFCanon fCanon;
336    SkTDArray<const SkPDFDevice*> fPageDevices;
337    SkAutoTUnref<SkCanvas> fCanvas;
338    SkScalar fRasterDpi;
339};
340}  // namespace
341///////////////////////////////////////////////////////////////////////////////
342
343SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
344    return stream ? SkNEW_ARGS(SkDocument_PDF, (stream, NULL, dpi)) : NULL;
345}
346
347SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
348    SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path));
349    if (!stream->isValid()) {
350        SkDELETE(stream);
351        return NULL;
352    }
353    auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); };
354    return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi));
355}
356