Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: src/pdf/SkPDFUtils.cpp

Issue 1720863003: SkPDF: fix scalar serialization (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2016-02-24 (Wednesday) 16:44:08 EST Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pdf/SkPDFUtils.h ('k') | tests/PDFPrimitivesTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 8
9 #include "SkData.h" 9 #include "SkData.h"
10 #include "SkGeometry.h" 10 #include "SkGeometry.h"
11 #include "SkPaint.h" 11 #include "SkPaint.h"
12 #include "SkPath.h" 12 #include "SkPath.h"
13 #include "SkPDFResourceDict.h" 13 #include "SkPDFResourceDict.h"
14 #include "SkPDFUtils.h" 14 #include "SkPDFUtils.h"
15 #include "SkStream.h" 15 #include "SkStream.h"
16 #include "SkString.h" 16 #include "SkString.h"
17 #include "SkPDFTypes.h" 17 #include "SkPDFTypes.h"
18 18
19 #include <cmath>
20
19 //static 21 //static
20 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) { 22 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
21 SkPDFArray* result = new SkPDFArray(); 23 SkPDFArray* result = new SkPDFArray();
22 result->reserve(4); 24 result->reserve(4);
23 result->appendScalar(rect.fLeft); 25 result->appendScalar(rect.fLeft);
24 result->appendScalar(rect.fTop); 26 result->appendScalar(rect.fTop);
25 result->appendScalar(rect.fRight); 27 result->appendScalar(rect.fRight);
26 result->appendScalar(rect.fBottom); 28 result->appendScalar(rect.fBottom);
27 return result; 29 return result;
28 } 30 }
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 SkPDFResourceDict::kPattern_ResourceType, 249 SkPDFResourceDict::kPattern_ResourceType,
248 objectIndex); 250 objectIndex);
249 content->writeText("/Pattern CS/Pattern cs/"); 251 content->writeText("/Pattern CS/Pattern cs/");
250 content->writeText(resourceName.c_str()); 252 content->writeText(resourceName.c_str());
251 content->writeText(" SCN/"); 253 content->writeText(" SCN/");
252 content->writeText(resourceName.c_str()); 254 content->writeText(resourceName.c_str());
253 content->writeText(" scn\n"); 255 content->writeText(" scn\n");
254 } 256 }
255 257
256 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) { 258 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
257 // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and 259 char result[kMaximumFloatDecimalLength];
258 // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31). 260 size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result);
259 // When using floats that are outside the whole value range, we can use 261 SkASSERT(len < kMaximumFloatDecimalLength);
260 // integers instead. 262 stream->write(result, len);
263 }
261 264
262 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) 265 /** Write a string into result, includeing a terminating '\0' (for
263 if (value > 32767 || value < -32767) { 266 unit testing). Return strlen(result) (for SkWStream::write) The
264 stream->writeDecAsText(SkScalarRoundToInt(value)); 267 resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
265 return; 268 sscanf(result, "%f", &x) will return the original value iff the
269 value is finite. This function accepts all possible input values.
270
271 Motivation: "PDF does not support [numbers] in exponential format
272 (such as 6.02e23)." Otherwise, this function would rely on a
273 sprintf-type function from the standard library. */
274 size_t SkPDFUtils::FloatToDecimal(float value,
275 char result[kMaximumFloatDecimalLength]) {
276 /* The longest result is -FLT_MIN.
277 We serialize it as "-.0000000000000000000000000000000000000117549435"
278 which has 48 characters plus a terminating '\0'. */
279
280 /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
281 most PDF rasterizers will use fixed-point scalars that lack the
282 dynamic range of floats. Even if this is the case, I want to
283 serialize these (uncommon) very small and very large scalar
284 values with enough precision to allow a floating-point
285 rasterizer to read them in with perfect accuracy.
286 Experimentally, rasterizers such as pdfium do seem to benefit
287 from this. Rasterizers that rely on fixed-point scalars should
288 gracefully ignore these values that they can not parse. */
289 char* output = &result[0];
290 const char* const end = &result[kMaximumFloatDecimalLength - 1];
291 // subtract one to leave space for '\0'.
292
293 /* This function is written to accept any possible input value,
294 including non-finite values such as INF and NAN. In that case,
295 we ignore value-correctness and and output a syntacticly-valid
296 number. */
297 if (value == SK_FloatInfinity) {
298 value = FLT_MAX; // nearest finite float.
266 } 299 }
300 if (value == SK_FloatNegativeInfinity) {
301 value = -FLT_MAX; // nearest finite float.
302 }
303 if (!std::isfinite(value) || value == 0.0f) {
304 // NAN is unsupported in PDF. Always output a valid number.
305 // Also catch zero here, as a special case.
306 *output++ = '0';
307 *output = '\0';
308 return output - result;
309 }
310 // Inspired by:
311 // http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal- conversion/
267 312
268 char buffer[SkStrAppendScalar_MaxSize]; 313 if (value < 0.0) {
269 char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value)); 314 *output++ = '-';
270 stream->write(buffer, end - buffer); 315 value = -value;
271 return; 316 }
272 #endif // !SK_ALLOW_LARGE_PDF_SCALARS 317 SkASSERT(value >= 0.0f);
273 318
274 #if defined(SK_ALLOW_LARGE_PDF_SCALARS) 319 // Must use double math to keep precision right.
275 // Floats have 24bits of significance, so anything outside that range is 320 double intPart;
276 // no more precise than an int. (Plus PDF doesn't support scientific 321 double fracPart = std::modf(static_cast<double>(value), &intPart);
277 // notation, so this clamps to SK_Max/MinS32). 322 SkASSERT(intPart + fracPart == static_cast<double>(value));
278 if (value > (1 << 24) || value < -(1 << 24)) { 323 size_t significantDigits = 0;
279 stream->writeDecAsText(value); 324 const size_t maxSignificantDigits = 9;
280 return; 325 // Any fewer significant digits loses precision. The unit test
326 // checks round-trip correctness.
327 SkASSERT(intPart >= 0.0 && fracPart >= 0.0); // negative handled already.
328 SkASSERT(intPart > 0.0 || fracPart > 0.0); // zero already caught.
329 if (intPart > 0.0) {
330 // put the intPart digits onto a stack for later reversal.
331 char reversed[1 + FLT_MAX_10_EXP]; // 39 == 1 + FLT_MAX_10_EXP
332 // the largest integer part is FLT_MAX; it has 39 decimal digits.
333 size_t reversedIndex = 0;
334 do {
335 SkASSERT(reversedIndex < sizeof(reversed));
336 int digit = static_cast<int>(std::fmod(intPart, 10.0));
337 SkASSERT(digit >= 0 && digit <= 9);
338 reversed[reversedIndex++] = '0' + digit;
339 intPart = std::floor(intPart / 10.0);
340 } while (intPart > 0.0);
341 significantDigits = reversedIndex;
342 SkASSERT(reversedIndex <= sizeof(reversed));
343 SkASSERT(output + reversedIndex <= end);
344 while (reversedIndex-- > 0) { // pop from stack, append to result
345 *output++ = reversed[reversedIndex];
346 }
281 } 347 }
282 // Continue to enforce the PDF limits for small floats. 348 if (fracPart > 0 && significantDigits < maxSignificantDigits) {
283 if (value < 1.0f/65536 && value > -1.0f/65536) { 349 *output++ = '.';
284 stream->writeDecAsText(0); 350 SkASSERT(output <= end);
285 return; 351 do {
352 fracPart = std::modf(fracPart * 10.0, &intPart);
353 int digit = static_cast<int>(intPart);
354 SkASSERT(digit >= 0 && digit <= 9);
355 *output++ = '0' + digit;
356 SkASSERT(output <= end);
357 if (digit > 0 || significantDigits > 0) {
358 // start counting significantDigits after first non-zero digit.
359 ++significantDigits;
360 }
361 } while (fracPart > 0.0
362 && significantDigits < maxSignificantDigits
363 && output < end);
364 // When fracPart == 0, additional digits will be zero.
365 // When significantDigits == maxSignificantDigits, we can stop.
366 // when output == end, we have filed the string.
367 // Note: denormalized numbers will not have the same number of
368 // significantDigits, but do not need them to round-trip.
286 } 369 }
287 // SkStrAppendFloat might still use scientific notation, so use snprintf 370 SkASSERT(output <= end);
288 // directly.. 371 *output = '\0';
289 static const int kFloat_MaxSize = 19; 372 return output - result;
290 char buffer[kFloat_MaxSize];
291 int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
292 // %f always prints trailing 0s, so strip them.
293 for (; buffer[len - 1] == '0' && len > 0; len--) {
294 buffer[len - 1] = '\0';
295 }
296 if (buffer[len - 1] == '.') {
297 buffer[len - 1] = '\0';
298 }
299 stream->writeText(buffer);
300 return;
301 #endif // SK_ALLOW_LARGE_PDF_SCALARS
302 } 373 }
303 374
304 SkString SkPDFUtils::FormatString(const char* cin, size_t len) { 375 SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
305 SkDEBUGCODE(static const size_t kMaxLen = 65535;) 376 SkDEBUGCODE(static const size_t kMaxLen = 65535;)
306 SkASSERT(len <= kMaxLen); 377 SkASSERT(len <= kMaxLen);
307 378
308 // 7-bit clean is a heuristic to decide what string format to use; 379 // 7-bit clean is a heuristic to decide what string format to use;
309 // a 7-bit clean string should require little escaping. 380 // a 7-bit clean string should require little escaping.
310 bool sevenBitClean = true; 381 bool sevenBitClean = true;
311 size_t characterCount = 2 + len; 382 size_t characterCount = 2 + len;
(...skipping 25 matching lines...) Expand all
337 for (size_t i = 0; i < len; i++) { 408 for (size_t i = 0; i < len; i++) {
338 uint8_t c = static_cast<uint8_t>(cin[i]); 409 uint8_t c = static_cast<uint8_t>(cin[i]);
339 static const char gHex[] = "0123456789ABCDEF"; 410 static const char gHex[] = "0123456789ABCDEF";
340 *str++ = gHex[(c >> 4) & 0xF]; 411 *str++ = gHex[(c >> 4) & 0xF];
341 *str++ = gHex[(c ) & 0xF]; 412 *str++ = gHex[(c ) & 0xF];
342 } 413 }
343 *str++ = '>'; 414 *str++ = '>';
344 } 415 }
345 return result; 416 return result;
346 } 417 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFUtils.h ('k') | tests/PDFPrimitivesTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698