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 "SkData.h"
9#include "SkDeflate.h"
10#include "SkMakeUnique.h"
11#include "SkPDFTypes.h"
12#include "SkPDFUtils.h"
13#include "SkStream.h"
14#include "SkStreamPriv.h"
15
16////////////////////////////////////////////////////////////////////////////////
17
18SkString* pun(char* x) { return reinterpret_cast<SkString*>(x); }
19const SkString* pun(const char* x) {
20    return reinterpret_cast<const SkString*>(x);
21}
22
23SkPDFUnion::SkPDFUnion(Type t) : fType(t) {}
24
25SkPDFUnion::~SkPDFUnion() {
26    switch (fType) {
27        case Type::kNameSkS:
28        case Type::kStringSkS:
29            pun(fSkString)->~SkString();
30            return;
31        case Type::kObjRef:
32        case Type::kObject:
33            SkASSERT(fObject);
34            fObject->unref();
35            return;
36        default:
37            return;
38    }
39}
40
41SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) {
42    if (this != &other) {
43        this->~SkPDFUnion();
44        new (this) SkPDFUnion(std::move(other));
45    }
46    return *this;
47}
48
49SkPDFUnion::SkPDFUnion(SkPDFUnion&& other) {
50    SkASSERT(this != &other);
51    memcpy(this, &other, sizeof(*this));
52    other.fType = Type::kDestroyed;
53}
54
55#if 0
56SkPDFUnion SkPDFUnion::copy() const {
57    SkPDFUnion u(fType);
58    memcpy(&u, this, sizeof(u));
59    switch (fType) {
60        case Type::kNameSkS:
61        case Type::kStringSkS:
62            new (pun(u.fSkString)) SkString(*pun(fSkString));
63            return u;
64        case Type::kObjRef:
65        case Type::kObject:
66            SkRef(u.fObject);
67            return u;
68        default:
69            return u;
70    }
71}
72SkPDFUnion& SkPDFUnion::operator=(const SkPDFUnion& other) {
73    return *this = other.copy();
74}
75SkPDFUnion::SkPDFUnion(const SkPDFUnion& other) {
76    *this = other.copy();
77}
78#endif
79
80bool SkPDFUnion::isName() const {
81    return Type::kName == fType || Type::kNameSkS == fType;
82}
83
84#ifdef SK_DEBUG
85// Most names need no escaping.  Such names are handled as static
86// const strings.
87bool is_valid_name(const char* n) {
88    static const char kControlChars[] = "/%()<>[]{}";
89    while (*n) {
90        if (*n < '!' || *n > '~' || strchr(kControlChars, *n)) {
91            return false;
92        }
93        ++n;
94    }
95    return true;
96}
97#endif  // SK_DEBUG
98
99// Given an arbitrary string, write it as a valid name (not including
100// leading slash).
101static void write_name_escaped(SkWStream* o, const char* name) {
102    static const char kToEscape[] = "#/%()<>[]{}";
103    for (const uint8_t* n = reinterpret_cast<const uint8_t*>(name); *n; ++n) {
104        uint8_t v = *n;
105        if (v < '!' || v > '~' || strchr(kToEscape, v)) {
106            char buffer[3] = {'#',
107                              SkHexadecimalDigits::gUpper[v >> 4],
108                              SkHexadecimalDigits::gUpper[v & 0xF]};
109            o->write(buffer, sizeof(buffer));
110        } else {
111            o->write(n, 1);
112        }
113    }
114}
115
116void SkPDFUnion::emitObject(SkWStream* stream,
117                            const SkPDFObjNumMap& objNumMap) const {
118    switch (fType) {
119        case Type::kInt:
120            stream->writeDecAsText(fIntValue);
121            return;
122        case Type::kColorComponent:
123            SkPDFUtils::AppendColorComponent(SkToU8(fIntValue), stream);
124            return;
125        case Type::kBool:
126            stream->writeText(fBoolValue ? "true" : "false");
127            return;
128        case Type::kScalar:
129            SkPDFUtils::AppendScalar(fScalarValue, stream);
130            return;
131        case Type::kName:
132            stream->writeText("/");
133            SkASSERT(is_valid_name(fStaticString));
134            stream->writeText(fStaticString);
135            return;
136        case Type::kString:
137            SkASSERT(fStaticString);
138            SkPDFUtils::WriteString(stream, fStaticString,
139                                    strlen(fStaticString));
140            return;
141        case Type::kNameSkS:
142            stream->writeText("/");
143            write_name_escaped(stream, pun(fSkString)->c_str());
144            return;
145        case Type::kStringSkS:
146            SkPDFUtils::WriteString(stream, pun(fSkString)->c_str(),
147                                    pun(fSkString)->size());
148            return;
149        case Type::kObjRef:
150            stream->writeDecAsText(objNumMap.getObjectNumber(fObject));
151            stream->writeText(" 0 R");  // Generation number is always 0.
152            return;
153        case Type::kObject:
154            fObject->emitObject(stream, objNumMap);
155            return;
156        default:
157            SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
158    }
159}
160
161void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap) const {
162    switch (fType) {
163        case Type::kInt:
164        case Type::kColorComponent:
165        case Type::kBool:
166        case Type::kScalar:
167        case Type::kName:
168        case Type::kString:
169        case Type::kNameSkS:
170        case Type::kStringSkS:
171            return;  // These have no resources.
172        case Type::kObjRef:
173            objNumMap->addObjectRecursively(fObject);
174            return;
175        case Type::kObject:
176            fObject->addResources(objNumMap);
177            return;
178        default:
179            SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
180    }
181}
182
183SkPDFUnion SkPDFUnion::Int(int32_t value) {
184    SkPDFUnion u(Type::kInt);
185    u.fIntValue = value;
186    return u;
187}
188
189SkPDFUnion SkPDFUnion::ColorComponent(uint8_t value) {
190    SkPDFUnion u(Type::kColorComponent);
191    u.fIntValue = value;
192    return u;
193}
194
195SkPDFUnion SkPDFUnion::Bool(bool value) {
196    SkPDFUnion u(Type::kBool);
197    u.fBoolValue = value;
198    return u;
199}
200
201SkPDFUnion SkPDFUnion::Scalar(SkScalar value) {
202    SkPDFUnion u(Type::kScalar);
203    u.fScalarValue = value;
204    return u;
205}
206
207SkPDFUnion SkPDFUnion::Name(const char* value) {
208    SkPDFUnion u(Type::kName);
209    SkASSERT(value);
210    SkASSERT(is_valid_name(value));
211    u.fStaticString = value;
212    return u;
213}
214
215SkPDFUnion SkPDFUnion::String(const char* value) {
216    SkPDFUnion u(Type::kString);
217    SkASSERT(value);
218    u.fStaticString = value;
219    return u;
220}
221
222SkPDFUnion SkPDFUnion::Name(const SkString& s) {
223    SkPDFUnion u(Type::kNameSkS);
224    new (pun(u.fSkString)) SkString(s);
225    return u;
226}
227
228SkPDFUnion SkPDFUnion::String(const SkString& s) {
229    SkPDFUnion u(Type::kStringSkS);
230    new (pun(u.fSkString)) SkString(s);
231    return u;
232}
233
234SkPDFUnion SkPDFUnion::ObjRef(sk_sp<SkPDFObject> objSp) {
235    SkPDFUnion u(Type::kObjRef);
236    SkASSERT(objSp.get());
237    u.fObject = objSp.release();  // take ownership into union{}
238    return u;
239}
240
241SkPDFUnion SkPDFUnion::Object(sk_sp<SkPDFObject> objSp) {
242    SkPDFUnion u(Type::kObject);
243    SkASSERT(objSp.get());
244    u.fObject = objSp.release();  // take ownership into union{}
245    return u;
246}
247
248////////////////////////////////////////////////////////////////////////////////
249
250#if 0  // Enable if needed.
251void SkPDFAtom::emitObject(SkWStream* stream,
252                           const SkPDFObjNumMap& objNumMap) const {
253    fValue.emitObject(stream, objNumMap);
254}
255void SkPDFAtom::addResources(SkPDFObjNumMap* map) const {
256    fValue.addResources(map);
257}
258#endif  // 0
259
260////////////////////////////////////////////////////////////////////////////////
261
262SkPDFArray::SkPDFArray() { SkDEBUGCODE(fDumped = false;) }
263
264SkPDFArray::~SkPDFArray() { this->drop(); }
265
266void SkPDFArray::drop() {
267    fValues.reset();
268    SkDEBUGCODE(fDumped = true;)
269}
270
271int SkPDFArray::size() const { return fValues.count(); }
272
273void SkPDFArray::reserve(int length) {
274    fValues.reserve(length);
275}
276
277void SkPDFArray::emitObject(SkWStream* stream,
278                            const SkPDFObjNumMap& objNumMap) const {
279    SkASSERT(!fDumped);
280    stream->writeText("[");
281    for (int i = 0; i < fValues.count(); i++) {
282        fValues[i].emitObject(stream, objNumMap);
283        if (i + 1 < fValues.count()) {
284            stream->writeText(" ");
285        }
286    }
287    stream->writeText("]");
288}
289
290void SkPDFArray::addResources(SkPDFObjNumMap* catalog) const {
291    SkASSERT(!fDumped);
292    for (const SkPDFUnion& value : fValues) {
293        value.addResources(catalog);
294    }
295}
296
297void SkPDFArray::append(SkPDFUnion&& value) {
298    fValues.emplace_back(std::move(value));
299}
300
301void SkPDFArray::appendInt(int32_t value) {
302    this->append(SkPDFUnion::Int(value));
303}
304
305void SkPDFArray::appendColorComponent(uint8_t value) {
306    this->append(SkPDFUnion::ColorComponent(value));
307}
308
309void SkPDFArray::appendBool(bool value) {
310    this->append(SkPDFUnion::Bool(value));
311}
312
313void SkPDFArray::appendScalar(SkScalar value) {
314    this->append(SkPDFUnion::Scalar(value));
315}
316
317void SkPDFArray::appendName(const char name[]) {
318    this->append(SkPDFUnion::Name(SkString(name)));
319}
320
321void SkPDFArray::appendName(const SkString& name) {
322    this->append(SkPDFUnion::Name(name));
323}
324
325void SkPDFArray::appendString(const SkString& value) {
326    this->append(SkPDFUnion::String(value));
327}
328
329void SkPDFArray::appendString(const char value[]) {
330    this->append(SkPDFUnion::String(value));
331}
332
333void SkPDFArray::appendObject(sk_sp<SkPDFObject> objSp) {
334    this->append(SkPDFUnion::Object(std::move(objSp)));
335}
336
337void SkPDFArray::appendObjRef(sk_sp<SkPDFObject> objSp) {
338    this->append(SkPDFUnion::ObjRef(std::move(objSp)));
339}
340
341///////////////////////////////////////////////////////////////////////////////
342
343SkPDFDict::~SkPDFDict() { this->drop(); }
344
345void SkPDFDict::drop() {
346    fRecords.reset();
347    SkDEBUGCODE(fDumped = true;)
348}
349
350SkPDFDict::SkPDFDict(const char type[]) {
351    SkDEBUGCODE(fDumped = false;)
352    if (type) {
353        this->insertName("Type", type);
354    }
355}
356
357void SkPDFDict::emitObject(SkWStream* stream,
358                           const SkPDFObjNumMap& objNumMap) const {
359    stream->writeText("<<");
360    this->emitAll(stream, objNumMap);
361    stream->writeText(">>");
362}
363
364void SkPDFDict::emitAll(SkWStream* stream,
365                        const SkPDFObjNumMap& objNumMap) const {
366    SkASSERT(!fDumped);
367    for (int i = 0; i < fRecords.count(); i++) {
368        fRecords[i].fKey.emitObject(stream, objNumMap);
369        stream->writeText(" ");
370        fRecords[i].fValue.emitObject(stream, objNumMap);
371        if (i + 1 < fRecords.count()) {
372            stream->writeText("\n");
373        }
374    }
375}
376
377void SkPDFDict::addResources(SkPDFObjNumMap* catalog) const {
378    SkASSERT(!fDumped);
379    for (int i = 0; i < fRecords.count(); i++) {
380        fRecords[i].fKey.addResources(catalog);
381        fRecords[i].fValue.addResources(catalog);
382    }
383}
384
385int SkPDFDict::size() const { return fRecords.count(); }
386
387void SkPDFDict::reserve(int n) {
388    fRecords.reserve(n);
389}
390
391void SkPDFDict::insertObjRef(const char key[], sk_sp<SkPDFObject> objSp) {
392    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
393}
394
395void SkPDFDict::insertObjRef(const SkString& key, sk_sp<SkPDFObject> objSp) {
396    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
397}
398
399void SkPDFDict::insertObject(const char key[], sk_sp<SkPDFObject> objSp) {
400    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp))});
401}
402void SkPDFDict::insertObject(const SkString& key, sk_sp<SkPDFObject> objSp) {
403    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp))});
404}
405
406void SkPDFDict::insertBool(const char key[], bool value) {
407    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Bool(value)});
408}
409
410void SkPDFDict::insertInt(const char key[], int32_t value) {
411    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Int(value)});
412}
413
414void SkPDFDict::insertInt(const char key[], size_t value) {
415    this->insertInt(key, SkToS32(value));
416}
417
418void SkPDFDict::insertScalar(const char key[], SkScalar value) {
419    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Scalar(value)});
420}
421
422void SkPDFDict::insertName(const char key[], const char name[]) {
423    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Name(name)});
424}
425
426void SkPDFDict::insertName(const char key[], const SkString& name) {
427    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::Name(name)});
428}
429
430void SkPDFDict::insertString(const char key[], const char value[]) {
431    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::String(value)});
432}
433
434void SkPDFDict::insertString(const char key[], const SkString& value) {
435    fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::String(value)});
436}
437
438////////////////////////////////////////////////////////////////////////////////
439
440SkPDFSharedStream::SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data)
441    : fAsset(std::move(data)) {
442    SkASSERT(fAsset);
443}
444
445SkPDFSharedStream::~SkPDFSharedStream() { this->drop(); }
446
447void SkPDFSharedStream::drop() {
448    fAsset = nullptr;;
449    fDict.drop();
450}
451
452#ifdef SK_PDF_LESS_COMPRESSION
453void SkPDFSharedStream::emitObject(
454        SkWStream* stream,
455        const SkPDFObjNumMap& objNumMap) const {
456    SkASSERT(fAsset);
457    std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
458    SkASSERT(dup && dup->hasLength());
459    size_t length = dup->getLength();
460    stream->writeText("<<");
461    fDict.emitAll(stream, objNumMap);
462    stream->writeText("\n");
463    SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
464    stream->writeText(" ");
465    SkPDFUnion::Int(length).emitObject(stream, objNumMap);
466    stream->writeText("\n>>stream\n");
467    SkStreamCopy(stream, dup.get());
468    stream->writeText("\nendstream");
469}
470#else
471void SkPDFSharedStream::emitObject(
472        SkWStream* stream,
473        const SkPDFObjNumMap& objNumMap) const {
474    SkASSERT(fAsset);
475    SkDynamicMemoryWStream buffer;
476    SkDeflateWStream deflateWStream(&buffer);
477    // Since emitObject is const, this function doesn't change the dictionary.
478    std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());  // Cheap copy
479    SkASSERT(dup);
480    SkStreamCopy(&deflateWStream, dup.get());
481    deflateWStream.finalize();
482    size_t length = buffer.bytesWritten();
483    stream->writeText("<<");
484    fDict.emitAll(stream, objNumMap);
485    stream->writeText("\n");
486    SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
487    stream->writeText(" ");
488    SkPDFUnion::Int(length).emitObject(stream, objNumMap);
489    stream->writeText("\n");
490    SkPDFUnion::Name("Filter").emitObject(stream, objNumMap);
491    stream->writeText(" ");
492    SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap);
493    stream->writeText(">>");
494    stream->writeText(" stream\n");
495    buffer.writeToAndReset(stream);
496    stream->writeText("\nendstream");
497}
498#endif
499
500void SkPDFSharedStream::addResources(
501        SkPDFObjNumMap* catalog) const {
502    SkASSERT(fAsset);
503    fDict.addResources(catalog);
504}
505
506
507////////////////////////////////////////////////////////////////////////////////
508
509SkPDFStream:: SkPDFStream(sk_sp<SkData> data) {
510    this->setData(skstd::make_unique<SkMemoryStream>(std::move(data)));
511}
512
513SkPDFStream::SkPDFStream(std::unique_ptr<SkStreamAsset> stream) {
514    this->setData(std::move(stream));
515}
516
517SkPDFStream::SkPDFStream() {}
518
519SkPDFStream::~SkPDFStream() {}
520
521void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
522    SkASSERT(fCompressedData);
523    fDict.addResources(catalog);
524}
525
526void SkPDFStream::drop() {
527    fCompressedData.reset(nullptr);
528    fDict.drop();
529}
530
531void SkPDFStream::emitObject(SkWStream* stream,
532                             const SkPDFObjNumMap& objNumMap) const {
533    SkASSERT(fCompressedData);
534    fDict.emitObject(stream, objNumMap);
535    // duplicate (a cheap operation) preserves const on fCompressedData.
536    std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
537    SkASSERT(dup);
538    SkASSERT(dup->hasLength());
539    stream->writeText(" stream\n");
540    stream->writeStream(dup.get(), dup->getLength());
541    stream->writeText("\nendstream");
542}
543
544void SkPDFStream::setData(std::unique_ptr<SkStreamAsset> stream) {
545    SkASSERT(!fCompressedData);  // Only call this function once.
546    SkASSERT(stream);
547    // Code assumes that the stream starts at the beginning.
548
549    #ifdef SK_PDF_LESS_COMPRESSION
550    fCompressedData = std::move(stream);
551    SkASSERT(fCompressedData && fCompressedData->hasLength());
552    fDict.insertInt("Length", fCompressedData->getLength());
553    #else
554
555    SkASSERT(stream->hasLength());
556    SkDynamicMemoryWStream compressedData;
557    SkDeflateWStream deflateWStream(&compressedData);
558    if (stream->getLength() > 0) {
559        SkStreamCopy(&deflateWStream, stream.get());
560    }
561    deflateWStream.finalize();
562    size_t compressedLength = compressedData.bytesWritten();
563    size_t originalLength = stream->getLength();
564
565    if (originalLength <= compressedLength + strlen("/Filter_/FlateDecode_")) {
566        SkAssertResult(stream->rewind());
567        fCompressedData = std::move(stream);
568        fDict.insertInt("Length", originalLength);
569        return;
570    }
571    fCompressedData = compressedData.detachAsStream();
572    fDict.insertName("Filter", "FlateDecode");
573    fDict.insertInt("Length", compressedLength);
574    #endif
575}
576
577////////////////////////////////////////////////////////////////////////////////
578
579void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
580    if (obj && !fObjectNumbers.find(obj)) {
581        fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
582        fObjects.emplace_back(sk_ref_sp(obj));
583        obj->addResources(this);
584    }
585}
586
587int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
588    int32_t* objectNumberFound = fObjectNumbers.find(obj);
589    SkASSERT(objectNumberFound);
590    return *objectNumberFound;
591}
592
593#ifdef SK_PDF_IMAGE_STATS
594SkAtomic<int> gDrawImageCalls(0);
595SkAtomic<int> gJpegImageObjects(0);
596SkAtomic<int> gRegularImageObjects(0);
597
598void SkPDFImageDumpStats() {
599    SkDebugf("\ntotal PDF drawImage/drawBitmap calls: %d\n"
600             "total PDF jpeg images: %d\n"
601             "total PDF regular images: %d\n",
602             gDrawImageCalls.load(),
603             gJpegImageObjects.load(),
604             gRegularImageObjects.load());
605}
606#endif // SK_PDF_IMAGE_STATS
607