From 094316bd284372c2a3d8ef02eec589901e503c59 Mon Sep 17 00:00:00 2001 From: "vandebo@chromium.org" Date: Fri, 4 Mar 2011 03:15:13 +0000 Subject: [PATCH] [PDF] Restrict scalars to the range that PDF understands. * 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 | 5 +++ include/pdf/SkPDFTypes.h | 2 ++ src/pdf/SkPDFDevice.cpp | 52 ++++++++++++++-------------- src/pdf/SkPDFTypes.cpp | 65 ++++++++++++++++++++++++++++++++++- tests/PDFPrimitivesTest.cpp | 18 ++++++++++ 5 files changed, 115 insertions(+), 27 deletions(-) diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h index c0bbb4be4..13169cae9 100644 --- a/include/config/SkUserConfig.h +++ b/include/config/SkUserConfig.h @@ -123,6 +123,11 @@ */ //#define SK_ZLIB_INCLUDE +/* 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. */ diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h index 7d596346e..efa03c370 100644 --- a/include/pdf/SkPDFTypes.h +++ b/include/pdf/SkPDFTypes.h @@ -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); diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index d0f32ac33..ff9e4db43 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -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"); diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp index 209a2cf52..bbeeeeb87 100644 --- a/src/pdf/SkPDFTypes.cpp +++ b/src/pdf/SkPDFTypes.cpp @@ -18,6 +18,12 @@ #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[]) diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index 6d33a010f..6feab51d6 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -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 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 biggerScalar = new SkPDFScalar(50000000.1); + biggerScalar->unref(); // SkRefPtr and new both took a reference. + CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true); + + SkRefPtr smallestScalar = new SkPDFScalar(1.0/65536); + smallestScalar->unref(); // SkRefPtr and new both took a reference. + CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true); +#endif + SkRefPtr stringSimple = new SkPDFString("test ) string ( foo"); stringSimple->unref(); // SkRefPtr and new both took a reference. CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)",