• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/pdf/SkPDFTypes.h"
9 
10 #include "include/core/SkData.h"
11 #include "include/core/SkExecutor.h"
12 #include "include/core/SkStream.h"
13 #include "include/private/base/SkTo.h"
14 #include "src/core/SkStreamPriv.h"
15 #include "src/pdf/SkDeflate.h"
16 #include "src/pdf/SkPDFDocumentPriv.h"
17 #include "src/pdf/SkPDFUnion.h"
18 #include "src/pdf/SkPDFUtils.h"
19 
20 #include <new>
21 
22 ////////////////////////////////////////////////////////////////////////////////
23 
SkPDFUnion(Type t,int32_t v)24 SkPDFUnion::SkPDFUnion(Type t, int32_t     v) : fIntValue          (v) , fType(t) {}
SkPDFUnion(Type t,bool v)25 SkPDFUnion::SkPDFUnion(Type t, bool        v) : fBoolValue         (v) , fType(t) {}
SkPDFUnion(Type t,SkScalar v)26 SkPDFUnion::SkPDFUnion(Type t, SkScalar    v) : fScalarValue       (v) , fType(t) {}
SkPDFUnion(Type t,const char * v)27 SkPDFUnion::SkPDFUnion(Type t, const char* v) : fStaticString      (v) , fType(t) {}
SkPDFUnion(Type t,SkString v)28 SkPDFUnion::SkPDFUnion(Type t, SkString    v) : fSkString(std::move(v)), fType(t) {}
SkPDFUnion(Type t,PDFObject v)29 SkPDFUnion::SkPDFUnion(Type t, PDFObject   v) : fObject  (std::move(v)), fType(t) {}
30 
~SkPDFUnion()31 SkPDFUnion::~SkPDFUnion() {
32     switch (fType) {
33         case Type::kNameSkS:
34         case Type::kByteStringSkS:
35         case Type::kTextStringSkS:
36             fSkString.~SkString();
37             return;
38         case Type::kObject:
39             fObject.~PDFObject();
40             return;
41         default:
42             return;
43     }
44 }
45 
SkPDFUnion(SkPDFUnion && that)46 SkPDFUnion::SkPDFUnion(SkPDFUnion&& that) : fType(that.fType) {
47     SkASSERT(this != &that);
48 
49     switch (fType) {
50         case Type::kDestroyed:
51             break;
52         case Type::kInt:
53         case Type::kColorComponent:
54         case Type::kRef:
55             fIntValue = that.fIntValue;
56             break;
57         case Type::kBool:
58             fBoolValue = that.fBoolValue;
59             break;
60         case Type::kColorComponentF:
61         case Type::kScalar:
62             fScalarValue = that.fScalarValue;
63             break;
64         case Type::kName:
65         case Type::kByteString:
66         case Type::kTextString:
67             fStaticString = that.fStaticString;
68             break;
69         case Type::kNameSkS:
70         case Type::kByteStringSkS:
71         case Type::kTextStringSkS:
72             new (&fSkString) SkString(std::move(that.fSkString));
73             break;
74         case Type::kObject:
75             new (&fObject) PDFObject(std::move(that.fObject));
76             break;
77         default:
78             SkDEBUGFAIL("SkPDFUnion::SkPDFUnion with bad type");
79     }
80     that.fType = Type::kDestroyed;
81 }
82 
operator =(SkPDFUnion && that)83 SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& that) {
84     if (this != &that) {
85         this->~SkPDFUnion();
86         new (this) SkPDFUnion(std::move(that));
87     }
88     return *this;
89 }
90 
isName() const91 bool SkPDFUnion::isName() const {
92     return Type::kName == fType || Type::kNameSkS == fType;
93 }
94 
95 #ifdef SK_DEBUG
96 // Most names need no escaping.  Such names are handled as static const strings.
is_valid_name(const char * n)97 bool is_valid_name(const char* n) {
98     static const char kControlChars[] = "/%()<>[]{}";
99     while (*n) {
100         if (*n < '!' || *n > '~' || strchr(kControlChars, *n)) {
101             return false;
102         }
103         ++n;
104     }
105     return true;
106 }
107 #endif  // SK_DEBUG
108 
109 // Given an arbitrary string, write it as a valid name (not including leading slash).
write_name_escaped(SkWStream * o,const char * name)110 static void write_name_escaped(SkWStream* o, const char* name) {
111     static const char kToEscape[] = "#/%()<>[]{}";
112     for (const uint8_t* n = reinterpret_cast<const uint8_t*>(name); *n; ++n) {
113         uint8_t v = *n;
114         if (v < '!' || v > '~' || strchr(kToEscape, v)) {
115             char buffer[3] = {'#',
116                               SkHexadecimalDigits::gUpper[v >> 4],
117                               SkHexadecimalDigits::gUpper[v & 0xF]};
118             o->write(buffer, sizeof(buffer));
119         } else {
120             o->write(n, 1);
121         }
122     }
123 }
124 
write_literal_byte_string(SkWStream * wStream,const char * cin,size_t len)125 static void write_literal_byte_string(SkWStream* wStream, const char* cin, size_t len) {
126     wStream->writeText("(");
127     for (size_t i = 0; i < len; i++) {
128         uint8_t c = static_cast<uint8_t>(cin[i]);
129         if (c < ' ' || '~' < c) {
130             uint8_t octal[4] = { '\\',
131                                  (uint8_t)('0' | ( c >> 6        )),
132                                  (uint8_t)('0' | ((c >> 3) & 0x07)),
133                                  (uint8_t)('0' | ( c       & 0x07)) };
134             wStream->write(octal, 4);
135         } else {
136             if (c == '\\' || c == '(' || c == ')') {
137                 wStream->writeText("\\");
138             }
139             wStream->write(&c, 1);
140         }
141     }
142     wStream->writeText(")");
143 }
144 
write_hex_byte_string(SkWStream * wStream,const char * cin,size_t len)145 static void write_hex_byte_string(SkWStream* wStream, const char* cin, size_t len) {
146     SkDEBUGCODE(static const size_t kMaxLen = 65535;)
147     SkASSERT(len <= kMaxLen);
148 
149     wStream->writeText("<");
150     for (size_t i = 0; i < len; i++) {
151         uint8_t c = static_cast<uint8_t>(cin[i]);
152         char hexValue[2] = { SkHexadecimalDigits::gUpper[c >> 4],
153                              SkHexadecimalDigits::gUpper[c & 0xF] };
154         wStream->write(hexValue, 2);
155     }
156     wStream->writeText(">");
157 }
158 
write_optimized_byte_string(SkWStream * wStream,const char * cin,size_t len,size_t literalExtras)159 static void write_optimized_byte_string(SkWStream* wStream, const char* cin, size_t len,
160                                         size_t literalExtras) {
161     const size_t hexLength     = 2 + 2*len;
162     const size_t literalLength = 2 +   len + literalExtras;
163     if (literalLength <= hexLength) {
164         write_literal_byte_string(wStream, cin, len);
165     } else {
166         write_hex_byte_string(wStream, cin, len);
167     }
168 }
169 
write_byte_string(SkWStream * wStream,const char * cin,size_t len)170 static void write_byte_string(SkWStream* wStream, const char* cin, size_t len) {
171     SkDEBUGCODE(static const size_t kMaxLen = 65535;)
172     SkASSERT(len <= kMaxLen);
173 
174     size_t literalExtras = 0;
175     {
176         for (size_t i = 0; i < len; i++) {
177             uint8_t c = static_cast<uint8_t>(cin[i]);
178             if (c < ' ' || '~' < c) {
179                 literalExtras += 3;
180             } else if (c == '\\' || c == '(' || c == ')') {
181                 ++literalExtras;
182             }
183         }
184     }
185     write_optimized_byte_string(wStream, cin, len, literalExtras);
186 }
187 
write_text_string(SkWStream * wStream,const char * cin,size_t len)188 static void write_text_string(SkWStream* wStream, const char* cin, size_t len) {
189     SkDEBUGCODE(static const size_t kMaxLen = 65535;)
190     SkASSERT(len <= kMaxLen);
191 
192     bool inputIsValidUTF8 = true;
193     bool inputIsPDFDocEncoding = true;
194     size_t literalExtras = 0;
195     {
196         const char* textPtr = cin;
197         const char* textEnd = cin + len;
198         while (textPtr < textEnd) {
199             SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd);
200             if (unichar < 0) {
201                 inputIsValidUTF8 = false;
202                 break;
203             }
204             // See Table D.2 (PDFDocEncoding Character Set) in the PDF3200_2008 spec.
205             // Could convert from UTF-8 to PDFDocEncoding and, if successful, use that.
206             if ((0x15 < unichar && unichar < 0x20) || 0x7E < unichar) {
207                 inputIsPDFDocEncoding = false;
208                 break;
209             }
210             if (unichar < ' ' || '~' < unichar) {
211                 literalExtras += 3;
212             } else if (unichar == '\\' || unichar == '(' || unichar == ')') {
213                 ++literalExtras;
214             }
215         }
216     }
217 
218     if (!inputIsValidUTF8) {
219         SkDebugf("Invalid UTF8: %.*s\n", (int)len, cin);
220         wStream->writeText("<>");
221         return;
222     }
223 
224     if (inputIsPDFDocEncoding) {
225         write_optimized_byte_string(wStream, cin, len, literalExtras);
226         return;
227     }
228 
229     wStream->writeText("<FEFF");
230     const char* textPtr = cin;
231     const char* textEnd = cin + len;
232     while (textPtr < textEnd) {
233         SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd);
234         SkPDFUtils::WriteUTF16beHex(wStream, unichar);
235     }
236     wStream->writeText(">");
237 }
238 
SkPDFWriteTextString(SkWStream * wStream,const char * cin,size_t len)239 void SkPDFWriteTextString(SkWStream* wStream, const char* cin, size_t len) {
240     write_text_string(wStream, cin, len);
241 }
SkPDFWriteByteString(SkWStream * wStream,const char * cin,size_t len)242 void SkPDFWriteByteString(SkWStream* wStream, const char* cin, size_t len) {
243     write_byte_string(wStream, cin, len);
244 }
245 
emitObject(SkWStream * stream) const246 void SkPDFUnion::emitObject(SkWStream* stream) const {
247     switch (fType) {
248         case Type::kInt:
249             stream->writeDecAsText(fIntValue);
250             return;
251         case Type::kColorComponent:
252             SkPDFUtils::AppendColorComponent(SkToU8(fIntValue), stream);
253             return;
254         case Type::kColorComponentF:
255             SkPDFUtils::AppendColorComponentF(fScalarValue, stream);
256             return;
257         case Type::kBool:
258             stream->writeText(fBoolValue ? "true" : "false");
259             return;
260         case Type::kScalar:
261             SkPDFUtils::AppendScalar(fScalarValue, stream);
262             return;
263         case Type::kName:
264             stream->writeText("/");
265             SkASSERT(is_valid_name(fStaticString));
266             stream->writeText(fStaticString);
267             return;
268         case Type::kByteString:
269             SkASSERT(fStaticString);
270             write_byte_string(stream, fStaticString, strlen(fStaticString));
271             return;
272         case Type::kTextString:
273             SkASSERT(fStaticString);
274             write_text_string(stream, fStaticString, strlen(fStaticString));
275             return;
276         case Type::kNameSkS:
277             stream->writeText("/");
278             write_name_escaped(stream, fSkString.c_str());
279             return;
280         case Type::kByteStringSkS:
281             write_byte_string(stream, fSkString.c_str(), fSkString.size());
282             return;
283         case Type::kTextStringSkS:
284             write_text_string(stream, fSkString.c_str(), fSkString.size());
285             return;
286         case Type::kObject:
287             fObject->emitObject(stream);
288             return;
289         case Type::kRef:
290             SkASSERT(fIntValue >= 0);
291             stream->writeDecAsText(fIntValue);
292             stream->writeText(" 0 R");  // Generation number is always 0.
293             return;
294         default:
295             SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
296     }
297 }
298 
Int(int32_t value)299 SkPDFUnion SkPDFUnion::Int(int32_t value) {
300     return SkPDFUnion(Type::kInt, value);
301 }
302 
ColorComponent(uint8_t value)303 SkPDFUnion SkPDFUnion::ColorComponent(uint8_t value) {
304     return SkPDFUnion(Type::kColorComponent,  SkTo<int32_t>(value));
305 }
306 
ColorComponentF(float value)307 SkPDFUnion SkPDFUnion::ColorComponentF(float value) {
308     return SkPDFUnion(Type::kColorComponentF, SkFloatToScalar(value));
309 }
310 
Bool(bool value)311 SkPDFUnion SkPDFUnion::Bool(bool value) {
312     return SkPDFUnion(Type::kBool, value);
313 }
314 
Scalar(SkScalar value)315 SkPDFUnion SkPDFUnion::Scalar(SkScalar value) {
316     return SkPDFUnion(Type::kScalar, value);
317 }
318 
Name(const char * value)319 SkPDFUnion SkPDFUnion::Name(const char* value) {
320     SkASSERT(value);
321     SkASSERT(is_valid_name(value));
322     return SkPDFUnion(Type::kName, value);
323 }
324 
ByteString(const char * value)325 SkPDFUnion SkPDFUnion::ByteString(const char* value) {
326     SkASSERT(value);
327     return SkPDFUnion(Type::kByteString, value);
328 }
329 
TextString(const char * value)330 SkPDFUnion SkPDFUnion::TextString(const char* value) {
331     SkASSERT(value);
332     return SkPDFUnion(Type::kTextString, value);
333 }
334 
Name(SkString s)335 SkPDFUnion SkPDFUnion::Name(SkString s) {
336     return SkPDFUnion(Type::kNameSkS, std::move(s));
337 }
338 
ByteString(SkString s)339 SkPDFUnion SkPDFUnion::ByteString(SkString s) {
340     return SkPDFUnion(Type::kByteStringSkS, std::move(s));
341 }
342 
TextString(SkString s)343 SkPDFUnion SkPDFUnion::TextString(SkString s) {
344     return SkPDFUnion(Type::kTextStringSkS, std::move(s));
345 }
346 
Object(std::unique_ptr<SkPDFObject> objSp)347 SkPDFUnion SkPDFUnion::Object(std::unique_ptr<SkPDFObject> objSp) {
348     SkASSERT(objSp.get());
349     return SkPDFUnion(Type::kObject, std::move(objSp));
350 }
351 
Ref(SkPDFIndirectReference ref)352 SkPDFUnion SkPDFUnion::Ref(SkPDFIndirectReference ref) {
353     SkASSERT(ref.fValue > 0);
354     return SkPDFUnion(Type::kRef, SkTo<int32_t>(ref.fValue));
355 }
356 
357 ////////////////////////////////////////////////////////////////////////////////
358 
359 #if 0  // Enable if needed.
360 void SkPDFAtom::emitObject(SkWStream* stream) const {
361     fValue.emitObject(stream);
362 }
363 #endif  // 0
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 
SkPDFArray()367 SkPDFArray::SkPDFArray() {}
368 
~SkPDFArray()369 SkPDFArray::~SkPDFArray() {}
370 
size() const371 size_t SkPDFArray::size() const { return fValues.size(); }
372 
reserve(int length)373 void SkPDFArray::reserve(int length) {
374     fValues.reserve(length);
375 }
376 
emitObject(SkWStream * stream) const377 void SkPDFArray::emitObject(SkWStream* stream) const {
378     stream->writeText("[");
379     for (size_t i = 0; i < fValues.size(); i++) {
380         fValues[i].emitObject(stream);
381         if (i + 1 < fValues.size()) {
382             stream->writeText(" ");
383         }
384     }
385     stream->writeText("]");
386 }
387 
append(SkPDFUnion && value)388 void SkPDFArray::append(SkPDFUnion&& value) {
389     fValues.emplace_back(std::move(value));
390 }
391 
appendInt(int32_t value)392 void SkPDFArray::appendInt(int32_t value) {
393     this->append(SkPDFUnion::Int(value));
394 }
395 
appendColorComponent(uint8_t value)396 void SkPDFArray::appendColorComponent(uint8_t value) {
397     this->append(SkPDFUnion::ColorComponent(value));
398 }
399 
appendBool(bool value)400 void SkPDFArray::appendBool(bool value) {
401     this->append(SkPDFUnion::Bool(value));
402 }
403 
appendScalar(SkScalar value)404 void SkPDFArray::appendScalar(SkScalar value) {
405     this->append(SkPDFUnion::Scalar(value));
406 }
407 
appendName(const char name[])408 void SkPDFArray::appendName(const char name[]) {
409     this->append(SkPDFUnion::Name(SkString(name)));
410 }
411 
appendName(SkString name)412 void SkPDFArray::appendName(SkString name) {
413     this->append(SkPDFUnion::Name(std::move(name)));
414 }
415 
appendByteString(SkString value)416 void SkPDFArray::appendByteString(SkString value) {
417     this->append(SkPDFUnion::ByteString(std::move(value)));
418 }
419 
appendTextString(SkString value)420 void SkPDFArray::appendTextString(SkString value) {
421     this->append(SkPDFUnion::TextString(std::move(value)));
422 }
423 
appendByteString(const char value[])424 void SkPDFArray::appendByteString(const char value[]) {
425     this->append(SkPDFUnion::ByteString(value));
426 }
427 
appendTextString(const char value[])428 void SkPDFArray::appendTextString(const char value[]) {
429     this->append(SkPDFUnion::TextString(value));
430 }
431 
appendObject(std::unique_ptr<SkPDFObject> && objSp)432 void SkPDFArray::appendObject(std::unique_ptr<SkPDFObject>&& objSp) {
433     this->append(SkPDFUnion::Object(std::move(objSp)));
434 }
435 
appendRef(SkPDFIndirectReference ref)436 void SkPDFArray::appendRef(SkPDFIndirectReference ref) {
437     this->append(SkPDFUnion::Ref(ref));
438 }
439 
440 ///////////////////////////////////////////////////////////////////////////////
441 
~SkPDFDict()442 SkPDFDict::~SkPDFDict() {}
443 
SkPDFDict(const char type[])444 SkPDFDict::SkPDFDict(const char type[]) {
445     if (type) {
446         this->insertName("Type", type);
447     }
448 }
449 
emitObject(SkWStream * stream) const450 void SkPDFDict::emitObject(SkWStream* stream) const {
451     stream->writeText("<<");
452     for (size_t i = 0; i < fRecords.size(); ++i) {
453         const std::pair<SkPDFUnion, SkPDFUnion>& record = fRecords[i];
454         record.first.emitObject(stream);
455         stream->writeText(" ");
456         record.second.emitObject(stream);
457         if (i + 1 < fRecords.size()) {
458             stream->writeText("\n");
459         }
460     }
461     stream->writeText(">>");
462 }
463 
size() const464 size_t SkPDFDict::size() const { return fRecords.size(); }
465 
reserve(int n)466 void SkPDFDict::reserve(int n) {
467     fRecords.reserve(n);
468 }
469 
insertRef(const char key[],SkPDFIndirectReference ref)470 void SkPDFDict::insertRef(const char key[], SkPDFIndirectReference ref) {
471     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Ref(ref));
472 }
473 
insertRef(SkString key,SkPDFIndirectReference ref)474 void SkPDFDict::insertRef(SkString key, SkPDFIndirectReference ref) {
475     fRecords.emplace_back(SkPDFUnion::Name(std::move(key)), SkPDFUnion::Ref(ref));
476 }
477 
insertObject(const char key[],std::unique_ptr<SkPDFObject> && objSp)478 void SkPDFDict::insertObject(const char key[], std::unique_ptr<SkPDFObject>&& objSp) {
479     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp)));
480 }
insertObject(SkString key,std::unique_ptr<SkPDFObject> && objSp)481 void SkPDFDict::insertObject(SkString key, std::unique_ptr<SkPDFObject>&& objSp) {
482     fRecords.emplace_back(SkPDFUnion::Name(std::move(key)),
483                           SkPDFUnion::Object(std::move(objSp)));
484 }
485 
insertBool(const char key[],bool value)486 void SkPDFDict::insertBool(const char key[], bool value) {
487     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
488 }
489 
insertInt(const char key[],int32_t value)490 void SkPDFDict::insertInt(const char key[], int32_t value) {
491     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
492 }
493 
insertInt(const char key[],size_t value)494 void SkPDFDict::insertInt(const char key[], size_t value) {
495     this->insertInt(key, SkToS32(value));
496 }
497 
insertColorComponentF(const char key[],SkScalar value)498 void SkPDFDict::insertColorComponentF(const char key[], SkScalar value) {
499     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ColorComponentF(value));
500 }
501 
insertScalar(const char key[],SkScalar value)502 void SkPDFDict::insertScalar(const char key[], SkScalar value) {
503     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
504 }
505 
insertName(const char key[],const char name[])506 void SkPDFDict::insertName(const char key[], const char name[]) {
507     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
508 }
509 
insertName(const char key[],SkString name)510 void SkPDFDict::insertName(const char key[], SkString name) {
511     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(std::move(name)));
512 }
513 
insertByteString(const char key[],const char value[])514 void SkPDFDict::insertByteString(const char key[], const char value[]) {
515     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ByteString(value));
516 }
517 
insertTextString(const char key[],const char value[])518 void SkPDFDict::insertTextString(const char key[], const char value[]) {
519     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(value));
520 }
521 
insertByteString(const char key[],SkString value)522 void SkPDFDict::insertByteString(const char key[], SkString value) {
523     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ByteString(std::move(value)));
524 }
525 
insertTextString(const char key[],SkString value)526 void SkPDFDict::insertTextString(const char key[], SkString value) {
527     fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(std::move(value)));
528 }
529 
530 ////////////////////////////////////////////////////////////////////////////////
531 
532 
533 
serialize_stream(SkPDFDict * origDict,SkStreamAsset * stream,SkPDFSteamCompressionEnabled compress,SkPDFDocument * doc,SkPDFIndirectReference ref)534 static void serialize_stream(SkPDFDict* origDict,
535                              SkStreamAsset* stream,
536                              SkPDFSteamCompressionEnabled compress,
537                              SkPDFDocument* doc,
538                              SkPDFIndirectReference ref) {
539     // Code assumes that the stream starts at the beginning.
540     SkASSERT(stream && stream->hasLength());
541 
542     std::unique_ptr<SkStreamAsset> tmp;
543     SkPDFDict tmpDict;
544     SkPDFDict& dict = origDict ? *origDict : tmpDict;
545     static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_");
546     if (doc->metadata().fCompressionLevel != SkPDF::Metadata::CompressionLevel::None &&
547         compress == SkPDFSteamCompressionEnabled::Yes &&
548         stream->getLength() > kMinimumSavings)
549     {
550         SkDynamicMemoryWStream compressedData;
551         SkDeflateWStream deflateWStream(&compressedData,SkToInt(doc->metadata().fCompressionLevel));
552         SkStreamCopy(&deflateWStream, stream);
553         deflateWStream.finalize();
554         #ifdef SK_PDF_BASE85_BINARY
555         {
556             SkPDFUtils::Base85Encode(compressedData.detachAsStream(), &compressedData);
557             tmp = compressedData.detachAsStream();
558             stream = tmp.get();
559             auto filters = SkPDFMakeArray();
560             filters->appendName("ASCII85Decode");
561             filters->appendName("FlateDecode");
562             dict.insertObject("Filter", std::move(filters));
563         }
564         #else
565         if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) {
566             tmp = compressedData.detachAsStream();
567             stream = tmp.get();
568             dict.insertName("Filter", "FlateDecode");
569         } else {
570             SkAssertResult(stream->rewind());
571         }
572         #endif
573 
574     }
575     dict.insertInt("Length", stream->getLength());
576     doc->emitStream(dict,
577                     [stream](SkWStream* dst) { dst->writeStream(stream, stream->getLength()); },
578                     ref);
579 }
580 
SkPDFStreamOut(std::unique_ptr<SkPDFDict> dict,std::unique_ptr<SkStreamAsset> content,SkPDFDocument * doc,SkPDFSteamCompressionEnabled compress)581 SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr<SkPDFDict> dict,
582                                       std::unique_ptr<SkStreamAsset> content,
583                                       SkPDFDocument* doc,
584                                       SkPDFSteamCompressionEnabled compress) {
585     SkPDFIndirectReference ref = doc->reserveRef();
586     if (SkExecutor* executor = doc->executor()) {
587         SkPDFDict* dictPtr = dict.release();
588         SkStreamAsset* contentPtr = content.release();
589         // Pass ownership of both pointers into a std::function, which should
590         // only be executed once.
591         doc->incrementJobCount();
592         executor->add([dictPtr, contentPtr, compress, doc, ref]() {
593             serialize_stream(dictPtr, contentPtr, compress, doc, ref);
594             delete dictPtr;
595             delete contentPtr;
596             doc->signalJobComplete();
597         });
598         return ref;
599     }
600     serialize_stream(dict.get(), content.get(), compress, doc, ref);
601     return ref;
602 }
603