• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
emit(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)22 void SkPDFObject::emit(SkWStream* stream, SkPDFCatalog* catalog,
23                        bool indirect) {
24     SkPDFObject* realObject = catalog->getSubstituteObject(this);
25     return realObject->emitObject(stream, catalog, indirect);
26 }
27 
getOutputSize(SkPDFCatalog * catalog,bool indirect)28 size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
29     SkDynamicMemoryWStream buffer;
30     emit(&buffer, catalog, indirect);
31     return buffer.getOffset();
32 }
33 
getResources(const SkTSet<SkPDFObject * > & knownResourceObjects,SkTSet<SkPDFObject * > * newResourceObjects)34 void SkPDFObject::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
35                                SkTSet<SkPDFObject*>* newResourceObjects) {}
36 
emitIndirectObject(SkWStream * stream,SkPDFCatalog * catalog)37 void 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 
getIndirectOutputSize(SkPDFCatalog * catalog)44 size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
45     return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
46         this->getOutputSize(catalog, false) + strlen("\nendobj\n");
47 }
48 
AddResourceHelper(SkPDFObject * resource,SkTDArray<SkPDFObject * > * list)49 void SkPDFObject::AddResourceHelper(SkPDFObject* resource,
50                                     SkTDArray<SkPDFObject*>* list) {
51     list->push(resource);
52     resource->ref();
53 }
54 
GetResourcesHelper(const SkTDArray<SkPDFObject * > * resources,const SkTSet<SkPDFObject * > & knownResourceObjects,SkTSet<SkPDFObject * > * newResourceObjects)55 void 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 
SkPDFObjRef(SkPDFObject * obj)74 SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
75     SkSafeRef(obj);
76 }
77 
~SkPDFObjRef()78 SkPDFObjRef::~SkPDFObjRef() {}
79 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)80 void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
81                              bool indirect) {
82     SkASSERT(!indirect);
83     catalog->emitObjectNumber(stream, fObj.get());
84     stream->writeText(" R");
85 }
86 
getOutputSize(SkPDFCatalog * catalog,bool indirect)87 size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
88     SkASSERT(!indirect);
89     return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
90 }
91 
SkPDFInt(int32_t value)92 SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
~SkPDFInt()93 SkPDFInt::~SkPDFInt() {}
94 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)95 void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
96                           bool indirect) {
97     if (indirect) {
98         return emitIndirectObject(stream, catalog);
99     }
100     stream->writeDecAsText(fValue);
101 }
102 
SkPDFBool(bool value)103 SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
~SkPDFBool()104 SkPDFBool::~SkPDFBool() {}
105 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)106 void 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 
getOutputSize(SkPDFCatalog * catalog,bool indirect)116 size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
117     SkASSERT(!indirect);
118     if (fValue) {
119         return strlen("true");
120     }
121     return strlen("false");
122 }
123 
SkPDFScalar(SkScalar value)124 SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
~SkPDFScalar()125 SkPDFScalar::~SkPDFScalar() {}
126 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)127 void 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
Append(SkScalar value,SkWStream * stream)137 void 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 
SkPDFString(const char value[])185 SkPDFString::SkPDFString(const char value[])
186     : fValue(FormatString(value, strlen(value))) {
187 }
188 
SkPDFString(const SkString & value)189 SkPDFString::SkPDFString(const SkString& value)
190     : fValue(FormatString(value.c_str(), value.size())) {
191 }
192 
SkPDFString(const uint16_t * value,size_t len,bool wideChars)193 SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
194     : fValue(FormatString(value, len, wideChars)) {
195 }
196 
~SkPDFString()197 SkPDFString::~SkPDFString() {}
198 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)199 void 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 
getOutputSize(SkPDFCatalog * catalog,bool indirect)206 size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
207     if (indirect)
208         return getIndirectOutputSize(catalog);
209     return fValue.size();
210 }
211 
212 // static
FormatString(const char * input,size_t len)213 SkString SkPDFString::FormatString(const char* input, size_t len) {
214     return DoFormatString(input, len, false, false);
215 }
216 
FormatString(const uint16_t * input,size_t len,bool wideChars)217 SkString SkPDFString::FormatString(const uint16_t* input, size_t len,
218                                    bool wideChars) {
219     return DoFormatString(input, len, true, wideChars);
220 }
221 
222 // static
DoFormatString(const void * input,size_t len,bool wideInput,bool wideOutput)223 SkString 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 
SkPDFName(const char name[])277 SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {}
SkPDFName(const SkString & name)278 SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {}
~SkPDFName()279 SkPDFName::~SkPDFName() {}
280 
operator ==(const SkPDFName & b) const281 bool SkPDFName::operator==(const SkPDFName& b) const {
282     return fValue == b.fValue;
283 }
284 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)285 void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
286                            bool indirect) {
287     SkASSERT(!indirect);
288     stream->write(fValue.c_str(), fValue.size());
289 }
290 
getOutputSize(SkPDFCatalog * catalog,bool indirect)291 size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
292     SkASSERT(!indirect);
293     return fValue.size();
294 }
295 
296 // static
FormatName(const SkString & input)297 SkString 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 
SkPDFArray()316 SkPDFArray::SkPDFArray() {}
~SkPDFArray()317 SkPDFArray::~SkPDFArray() {
318     fValue.unrefAll();
319 }
320 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)321 void 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 
getOutputSize(SkPDFCatalog * catalog,bool indirect)337 size_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 
reserve(int length)352 void SkPDFArray::reserve(int length) {
353     SkASSERT(length <= kMaxLen);
354     fValue.setReserve(length);
355 }
356 
setAt(int offset,SkPDFObject * value)357 SkPDFObject* 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 
append(SkPDFObject * value)365 SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
366     SkASSERT(fValue.count() < kMaxLen);
367     value->ref();
368     fValue.push(value);
369     return value;
370 }
371 
appendInt(int32_t value)372 void SkPDFArray::appendInt(int32_t value) {
373     SkASSERT(fValue.count() < kMaxLen);
374     fValue.push(new SkPDFInt(value));
375 }
376 
appendScalar(SkScalar value)377 void SkPDFArray::appendScalar(SkScalar value) {
378     SkASSERT(fValue.count() < kMaxLen);
379     fValue.push(new SkPDFScalar(value));
380 }
381 
appendName(const char name[])382 void SkPDFArray::appendName(const char name[]) {
383     SkASSERT(fValue.count() < kMaxLen);
384     fValue.push(new SkPDFName(name));
385 }
386 
387 ///////////////////////////////////////////////////////////////////////////////
388 
SkPDFDict()389 SkPDFDict::SkPDFDict() {}
390 
SkPDFDict(const char type[])391 SkPDFDict::SkPDFDict(const char type[]) {
392     insertName("Type", type);
393 }
394 
~SkPDFDict()395 SkPDFDict::~SkPDFDict() {
396     clear();
397 }
398 
size() const399 int SkPDFDict::size() const {
400     SkAutoMutexAcquire lock(fMutex);
401     return fValue.count();
402 }
403 
404 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)405 void 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 
getOutputSize(SkPDFCatalog * catalog,bool indirect)427 size_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 
append(SkPDFName * key,SkPDFObject * value)446 SkPDFObject*  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 
insert(SkPDFName * key,SkPDFObject * value)456 SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
457     return this->append(SkRef(key), SkRef(value));
458 }
459 
insert(const char key[],SkPDFObject * value)460 SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
461     return this->append(new SkPDFName(key), SkRef(value));
462 }
463 
insertInt(const char key[],int32_t value)464 void SkPDFDict::insertInt(const char key[], int32_t value) {
465     (void)this->append(new SkPDFName(key), new SkPDFInt(value));
466 }
467 
insertScalar(const char key[],SkScalar value)468 void SkPDFDict::insertScalar(const char key[], SkScalar value) {
469     (void)this->append(new SkPDFName(key), new SkPDFScalar(value));
470 }
471 
insertName(const char key[],const char name[])472 void SkPDFDict::insertName(const char key[], const char name[]) {
473     (void)this->append(new SkPDFName(key), new SkPDFName(name));
474 }
475 
clear()476 void 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 
remove(const char key[])487 void 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 
mergeFrom(const SkPDFDict & other)503 void 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