xref: /aosp_15_r20/external/pdfium/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 
13 namespace {
14 
15 constexpr unsigned kMaximumSkFloatToDecimalLength = 49;
16 
17 // Return pow(10.0, e), optimized for common cases.
pow10(int e)18 double 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.
SkFloatToDecimal(float value,char output[kMaximumSkFloatToDecimalLength])99 unsigned 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 
WriteFloat(std::ostream & stream,float value)218 std::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 
WriteMatrix(std::ostream & stream,const CFX_Matrix & matrix)225 std::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 
WritePoint(std::ostream & stream,const CFX_PointF & point)235 std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point) {
236   WriteFloat(stream, point.x) << " ";
237   WriteFloat(stream, point.y);
238   return stream;
239 }
240 
WriteRect(std::ostream & stream,const CFX_FloatRect & rect)241 std::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 }
248