• Home
  • 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 #include "SkDeflate.h"
10 #include "SkPDFTypes.h"
11 #include "SkPDFUtils.h"
12 #include "SkStreamPriv.h"
13 
14 ////////////////////////////////////////////////////////////////////////////////
15 
pun(char * x)16 SkString* pun(char* x) { return reinterpret_cast<SkString*>(x); }
pun(const char * x)17 const SkString* pun(const char* x) {
18     return reinterpret_cast<const SkString*>(x);
19 }
20 
SkPDFUnion(Type t)21 SkPDFUnion::SkPDFUnion(Type t) : fType(t) {}
22 
~SkPDFUnion()23 SkPDFUnion::~SkPDFUnion() {
24     switch (fType) {
25         case Type::kNameSkS:
26         case Type::kStringSkS:
27             pun(fSkString)->~SkString();
28             return;
29         case Type::kObjRef:
30         case Type::kObject:
31             SkSafeUnref(fObject);
32             return;
33         default:
34             return;
35     }
36 }
37 
operator =(SkPDFUnion && other)38 SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) {
39     if (this != &other) {
40         this->~SkPDFUnion();
41         new (this) SkPDFUnion(other.move());
42     }
43     return *this;
44 }
45 
SkPDFUnion(SkPDFUnion && other)46 SkPDFUnion::SkPDFUnion(SkPDFUnion&& other) {
47     SkASSERT(this != &other);
48     memcpy(this, &other, sizeof(*this));
49     other.fType = Type::kDestroyed;
50 }
51 
52 #if 0
53 SkPDFUnion SkPDFUnion::copy() const {
54     SkPDFUnion u(fType);
55     memcpy(&u, this, sizeof(u));
56     switch (fType) {
57         case Type::kNameSkS:
58         case Type::kStringSkS:
59             new (pun(u.fSkString))  SkString                                   (*pun(fSkString));
60             return u.move();
61         case Type::kObjRef:
62         case Type::kObject:
63             SkRef(u.fObject);
64             return u.move();
65         default:
66             return u.move();
67     }
68 }
69 SkPDFUnion& SkPDFUnion::operator=(const SkPDFUnion& other) {
70     return *this = other.copy();
71 }
72 SkPDFUnion::SkPDFUnion(const SkPDFUnion& other) {
73     *this = other.copy();
74 }
75 #endif
76 
isName() const77 bool SkPDFUnion::isName() const {
78     return Type::kName == fType || Type::kNameSkS == fType;
79 }
80 
81 #ifdef SK_DEBUG
82 // Most names need no escaping.  Such names are handled as static
83 // const strings.
is_valid_name(const char * n)84 bool is_valid_name(const char* n) {
85     static const char kControlChars[] = "/%()<>[]{}";
86     while (*n) {
87         if (*n < '!' || *n > '~' || strchr(kControlChars, *n)) {
88             return false;
89         }
90         ++n;
91     }
92     return true;
93 }
94 #endif  // SK_DEBUG
95 
96 // Given an arbitrary string, write it as a valid name (not including
97 // leading slash).
write_name_escaped(SkWStream * o,const char * name)98 static void write_name_escaped(SkWStream* o, const char* name) {
99     static const char kToEscape[] = "#/%()<>[]{}";
100     static const char kHex[] = "0123456789ABCDEF";
101     for (const uint8_t* n = reinterpret_cast<const uint8_t*>(name); *n; ++n) {
102         if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) {
103             char buffer[3] = {'#', '\0', '\0'};
104             buffer[1] = kHex[(*n >> 4) & 0xF];
105             buffer[2] = kHex[*n & 0xF];
106             o->write(buffer, sizeof(buffer));
107         } else {
108             o->write(n, 1);
109         }
110     }
111 }
112 
write_string(SkWStream * o,const SkString & s)113 static void write_string(SkWStream* o, const SkString& s) {
114     o->write(s.c_str(), s.size());
115 }
116 
format_string(const SkString & s)117 static SkString format_string(const SkString& s) {
118     return SkPDFUtils::FormatString(s.c_str(), s.size());
119 }
120 
format_string(const char * s)121 static SkString format_string(const char* s) {
122     return SkPDFUtils::FormatString(s, strlen(s));
123 }
124 
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes) const125 void SkPDFUnion::emitObject(SkWStream* stream,
126                             const SkPDFObjNumMap& objNumMap,
127                             const SkPDFSubstituteMap& substitutes) const {
128     switch (fType) {
129         case Type::kInt:
130             stream->writeDecAsText(fIntValue);
131             return;
132         case Type::kBool:
133             stream->writeText(fBoolValue ? "true" : "false");
134             return;
135         case Type::kScalar:
136             SkPDFUtils::AppendScalar(fScalarValue, stream);
137             return;
138         case Type::kName:
139             stream->writeText("/");
140             SkASSERT(is_valid_name(fStaticString));
141             stream->writeText(fStaticString);
142             return;
143         case Type::kString:
144             SkASSERT(fStaticString);
145             write_string(stream, format_string(fStaticString));
146             return;
147         case Type::kNameSkS:
148             stream->writeText("/");
149             write_name_escaped(stream, pun(fSkString)->c_str());
150             return;
151         case Type::kStringSkS:
152             write_string(stream, format_string(*pun(fSkString)));
153             return;
154         case Type::kObjRef:
155             stream->writeDecAsText(objNumMap.getObjectNumber(
156                     substitutes.getSubstitute(fObject)));
157             stream->writeText(" 0 R");  // Generation number is always 0.
158             return;
159         case Type::kObject:
160             fObject->emitObject(stream, objNumMap, substitutes);
161             return;
162         default:
163             SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
164     }
165 }
166 
addResources(SkPDFObjNumMap * objNumMap,const SkPDFSubstituteMap & substituteMap) const167 void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
168                               const SkPDFSubstituteMap& substituteMap) const {
169     switch (fType) {
170         case Type::kInt:
171         case Type::kBool:
172         case Type::kScalar:
173         case Type::kName:
174         case Type::kString:
175         case Type::kNameSkS:
176         case Type::kStringSkS:
177             return;  // These have no resources.
178         case Type::kObjRef: {
179             SkPDFObject* obj = substituteMap.getSubstitute(fObject);
180             objNumMap->addObjectRecursively(obj, substituteMap);
181             return;
182         }
183         case Type::kObject:
184             fObject->addResources(objNumMap, substituteMap);
185             return;
186         default:
187             SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
188     }
189 }
190 
Int(int32_t value)191 SkPDFUnion SkPDFUnion::Int(int32_t value) {
192     SkPDFUnion u(Type::kInt);
193     u.fIntValue = value;
194     return u.move();
195 }
196 
Bool(bool value)197 SkPDFUnion SkPDFUnion::Bool(bool value) {
198     SkPDFUnion u(Type::kBool);
199     u.fBoolValue = value;
200     return u.move();
201 }
202 
Scalar(SkScalar value)203 SkPDFUnion SkPDFUnion::Scalar(SkScalar value) {
204     SkPDFUnion u(Type::kScalar);
205     u.fScalarValue = value;
206     return u.move();
207 }
208 
Name(const char * value)209 SkPDFUnion SkPDFUnion::Name(const char* value) {
210     SkPDFUnion u(Type::kName);
211     SkASSERT(value);
212     SkASSERT(is_valid_name(value));
213     u.fStaticString = value;
214     return u.move();
215 }
216 
String(const char * value)217 SkPDFUnion SkPDFUnion::String(const char* value) {
218     SkPDFUnion u(Type::kString);
219     SkASSERT(value);
220     u.fStaticString = value;
221     return u.move();
222 }
223 
Name(const SkString & s)224 SkPDFUnion SkPDFUnion::Name(const SkString& s) {
225     SkPDFUnion u(Type::kNameSkS);
226     new (pun(u.fSkString)) SkString(s);
227     return u.move();
228 }
229 
String(const SkString & s)230 SkPDFUnion SkPDFUnion::String(const SkString& s) {
231     SkPDFUnion u(Type::kStringSkS);
232     new (pun(u.fSkString)) SkString(s);
233     return u.move();
234 }
235 
ObjRef(SkPDFObject * ptr)236 SkPDFUnion SkPDFUnion::ObjRef(SkPDFObject* ptr) {
237     SkPDFUnion u(Type::kObjRef);
238     SkASSERT(ptr);
239     u.fObject = ptr;
240     return u.move();
241 }
242 
Object(SkPDFObject * ptr)243 SkPDFUnion SkPDFUnion::Object(SkPDFObject* ptr) {
244     SkPDFUnion u(Type::kObject);
245     SkASSERT(ptr);
246     u.fObject = ptr;
247     return u.move();
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
251 
252 #if 0  // Enable if needed.
253 void SkPDFAtom::emitObject(SkWStream* stream,
254                            const SkPDFObjNumMap& objNumMap,
255                            const SkPDFSubstituteMap& substitutes) const {
256     fValue.emitObject(stream, objNumMap, substitutes);
257 }
258 void SkPDFAtom::addResources(SkPDFObjNumMap* map,
259                              const SkPDFSubstituteMap& substitutes) const {
260     fValue.addResources(map, substitutes);
261 }
262 #endif  // 0
263 
264 ////////////////////////////////////////////////////////////////////////////////
265 
SkPDFArray()266 SkPDFArray::SkPDFArray() {}
~SkPDFArray()267 SkPDFArray::~SkPDFArray() {
268     for (SkPDFUnion& value : fValues) {
269         value.~SkPDFUnion();
270     }
271     fValues.reset();
272 }
273 
size() const274 int SkPDFArray::size() const { return fValues.count(); }
275 
reserve(int length)276 void SkPDFArray::reserve(int length) { fValues.setReserve(length); }
277 
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes) const278 void SkPDFArray::emitObject(SkWStream* stream,
279                             const SkPDFObjNumMap& objNumMap,
280                             const SkPDFSubstituteMap& substitutes) const {
281     stream->writeText("[");
282     for (int i = 0; i < fValues.count(); i++) {
283         fValues[i].emitObject(stream, objNumMap, substitutes);
284         if (i + 1 < fValues.count()) {
285             stream->writeText(" ");
286         }
287     }
288     stream->writeText("]");
289 }
290 
addResources(SkPDFObjNumMap * catalog,const SkPDFSubstituteMap & substitutes) const291 void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
292                               const SkPDFSubstituteMap& substitutes) const {
293     for (const SkPDFUnion& value : fValues) {
294         value.addResources(catalog, substitutes);
295     }
296 }
297 
append(SkPDFUnion && value)298 void SkPDFArray::append(SkPDFUnion&& value) { new (fValues.append()) SkPDFUnion(value.move()); }
299 
appendInt(int32_t value)300 void SkPDFArray::appendInt(int32_t value) {
301     this->append(SkPDFUnion::Int(value));
302 }
303 
appendBool(bool value)304 void SkPDFArray::appendBool(bool value) {
305     this->append(SkPDFUnion::Bool(value));
306 }
307 
appendScalar(SkScalar value)308 void SkPDFArray::appendScalar(SkScalar value) {
309     this->append(SkPDFUnion::Scalar(value));
310 }
311 
appendName(const char name[])312 void SkPDFArray::appendName(const char name[]) {
313     this->append(SkPDFUnion::Name(SkString(name)));
314 }
315 
appendName(const SkString & name)316 void SkPDFArray::appendName(const SkString& name) {
317     this->append(SkPDFUnion::Name(name));
318 }
319 
appendString(const SkString & value)320 void SkPDFArray::appendString(const SkString& value) {
321     this->append(SkPDFUnion::String(value));
322 }
323 
appendString(const char value[])324 void SkPDFArray::appendString(const char value[]) {
325     this->append(SkPDFUnion::String(value));
326 }
327 
appendObject(SkPDFObject * value)328 void SkPDFArray::appendObject(SkPDFObject* value) {
329     this->append(SkPDFUnion::Object(value));
330 }
331 
appendObjRef(SkPDFObject * value)332 void SkPDFArray::appendObjRef(SkPDFObject* value) {
333     this->append(SkPDFUnion::ObjRef(value));
334 }
335 
336 ///////////////////////////////////////////////////////////////////////////////
337 
SkPDFDict()338 SkPDFDict::SkPDFDict() {}
339 
~SkPDFDict()340 SkPDFDict::~SkPDFDict() { this->clear(); }
341 
SkPDFDict(const char type[])342 SkPDFDict::SkPDFDict(const char type[]) { this->insertName("Type", type); }
343 
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes) const344 void SkPDFDict::emitObject(SkWStream* stream,
345                            const SkPDFObjNumMap& objNumMap,
346                            const SkPDFSubstituteMap& substitutes) const {
347     stream->writeText("<<");
348     this->emitAll(stream, objNumMap, substitutes);
349     stream->writeText(">>");
350 }
351 
emitAll(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes) const352 void SkPDFDict::emitAll(SkWStream* stream,
353                         const SkPDFObjNumMap& objNumMap,
354                         const SkPDFSubstituteMap& substitutes) const {
355     for (int i = 0; i < fRecords.count(); i++) {
356         fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
357         stream->writeText(" ");
358         fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
359         if (i + 1 < fRecords.count()) {
360             stream->writeText("\n");
361         }
362     }
363 }
364 
addResources(SkPDFObjNumMap * catalog,const SkPDFSubstituteMap & substitutes) const365 void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
366                              const SkPDFSubstituteMap& substitutes) const {
367     for (int i = 0; i < fRecords.count(); i++) {
368         fRecords[i].fKey.addResources(catalog, substitutes);
369         fRecords[i].fValue.addResources(catalog, substitutes);
370     }
371 }
372 
set(SkPDFUnion && name,SkPDFUnion && value)373 void SkPDFDict::set(SkPDFUnion&& name, SkPDFUnion&& value) {
374     Record* rec = fRecords.append();
375     SkASSERT(name.isName());
376     new (&rec->fKey) SkPDFUnion(name.move());
377     new (&rec->fValue) SkPDFUnion(value.move());
378 }
379 
size() const380 int SkPDFDict::size() const { return fRecords.count(); }
381 
insertObjRef(const char key[],SkPDFObject * value)382 void SkPDFDict::insertObjRef(const char key[], SkPDFObject* value) {
383     this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
384 }
insertObjRef(const SkString & key,SkPDFObject * value)385 void SkPDFDict::insertObjRef(const SkString& key, SkPDFObject* value) {
386     this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
387 }
388 
insertObject(const char key[],SkPDFObject * value)389 void SkPDFDict::insertObject(const char key[], SkPDFObject* value) {
390     this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
391 }
insertObject(const SkString & key,SkPDFObject * value)392 void SkPDFDict::insertObject(const SkString& key, SkPDFObject* value) {
393     this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
394 }
395 
insertBool(const char key[],bool value)396 void SkPDFDict::insertBool(const char key[], bool value) {
397     this->set(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
398 }
399 
insertInt(const char key[],int32_t value)400 void SkPDFDict::insertInt(const char key[], int32_t value) {
401     this->set(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
402 }
403 
insertInt(const char key[],size_t value)404 void SkPDFDict::insertInt(const char key[], size_t value) {
405     this->insertInt(key, SkToS32(value));
406 }
407 
insertScalar(const char key[],SkScalar value)408 void SkPDFDict::insertScalar(const char key[], SkScalar value) {
409     this->set(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
410 }
411 
insertName(const char key[],const char name[])412 void SkPDFDict::insertName(const char key[], const char name[]) {
413     this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
414 }
415 
insertName(const char key[],const SkString & name)416 void SkPDFDict::insertName(const char key[], const SkString& name) {
417     this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
418 }
419 
insertString(const char key[],const char value[])420 void SkPDFDict::insertString(const char key[], const char value[]) {
421     this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
422 }
423 
insertString(const char key[],const SkString & value)424 void SkPDFDict::insertString(const char key[], const SkString& value) {
425     this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
426 }
427 
clear()428 void SkPDFDict::clear() {
429     for (Record& rec : fRecords) {
430         rec.fKey.~SkPDFUnion();
431         rec.fValue.~SkPDFUnion();
432     }
433     fRecords.reset();
434 }
435 
436 ////////////////////////////////////////////////////////////////////////////////
437 
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes) const438 void SkPDFSharedStream::emitObject(
439         SkWStream* stream,
440         const SkPDFObjNumMap& objNumMap,
441         const SkPDFSubstituteMap& substitutes) const {
442     SkDynamicMemoryWStream buffer;
443     SkDeflateWStream deflateWStream(&buffer);
444     // Since emitObject is const, this function doesn't change the dictionary.
445     SkAutoTDelete<SkStreamAsset> dup(fAsset->duplicate());  // Cheap copy
446     SkASSERT(dup);
447     SkStreamCopy(&deflateWStream, dup.get());
448     deflateWStream.finalize();
449     size_t length = buffer.bytesWritten();
450     stream->writeText("<<");
451     fDict->emitAll(stream, objNumMap, substitutes);
452     stream->writeText("\n");
453     SkPDFUnion::Name("Length").emitObject(stream, objNumMap, substitutes);
454     stream->writeText(" ");
455     SkPDFUnion::Int(length).emitObject(stream, objNumMap, substitutes);
456     stream->writeText("\n");
457     SkPDFUnion::Name("Filter").emitObject(stream, objNumMap, substitutes);
458     stream->writeText(" ");
459     SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap, substitutes);
460     stream->writeText(">>");
461     stream->writeText(" stream\n");
462     buffer.writeToStream(stream);
463     stream->writeText("\nendstream");
464 }
465 
addResources(SkPDFObjNumMap * catalog,const SkPDFSubstituteMap & substitutes) const466 void SkPDFSharedStream::addResources(
467         SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const {
468     fDict->addResources(catalog, substitutes);
469 }
470 
471 ////////////////////////////////////////////////////////////////////////////////
472 
~SkPDFSubstituteMap()473 SkPDFSubstituteMap::~SkPDFSubstituteMap() {
474     fSubstituteMap.foreach(
475             [](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
476 }
477 
setSubstitute(SkPDFObject * original,SkPDFObject * substitute)478 void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
479                                        SkPDFObject* substitute) {
480     SkASSERT(original != substitute);
481     SkASSERT(!fSubstituteMap.find(original));
482     fSubstituteMap.set(original, SkRef(substitute));
483 }
484 
getSubstitute(SkPDFObject * object) const485 SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
486     SkPDFObject** found = fSubstituteMap.find(object);
487     return found ? *found : object;
488 }
489 
490 ////////////////////////////////////////////////////////////////////////////////
491 
addObject(SkPDFObject * obj)492 bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
493     if (fObjectNumbers.find(obj)) {
494         return false;
495     }
496     fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
497     fObjects.push(obj);
498     return true;
499 }
500 
addObjectRecursively(SkPDFObject * obj,const SkPDFSubstituteMap & subs)501 void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj,
502                                           const SkPDFSubstituteMap& subs) {
503     if (obj && this->addObject(obj)) {
504         obj->addResources(this, subs);
505     }
506 }
507 
getObjectNumber(SkPDFObject * obj) const508 int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
509     int32_t* objectNumberFound = fObjectNumbers.find(obj);
510     SkASSERT(objectNumberFound);
511     return *objectNumberFound;
512 }
513 
514 #ifdef SK_PDF_IMAGE_STATS
515 SkAtomic<int> gDrawImageCalls(0);
516 SkAtomic<int> gJpegImageObjects(0);
517 SkAtomic<int> gRegularImageObjects(0);
518 
SkPDFImageDumpStats()519 void SkPDFImageDumpStats() {
520     SkDebugf("\ntotal PDF drawImage/drawBitmap calls: %d\n"
521              "total PDF jpeg images: %d\n"
522              "total PDF regular images: %d\n",
523              gDrawImageCalls.load(),
524              gJpegImageObjects.load(),
525              gRegularImageObjects.load());
526 }
527 #endif // SK_PDF_IMAGE_STATS
528