aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2015-10-12 13:05:04 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-10-12 13:05:04 -0700
commit66a82f3872abf4ebb98b3915b2a9ecc73ad352c5 (patch)
treee8bafe893fcce8e95d9d8b20f0c7f12d80ea8e0e
parent88d064d0e481949184305c7b1d6b282dddffac39 (diff)
SkPDF: fall back on paths for unembeddable fonts.
Add GM, SkPDFFont::CanEmbedTypeface BUG=skia:3866 Review URL: https://codereview.chromium.org/1401763002
-rw-r--r--gm/pdf_never_embed.cpp58
-rw-r--r--resources/fonts/Roboto2-Regular_NoEmbed.ttfbin0 -> 6156 bytes
-rw-r--r--src/pdf/SkPDFCanon.h2
-rw-r--r--src/pdf/SkPDFDevice.cpp72
-rw-r--r--src/pdf/SkPDFDevice.h2
-rw-r--r--src/pdf/SkPDFFont.cpp37
-rw-r--r--src/pdf/SkPDFFont.h6
-rw-r--r--tests/PDFPrimitivesTest.cpp20
8 files changed, 184 insertions, 13 deletions
diff --git a/gm/pdf_never_embed.cpp b/gm/pdf_never_embed.cpp
new file mode 100644
index 0000000000..ef7974e3d7
--- /dev/null
+++ b/gm/pdf_never_embed.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkTypeface.h"
+#include "gm.h"
+
+static void excercise_draw_pos_text(SkCanvas* canvas,
+ const char* text,
+ SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ size_t textLen = strlen(text);
+ SkAutoTArray<SkScalar> widths(SkToInt(textLen));
+ paint.getTextWidths(text, textLen, &widths[0]);
+ SkAutoTArray<SkPoint> pos(SkToInt(textLen));
+ for (int i = 0; i < SkToInt(textLen); ++i) {
+ pos[i].set(x, y);
+ x += widths[i];
+ }
+ canvas->drawPosText(text, textLen, &pos[0], paint);
+}
+
+DEF_SIMPLE_GM(pdf_never_embed, canvas, 512, 512) {
+ const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf";
+ SkAutoTUnref<SkTypeface> typeface(GetResourceAsTypeface(resource));
+ if (!typeface) {
+ return;
+ }
+ SkPaint p;
+ p.setTextSize(60);
+ p.setTypeface(typeface);
+ p.setAntiAlias(true);
+
+ const char text[] = "HELLO, WORLD!";
+
+ canvas->drawColor(SK_ColorWHITE);
+ excercise_draw_pos_text(canvas, text, 30, 90, p);
+
+ canvas->save();
+ canvas->rotate(45.0f);
+ p.setColor(0xF0800000);
+ excercise_draw_pos_text(canvas, text, 30, 45, p);
+ canvas->restore();
+
+ canvas->save();
+ canvas->scale(1, 4.0);
+ p.setColor(0xF0008000);
+ excercise_draw_pos_text(canvas, text, 15, 70, p);
+ canvas->restore();
+
+ canvas->scale(1.0, 0.5);
+ p.setColor(0xF0000080);
+ canvas->drawText(text, strlen(text), 30, 700, p);
+}
diff --git a/resources/fonts/Roboto2-Regular_NoEmbed.ttf b/resources/fonts/Roboto2-Regular_NoEmbed.ttf
new file mode 100644
index 0000000000..0892862370
--- /dev/null
+++ b/resources/fonts/Roboto2-Regular_NoEmbed.ttf
Binary files differ
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index a55024d611..988b2855e1 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -78,6 +78,8 @@ public:
void addPDFBitmap(uint32_t imageUniqueID, SkPDFObject*);
const SkImage* bitmapToImage(const SkBitmap&);
+ SkTHashMap<uint32_t, bool> fCanEmbedTypeface;
+
private:
struct FontRec {
SkPDFFont* fFont;
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 9134d802ff..c27cc64bfb 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1231,8 +1231,57 @@ static SkString format_wide_string(const uint16_t* input,
}
}
+static void draw_transparent_text(SkPDFDevice* device,
+ const SkDraw& d,
+ const void* text, size_t len,
+ SkScalar x, SkScalar y,
+ const SkPaint& srcPaint) {
+
+ SkPaint transparent;
+ if (!SkPDFFont::CanEmbedTypeface(transparent.getTypeface(),
+ device->getCanon())) {
+ SkDEBUGFAIL("default typeface should be embeddable");
+ return; // Avoid infinite loop in release.
+ }
+ transparent.setTextSize(srcPaint.getTextSize());
+ transparent.setColor(SK_ColorTRANSPARENT);
+ switch (srcPaint.getTextEncoding()) {
+ case SkPaint::kGlyphID_TextEncoding: {
+ // Since a glyphId<->Unicode mapping is typeface-specific,
+ // map back to Unicode first.
+ size_t glyphCount = len / 2;
+ SkAutoTMalloc<SkUnichar> unichars(glyphCount);
+ srcPaint.glyphsToUnichars(
+ (const uint16_t*)text, SkToInt(glyphCount), &unichars[0]);
+ transparent.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+ device->drawText(d, &unichars[0],
+ glyphCount * sizeof(SkUnichar),
+ x, y, transparent);
+ break;
+ }
+ case SkPaint::kUTF8_TextEncoding:
+ case SkPaint::kUTF16_TextEncoding:
+ case SkPaint::kUTF32_TextEncoding:
+ transparent.setTextEncoding(srcPaint.getTextEncoding());
+ device->drawText(d, text, len, x, y, transparent);
+ break;
+ default:
+ SkFAIL("unknown text encoding");
+ }
+}
+
+
void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& srcPaint) {
+ if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) {
+ // http://skbug.com/3866
+ SkPath path;
+ srcPaint.getTextPath(text, len, x, y, &path);
+ this->drawPath(d, path, srcPaint, &SkMatrix::I(), true);
+ // Draw text transparently to make it copyable/searchable/accessable.
+ draw_transparent_text(this, d, text, len, x, y, srcPaint);
+ return;
+ }
SkPaint paint = srcPaint;
replace_srcmode_on_opaque_paint(&paint);
@@ -1285,6 +1334,29 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
const SkScalar pos[], int scalarsPerPos,
const SkPoint& offset, const SkPaint& srcPaint) {
+ if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) {
+ const SkPoint* positions = reinterpret_cast<const SkPoint*>(pos);
+ SkAutoTMalloc<SkPoint> positionsBuffer;
+ if (2 != scalarsPerPos) {
+ int glyphCount = srcPaint.textToGlyphs(text, len, NULL);
+ positionsBuffer.reset(glyphCount);
+ for (int i = 0; i < glyphCount; ++i) {
+ positionsBuffer[i].set(pos[i], 0.0f);
+ }
+ positions = &positionsBuffer[0];
+ }
+ SkPath path;
+ srcPaint.getPosTextPath(text, len, positions, &path);
+ SkMatrix matrix;
+ matrix.setTranslate(offset);
+ this->drawPath(d, path, srcPaint, &matrix, true);
+ // Draw text transparently to make it copyable/searchable/accessable.
+ draw_transparent_text(
+ this, d, text, len, offset.x() + positions[0].x(),
+ offset.y() + positions[0].y(), srcPaint);
+ return;
+ }
+
SkPaint paint = srcPaint;
replace_srcmode_on_opaque_paint(&paint);
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 75447f9be2..742e45ca5c 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -189,9 +189,7 @@ public:
return *(fFontGlyphUsage.get());
}
-#ifdef SK_DEBUG
SkPDFCanon* getCanon() const { return fCanon; }
-#endif // SK_DEBUG
protected:
const SkBitmap& onAccessBitmap() override {
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 8df59b77f1..9cbada8364 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -1051,10 +1051,7 @@ bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
this->insertObjRef("FontDescriptor", descriptor.detach());
return false;
}
- if (!canEmbed()) {
- this->insertObjRef("FontDescriptor", descriptor.detach());
- return true;
- }
+ SkASSERT(this->canEmbed());
switch (getType()) {
case SkAdvancedTypefaceMetrics::kTrueType_Font: {
@@ -1222,13 +1219,12 @@ bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
if (fontData.get() == nullptr) {
return false;
}
- if (canEmbed()) {
- SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData.get()));
- fontStream->insertInt("Length1", header);
- fontStream->insertInt("Length2", data);
- fontStream->insertInt("Length3", trailer);
- descriptor->insertObjRef("FontFile", fontStream.detach());
- }
+ SkASSERT(this->canEmbed());
+ SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData.get()));
+ fontStream->insertInt("Length1", header);
+ fontStream->insertInt("Length2", data);
+ fontStream->insertInt("Length3", trailer);
+ descriptor->insertObjRef("FontFile", fontStream.detach());
this->insertObjRef("FontDescriptor", descriptor.detach());
@@ -1418,3 +1414,22 @@ SkPDFFont::Match SkPDFFont::IsMatch(SkPDFFont* existingFont,
return (existingGlyphID == searchGlyphID) ? SkPDFFont::kExact_Match
: SkPDFFont::kRelated_Match;
}
+
+// Since getAdvancedTypefaceMetrics is expensive, cache the result.
+bool SkPDFFont::CanEmbedTypeface(SkTypeface* typeface, SkPDFCanon* canon) {
+ SkAutoResolveDefaultTypeface face(typeface);
+ uint32_t id = face->uniqueID();
+ if (bool* value = canon->fCanEmbedTypeface.find(id)) {
+ return *value;
+ }
+ bool canEmbed = true;
+ SkAutoTUnref<const SkAdvancedTypefaceMetrics> fontMetrics(
+ face->getAdvancedTypefaceMetrics(
+ SkTypeface::kNo_PerGlyphInfo, nullptr, 0));
+ if (fontMetrics) {
+ canEmbed = !SkToBool(
+ fontMetrics->fFlags &
+ SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
+ }
+ return *canon->fCanEmbedTypeface.set(id, canEmbed);
+}
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index 404e8b7621..ca0a51fca4 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -149,6 +149,12 @@ public:
uint32_t searchFontID,
uint16_t searchGlyphID);
+ /**
+ * Return false iff the typeface has its NotEmbeddable flag set.
+ * If typeface is NULL, the default typeface is checked.
+ */
+ static bool CanEmbedTypeface(SkTypeface*, SkPDFCanon*);
+
protected:
// Common constructor to handle common members.
SkPDFFont(const SkAdvancedTypefaceMetrics* fontInfo,
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 3253fcaf71..5fce53ea0a 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
+#include "Resources.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkData.h"
@@ -14,6 +15,7 @@
#include "SkMatrix.h"
#include "SkPDFCanon.h"
#include "SkPDFDevice.h"
+#include "SkPDFFont.h"
#include "SkPDFStream.h"
#include "SkPDFTypes.h"
#include "SkReadBuffer.h"
@@ -21,6 +23,7 @@
#include "SkStream.h"
#include "SkTypes.h"
#include "Test.h"
+#include "sk_tool_utils.h"
#define DUMMY_TEXT "DCT compessed stream."
@@ -415,3 +418,20 @@ DEF_TEST(PDFImageFilter, reporter) {
// Filter was used in rendering; should be visited.
REPORTER_ASSERT(reporter, filter->visited());
}
+
+// Check that PDF rendering of image filters successfully falls back to
+// CPU rasterization.
+DEF_TEST(PDFFontCanEmbedTypeface, reporter) {
+ SkPDFCanon canon;
+
+ const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf";
+ SkAutoTUnref<SkTypeface> noEmbedTypeface(GetResourceAsTypeface(resource));
+ if (noEmbedTypeface) {
+ REPORTER_ASSERT(reporter,
+ !SkPDFFont::CanEmbedTypeface(noEmbedTypeface, &canon));
+ }
+ SkAutoTUnref<SkTypeface> portableTypeface(
+ sk_tool_utils::create_portable_typeface(NULL, SkTypeface::kNormal));
+ REPORTER_ASSERT(reporter,
+ SkPDFFont::CanEmbedTypeface(portableTypeface, &canon));
+}