[PDF] Handle invalid glyph IDs on drawText methods.
Review URL: https://codereview.appspot.com/7179053
git-svn-id: http://skia.googlecode.com/svn/trunk@7401 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 63cc8c4..e253a5f 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -14,6 +14,7 @@
#include "SkClipStack.h"
#include "SkData.h"
#include "SkDraw.h"
+#include "SkFontHost.h"
#include "SkGlyphCache.h"
#include "SkPaint.h"
#include "SkPath.h"
@@ -28,6 +29,7 @@
#include "SkRect.h"
#include "SkString.h"
#include "SkTextFormatParams.h"
+#include "SkTemplates.h"
#include "SkTypeface.h"
#include "SkTypes.h"
@@ -102,6 +104,69 @@
*y = *y - yAdj;
}
+static size_t max_glyphid_for_typeface(const SkTypeface* typeface) {
+ SkAdvancedTypefaceMetrics* metrics;
+ metrics = SkFontHost::GetAdvancedTypefaceMetrics(
+ SkTypeface::UniqueID(typeface),
+ SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo,
+ NULL, 0);
+
+ int lastGlyphID = 0;
+ if (metrics) {
+ lastGlyphID = metrics->fLastGlyphID;
+ metrics->unref();
+ }
+ return lastGlyphID;
+}
+
+typedef SkAutoSTMalloc<128, uint16_t> SkGlyphStorage;
+
+static size_t force_glyph_encoding(const SkPaint& paint, const void* text,
+ size_t len, SkGlyphStorage* storage,
+ uint16_t** glyphIDs) {
+ // Make sure we have a glyph id encoding.
+ if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+ size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
+ storage->reset(numGlyphs);
+ paint.textToGlyphs(text, len, storage->get());
+ *glyphIDs = storage->get();
+ return numGlyphs;
+ }
+
+ // For user supplied glyph ids we need to validate them.
+ SkASSERT((len & 1) == 0);
+ size_t numGlyphs = len / 2;
+ const uint16_t* input =
+ reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
+
+ int maxGlyphID = max_glyphid_for_typeface(paint.getTypeface());
+ size_t validated;
+ for (validated = 0; validated < numGlyphs; ++validated) {
+ if (input[validated] > maxGlyphID) {
+ break;
+ }
+ }
+ if (validated >= numGlyphs) {
+ *glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
+ return numGlyphs;
+ }
+
+ // Silently drop anything out of range.
+ storage->reset(numGlyphs);
+ if (validated > 0) {
+ memcpy(storage->get(), input, validated * sizeof(uint16_t));
+ }
+
+ for (size_t i = validated; i < numGlyphs; ++i) {
+ storage->get()[i] = input[i];
+ if (input[i] > maxGlyphID) {
+ storage->get()[i] = 0;
+ }
+ }
+ *glyphIDs = storage->get();
+ return numGlyphs;
+}
+
static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
SkWStream* content) {
// Flip the text about the x-axis to account for origin swap and include
@@ -816,20 +881,11 @@
return;
}
- // We want the text in glyph id encoding and a writable buffer, so we end
- // up making a copy either way.
- size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
- uint16_t* glyphIDs = reinterpret_cast<uint16_t*>(
- sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
- SkAutoFree autoFreeGlyphIDs(glyphIDs);
- if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
- paint.textToGlyphs(text, len, glyphIDs);
- textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- } else {
- SkASSERT((len & 1) == 0);
- SkASSERT(len / 2 == numGlyphs);
- memcpy(glyphIDs, text, len);
- }
+ SkGlyphStorage storage(0);
+ uint16_t* glyphIDs = NULL;
+ size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
+ &glyphIDs);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y);
@@ -865,22 +921,11 @@
return;
}
- // Make sure we have a glyph id encoding.
- SkAutoFree glyphStorage;
- uint16_t* glyphIDs;
- size_t numGlyphs;
- if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
- numGlyphs = paint.textToGlyphs(text, len, NULL);
- glyphIDs = reinterpret_cast<uint16_t*>(sk_malloc_flags(
- numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
- glyphStorage.set(glyphIDs);
- paint.textToGlyphs(text, len, glyphIDs);
- textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- } else {
- SkASSERT((len & 1) == 0);
- numGlyphs = len / 2;
- glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
- }
+ SkGlyphStorage storage(0);
+ uint16_t* glyphIDs = NULL;
+ size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
+ &glyphIDs);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
content.entry()->fContent.writeText("BT\n");
@@ -1615,4 +1660,3 @@
bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
return false;
}
-
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 42fc133..6e7d616 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -8,9 +8,11 @@
#include "Test.h"
+#include "SkCanvas.h"
#include "SkData.h"
#include "SkFlate.h"
#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
#include "SkPDFStream.h"
#include "SkPDFTypes.h"
#include "SkScalar.h"
@@ -217,6 +219,28 @@
buffer.getOffset()));
}
+// This test used to assert without the fix submitted for
+// http://code.google.com/p/skia/issues/detail?id=1083.
+// SKP files might have invalid glyph ids. This test ensures they are ignored,
+// and there is no assert on input data in Debug mode.
+static void test_issue1083(skiatest::Reporter* reporter) {
+ SkISize pageSize = SkISize::Make(100, 100);
+ SkPDFDevice* dev = new SkPDFDevice(pageSize, pageSize, SkMatrix::I());
+
+ SkCanvas c(dev);
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ uint16_t glyphID = 65000;
+ c.drawText(&glyphID, 2, 0, 0, paint);
+
+ SkPDFDocument doc;
+ doc.appendPage(dev);
+
+ SkDynamicMemoryWStream stream;
+ doc.emitPDF(&stream);
+}
+
static void TestPDFPrimitives(skiatest::Reporter* reporter) {
SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
SimpleCheckObjectOutput(reporter, int42.get(), "42");
@@ -298,6 +322,8 @@
TestObjectRef(reporter);
TestSubstitute(reporter);
+
+ test_issue1083(reporter);
}
#include "TestClassDef.h"