| // Copyright 2019 The PDFium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" | |
| #include <array> | |
| #include <cmath> | |
| #include <limits> | |
| #include <ostream> | |
| #include "core/fxcrt/span.h" | |
| #include "third_party/dragonbox/src/include/dragonbox/dragonbox.h" | |
| namespace { | |
| constexpr unsigned kMaximumFloatToDecimalLength = 49; | |
| // Convert a float into a decimal string. | |
| // | |
| // The resulting string will be in the form `[-]?([0-9]*\.)?[0-9]+` (it does | |
| // not use scientific notation). | |
| // | |
| // INFINITY and -INFINITY are rounded to FLT_MAX and -FLT_MAX. | |
| // | |
| // NAN values are converted to 0. | |
| // | |
| // This function will always add a terminating '\0' to the output. | |
| unsigned FloatToDecimal( | |
| float value, | |
| pdfium::span<char, kMaximumFloatToDecimalLength> output) { | |
| // The longest result is -FLT_MIN. | |
| // Serialize it as "-.0000000000000000000000000000000000000117549435" | |
| // which has 48 characters plus a terminating '\0'. | |
| static_assert(kMaximumFloatToDecimalLength == 49, ""); | |
| static_assert(kMaximumFloatToDecimalLength == | |
| 3 + 9 - std::numeric_limits<float>::min_exponent10, | |
| ""); | |
| size_t out_idx = 0; | |
| if (std::isnan(value) || value == 0.0f) { | |
| // NAN is unsupported in PDF. Always output a valid number. | |
| // Also catch zero here, as a special case. | |
| output[out_idx++] = '0'; | |
| output[out_idx] = '\0'; | |
| return static_cast<unsigned>(out_idx); | |
| } | |
| if (value == INFINITY) { | |
| value = std::numeric_limits<float>::max(); | |
| } else if (value == -INFINITY) { | |
| value = std::numeric_limits<float>::lowest(); | |
| } | |
| if (value < 0.0f) { | |
| output[out_idx++] = '-'; | |
| value = -value; | |
| } | |
| const auto decimal = | |
| jkj::dragonbox::to_decimal(value, jkj::dragonbox::policy::sign::ignore, | |
| jkj::dragonbox::policy::trailing_zero::remove); | |
| std::array<char, 10> digits; | |
| size_t num_digits = 0; | |
| uint32_t significand = decimal.significand; | |
| do { | |
| digits[num_digits++] = static_cast<char>('0' + (significand % 10)); | |
| significand /= 10; | |
| } while (significand > 0); | |
| const int exponent = decimal.exponent; | |
| if (exponent >= 0) { | |
| while (num_digits > 0) { | |
| output[out_idx++] = digits[--num_digits]; | |
| } | |
| for (int i = 0; i < exponent; ++i) { | |
| output[out_idx++] = '0'; | |
| } | |
| } else { | |
| const int places_before_decimal = static_cast<int>(num_digits) + exponent; | |
| if (places_before_decimal > 0) { | |
| for (int i = 0; i < places_before_decimal; ++i) { | |
| output[out_idx++] = digits[--num_digits]; | |
| } | |
| output[out_idx++] = '.'; | |
| while (num_digits > 0) { | |
| output[out_idx++] = digits[--num_digits]; | |
| } | |
| } else { | |
| output[out_idx++] = '.'; | |
| for (int i = 0; i < -places_before_decimal; ++i) { | |
| output[out_idx++] = '0'; | |
| } | |
| while (num_digits > 0) { | |
| output[out_idx++] = digits[--num_digits]; | |
| if (out_idx + 1 >= output.size()) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| output[out_idx] = '\0'; | |
| return static_cast<unsigned>(out_idx); | |
| } | |
| } // namespace | |
| std::ostream& WriteFloat(std::ostream& stream, float value) { | |
| char buffer[kMaximumFloatToDecimalLength]; | |
| unsigned size = FloatToDecimal(value, buffer); | |
| stream.write(buffer, size); | |
| return stream; | |
| } | |
| std::ostream& WriteMatrix(std::ostream& stream, const CFX_Matrix& matrix) { | |
| WriteFloat(stream, matrix.a) << " "; | |
| WriteFloat(stream, matrix.b) << " "; | |
| WriteFloat(stream, matrix.c) << " "; | |
| WriteFloat(stream, matrix.d) << " "; | |
| WriteFloat(stream, matrix.e) << " "; | |
| WriteFloat(stream, matrix.f); | |
| return stream; | |
| } | |
| std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point) { | |
| WriteFloat(stream, point.x) << " "; | |
| WriteFloat(stream, point.y); | |
| return stream; | |
| } | |
| std::ostream& WriteRect(std::ostream& stream, const CFX_FloatRect& rect) { | |
| WriteFloat(stream, rect.left) << " "; | |
| WriteFloat(stream, rect.bottom) << " "; | |
| WriteFloat(stream, rect.Width()) << " "; | |
| WriteFloat(stream, rect.Height()); | |
| return stream; | |
| } |