Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkPDFTypes.cpp
Go to the documentation of this file.
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
9
10#include "include/core/SkData.h"
15#include "src/pdf/SkDeflate.h"
17#include "src/pdf/SkPDFUnion.h"
18#include "src/pdf/SkPDFUtils.h"
19
20#include <new>
21
22////////////////////////////////////////////////////////////////////////////////
23
24SkPDFUnion::SkPDFUnion(Type t, int32_t v) : fIntValue (v) , fType(t) {}
25SkPDFUnion::SkPDFUnion(Type t, bool v) : fBoolValue (v) , fType(t) {}
26SkPDFUnion::SkPDFUnion(Type t, SkScalar v) : fScalarValue (v) , fType(t) {}
27SkPDFUnion::SkPDFUnion(Type t, const char* v) : fStaticString (v) , fType(t) {}
28SkPDFUnion::SkPDFUnion(Type t, SkString v) : fSkString(std::move(v)), fType(t) {}
29SkPDFUnion::SkPDFUnion(Type t, PDFObject v) : fObject (std::move(v)), fType(t) {}
30
32 switch (fType) {
33 case Type::kNameSkS:
34 case Type::kByteStringSkS:
35 case Type::kTextStringSkS:
37 return;
38 case Type::kObject:
39 fObject.~PDFObject();
40 return;
41 default:
42 return;
43 }
44}
45
46SkPDFUnion::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
84 if (this != &that) {
85 this->~SkPDFUnion();
86 new (this) SkPDFUnion(std::move(that));
87 }
88 return *this;
89}
90
91bool 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.
97bool 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).
110static 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] = {'#',
118 o->write(buffer, sizeof(buffer));
119 } else {
120 o->write(n, 1);
121 }
122 }
123}
124
125static 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
145static 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],
154 wStream->write(hexValue, 2);
155 }
156 wStream->writeText(">");
157}
158
159static 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
170static 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
188static 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
239void SkPDFWriteTextString(SkWStream* wStream, const char* cin, size_t len) {
240 write_text_string(wStream, cin, len);
241}
242void SkPDFWriteByteString(SkWStream* wStream, const char* cin, size_t len) {
243 write_byte_string(wStream, cin, len);
244}
245
247 switch (fType) {
248 case Type::kInt:
249 stream->writeDecAsText(fIntValue);
250 return;
251 case Type::kColorComponent:
253 return;
254 case Type::kColorComponentF:
256 return;
257 case Type::kBool:
258 stream->writeText(fBoolValue ? "true" : "false");
259 return;
260 case Type::kScalar:
262 return;
263 case Type::kName:
264 stream->writeText("/");
266 stream->writeText(fStaticString);
267 return;
268 case Type::kByteString:
271 return;
272 case Type::kTextString:
275 return;
276 case Type::kNameSkS:
277 stream->writeText("/");
279 return;
280 case Type::kByteStringSkS:
282 return;
283 case Type::kTextStringSkS:
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
300 return SkPDFUnion(Type::kInt, value);
301}
302
304 return SkPDFUnion(Type::kColorComponent, SkTo<int32_t>(value));
305}
306
308 return SkPDFUnion(Type::kColorComponentF, value);
309}
310
312 return SkPDFUnion(Type::kBool, value);
313}
314
316 return SkPDFUnion(Type::kScalar, value);
317}
318
319SkPDFUnion SkPDFUnion::Name(const char* value) {
322 return SkPDFUnion(Type::kName, value);
323}
324
327 return SkPDFUnion(Type::kByteString, value);
328}
329
332 return SkPDFUnion(Type::kTextString, value);
333}
334
336 return SkPDFUnion(Type::kNameSkS, std::move(s));
337}
338
340 return SkPDFUnion(Type::kByteStringSkS, std::move(s));
341}
342
344 return SkPDFUnion(Type::kTextStringSkS, std::move(s));
345}
346
347SkPDFUnion SkPDFUnion::Object(std::unique_ptr<SkPDFObject> objSp) {
348 SkASSERT(objSp.get());
349 return SkPDFUnion(Type::kObject, std::move(objSp));
350}
351
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.
360void SkPDFAtom::emitObject(SkWStream* stream) const {
361 fValue.emitObject(stream);
362}
363#endif // 0
364
365////////////////////////////////////////////////////////////////////////////////
366
368
370
371size_t SkPDFArray::size() const { return fValues.size(); }
372
374 fValues.reserve(length);
375}
376
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
388void SkPDFArray::append(SkPDFUnion&& value) {
389 fValues.emplace_back(std::move(value));
390}
391
392void SkPDFArray::appendInt(int32_t value) {
393 this->append(SkPDFUnion::Int(value));
394}
395
397 this->append(SkPDFUnion::ColorComponent(value));
398}
399
400void SkPDFArray::appendBool(bool value) {
401 this->append(SkPDFUnion::Bool(value));
402}
403
405 this->append(SkPDFUnion::Scalar(value));
406}
407
408void SkPDFArray::appendName(const char name[]) {
409 this->append(SkPDFUnion::Name(SkString(name)));
410}
411
413 this->append(SkPDFUnion::Name(std::move(name)));
414}
415
417 this->append(SkPDFUnion::ByteString(std::move(value)));
418}
419
421 this->append(SkPDFUnion::TextString(std::move(value)));
422}
423
424void SkPDFArray::appendByteString(const char value[]) {
425 this->append(SkPDFUnion::ByteString(value));
426}
427
428void SkPDFArray::appendTextString(const char value[]) {
429 this->append(SkPDFUnion::TextString(value));
430}
431
432void SkPDFArray::appendObject(std::unique_ptr<SkPDFObject>&& objSp) {
433 this->append(SkPDFUnion::Object(std::move(objSp)));
434}
435
437 this->append(SkPDFUnion::Ref(ref));
438}
439
440///////////////////////////////////////////////////////////////////////////////
441
443
445 if (type) {
446 this->insertName("Type", type);
447 }
448}
449
450void 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
464size_t SkPDFDict::size() const { return fRecords.size(); }
465
467 fRecords.reserve(n);
468}
469
471 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Ref(ref));
472}
473
475 fRecords.emplace_back(SkPDFUnion::Name(std::move(key)), SkPDFUnion::Ref(ref));
476}
477
478void SkPDFDict::insertObject(const char key[], std::unique_ptr<SkPDFObject>&& objSp) {
479 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Object(std::move(objSp)));
480}
481void 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
486void SkPDFDict::insertBool(const char key[], bool value) {
487 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
488}
489
490void SkPDFDict::insertInt(const char key[], int32_t value) {
491 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
492}
493
494void SkPDFDict::insertInt(const char key[], size_t value) {
495 this->insertInt(key, SkToS32(value));
496}
497
499 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ColorComponentF(value));
500}
501
502void SkPDFDict::insertScalar(const char key[], SkScalar value) {
503 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
504}
505
506void SkPDFDict::insertName(const char key[], const char name[]) {
507 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
508}
509
511 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::Name(std::move(name)));
512}
513
514void SkPDFDict::insertByteString(const char key[], const char value[]) {
515 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ByteString(value));
516}
517
518void SkPDFDict::insertTextString(const char key[], const char value[]) {
519 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(value));
520}
521
522void SkPDFDict::insertByteString(const char key[], SkString value) {
523 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::ByteString(std::move(value)));
524}
525
526void SkPDFDict::insertTextString(const char key[], SkString value) {
527 fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(std::move(value)));
528}
529
530void SkPDFDict::insertUnion(const char key[], SkPDFUnion&& value) {
531 fRecords.emplace_back(SkPDFUnion::Name(key), std::move(value));
532}
533
534////////////////////////////////////////////////////////////////////////////////
535
536
537
538static void serialize_stream(SkPDFDict* origDict,
539 SkStreamAsset* stream,
541 SkPDFDocument* doc,
543 // Code assumes that the stream starts at the beginning.
544 SkASSERT(stream && stream->hasLength());
545
546 std::unique_ptr<SkStreamAsset> tmp;
547 SkPDFDict tmpDict;
548 SkPDFDict& dict = origDict ? *origDict : tmpDict;
549 static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_");
552 stream->getLength() > kMinimumSavings)
553 {
554 SkDynamicMemoryWStream compressedData;
555 SkDeflateWStream deflateWStream(&compressedData,SkToInt(doc->metadata().fCompressionLevel));
556 SkStreamCopy(&deflateWStream, stream);
557 deflateWStream.finalize();
558 #ifdef SK_PDF_BASE85_BINARY
559 {
560 SkPDFUtils::Base85Encode(compressedData.detachAsStream(), &compressedData);
561 tmp = compressedData.detachAsStream();
562 stream = tmp.get();
563 auto filters = SkPDFMakeArray();
564 filters->appendName("ASCII85Decode");
565 filters->appendName("FlateDecode");
566 dict.insertObject("Filter", std::move(filters));
567 }
568 #else
569 if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) {
570 tmp = compressedData.detachAsStream();
571 stream = tmp.get();
572 dict.insertName("Filter", "FlateDecode");
573 } else {
574 SkAssertResult(stream->rewind());
575 }
576 #endif
577
578 }
579 dict.insertInt("Length", stream->getLength());
580 doc->emitStream(dict,
581 [stream](SkWStream* dst) { dst->writeStream(stream, stream->getLength()); },
582 ref);
583}
584
585SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr<SkPDFDict> dict,
586 std::unique_ptr<SkStreamAsset> content,
587 SkPDFDocument* doc,
590 if (SkExecutor* executor = doc->executor()) {
591 SkPDFDict* dictPtr = dict.release();
592 SkStreamAsset* contentPtr = content.release();
593 // Pass ownership of both pointers into a std::function, which should
594 // only be executed once.
595 doc->incrementJobCount();
596 executor->add([dictPtr, contentPtr, compress, doc, ref]() {
597 serialize_stream(dictPtr, contentPtr, compress, doc, ref);
598 delete dictPtr;
599 delete contentPtr;
600 doc->signalJobComplete();
601 });
602 return ref;
603 }
604 serialize_stream(dict.get(), content.get(), compress, doc, ref);
605 return ref;
606}
static bool is_valid_name(const SkString &name)
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static void write_byte_string(SkWStream *wStream, const char *cin, size_t len)
static void write_optimized_byte_string(SkWStream *wStream, const char *cin, size_t len, size_t literalExtras)
static void write_name_escaped(SkWStream *o, const char *name)
void SkPDFWriteByteString(SkWStream *wStream, const char *cin, size_t len)
static void write_hex_byte_string(SkWStream *wStream, const char *cin, size_t len)
static void write_literal_byte_string(SkWStream *wStream, const char *cin, size_t len)
static void write_text_string(SkWStream *wStream, const char *cin, size_t len)
void SkPDFWriteTextString(SkWStream *wStream, const char *cin, size_t len)
static void serialize_stream(SkPDFDict *origDict, SkStreamAsset *stream, SkPDFSteamCompressionEnabled compress, SkPDFDocument *doc, SkPDFIndirectReference ref)
SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr< SkPDFDict > dict, std::unique_ptr< SkStreamAsset > content, SkPDFDocument *doc, SkPDFSteamCompressionEnabled compress)
static std::unique_ptr< SkPDFArray > SkPDFMakeArray(Args... args)
Definition SkPDFTypes.h:135
SkPDFSteamCompressionEnabled
Definition SkPDFTypes.h:199
bool SkStreamCopy(SkWStream *out, SkStream *input)
Definition SkStream.cpp:954
constexpr int32_t SkToS32(S x)
Definition SkTo.h:25
constexpr int SkToInt(S x)
Definition SkTo.h:29
constexpr uint8_t SkToU8(S x)
Definition SkTo.h:22
int32_t SkUnichar
Definition SkTypes.h:175
size_t bytesWritten() const override
Definition SkStream.cpp:526
std::unique_ptr< SkStreamAsset > detachAsStream()
Definition SkStream.cpp:876
size_t size() const
void appendRef(SkPDFIndirectReference)
void reserve(int length)
void appendTextString(const char[])
~SkPDFArray() override
void appendInt(int32_t)
void appendByteString(const char[])
void appendObject(std::unique_ptr< SkPDFObject > &&)
void appendBool(bool)
void appendColorComponent(uint8_t)
void appendName(const char[])
void appendScalar(SkScalar)
void emitObject(SkWStream *stream) const override
void reserve(int n)
void insertName(const char key[], const char nameValue[])
void insertObject(const char key[], std::unique_ptr< SkPDFObject > &&)
~SkPDFDict() override
void insertColorComponentF(const char key[], SkScalar value)
void insertUnion(const char key[], SkPDFUnion &&)
void insertInt(const char key[], int32_t value)
void insertTextString(const char key[], const char value[])
void insertRef(const char key[], SkPDFIndirectReference)
void insertScalar(const char key[], SkScalar value)
void insertByteString(const char key[], const char value[])
size_t size() const
void emitObject(SkWStream *stream) const override
void insertBool(const char key[], bool value)
SkPDFDict(const char type[]=nullptr)
SkExecutor * executor() const
const SkPDF::Metadata & metadata() const
void emitStream(const SkPDFDict &dict, T writeStream, SkPDFIndirectReference ref)
SkPDFIndirectReference reserveRef()
int32_t fIntValue
Definition SkPDFUnion.h:74
static SkPDFUnion Ref(SkPDFIndirectReference)
static SkPDFUnion ColorComponentF(float)
SkPDFUnion(SkPDFUnion &&)
SkScalar fScalarValue
Definition SkPDFUnion.h:76
static SkPDFUnion Bool(bool)
static SkPDFUnion Int(int32_t)
static SkPDFUnion TextString(const char *)
PDFObject fObject
Definition SkPDFUnion.h:79
void emitObject(SkWStream *) const
static SkPDFUnion ByteString(const char *)
static SkPDFUnion Object(std::unique_ptr< SkPDFObject >)
SkPDFUnion & operator=(SkPDFUnion &&)
static SkPDFUnion Name(const char *)
const char * fStaticString
Definition SkPDFUnion.h:77
SkString fSkString
Definition SkPDFUnion.h:78
bool fBoolValue
Definition SkPDFUnion.h:75
bool isName() const
static SkPDFUnion Scalar(SkScalar)
static SkPDFUnion ColorComponent(uint8_t)
size_t size() const
Definition SkString.h:131
const char * c_str() const
Definition SkString.h:133
virtual bool write(const void *buffer, size_t size)=0
bool writeText(const char text[])
Definition SkStream.h:247
float SkScalar
Definition extension.cpp:12
struct MyStruct s
static const uint8_t buffer[]
uint8_t value
const char * name
Definition fuchsia.cc:50
size_t length
union flutter::testing::@2838::KeyboardChange::@76 content
const char gUpper[16]
Definition SkUtils.cpp:10
void AppendColorComponent(uint8_t value, SkWStream *wStream)
Definition SkPDFUtils.h:75
void AppendScalar(SkScalar value, SkWStream *stream)
Definition SkPDFUtils.h:86
void AppendColorComponentF(float value, SkWStream *wStream)
Definition SkPDFUtils.h:80
void WriteUTF16beHex(SkWStream *wStream, SkUnichar utf32)
Definition SkPDFUtils.h:107
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition SkUTF.cpp:118
Definition ref_ptr.h:256
enum SkPDF::Metadata::CompressionLevel fCompressionLevel