1
2/*
3 * Copyright 2011 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 "SkPDFCatalog.h"
11#include "SkPDFTypes.h"
12#include "SkStream.h"
13
14#ifdef SK_BUILD_FOR_WIN
15    #define SNPRINTF    _snprintf
16#else
17    #define SNPRINTF    snprintf
18#endif
19
20///////////////////////////////////////////////////////////////////////////////
21
22void SkPDFObject::emit(SkWStream* stream, SkPDFCatalog* catalog,
23                       bool indirect) {
24    SkPDFObject* realObject = catalog->getSubstituteObject(this);
25    return realObject->emitObject(stream, catalog, indirect);
26}
27
28size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
29    SkDynamicMemoryWStream buffer;
30    emit(&buffer, catalog, indirect);
31    return buffer.getOffset();
32}
33
34void SkPDFObject::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
35                               SkTSet<SkPDFObject*>* newResourceObjects) {}
36
37void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) {
38    catalog->emitObjectNumber(stream, this);
39    stream->writeText(" obj\n");
40    emit(stream, catalog, false);
41    stream->writeText("\nendobj\n");
42}
43
44size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
45    return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
46        this->getOutputSize(catalog, false) + strlen("\nendobj\n");
47}
48
49void SkPDFObject::AddResourceHelper(SkPDFObject* resource,
50                                    SkTDArray<SkPDFObject*>* list) {
51    list->push(resource);
52    resource->ref();
53}
54
55void SkPDFObject::GetResourcesHelper(
56        const SkTDArray<SkPDFObject*>* resources,
57        const SkTSet<SkPDFObject*>& knownResourceObjects,
58        SkTSet<SkPDFObject*>* newResourceObjects) {
59    if (resources->count()) {
60        newResourceObjects->setReserve(
61            newResourceObjects->count() + resources->count());
62        for (int i = 0; i < resources->count(); i++) {
63            if (!knownResourceObjects.contains((*resources)[i]) &&
64                    !newResourceObjects->contains((*resources)[i])) {
65                newResourceObjects->add((*resources)[i]);
66                (*resources)[i]->ref();
67                (*resources)[i]->getResources(knownResourceObjects,
68                                              newResourceObjects);
69            }
70        }
71    }
72}
73
74SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
75    SkSafeRef(obj);
76}
77
78SkPDFObjRef::~SkPDFObjRef() {}
79
80void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
81                             bool indirect) {
82    SkASSERT(!indirect);
83    catalog->emitObjectNumber(stream, fObj.get());
84    stream->writeText(" R");
85}
86
87size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
88    SkASSERT(!indirect);
89    return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
90}
91
92SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
93SkPDFInt::~SkPDFInt() {}
94
95void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
96                          bool indirect) {
97    if (indirect) {
98        return emitIndirectObject(stream, catalog);
99    }
100    stream->writeDecAsText(fValue);
101}
102
103SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
104SkPDFBool::~SkPDFBool() {}
105
106void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
107                          bool indirect) {
108    SkASSERT(!indirect);
109    if (fValue) {
110        stream->writeText("true");
111    } else {
112        stream->writeText("false");
113    }
114}
115
116size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
117    SkASSERT(!indirect);
118    if (fValue) {
119        return strlen("true");
120    }
121    return strlen("false");
122}
123
124SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
125SkPDFScalar::~SkPDFScalar() {}
126
127void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
128                             bool indirect) {
129    if (indirect) {
130        return emitIndirectObject(stream, catalog);
131    }
132
133    Append(fValue, stream);
134}
135
136// static
137void SkPDFScalar::Append(SkScalar value, SkWStream* stream) {
138    // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
139    // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
140    // When using floats that are outside the whole value range, we can use
141    // integers instead.
142
143#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
144    if (value > 32767 || value < -32767) {
145        stream->writeDecAsText(SkScalarRoundToInt(value));
146        return;
147    }
148
149    char buffer[SkStrAppendScalar_MaxSize];
150    char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
151    stream->write(buffer, end - buffer);
152    return;
153#endif  // !SK_ALLOW_LARGE_PDF_SCALARS
154
155#if defined(SK_ALLOW_LARGE_PDF_SCALARS)
156    // Floats have 24bits of significance, so anything outside that range is
157    // no more precise than an int. (Plus PDF doesn't support scientific
158    // notation, so this clamps to SK_Max/MinS32).
159    if (value > (1 << 24) || value < -(1 << 24)) {
160        stream->writeDecAsText(value);
161        return;
162    }
163    // Continue to enforce the PDF limits for small floats.
164    if (value < 1.0f/65536 && value > -1.0f/65536) {
165        stream->writeDecAsText(0);
166        return;
167    }
168    // SkStrAppendFloat might still use scientific notation, so use snprintf
169    // directly..
170    static const int kFloat_MaxSize = 19;
171    char buffer[kFloat_MaxSize];
172    int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
173    // %f always prints trailing 0s, so strip them.
174    for (; buffer[len - 1] == '0' && len > 0; len--) {
175        buffer[len - 1] = '\0';
176    }
177    if (buffer[len - 1] == '.') {
178        buffer[len - 1] = '\0';
179    }
180    stream->writeText(buffer);
181    return;
182#endif  // SK_ALLOW_LARGE_PDF_SCALARS
183}
184
185SkPDFString::SkPDFString(const char value[])
186    : fValue(FormatString(value, strlen(value))) {
187}
188
189SkPDFString::SkPDFString(const SkString& value)
190    : fValue(FormatString(value.c_str(), value.size())) {
191}
192
193SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
194    : fValue(FormatString(value, len, wideChars)) {
195}
196
197SkPDFString::~SkPDFString() {}
198
199void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
200                             bool indirect) {
201    if (indirect)
202        return emitIndirectObject(stream, catalog);
203    stream->write(fValue.c_str(), fValue.size());
204}
205
206size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
207    if (indirect)
208        return getIndirectOutputSize(catalog);
209    return fValue.size();
210}
211
212// static
213SkString SkPDFString::FormatString(const char* input, size_t len) {
214    return DoFormatString(input, len, false, false);
215}
216
217SkString SkPDFString::FormatString(const uint16_t* input, size_t len,
218                                   bool wideChars) {
219    return DoFormatString(input, len, true, wideChars);
220}
221
222// static
223SkString SkPDFString::DoFormatString(const void* input, size_t len,
224                                     bool wideInput, bool wideOutput) {
225    SkASSERT(len <= kMaxLen);
226    const uint16_t* win = (const uint16_t*) input;
227    const char* cin = (const char*) input;
228
229    if (wideOutput) {
230        SkASSERT(wideInput);
231        SkString result;
232        result.append("<");
233        for (size_t i = 0; i < len; i++) {
234            result.appendHex(win[i], 4);
235        }
236        result.append(">");
237        return result;
238    }
239
240    // 7-bit clean is a heuristic to decide what string format to use;
241    // a 7-bit clean string should require little escaping.
242    bool sevenBitClean = true;
243    for (size_t i = 0; i < len; i++) {
244        SkASSERT(!wideInput || !(win[i] & ~0xFF));
245        char val = wideInput ? win[i] : cin[i];
246        if (val > '~' || val < ' ') {
247            sevenBitClean = false;
248            break;
249        }
250    }
251
252    SkString result;
253    if (sevenBitClean) {
254        result.append("(");
255        for (size_t i = 0; i < len; i++) {
256            SkASSERT(!wideInput || !(win[i] & ~0xFF));
257            char val = wideInput ? win[i] : cin[i];
258            if (val == '\\' || val == '(' || val == ')') {
259                result.append("\\");
260            }
261            result.append(&val, 1);
262        }
263        result.append(")");
264    } else {
265        result.append("<");
266        for (size_t i = 0; i < len; i++) {
267            SkASSERT(!wideInput || !(win[i] & ~0xFF));
268            unsigned char val = wideInput ? win[i] : cin[i];
269            result.appendHex(val, 2);
270        }
271        result.append(">");
272    }
273
274    return result;
275}
276
277SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {}
278SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {}
279SkPDFName::~SkPDFName() {}
280
281bool SkPDFName::operator==(const SkPDFName& b) const {
282    return fValue == b.fValue;
283}
284
285void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
286                           bool indirect) {
287    SkASSERT(!indirect);
288    stream->write(fValue.c_str(), fValue.size());
289}
290
291size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
292    SkASSERT(!indirect);
293    return fValue.size();
294}
295
296// static
297SkString SkPDFName::FormatName(const SkString& input) {
298    SkASSERT(input.size() <= kMaxLen);
299    // TODO(vandebo) If more escaping is needed, improve the linear scan.
300    static const char escaped[] = "#/%()<>[]{}";
301
302    SkString result("/");
303    for (size_t i = 0; i < input.size(); i++) {
304        if (input[i] & 0x80 || input[i] < '!' || strchr(escaped, input[i])) {
305            result.append("#");
306            // Mask with 0xFF to avoid sign extension. i.e. #FFFFFF81
307            result.appendHex(input[i] & 0xFF, 2);
308        } else {
309            result.append(input.c_str() + i, 1);
310        }
311    }
312
313    return result;
314}
315
316SkPDFArray::SkPDFArray() {}
317SkPDFArray::~SkPDFArray() {
318    fValue.unrefAll();
319}
320
321void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
322                            bool indirect) {
323    if (indirect) {
324        return emitIndirectObject(stream, catalog);
325    }
326
327    stream->writeText("[");
328    for (int i = 0; i < fValue.count(); i++) {
329        fValue[i]->emit(stream, catalog, false);
330        if (i + 1 < fValue.count()) {
331            stream->writeText(" ");
332        }
333    }
334    stream->writeText("]");
335}
336
337size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
338    if (indirect) {
339        return getIndirectOutputSize(catalog);
340    }
341
342    size_t result = strlen("[]");
343    if (fValue.count()) {
344        result += fValue.count() - 1;
345    }
346    for (int i = 0; i < fValue.count(); i++) {
347        result += fValue[i]->getOutputSize(catalog, false);
348    }
349    return result;
350}
351
352void SkPDFArray::reserve(int length) {
353    SkASSERT(length <= kMaxLen);
354    fValue.setReserve(length);
355}
356
357SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) {
358    SkASSERT(offset < fValue.count());
359    value->ref();
360    fValue[offset]->unref();
361    fValue[offset] = value;
362    return value;
363}
364
365SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
366    SkASSERT(fValue.count() < kMaxLen);
367    value->ref();
368    fValue.push(value);
369    return value;
370}
371
372void SkPDFArray::appendInt(int32_t value) {
373    SkASSERT(fValue.count() < kMaxLen);
374    fValue.push(new SkPDFInt(value));
375}
376
377void SkPDFArray::appendScalar(SkScalar value) {
378    SkASSERT(fValue.count() < kMaxLen);
379    fValue.push(new SkPDFScalar(value));
380}
381
382void SkPDFArray::appendName(const char name[]) {
383    SkASSERT(fValue.count() < kMaxLen);
384    fValue.push(new SkPDFName(name));
385}
386
387///////////////////////////////////////////////////////////////////////////////
388
389SkPDFDict::SkPDFDict() {}
390
391SkPDFDict::SkPDFDict(const char type[]) {
392    insertName("Type", type);
393}
394
395SkPDFDict::~SkPDFDict() {
396    clear();
397}
398
399int SkPDFDict::size() const {
400    SkAutoMutexAcquire lock(fMutex);
401    return fValue.count();
402}
403
404
405void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
406                           bool indirect) {
407    if (indirect) {
408        return emitIndirectObject(stream, catalog);
409    }
410
411    SkAutoMutexAcquire lock(fMutex); // If another thread triggers a
412                                     // resize while this thread is in
413                                     // the for-loop, we can be left
414                                     // with a bad fValue[i] reference.
415    stream->writeText("<<");
416    for (int i = 0; i < fValue.count(); i++) {
417        SkASSERT(fValue[i].key);
418        SkASSERT(fValue[i].value);
419        fValue[i].key->emitObject(stream, catalog, false);
420        stream->writeText(" ");
421        fValue[i].value->emit(stream, catalog, false);
422        stream->writeText("\n");
423    }
424    stream->writeText(">>");
425}
426
427size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
428    if (indirect) {
429        return getIndirectOutputSize(catalog);
430    }
431
432    SkAutoMutexAcquire lock(fMutex); // If another thread triggers a
433                                     // resize while this thread is in
434                                     // the for-loop, we can be left
435                                     // with a bad fValue[i] reference.
436    size_t result = strlen("<<>>") + (fValue.count() * 2);
437    for (int i = 0; i < fValue.count(); i++) {
438        SkASSERT(fValue[i].key);
439        SkASSERT(fValue[i].value);
440        result += fValue[i].key->getOutputSize(catalog, false);
441        result += fValue[i].value->getOutputSize(catalog, false);
442    }
443    return result;
444}
445
446SkPDFObject*  SkPDFDict::append(SkPDFName* key, SkPDFObject* value) {
447    SkASSERT(key);
448    SkASSERT(value);
449    SkAutoMutexAcquire lock(fMutex); // If the SkTDArray resizes while
450                                     // two threads access array, one
451                                     // is left with a bad pointer.
452    *(fValue.append()) = Rec(key, value);
453    return value;
454}
455
456SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
457    return this->append(SkRef(key), SkRef(value));
458}
459
460SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
461    return this->append(new SkPDFName(key), SkRef(value));
462}
463
464void SkPDFDict::insertInt(const char key[], int32_t value) {
465    (void)this->append(new SkPDFName(key), new SkPDFInt(value));
466}
467
468void SkPDFDict::insertScalar(const char key[], SkScalar value) {
469    (void)this->append(new SkPDFName(key), new SkPDFScalar(value));
470}
471
472void SkPDFDict::insertName(const char key[], const char name[]) {
473    (void)this->append(new SkPDFName(key), new SkPDFName(name));
474}
475
476void SkPDFDict::clear() {
477    SkAutoMutexAcquire lock(fMutex);
478    for (int i = 0; i < fValue.count(); i++) {
479        SkASSERT(fValue[i].key);
480        SkASSERT(fValue[i].value);
481        fValue[i].key->unref();
482        fValue[i].value->unref();
483    }
484    fValue.reset();
485}
486
487void SkPDFDict::remove(const char key[]) {
488    SkASSERT(key);
489    SkPDFName name(key);
490    SkAutoMutexAcquire lock(fMutex);
491    for (int i = 0; i < fValue.count(); i++) {
492        SkASSERT(fValue[i].key);
493        if (*(fValue[i].key) == name) {
494            fValue[i].key->unref();
495            SkASSERT(fValue[i].value);
496            fValue[i].value->unref();
497            fValue.removeShuffle(i);
498            return;
499        }
500    }
501}
502
503void SkPDFDict::mergeFrom(const SkPDFDict& other) {
504    SkAutoMutexAcquire lockOther(other.fMutex);
505    SkTDArray<Rec> copy(other.fValue);
506    lockOther.release();  // Do not hold both mutexes at once.
507
508    SkAutoMutexAcquire lock(fMutex);
509    for (int i = 0; i < copy.count(); i++) {
510        *(fValue.append()) = Rec(SkRef(copy[i].key), SkRef(copy[i].value));
511    }
512}
513