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