Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_contentstream_write_utils.cpp
Go to the documentation of this file.
1// Copyright 2019 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
6
7#include <cassert>
8#include <cfloat>
9#include <climits>
10#include <cmath>
11#include <ostream>
12
13namespace {
14
15constexpr unsigned kMaximumSkFloatToDecimalLength = 49;
16
17// Return pow(10.0, e), optimized for common cases.
18double pow10(int e) {
19 switch (e) {
20 case 0:
21 return 1.0; // common cases
22 case 1:
23 return 10.0;
24 case 2:
25 return 100.0;
26 case 3:
27 return 1e+03;
28 case 4:
29 return 1e+04;
30 case 5:
31 return 1e+05;
32 case 6:
33 return 1e+06;
34 case 7:
35 return 1e+07;
36 case 8:
37 return 1e+08;
38 case 9:
39 return 1e+09;
40 case 10:
41 return 1e+10;
42 case 11:
43 return 1e+11;
44 case 12:
45 return 1e+12;
46 case 13:
47 return 1e+13;
48 case 14:
49 return 1e+14;
50 case 15:
51 return 1e+15;
52 default:
53 if (e > 15) {
54 double value = 1e+15;
55 while (e-- > 15) {
56 value *= 10.0;
57 }
58 return value;
59 } else {
60 assert(e < 0);
61 double value = 1.0;
62 while (e++ < 0) {
63 value /= 10.0;
64 }
65 return value;
66 }
67 }
68}
69
70// SkFloatToDecimal
71//
72// Convert a float into a decimal string.
73//
74// The resulting string will be in the form `[-]?([0-9]*\.)?[0-9]+` (It does
75// not use scientific notation.) and `sscanf(output, "%f", &x)` will return
76// the original value if the value is finite. This function accepts all
77// possible input values.
78//
79// INFINITY and -INFINITY are rounded to FLT_MAX and -FLT_MAX.
80//
81// NAN values are converted to 0.
82//
83// This function will always add a terminating '\0' to the output.
84//
85// @param value Any floating-point number
86// @param output The buffer to write the string into. Must be non-null.
87//
88// @return strlen(output)
89//
90// Write a string into output, including a terminating '\0' (for
91// unit testing). Return strlen(output) (for SkWStream::write) The
92// resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
93// sscanf(output, "%f", &x) will return the original value iff the
94// value is finite. This function accepts all possible input values.
95//
96// Motivation: "PDF does not support [numbers] in exponential format
97// (such as 6.02e23)." Otherwise, this function would rely on a
98// sprintf-type function from the standard library.
99unsigned SkFloatToDecimal(float value,
100 char output[kMaximumSkFloatToDecimalLength]) {
101 // The longest result is -FLT_MIN.
102 // We serialize it as "-.0000000000000000000000000000000000000117549435"
103 // which has 48 characters plus a terminating '\0'.
104
105 static_assert(kMaximumSkFloatToDecimalLength == 49, "");
106 // 3 = '-', '.', and '\0' characters.
107 // 9 = number of significant digits
108 // abs(FLT_MIN_10_EXP) = number of zeros in FLT_MIN
109 static_assert(kMaximumSkFloatToDecimalLength == 3 + 9 - FLT_MIN_10_EXP, "");
110
111 // section C.1 of the PDF 1.4 spec (http://goo.gl/0SCswJ) says that
112 // most PDF rasterizers will use fixed-point scalars that lack the
113 // dynamic range of floats. Even if this is the case, I want to
114 // serialize these (uncommon) very small and very large scalar
115 // values with enough precision to allow a floating-point
116 // rasterizer to read them in with perfect accuracy.
117 // Experimentally, rasterizers such as pdfium do seem to benefit
118 // from this. Rasterizers that rely on fixed-point scalars should
119 // gracefully ignore these values that they can not parse.
120 char* output_ptr = &output[0];
121 const char* const end = &output[kMaximumSkFloatToDecimalLength - 1];
122 // subtract one to leave space for '\0'.
123
124 // This function is written to accept any possible input value,
125 // including non-finite values such as INF and NAN. In that case,
126 // we ignore value-correctness and output a syntacticly-valid
127 // number.
128 if (value == INFINITY) {
129 value = FLT_MAX; // nearest finite float.
130 }
131 if (value == -INFINITY) {
132 value = -FLT_MAX; // nearest finite float.
133 }
134 if (!std::isfinite(value) || value == 0.0f) {
135 // NAN is unsupported in PDF. Always output a valid number.
136 // Also catch zero here, as a special case.
137 *output_ptr++ = '0';
138 *output_ptr = '\0';
139 return static_cast<unsigned>(output_ptr - output);
140 }
141 if (value < 0.0) {
142 *output_ptr++ = '-';
143 value = -value;
144 }
145 assert(value >= 0.0f);
146
147 int binaryExponent;
148 (void)std::frexp(value, &binaryExponent);
149 static const double kLog2 = 0.3010299956639812; // log10(2.0);
150 int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
151 int decimalShift = decimalExponent - 8;
152 double power = pow10(-decimalShift);
153 assert(value * power <= (double)INT_MAX);
154 int d = static_cast<int>(value * power + 0.5);
155 // assert(value == (float)(d * pow(10.0, decimalShift)));
156 assert(d <= 999999999);
157 if (d > 167772159) { // floor(pow(10,1+log10(1<<24)))
158 // need one fewer decimal digits for 24-bit precision.
159 decimalShift = decimalExponent - 7;
160 // assert(power * 0.1 = pow10(-decimalShift));
161 // recalculate to get rounding right.
162 d = static_cast<int>(value * (power * 0.1) + 0.5);
163 assert(d <= 99999999);
164 }
165 while (d % 10 == 0) {
166 d /= 10;
167 ++decimalShift;
168 }
169 assert(d > 0);
170 // assert(value == (float)(d * pow(10.0, decimalShift)));
171 unsigned char buffer[9]; // decimal value buffer.
172 int bufferIndex = 0;
173 do {
174 buffer[bufferIndex++] = d % 10;
175 d /= 10;
176 } while (d != 0);
177 assert(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
178 if (decimalShift >= 0) {
179 do {
180 --bufferIndex;
181 *output_ptr++ = '0' + buffer[bufferIndex];
182 } while (bufferIndex);
183 for (int i = 0; i < decimalShift; ++i) {
184 *output_ptr++ = '0';
185 }
186 } else {
187 int placesBeforeDecimal = bufferIndex + decimalShift;
188 if (placesBeforeDecimal > 0) {
189 while (placesBeforeDecimal-- > 0) {
190 --bufferIndex;
191 *output_ptr++ = '0' + buffer[bufferIndex];
192 }
193 *output_ptr++ = '.';
194 } else {
195 *output_ptr++ = '.';
196 int placesAfterDecimal = -placesBeforeDecimal;
197 while (placesAfterDecimal-- > 0) {
198 *output_ptr++ = '0';
199 }
200 }
201 while (bufferIndex > 0) {
202 --bufferIndex;
203 *output_ptr++ = '0' + buffer[bufferIndex];
204 if (output_ptr == end) {
205 break; // denormalized: don't need extra precision.
206 // Note: denormalized numbers will not have the same number of
207 // significantDigits, but do not need them to round-trip.
208 }
209 }
210 }
211 assert(output_ptr <= end);
212 *output_ptr = '\0';
213 return static_cast<unsigned>(output_ptr - output);
214}
215
216} // namespace
217
218std::ostream& WriteFloat(std::ostream& stream, float value) {
219 char buffer[kMaximumSkFloatToDecimalLength];
220 unsigned size = SkFloatToDecimal(value, buffer);
221 stream.write(buffer, size);
222 return stream;
223}
224
225std::ostream& WriteMatrix(std::ostream& stream, const CFX_Matrix& matrix) {
226 WriteFloat(stream, matrix.a) << " ";
227 WriteFloat(stream, matrix.b) << " ";
228 WriteFloat(stream, matrix.c) << " ";
229 WriteFloat(stream, matrix.d) << " ";
230 WriteFloat(stream, matrix.e) << " ";
231 WriteFloat(stream, matrix.f);
232 return stream;
233}
234
235std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point) {
236 WriteFloat(stream, point.x) << " ";
237 WriteFloat(stream, point.y);
238 return stream;
239}
240
241std::ostream& WriteRect(std::ostream& stream, const CFX_FloatRect& rect) {
242 WriteFloat(stream, rect.left) << " ";
243 WriteFloat(stream, rect.bottom) << " ";
244 WriteFloat(stream, rect.Width()) << " ";
245 WriteFloat(stream, rect.Height());
246 return stream;
247}
float Width() const
float Height() const
std::ostream & WritePoint(std::ostream &stream, const CFX_PointF &point)
std::ostream & WriteFloat(std::ostream &stream, float value)
std::ostream & WriteRect(std::ostream &stream, const CFX_FloatRect &rect)
std::ostream & WriteMatrix(std::ostream &stream, const CFX_Matrix &matrix)
#define assert