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 "SkPDFTypes.h"
10 #include "SkPDFUtils.h"
11 #include "SkStream.h"
12
13 #ifdef SK_BUILD_FOR_WIN
14 #define SNPRINTF _snprintf
15 #else
16 #define SNPRINTF snprintf
17 #endif
18
19 ////////////////////////////////////////////////////////////////////////////////
20
pun(char * x)21 SkString* pun(char* x) { return reinterpret_cast<SkString*>(x); }
pun(const char * x)22 const SkString* pun(const char* x) {
23 return reinterpret_cast<const SkString*>(x);
24 }
25
SkPDFUnion(Type t)26 SkPDFUnion::SkPDFUnion(Type t) : fType(t) {}
27
~SkPDFUnion()28 SkPDFUnion::~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;
40 }
41 }
42
operator =(SkPDFUnion && other)43 SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) {
44 if (this != &other) {
45 this->~SkPDFUnion();
46 SkNEW_PLACEMENT_ARGS(this, SkPDFUnion, (other.move()));
47 }
48 return *this;
49 }
50
SkPDFUnion(SkPDFUnion && other)51 SkPDFUnion::SkPDFUnion(SkPDFUnion&& other) {
52 SkASSERT(this != &other);
53 memcpy(this, &other, sizeof(*this));
54 other.fType = Type::kDestroyed;
55 }
56
57 #if 0
58 SkPDFUnion 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 }
75 SkPDFUnion& SkPDFUnion::operator=(const SkPDFUnion& other) {
76 return *this = other.copy();
77 }
78 SkPDFUnion::SkPDFUnion(const SkPDFUnion& other) {
79 *this = other.copy();
80 }
81 #endif
82
isName() const83 bool 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.
is_valid_name(const char * n)90 bool 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
102 // Given an arbitrary string, convert it to a valid name.
escape_name(const char * name,size_t len)103 static SkString escape_name(const char* name, size_t len) {
104 static const char kToEscape[] = "#/%()<>[]{}";
105 int count = 0;
106 const char* const end = &name[len];
107 for (const char* n = name; n != end; ++n) {
108 if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) {
109 count += 2;
110 }
111 ++count;
112 }
113 SkString result(count);
114 char* s = result.writable_str();
115 static const char kHex[] = "0123456789ABCDEF";
116 for (const char* n = name; n != end; ++n) {
117 if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) {
118 *s++ = '#';
119 *s++ = kHex[(*n >> 4) & 0xF];
120 *s++ = kHex[*n & 0xF];
121 } else {
122 *s++ = *n;
123 }
124 }
125 SkASSERT(&result.writable_str()[count] == s); // don't over-write
126 return result; // allocated space
127 }
128
escape_name(const SkString & name)129 static SkString escape_name(const SkString& name) {
130 return escape_name(name.c_str(), name.size());
131 }
132
write_string(SkWStream * o,const SkString & s)133 static void write_string(SkWStream* o, const SkString& s) {
134 o->write(s.c_str(), s.size());
135 }
136
format_string(const SkString & s)137 static SkString format_string(const SkString& s) {
138 return SkPDFUtils::FormatString(s.c_str(), s.size());
139 }
140
format_string(const char * s)141 static SkString format_string(const char* s) {
142 return SkPDFUtils::FormatString(s, strlen(s));
143 }
144
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes) const145 void SkPDFUnion::emitObject(SkWStream* stream,
146 const SkPDFObjNumMap& objNumMap,
147 const SkPDFSubstituteMap& substitutes) const {
148 switch (fType) {
149 case Type::kInt:
150 stream->writeDecAsText(fIntValue);
151 return;
152 case Type::kBool:
153 stream->writeText(fBoolValue ? "true" : "false");
154 return;
155 case Type::kScalar:
156 SkPDFUtils::AppendScalar(fScalarValue, stream);
157 return;
158 case Type::kName:
159 stream->writeText("/");
160 SkASSERT(is_valid_name(fStaticString));
161 stream->writeText(fStaticString);
162 return;
163 case Type::kString:
164 SkASSERT(fStaticString);
165 write_string(stream, format_string(fStaticString));
166 return;
167 case Type::kNameSkS:
168 stream->writeText("/");
169 write_string(stream, escape_name(*pun(fSkString)));
170 return;
171 case Type::kStringSkS:
172 write_string(stream, format_string(*pun(fSkString)));
173 return;
174 case Type::kObjRef:
175 stream->writeDecAsText(objNumMap.getObjectNumber(
176 substitutes.getSubstitute(fObject)));
177 stream->writeText(" 0 R"); // Generation number is always 0.
178 return;
179 case Type::kObject:
180 fObject->emitObject(stream, objNumMap, substitutes);
181 return;
182 default:
183 SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
184 }
185 }
186
addResources(SkPDFObjNumMap * objNumMap,const SkPDFSubstituteMap & substituteMap) const187 void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
188 const SkPDFSubstituteMap& substituteMap) const {
189 switch (fType) {
190 case Type::kInt:
191 case Type::kBool:
192 case Type::kScalar:
193 case Type::kName:
194 case Type::kString:
195 case Type::kNameSkS:
196 case Type::kStringSkS:
197 return; // These have no resources.
198 case Type::kObjRef: {
199 SkPDFObject* obj = substituteMap.getSubstitute(fObject);
200 if (objNumMap->addObject(obj)) {
201 obj->addResources(objNumMap, substituteMap);
202 }
203 return;
204 }
205 case Type::kObject:
206 fObject->addResources(objNumMap, substituteMap);
207 return;
208 default:
209 SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
210 }
211 }
212
Int(int32_t value)213 SkPDFUnion SkPDFUnion::Int(int32_t value) {
214 SkPDFUnion u(Type::kInt);
215 u.fIntValue = value;
216 return u.move();
217 }
218
Bool(bool value)219 SkPDFUnion SkPDFUnion::Bool(bool value) {
220 SkPDFUnion u(Type::kBool);
221 u.fBoolValue = value;
222 return u.move();
223 }
224
Scalar(SkScalar value)225 SkPDFUnion SkPDFUnion::Scalar(SkScalar value) {
226 SkPDFUnion u(Type::kScalar);
227 u.fScalarValue = value;
228 return u.move();
229 }
230
Name(const char * value)231 SkPDFUnion SkPDFUnion::Name(const char* value) {
232 SkPDFUnion u(Type::kName);
233 SkASSERT(value);
234 SkASSERT(is_valid_name(value));
235 u.fStaticString = value;
236 return u.move();
237 }
238
String(const char * value)239 SkPDFUnion SkPDFUnion::String(const char* value) {
240 SkPDFUnion u(Type::kString);
241 SkASSERT(value);
242 u.fStaticString = value;
243 return u.move();
244 }
245
Name(const SkString & s)246 SkPDFUnion SkPDFUnion::Name(const SkString& s) {
247 SkPDFUnion u(Type::kNameSkS);
248 SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s));
249 return u.move();
250 }
251
String(const SkString & s)252 SkPDFUnion SkPDFUnion::String(const SkString& s) {
253 SkPDFUnion u(Type::kStringSkS);
254 SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s));
255 return u.move();
256 }
257
ObjRef(SkPDFObject * ptr)258 SkPDFUnion SkPDFUnion::ObjRef(SkPDFObject* ptr) {
259 SkPDFUnion u(Type::kObjRef);
260 SkASSERT(ptr);
261 u.fObject = ptr;
262 return u.move();
263 }
264
Object(SkPDFObject * ptr)265 SkPDFUnion SkPDFUnion::Object(SkPDFObject* ptr) {
266 SkPDFUnion u(Type::kObject);
267 SkASSERT(ptr);
268 u.fObject = ptr;
269 return u.move();
270 }
271
272 ////////////////////////////////////////////////////////////////////////////////
273
274 #if 0 // Enable if needed.
275 void SkPDFAtom::emitObject(SkWStream* stream,
276 const SkPDFObjNumMap& objNumMap,
277 const SkPDFSubstituteMap& substitutes) {
278 fValue.emitObject(stream, objNumMap, substitutes);
279 }
280 void SkPDFAtom::addResources(SkPDFObjNumMap* map,
281 const SkPDFSubstituteMap& substitutes) const {
282 fValue.addResources(map, substitutes);
283 }
284 #endif // 0
285
286 ////////////////////////////////////////////////////////////////////////////////
287
SkPDFArray()288 SkPDFArray::SkPDFArray() {}
~SkPDFArray()289 SkPDFArray::~SkPDFArray() {
290 for (SkPDFUnion& value : fValues) {
291 value.~SkPDFUnion();
292 }
293 fValues.reset();
294 }
295
size() const296 int SkPDFArray::size() const { return fValues.count(); }
297
reserve(int length)298 void SkPDFArray::reserve(int length) { fValues.setReserve(length); }
299
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes)300 void SkPDFArray::emitObject(SkWStream* stream,
301 const SkPDFObjNumMap& objNumMap,
302 const SkPDFSubstituteMap& substitutes) {
303 stream->writeText("[");
304 for (int i = 0; i < fValues.count(); i++) {
305 fValues[i].emitObject(stream, objNumMap, substitutes);
306 if (i + 1 < fValues.count()) {
307 stream->writeText(" ");
308 }
309 }
310 stream->writeText("]");
311 }
312
addResources(SkPDFObjNumMap * catalog,const SkPDFSubstituteMap & substitutes) const313 void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
314 const SkPDFSubstituteMap& substitutes) const {
315 for (const SkPDFUnion& value : fValues) {
316 value.addResources(catalog, substitutes);
317 }
318 }
319
append(SkPDFUnion && value)320 void SkPDFArray::append(SkPDFUnion&& value) {
321 SkNEW_PLACEMENT_ARGS(fValues.append(), SkPDFUnion, (value.move()));
322 }
323
appendInt(int32_t value)324 void SkPDFArray::appendInt(int32_t value) {
325 this->append(SkPDFUnion::Int(value));
326 }
327
appendBool(bool value)328 void SkPDFArray::appendBool(bool value) {
329 this->append(SkPDFUnion::Bool(value));
330 }
331
appendScalar(SkScalar value)332 void SkPDFArray::appendScalar(SkScalar value) {
333 this->append(SkPDFUnion::Scalar(value));
334 }
335
appendName(const char name[])336 void SkPDFArray::appendName(const char name[]) {
337 this->append(SkPDFUnion::Name(SkString(name)));
338 }
339
appendName(const SkString & name)340 void SkPDFArray::appendName(const SkString& name) {
341 this->append(SkPDFUnion::Name(name));
342 }
343
appendString(const SkString & value)344 void SkPDFArray::appendString(const SkString& value) {
345 this->append(SkPDFUnion::String(value));
346 }
347
appendString(const char value[])348 void SkPDFArray::appendString(const char value[]) {
349 this->append(SkPDFUnion::String(value));
350 }
351
appendObject(SkPDFObject * value)352 void SkPDFArray::appendObject(SkPDFObject* value) {
353 this->append(SkPDFUnion::Object(value));
354 }
355
appendObjRef(SkPDFObject * value)356 void SkPDFArray::appendObjRef(SkPDFObject* value) {
357 this->append(SkPDFUnion::ObjRef(value));
358 }
359
360 ///////////////////////////////////////////////////////////////////////////////
361
SkPDFDict()362 SkPDFDict::SkPDFDict() {}
363
~SkPDFDict()364 SkPDFDict::~SkPDFDict() { this->clear(); }
365
SkPDFDict(const char type[])366 SkPDFDict::SkPDFDict(const char type[]) { this->insertName("Type", type); }
367
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap,const SkPDFSubstituteMap & substitutes)368 void SkPDFDict::emitObject(SkWStream* stream,
369 const SkPDFObjNumMap& objNumMap,
370 const SkPDFSubstituteMap& substitutes) {
371 stream->writeText("<<");
372 for (int i = 0; i < fRecords.count(); i++) {
373 fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
374 stream->writeText(" ");
375 fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
376 if (i + 1 < fRecords.count()) {
377 stream->writeText("\n");
378 }
379 }
380 stream->writeText(">>");
381 }
382
addResources(SkPDFObjNumMap * catalog,const SkPDFSubstituteMap & substitutes) const383 void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
384 const SkPDFSubstituteMap& substitutes) const {
385 for (int i = 0; i < fRecords.count(); i++) {
386 fRecords[i].fKey.addResources(catalog, substitutes);
387 fRecords[i].fValue.addResources(catalog, substitutes);
388 }
389 }
390
set(SkPDFUnion && name,SkPDFUnion && value)391 void SkPDFDict::set(SkPDFUnion&& name, SkPDFUnion&& value) {
392 Record* rec = fRecords.append();
393 SkASSERT(name.isName());
394 SkNEW_PLACEMENT_ARGS(&rec->fKey, SkPDFUnion, (name.move()));
395 SkNEW_PLACEMENT_ARGS(&rec->fValue, SkPDFUnion, (value.move()));
396 }
397
size() const398 int SkPDFDict::size() const { return fRecords.count(); }
399
insertObjRef(const char key[],SkPDFObject * value)400 void SkPDFDict::insertObjRef(const char key[], SkPDFObject* value) {
401 this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
402 }
insertObjRef(const SkString & key,SkPDFObject * value)403 void SkPDFDict::insertObjRef(const SkString& key, SkPDFObject* value) {
404 this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
405 }
406
insertObject(const char key[],SkPDFObject * value)407 void SkPDFDict::insertObject(const char key[], SkPDFObject* value) {
408 this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
409 }
insertObject(const SkString & key,SkPDFObject * value)410 void SkPDFDict::insertObject(const SkString& key, SkPDFObject* value) {
411 this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
412 }
413
insertBool(const char key[],bool value)414 void SkPDFDict::insertBool(const char key[], bool value) {
415 this->set(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
416 }
417
insertInt(const char key[],int32_t value)418 void SkPDFDict::insertInt(const char key[], int32_t value) {
419 this->set(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
420 }
421
insertInt(const char key[],size_t value)422 void SkPDFDict::insertInt(const char key[], size_t value) {
423 this->insertInt(key, SkToS32(value));
424 }
425
insertScalar(const char key[],SkScalar value)426 void SkPDFDict::insertScalar(const char key[], SkScalar value) {
427 this->set(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
428 }
429
insertName(const char key[],const char name[])430 void SkPDFDict::insertName(const char key[], const char name[]) {
431 this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
432 }
433
insertName(const char key[],const SkString & name)434 void SkPDFDict::insertName(const char key[], const SkString& name) {
435 this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
436 }
437
insertString(const char key[],const char value[])438 void SkPDFDict::insertString(const char key[], const char value[]) {
439 this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
440 }
441
insertString(const char key[],const SkString & value)442 void SkPDFDict::insertString(const char key[], const SkString& value) {
443 this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
444 }
445
clear()446 void SkPDFDict::clear() {
447 for (Record& rec : fRecords) {
448 rec.fKey.~SkPDFUnion();
449 rec.fValue.~SkPDFUnion();
450 }
451 fRecords.reset();
452 }
453
454 ////////////////////////////////////////////////////////////////////////////////
455
~SkPDFSubstituteMap()456 SkPDFSubstituteMap::~SkPDFSubstituteMap() {
457 fSubstituteMap.foreach(
458 [](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
459 }
460
setSubstitute(SkPDFObject * original,SkPDFObject * substitute)461 void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
462 SkPDFObject* substitute) {
463 SkASSERT(original != substitute);
464 SkASSERT(!fSubstituteMap.find(original));
465 fSubstituteMap.set(original, SkRef(substitute));
466 }
467
getSubstitute(SkPDFObject * object) const468 SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
469 SkPDFObject** found = fSubstituteMap.find(object);
470 return found ? *found : object;
471 }
472
473 ////////////////////////////////////////////////////////////////////////////////
474
addObject(SkPDFObject * obj)475 bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
476 if (fObjectNumbers.find(obj)) {
477 return false;
478 }
479 fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
480 fObjects.push(obj);
481 return true;
482 }
483
getObjectNumber(SkPDFObject * obj) const484 int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
485 int32_t* objectNumberFound = fObjectNumbers.find(obj);
486 SkASSERT(objectNumberFound);
487 return *objectNumberFound;
488 }
489
490