blob: 619870a4cc5ffec50b4765f7f946415d7e8e1325 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00007 */
8
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00009#include "SkPDFTypes.h"
halcanarybc4696b2015-05-06 10:56:04 -070010#include "SkPDFUtils.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000011#include "SkStream.h"
12
vandebo@chromium.org094316b2011-03-04 03:15:13 +000013#ifdef SK_BUILD_FOR_WIN
14 #define SNPRINTF _snprintf
15#else
16 #define SNPRINTF snprintf
17#endif
18
halcanary6a144342015-01-23 11:45:10 -080019////////////////////////////////////////////////////////////////////////////////
20
halcanary130444f2015-04-25 06:45:07 -070021SkString* pun(char* x) { return reinterpret_cast<SkString*>(x); }
22const SkString* pun(const char* x) {
23 return reinterpret_cast<const SkString*>(x);
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +000024}
25
halcanary130444f2015-04-25 06:45:07 -070026SkPDFUnion::SkPDFUnion(Type t) : fType(t) {}
vandebo@chromium.org421d6442011-07-20 17:39:01 +000027
halcanary130444f2015-04-25 06:45:07 -070028SkPDFUnion::~SkPDFUnion() {
29 switch (fType) {
30 case Type::kNameSkS:
31 case Type::kStringSkS:
32 pun(fSkString)->~SkString();
33 return;
34 case Type::kObjRef:
35 case Type::kObject:
36 SkSafeUnref(fObject);
37 return;
38 default:
39 return;
halcanarybf799cd2015-02-10 13:32:09 -080040 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000041}
42
halcanary130444f2015-04-25 06:45:07 -070043SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) {
44 if (this != &other) {
45 this->~SkPDFUnion();
46 SkNEW_PLACEMENT_ARGS(this, SkPDFUnion, (other.move()));
47 }
48 return *this;
49}
halcanary6a144342015-01-23 11:45:10 -080050
halcanary130444f2015-04-25 06:45:07 -070051SkPDFUnion::SkPDFUnion(SkPDFUnion&& other) {
52 SkASSERT(this != &other);
53 memcpy(this, &other, sizeof(*this));
54 other.fType = Type::kDestroyed;
55}
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000056
halcanary130444f2015-04-25 06:45:07 -070057#if 0
58SkPDFUnion SkPDFUnion::copy() const {
59 SkPDFUnion u(fType);
60 memcpy(&u, this, sizeof(u));
61 switch (fType) {
62 case Type::kNameSkS:
63 case Type::kStringSkS:
64 SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString,
65 (*pun(fSkString)));
66 return u.move();
67 case Type::kObjRef:
68 case Type::kObject:
69 SkRef(u.fObject);
70 return u.move();
71 default:
72 return u.move();
73 }
74}
75SkPDFUnion& SkPDFUnion::operator=(const SkPDFUnion& other) {
76 return *this = other.copy();
77}
78SkPDFUnion::SkPDFUnion(const SkPDFUnion& other) {
79 *this = other.copy();
80}
81#endif
82
83bool SkPDFUnion::isName() const {
84 return Type::kName == fType || Type::kNameSkS == fType;
85}
86
87#ifdef SK_DEBUG
88// Most names need no escaping. Such names are handled as static
89// const strings.
90bool is_valid_name(const char* n) {
91 static const char kControlChars[] = "/%()<>[]{}";
92 while (*n) {
93 if (*n < '!' || *n > '~' || strchr(kControlChars, *n)) {
94 return false;
95 }
96 ++n;
97 }
98 return true;
99}
100#endif // SK_DEBUG
101
halcanary8ca89e12015-06-08 12:14:56 -0700102// Given an arbitrary string, write it as a valid name (not including
103// leading slash).
104static void write_name_escaped(SkWStream* o, const char* name) {
halcanary130444f2015-04-25 06:45:07 -0700105 static const char kToEscape[] = "#/%()<>[]{}";
halcanary130444f2015-04-25 06:45:07 -0700106 static const char kHex[] = "0123456789ABCDEF";
halcanary8ca89e12015-06-08 12:14:56 -0700107 for (const uint8_t* n = reinterpret_cast<const uint8_t*>(name); *n; ++n) {
halcanary130444f2015-04-25 06:45:07 -0700108 if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) {
halcanary8ca89e12015-06-08 12:14:56 -0700109 char buffer[3] = {'#', '\0', '\0'};
110 buffer[1] = kHex[(*n >> 4) & 0xF];
111 buffer[2] = kHex[*n & 0xF];
112 o->write(buffer, sizeof(buffer));
halcanary130444f2015-04-25 06:45:07 -0700113 } else {
halcanary8ca89e12015-06-08 12:14:56 -0700114 o->write(n, 1);
halcanary130444f2015-04-25 06:45:07 -0700115 }
116 }
halcanary130444f2015-04-25 06:45:07 -0700117}
118
119static void write_string(SkWStream* o, const SkString& s) {
120 o->write(s.c_str(), s.size());
121}
122
123static SkString format_string(const SkString& s) {
halcanarybc4696b2015-05-06 10:56:04 -0700124 return SkPDFUtils::FormatString(s.c_str(), s.size());
halcanary130444f2015-04-25 06:45:07 -0700125}
126
127static SkString format_string(const char* s) {
halcanarybc4696b2015-05-06 10:56:04 -0700128 return SkPDFUtils::FormatString(s, strlen(s));
halcanary130444f2015-04-25 06:45:07 -0700129}
130
131void SkPDFUnion::emitObject(SkWStream* stream,
132 const SkPDFObjNumMap& objNumMap,
133 const SkPDFSubstituteMap& substitutes) const {
134 switch (fType) {
135 case Type::kInt:
136 stream->writeDecAsText(fIntValue);
137 return;
138 case Type::kBool:
139 stream->writeText(fBoolValue ? "true" : "false");
140 return;
141 case Type::kScalar:
halcanarybc4696b2015-05-06 10:56:04 -0700142 SkPDFUtils::AppendScalar(fScalarValue, stream);
halcanary130444f2015-04-25 06:45:07 -0700143 return;
144 case Type::kName:
145 stream->writeText("/");
146 SkASSERT(is_valid_name(fStaticString));
147 stream->writeText(fStaticString);
148 return;
149 case Type::kString:
150 SkASSERT(fStaticString);
151 write_string(stream, format_string(fStaticString));
152 return;
153 case Type::kNameSkS:
154 stream->writeText("/");
halcanary8ca89e12015-06-08 12:14:56 -0700155 write_name_escaped(stream, pun(fSkString)->c_str());
halcanary130444f2015-04-25 06:45:07 -0700156 return;
157 case Type::kStringSkS:
158 write_string(stream, format_string(*pun(fSkString)));
159 return;
160 case Type::kObjRef:
161 stream->writeDecAsText(objNumMap.getObjectNumber(
162 substitutes.getSubstitute(fObject)));
163 stream->writeText(" 0 R"); // Generation number is always 0.
164 return;
165 case Type::kObject:
166 fObject->emitObject(stream, objNumMap, substitutes);
167 return;
168 default:
169 SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
170 }
171}
172
173void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
174 const SkPDFSubstituteMap& substituteMap) const {
175 switch (fType) {
176 case Type::kInt:
177 case Type::kBool:
178 case Type::kScalar:
179 case Type::kName:
180 case Type::kString:
181 case Type::kNameSkS:
182 case Type::kStringSkS:
183 return; // These have no resources.
184 case Type::kObjRef: {
185 SkPDFObject* obj = substituteMap.getSubstitute(fObject);
186 if (objNumMap->addObject(obj)) {
187 obj->addResources(objNumMap, substituteMap);
188 }
189 return;
190 }
191 case Type::kObject:
192 fObject->addResources(objNumMap, substituteMap);
193 return;
194 default:
195 SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
196 }
197}
198
199SkPDFUnion SkPDFUnion::Int(int32_t value) {
200 SkPDFUnion u(Type::kInt);
201 u.fIntValue = value;
202 return u.move();
203}
204
205SkPDFUnion SkPDFUnion::Bool(bool value) {
206 SkPDFUnion u(Type::kBool);
207 u.fBoolValue = value;
208 return u.move();
209}
210
211SkPDFUnion SkPDFUnion::Scalar(SkScalar value) {
212 SkPDFUnion u(Type::kScalar);
213 u.fScalarValue = value;
214 return u.move();
215}
216
217SkPDFUnion SkPDFUnion::Name(const char* value) {
218 SkPDFUnion u(Type::kName);
219 SkASSERT(value);
220 SkASSERT(is_valid_name(value));
221 u.fStaticString = value;
222 return u.move();
223}
224
225SkPDFUnion SkPDFUnion::String(const char* value) {
226 SkPDFUnion u(Type::kString);
227 SkASSERT(value);
228 u.fStaticString = value;
229 return u.move();
230}
231
232SkPDFUnion SkPDFUnion::Name(const SkString& s) {
233 SkPDFUnion u(Type::kNameSkS);
234 SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s));
235 return u.move();
236}
237
238SkPDFUnion SkPDFUnion::String(const SkString& s) {
239 SkPDFUnion u(Type::kStringSkS);
240 SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s));
241 return u.move();
242}
243
244SkPDFUnion SkPDFUnion::ObjRef(SkPDFObject* ptr) {
245 SkPDFUnion u(Type::kObjRef);
246 SkASSERT(ptr);
247 u.fObject = ptr;
248 return u.move();
249}
250
251SkPDFUnion SkPDFUnion::Object(SkPDFObject* ptr) {
252 SkPDFUnion u(Type::kObject);
253 SkASSERT(ptr);
254 u.fObject = ptr;
255 return u.move();
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000256}
257
halcanary6a144342015-01-23 11:45:10 -0800258////////////////////////////////////////////////////////////////////////////////
259
halcanary002653e2015-05-05 11:28:55 -0700260#if 0 // Enable if needed.
halcanary130444f2015-04-25 06:45:07 -0700261void SkPDFAtom::emitObject(SkWStream* stream,
262 const SkPDFObjNumMap& objNumMap,
263 const SkPDFSubstituteMap& substitutes) {
264 fValue.emitObject(stream, objNumMap, substitutes);
265}
266void SkPDFAtom::addResources(SkPDFObjNumMap* map,
267 const SkPDFSubstituteMap& substitutes) const {
268 fValue.addResources(map, substitutes);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000269}
halcanary002653e2015-05-05 11:28:55 -0700270#endif // 0
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000271
halcanary6a144342015-01-23 11:45:10 -0800272////////////////////////////////////////////////////////////////////////////////
273
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000274SkPDFArray::SkPDFArray() {}
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000275SkPDFArray::~SkPDFArray() {
halcanary130444f2015-04-25 06:45:07 -0700276 for (SkPDFUnion& value : fValues) {
277 value.~SkPDFUnion();
278 }
279 fValues.reset();
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000280}
281
halcanary130444f2015-04-25 06:45:07 -0700282int SkPDFArray::size() const { return fValues.count(); }
283
284void SkPDFArray::reserve(int length) { fValues.setReserve(length); }
285
halcanary37c46ca2015-03-31 12:30:20 -0700286void SkPDFArray::emitObject(SkWStream* stream,
287 const SkPDFObjNumMap& objNumMap,
288 const SkPDFSubstituteMap& substitutes) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000289 stream->writeText("[");
halcanary130444f2015-04-25 06:45:07 -0700290 for (int i = 0; i < fValues.count(); i++) {
291 fValues[i].emitObject(stream, objNumMap, substitutes);
292 if (i + 1 < fValues.count()) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000293 stream->writeText(" ");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000294 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000295 }
296 stream->writeText("]");
297}
298
halcanary37c46ca2015-03-31 12:30:20 -0700299void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
300 const SkPDFSubstituteMap& substitutes) const {
halcanary130444f2015-04-25 06:45:07 -0700301 for (const SkPDFUnion& value : fValues) {
302 value.addResources(catalog, substitutes);
halcanarybf799cd2015-02-10 13:32:09 -0800303 }
304}
305
halcanary130444f2015-04-25 06:45:07 -0700306void SkPDFArray::append(SkPDFUnion&& value) {
307 SkNEW_PLACEMENT_ARGS(fValues.append(), SkPDFUnion, (value.move()));
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000308}
309
reed@google.comc789cf12011-07-20 12:14:33 +0000310void SkPDFArray::appendInt(int32_t value) {
halcanary130444f2015-04-25 06:45:07 -0700311 this->append(SkPDFUnion::Int(value));
312}
313
314void SkPDFArray::appendBool(bool value) {
315 this->append(SkPDFUnion::Bool(value));
reed@google.comc789cf12011-07-20 12:14:33 +0000316}
317
318void SkPDFArray::appendScalar(SkScalar value) {
halcanary130444f2015-04-25 06:45:07 -0700319 this->append(SkPDFUnion::Scalar(value));
reed@google.comc789cf12011-07-20 12:14:33 +0000320}
321
322void SkPDFArray::appendName(const char name[]) {
halcanary130444f2015-04-25 06:45:07 -0700323 this->append(SkPDFUnion::Name(SkString(name)));
324}
325
326void SkPDFArray::appendName(const SkString& name) {
327 this->append(SkPDFUnion::Name(name));
328}
329
330void SkPDFArray::appendString(const SkString& value) {
331 this->append(SkPDFUnion::String(value));
332}
333
334void SkPDFArray::appendString(const char value[]) {
335 this->append(SkPDFUnion::String(value));
336}
337
338void SkPDFArray::appendObject(SkPDFObject* value) {
339 this->append(SkPDFUnion::Object(value));
340}
341
342void SkPDFArray::appendObjRef(SkPDFObject* value) {
343 this->append(SkPDFUnion::ObjRef(value));
reed@google.comc789cf12011-07-20 12:14:33 +0000344}
345
346///////////////////////////////////////////////////////////////////////////////
347
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000348SkPDFDict::SkPDFDict() {}
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000349
halcanary130444f2015-04-25 06:45:07 -0700350SkPDFDict::~SkPDFDict() { this->clear(); }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000351
halcanary130444f2015-04-25 06:45:07 -0700352SkPDFDict::SkPDFDict(const char type[]) { this->insertName("Type", type); }
halcanary1f8ed022014-06-27 10:37:27 -0700353
halcanary37c46ca2015-03-31 12:30:20 -0700354void SkPDFDict::emitObject(SkWStream* stream,
355 const SkPDFObjNumMap& objNumMap,
356 const SkPDFSubstituteMap& substitutes) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000357 stream->writeText("<<");
halcanary130444f2015-04-25 06:45:07 -0700358 for (int i = 0; i < fRecords.count(); i++) {
359 fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000360 stream->writeText(" ");
halcanary130444f2015-04-25 06:45:07 -0700361 fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
362 if (i + 1 < fRecords.count()) {
halcanary37c46ca2015-03-31 12:30:20 -0700363 stream->writeText("\n");
364 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000365 }
366 stream->writeText(">>");
367}
368
halcanary37c46ca2015-03-31 12:30:20 -0700369void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
370 const SkPDFSubstituteMap& substitutes) const {
halcanary130444f2015-04-25 06:45:07 -0700371 for (int i = 0; i < fRecords.count(); i++) {
372 fRecords[i].fKey.addResources(catalog, substitutes);
373 fRecords[i].fValue.addResources(catalog, substitutes);
halcanarybf799cd2015-02-10 13:32:09 -0800374 }
375}
376
halcanary130444f2015-04-25 06:45:07 -0700377void SkPDFDict::set(SkPDFUnion&& name, SkPDFUnion&& value) {
378 Record* rec = fRecords.append();
379 SkASSERT(name.isName());
380 SkNEW_PLACEMENT_ARGS(&rec->fKey, SkPDFUnion, (name.move()));
381 SkNEW_PLACEMENT_ARGS(&rec->fValue, SkPDFUnion, (value.move()));
382}
383
384int SkPDFDict::size() const { return fRecords.count(); }
385
386void SkPDFDict::insertObjRef(const char key[], SkPDFObject* value) {
387 this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
388}
389void SkPDFDict::insertObjRef(const SkString& key, SkPDFObject* value) {
390 this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
391}
392
393void SkPDFDict::insertObject(const char key[], SkPDFObject* value) {
394 this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
395}
396void SkPDFDict::insertObject(const SkString& key, SkPDFObject* value) {
397 this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
398}
399
halcanarya25b3372015-04-27 14:00:09 -0700400void SkPDFDict::insertBool(const char key[], bool value) {
401 this->set(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
402}
403
reed@google.comc789cf12011-07-20 12:14:33 +0000404void SkPDFDict::insertInt(const char key[], int32_t value) {
halcanary130444f2015-04-25 06:45:07 -0700405 this->set(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
406}
407
408void SkPDFDict::insertInt(const char key[], size_t value) {
409 this->insertInt(key, SkToS32(value));
reed@google.comc789cf12011-07-20 12:14:33 +0000410}
411
412void SkPDFDict::insertScalar(const char key[], SkScalar value) {
halcanary130444f2015-04-25 06:45:07 -0700413 this->set(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
reed@google.comc789cf12011-07-20 12:14:33 +0000414}
415
416void SkPDFDict::insertName(const char key[], const char name[]) {
halcanary130444f2015-04-25 06:45:07 -0700417 this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
418}
419
420void SkPDFDict::insertName(const char key[], const SkString& name) {
421 this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
422}
423
424void SkPDFDict::insertString(const char key[], const char value[]) {
425 this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
426}
427
428void SkPDFDict::insertString(const char key[], const SkString& value) {
429 this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
reed@google.comc789cf12011-07-20 12:14:33 +0000430}
431
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000432void SkPDFDict::clear() {
halcanary130444f2015-04-25 06:45:07 -0700433 for (Record& rec : fRecords) {
434 rec.fKey.~SkPDFUnion();
435 rec.fValue.~SkPDFUnion();
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000436 }
halcanary130444f2015-04-25 06:45:07 -0700437 fRecords.reset();
vandebo@chromium.org421d6442011-07-20 17:39:01 +0000438}
halcanary37c46ca2015-03-31 12:30:20 -0700439
440////////////////////////////////////////////////////////////////////////////////
441
442SkPDFSubstituteMap::~SkPDFSubstituteMap() {
443 fSubstituteMap.foreach(
444 [](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
445}
446
447void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
448 SkPDFObject* substitute) {
449 SkASSERT(original != substitute);
450 SkASSERT(!fSubstituteMap.find(original));
451 fSubstituteMap.set(original, SkRef(substitute));
452}
453
454SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
455 SkPDFObject** found = fSubstituteMap.find(object);
456 return found ? *found : object;
457}
458
459////////////////////////////////////////////////////////////////////////////////
460
461bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
462 if (fObjectNumbers.find(obj)) {
463 return false;
464 }
465 fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
466 fObjects.push(obj);
467 return true;
468}
469
470int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
471 int32_t* objectNumberFound = fObjectNumbers.find(obj);
472 SkASSERT(objectNumberFound);
473 return *objectNumberFound;
474}
475