diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h index 38dd35895c..12ba051cf7 100644 --- a/src/pdf/SkPDFCanon.h +++ b/src/pdf/SkPDFCanon.h @@ -52,25 +52,6 @@ public: sk_sp fInvertFunction; sk_sp fNoSmaskGraphicState; sk_sp 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 fImage; - SkIPoint fOffset; - }; - SkTHashMap fBitmapGlyphImages; }; -inline bool operator==(const SkPDFCanon::BitmapGlyphKey& u, const SkPDFCanon::BitmapGlyphKey& v) { - return memcmp(&u, &v, sizeof(SkPDFCanon::BitmapGlyphKey)) == 0; -} #endif // SkPDFCanon_DEFINED diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 996eb45cad..13b41dc61f 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -937,12 +937,10 @@ class GlyphPositioner { public: GlyphPositioner(SkDynamicMemoryWStream* content, SkScalar textSkewX, - bool wideChars, SkPoint origin) : fContent(content) , fCurrentMatrixOrigin(origin) - , fTextSkewX(textSkewX) - , fWideChars(wideChars) { + , fTextSkewX(textSkewX) { } ~GlyphPositioner() { this->flush(); } void flush() { @@ -951,6 +949,10 @@ public: fInText = false; } } + void setWideChars(bool wide) { + this->flush(); + fWideChars = wide; + } void writeGlyph(SkPoint xy, SkScalar advanceWidth, uint16_t glyph) { @@ -995,7 +997,7 @@ private: SkPoint fCurrentMatrixOrigin; SkScalar fXAdvance = 0.0f; SkScalar fTextSkewX; - bool fWideChars; + bool fWideChars = true; bool fInText = false; bool fInitialized = false; }; @@ -1012,12 +1014,6 @@ struct PositionedGlyph { }; } -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) { @@ -1036,88 +1032,6 @@ static bool contains(const SkRect& r, SkPoint p) { r.top() <= p.y() && p.y() <= r.bottom(); } -static sk_sp 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 draw_missing_glyphs(SkPDFDevice* dev, const SkPaint& paint, SkTypeface* typeface, - const std::vector& missingGlyphs) { - if (missingGlyphs.size() == 0) { - return; - } - // Fall back on images. - SkPaint scaledGlyphCachePaint; - scaledGlyphCachePaint.setTextSize(paint.getTextSize()); - scaledGlyphCachePaint.setTextScaleX(paint.getTextScaleX()); - scaledGlyphCachePaint.setTextSkewX(paint.getTextSkewX()); - scaledGlyphCachePaint.setTypeface(sk_ref_sp(typeface)); - auto scaledGlyphCache = SkStrikeCache::FindOrCreateStrikeExclusive(scaledGlyphCachePaint); - SkTHashMap* map = - &dev->getCanon()->fBitmapGlyphImages; - for (PositionedGlyph positionedGlyph : missingGlyphs) { - 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()}; - dev->drawImage(img, pt.x(), pt.y(), paint); - } - } -} - void SkPDFDevice::drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset) { const SkPaint& paint = glyphRun.paint(); SkPath path; @@ -1150,21 +1064,6 @@ void SkPDFDevice::drawGlyphRunAsPath(const SkGlyphRun& glyphRun, SkPoint offset) } else { this->internalDrawGlyphRun(tmp, offset); } - - if (!tmp.paint().getTypeface()) { - return; - } - std::vector missingGlyphs; - int emSize; - auto glyphCache = SkPDFFont::MakeVectorCache(tmp.paint().getTypeface(), &emSize); - for (size_t i = 0; i < glyphRun.shuntGlyphsIDs().size(); ++i) { - SkGlyphID gid = glyphRun.shuntGlyphsIDs()[i]; - if (!has_outline_glyph(gid, glyphCache.get())) { - SkPoint xy = glyphRun.positions()[i]; - missingGlyphs.push_back({xy + offset, gid}); - } - } - draw_missing_glyphs(this, paint, tmp.paint().getTypeface(), missingGlyphs); } void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offset) { @@ -1217,7 +1116,6 @@ void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offse SkASSERT(paint.getTextAlign() == SkPaint::kLeft_Align); SkRect clipStackBounds = this->cs().bounds(this->bounds()); - std::vector missingGlyphs; { ScopedContentEntry content(this, paint, true); if (!content.entry()) { @@ -1243,15 +1141,11 @@ void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offse 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, - offset); + GlyphPositioner glyphPositioner(out, paint.getTextSkewX(), offset); SkPDFFont* font = nullptr; while (SkClusterator::Cluster c = clusterator.next()) { @@ -1300,7 +1194,8 @@ void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offse if (!font || !font->hasGlyph(gid)) { // Not yet specified font or need to switch font. sk_sp newFont = - SkPDFFont::GetFontResource(fDocument->canon(), typeface, gid); + SkPDFFont::GetFontResource( + fDocument->canon(), glyphCache.get(), typeface, gid); SkASSERT(newFont); // All preconditions for SkPDFFont::GetFontResource are met. if (!newFont) { return; @@ -1310,13 +1205,12 @@ void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offse int fontIndex = find_or_add(&fFontResources, std::move(newFont)); glyphPositioner.flush(); - + glyphPositioner.setWideChars(font->multiByteGlyphs()); SkPDFWriteResourceName(out, SkPDFResourceType::kFont, fontIndex); out->writeText(" "); SkPDFUtils::AppendScalar(textSize, out); out->writeText(" Tf\n"); - SkASSERT(font->multiByteGlyphs() == multiByteGlyphs); } SkPoint xy = glyphRun.positions()[index]; // Do a glyph-by-glyph bounds-reject if positions are absolute. @@ -1332,21 +1226,15 @@ void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offse continue; // reject glyphs as out of bounds } } - if (!has_outline_glyph(gid, glyphCache.get())) { - missingGlyphs.push_back({xy + offset, gid}); - } - font->noteGlyphUsage(gid); - SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid); + SkGlyphID encodedGlyph = font->multiByteGlyphs() + ? gid : font->glyphToPDFFontEncoding(gid); SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX; glyphPositioner.writeGlyph(xy, advance, encodedGlyph); } } } - if (paint.getColor() != SK_ColorTRANSPARENT) { - draw_missing_glyphs(this, srcPaint, typeface, missingGlyphs); - } } void SkPDFDevice::drawGlyphRunList(const SkGlyphRunList& glyphRunList) { diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp index 6844a5461c..6d4c8305e6 100644 --- a/src/pdf/SkPDFFont.cpp +++ b/src/pdf/SkPDFFont.cpp @@ -9,13 +9,16 @@ #include "SkData.h" #include "SkGlyphCache.h" +#include "SkImagePriv.h" #include "SkMacros.h" #include "SkMakeUnique.h" +#include "SkPDFBitmap.h" #include "SkPDFCanon.h" #include "SkPDFConvertType1FontStream.h" #include "SkPDFDevice.h" #include "SkPDFMakeCIDGlyphWidthsArray.h" #include "SkPDFMakeToUnicodeCmap.h" +#include "SkPDFResourceDict.h" #include "SkPDFUtils.h" #include "SkPaint.h" #include "SkRefCnt.h" @@ -205,7 +208,14 @@ static SkGlyphID first_nonzero_glyph_for_single_byte_encoding(SkGlyphID gid) { return gid != 0 ? gid - (gid - 1) % 255 : 1; } +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); +} + sk_sp SkPDFFont::GetFontResource(SkPDFCanon* canon, + SkGlyphCache* cache, SkTypeface* face, SkGlyphID glyphID) { SkASSERT(canon); @@ -215,6 +225,9 @@ sk_sp SkPDFFont::GetFontResource(SkPDFCanon* canon, // GetMetrics only returns null to signify a bad typeface. const SkAdvancedTypefaceMetrics& metrics = *fontMetrics; SkAdvancedTypefaceMetrics::FontType type = SkPDFFont::FontType(metrics); + if (!has_outline_glyph(glyphID, cache)) { + type = SkAdvancedTypefaceMetrics::kOther_Font; + } bool multibyte = SkPDFFont::IsMultiByte(type); SkGlyphID subsetCode = multibyte ? 0 : first_nonzero_glyph_for_single_byte_encoding(glyphID); uint64_t fontID = (static_cast(SkTypeface::UniqueID(face)) << 16) | subsetCode; @@ -636,6 +649,51 @@ private: }; } +struct ImageAndOffset { + sk_sp fImage; + SkIPoint fOffset; +}; +static ImageAndOffset to_image(SkGlyphID gid, SkGlyphCache* cache) { + (void)cache->findImage(cache->getGlyphIDMetrics(gid)); + SkMask mask; + cache->getGlyphIDMetrics(gid).toMask(&mask); + if (!mask.fImage) { + return {nullptr, {0, 0}}; + } + 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), {bounds.x(), bounds.y()}}; + case SkMask::kA8_Format: + bm.installPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()), + mask.fImage, mask.fRowBytes); + return {SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode), + {bounds.x(), bounds.y()}}; + case SkMask::kARGB32_Format: + bm.installPixels(SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()), + mask.fImage, mask.fRowBytes); + return {SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode), + {bounds.x(), bounds.y()}}; + case SkMask::k3D_Format: + case SkMask::kLCD16_Format: + default: + SkASSERT(false); + return {nullptr, {0, 0}}; + } +} + static void add_type3_font_info(SkPDFCanon* canon, SkPDFDict* font, SkTypeface* typeface, @@ -704,12 +762,35 @@ static void add_type3_font_info(SkPDFCanon* canon, characterName, sk_make_sp( std::unique_ptr(content.detachAsStream()))); } else { - if (!emptyStream) { - emptyStream = sk_make_sp( - std::unique_ptr( - new SkMemoryStream((size_t)0))); + auto pimg = to_image(gID, cache.get()); + if (!pimg.fImage) { + if (!emptyStream) { + emptyStream = sk_make_sp( + std::unique_ptr( + new SkMemoryStream((size_t)0))); + } + charProcs->insertObjRef(characterName, emptyStream); + } else { + SkDynamicMemoryWStream content; + SkPDFUtils::AppendScalar(SkFloatToScalar(glyph.fAdvanceX), &content); + content.writeText(" 0 d0\n"); + content.writeDecAsText(pimg.fImage->width()); + content.writeText(" 0 0 "); + content.writeDecAsText(-pimg.fImage->height()); + content.writeText(" "); + content.writeDecAsText(pimg.fOffset.x()); + content.writeText(" "); + content.writeDecAsText(pimg.fImage->height() + pimg.fOffset.y()); + content.writeText(" cm\n"); + content.writeText("/X Do\n"); + auto proc = sk_make_sp(content.detachAsStream()); + auto d0 = sk_make_sp(); + d0->insertObjRef("X", SkPDFCreateBitmapObject(std::move(pimg.fImage))); + auto d1 = sk_make_sp(); + d1->insertObject("XObject", std::move(d0)); + proc->dict()->insertObject("Resources", std::move(d1)); + charProcs->insertObjRef(characterName, std::move(proc)); } - charProcs->insertObjRef(characterName, emptyStream); } } encDiffs->appendName(characterName.c_str()); diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h index 3305213657..5efcdaf4bd 100644 --- a/src/pdf/SkPDFFont.h +++ b/src/pdf/SkPDFFont.h @@ -11,13 +11,11 @@ #include "SkAdvancedTypefaceMetrics.h" #include "SkBitSet.h" -#include "SkStrikeCache.h" +#include "SkPDFCanon.h" #include "SkPDFTypes.h" +#include "SkStrikeCache.h" #include "SkTypeface.h" -class SkPDFCanon; -class SkPDFFont; - /** \class SkPDFFont A PDF Object class representing a font. The font may have resources attached to it in order to embed the font. SkPDFFonts are canonicalized @@ -84,6 +82,7 @@ public: * @param glyphID Specify which section of a large font is of interest. */ static sk_sp GetFontResource(SkPDFCanon* canon, + SkGlyphCache* cache, SkTypeface* typeface, SkGlyphID glyphID);