Index: src/pdf/SkPDFDevice.cpp |
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp |
index d13d30cbfb46efbbbe29f6bc3f22522190af75de..e4b07f04fa794bcd27373564411445a039cd4afe 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<uint16_t*>(const_cast<void*>(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<SkTypeface> 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<TypefaceFallbackData> 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<SkScalar> 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, |