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