| Index: src/pdf/SkPDFUtils.cpp
|
| diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
|
| index 108f2c10abd56e6d0ae94b263c89c5c9a5d34dd4..30d6ee7d68899b7f3415352abb668d1966c432fb 100644
|
| --- a/src/pdf/SkPDFUtils.cpp
|
| +++ b/src/pdf/SkPDFUtils.cpp
|
| @@ -16,6 +16,8 @@
|
| #include "SkString.h"
|
| #include "SkPDFTypes.h"
|
|
|
| +#include <cmath>
|
| +
|
| //static
|
| SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
|
| SkPDFArray* result = new SkPDFArray();
|
| @@ -254,51 +256,120 @@ void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
|
| }
|
|
|
| void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
|
| - // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
|
| - // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
|
| - // When using floats that are outside the whole value range, we can use
|
| - // integers instead.
|
| -
|
| -#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
|
| - if (value > 32767 || value < -32767) {
|
| - stream->writeDecAsText(SkScalarRoundToInt(value));
|
| - return;
|
| - }
|
| + char result[kMaximumFloatDecimalLength];
|
| + size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result);
|
| + SkASSERT(len < kMaximumFloatDecimalLength);
|
| + stream->write(result, len);
|
| +}
|
| +
|
| +/** Write a string into result, includeing a terminating '\0' (for
|
| + unit testing). Return strlen(result) (for SkWStream::write) The
|
| + resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
|
| + sscanf(result, "%f", &x) will return the original value iff the
|
| + value is finite. This function accepts all possible input values.
|
| +
|
| + Motivation: "PDF does not support [numbers] in exponential format
|
| + (such as 6.02e23)." Otherwise, this function would rely on a
|
| + sprintf-type function from the standard library. */
|
| +size_t SkPDFUtils::FloatToDecimal(float value,
|
| + char result[kMaximumFloatDecimalLength]) {
|
| + /* The longest result is -FLT_MIN.
|
| + We serialize it as "-.0000000000000000000000000000000000000117549435"
|
| + which has 48 characters plus a terminating '\0'. */
|
| +
|
| + /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
|
| + most PDF rasterizers will use fixed-point scalars that lack the
|
| + dynamic range of floats. Even if this is the case, I want to
|
| + serialize these (uncommon) very small and very large scalar
|
| + values with enough precision to allow a floating-point
|
| + rasterizer to read them in with perfect accuracy.
|
| + Experimentally, rasterizers such as pdfium do seem to benefit
|
| + from this. Rasterizers that rely on fixed-point scalars should
|
| + gracefully ignore these values that they can not parse. */
|
| + char* output = &result[0];
|
| + const char* const end = &result[kMaximumFloatDecimalLength - 1];
|
| + // subtract one to leave space for '\0'.
|
|
|
| - char buffer[SkStrAppendScalar_MaxSize];
|
| - char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
|
| - stream->write(buffer, end - buffer);
|
| - return;
|
| -#endif // !SK_ALLOW_LARGE_PDF_SCALARS
|
| -
|
| -#if defined(SK_ALLOW_LARGE_PDF_SCALARS)
|
| - // Floats have 24bits of significance, so anything outside that range is
|
| - // no more precise than an int. (Plus PDF doesn't support scientific
|
| - // notation, so this clamps to SK_Max/MinS32).
|
| - if (value > (1 << 24) || value < -(1 << 24)) {
|
| - stream->writeDecAsText(value);
|
| - return;
|
| + /* This function is written to accept any possible input value,
|
| + including non-finite values such as INF and NAN. In that case,
|
| + we ignore value-correctness and and output a syntacticly-valid
|
| + number. */
|
| + if (value == SK_FloatInfinity) {
|
| + value = FLT_MAX; // nearest finite float.
|
| }
|
| - // Continue to enforce the PDF limits for small floats.
|
| - if (value < 1.0f/65536 && value > -1.0f/65536) {
|
| - stream->writeDecAsText(0);
|
| - return;
|
| + if (value == SK_FloatNegativeInfinity) {
|
| + value = -FLT_MAX; // nearest finite float.
|
| }
|
| - // SkStrAppendFloat might still use scientific notation, so use snprintf
|
| - // directly..
|
| - static const int kFloat_MaxSize = 19;
|
| - char buffer[kFloat_MaxSize];
|
| - int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
|
| - // %f always prints trailing 0s, so strip them.
|
| - for (; buffer[len - 1] == '0' && len > 0; len--) {
|
| - buffer[len - 1] = '\0';
|
| + if (!std::isfinite(value) || value == 0.0f) {
|
| + // NAN is unsupported in PDF. Always output a valid number.
|
| + // Also catch zero here, as a special case.
|
| + *output++ = '0';
|
| + *output = '\0';
|
| + return output - result;
|
| }
|
| - if (buffer[len - 1] == '.') {
|
| - buffer[len - 1] = '\0';
|
| + // Inspired by:
|
| + // http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/
|
| +
|
| + if (value < 0.0) {
|
| + *output++ = '-';
|
| + value = -value;
|
| + }
|
| + SkASSERT(value >= 0.0f);
|
| +
|
| + // Must use double math to keep precision right.
|
| + double intPart;
|
| + double fracPart = std::modf(static_cast<double>(value), &intPart);
|
| + SkASSERT(intPart + fracPart == static_cast<double>(value));
|
| + size_t significantDigits = 0;
|
| + const size_t maxSignificantDigits = 9;
|
| + // Any fewer significant digits loses precision. The unit test
|
| + // checks round-trip correctness.
|
| + SkASSERT(intPart >= 0.0 && fracPart >= 0.0); // negative handled already.
|
| + SkASSERT(intPart > 0.0 || fracPart > 0.0); // zero already caught.
|
| + if (intPart > 0.0) {
|
| + // put the intPart digits onto a stack for later reversal.
|
| + char reversed[1 + FLT_MAX_10_EXP]; // 39 == 1 + FLT_MAX_10_EXP
|
| + // the largest integer part is FLT_MAX; it has 39 decimal digits.
|
| + size_t reversedIndex = 0;
|
| + do {
|
| + SkASSERT(reversedIndex < sizeof(reversed));
|
| + int digit = static_cast<int>(std::fmod(intPart, 10.0));
|
| + SkASSERT(digit >= 0 && digit <= 9);
|
| + reversed[reversedIndex++] = '0' + digit;
|
| + intPart = std::floor(intPart / 10.0);
|
| + } while (intPart > 0.0);
|
| + significantDigits = reversedIndex;
|
| + SkASSERT(reversedIndex <= sizeof(reversed));
|
| + SkASSERT(output + reversedIndex <= end);
|
| + while (reversedIndex-- > 0) { // pop from stack, append to result
|
| + *output++ = reversed[reversedIndex];
|
| + }
|
| + }
|
| + if (fracPart > 0 && significantDigits < maxSignificantDigits) {
|
| + *output++ = '.';
|
| + SkASSERT(output <= end);
|
| + do {
|
| + fracPart = std::modf(fracPart * 10.0, &intPart);
|
| + int digit = static_cast<int>(intPart);
|
| + SkASSERT(digit >= 0 && digit <= 9);
|
| + *output++ = '0' + digit;
|
| + SkASSERT(output <= end);
|
| + if (digit > 0 || significantDigits > 0) {
|
| + // start counting significantDigits after first non-zero digit.
|
| + ++significantDigits;
|
| + }
|
| + } while (fracPart > 0.0
|
| + && significantDigits < maxSignificantDigits
|
| + && output < end);
|
| + // When fracPart == 0, additional digits will be zero.
|
| + // When significantDigits == maxSignificantDigits, we can stop.
|
| + // when output == end, we have filed the string.
|
| + // Note: denormalized numbers will not have the same number of
|
| + // significantDigits, but do not need them to round-trip.
|
| }
|
| - stream->writeText(buffer);
|
| - return;
|
| -#endif // SK_ALLOW_LARGE_PDF_SCALARS
|
| + SkASSERT(output <= end);
|
| + *output = '\0';
|
| + return output - result;
|
| }
|
|
|
| SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
|
|
|