SkPDF: in-place font subsetting

Motivation: gross code simplification, also no bitset lookups at draw time.

SkPDFFont owns its glyph useage bitset.

SkPDFSubstituteMap goes away.

SkPDFObject interface is simplified.

SkPDFDocument tracks font usage (as hash set), not glyph usage.

SkPDFFont gets a simpler constructor.

SkPDFFont has first and last glyph set in constructor, not adjusted later.

SkPDFFont implementations are simplified.

SkPDFGlyphSet is replaced with simple SkBitSet.

SkPDFFont sizes its SkBitSets based on glyph count.

SkPDFGlyphSetMap goes away.

SkBitSet is now non-copyable.

SkBitSet now how utility methods to match old SkPDFGlyphSet.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2253283004

CQ_INCLUDE_TRYBOTS=master.client.skia:Test-Win-MSVC-GCE-CPU-AVX2-x86_64-Release-GDI-Trybot,Test-Win-MSVC-GCE-CPU-AVX2-x86_64-Debug-GDI-Trybot

Review-Url: https://codereview.chromium.org/2253283004
This commit is contained in:
halcanary 2016-08-18 14:22:52 -07:00 committed by Commit bot
parent 9637ea91b8
commit 530032a18e
20 changed files with 222 additions and 506 deletions

View File

@ -30,14 +30,13 @@ struct NullWStream : public SkWStream {
static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) { static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) {
// SkDebugWStream wStream; // SkDebugWStream wStream;
NullWStream wStream; NullWStream wStream;
SkPDFSubstituteMap substitutes;
SkPDFObjNumMap objNumMap; SkPDFObjNumMap objNumMap;
objNumMap.addObjectRecursively(object.get(), substitutes); objNumMap.addObjectRecursively(object.get());
for (int i = 0; i < objNumMap.objects().count(); ++i) { for (int i = 0; i < objNumMap.objects().count(); ++i) {
SkPDFObject* object = objNumMap.objects()[i].get(); SkPDFObject* object = objNumMap.objects()[i].get();
wStream.writeDecAsText(i + 1); wStream.writeDecAsText(i + 1);
wStream.writeText(" 0 obj\n"); wStream.writeText(" 0 obj\n");
object->emitObject(&wStream, objNumMap, substitutes); object->emitObject(&wStream, objNumMap);
wStream.writeText("\nendobj\n"); wStream.writeText("\nendobj\n");
} }
} }

View File

@ -346,8 +346,7 @@ static void emit_image_xobject(SkWStream* stream,
const SkImage* image, const SkImage* image,
bool alpha, bool alpha,
const sk_sp<SkPDFObject>& smask, const sk_sp<SkPDFObject>& smask,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) {
const SkPDFSubstituteMap& substitutes) {
SkBitmap bitmap; SkBitmap bitmap;
image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
SkAutoLockPixels autoLockPixels(bitmap); // with malformed images. SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
@ -385,7 +384,7 @@ static void emit_image_xobject(SkWStream* stream,
pdfDict.insertInt("BitsPerComponent", 8); pdfDict.insertInt("BitsPerComponent", 8);
pdfDict.insertName("Filter", "FlateDecode"); pdfDict.insertName("Filter", "FlateDecode");
pdfDict.insertInt("Length", asset->getLength()); pdfDict.insertInt("Length", asset->getLength());
pdfDict.emitObject(stream, objNumMap, substitutes); pdfDict.emitObject(stream, objNumMap);
pdf_stream_begin(stream); pdf_stream_begin(stream);
stream->writeStream(asset.get(), asset->getLength()); stream->writeStream(asset.get(), asset->getLength());
@ -400,10 +399,9 @@ class PDFAlphaBitmap final : public SkPDFObject {
public: public:
PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); } PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const override {
const SkPDFSubstituteMap& subs) const override {
SkASSERT(fImage); SkASSERT(fImage);
emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap, subs); emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap);
} }
void drop() override { fImage = nullptr; } void drop() override { fImage = nullptr; }
@ -419,19 +417,12 @@ namespace {
class PDFDefaultBitmap final : public SkPDFObject { class PDFDefaultBitmap final : public SkPDFObject {
public: public:
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const override {
const SkPDFSubstituteMap& subs) const override {
SkASSERT(fImage); SkASSERT(fImage);
emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap, subs); emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap);
} }
void addResources(SkPDFObjNumMap* catalog, void addResources(SkPDFObjNumMap* catalog) const override {
const SkPDFSubstituteMap& subs) const override { catalog->addObjectRecursively(fSMask.get());
SkASSERT(fImage);
if (fSMask.get()) {
SkPDFObject* obj = subs.getSubstitute(fSMask.get());
SkASSERT(obj);
catalog->addObjectRecursively(obj, subs);
}
} }
void drop() override { fImage = nullptr; fSMask = nullptr; } void drop() override { fImage = nullptr; fSMask = nullptr; }
PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask) PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
@ -458,15 +449,12 @@ public:
bool fIsYUV; bool fIsYUV;
PDFJpegBitmap(SkISize size, SkData* data, bool isYUV) PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
: fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); } : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
void emitObject(SkWStream*, void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
const SkPDFObjNumMap&,
const SkPDFSubstituteMap&) const override;
void drop() override { fData = nullptr; } void drop() override { fData = nullptr; }
}; };
void PDFJpegBitmap::emitObject(SkWStream* stream, void PDFJpegBitmap::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substituteMap) const {
SkASSERT(fData); SkASSERT(fData);
SkPDFDict pdfDict("XObject"); SkPDFDict pdfDict("XObject");
pdfDict.insertName("Subtype", "Image"); pdfDict.insertName("Subtype", "Image");
@ -481,7 +469,7 @@ void PDFJpegBitmap::emitObject(SkWStream* stream,
pdfDict.insertName("Filter", "DCTDecode"); pdfDict.insertName("Filter", "DCTDecode");
pdfDict.insertInt("ColorTransform", 0); pdfDict.insertInt("ColorTransform", 0);
pdfDict.insertInt("Length", SkToInt(fData->size())); pdfDict.insertInt("Length", SkToInt(fData->size()));
pdfDict.emitObject(stream, objNumMap, substituteMap); pdfDict.emitObject(stream, objNumMap);
pdf_stream_begin(stream); pdf_stream_begin(stream);
stream->write(fData->data(), fData->size()); stream->write(fData->data(), fData->size());
pdf_stream_end(stream); pdf_stream_end(stream);

View File

@ -1153,7 +1153,6 @@ void SkPDFDevice::internalDrawText(
font->multiByteGlyphs(), font->multiByteGlyphs(),
defaultPositioning, defaultPositioning,
offset); offset);
SkPDFGlyphSetMap* fontGlyphUsage = fDocument->getGlyphUsage();
const SkGlyphID* const glyphsEnd = glyphs + glyphCount; const SkGlyphID* const glyphsEnd = glyphs + glyphCount;
while (glyphs < glyphsEnd) { while (glyphs < glyphsEnd) {
@ -1184,7 +1183,7 @@ void SkPDFDevice::internalDrawText(
return; return;
} }
} }
fontGlyphUsage->noteGlyphUsage(font, glyphs, stretch); font->noteGlyphUsage(glyphs, stretch);
if (defaultPositioning) { if (defaultPositioning) {
(void)font->glyphsToPDFFontEncoding(glyphs, SkToInt(glyphsEnd - glyphs)); (void)font->glyphsToPDFFontEncoding(glyphs, SkToInt(glyphsEnd - glyphs));
while (stretch-- > 0) { while (stretch-- > 0) {
@ -1318,10 +1317,6 @@ sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() const {
&fonts); &fonts);
} }
const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
return fFontResources;
}
sk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const { sk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const {
auto mediaBox = sk_make_sp<SkPDFArray>(); auto mediaBox = sk_make_sp<SkPDFArray>();
mediaBox->reserve(4); mediaBox->reserve(4);
@ -1948,9 +1943,9 @@ int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
} }
int resourceIndex = fFontResources.find(newFont.get()); int resourceIndex = fFontResources.find(newFont.get());
if (resourceIndex < 0) { if (resourceIndex < 0) {
fDocument->registerFont(newFont.get());
resourceIndex = fFontResources.count(); resourceIndex = fFontResources.count();
fFontResources.push(newFont.get()); fFontResources.push(newFont.release());
newFont.get()->ref();
} }
return resourceIndex; return resourceIndex;
} }

View File

@ -126,10 +126,6 @@ public:
/** Create the resource dictionary for this device. */ /** Create the resource dictionary for this device. */
sk_sp<SkPDFDict> makeResourceDict() const; sk_sp<SkPDFDict> makeResourceDict() const;
/** Get the fonts used on this device.
*/
const SkTDArray<SkPDFFont*>& getFontResources() const;
/** Add our annotations (link to urls and destinations) to the supplied /** Add our annotations (link to urls and destinations) to the supplied
* array. * array.
* @param array Array to add annotations to. * @param array Array to add annotations to.

View File

@ -23,7 +23,7 @@ SkPDFObjectSerializer::~SkPDFObjectSerializer() {
} }
void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) { void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) {
fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap); fObjNumMap.addObjectRecursively(object.get());
} }
#define SKPDF_MAGIC "\xD3\xEB\xE9\xE1" #define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
@ -58,10 +58,9 @@ void SkPDFObjectSerializer::serializeObjects(SkWStream* wStream) {
// the head of the linked list of free objects." // the head of the linked list of free objects."
SkASSERT(fOffsets.count() == fNextToBeSerialized); SkASSERT(fOffsets.count() == fNextToBeSerialized);
fOffsets.push(this->offset(wStream)); fOffsets.push(this->offset(wStream));
SkASSERT(object == fSubstituteMap.getSubstitute(object));
wStream->writeDecAsText(index); wStream->writeDecAsText(index);
wStream->writeText(" 0 obj\n"); // Generation number is always 0. wStream->writeText(" 0 obj\n"); // Generation number is always 0.
object->emitObject(wStream, fObjNumMap, fSubstituteMap); object->emitObject(wStream, fObjNumMap);
wStream->writeText("\nendobj\n"); wStream->writeText("\nendobj\n");
object->drop(); object->drop();
++fNextToBeSerialized; ++fNextToBeSerialized;
@ -93,7 +92,7 @@ void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
trailerDict.insertObject("ID", std::move(id)); trailerDict.insertObject("ID", std::move(id));
} }
wStream->writeText("trailer\n"); wStream->writeText("trailer\n");
trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap); trailerDict.emitObject(wStream, fObjNumMap);
wStream->writeText("\nstartxref\n"); wStream->writeText("\nstartxref\n");
wStream->writeBigDecAsText(xRefFileOffset); wStream->writeBigDecAsText(xRefFileOffset);
wStream->writeText("\n%%EOF"); wStream->writeText("\n%%EOF");
@ -246,11 +245,15 @@ void SkPDFDocument::onEndPage() {
} }
void SkPDFDocument::onAbort() { void SkPDFDocument::onAbort() {
this->reset();
}
void SkPDFDocument::reset() {
fCanvas.reset(nullptr); fCanvas.reset(nullptr);
fPages.reset(); fPages.reset();
fCanon.reset(); fCanon.reset();
renew(&fObjectSerializer); renew(&fObjectSerializer);
renew(&fGlyphUsage); fFonts.reset();
} }
#ifdef SK_SUPPORT_LEGACY_DOCUMENT_API #ifdef SK_SUPPORT_LEGACY_DOCUMENT_API
@ -419,10 +422,7 @@ static sk_sp<SkPDFArray> make_srgb_output_intents() {
bool SkPDFDocument::onClose(SkWStream* stream) { bool SkPDFDocument::onClose(SkWStream* stream) {
SkASSERT(!fCanvas.get()); SkASSERT(!fCanvas.get());
if (fPages.empty()) { if (fPages.empty()) {
fPages.reset(); this->reset();
fCanon.reset();
renew(&fObjectSerializer);
renew(&fGlyphUsage);
return false; return false;
} }
auto docCatalog = sk_make_sp<SkPDFDict>("Catalog"); auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
@ -442,21 +442,12 @@ bool SkPDFDocument::onClose(SkWStream* stream) {
} }
// Build font subsetting info before calling addObjectRecursively(). // Build font subsetting info before calling addObjectRecursively().
for (const auto& entry : fGlyphUsage) { SkPDFCanon* canon = &fCanon;
sk_sp<SkPDFObject> subsetFont = fFonts.foreach([canon](SkPDFFont* p){ p->getFontSubset(canon); });
entry.fFont->getFontSubset(&fCanon, &entry.fGlyphSet);
if (subsetFont) {
fObjectSerializer.fSubstituteMap.setSubstitute(
entry.fFont, subsetFont.get());
}
}
fObjectSerializer.addObjectRecursively(docCatalog); fObjectSerializer.addObjectRecursively(docCatalog);
fObjectSerializer.serializeObjects(this->getStream()); fObjectSerializer.serializeObjects(this->getStream());
fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID); fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
fCanon.reset(); this->reset();
renew(&fObjectSerializer);
renew(&fGlyphUsage);
return true; return true;
} }

View File

@ -25,7 +25,6 @@ sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
// keep similar functionality together. // keep similar functionality together.
struct SkPDFObjectSerializer : SkNoncopyable { struct SkPDFObjectSerializer : SkNoncopyable {
SkPDFObjNumMap fObjNumMap; SkPDFObjNumMap fObjNumMap;
SkPDFSubstituteMap fSubstituteMap;
SkTDArray<int32_t> fOffsets; SkTDArray<int32_t> fOffsets;
sk_sp<SkPDFObject> fInfoDict; sk_sp<SkPDFObject> fInfoDict;
size_t fBaseOffset; size_t fBaseOffset;
@ -70,18 +69,16 @@ public:
It might go without saying that objects should not be changed It might go without saying that objects should not be changed
after calling serialize, since those changes will be too late. after calling serialize, since those changes will be too late.
The same goes for changes to the SkPDFSubstituteMap that effect
the object or its dependencies.
*/ */
void serialize(const sk_sp<SkPDFObject>&); void serialize(const sk_sp<SkPDFObject>&);
SkPDFCanon* canon() { return &fCanon; } SkPDFCanon* canon() { return &fCanon; }
SkPDFGlyphSetMap* getGlyphUsage() { return &fGlyphUsage; } void registerFont(SkPDFFont* f) { fFonts.add(f); }
private: private:
SkPDFObjectSerializer fObjectSerializer; SkPDFObjectSerializer fObjectSerializer;
SkPDFCanon fCanon; SkPDFCanon fCanon;
SkPDFGlyphSetMap fGlyphUsage;
SkTArray<sk_sp<SkPDFDict>> fPages; SkTArray<sk_sp<SkPDFDict>> fPages;
SkTHashSet<SkPDFFont*> fFonts;
sk_sp<SkPDFDict> fDests; sk_sp<SkPDFDict> fDests;
sk_sp<SkPDFDevice> fPageDevice; sk_sp<SkPDFDevice> fPageDevice;
sk_sp<SkCanvas> fCanvas; sk_sp<SkCanvas> fCanvas;
@ -90,6 +87,8 @@ private:
SkScalar fRasterDpi; SkScalar fRasterDpi;
SkDocument::PDFMetadata fMetadata; SkDocument::PDFMetadata fMetadata;
bool fPDFA; bool fPDFA;
void reset();
}; };
#endif // SkPDFDocument_DEFINED #endif // SkPDFDocument_DEFINED

View File

@ -37,50 +37,27 @@ namespace {
// non-symbolic, so always call it symbolic. (PDF 1.4 spec, section 5.7.1) // non-symbolic, so always call it symbolic. (PDF 1.4 spec, section 5.7.1)
static const int kPdfSymbolic = 4; static const int kPdfSymbolic = 4;
class SkPDFType0Font final : public SkPDFFont { struct SkPDFType0Font final : public SkPDFFont {
public: SkPDFType0Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
SkPDFType0Font(const SkAdvancedTypefaceMetrics& info,
sk_sp<SkTypeface> typeface,
SkAdvancedTypefaceMetrics::FontType type);
virtual ~SkPDFType0Font(); virtual ~SkPDFType0Font();
sk_sp<SkPDFObject> getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) override; void getFontSubset(SkPDFCanon*) override;
#ifdef SK_DEBUG
void emitObject(SkWStream*,
const SkPDFObjNumMap&,
const SkPDFSubstituteMap&) const override;
#endif
private:
#ifdef SK_DEBUG #ifdef SK_DEBUG
void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
bool fPopulated; bool fPopulated;
#endif #endif
bool populate(const SkPDFGlyphSet* subset,
const SkAdvancedTypefaceMetrics& metrics);
typedef SkPDFDict INHERITED; typedef SkPDFDict INHERITED;
}; };
class SkPDFType1Font final : public SkPDFFont { struct SkPDFType1Font final : public SkPDFFont {
public: SkPDFType1Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&, SkPDFCanon*);
SkPDFType1Font(const SkAdvancedTypefaceMetrics& info,
sk_sp<SkTypeface> typeface,
uint16_t glyphID,
SkPDFCanon* canon);
virtual ~SkPDFType1Font() {} virtual ~SkPDFType1Font() {}
void getFontSubset(SkPDFCanon*) override {} // TODO(halcanary): implement
}; };
class SkPDFType3Font final : public SkPDFFont { struct SkPDFType3Font final : public SkPDFFont {
public: SkPDFType3Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
SkPDFType3Font(const SkAdvancedTypefaceMetrics& info,
sk_sp<SkTypeface> typeface,
SkAdvancedTypefaceMetrics::FontType fontType,
uint16_t glyphID);
virtual ~SkPDFType3Font() {} virtual ~SkPDFType3Font() {}
void emitObject(SkWStream*, void getFontSubset(SkPDFCanon*) override;
const SkPDFObjNumMap&,
const SkPDFSubstituteMap&) const override {
SkDEBUGFAIL("should call getFontSubset!");
}
sk_sp<SkPDFObject> getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) override;
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -143,58 +120,6 @@ static sk_sp<SkPDFArray> makeFontBBox(SkIRect glyphBBox, uint16_t emSize) {
} }
} // namespace } // namespace
///////////////////////////////////////////////////////////////////////////////
// class SkPDFGlyphSet
///////////////////////////////////////////////////////////////////////////////
SkPDFGlyphSet::SkPDFGlyphSet() : fBitSet(SK_MaxU16 + 1) {
}
void SkPDFGlyphSet::set(const uint16_t* glyphIDs, int numGlyphs) {
for (int i = 0; i < numGlyphs; ++i) {
fBitSet.setBit(glyphIDs[i], true);
}
}
bool SkPDFGlyphSet::has(uint16_t glyphID) const {
return fBitSet.isBitSet(glyphID);
}
void SkPDFGlyphSet::exportTo(SkTDArray<unsigned int>* glyphIDs) const {
fBitSet.exportTo(glyphIDs);
}
///////////////////////////////////////////////////////////////////////////////
// class SkPDFGlyphSetMap
///////////////////////////////////////////////////////////////////////////////
SkPDFGlyphSetMap::SkPDFGlyphSetMap() {}
SkPDFGlyphSetMap::~SkPDFGlyphSetMap() {
fMap.reset();
}
void SkPDFGlyphSetMap::noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
int numGlyphs) {
SkPDFGlyphSet* subset = getGlyphSetForFont(font);
if (subset) {
subset->set(glyphIDs, numGlyphs);
}
}
SkPDFGlyphSet* SkPDFGlyphSetMap::getGlyphSetForFont(SkPDFFont* font) {
int index = fMap.count();
for (int i = 0; i < index; ++i) {
if (fMap[i].fFont == font) {
return &fMap[i].fGlyphSet;
}
}
FontGlyphSetPair& pair = fMap.push_back();
pair.fFont = font;
return &pair.fGlyphSet;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// class SkPDFFont // class SkPDFFont
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -322,47 +247,46 @@ SkPDFFont* SkPDFFont::GetFontResource(SkPDFCanon* canon,
return nullptr; return nullptr;
} }
SkGlyphID firstNonZeroGlyph;
SkGlyphID lastGlyph;
if (multibyte) {
firstNonZeroGlyph = 1;
lastGlyph = SkToU16(glyphCount - 1);
} else {
firstNonZeroGlyph = firstGlyph;
lastGlyph = SkToU16(SkTMin<int>(glyphCount - 1,
254 + (int)firstGlyph));
}
SkPDFFont::Info info = {std::move(typeface), firstNonZeroGlyph, lastGlyph, type};
sk_sp<SkPDFFont> font; sk_sp<SkPDFFont> font;
switch (type) { switch (type) {
case SkAdvancedTypefaceMetrics::kType1CID_Font: case SkAdvancedTypefaceMetrics::kType1CID_Font:
case SkAdvancedTypefaceMetrics::kTrueType_Font: case SkAdvancedTypefaceMetrics::kTrueType_Font:
SkASSERT(multibyte); SkASSERT(multibyte);
font = sk_make_sp<SkPDFType0Font>(metrics, font = sk_make_sp<SkPDFType0Font>(std::move(info), metrics);
std::move(typeface),
type);
break; break;
case SkAdvancedTypefaceMetrics::kType1_Font: case SkAdvancedTypefaceMetrics::kType1_Font:
SkASSERT(!multibyte); SkASSERT(!multibyte);
font = sk_make_sp<SkPDFType1Font>(metrics, font = sk_make_sp<SkPDFType1Font>(std::move(info), metrics, canon);
std::move(typeface),
glyphID,
canon);
break; break;
default: default:
SkASSERT(!multibyte); SkASSERT(!multibyte);
// Type3 is our fallback font. // Type3 is our fallback font.
font = sk_make_sp<SkPDFType3Font>(metrics, font = sk_make_sp<SkPDFType3Font>(std::move(info), metrics);
std::move(typeface),
type,
glyphID);
break; break;
} }
canon->fFontMap.set(fontID, SkRef(font.get())); canon->fFontMap.set(fontID, SkRef(font.get()));
return font.release(); // TODO(halcanary) return sk_sp<SkPDFFont>. return font.release(); // TODO(halcanary) return sk_sp<SkPDFFont>.
} }
sk_sp<SkPDFObject> SkPDFFont::getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) { SkPDFFont::SkPDFFont(SkPDFFont::Info info)
return nullptr; // Default: no support.
}
SkPDFFont::SkPDFFont(sk_sp<SkTypeface> typeface,
SkAdvancedTypefaceMetrics::FontType fontType)
: SkPDFDict("Font") : SkPDFDict("Font")
, fTypeface(std::move(typeface)) , fTypeface(std::move(info.fTypeface))
, fFirstGlyphID(1) , fGlyphUsage(info.fLastGlyphID + 1) // TODO(halcanary): Adjust mapping?
, fFontType(fontType) { , fFirstGlyphID(info.fFirstGlyphID)
, fLastGlyphID(info.fLastGlyphID)
, fFontType(info.fFontType) {
SkASSERT(fTypeface); SkASSERT(fTypeface);
fLastGlyphID = SkToU16(fTypeface->countGlyphs() - 1);
} }
static void add_common_font_descriptor_entries(SkPDFDict* descriptor, static void add_common_font_descriptor_entries(SkPDFDict* descriptor,
@ -388,50 +312,25 @@ static void add_common_font_descriptor_entries(SkPDFDict* descriptor,
} }
} }
void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(SkGlyphID glyphID) {
// Single byte glyph encoding supports a max of 255 glyphs.
fFirstGlyphID = first_glyph_for_single_byte_encoding(glyphID);
if (fLastGlyphID > fFirstGlyphID + 255 - 1) {
fLastGlyphID = fFirstGlyphID + 255 - 1;
}
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// class SkPDFType0Font // class SkPDFType0Font
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
SkPDFType0Font::SkPDFType0Font(const SkAdvancedTypefaceMetrics& info, SkPDFType0Font::SkPDFType0Font(
sk_sp<SkTypeface> typeface, SkPDFFont::Info info,
SkAdvancedTypefaceMetrics::FontType fontType) const SkAdvancedTypefaceMetrics& metrics)
: SkPDFFont(std::move(typeface), fontType) { : SkPDFFont(std::move(info)) {
SkDEBUGCODE(fPopulated = false); SkDEBUGCODE(fPopulated = false);
if (!can_subset(info)) {
this->populate(nullptr, info);
}
} }
SkPDFType0Font::~SkPDFType0Font() {} SkPDFType0Font::~SkPDFType0Font() {}
sk_sp<SkPDFObject> SkPDFType0Font::getFontSubset(SkPDFCanon* canon,
const SkPDFGlyphSet* subset) {
const SkAdvancedTypefaceMetrics* metrics =
SkPDFFont::GetMetrics(this->typeface(), canon);
SkASSERT(metrics);
if (!metrics || !can_subset(*metrics)) {
return nullptr;
}
auto newSubset = sk_make_sp<SkPDFType0Font>(
*metrics, this->refTypeface(), this->getType());
newSubset->populate(subset, *metrics);
return newSubset;
}
#ifdef SK_DEBUG #ifdef SK_DEBUG
void SkPDFType0Font::emitObject(SkWStream* stream, void SkPDFType0Font::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(fPopulated); SkASSERT(fPopulated);
return INHERITED::emitObject(stream, objNumMap, substitutes); return INHERITED::emitObject(stream, objNumMap);
} }
#endif #endif
@ -488,8 +387,12 @@ static sk_sp<SkPDFObject> get_subset_font_stream(
} }
#endif // SK_SFNTLY_SUBSETTER #endif // SK_SFNTLY_SUBSETTER
bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset, void SkPDFType0Font::getFontSubset(SkPDFCanon* canon) {
const SkAdvancedTypefaceMetrics& metrics) { const SkAdvancedTypefaceMetrics* metricsPtr =
SkPDFFont::GetMetrics(this->typeface(), canon);
SkASSERT(metricsPtr);
if (!metricsPtr) { return; }
const SkAdvancedTypefaceMetrics& metrics = *metricsPtr;
SkASSERT(can_embed(metrics)); SkASSERT(can_embed(metrics));
SkAdvancedTypefaceMetrics::FontType type = this->getType(); SkAdvancedTypefaceMetrics::FontType type = this->getType();
SkTypeface* face = this->typeface(); SkTypeface* face = this->typeface();
@ -504,24 +407,22 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
std::unique_ptr<SkStreamAsset> fontAsset(face->openStream(&ttcIndex)); std::unique_ptr<SkStreamAsset> fontAsset(face->openStream(&ttcIndex));
SkASSERT(fontAsset); SkASSERT(fontAsset);
if (!fontAsset) { if (!fontAsset) {
return false; return;
} }
size_t fontSize = fontAsset->getLength(); size_t fontSize = fontAsset->getLength();
SkASSERT(fontSize > 0); SkASSERT(fontSize > 0);
if (fontSize == 0) { if (fontSize == 0) {
return false; return;
} }
#ifdef SK_SFNTLY_SUBSETTER #ifdef SK_SFNTLY_SUBSETTER
if (can_subset(metrics) && subset) { if (can_subset(metrics)) {
// Generate glyph id array. in format needed by sfntly // Generate glyph id array. in format needed by sfntly
SkTDArray<uint32_t> glyphIDs; SkTDArray<uint32_t> glyphIDs;
if (subset) { if (!this->glyphUsage().has(0)) {
if (!subset->has(0)) { glyphIDs.push(0); // Always include glyph 0.
glyphIDs.push(0); // Always include glyph 0.
}
subset->exportTo(&glyphIDs);
} }
this->glyphUsage().exportTo(&glyphIDs);
sk_sp<SkPDFObject> subsetStream = get_subset_font_stream( sk_sp<SkPDFObject> subsetStream = get_subset_font_stream(
std::move(fontAsset), glyphIDs, name.c_str()); std::move(fontAsset), glyphIDs, name.c_str());
if (subsetStream) { if (subsetStream) {
@ -542,7 +443,7 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
SkASSERT(fontData); SkASSERT(fontData);
SkASSERT(fontData->getLength() > 0); SkASSERT(fontData->getLength() > 0);
if (!fontData || 0 == fontData->getLength()) { if (!fontData || 0 == fontData->getLength()) {
return false; return;
} }
auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontData)); auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontData));
fontStream->dict()->insertName("Subtype", "CIDFontType0c"); fontStream->dict()->insertName("Subtype", "CIDFontType0c");
@ -574,11 +475,10 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
uint16_t emSize = metrics.fEmSize; uint16_t emSize = metrics.fEmSize;
int16_t defaultWidth = 0; int16_t defaultWidth = 0;
const SkBitSet* bitSet = subset ? &subset->bitSet() : nullptr;
{ {
SkAutoGlyphCache glyphCache = vector_cache(face); SkAutoGlyphCache glyphCache = vector_cache(face);
sk_sp<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray( sk_sp<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray(
glyphCache.get(), bitSet, emSize, &defaultWidth); glyphCache.get(), &this->glyphUsage(), emSize, &defaultWidth);
if (widths && widths->size() > 0) { if (widths && widths->size() > 0) {
newCIDFont->insertObject("W", std::move(widths)); newCIDFont->insertObject("W", std::move(widths));
} }
@ -598,13 +498,13 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
if (metrics.fGlyphToUnicode.count() > 0) { if (metrics.fGlyphToUnicode.count() > 0) {
this->insertObjRef("ToUnicode", this->insertObjRef("ToUnicode",
SkPDFMakeToUnicodeCmap(metrics.fGlyphToUnicode, SkPDFMakeToUnicodeCmap(metrics.fGlyphToUnicode,
subset, &this->glyphUsage(),
multiByteGlyphs(), multiByteGlyphs(),
firstGlyphID(), firstGlyphID(),
lastGlyphID())); lastGlyphID()));
} }
SkDEBUGCODE(fPopulated = true); SkDEBUGCODE(fPopulated = true);
return true; return;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -679,24 +579,22 @@ static void populate_type_1_font(SkPDFDict* font,
font->insertObject("Encoding", std::move(encoding)); font->insertObject("Encoding", std::move(encoding));
} }
SkPDFType1Font::SkPDFType1Font(const SkAdvancedTypefaceMetrics& info, SkPDFType1Font::SkPDFType1Font(SkPDFFont::Info info,
sk_sp<SkTypeface> typeface, const SkAdvancedTypefaceMetrics& metrics,
uint16_t glyphID,
SkPDFCanon* canon) SkPDFCanon* canon)
: SkPDFFont(std::move(typeface), SkAdvancedTypefaceMetrics::kType1_Font) : SkPDFFont(std::move(info))
{ {
SkFontID fontID = this->typeface()->uniqueID(); SkFontID fontID = this->typeface()->uniqueID();
sk_sp<SkPDFDict> fontDescriptor; sk_sp<SkPDFDict> fontDescriptor;
if (SkPDFDict** ptr = canon->fFontDescriptors.find(fontID)) { if (SkPDFDict** ptr = canon->fFontDescriptors.find(fontID)) {
fontDescriptor = sk_ref_sp(*ptr); fontDescriptor = sk_ref_sp(*ptr);
} else { } else {
fontDescriptor = make_type1_font_descriptor(this->typeface(), info); fontDescriptor = make_type1_font_descriptor(this->typeface(), metrics);
canon->fFontDescriptors.set(fontID, SkRef(fontDescriptor.get())); canon->fFontDescriptors.set(fontID, SkRef(fontDescriptor.get()));
} }
this->insertObjRef("FontDescriptor", std::move(fontDescriptor)); this->insertObjRef("FontDescriptor", std::move(fontDescriptor));
this->adjustGlyphRangeForSingleByteEncoding(glyphID);
// TODO(halcanary): subset this (advances and names). // TODO(halcanary): subset this (advances and names).
populate_type_1_font(this, info, this->typeface(), populate_type_1_font(this, metrics, this->typeface(),
this->firstGlyphID(), this->lastGlyphID()); this->firstGlyphID(), this->lastGlyphID());
} }
@ -738,7 +636,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
SkPDFDict* font, SkPDFDict* font,
SkTypeface* typeface, SkTypeface* typeface,
SkScalar emSize, SkScalar emSize,
const SkPDFGlyphSet* subset, const SkBitSet& subset,
SkGlyphID firstGlyphID, SkGlyphID firstGlyphID,
SkGlyphID lastGlyphID) { SkGlyphID lastGlyphID) {
SkASSERT(lastGlyphID >= firstGlyphID); SkASSERT(lastGlyphID >= firstGlyphID);
@ -770,7 +668,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
sk_sp<SkPDFStream> emptyStream; sk_sp<SkPDFStream> emptyStream;
for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) { for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) {
bool skipGlyph = subset && gID != 0 && !subset->has(gID); bool skipGlyph = gID != 0 && !subset.has(gID);
SkString characterName; SkString characterName;
SkScalar advance = 0.0f; SkScalar advance = 0.0f;
SkIRect glyphBBox; SkIRect glyphBBox;
@ -827,7 +725,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
if (metrics /* && metrics->fGlyphToUnicode.count() > 0 */) { if (metrics /* && metrics->fGlyphToUnicode.count() > 0 */) {
font->insertObjRef("ToUnicode", font->insertObjRef("ToUnicode",
SkPDFMakeToUnicodeCmap(metrics->fGlyphToUnicode, SkPDFMakeToUnicodeCmap(metrics->fGlyphToUnicode,
subset, &subset,
false, false,
firstGlyphID, firstGlyphID,
lastGlyphID)); lastGlyphID));
@ -837,29 +735,20 @@ static void add_type3_font_info(SkPDFCanon* canon,
font->insertObject("CharProcs", std::move(charProcs)); font->insertObject("CharProcs", std::move(charProcs));
} }
SkPDFType3Font::SkPDFType3Font(const SkAdvancedTypefaceMetrics& info, SkPDFType3Font::SkPDFType3Font(SkPDFFont::Info info,
sk_sp<SkTypeface> typeface, const SkAdvancedTypefaceMetrics& metrics)
SkAdvancedTypefaceMetrics::FontType fontType, : SkPDFFont(std::move(info)) {}
uint16_t glyphID)
: SkPDFFont(std::move(typeface), fontType) {
this->adjustGlyphRangeForSingleByteEncoding(glyphID);
}
sk_sp<SkPDFObject> SkPDFType3Font::getFontSubset(SkPDFCanon* canon, void SkPDFType3Font::getFontSubset(SkPDFCanon* canon) {
const SkPDFGlyphSet* usage) {
// All fonts are subset before serialization.
// TODO(halcanary): all fonts should follow this pattern.
const SkAdvancedTypefaceMetrics* info = const SkAdvancedTypefaceMetrics* info =
SkPDFFont::GetMetrics(this->typeface(), canon); SkPDFFont::GetMetrics(this->typeface(), canon);
SkASSERT(info); SkASSERT(info);
uint16_t emSize = info->fEmSize > 0 ? info->fEmSize : 1000; uint16_t emSize = info->fEmSize > 0 ? info->fEmSize : 1000;
auto font = sk_make_sp<SkPDFDict>("Font"); add_type3_font_info(canon, this, this->typeface(), (SkScalar)emSize,
add_type3_font_info(canon, font.get(), this->typeface(), (SkScalar)emSize, usage, this->glyphUsage(),
this->firstGlyphID(), this->lastGlyphID()); this->firstGlyphID(), this->lastGlyphID());
return font;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool SkPDFFont::CanEmbedTypeface(SkTypeface* typeface, SkPDFCanon* canon) { bool SkPDFFont::CanEmbedTypeface(SkTypeface* typeface, SkPDFCanon* canon) {

View File

@ -18,48 +18,6 @@
class SkPDFCanon; class SkPDFCanon;
class SkPDFFont; class SkPDFFont;
class SkPDFGlyphSet : SkNoncopyable {
public:
SkPDFGlyphSet();
SkPDFGlyphSet(SkPDFGlyphSet&& o) : fBitSet(std::move(o.fBitSet)) {}
void set(const uint16_t* glyphIDs, int numGlyphs);
bool has(uint16_t glyphID) const;
void exportTo(SkTDArray<uint32_t>* glyphIDs) const;
const SkBitSet& bitSet() const { return fBitSet; }
private:
SkBitSet fBitSet;
};
class SkPDFGlyphSetMap : SkNoncopyable {
public:
struct FontGlyphSetPair : SkNoncopyable {
FontGlyphSetPair() : fFont(nullptr) {}
FontGlyphSetPair(FontGlyphSetPair&& o)
: fFont(o.fFont)
, fGlyphSet(std::move(o.fGlyphSet)) {
o.fFont = nullptr;
}
SkPDFFont* fFont;
SkPDFGlyphSet fGlyphSet;
};
SkPDFGlyphSetMap();
~SkPDFGlyphSetMap();
const FontGlyphSetPair* begin() const { return fMap.begin(); }
const FontGlyphSetPair* end() const { return fMap.end(); }
void noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
int numGlyphs);
private:
SkPDFGlyphSet* getGlyphSetForFont(SkPDFFont* font);
SkTArray<FontGlyphSetPair> fMap;
};
/** \class SkPDFFont /** \class SkPDFFont
A PDF Object class representing a font. The font may have resources A PDF Object class representing a font. The font may have resources
attached to it in order to embed the font. SkPDFFonts are canonicalized attached to it in order to embed the font. SkPDFFonts are canonicalized
@ -112,6 +70,10 @@ public:
int glyphsToPDFFontEncodingCount(const SkGlyphID* glyphIDs, int glyphsToPDFFontEncodingCount(const SkGlyphID* glyphIDs,
int numGlyphs) const; int numGlyphs) const;
void noteGlyphUsage(const SkGlyphID* glyphs, int count) {
fGlyphUsage.setAll(glyphs, count);
}
/** Get the font resource for the passed typeface and glyphID. The /** Get the font resource for the passed typeface and glyphID. The
* reference count of the object is incremented and it is the caller's * reference count of the object is incremented and it is the caller's
* responsibility to unreference it when done. This is needed to * responsibility to unreference it when done. This is needed to
@ -128,14 +90,10 @@ public:
static const SkAdvancedTypefaceMetrics* GetMetrics(SkTypeface* typeface, static const SkAdvancedTypefaceMetrics* GetMetrics(SkTypeface* typeface,
SkPDFCanon* canon); SkPDFCanon* canon);
/** Subset the font based on usage set. Returns a SkPDFFont instance with /** Subset the font based on current usage.
* subset. * Must be called before emitObject().
* @param usage Glyph subset requested.
* @return nullptr if font does not support subsetting, a new instance
* of SkPDFFont otherwise.
*/ */
virtual sk_sp<SkPDFObject> getFontSubset(SkPDFCanon* canon, virtual void getFontSubset(SkPDFCanon*) = 0;
const SkPDFGlyphSet* usage);
/** /**
* Return false iff the typeface has its NotEmbeddable flag set. * Return false iff the typeface has its NotEmbeddable flag set.
@ -145,23 +103,24 @@ public:
protected: protected:
// Common constructor to handle common members. // Common constructor to handle common members.
SkPDFFont(sk_sp<SkTypeface> typeface, struct Info {
SkAdvancedTypefaceMetrics::FontType fontType); sk_sp<SkTypeface> fTypeface;
SkGlyphID fFirstGlyphID;
SkGlyphID fLastGlyphID;
SkAdvancedTypefaceMetrics::FontType fFontType;
};
SkPDFFont(Info);
SkGlyphID firstGlyphID() const { return fFirstGlyphID; } SkGlyphID firstGlyphID() const { return fFirstGlyphID; }
SkGlyphID lastGlyphID() const { return fLastGlyphID; } SkGlyphID lastGlyphID() const { return fLastGlyphID; }
const SkBitSet& glyphUsage() const { return fGlyphUsage; }
sk_sp<SkTypeface> refTypeface() const { return fTypeface; } sk_sp<SkTypeface> refTypeface() const { return fTypeface; }
/** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
* including the passed glyphID.
*/
void adjustGlyphRangeForSingleByteEncoding(SkGlyphID glyphID);
void drop() override; void drop() override;
private: private:
sk_sp<SkTypeface> fTypeface; sk_sp<SkTypeface> fTypeface;
SkBitSet fGlyphUsage;
// The glyph IDs accessible with this font. For Type1 (non CID) fonts, // The glyph IDs accessible with this font. For Type1 (non CID) fonts,
// this will be a subset if the font has more than 255 glyphs. // this will be a subset if the font has more than 255 glyphs.

View File

@ -176,8 +176,7 @@ sk_sp<SkPDFDict> SkPDFGraphicState::MakeNoSmaskGraphicState() {
void SkPDFGraphicState::emitObject( void SkPDFGraphicState::emitObject(
SkWStream* stream, SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
auto dict = sk_make_sp<SkPDFDict>("ExtGState"); auto dict = sk_make_sp<SkPDFDict>("ExtGState");
dict->insertName("Type", "ExtGState"); dict->insertName("Type", "ExtGState");
@ -207,5 +206,5 @@ void SkPDFGraphicState::emitObject(
dict->insertScalar("ML", fStrokeMiter); dict->insertScalar("ML", fStrokeMiter);
dict->insertBool("SA", true); // SA = Auto stroke adjustment. dict->insertBool("SA", true); // SA = Auto stroke adjustment.
dict->insertName("BM", as_blend_mode(xferMode)); dict->insertName("BM", as_blend_mode(xferMode));
dict->emitObject(stream, objNumMap, substitutes); dict->emitObject(stream, objNumMap);
} }

View File

@ -31,8 +31,7 @@ public:
// Override emitObject so that we can populate the dictionary on // Override emitObject so that we can populate the dictionary on
// demand. // demand.
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const override;
const SkPDFSubstituteMap& substitutes) const override;
/** Get the graphic state for the passed SkPaint. The reference count of /** Get the graphic state for the passed SkPaint. The reference count of
* the object is incremented and it is the caller's responsibility to * the object is incremented and it is the caller's responsibility to

View File

@ -149,7 +149,7 @@ static void append_bfrange_section(const SkTDArray<BFRange>& bfrange,
// one of them), the possible savings by aggressive optimization is 416KB // one of them), the possible savings by aggressive optimization is 416KB
// pre-compressed and does not provide enough motivation for implementation. // pre-compressed and does not provide enough motivation for implementation.
void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode, void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
const SkPDFGlyphSet* subset, const SkBitSet* subset,
SkDynamicMemoryWStream* cmap, SkDynamicMemoryWStream* cmap,
bool multiByteGlyphs, bool multiByteGlyphs,
SkGlyphID firstGlyphID, SkGlyphID firstGlyphID,
@ -212,7 +212,7 @@ void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap( sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
const SkTDArray<SkUnichar>& glyphToUnicode, const SkTDArray<SkUnichar>& glyphToUnicode,
const SkPDFGlyphSet* subset, const SkBitSet* subset,
bool multiByteGlyphs, bool multiByteGlyphs,
SkGlyphID firstGlyphID, SkGlyphID firstGlyphID,
SkGlyphID lastGlyphID) { SkGlyphID lastGlyphID) {

View File

@ -13,14 +13,14 @@
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap( sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
const SkTDArray<SkUnichar>& glyphToUnicode, const SkTDArray<SkUnichar>& glyphToUnicode,
const SkPDFGlyphSet* subset, const SkBitSet* subset,
bool multiByteGlyphs, bool multiByteGlyphs,
SkGlyphID firstGlyphID, SkGlyphID firstGlyphID,
SkGlyphID lastGlyphID); SkGlyphID lastGlyphID);
// Exposed for unit testing. // Exposed for unit testing.
void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode, void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
const SkPDFGlyphSet* subset, const SkBitSet* subset,
SkDynamicMemoryWStream* cmap, SkDynamicMemoryWStream* cmap,
bool multiByteGlyphs, bool multiByteGlyphs,
SkGlyphID firstGlyphID, SkGlyphID firstGlyphID,

View File

@ -168,12 +168,11 @@ class PDFXMLObject final : public SkPDFObject {
public: public:
PDFXMLObject(SkString xml) : fXML(std::move(xml)) {} PDFXMLObject(SkString xml) : fXML(std::move(xml)) {}
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& omap, const SkPDFObjNumMap& omap) const override {
const SkPDFSubstituteMap& smap) const override {
SkPDFDict dict("Metadata"); SkPDFDict dict("Metadata");
dict.insertName("Subtype", "XML"); dict.insertName("Subtype", "XML");
dict.insertInt("Length", fXML.size()); dict.insertInt("Length", fXML.size());
dict.emitObject(stream, omap, smap); dict.emitObject(stream, omap);
static const char streamBegin[] = " stream\n"; static const char streamBegin[] = " stream\n";
stream->write(streamBegin, strlen(streamBegin)); stream->write(streamBegin, strlen(streamBegin));
// Do not compress this. The standard requires that a // Do not compress this. The standard requires that a

View File

@ -113,8 +113,7 @@ static void write_name_escaped(SkWStream* o, const char* name) {
} }
void SkPDFUnion::emitObject(SkWStream* stream, void SkPDFUnion::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
switch (fType) { switch (fType) {
case Type::kInt: case Type::kInt:
stream->writeDecAsText(fIntValue); stream->writeDecAsText(fIntValue);
@ -147,20 +146,18 @@ void SkPDFUnion::emitObject(SkWStream* stream,
pun(fSkString)->size()); pun(fSkString)->size());
return; return;
case Type::kObjRef: case Type::kObjRef:
stream->writeDecAsText(objNumMap.getObjectNumber( stream->writeDecAsText(objNumMap.getObjectNumber(fObject));
substitutes.getSubstitute(fObject)));
stream->writeText(" 0 R"); // Generation number is always 0. stream->writeText(" 0 R"); // Generation number is always 0.
return; return;
case Type::kObject: case Type::kObject:
fObject->emitObject(stream, objNumMap, substitutes); fObject->emitObject(stream, objNumMap);
return; return;
default: default:
SkDEBUGFAIL("SkPDFUnion::emitObject with bad type"); SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
} }
} }
void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap, void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap) const {
const SkPDFSubstituteMap& substituteMap) const {
switch (fType) { switch (fType) {
case Type::kInt: case Type::kInt:
case Type::kColorComponent: case Type::kColorComponent:
@ -171,13 +168,11 @@ void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
case Type::kNameSkS: case Type::kNameSkS:
case Type::kStringSkS: case Type::kStringSkS:
return; // These have no resources. return; // These have no resources.
case Type::kObjRef: { case Type::kObjRef:
SkPDFObject* obj = substituteMap.getSubstitute(fObject); objNumMap->addObjectRecursively(fObject);
objNumMap->addObjectRecursively(obj, substituteMap);
return; return;
}
case Type::kObject: case Type::kObject:
fObject->addResources(objNumMap, substituteMap); fObject->addResources(objNumMap);
return; return;
default: default:
SkDEBUGFAIL("SkPDFUnion::addResources with bad type"); SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
@ -253,13 +248,11 @@ SkPDFUnion SkPDFUnion::Object(sk_sp<SkPDFObject> objSp) {
#if 0 // Enable if needed. #if 0 // Enable if needed.
void SkPDFAtom::emitObject(SkWStream* stream, void SkPDFAtom::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const { fValue.emitObject(stream, objNumMap);
fValue.emitObject(stream, objNumMap, substitutes);
} }
void SkPDFAtom::addResources(SkPDFObjNumMap* map, void SkPDFAtom::addResources(SkPDFObjNumMap* map) const {
const SkPDFSubstituteMap& substitutes) const { fValue.addResources(map);
fValue.addResources(map, substitutes);
} }
#endif // 0 #endif // 0
@ -282,12 +275,11 @@ void SkPDFArray::reserve(int length) {
} }
void SkPDFArray::emitObject(SkWStream* stream, void SkPDFArray::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(!fDumped); SkASSERT(!fDumped);
stream->writeText("["); stream->writeText("[");
for (int i = 0; i < fValues.count(); i++) { for (int i = 0; i < fValues.count(); i++) {
fValues[i].emitObject(stream, objNumMap, substitutes); fValues[i].emitObject(stream, objNumMap);
if (i + 1 < fValues.count()) { if (i + 1 < fValues.count()) {
stream->writeText(" "); stream->writeText(" ");
} }
@ -295,11 +287,10 @@ void SkPDFArray::emitObject(SkWStream* stream,
stream->writeText("]"); stream->writeText("]");
} }
void SkPDFArray::addResources(SkPDFObjNumMap* catalog, void SkPDFArray::addResources(SkPDFObjNumMap* catalog) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(!fDumped); SkASSERT(!fDumped);
for (const SkPDFUnion& value : fValues) { for (const SkPDFUnion& value : fValues) {
value.addResources(catalog, substitutes); value.addResources(catalog);
} }
} }
@ -364,33 +355,30 @@ SkPDFDict::SkPDFDict(const char type[]) {
} }
void SkPDFDict::emitObject(SkWStream* stream, void SkPDFDict::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
stream->writeText("<<"); stream->writeText("<<");
this->emitAll(stream, objNumMap, substitutes); this->emitAll(stream, objNumMap);
stream->writeText(">>"); stream->writeText(">>");
} }
void SkPDFDict::emitAll(SkWStream* stream, void SkPDFDict::emitAll(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(!fDumped); SkASSERT(!fDumped);
for (int i = 0; i < fRecords.count(); i++) { for (int i = 0; i < fRecords.count(); i++) {
fRecords[i].fKey.emitObject(stream, objNumMap, substitutes); fRecords[i].fKey.emitObject(stream, objNumMap);
stream->writeText(" "); stream->writeText(" ");
fRecords[i].fValue.emitObject(stream, objNumMap, substitutes); fRecords[i].fValue.emitObject(stream, objNumMap);
if (i + 1 < fRecords.count()) { if (i + 1 < fRecords.count()) {
stream->writeText("\n"); stream->writeText("\n");
} }
} }
} }
void SkPDFDict::addResources(SkPDFObjNumMap* catalog, void SkPDFDict::addResources(SkPDFObjNumMap* catalog) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(!fDumped); SkASSERT(!fDumped);
for (int i = 0; i < fRecords.count(); i++) { for (int i = 0; i < fRecords.count(); i++) {
fRecords[i].fKey.addResources(catalog, substitutes); fRecords[i].fKey.addResources(catalog);
fRecords[i].fValue.addResources(catalog, substitutes); fRecords[i].fValue.addResources(catalog);
} }
} }
@ -463,20 +451,17 @@ void SkPDFSharedStream::drop() {
#ifdef SK_PDF_LESS_COMPRESSION #ifdef SK_PDF_LESS_COMPRESSION
void SkPDFSharedStream::emitObject( void SkPDFSharedStream::emitObject(
SkWStream* stream, SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(fAsset); SkASSERT(fAsset);
std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate()); std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
SkASSERT(dup && dup->hasLength()); SkASSERT(dup && dup->hasLength());
size_t length = dup->getLength(); size_t length = dup->getLength();
stream->writeText("<<"); stream->writeText("<<");
fDict.emitAll(stream, objNumMap, substitutes); fDict.emitAll(stream, objNumMap);
stream->writeText("\n"); stream->writeText("\n");
SkPDFUnion::Name("Length").emitObject( SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
stream, objNumMap, substitutes);
stream->writeText(" "); stream->writeText(" ");
SkPDFUnion::Int(length).emitObject( SkPDFUnion::Int(length).emitObject(stream, objNumMap);
stream, objNumMap, substitutes);
stream->writeText("\n>>stream\n"); stream->writeText("\n>>stream\n");
SkStreamCopy(stream, dup.get()); SkStreamCopy(stream, dup.get());
stream->writeText("\nendstream"); stream->writeText("\nendstream");
@ -484,8 +469,7 @@ void SkPDFSharedStream::emitObject(
#else #else
void SkPDFSharedStream::emitObject( void SkPDFSharedStream::emitObject(
SkWStream* stream, SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(fAsset); SkASSERT(fAsset);
SkDynamicMemoryWStream buffer; SkDynamicMemoryWStream buffer;
SkDeflateWStream deflateWStream(&buffer); SkDeflateWStream deflateWStream(&buffer);
@ -496,15 +480,15 @@ void SkPDFSharedStream::emitObject(
deflateWStream.finalize(); deflateWStream.finalize();
size_t length = buffer.bytesWritten(); size_t length = buffer.bytesWritten();
stream->writeText("<<"); stream->writeText("<<");
fDict.emitAll(stream, objNumMap, substitutes); fDict.emitAll(stream, objNumMap);
stream->writeText("\n"); stream->writeText("\n");
SkPDFUnion::Name("Length").emitObject(stream, objNumMap, substitutes); SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
stream->writeText(" "); stream->writeText(" ");
SkPDFUnion::Int(length).emitObject(stream, objNumMap, substitutes); SkPDFUnion::Int(length).emitObject(stream, objNumMap);
stream->writeText("\n"); stream->writeText("\n");
SkPDFUnion::Name("Filter").emitObject(stream, objNumMap, substitutes); SkPDFUnion::Name("Filter").emitObject(stream, objNumMap);
stream->writeText(" "); stream->writeText(" ");
SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap, substitutes); SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap);
stream->writeText(">>"); stream->writeText(">>");
stream->writeText(" stream\n"); stream->writeText(" stream\n");
buffer.writeToStream(stream); buffer.writeToStream(stream);
@ -513,9 +497,9 @@ void SkPDFSharedStream::emitObject(
#endif #endif
void SkPDFSharedStream::addResources( void SkPDFSharedStream::addResources(
SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const { SkPDFObjNumMap* catalog) const {
SkASSERT(fAsset); SkASSERT(fAsset);
fDict.addResources(catalog, substitutes); fDict.addResources(catalog);
} }
@ -534,10 +518,9 @@ SkPDFStream::SkPDFStream() {}
SkPDFStream::~SkPDFStream() {} SkPDFStream::~SkPDFStream() {}
void SkPDFStream::addResources( void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const {
SkASSERT(fCompressedData); SkASSERT(fCompressedData);
fDict.addResources(catalog, substitutes); fDict.addResources(catalog);
} }
void SkPDFStream::drop() { void SkPDFStream::drop() {
@ -546,10 +529,9 @@ void SkPDFStream::drop() {
} }
void SkPDFStream::emitObject(SkWStream* stream, void SkPDFStream::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const {
const SkPDFSubstituteMap& substitutes) const {
SkASSERT(fCompressedData); SkASSERT(fCompressedData);
fDict.emitObject(stream, objNumMap, substitutes); fDict.emitObject(stream, objNumMap);
// duplicate (a cheap operation) preserves const on fCompressedData. // duplicate (a cheap operation) preserves const on fCompressedData.
std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate()); std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
SkASSERT(dup); SkASSERT(dup);
@ -594,25 +576,6 @@ void SkPDFStream::setData(std::unique_ptr<SkStreamAsset> stream) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SkPDFSubstituteMap::~SkPDFSubstituteMap() {
fSubstituteMap.foreach(
[](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
}
void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
SkPDFObject* substitute) {
SkASSERT(original != substitute);
SkASSERT(!fSubstituteMap.find(original));
fSubstituteMap.set(original, SkRef(substitute));
}
SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
SkPDFObject** found = fSubstituteMap.find(object);
return found ? *found : object;
}
////////////////////////////////////////////////////////////////////////////////
bool SkPDFObjNumMap::addObject(SkPDFObject* obj) { bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
if (fObjectNumbers.find(obj)) { if (fObjectNumbers.find(obj)) {
return false; return false;
@ -622,10 +585,9 @@ bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
return true; return true;
} }
void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj, void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
const SkPDFSubstituteMap& subs) {
if (obj && this->addObject(obj)) { if (obj && this->addObject(obj)) {
obj->addResources(this, subs); obj->addResources(this);
} }
} }

View File

@ -17,7 +17,6 @@
class SkData; class SkData;
class SkPDFObjNumMap; class SkPDFObjNumMap;
class SkPDFObject; class SkPDFObject;
class SkPDFSubstituteMap;
class SkStreamAsset; class SkStreamAsset;
class SkString; class SkString;
class SkWStream; class SkWStream;
@ -41,16 +40,14 @@ public:
* @param stream The writable output stream to send the output to. * @param stream The writable output stream to send the output to.
*/ */
virtual void emitObject(SkWStream* stream, virtual void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const = 0;
const SkPDFSubstituteMap& substitutes) const = 0;
/** /**
* Adds all transitive dependencies of this object to the * Adds all transitive dependencies of this object to the
* catalog. Implementations should respect the catalog's object * catalog. Implementations should respect the catalog's object
* substitution map. * substitution map.
*/ */
virtual void addResources(SkPDFObjNumMap* catalog, virtual void addResources(SkPDFObjNumMap* catalog) const {}
const SkPDFSubstituteMap& substitutes) const {}
/** /**
* Release all resources associated with this SkPDFObject. It is * Release all resources associated with this SkPDFObject. It is
@ -121,10 +118,8 @@ public:
/** These two non-virtual methods mirror SkPDFObject's /** These two non-virtual methods mirror SkPDFObject's
corresponding virtuals. */ corresponding virtuals. */
void emitObject(SkWStream*, void emitObject(SkWStream*, const SkPDFObjNumMap&) const;
const SkPDFObjNumMap&, void addResources(SkPDFObjNumMap*) const;
const SkPDFSubstituteMap&) const;
void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const;
bool isName() const; bool isName() const;
@ -171,9 +166,8 @@ static_assert(sizeof(SkString) == sizeof(void*), "SkString_size");
class SkPDFAtom final : public SkPDFObject { class SkPDFAtom final : public SkPDFObject {
public: public:
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) final;
const SkPDFSubstituteMap& substitutes) final; void addResources(SkPDFObjNumMap* const final;
void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
SkPDFAtom(SkPDFUnion&& v) : fValue(std::move(v) {} SkPDFAtom(SkPDFUnion&& v) : fValue(std::move(v) {}
private: private:
@ -197,10 +191,8 @@ public:
// The SkPDFObject interface. // The SkPDFObject interface.
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const override;
const SkPDFSubstituteMap& substitutes) const override; void addResources(SkPDFObjNumMap*) const override;
void addResources(SkPDFObjNumMap*,
const SkPDFSubstituteMap&) const override;
void drop() override; void drop() override;
/** The size of the array. /** The size of the array.
@ -247,10 +239,8 @@ public:
// The SkPDFObject interface. // The SkPDFObject interface.
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const override;
const SkPDFSubstituteMap& substitutes) const override; void addResources(SkPDFObjNumMap*) const override;
void addResources(SkPDFObjNumMap*,
const SkPDFSubstituteMap&) const override;
void drop() override; void drop() override;
/** The size of the dictionary. /** The size of the dictionary.
@ -282,8 +272,7 @@ public:
/** Emit the dictionary, without the "<<" and ">>". /** Emit the dictionary, without the "<<" and ">>".
*/ */
void emitAll(SkWStream* stream, void emitAll(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const;
const SkPDFSubstituteMap& substitutes) const;
private: private:
struct Record { struct Record {
@ -312,10 +301,8 @@ public:
~SkPDFSharedStream(); ~SkPDFSharedStream();
SkPDFDict* dict() { return &fDict; } SkPDFDict* dict() { return &fDict; }
void emitObject(SkWStream*, void emitObject(SkWStream*,
const SkPDFObjNumMap&, const SkPDFObjNumMap&) const override;
const SkPDFSubstituteMap&) const override; void addResources(SkPDFObjNumMap*) const override;
void addResources(SkPDFObjNumMap*,
const SkPDFSubstituteMap&) const override;
void drop() override; void drop() override;
private: private:
@ -346,9 +333,8 @@ public:
// The SkPDFObject interface. // The SkPDFObject interface.
void emitObject(SkWStream* stream, void emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap, const SkPDFObjNumMap& objNumMap) const override;
const SkPDFSubstituteMap& substitutes) const override; void addResources(SkPDFObjNumMap*) const final;
void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
void drop() override; void drop() override;
protected: protected:
@ -383,9 +369,8 @@ public:
/** Add the passed object to the catalog, as well as all its dependencies. /** Add the passed object to the catalog, as well as all its dependencies.
* @param obj The object to add. If nullptr, this is a noop. * @param obj The object to add. If nullptr, this is a noop.
* @param subs Will be passed to obj->addResources().
*/ */
void addObjectRecursively(SkPDFObject* obj, const SkPDFSubstituteMap& subs); void addObjectRecursively(SkPDFObject* obj);
/** Get the object number for the passed object. /** Get the object number for the passed object.
* @param obj The object of interest. * @param obj The object of interest.
@ -401,32 +386,6 @@ private:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/** \class SkPDFSubstituteMap
The PDF Substitute Map manages substitute objects and owns the
substitutes.
*/
class SkPDFSubstituteMap : SkNoncopyable {
public:
~SkPDFSubstituteMap();
/** Set substitute object for the passed object.
Refs substitute.
*/
void setSubstitute(SkPDFObject* original, SkPDFObject* substitute);
/** Find and return any substitute object set for the passed object. If
* there is none, return the passed object.
*/
SkPDFObject* getSubstitute(SkPDFObject* object) const;
SkPDFObject* operator()(SkPDFObject* o) const {
return this->getSubstitute(o);
}
private:
SkTHashMap<SkPDFObject*, SkPDFObject*> fSubstituteMap;
};
#ifdef SK_PDF_IMAGE_STATS #ifdef SK_PDF_IMAGE_STATS
extern SkAtomic<int> gDrawImageCalls; extern SkAtomic<int> gDrawImageCalls;
extern SkAtomic<int> gJpegImageObjects; extern SkAtomic<int> gJpegImageObjects;

View File

@ -16,11 +16,6 @@ SkBitSet::SkBitSet(int numberOfBits)
fBitData.set(sk_calloc_throw(fDwordCount * sizeof(uint32_t))); fBitData.set(sk_calloc_throw(fDwordCount * sizeof(uint32_t)));
} }
SkBitSet::SkBitSet(const SkBitSet& source)
: fBitData(nullptr), fDwordCount(0), fBitCount(0) {
*this = source;
}
SkBitSet::SkBitSet(SkBitSet&& source) SkBitSet::SkBitSet(SkBitSet&& source)
: fBitData(source.fBitData.release()) : fBitData(source.fBitData.release())
, fDwordCount(source.fDwordCount) , fDwordCount(source.fDwordCount)
@ -29,15 +24,15 @@ SkBitSet::SkBitSet(SkBitSet&& source)
source.fBitCount = 0; source.fBitCount = 0;
} }
SkBitSet& SkBitSet::operator=(const SkBitSet& rhs) { SkBitSet& SkBitSet::operator=(SkBitSet&& rhs) {
if (this == &rhs) { if (this != &rhs) {
return *this; fBitCount = rhs.fBitCount;
fDwordCount = rhs.fDwordCount;
fBitData.reset(); // Free old pointer.
fBitData.set(rhs.fBitData.release());
rhs.fBitCount = 0;
rhs.fDwordCount = 0;
} }
fBitCount = rhs.fBitCount;
fBitData.reset();
fDwordCount = rhs.fDwordCount;
fBitData.set(sk_malloc_throw(fDwordCount * sizeof(uint32_t)));
memcpy(fBitData.get(), rhs.fBitData.get(), fDwordCount * sizeof(uint32_t));
return *this; return *this;
} }

View File

@ -17,10 +17,11 @@ public:
/** NumberOfBits must be greater than zero. /** NumberOfBits must be greater than zero.
*/ */
explicit SkBitSet(int numberOfBits); explicit SkBitSet(int numberOfBits);
explicit SkBitSet(const SkBitSet& source); SkBitSet(const SkBitSet&) = delete;
explicit SkBitSet(SkBitSet&&); SkBitSet(SkBitSet&&);
SkBitSet& operator=(const SkBitSet&) = delete;
SkBitSet& operator=(SkBitSet&& rhs);
SkBitSet& operator=(const SkBitSet& rhs);
bool operator==(const SkBitSet& rhs); bool operator==(const SkBitSet& rhs);
bool operator!=(const SkBitSet& rhs); bool operator!=(const SkBitSet& rhs);
@ -39,6 +40,15 @@ public:
*chunk &= ~mask; *chunk &= ~mask;
} }
} }
void set(int index) { this->setBit(index, true); }
template<typename T>
void setAll(T* array, int len) {
static_assert(std::is_integral<T>::value, "T is integral");
for (int i = 0; i < len; ++i) {
this->set(static_cast<int>(array[i]));
}
}
/** Test if bit index is set. /** Test if bit index is set.
*/ */
@ -46,6 +56,7 @@ public:
uint32_t mask = 1 << (index & 31); uint32_t mask = 1 << (index & 31);
return SkToBool(*this->internalGet(index) & mask); return SkToBool(*this->internalGet(index) & mask);
} }
bool has(int index) const { return this->isBitSet(index); }
/** Or bits from source. false is returned if this doesn't have the same /** Or bits from source. false is returned if this doesn't have the same
* bit count as source. * bit count as source.
@ -56,6 +67,7 @@ public:
*/ */
template<typename T> template<typename T>
void exportTo(SkTDArray<T>* array) const { void exportTo(SkTDArray<T>* array) const {
static_assert(std::is_integral<T>::value, "T is integral");
SkASSERT(array); SkASSERT(array);
uint32_t* data = reinterpret_cast<uint32_t*>(fBitData.get()); uint32_t* data = reinterpret_cast<uint32_t*>(fBitData.get());
for (unsigned int i = 0; i < fDwordCount; ++i) { for (unsigned int i = 0; i < fDwordCount; ++i) {

View File

@ -70,7 +70,4 @@ DEF_TEST(BitSet, reporter) {
set3.setBit(0, true); set3.setBit(0, true);
REPORTER_ASSERT(reporter, set2 == set3); REPORTER_ASSERT(reporter, set2 == set3);
set3.clearAll(); set3.clearAll();
set3 = set2;
set2 = set2;
REPORTER_ASSERT(reporter, set2 == set3);
} }

View File

@ -5,12 +5,14 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#include "SkBitSet.h"
#include "SkData.h" #include "SkData.h"
#include "SkPDFFont.h"
#include "SkPDFMakeToUnicodeCmap.h" #include "SkPDFMakeToUnicodeCmap.h"
#include "SkStream.h" #include "SkStream.h"
#include "Test.h" #include "Test.h"
static const int kMaximumGlyphCount = SK_MaxU16 + 1;
static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
const char* buffer, size_t len) { const char* buffer, size_t len) {
sk_sp<SkData> data(stream.copyToData()); sk_sp<SkData> data(stream.copyToData());
@ -26,7 +28,7 @@ static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
DEF_TEST(ToUnicode, reporter) { DEF_TEST(ToUnicode, reporter) {
SkTDArray<SkUnichar> glyphToUnicode; SkTDArray<SkUnichar> glyphToUnicode;
SkTDArray<uint16_t> glyphsInSubset; SkTDArray<uint16_t> glyphsInSubset;
SkPDFGlyphSet subset; SkBitSet subset(kMaximumGlyphCount);
glyphToUnicode.push(0); // 0 glyphToUnicode.push(0); // 0
glyphToUnicode.push(0); // 1 glyphToUnicode.push(0); // 1
@ -65,7 +67,7 @@ DEF_TEST(ToUnicode, reporter) {
glyphToUnicode.push(0x1013); glyphToUnicode.push(0x1013);
SkDynamicMemoryWStream buffer; SkDynamicMemoryWStream buffer;
subset.set(glyphsInSubset.begin(), glyphsInSubset.count()); subset.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF); SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF);
char expectedResult[] = char expectedResult[] =
@ -136,7 +138,7 @@ endbfrange\n";
glyphToUnicode.reset(); glyphToUnicode.reset();
glyphsInSubset.reset(); glyphsInSubset.reset();
SkPDFGlyphSet subset2; SkBitSet subset2(kMaximumGlyphCount);
// Test mapping: // Test mapping:
// I n s t a l // I n s t a l
@ -154,7 +156,7 @@ endbfrange\n";
glyphsInSubset.push(0x57); glyphsInSubset.push(0x57);
SkDynamicMemoryWStream buffer2; SkDynamicMemoryWStream buffer2;
subset2.set(glyphsInSubset.begin(), glyphsInSubset.count()); subset2.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
SkPDFAppendCmapSections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff); SkPDFAppendCmapSections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff);
char expectedResult2[] = char expectedResult2[] =

View File

@ -31,21 +31,14 @@
#define DUMMY_TEXT "DCT compessed stream." #define DUMMY_TEXT "DCT compessed stream."
namespace {
struct Catalog {
SkPDFSubstituteMap substitutes;
SkPDFObjNumMap numbers;
};
} // namespace
template <typename T> template <typename T>
static SkString emit_to_string(T& obj, Catalog* catPtr = nullptr) { static SkString emit_to_string(T& obj, SkPDFObjNumMap* catPtr = nullptr) {
Catalog catalog; SkPDFObjNumMap catalog;
SkDynamicMemoryWStream buffer; SkDynamicMemoryWStream buffer;
if (!catPtr) { if (!catPtr) {
catPtr = &catalog; catPtr = &catalog;
} }
obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes); obj.emitObject(&buffer, *catPtr);
SkString tmp(buffer.bytesWritten()); SkString tmp(buffer.bytesWritten());
buffer.copyTo(tmp.writable_str()); buffer.copyTo(tmp.writable_str());
return tmp; return tmp;
@ -148,9 +141,9 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
sk_sp<SkPDFArray> a2(new SkPDFArray); sk_sp<SkPDFArray> a2(new SkPDFArray);
a2->appendObjRef(a1); a2->appendObjRef(a1);
Catalog catalog; SkPDFObjNumMap catalog;
catalog.numbers.addObject(a1.get()); catalog.addObject(a1.get());
REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(a1.get()) == 1); REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
SkString result = emit_to_string(*a2, &catalog); SkString result = emit_to_string(*a2, &catalog);
// If appendObjRef misbehaves, then the result would // If appendObjRef misbehaves, then the result would
@ -158,22 +151,6 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
assert_eq(reporter, result, "[1 0 R]"); assert_eq(reporter, result, "[1 0 R]");
} }
static void TestSubstitute(skiatest::Reporter* reporter) {
sk_sp<SkPDFDict> proxy(new SkPDFDict());
sk_sp<SkPDFDict> stub(new SkPDFDict());
proxy->insertInt("Value", 33);
stub->insertInt("Value", 44);
SkPDFSubstituteMap substituteMap;
substituteMap.setSubstitute(proxy.get(), stub.get());
SkPDFObjNumMap catalog;
catalog.addObject(proxy.get());
REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy.get()));
REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub.get()));
}
// This test used to assert without the fix submitted for // This test used to assert without the fix submitted for
// http://code.google.com/p/skia/issues/detail?id=1083. // http://code.google.com/p/skia/issues/detail?id=1083.
// SKP files might have invalid glyph ids. This test ensures they are ignored, // SKP files might have invalid glyph ids. This test ensures they are ignored,
@ -283,9 +260,9 @@ static void TestPDFArray(skiatest::Reporter* reporter) {
"(Another String) [-1]]"); "(Another String) [-1]]");
sk_sp<SkPDFArray> referencedArray(new SkPDFArray); sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
Catalog catalog; SkPDFObjNumMap catalog;
catalog.numbers.addObject(referencedArray.get()); catalog.addObject(referencedArray.get());
REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber( REPORTER_ASSERT(reporter, catalog.getObjectNumber(
referencedArray.get()) == 1); referencedArray.get()) == 1);
array->appendObjRef(std::move(referencedArray)); array->appendObjRef(std::move(referencedArray));
@ -347,9 +324,9 @@ static void TestPDFDict(skiatest::Reporter* reporter) {
assert_emit_eq(reporter, *dict, "<</Type /DType>>"); assert_emit_eq(reporter, *dict, "<</Type /DType>>");
sk_sp<SkPDFArray> referencedArray(new SkPDFArray); sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
Catalog catalog; SkPDFObjNumMap catalog;
catalog.numbers.addObject(referencedArray.get()); catalog.addObject(referencedArray.get());
REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber( REPORTER_ASSERT(reporter, catalog.getObjectNumber(
referencedArray.get()) == 1); referencedArray.get()) == 1);
dict->insertObjRef("n1", std::move(referencedArray)); dict->insertObjRef("n1", std::move(referencedArray));
SkString result = emit_to_string(*dict, &catalog); SkString result = emit_to_string(*dict, &catalog);
@ -363,7 +340,6 @@ DEF_TEST(PDFPrimitives, reporter) {
TestPDFStream(reporter); TestPDFStream(reporter);
TestObjectNumberMap(reporter); TestObjectNumberMap(reporter);
TestObjectRef(reporter); TestObjectRef(reporter);
TestSubstitute(reporter);
test_issue1083(); test_issue1083();
} }