[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 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.
*/

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

@ -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);

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

@ -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");

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

@ -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[])

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

@ -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)",