1 /*
2 * Copyright (C) 2011 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "SkPDFCatalog.h"
18 #include "SkPDFTypes.h"
19 #include "SkStream.h"
20
21 #ifdef SK_BUILD_FOR_WIN
22 #define SNPRINTF _snprintf
23 #else
24 #define SNPRINTF snprintf
25 #endif
26
SkPDFObject()27 SkPDFObject::SkPDFObject() {}
~SkPDFObject()28 SkPDFObject::~SkPDFObject() {}
29
getOutputSize(SkPDFCatalog * catalog,bool indirect)30 size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
31 SkDynamicMemoryWStream buffer;
32 emitObject(&buffer, catalog, indirect);
33 return buffer.getOffset();
34 }
35
getResources(SkTDArray<SkPDFObject * > * resourceList)36 void SkPDFObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {}
37
emitIndirectObject(SkWStream * stream,SkPDFCatalog * catalog)38 void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) {
39 catalog->emitObjectNumber(stream, this);
40 stream->writeText(" obj\n");
41 emitObject(stream, catalog, false);
42 stream->writeText("\nendobj\n");
43 }
44
SkPDFObjRef(SkPDFObject * obj)45 SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
~SkPDFObjRef()46 SkPDFObjRef::~SkPDFObjRef() {}
47
getIndirectOutputSize(SkPDFCatalog * catalog)48 size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
49 return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
50 this->getOutputSize(catalog, false) + strlen("\nendobj\n");
51 }
52
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)53 void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
54 bool indirect) {
55 SkASSERT(!indirect);
56 catalog->emitObjectNumber(stream, fObj.get());
57 stream->writeText(" R");
58 }
59
getOutputSize(SkPDFCatalog * catalog,bool indirect)60 size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
61 SkASSERT(!indirect);
62 return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
63 }
64
SkPDFInt(int32_t value)65 SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
~SkPDFInt()66 SkPDFInt::~SkPDFInt() {}
67
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)68 void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
69 bool indirect) {
70 if (indirect)
71 return emitIndirectObject(stream, catalog);
72 stream->writeDecAsText(fValue);
73 }
74
SkPDFBool(bool value)75 SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
~SkPDFBool()76 SkPDFBool::~SkPDFBool() {}
77
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)78 void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
79 bool indirect) {
80 SkASSERT(!indirect);
81 if (fValue) {
82 stream->writeText("true");
83 } else {
84 stream->writeText("false");
85 }
86 }
87
getOutputSize(SkPDFCatalog * catalog,bool indirect)88 size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
89 SkASSERT(!indirect);
90 if (fValue)
91 return strlen("true");
92 return strlen("false");
93 }
94
SkPDFScalar(SkScalar value)95 SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
~SkPDFScalar()96 SkPDFScalar::~SkPDFScalar() {}
97
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)98 void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
99 bool indirect) {
100 if (indirect)
101 return emitIndirectObject(stream, catalog);
102
103 Append(fValue, stream);
104 }
105
106 // static
Append(SkScalar value,SkWStream * stream)107 void SkPDFScalar::Append(SkScalar value, SkWStream* stream) {
108 // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
109 // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
110 // When using floats that are outside the whole value range, we can use
111 // integers instead.
112
113
114 #if defined(SK_SCALAR_IS_FIXED)
115 stream->writeScalarAsText(value);
116 return;
117 #endif // SK_SCALAR_IS_FIXED
118
119 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
120 if (value > 32767 || value < -32767) {
121 stream->writeDecAsText(SkScalarRound(value));
122 return;
123 }
124
125 char buffer[SkStrAppendScalar_MaxSize];
126 char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
127 stream->write(buffer, end - buffer);
128 return;
129 #endif // !SK_ALLOW_LARGE_PDF_SCALARS
130
131 #if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
132 // Floats have 24bits of significance, so anything outside that range is
133 // no more precise than an int. (Plus PDF doesn't support scientific
134 // notation, so this clamps to SK_Max/MinS32).
135 if (value > (1 << 24) || value < -(1 << 24)) {
136 stream->writeDecAsText(value);
137 return;
138 }
139 // Continue to enforce the PDF limits for small floats.
140 if (value < 1.0f/65536 && value > -1.0f/65536) {
141 stream->writeDecAsText(0);
142 return;
143 }
144 // SkStrAppendFloat might still use scientific notation, so use snprintf
145 // directly..
146 static const int kFloat_MaxSize = 19;
147 char buffer[kFloat_MaxSize];
148 int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
149 // %f always prints trailing 0s, so strip them.
150 for (; buffer[len - 1] == '0' && len > 0; len--) {
151 buffer[len - 1] = '\0';
152 }
153 if (buffer[len - 1] == '.') {
154 buffer[len - 1] = '\0';
155 }
156 stream->writeText(buffer);
157 return;
158 #endif // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS
159 }
160
SkPDFString(const char value[])161 SkPDFString::SkPDFString(const char value[])
162 : fValue(formatString(value, strlen(value))) {
163 }
164
SkPDFString(const SkString & value)165 SkPDFString::SkPDFString(const SkString& value)
166 : fValue(formatString(value.c_str(), value.size())) {
167 }
168
SkPDFString(const uint16_t * value,size_t len,bool wideChars)169 SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
170 : fValue(formatString(value, len, wideChars)) {
171 }
172
~SkPDFString()173 SkPDFString::~SkPDFString() {}
174
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)175 void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
176 bool indirect) {
177 if (indirect)
178 return emitIndirectObject(stream, catalog);
179 stream->write(fValue.c_str(), fValue.size());
180 }
181
getOutputSize(SkPDFCatalog * catalog,bool indirect)182 size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
183 if (indirect)
184 return getIndirectOutputSize(catalog);
185 return fValue.size();
186 }
187
188 // static
formatString(const char * input,size_t len)189 SkString SkPDFString::formatString(const char* input, size_t len) {
190 return doFormatString(input, len, false, false);
191 }
192
formatString(const uint16_t * input,size_t len,bool wideChars)193 SkString SkPDFString::formatString(const uint16_t* input, size_t len,
194 bool wideChars) {
195 return doFormatString(input, len, true, wideChars);
196 }
197
198 // static
doFormatString(const void * input,size_t len,bool wideInput,bool wideOutput)199 SkString SkPDFString::doFormatString(const void* input, size_t len,
200 bool wideInput, bool wideOutput) {
201 SkASSERT(len <= kMaxLen);
202 const uint16_t* win = (const uint16_t*) input;
203 const char* cin = (const char*) input;
204
205 if (wideOutput) {
206 SkASSERT(wideInput);
207 SkString result;
208 result.append("<");
209 for (size_t i = 0; i < len; i++)
210 result.appendHex(win[i], 4);
211 result.append(">");
212 return result;
213 }
214
215 // 7-bit clean is a heuristic to decide what string format to use;
216 // a 7-bit clean string should require little escaping.
217 bool sevenBitClean = true;
218 for (size_t i = 0; i < len; i++) {
219 SkASSERT(!wideInput || !(win[i] & ~0xFF));
220 char val = wideInput ? win[i] : cin[i];
221 if (val > '~' || val < ' ') {
222 sevenBitClean = false;
223 break;
224 }
225 }
226
227 SkString result;
228 if (sevenBitClean) {
229 result.append("(");
230 for (size_t i = 0; i < len; i++) {
231 SkASSERT(!wideInput || !(win[i] & ~0xFF));
232 char val = wideInput ? win[i] : cin[i];
233 if (val == '\\' || val == '(' || val == ')')
234 result.append("\\");
235 result.append(&val, 1);
236 }
237 result.append(")");
238 } else {
239 result.append("<");
240 for (size_t i = 0; i < len; i++) {
241 SkASSERT(!wideInput || !(win[i] & ~0xFF));
242 unsigned char val = wideInput ? win[i] : cin[i];
243 result.appendHex(val, 2);
244 }
245 result.append(">");
246 }
247
248 return result;
249 }
250
SkPDFName(const char name[])251 SkPDFName::SkPDFName(const char name[]) : fValue(formatName(SkString(name))) {}
SkPDFName(const SkString & name)252 SkPDFName::SkPDFName(const SkString& name) : fValue(formatName(name)) {}
~SkPDFName()253 SkPDFName::~SkPDFName() {}
254
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)255 void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
256 bool indirect) {
257 SkASSERT(!indirect);
258 stream->write(fValue.c_str(), fValue.size());
259 }
260
getOutputSize(SkPDFCatalog * catalog,bool indirect)261 size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
262 SkASSERT(!indirect);
263 return fValue.size();
264 }
265
266 // static
formatName(const SkString & input)267 SkString SkPDFName::formatName(const SkString& input) {
268 SkASSERT(input.size() <= kMaxLen);
269
270 SkString result("/");
271 for (size_t i = 0; i < input.size(); i++) {
272 if (input[i] & 0x80 || input[i] < '!' || input[i] == '#') {
273 result.append("#");
274 result.appendHex(input[i], 2);
275 } else {
276 result.append(input.c_str() + i, 1);
277 }
278 }
279
280 return result;
281 }
282
SkPDFArray()283 SkPDFArray::SkPDFArray() {}
~SkPDFArray()284 SkPDFArray::~SkPDFArray() {
285 fValue.safeUnrefAll();
286 }
287
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)288 void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
289 bool indirect) {
290 if (indirect)
291 return emitIndirectObject(stream, catalog);
292
293 stream->writeText("[");
294 for (int i = 0; i < fValue.count(); i++) {
295 fValue[i]->emitObject(stream, catalog, false);
296 if (i + 1 < fValue.count())
297 stream->writeText(" ");
298 }
299 stream->writeText("]");
300 }
301
getOutputSize(SkPDFCatalog * catalog,bool indirect)302 size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
303 if (indirect)
304 return getIndirectOutputSize(catalog);
305
306 size_t result = strlen("[]");
307 if (fValue.count())
308 result += fValue.count() - 1;
309 for (int i = 0; i < fValue.count(); i++)
310 result += fValue[i]->getOutputSize(catalog, false);
311 return result;
312 }
313
reserve(int length)314 void SkPDFArray::reserve(int length) {
315 SkASSERT(length <= kMaxLen);
316 fValue.setReserve(length);
317 }
318
setAt(int offset,SkPDFObject * value)319 SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) {
320 SkASSERT(offset < fValue.count());
321 SkSafeUnref(fValue[offset]);
322 fValue[offset] = value;
323 SkSafeRef(fValue[offset]);
324 return value;
325 }
326
append(SkPDFObject * value)327 SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
328 SkASSERT(fValue.count() < kMaxLen);
329 SkSafeRef(value);
330 fValue.push(value);
331 return value;
332 }
333
SkPDFDict()334 SkPDFDict::SkPDFDict() {}
335
SkPDFDict(const char type[])336 SkPDFDict::SkPDFDict(const char type[]) {
337 insert("Type", new SkPDFName(type))->unref();
338 }
339
~SkPDFDict()340 SkPDFDict::~SkPDFDict() {
341 clear();
342 }
343
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)344 void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
345 bool indirect) {
346 if (indirect)
347 return emitIndirectObject(stream, catalog);
348
349 stream->writeText("<<");
350 for (int i = 0; i < fValue.count(); i++) {
351 fValue[i].key->emitObject(stream, catalog, false);
352 stream->writeText(" ");
353 fValue[i].value->emitObject(stream, catalog, false);
354 stream->writeText("\n");
355 }
356 stream->writeText(">>");
357 }
358
getOutputSize(SkPDFCatalog * catalog,bool indirect)359 size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
360 if (indirect)
361 return getIndirectOutputSize(catalog);
362
363 size_t result = strlen("<<>>") + (fValue.count() * 2);
364 for (int i = 0; i < fValue.count(); i++) {
365 result += fValue[i].key->getOutputSize(catalog, false);
366 result += fValue[i].value->getOutputSize(catalog, false);
367 }
368 return result;
369 }
370
insert(SkPDFName * key,SkPDFObject * value)371 SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
372 struct Rec* newEntry = fValue.append();
373 newEntry->key = key;
374 SkSafeRef(newEntry->key);
375 newEntry->value = value;
376 SkSafeRef(newEntry->value);
377 return value;
378 }
379
insert(const char key[],SkPDFObject * value)380 SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
381 SkRefPtr<SkPDFName> keyName = new SkPDFName(key);
382 keyName->unref(); // SkRefPtr and new both took a reference.
383 return insert(keyName.get(), value);
384 }
385
clear()386 void SkPDFDict::clear() {
387 for (int i = 0; i < fValue.count(); i++) {
388 SkSafeUnref(fValue[i].key);
389 SkSafeUnref(fValue[i].value);
390 }
391 fValue.reset();
392 }
393