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#include "SkStreamPriv.h"
16
17static bool skip_compression(SkPDFCatalog* catalog) {
18    return SkToBool(catalog->getDocumentFlags() &
19                    SkPDFDocument::kFavorSpeedOverSize_Flags);
20}
21
22SkPDFStream::SkPDFStream(SkStream* stream) : fState(kUnused_State) {
23    this->setData(stream);
24}
25
26SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) {
27    this->setData(data);
28}
29
30SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
31        : SkPDFDict(),
32          fState(kUnused_State) {
33    this->setData(pdfStream.fDataStream.get());
34    bool removeLength = true;
35    // Don't uncompress an already compressed stream, but we could.
36    if (pdfStream.fState == kCompressed_State) {
37        fState = kCompressed_State;
38        removeLength = false;
39    }
40    this->mergeFrom(pdfStream);
41    if (removeLength) {
42        this->remove("Length");
43    }
44}
45
46SkPDFStream::~SkPDFStream() {}
47
48void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
49                             bool indirect) {
50    if (indirect) {
51        return emitIndirectObject(stream, catalog);
52    }
53    SkAutoMutexAcquire lock(fMutex);  // multiple threads could be calling emit
54    if (!this->populate(catalog)) {
55        return fSubstitute->emitObject(stream, catalog, indirect);
56    }
57
58    this->INHERITED::emitObject(stream, catalog, false);
59    stream->writeText(" stream\n");
60    stream->writeStream(fDataStream.get(), fDataStream->getLength());
61    SkAssertResult(fDataStream->rewind());
62    stream->writeText("\nendstream");
63}
64
65size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
66    if (indirect) {
67        return getIndirectOutputSize(catalog);
68    }
69    SkAutoMutexAcquire lock(fMutex);  // multiple threads could be calling emit
70    if (!this->populate(catalog)) {
71        return fSubstitute->getOutputSize(catalog, indirect);
72    }
73
74    return this->INHERITED::getOutputSize(catalog, false) +
75        strlen(" stream\n\nendstream") + this->dataSize();
76}
77
78SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
79
80void SkPDFStream::setData(SkData* data) {
81    fMemoryStream.setData(data);
82    if (&fMemoryStream != fDataStream.get()) {
83        fDataStream.reset(SkRef(&fMemoryStream));
84    }
85}
86
87void SkPDFStream::setData(SkStream* stream) {
88    // Code assumes that the stream starts at the beginning and is rewindable.
89    if (&fMemoryStream == fDataStream.get()) {
90        SkASSERT(&fMemoryStream != stream);
91        fMemoryStream.setData(NULL);
92    }
93    SkASSERT(0 == fMemoryStream.getLength());
94    if (stream) {
95        // SkStreamRewindableFromSkStream will try stream->duplicate().
96        fDataStream.reset(SkStreamRewindableFromSkStream(stream));
97        SkASSERT(fDataStream.get());
98    } else {
99        fDataStream.reset(SkRef(&fMemoryStream));
100    }
101}
102
103size_t SkPDFStream::dataSize() const {
104    SkASSERT(fDataStream->hasLength());
105    return fDataStream->getLength();
106}
107
108bool SkPDFStream::populate(SkPDFCatalog* catalog) {
109    if (fState == kUnused_State) {
110        if (!skip_compression(catalog) && SkFlate::HaveFlate()) {
111            SkDynamicMemoryWStream compressedData;
112
113            SkAssertResult(
114                    SkFlate::Deflate(fDataStream.get(), &compressedData));
115            SkAssertResult(fDataStream->rewind());
116            if (compressedData.getOffset() < this->dataSize()) {
117                SkAutoTUnref<SkStream> compressed(
118                        compressedData.detachAsStream());
119                this->setData(compressed.get());
120                insertName("Filter", "FlateDecode");
121            }
122            fState = kCompressed_State;
123        } else {
124            fState = kNoCompression_State;
125        }
126        insertInt("Length", this->dataSize());
127    } else if (fState == kNoCompression_State && !skip_compression(catalog) &&
128               SkFlate::HaveFlate()) {
129        if (!fSubstitute.get()) {
130            fSubstitute.reset(new SkPDFStream(*this));
131            catalog->setSubstitute(this, fSubstitute.get());
132        }
133        return false;
134    }
135    return true;
136}
137