1
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkData.h"
11#include "SkFlate.h"
12#include "SkPDFCatalog.h"
13#include "SkPDFStream.h"
14#include "SkStream.h"
15
16static bool skip_compression(SkPDFCatalog* catalog) {
17    return SkToBool(catalog->getDocumentFlags() &
18                    SkPDFDocument::kFavorSpeedOverSize_Flags);
19}
20
21SkPDFStream::SkPDFStream(SkStream* stream) : fState(kUnused_State) {
22    setData(stream);
23}
24
25SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) {
26    setData(data);
27}
28
29SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
30        : SkPDFDict(),
31          fState(kUnused_State) {
32    setData(pdfStream.fData.get());
33    bool removeLength = true;
34    // Don't uncompress an already compressed stream, but we could.
35    if (pdfStream.fState == kCompressed_State) {
36        fState = kCompressed_State;
37        removeLength = false;
38    }
39    SkPDFDict::Iter dict(pdfStream);
40    SkPDFName* key;
41    SkPDFObject* value;
42    SkPDFName lengthName("Length");
43    for (key = dict.next(&value); key != NULL; key = dict.next(&value)) {
44        if (removeLength && *key == lengthName) {
45            continue;
46        }
47        this->insert(key, value);
48    }
49}
50
51SkPDFStream::~SkPDFStream() {}
52
53void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
54                             bool indirect) {
55    if (indirect) {
56        return emitIndirectObject(stream, catalog);
57    }
58    if (!this->populate(catalog)) {
59        return fSubstitute->emitObject(stream, catalog, indirect);
60    }
61
62    this->INHERITED::emitObject(stream, catalog, false);
63    stream->writeText(" stream\n");
64    stream->writeStream(fData.get(), fData->getLength());
65    fData->rewind();
66    stream->writeText("\nendstream");
67}
68
69size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
70    if (indirect) {
71        return getIndirectOutputSize(catalog);
72    }
73    if (!this->populate(catalog)) {
74        return fSubstitute->getOutputSize(catalog, indirect);
75    }
76
77    return this->INHERITED::getOutputSize(catalog, false) +
78        strlen(" stream\n\nendstream") + fData->getLength();
79}
80
81SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
82
83void SkPDFStream::setData(SkData* data) {
84    SkMemoryStream* stream = new SkMemoryStream;
85    stream->setData(data);
86    fData.reset(stream);  // Transfer ownership.
87}
88
89void SkPDFStream::setData(SkStream* stream) {
90    // Code assumes that the stream starts at the beginning and is rewindable.
91    if (stream) {
92        SkASSERT(stream->getPosition() == 0);
93        SkASSERT(stream->rewind());
94    }
95    fData.reset(stream);
96    SkSafeRef(stream);
97}
98
99bool SkPDFStream::populate(SkPDFCatalog* catalog) {
100    if (fState == kUnused_State) {
101        if (!skip_compression(catalog) && SkFlate::HaveFlate()) {
102            SkDynamicMemoryWStream compressedData;
103
104            SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData));
105            if (compressedData.getOffset() < fData->getLength()) {
106                SkMemoryStream* stream = new SkMemoryStream;
107                stream->setData(compressedData.copyToData())->unref();
108                fData.reset(stream);  // Transfer ownership.
109                insertName("Filter", "FlateDecode");
110            }
111            fState = kCompressed_State;
112        } else {
113            fState = kNoCompression_State;
114        }
115        insertInt("Length", fData->getLength());
116    } else if (fState == kNoCompression_State && !skip_compression(catalog) &&
117               SkFlate::HaveFlate()) {
118        if (!fSubstitute.get()) {
119            fSubstitute.reset(new SkPDFStream(*this));
120            catalog->setSubstitute(this, fSubstitute.get());
121        }
122        return false;
123    }
124    return true;
125}
126