[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
This commit is contained in:
vandebo@chromium.org 2011-03-04 03:15:13 +00:00
Родитель e280f1d164
Коммит 094316bd28
5 изменённых файлов: 115 добавлений и 27 удалений

Просмотреть файл

@ -123,6 +123,11 @@
*/ */
//#define SK_ZLIB_INCLUDE <zlib.h> //#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 /* Define this to remove dimension checks on bitmaps. Not all blits will be
correct yet, so this is mostly for debugging the implementation. correct yet, so this is mostly for debugging the implementation.
*/ */

Просмотреть файл

@ -151,6 +151,8 @@ public:
explicit SkPDFScalar(SkScalar value); explicit SkPDFScalar(SkScalar value);
virtual ~SkPDFScalar(); virtual ~SkPDFScalar();
static void Append(SkScalar value, SkString* string);
// The SkPDFObject interface. // The SkPDFObject interface.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect); bool indirect);

Просмотреть файл

@ -48,14 +48,14 @@ SkString toPDFColor(SkColor color) {
SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere. SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
SkScalar colorMax = SkIntToScalar(0xFF); SkScalar colorMax = SkIntToScalar(0xFF);
SkString result; SkString result;
result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetR(color)), SkPDFScalar::Append(
colorMax)); SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), &result);
result.append(" "); result.append(" ");
result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetG(color)), SkPDFScalar::Append(
colorMax)); SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), &result);
result.append(" "); result.append(" ");
result.appendScalar(SkScalarDiv(SkIntToScalar(SkColorGetB(color)), SkPDFScalar::Append(
colorMax)); SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), &result);
result.append(" "); result.append(" ");
return result; return result;
} }
@ -610,7 +610,7 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
newPaint.getTextScaleX()) { newPaint.getTextScaleX()) {
SkScalar scale = newPaint.getTextScaleX(); SkScalar scale = newPaint.getTextScaleX();
SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100)); SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
fContent.appendScalar(pdfScale); SkPDFScalar::Append(pdfScale, &fContent);
fContent.append(" Tz\n"); fContent.append(" Tz\n");
fGraphicStack[fGraphicStackIndex].fTextScaleX = scale; fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
} }
@ -639,7 +639,7 @@ void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
fContent.append("/F"); fContent.append("/F");
fContent.appendS32(fontIndex); fContent.appendS32(fontIndex);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(paint.getTextSize()); SkPDFScalar::Append(paint.getTextSize(), &fContent);
fContent.append(" Tf\n"); fContent.append(" Tf\n");
fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize(); fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex]; 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) { void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
fContent.appendScalar(x); SkPDFScalar::Append(x, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(y); SkPDFScalar::Append(y, &fContent);
fContent.append(" m\n"); fContent.append(" m\n");
} }
void SkPDFDevice::appendLine(SkScalar x, SkScalar y) { void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
fContent.appendScalar(x); SkPDFScalar::Append(x, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(y); SkPDFScalar::Append(y, &fContent);
fContent.append(" l\n"); fContent.append(" l\n");
} }
@ -677,33 +677,33 @@ void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
SkScalar ctl2X, SkScalar ctl2Y, SkScalar ctl2X, SkScalar ctl2Y,
SkScalar dstX, SkScalar dstY) { SkScalar dstX, SkScalar dstY) {
SkString cmd("y\n"); SkString cmd("y\n");
fContent.appendScalar(ctl1X); SkPDFScalar::Append(ctl1X, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(ctl1Y); SkPDFScalar::Append(ctl1Y, &fContent);
fContent.append(" "); fContent.append(" ");
if (ctl2X != dstX || ctl2Y != dstY) { if (ctl2X != dstX || ctl2Y != dstY) {
cmd.set("c\n"); cmd.set("c\n");
fContent.appendScalar(ctl2X); SkPDFScalar::Append(ctl2X, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(ctl2Y); SkPDFScalar::Append(ctl2Y, &fContent);
fContent.append(" "); fContent.append(" ");
} }
fContent.appendScalar(dstX); SkPDFScalar::Append(dstX, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(dstY); SkPDFScalar::Append(dstY, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.append(cmd); fContent.append(cmd);
} }
void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y, void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
SkScalar w, SkScalar h) { SkScalar w, SkScalar h) {
fContent.appendScalar(x); SkPDFScalar::Append(x, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(y); SkPDFScalar::Append(y, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(w); SkPDFScalar::Append(w, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(h); SkPDFScalar::Append(h, &fContent);
fContent.append(" re\n"); 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 // Flip the text about the x-axis to account for origin swap and include
// the passed parameters. // the passed parameters.
fContent.append("1 0 "); fContent.append("1 0 ");
fContent.appendScalar(0 - textSkewX); SkPDFScalar::Append(0 - textSkewX, &fContent);
fContent.append(" -1 "); fContent.append(" -1 ");
fContent.appendScalar(x); SkPDFScalar::Append(x, &fContent);
fContent.append(" "); fContent.append(" ");
fContent.appendScalar(y); SkPDFScalar::Append(y, &fContent);
fContent.append(" Tm\n"); fContent.append(" Tm\n");
} }
@ -851,7 +851,7 @@ SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
SkScalar transform[6]; SkScalar transform[6];
SkAssertResult(m.pdfTransform(transform)); SkAssertResult(m.pdfTransform(transform));
for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) { for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
fContent.appendScalar(transform[i]); SkPDFScalar::Append(transform[i], &fContent);
fContent.append(" "); fContent.append(" ");
} }
fContent.append("cm\n"); fContent.append("cm\n");

Просмотреть файл

@ -18,6 +18,12 @@
#include "SkPDFTypes.h" #include "SkPDFTypes.h"
#include "SkStream.h" #include "SkStream.h"
#ifdef SK_BUILD_FOR_WIN
#define SNPRINTF _snprintf
#else
#define SNPRINTF snprintf
#endif
SkPDFObject::SkPDFObject() {} SkPDFObject::SkPDFObject() {}
SkPDFObject::~SkPDFObject() {} SkPDFObject::~SkPDFObject() {}
@ -93,7 +99,64 @@ void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect) { bool indirect) {
if (indirect) if (indirect)
return emitIndirectObject(stream, catalog); 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[]) SkPDFString::SkPDFString(const char value[])

Просмотреть файл

@ -122,6 +122,24 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
realHalf->unref(); // SkRefPtr and new both took a reference. realHalf->unref(); // SkRefPtr and new both took a reference.
CheckObjectOutput(reporter, realHalf.get(), "0.5", true); 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"); SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
stringSimple->unref(); // SkRefPtr and new both took a reference. stringSimple->unref(); // SkRefPtr and new both took a reference.
CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)", CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)",