[PDF] Restrict scalars to the range that PDF understands.
authorvandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 4 Mar 2011 03:15:13 +0000 (03:15 +0000)
committervandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 4 Mar 2011 03:15:13 +0000 (03:15 +0000)
* Add a config flag to ignore the restrictions
* Apply restriction to both SkPDFScalar and scalars used in content streams.
* +/- 32,767 for the integer part.
* +/1 1/65536 for the fraction part.

Review URL: http://codereview.appspot.com/4240050

git-svn-id: http://skia.googlecode.com/svn/trunk@882 2bbb7eff-a529-9590-31e7-b0007b416f81

include/config/SkUserConfig.h
include/pdf/SkPDFTypes.h
src/pdf/SkPDFDevice.cpp
src/pdf/SkPDFTypes.cpp
tests/PDFPrimitivesTest.cpp

index c0bbb4be4903aa9733dd223743d08d29252e7138..13169cae99799066c25d41617a7e378fe384f376 100644 (file)
  */
 //#define SK_ZLIB_INCLUDE <zlib.h>
 
+/*  Define this to allow PDF scalars above 32k.  The PDF/A spec doesn't allow
+    them, but modern PDF interpreters should handle them just fine.
+ */
+//#define SK_ALLOW_LARGE_PDF_SCALARS
+
 /*  Define this to remove dimension checks on bitmaps. Not all blits will be
     correct yet, so this is mostly for debugging the implementation.
  */
index 7d596346e69a56955a3d6082ad6cfb7bec59ee55..efa03c370eaf13bf0236d1af4dfabf77fdafbb0e 100644 (file)
@@ -151,6 +151,8 @@ public:
     explicit SkPDFScalar(SkScalar value);
     virtual ~SkPDFScalar();
 
+    static void Append(SkScalar value, SkString* string);
+
     // The SkPDFObject interface.
     virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
                             bool indirect);
index d0f32ac338f8bb692ec89e04bcf4e71ef50bb9e6..ff9e4db433a8bf921ae49cf59ea6771104b03d8a 100644 (file)
@@ -48,14 +48,14 @@ SkString toPDFColor(SkColor color) {
     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
     SkScalar colorMax = SkIntToScalar(0xFF);
     SkString result;
-    result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetR(color)),
-                                    colorMax));
+    SkPDFScalar::Append(
+            SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), &result);
     result.append(" ");
-    result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetG(color)),
-                                    colorMax));
+    SkPDFScalar::Append(
+            SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), &result);
     result.append(" ");
-    result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetB(color)),
-                                    colorMax));
+    SkPDFScalar::Append(
+            SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), &result);
     result.append(" ");
     return result;
 }
@@ -610,7 +610,7 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
                 newPaint.getTextScaleX()) {
             SkScalar scale = newPaint.getTextScaleX();
             SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
-            fContent.appendScalar(pdfScale);
+            SkPDFScalar::Append(pdfScale, &fContent);
             fContent.append(" Tz\n");
             fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
         }
@@ -639,7 +639,7 @@ void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
         fContent.append("/F");
         fContent.appendS32(fontIndex);
         fContent.append(" ");
-        fContent.appendScalar(paint.getTextSize());
+        SkPDFScalar::Append(paint.getTextSize(), &fContent);
         fContent.append(" Tf\n");
         fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
         fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
@@ -660,16 +660,16 @@ int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
 }
 
 void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
-    fContent.appendScalar(x);
+    SkPDFScalar::Append(x, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(y);
+    SkPDFScalar::Append(y, &fContent);
     fContent.append(" m\n");
 }
 
 void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
-    fContent.appendScalar(x);
+    SkPDFScalar::Append(x, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(y);
+    SkPDFScalar::Append(y, &fContent);
     fContent.append(" l\n");
 }
 
@@ -677,33 +677,33 @@ void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
                               SkScalar ctl2X, SkScalar ctl2Y,
                               SkScalar dstX, SkScalar dstY) {
     SkString cmd("y\n");
-    fContent.appendScalar(ctl1X);
+    SkPDFScalar::Append(ctl1X, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(ctl1Y);
+    SkPDFScalar::Append(ctl1Y, &fContent);
     fContent.append(" ");
     if (ctl2X != dstX || ctl2Y != dstY) {
         cmd.set("c\n");
-        fContent.appendScalar(ctl2X);
+        SkPDFScalar::Append(ctl2X, &fContent);
         fContent.append(" ");
-        fContent.appendScalar(ctl2Y);
+        SkPDFScalar::Append(ctl2Y, &fContent);
         fContent.append(" ");
     }
-    fContent.appendScalar(dstX);
+    SkPDFScalar::Append(dstX, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(dstY);
+    SkPDFScalar::Append(dstY, &fContent);
     fContent.append(" ");
     fContent.append(cmd);
 }
 
 void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
                                   SkScalar w, SkScalar h) {
-    fContent.appendScalar(x);
+    SkPDFScalar::Append(x, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(y);
+    SkPDFScalar::Append(y, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(w);
+    SkPDFScalar::Append(w, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(h);
+    SkPDFScalar::Append(h, &fContent);
     fContent.append(" re\n");
 }
 
@@ -791,11 +791,11 @@ void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
     // Flip the text about the x-axis to account for origin swap and include
     // the passed parameters.
     fContent.append("1 0 ");
-    fContent.appendScalar(0 - textSkewX);
+    SkPDFScalar::Append(0 - textSkewX, &fContent);
     fContent.append(" -1 ");
-    fContent.appendScalar(x);
+    SkPDFScalar::Append(x, &fContent);
     fContent.append(" ");
-    fContent.appendScalar(y);
+    SkPDFScalar::Append(y, &fContent);
     fContent.append(" Tm\n");
 }
 
@@ -851,7 +851,7 @@ SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
     SkScalar transform[6];
     SkAssertResult(m.pdfTransform(transform));
     for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
-        fContent.appendScalar(transform[i]);
+        SkPDFScalar::Append(transform[i], &fContent);
         fContent.append(" ");
     }
     fContent.append("cm\n");
index 209a2cf527dce65ceca97a4474226c39b158c3ae..bbeeeeb87b48a82e8a762458a932c051ea402d16 100644 (file)
 #include "SkPDFTypes.h"
 #include "SkStream.h"
 
+#ifdef SK_BUILD_FOR_WIN
+    #define SNPRINTF    _snprintf
+#else
+    #define SNPRINTF    snprintf
+#endif
+
 SkPDFObject::SkPDFObject() {}
 SkPDFObject::~SkPDFObject() {}
 
@@ -93,7 +99,64 @@ void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
                              bool indirect) {
     if (indirect)
         return emitIndirectObject(stream, catalog);
-    stream->writeScalarAsText(fValue);
+
+    SkString tmp;
+    Append(fValue, &tmp);
+    stream->write(tmp.c_str(), tmp.size());
+}
+
+// static
+void SkPDFScalar::Append(SkScalar value, SkString* string) {
+    // 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_SCALAR_IS_FIXED)
+    string->appendScalar(value);
+    return;
+#endif  // SK_SCALAR_IS_FIXED
+
+#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    if (value > 32767 || value < -32767) {
+        string->appendS32(SkScalarRound(value));
+        return;
+    }
+
+    char buffer[SkStrAppendScalar_MaxSize];
+    char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
+    string->append(buffer, end - buffer);
+    return;
+#endif  // !SK_ALLOW_LARGE_PDF_SCALARS
+
+#if defined(SK_SCALAR_IS_FLOAT) && 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)) {
+        string->appendS32(value);
+        return;
+    }
+    // Continue to enforce the PDF limits for small floats.
+    if (value < 1.0f/65536 && value > -1.0f/65536) {
+        string->appendS32(0);
+        return;
+    }
+    // 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 (buffer[len - 1] == '.') {
+        buffer[len - 1] = '\0';
+    }
+    string->append(buffer);
+    return;
+#endif  // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS
 }
 
 SkPDFString::SkPDFString(const char value[])
index 6d33a010f79285d38ee29dff510dc7e2cf50fa37..6feab51d6ab661b4a14db46677086e78455cabf8 100644 (file)
@@ -122,6 +122,24 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
     realHalf->unref();  // SkRefPtr and new both took a reference.
     CheckObjectOutput(reporter, realHalf.get(), "0.5", true);
 
+    SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
+    bigScalar->unref();  // SkRefPtr and new both took a reference.
+#if defined(SK_SCALAR_IS_FIXED) || !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    CheckObjectOutput(reporter, bigScalar.get(), "111000", true);
+#else
+    CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true);
+#endif
+
+#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
+    biggerScalar->unref();  // SkRefPtr and new both took a reference.
+    CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true);
+
+    SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
+    smallestScalar->unref();  // SkRefPtr and new both took a reference.
+    CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true);
+#endif
+
     SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
     stringSimple->unref();  // SkRefPtr and new both took a reference.
     CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)",