Revert "SkPDF: Non-outline glyphs as images"
This reverts commit 3cf6b79e50
.
Reason for revert: breaking build
../../../../../work/skia/src/pdf/SkPDFDevice.cpp: In member function 'void SkPDFDevice::internalDrawText(const void*, size_t, const SkScalar*, SkTextBlob::GlyphPositioning, SkPoint, const SkPaint&, const uint32_t*, uint32_t, const char*)':
../../../../../work/skia/src/pdf/SkPDFDevice.cpp:1578:31: error: missing initializer for member 'SkPoint::fX' [-Werror=missing-field-initializers]
SkPoint xy = {};
^
Original change's description:
> SkPDF: Non-outline glyphs as images
>
> - Cache images in PDF Canon for de-duping
> - For now, rasterize at text scale. in the future, look at CTM and
> fRasterScale.
> - add a test that works on MacOS
>
> BUG=chromium:705480
> BUG=skia:3489
> Change-Id: I4fbc42f218e5b81ea15ea6cdccc61c91e532f6d0
> Reviewed-on: https://skia-review.googlesource.com/24080
> Commit-Queue: Hal Canary <halcanary@google.com>
> Reviewed-by: Ben Wagner <bungeman@google.com>
TBR=halcanary@google.com,bungeman@google.com
Change-Id: I3d8800fcb33817c6065da0860a534eb14350d132
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: chromium:705480, skia:3489
Reviewed-on: https://skia-review.googlesource.com/24160
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
parent
0983268dd5
commit
63b6f8b932
@ -7,14 +7,13 @@
|
||||
#ifndef SkPDFCanon_DEFINED
|
||||
#define SkPDFCanon_DEFINED
|
||||
|
||||
#include "SkBitmapKey.h"
|
||||
#include "SkPDFGradientShader.h"
|
||||
#include "SkPDFGraphicState.h"
|
||||
#include "SkPDFShader.h"
|
||||
#include "SkPDFGradientShader.h"
|
||||
#include "SkPixelSerializer.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "SkTHash.h"
|
||||
#include "SkTypeface.h"
|
||||
#include "SkBitmapKey.h"
|
||||
|
||||
class SkPDFFont;
|
||||
struct SkAdvancedTypefaceMetrics;
|
||||
@ -47,25 +46,5 @@ public:
|
||||
sk_sp<SkPDFStream> fInvertFunction;
|
||||
sk_sp<SkPDFDict> fNoSmaskGraphicState;
|
||||
sk_sp<SkPDFArray> fRangeObject;
|
||||
|
||||
SK_BEGIN_REQUIRE_DENSE
|
||||
struct BitmapGlyphKey {
|
||||
SkFontID fFontID; // uint32_t
|
||||
SkScalar fTextSize; // float32
|
||||
SkScalar fTextScaleX; // float32
|
||||
SkScalar fTextSkewX; // float32
|
||||
SkGlyphID fGlyphID; // uint16_t
|
||||
uint16_t fPadding;
|
||||
};
|
||||
SK_END_REQUIRE_DENSE
|
||||
struct BitmapGlyph {
|
||||
sk_sp<SkImage> fImage;
|
||||
SkIPoint fOffset;
|
||||
};
|
||||
SkTHashMap<BitmapGlyphKey, BitmapGlyph> fBitmapGlyphImages;
|
||||
};
|
||||
|
||||
inline bool operator==(const SkPDFCanon::BitmapGlyphKey& u, const SkPDFCanon::BitmapGlyphKey& v) {
|
||||
return memcmp(&u, &u, sizeof(SkPDFCanon::BitmapGlyphKey)) == 0;
|
||||
}
|
||||
#endif // SkPDFCanon_DEFINED
|
||||
|
@ -1320,12 +1320,6 @@ static SkPath draw_text_as_path(const void* sourceText, size_t sourceByteCount,
|
||||
return path;
|
||||
}
|
||||
|
||||
static bool has_outline_glyph(SkGlyphID gid, SkGlyphCache* cache) {
|
||||
const SkGlyph& glyph = cache->getGlyphIDMetrics(gid);
|
||||
const SkPath* path = cache->findPath(glyph);
|
||||
return (path && !path->isEmpty()) || (glyph.fWidth == 0 && glyph.fHeight == 0);
|
||||
}
|
||||
|
||||
static SkRect get_glyph_bounds_device_space(SkGlyphID gid, SkGlyphCache* cache,
|
||||
SkScalar xScale, SkScalar yScale,
|
||||
SkPoint xy, const SkMatrix& ctm) {
|
||||
@ -1344,46 +1338,6 @@ static bool contains(const SkRect& r, SkPoint p) {
|
||||
r.top() <= p.y() && p.y() <= r.bottom();
|
||||
}
|
||||
|
||||
static sk_sp<SkImage> image_from_mask(const SkMask& mask) {
|
||||
if (!mask.fImage) {
|
||||
return nullptr;
|
||||
}
|
||||
SkIRect bounds = mask.fBounds;
|
||||
SkBitmap bm;
|
||||
switch (mask.fFormat) {
|
||||
case SkMask::kBW_Format:
|
||||
bm.allocPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()));
|
||||
for (int y = 0; y < bm.height(); ++y) {
|
||||
for (int x8 = 0; x8 < bm.width(); x8 += 8) {
|
||||
uint8_t v = *mask.getAddr1(x8 + bounds.x(), y + bounds.y());
|
||||
int e = SkTMin(x8 + 8, bm.width());
|
||||
for (int x = x8; x < e; ++x) {
|
||||
*bm.getAddr8(x, y) = (v >> (x & 0x7)) & 0x1 ? 0xFF : 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
bm.setImmutable();
|
||||
return SkImage::MakeFromBitmap(bm);
|
||||
case SkMask::kA8_Format:
|
||||
bm.installPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()),
|
||||
mask.fImage, mask.fRowBytes);
|
||||
return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode);
|
||||
case SkMask::kARGB32_Format:
|
||||
bm.installPixels(SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()),
|
||||
mask.fImage, mask.fRowBytes);
|
||||
return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode);
|
||||
case SkMask::k3D_Format:
|
||||
SkASSERT(false);
|
||||
return nullptr;
|
||||
case SkMask::kLCD16_Format:
|
||||
SkASSERT(false);
|
||||
return nullptr;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SkPDFDevice::internalDrawText(
|
||||
const void* sourceText, size_t sourceByteCount,
|
||||
const SkScalar pos[], SkTextBlob::GlyphPositioning positioning,
|
||||
@ -1485,169 +1439,113 @@ void SkPDFDevice::internalDrawText(
|
||||
offset.offset(alignmentFactor * advance, 0);
|
||||
}
|
||||
SkRect clipStackBounds = this->cs().bounds(size(*this));
|
||||
struct PositionedGlyph {
|
||||
SkPoint fPos;
|
||||
SkGlyphID fGlyph;
|
||||
};
|
||||
SkTArray<PositionedGlyph> fMissingGlyphs;
|
||||
{
|
||||
ScopedContentEntry content(this, paint, true);
|
||||
if (!content.entry()) {
|
||||
return;
|
||||
ScopedContentEntry content(this, paint, true);
|
||||
if (!content.entry()) {
|
||||
return;
|
||||
}
|
||||
SkDynamicMemoryWStream* out = content.stream();
|
||||
const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode;
|
||||
|
||||
out->writeText("BT\n");
|
||||
SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
|
||||
|
||||
const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
|
||||
|
||||
bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics));
|
||||
if (clusterator.reversedChars()) {
|
||||
out->writeText("/ReversedChars BMC\n");
|
||||
}
|
||||
SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
|
||||
GlyphPositioner glyphPositioner(out,
|
||||
paint.getTextSkewX(),
|
||||
multiByteGlyphs,
|
||||
defaultPositioning,
|
||||
offset);
|
||||
SkPDFFont* font = nullptr;
|
||||
|
||||
while (Clusterator::Cluster c = clusterator.next()) {
|
||||
int index = c.fGlyphIndex;
|
||||
int glyphLimit = index + c.fGlyphCount;
|
||||
|
||||
bool actualText = false;
|
||||
SK_AT_SCOPE_EXIT(if (actualText) { glyphPositioner.flush(); out->writeText("EMC\n"); } );
|
||||
if (c.fUtf8Text) { // real cluster
|
||||
// Check if `/ActualText` needed.
|
||||
const char* textPtr = c.fUtf8Text;
|
||||
const char* textEnd = c.fUtf8Text + c.fTextByteLength;
|
||||
SkUnichar unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
|
||||
if (unichar < 0) {
|
||||
return;
|
||||
}
|
||||
if (textPtr < textEnd || // more characters left
|
||||
glyphLimit > index + 1 || // toUnicode wouldn't work
|
||||
unichar != map_glyph(glyphToUnicode, glyphs[index])) // test single Unichar map
|
||||
{
|
||||
glyphPositioner.flush();
|
||||
out->writeText("/Span<</ActualText <");
|
||||
SkPDFUtils::WriteUTF16beHex(out, 0xFEFF); // U+FEFF = BYTE ORDER MARK
|
||||
// the BOM marks this text as UTF-16BE, not PDFDocEncoding.
|
||||
SkPDFUtils::WriteUTF16beHex(out, unichar); // first char
|
||||
while (textPtr < textEnd) {
|
||||
unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
|
||||
if (unichar < 0) {
|
||||
break;
|
||||
}
|
||||
SkPDFUtils::WriteUTF16beHex(out, unichar);
|
||||
}
|
||||
out->writeText("> >> BDC\n"); // begin marked-content sequence
|
||||
// with an associated property list.
|
||||
actualText = true;
|
||||
}
|
||||
}
|
||||
SkDynamicMemoryWStream* out = content.stream();
|
||||
const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode;
|
||||
|
||||
out->writeText("BT\n");
|
||||
SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
|
||||
|
||||
const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
|
||||
|
||||
bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics));
|
||||
if (clusterator.reversedChars()) {
|
||||
out->writeText("/ReversedChars BMC\n");
|
||||
}
|
||||
SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
|
||||
GlyphPositioner glyphPositioner(out,
|
||||
paint.getTextSkewX(),
|
||||
multiByteGlyphs,
|
||||
defaultPositioning,
|
||||
offset);
|
||||
SkPDFFont* font = nullptr;
|
||||
|
||||
while (Clusterator::Cluster c = clusterator.next()) {
|
||||
int index = c.fGlyphIndex;
|
||||
int glyphLimit = index + c.fGlyphCount;
|
||||
|
||||
bool actualText = false;
|
||||
SK_AT_SCOPE_EXIT(if (actualText) {
|
||||
glyphPositioner.flush();
|
||||
out->writeText("EMC\n");
|
||||
});
|
||||
if (c.fUtf8Text) { // real cluster
|
||||
// Check if `/ActualText` needed.
|
||||
const char* textPtr = c.fUtf8Text;
|
||||
const char* textEnd = c.fUtf8Text + c.fTextByteLength;
|
||||
SkUnichar unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
|
||||
if (unichar < 0) {
|
||||
for (; index < glyphLimit; ++index) {
|
||||
SkGlyphID gid = glyphs[index];
|
||||
if (gid > maxGlyphID) {
|
||||
continue;
|
||||
}
|
||||
if (!font || !font->hasGlyph(gid)) {
|
||||
// Not yet specified font or need to switch font.
|
||||
int fontIndex = this->getFontResourceIndex(typeface, gid);
|
||||
// All preconditions for SkPDFFont::GetFontResource are met.
|
||||
SkASSERT(fontIndex >= 0);
|
||||
if (fontIndex < 0) {
|
||||
return;
|
||||
}
|
||||
if (textPtr < textEnd || // more characters left
|
||||
glyphLimit > index + 1 || // toUnicode wouldn't work
|
||||
unichar != map_glyph(glyphToUnicode, glyphs[index])) // test single Unichar map
|
||||
{
|
||||
glyphPositioner.flush();
|
||||
out->writeText("/Span<</ActualText <");
|
||||
SkPDFUtils::WriteUTF16beHex(out, 0xFEFF); // U+FEFF = BYTE ORDER MARK
|
||||
// the BOM marks this text as UTF-16BE, not PDFDocEncoding.
|
||||
SkPDFUtils::WriteUTF16beHex(out, unichar); // first char
|
||||
while (textPtr < textEnd) {
|
||||
unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
|
||||
if (unichar < 0) {
|
||||
break;
|
||||
}
|
||||
SkPDFUtils::WriteUTF16beHex(out, unichar);
|
||||
}
|
||||
out->writeText("> >> BDC\n"); // begin marked-content sequence
|
||||
// with an associated property list.
|
||||
actualText = true;
|
||||
glyphPositioner.flush();
|
||||
update_font(out, fontIndex, textSize);
|
||||
font = fFontResources[fontIndex];
|
||||
SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(font->multiByteGlyphs() == multiByteGlyphs);
|
||||
}
|
||||
for (; index < glyphLimit; ++index) {
|
||||
SkGlyphID gid = glyphs[index];
|
||||
if (gid > maxGlyphID) {
|
||||
continue;
|
||||
SkPoint xy{0, 0};
|
||||
SkScalar advance{0};
|
||||
if (!defaultPositioning) {
|
||||
advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
|
||||
xy = SkTextBlob::kFull_Positioning == positioning
|
||||
? SkPoint{pos[2 * index], pos[2 * index + 1]}
|
||||
: SkPoint{pos[index], 0};
|
||||
if (alignment != SkPaint::kLeft_Align) {
|
||||
xy.offset(alignmentFactor * advance, 0);
|
||||
}
|
||||
if (!font || !font->hasGlyph(gid)) {
|
||||
// Not yet specified font or need to switch font.
|
||||
int fontIndex = this->getFontResourceIndex(typeface, gid);
|
||||
// All preconditions for SkPDFFont::GetFontResource are met.
|
||||
SkASSERT(fontIndex >= 0);
|
||||
if (fontIndex < 0) {
|
||||
return;
|
||||
}
|
||||
glyphPositioner.flush();
|
||||
update_font(out, fontIndex, textSize);
|
||||
font = fFontResources[fontIndex];
|
||||
SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(font->multiByteGlyphs() == multiByteGlyphs);
|
||||
}
|
||||
SkPoint xy = {};
|
||||
SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
|
||||
if (!defaultPositioning) {
|
||||
xy = SkTextBlob::kFull_Positioning == positioning
|
||||
? SkPoint{pos[2 * index], pos[2 * index + 1]}
|
||||
: SkPoint{pos[index], 0};
|
||||
if (alignment != SkPaint::kLeft_Align) {
|
||||
xy.offset(alignmentFactor * advance, 0);
|
||||
}
|
||||
// Do a glyph-by-glyph bounds-reject if positions are absolute.
|
||||
SkRect glyphBounds = get_glyph_bounds_device_space(
|
||||
gid, glyphCache.get(), textScaleX, textScaleY,
|
||||
xy + offset, this->ctm());
|
||||
if (glyphBounds.isEmpty()) {
|
||||
if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!clipStackBounds.intersects(glyphBounds)) {
|
||||
continue; // reject glyphs as out of bounds
|
||||
}
|
||||
}
|
||||
if (!has_outline_glyph(gid, glyphCache.get())) {
|
||||
fMissingGlyphs.push_back({xy, gid});
|
||||
// Do a glyph-by-glyph bounds-reject if positions are absolute.
|
||||
SkRect glyphBounds = get_glyph_bounds_device_space(
|
||||
gid, glyphCache.get(), textScaleX, textScaleY, xy + offset, this->ctm());
|
||||
if (glyphBounds.isEmpty()) {
|
||||
if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!has_outline_glyph(gid, glyphCache.get())) {
|
||||
fMissingGlyphs.push_back({offset, gid});
|
||||
if (!clipStackBounds.intersects(glyphBounds)) {
|
||||
continue; // reject glyphs as out of bounds
|
||||
}
|
||||
offset += SkPoint{advance, 0};
|
||||
}
|
||||
font->noteGlyphUsage(gid);
|
||||
|
||||
SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid);
|
||||
glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fMissingGlyphs.count() > 0) {
|
||||
// Fall back on images.
|
||||
SkPaint scaledGlyphCachePaint;
|
||||
scaledGlyphCachePaint.setTextSize(paint.getTextSize());
|
||||
scaledGlyphCachePaint.setTextScaleX(paint.getTextScaleX());
|
||||
scaledGlyphCachePaint.setTextSkewX(paint.getTextSkewX());
|
||||
scaledGlyphCachePaint.setTypeface(sk_ref_sp(typeface));
|
||||
SkAutoGlyphCache scaledGlyphCache(scaledGlyphCachePaint, nullptr, nullptr);
|
||||
SkTHashMap<SkPDFCanon::BitmapGlyphKey, SkPDFCanon::BitmapGlyph>* map =
|
||||
&this->getCanon()->fBitmapGlyphImages;
|
||||
for (PositionedGlyph positionedGlyph : fMissingGlyphs) {
|
||||
SkPDFCanon::BitmapGlyphKey key = {typeface->uniqueID(),
|
||||
paint.getTextSize(),
|
||||
paint.getTextScaleX(),
|
||||
paint.getTextSkewX(),
|
||||
positionedGlyph.fGlyph,
|
||||
0};
|
||||
SkImage* img = nullptr;
|
||||
SkIPoint imgOffset = {0, 0};
|
||||
if (SkPDFCanon::BitmapGlyph* ptr = map->find(key)) {
|
||||
img = ptr->fImage.get();
|
||||
imgOffset = ptr->fOffset;
|
||||
} else {
|
||||
(void)scaledGlyphCache->findImage(
|
||||
scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph));
|
||||
SkMask mask;
|
||||
scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph).toMask(&mask);
|
||||
imgOffset = {mask.fBounds.x(), mask.fBounds.y()};
|
||||
img = map->set(key, {image_from_mask(mask), imgOffset})->fImage.get();
|
||||
}
|
||||
if (img) {
|
||||
SkPoint pt = positionedGlyph.fPos +
|
||||
SkPoint{(SkScalar)imgOffset.x(), (SkScalar)imgOffset.y()};
|
||||
this->drawImage(img, pt.x(), pt.y(), srcPaint);
|
||||
}
|
||||
font->noteGlyphUsage(gid);
|
||||
SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid);
|
||||
glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user