blob: bbeeeeb87b48a82e8a762458a932c051ea402d16 [file] [log] [blame]
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00001/*
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00002 * Copyright (C) 2011 Google Inc.
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00003 *
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
vandebo@chromium.org094316b2011-03-04 03:15:13 +000021#ifdef SK_BUILD_FOR_WIN
22 #define SNPRINTF _snprintf
23#else
24 #define SNPRINTF snprintf
25#endif
26
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000027SkPDFObject::SkPDFObject() {}
28SkPDFObject::~SkPDFObject() {}
29
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000030size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
31 SkDynamicMemoryWStream buffer;
32 emitObject(&buffer, catalog, indirect);
33 return buffer.getOffset();
34}
35
vandebo@chromium.orga5180862010-10-26 19:48:49 +000036void SkPDFObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {}
37
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000038void 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
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000045SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
46SkPDFObjRef::~SkPDFObjRef() {}
47
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000048size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
49 return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
50 this->getOutputSize(catalog, false) + strlen("\nendobj\n");
51}
52
53void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
54 bool indirect) {
55 SkASSERT(!indirect);
56 catalog->emitObjectNumber(stream, fObj.get());
57 stream->writeText(" R");
58}
59
60size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
61 SkASSERT(!indirect);
62 return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
63}
64
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000065SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
66SkPDFInt::~SkPDFInt() {}
67
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000068void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
69 bool indirect) {
70 if (indirect)
71 return emitIndirectObject(stream, catalog);
72 stream->writeDecAsText(fValue);
73}
74
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000075SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
76SkPDFBool::~SkPDFBool() {}
77
78void 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
88size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
89 SkASSERT(!indirect);
90 if (fValue)
91 return strlen("true");
92 return strlen("false");
93}
94
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000095SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
96SkPDFScalar::~SkPDFScalar() {}
97
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000098void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
99 bool indirect) {
100 if (indirect)
101 return emitIndirectObject(stream, catalog);
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000102
103 SkString tmp;
104 Append(fValue, &tmp);
105 stream->write(tmp.c_str(), tmp.size());
106}
107
108// static
109void SkPDFScalar::Append(SkScalar value, SkString* string) {
110 // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
111 // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
112 // When using floats that are outside the whole value range, we can use
113 // integers instead.
114
115#if defined(SK_SCALAR_IS_FIXED)
116 string->appendScalar(value);
117 return;
118#endif // SK_SCALAR_IS_FIXED
119
120#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
121 if (value > 32767 || value < -32767) {
122 string->appendS32(SkScalarRound(value));
123 return;
124 }
125
126 char buffer[SkStrAppendScalar_MaxSize];
127 char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
128 string->append(buffer, end - buffer);
129 return;
130#endif // !SK_ALLOW_LARGE_PDF_SCALARS
131
132#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
133 // Floats have 24bits of significance, so anything outside that range is
134 // no more precise than an int. (Plus PDF doesn't support scientific
135 // notation, so this clamps to SK_Max/MinS32).
136 if (value > (1 << 24) || value < -(1 << 24)) {
137 string->appendS32(value);
138 return;
139 }
140 // Continue to enforce the PDF limits for small floats.
141 if (value < 1.0f/65536 && value > -1.0f/65536) {
142 string->appendS32(0);
143 return;
144 }
145 // SkStrAppendFloat might still use scientific notation, so use snprintf
146 // directly..
147 static const int kFloat_MaxSize = 19;
148 char buffer[kFloat_MaxSize];
149 int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
150 // %f always prints trailing 0s, so strip them.
151 for (; buffer[len - 1] == '0' && len > 0; len--) {
152 buffer[len - 1] = '\0';
153 }
154 if (buffer[len - 1] == '.') {
155 buffer[len - 1] = '\0';
156 }
157 string->append(buffer);
158 return;
159#endif // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000160}
161
162SkPDFString::SkPDFString(const char value[])
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000163 : fValue(formatString(value, strlen(value))) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000164}
165
166SkPDFString::SkPDFString(const SkString& value)
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000167 : fValue(formatString(value.c_str(), value.size())) {
168}
169
170SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
171 : fValue(formatString(value, len, wideChars)) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000172}
173
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000174SkPDFString::~SkPDFString() {}
175
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000176void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
177 bool indirect) {
178 if (indirect)
179 return emitIndirectObject(stream, catalog);
180 stream->write(fValue.c_str(), fValue.size());
181}
182
183size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
184 if (indirect)
185 return getIndirectOutputSize(catalog);
186 return fValue.size();
187}
188
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000189// static
190SkString SkPDFString::formatString(const char* input, size_t len) {
191 return doFormatString(input, len, false, false);
192}
193
194SkString SkPDFString::formatString(const uint16_t* input, size_t len,
195 bool wideChars) {
196 return doFormatString(input, len, true, wideChars);
197}
198
199// static
200SkString SkPDFString::doFormatString(const void* input, size_t len,
201 bool wideInput, bool wideOutput) {
202 SkASSERT(len <= kMaxLen);
203 const uint16_t* win = (const uint16_t*) input;
204 const char* cin = (const char*) input;
205
206 if (wideOutput) {
207 SkASSERT(wideInput);
208 SkString result;
209 result.append("<");
210 for (size_t i = 0; i < len; i++)
211 result.appendHex(win[i], 4);
212 result.append(">");
213 return result;
214 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000215
216 // 7-bit clean is a heuristic to decide what string format to use;
217 // a 7-bit clean string should require little escaping.
218 bool sevenBitClean = true;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000219 for (size_t i = 0; i < len; i++) {
220 SkASSERT(!wideInput || !(win[i] & ~0xFF));
221 char val = wideInput ? win[i] : cin[i];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000222 if (val > '~' || val < ' ') {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000223 sevenBitClean = false;
224 break;
225 }
226 }
227
228 SkString result;
229 if (sevenBitClean) {
230 result.append("(");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000231 for (size_t i = 0; i < len; i++) {
232 SkASSERT(!wideInput || !(win[i] & ~0xFF));
233 char val = wideInput ? win[i] : cin[i];
234 if (val == '\\' || val == '(' || val == ')')
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000235 result.append("\\");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000236 result.append(&val, 1);
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000237 }
238 result.append(")");
239 } else {
240 result.append("<");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000241 for (size_t i = 0; i < len; i++) {
242 SkASSERT(!wideInput || !(win[i] & ~0xFF));
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000243 unsigned char val = wideInput ? win[i] : cin[i];
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000244 result.appendHex(val, 2);
245 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000246 result.append(">");
247 }
248
249 return result;
250}
251
252SkPDFName::SkPDFName(const char name[]) : fValue(formatName(SkString(name))) {}
253SkPDFName::SkPDFName(const SkString& name) : fValue(formatName(name)) {}
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000254SkPDFName::~SkPDFName() {}
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000255
256void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
257 bool indirect) {
258 SkASSERT(!indirect);
259 stream->write(fValue.c_str(), fValue.size());
260}
261
262size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
263 SkASSERT(!indirect);
264 return fValue.size();
265}
266
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000267// static
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000268SkString SkPDFName::formatName(const SkString& input) {
269 SkASSERT(input.size() <= kMaxLen);
270
271 SkString result("/");
272 for (size_t i = 0; i < input.size(); i++) {
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000273 if (input[i] & 0x80 || input[i] < '!' || input[i] == '#') {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000274 result.append("#");
275 result.appendHex(input[i], 2);
276 } else {
277 result.append(input.c_str() + i, 1);
278 }
279 }
280
281 return result;
282}
283
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000284SkPDFArray::SkPDFArray() {}
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000285SkPDFArray::~SkPDFArray() {
286 fValue.safeUnrefAll();
287}
288
289void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
290 bool indirect) {
291 if (indirect)
292 return emitIndirectObject(stream, catalog);
293
294 stream->writeText("[");
295 for (int i = 0; i < fValue.count(); i++) {
296 fValue[i]->emitObject(stream, catalog, false);
297 if (i + 1 < fValue.count())
298 stream->writeText(" ");
299 }
300 stream->writeText("]");
301}
302
303size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
304 if (indirect)
305 return getIndirectOutputSize(catalog);
306
307 size_t result = strlen("[]");
308 if (fValue.count())
309 result += fValue.count() - 1;
310 for (int i = 0; i < fValue.count(); i++)
311 result += fValue[i]->getOutputSize(catalog, false);
312 return result;
313}
314
315void SkPDFArray::reserve(int length) {
316 SkASSERT(length <= kMaxLen);
317 fValue.setReserve(length);
318}
319
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000320SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000321 SkASSERT(offset < fValue.count());
322 SkSafeUnref(fValue[offset]);
323 fValue[offset] = value;
324 SkSafeRef(fValue[offset]);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000325 return value;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000326}
327
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000328SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000329 SkASSERT(fValue.count() < kMaxLen);
330 SkSafeRef(value);
331 fValue.push(value);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000332 return value;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000333}
334
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +0000335SkPDFDict::SkPDFDict() {}
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000336
337SkPDFDict::SkPDFDict(const char type[]) {
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000338 insert("Type", new SkPDFName(type))->unref();
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000339}
340
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000341SkPDFDict::~SkPDFDict() {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000342 clear();
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000343}
344
345void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
346 bool indirect) {
347 if (indirect)
348 return emitIndirectObject(stream, catalog);
349
350 stream->writeText("<<");
351 for (int i = 0; i < fValue.count(); i++) {
352 fValue[i].key->emitObject(stream, catalog, false);
353 stream->writeText(" ");
354 fValue[i].value->emitObject(stream, catalog, false);
355 stream->writeText("\n");
356 }
357 stream->writeText(">>");
358}
359
360size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
361 if (indirect)
362 return getIndirectOutputSize(catalog);
363
364 size_t result = strlen("<<>>") + (fValue.count() * 2);
365 for (int i = 0; i < fValue.count(); i++) {
366 result += fValue[i].key->getOutputSize(catalog, false);
367 result += fValue[i].value->getOutputSize(catalog, false);
368 }
369 return result;
370}
371
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000372SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000373 struct Rec* newEntry = fValue.append();
374 newEntry->key = key;
375 SkSafeRef(newEntry->key);
376 newEntry->value = value;
377 SkSafeRef(newEntry->value);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000378 return value;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000379}
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000380
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000381SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000382 SkRefPtr<SkPDFName> keyName = new SkPDFName(key);
383 keyName->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000384 return insert(keyName.get(), value);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000385}
386
387void SkPDFDict::clear() {
388 for (int i = 0; i < fValue.count(); i++) {
reed@google.com82065d62011-02-07 15:30:46 +0000389 SkSafeUnref(fValue[i].key);
390 SkSafeUnref(fValue[i].value);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000391 }
392 fValue.reset();
393}