From 5df5e6126d43f65d47092d682e7a7dd64a77ace3 Mon Sep 17 00:00:00 2001 From: "djsollen@google.com" Date: Thu, 3 Oct 2013 14:42:24 +0000 Subject: Update PDF backend to support fallback fonts on Android. R=edisonn@google.com, reed@google.com, vandebo@chromium.org Review URL: https://codereview.chromium.org/24364008 git-svn-id: http://skia.googlecode.com/svn/trunk@11586 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/pdf/SkPDFDevice.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) (limited to 'src/pdf/SkPDFDevice.cpp') diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index d13d30cbfb..e4b07f04fa 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -33,6 +33,22 @@ #include "SkTypefacePriv.h" #include "SkTSet.h" +#ifdef SK_BUILD_FOR_ANDROID +#include "SkTypeface_android.h" + +struct TypefaceFallbackData { + SkTypeface* typeface; + int lowerBounds; + int upperBounds; + + bool operator==(const TypefaceFallbackData& b) const { + return typeface == b.typeface && + lowerBounds == b.lowerBounds && + upperBounds == b.upperBounds; + } +}; +#endif + // Utility functions static void emit_pdf_color(SkColor color, SkWStream* result) { @@ -1115,6 +1131,112 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, return; } +#ifdef SK_BUILD_FOR_ANDROID + /* + * In the case that we have enabled fallback fonts on Android we need to + * take the following steps to ensure that the PDF draws all characters, + * regardless of their underlying font file, correctly. + * + * 1. Convert input into GlyphID encoding if it currently is not + * 2. Iterate over the glyphIDs and identify the actual typeface that each + * glyph resolves to + * 3. Iterate over those typefaces and recursively call this function with + * only the glyphs (and their positions) that the typeface is capable of + * resolving. + */ + if (paint.getPaintOptionsAndroid().isUsingFontFallbacks()) { + uint16_t* glyphIDs = NULL; + SkGlyphStorage tmpStorage(0); + size_t numGlyphs = 0; + + // convert to glyphIDs + if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { + numGlyphs = len / 2; + glyphIDs = reinterpret_cast(const_cast(text)); + } else { + numGlyphs = paint.textToGlyphs(text, len, NULL); + tmpStorage.reset(numGlyphs); + paint.textToGlyphs(text, len, tmpStorage.get()); + glyphIDs = tmpStorage.get(); + } + + // if no typeface is provided in the paint get the default + SkAutoTUnref origFace(SkSafeRef(paint.getTypeface())); + if (NULL == origFace.get()) { + origFace.reset(SkTypeface::RefDefault()); + } + const uint16_t origGlyphCount = origFace->countGlyphs(); + + // keep a list of the already visited typefaces and some data about them + SkTDArray visitedTypefaces; + + // find all the typefaces needed to resolve this run of text + bool usesOriginalTypeface = false; + for (uint16_t x = 0; x < numGlyphs; ++x) { + // optimization that checks to see if original typeface can resolve the glyph + if (glyphIDs[x] < origGlyphCount) { + usesOriginalTypeface = true; + continue; + } + + // find the fallback typeface that supports this glyph + TypefaceFallbackData data; + data.typeface = SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(), + paint.getPaintOptionsAndroid(), + &data.lowerBounds, &data.upperBounds); + // add the typeface and its data if we don't have it + if (data.typeface && !visitedTypefaces.contains(data)) { + visitedTypefaces.push(data); + } + } + + // if the original font was used then add it to the list as well + if (usesOriginalTypeface) { + TypefaceFallbackData* data = visitedTypefaces.push(); + data->typeface = origFace.get(); + data->lowerBounds = 0; + data->upperBounds = origGlyphCount; + } + + // keep a scratch glyph and pos storage + SkAutoTMalloc posStorage(len * scalarsPerPos); + SkScalar* tmpPos = posStorage.get(); + SkGlyphStorage glyphStorage(numGlyphs); + uint16_t* tmpGlyphIDs = glyphStorage.get(); + + // loop through all the valid typefaces, trim the glyphs to only those + // resolved by the typeface, and then draw that run of glyphs + for (int x = 0; x < visitedTypefaces.count(); ++x) { + const TypefaceFallbackData& data = visitedTypefaces[x]; + + int tmpGlyphCount = 0; + for (uint16_t y = 0; y < numGlyphs; ++y) { + if (glyphIDs[y] >= data.lowerBounds && glyphIDs[y] < data.upperBounds) { + tmpGlyphIDs[tmpGlyphCount] = glyphIDs[y] - data.lowerBounds; + memcpy(&(tmpPos[tmpGlyphCount * scalarsPerPos]), + &(pos[y * scalarsPerPos]), + scalarsPerPos * sizeof(SkScalar)); + tmpGlyphCount++; + } + } + + // recursively call this function with the right typeface + SkPaint tmpPaint = paint; + tmpPaint.setTypeface(data.typeface); + tmpPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + // turn off fallback chaining + SkPaintOptionsAndroid paintOpts = tmpPaint.getPaintOptionsAndroid(); + paintOpts.setUseFontFallbacks(false); + tmpPaint.setPaintOptionsAndroid(paintOpts); + + this->drawPosText(d, tmpGlyphIDs, tmpGlyphCount * 2, tmpPos, constY, + scalarsPerPos, tmpPaint); + } + return; + } +#endif + SkGlyphStorage storage(0); uint16_t* glyphIDs = NULL; size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage, -- cgit v1.2.3