aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/androidfallback.cpp87
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/ports/SkTypeface_android.h16
-rw-r--r--src/pdf/SkPDFDevice.cpp122
-rw-r--r--src/ports/SkFontConfigInterface_android.cpp66
5 files changed, 292 insertions, 0 deletions
diff --git a/gm/androidfallback.cpp b/gm/androidfallback.cpp
new file mode 100644
index 0000000000..3ef8f1ca51
--- /dev/null
+++ b/gm/androidfallback.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+
+namespace skiagm {
+
+class AndroidFallbackGM : public GM {
+public:
+ AndroidFallbackGM() {
+ this->setBGColor(0xFFCCCCCC);
+ }
+
+protected:
+ virtual SkString onShortName() SK_OVERRIDE {
+ return SkString("android_paint");
+ }
+
+ virtual SkISize onISize() SK_OVERRIDE {
+ return make_isize(500, 500);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ paint.setTextSize(24);
+
+#if SK_BUILD_FOR_ANDROID
+ SkPaintOptionsAndroid options = paint.getPaintOptionsAndroid();
+ options.setUseFontFallbacks(true);
+ paint.setPaintOptionsAndroid(options);
+#endif
+
+ // "ื foo ๅ…่ˆŒ bar เค•"
+ const uint16_t unicodeStr[] = {0x05D0, 0x0020, 0x0066, 0x006F, 0x006F, 0x0020, 0x514D,
+ 0x820c, 0x0020, 0x0062, 0x0061, 0x0072, 0x0020, 0x0915};
+ const int strLength = sizeof(unicodeStr) / sizeof(uint16_t);
+ const int strByteLength = sizeof(unicodeStr);
+
+ SkScalar posX[strLength];
+ SkPoint posXY[strLength];
+
+ for (int i = 0; i < strLength; ++i) {
+ posX[i] = SkIntToScalar(i * 24);
+ posXY[i].fX = posX[i];
+ posXY[i].fY = SkIntToScalar(24 + i);
+ }
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(25));
+ // This currently causes the PDF backend to assert
+ // canvas->drawText(unicodeStr, strByteLength, 0, 0, paint);
+
+ canvas->translate(0, SkIntToScalar(75));
+ canvas->drawPosTextH(unicodeStr, strByteLength, posX, 0, paint);
+
+#if SK_BUILD_FOR_ANDROID
+ options.setLanguage("ja");
+ paint.setPaintOptionsAndroid(options);
+#endif
+
+ canvas->translate(0, SkIntToScalar(75));
+ canvas->drawPosText(unicodeStr, strByteLength, posXY, paint);
+
+ SkPath path;
+ path.moveTo(0, 0);
+ path.quadTo(50.0f, 100.0f, 250.0f, 150.0f);
+
+ canvas->translate(0, SkIntToScalar(75));
+ canvas->drawTextOnPath(unicodeStr, strByteLength, path, NULL, paint);
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#if SK_BUILD_FOR_ANDROID
+DEF_GM( return SkNEW(AndroidFallbackGM); )
+#endif
+
+}
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index bace62fd75..5e2dfc17e1 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -4,6 +4,7 @@
'../gm/aaclip.cpp',
'../gm/aarectmodes.cpp',
'../gm/alphagradients.cpp',
+ '../gm/androidfallback.cpp',
'../gm/arcofzorro.cpp',
'../gm/arithmode.cpp',
'../gm/beziereffects.cpp',
diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h
index 1ee923c4e6..2166e08394 100644
--- a/include/ports/SkTypeface_android.h
+++ b/include/ports/SkTypeface_android.h
@@ -68,6 +68,22 @@ SK_API void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackco
SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
const SkPaintOptionsAndroid& options);
+/**
+ * Given a glyphID (built using fallback font chaining) and its origin typeface
+ * return the actual typeface within the fallback chain that this glyphID
+ * resolves to. If no suitable typeface is found then NULL is returned. However,
+ * if returned typeface is not NULL it is assumed to be globally cached so the
+ * caller need not ref it.
+ *
+ * Optionally, if lower/upper bound params are provided and the returned
+ * typeface is not NULL, then these params are populated with the range of
+ * glyphIDs that this typeface is capable of resolving. The lower bound is
+ * inclusive while the upper bound is exclusive.
+ */
+SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
+ const SkPaintOptionsAndroid& options,
+ int* lowerBounds = NULL, int* upperBounds = NULL);
+
#endif // #ifdef SK_BUILD_FOR_ANDROID
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
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<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,
diff --git a/src/ports/SkFontConfigInterface_android.cpp b/src/ports/SkFontConfigInterface_android.cpp
index 7382d00b45..9c188d90c6 100644
--- a/src/ports/SkFontConfigInterface_android.cpp
+++ b/src/ports/SkFontConfigInterface_android.cpp
@@ -104,6 +104,9 @@ public:
SkPaintOptionsAndroid::FontVariant fontVariant);
SkTypeface* nextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
const SkPaintOptionsAndroid& options);
+ SkTypeface* getTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
+ const SkPaintOptionsAndroid& options,
+ int* lowerBounds, int* upperBounds);
private:
void addFallbackFamily(FamilyRecID fontRecID);
@@ -672,6 +675,61 @@ SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontI
return SkSafeRef(nextLogicalTypeface);
}
+SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForGlyphID(uint16_t glyphID,
+ const SkTypeface* origTypeface,
+ const SkPaintOptionsAndroid& opts,
+ int* lBounds, int* uBounds) {
+ // If we aren't using fallbacks then we shouldn't be calling this
+ SkASSERT(opts.isUsingFontFallbacks());
+ SkASSERT(origTypeface);
+
+ SkTypeface* currentTypeface = NULL;
+ int lowerBounds = 0; //inclusive
+ int upperBounds = origTypeface->countGlyphs(); //exclusive
+
+ // check to see if the glyph is in the bounds of the origTypeface
+ if (glyphID < upperBounds) {
+ currentTypeface = const_cast<SkTypeface*>(origTypeface);
+ } else {
+ FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
+ SkASSERT(currentFallbackList);
+
+ // If an object is set to prefer "kDefault_Variant" it means they have no preference
+ // In this case, we set the value to "kCompact_Variant"
+ SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant();
+ if (variant == SkPaintOptionsAndroid::kDefault_Variant) {
+ variant = SkPaintOptionsAndroid::kCompact_Variant;
+ }
+
+ int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant;
+ SkTypeface::Style origStyle = origTypeface->style();
+
+ for (int x = 0; x < currentFallbackList->count(); ++x) {
+ const FamilyRecID familyRecID = currentFallbackList->getAt(x);
+ const SkPaintOptionsAndroid& familyOptions = fFontFamilies[familyRecID].fPaintOptions;
+ if ((familyOptions.getFontVariant() & acceptedVariants) != 0) {
+ FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle);
+ currentTypeface = this->getTypefaceForFontRec(matchedFont);
+ lowerBounds = upperBounds;
+ upperBounds += currentTypeface->countGlyphs();
+ if (glyphID < upperBounds) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (NULL != currentTypeface) {
+ if (lBounds) {
+ *lBounds = lowerBounds;
+ }
+ if (uBounds) {
+ *uBounds = upperBounds;
+ }
+ }
+ return currentTypeface;
+}
+
///////////////////////////////////////////////////////////////////////////////
bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
@@ -704,6 +762,14 @@ SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontI
}
+SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
+ const SkPaintOptionsAndroid& options,
+ int* lowerBounds, int* upperBounds) {
+ SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
+ return fontConfig->getTypefaceForGlyphID(glyphID, origTypeface, options,
+ lowerBounds, upperBounds);
+}
+
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK