[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
This commit is contained in:
vandebo@chromium.org 2013-01-25 19:27:23 +00:00
parent 6d5d08f14f
commit 4e1cc6ac45
2 changed files with 101 additions and 31 deletions

View File

@ -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 @@ static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
*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 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
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 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
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::onReadPixels(const SkBitmap& bitmap, int x, int y,
bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
return false;
}

View File

@ -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 @@ static void TestSubstitute(skiatest::Reporter* reporter) {
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 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
TestObjectRef(reporter);
TestSubstitute(reporter);
test_issue1083(reporter);
}
#include "TestClassDef.h"