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:
parent
9637ea91b8
commit
530032a18e
@ -30,14 +30,13 @@ struct NullWStream : public SkWStream {
|
||||
static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) {
|
||||
// SkDebugWStream wStream;
|
||||
NullWStream wStream;
|
||||
SkPDFSubstituteMap substitutes;
|
||||
SkPDFObjNumMap objNumMap;
|
||||
objNumMap.addObjectRecursively(object.get(), substitutes);
|
||||
objNumMap.addObjectRecursively(object.get());
|
||||
for (int i = 0; i < objNumMap.objects().count(); ++i) {
|
||||
SkPDFObject* object = objNumMap.objects()[i].get();
|
||||
wStream.writeDecAsText(i + 1);
|
||||
wStream.writeText(" 0 obj\n");
|
||||
object->emitObject(&wStream, objNumMap, substitutes);
|
||||
object->emitObject(&wStream, objNumMap);
|
||||
wStream.writeText("\nendobj\n");
|
||||
}
|
||||
}
|
||||
|
@ -346,8 +346,7 @@ static void emit_image_xobject(SkWStream* stream,
|
||||
const SkImage* image,
|
||||
bool alpha,
|
||||
const sk_sp<SkPDFObject>& smask,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) {
|
||||
const SkPDFObjNumMap& objNumMap) {
|
||||
SkBitmap bitmap;
|
||||
image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
|
||||
SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
|
||||
@ -385,7 +384,7 @@ static void emit_image_xobject(SkWStream* stream,
|
||||
pdfDict.insertInt("BitsPerComponent", 8);
|
||||
pdfDict.insertName("Filter", "FlateDecode");
|
||||
pdfDict.insertInt("Length", asset->getLength());
|
||||
pdfDict.emitObject(stream, objNumMap, substitutes);
|
||||
pdfDict.emitObject(stream, objNumMap);
|
||||
|
||||
pdf_stream_begin(stream);
|
||||
stream->writeStream(asset.get(), asset->getLength());
|
||||
@ -400,10 +399,9 @@ class PDFAlphaBitmap final : public SkPDFObject {
|
||||
public:
|
||||
PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& subs) const override {
|
||||
const SkPDFObjNumMap& objNumMap) const override {
|
||||
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; }
|
||||
|
||||
@ -419,19 +417,12 @@ namespace {
|
||||
class PDFDefaultBitmap final : public SkPDFObject {
|
||||
public:
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& subs) const override {
|
||||
const SkPDFObjNumMap& objNumMap) const override {
|
||||
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,
|
||||
const SkPDFSubstituteMap& subs) const override {
|
||||
SkASSERT(fImage);
|
||||
if (fSMask.get()) {
|
||||
SkPDFObject* obj = subs.getSubstitute(fSMask.get());
|
||||
SkASSERT(obj);
|
||||
catalog->addObjectRecursively(obj, subs);
|
||||
}
|
||||
void addResources(SkPDFObjNumMap* catalog) const override {
|
||||
catalog->addObjectRecursively(fSMask.get());
|
||||
}
|
||||
void drop() override { fImage = nullptr; fSMask = nullptr; }
|
||||
PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
|
||||
@ -458,15 +449,12 @@ public:
|
||||
bool fIsYUV;
|
||||
PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
|
||||
: fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
|
||||
void emitObject(SkWStream*,
|
||||
const SkPDFObjNumMap&,
|
||||
const SkPDFSubstituteMap&) const override;
|
||||
void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
|
||||
void drop() override { fData = nullptr; }
|
||||
};
|
||||
|
||||
void PDFJpegBitmap::emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substituteMap) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(fData);
|
||||
SkPDFDict pdfDict("XObject");
|
||||
pdfDict.insertName("Subtype", "Image");
|
||||
@ -481,7 +469,7 @@ void PDFJpegBitmap::emitObject(SkWStream* stream,
|
||||
pdfDict.insertName("Filter", "DCTDecode");
|
||||
pdfDict.insertInt("ColorTransform", 0);
|
||||
pdfDict.insertInt("Length", SkToInt(fData->size()));
|
||||
pdfDict.emitObject(stream, objNumMap, substituteMap);
|
||||
pdfDict.emitObject(stream, objNumMap);
|
||||
pdf_stream_begin(stream);
|
||||
stream->write(fData->data(), fData->size());
|
||||
pdf_stream_end(stream);
|
||||
|
@ -1153,7 +1153,6 @@ void SkPDFDevice::internalDrawText(
|
||||
font->multiByteGlyphs(),
|
||||
defaultPositioning,
|
||||
offset);
|
||||
SkPDFGlyphSetMap* fontGlyphUsage = fDocument->getGlyphUsage();
|
||||
const SkGlyphID* const glyphsEnd = glyphs + glyphCount;
|
||||
|
||||
while (glyphs < glyphsEnd) {
|
||||
@ -1184,7 +1183,7 @@ void SkPDFDevice::internalDrawText(
|
||||
return;
|
||||
}
|
||||
}
|
||||
fontGlyphUsage->noteGlyphUsage(font, glyphs, stretch);
|
||||
font->noteGlyphUsage(glyphs, stretch);
|
||||
if (defaultPositioning) {
|
||||
(void)font->glyphsToPDFFontEncoding(glyphs, SkToInt(glyphsEnd - glyphs));
|
||||
while (stretch-- > 0) {
|
||||
@ -1318,10 +1317,6 @@ sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() const {
|
||||
&fonts);
|
||||
}
|
||||
|
||||
const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
|
||||
return fFontResources;
|
||||
}
|
||||
|
||||
sk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const {
|
||||
auto mediaBox = sk_make_sp<SkPDFArray>();
|
||||
mediaBox->reserve(4);
|
||||
@ -1948,9 +1943,9 @@ int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
|
||||
}
|
||||
int resourceIndex = fFontResources.find(newFont.get());
|
||||
if (resourceIndex < 0) {
|
||||
fDocument->registerFont(newFont.get());
|
||||
resourceIndex = fFontResources.count();
|
||||
fFontResources.push(newFont.get());
|
||||
newFont.get()->ref();
|
||||
fFontResources.push(newFont.release());
|
||||
}
|
||||
return resourceIndex;
|
||||
}
|
||||
|
@ -126,10 +126,6 @@ public:
|
||||
/** Create the resource dictionary for this device. */
|
||||
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
|
||||
* array.
|
||||
* @param array Array to add annotations to.
|
||||
|
@ -23,7 +23,7 @@ SkPDFObjectSerializer::~SkPDFObjectSerializer() {
|
||||
}
|
||||
|
||||
void SkPDFObjectSerializer::addObjectRecursively(const sk_sp<SkPDFObject>& object) {
|
||||
fObjNumMap.addObjectRecursively(object.get(), fSubstituteMap);
|
||||
fObjNumMap.addObjectRecursively(object.get());
|
||||
}
|
||||
|
||||
#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."
|
||||
SkASSERT(fOffsets.count() == fNextToBeSerialized);
|
||||
fOffsets.push(this->offset(wStream));
|
||||
SkASSERT(object == fSubstituteMap.getSubstitute(object));
|
||||
wStream->writeDecAsText(index);
|
||||
wStream->writeText(" 0 obj\n"); // Generation number is always 0.
|
||||
object->emitObject(wStream, fObjNumMap, fSubstituteMap);
|
||||
object->emitObject(wStream, fObjNumMap);
|
||||
wStream->writeText("\nendobj\n");
|
||||
object->drop();
|
||||
++fNextToBeSerialized;
|
||||
@ -93,7 +92,7 @@ void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
|
||||
trailerDict.insertObject("ID", std::move(id));
|
||||
}
|
||||
wStream->writeText("trailer\n");
|
||||
trailerDict.emitObject(wStream, fObjNumMap, fSubstituteMap);
|
||||
trailerDict.emitObject(wStream, fObjNumMap);
|
||||
wStream->writeText("\nstartxref\n");
|
||||
wStream->writeBigDecAsText(xRefFileOffset);
|
||||
wStream->writeText("\n%%EOF");
|
||||
@ -246,11 +245,15 @@ void SkPDFDocument::onEndPage() {
|
||||
}
|
||||
|
||||
void SkPDFDocument::onAbort() {
|
||||
this->reset();
|
||||
}
|
||||
|
||||
void SkPDFDocument::reset() {
|
||||
fCanvas.reset(nullptr);
|
||||
fPages.reset();
|
||||
fCanon.reset();
|
||||
renew(&fObjectSerializer);
|
||||
renew(&fGlyphUsage);
|
||||
fFonts.reset();
|
||||
}
|
||||
|
||||
#ifdef SK_SUPPORT_LEGACY_DOCUMENT_API
|
||||
@ -419,10 +422,7 @@ static sk_sp<SkPDFArray> make_srgb_output_intents() {
|
||||
bool SkPDFDocument::onClose(SkWStream* stream) {
|
||||
SkASSERT(!fCanvas.get());
|
||||
if (fPages.empty()) {
|
||||
fPages.reset();
|
||||
fCanon.reset();
|
||||
renew(&fObjectSerializer);
|
||||
renew(&fGlyphUsage);
|
||||
this->reset();
|
||||
return false;
|
||||
}
|
||||
auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
|
||||
@ -442,21 +442,12 @@ bool SkPDFDocument::onClose(SkWStream* stream) {
|
||||
}
|
||||
|
||||
// Build font subsetting info before calling addObjectRecursively().
|
||||
for (const auto& entry : fGlyphUsage) {
|
||||
sk_sp<SkPDFObject> subsetFont =
|
||||
entry.fFont->getFontSubset(&fCanon, &entry.fGlyphSet);
|
||||
if (subsetFont) {
|
||||
fObjectSerializer.fSubstituteMap.setSubstitute(
|
||||
entry.fFont, subsetFont.get());
|
||||
}
|
||||
}
|
||||
|
||||
SkPDFCanon* canon = &fCanon;
|
||||
fFonts.foreach([canon](SkPDFFont* p){ p->getFontSubset(canon); });
|
||||
fObjectSerializer.addObjectRecursively(docCatalog);
|
||||
fObjectSerializer.serializeObjects(this->getStream());
|
||||
fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
|
||||
fCanon.reset();
|
||||
renew(&fObjectSerializer);
|
||||
renew(&fGlyphUsage);
|
||||
this->reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ sk_sp<SkDocument> SkPDFMakeDocument(SkWStream* stream,
|
||||
// keep similar functionality together.
|
||||
struct SkPDFObjectSerializer : SkNoncopyable {
|
||||
SkPDFObjNumMap fObjNumMap;
|
||||
SkPDFSubstituteMap fSubstituteMap;
|
||||
SkTDArray<int32_t> fOffsets;
|
||||
sk_sp<SkPDFObject> fInfoDict;
|
||||
size_t fBaseOffset;
|
||||
@ -70,18 +69,16 @@ public:
|
||||
|
||||
It might go without saying that objects should not be changed
|
||||
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>&);
|
||||
SkPDFCanon* canon() { return &fCanon; }
|
||||
SkPDFGlyphSetMap* getGlyphUsage() { return &fGlyphUsage; }
|
||||
void registerFont(SkPDFFont* f) { fFonts.add(f); }
|
||||
|
||||
private:
|
||||
SkPDFObjectSerializer fObjectSerializer;
|
||||
SkPDFCanon fCanon;
|
||||
SkPDFGlyphSetMap fGlyphUsage;
|
||||
SkTArray<sk_sp<SkPDFDict>> fPages;
|
||||
SkTHashSet<SkPDFFont*> fFonts;
|
||||
sk_sp<SkPDFDict> fDests;
|
||||
sk_sp<SkPDFDevice> fPageDevice;
|
||||
sk_sp<SkCanvas> fCanvas;
|
||||
@ -90,6 +87,8 @@ private:
|
||||
SkScalar fRasterDpi;
|
||||
SkDocument::PDFMetadata fMetadata;
|
||||
bool fPDFA;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif // SkPDFDocument_DEFINED
|
||||
|
@ -37,50 +37,27 @@ namespace {
|
||||
// non-symbolic, so always call it symbolic. (PDF 1.4 spec, section 5.7.1)
|
||||
static const int kPdfSymbolic = 4;
|
||||
|
||||
class SkPDFType0Font final : public SkPDFFont {
|
||||
public:
|
||||
SkPDFType0Font(const SkAdvancedTypefaceMetrics& info,
|
||||
sk_sp<SkTypeface> typeface,
|
||||
SkAdvancedTypefaceMetrics::FontType type);
|
||||
struct SkPDFType0Font final : public SkPDFFont {
|
||||
SkPDFType0Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
|
||||
virtual ~SkPDFType0Font();
|
||||
sk_sp<SkPDFObject> getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) override;
|
||||
#ifdef SK_DEBUG
|
||||
void emitObject(SkWStream*,
|
||||
const SkPDFObjNumMap&,
|
||||
const SkPDFSubstituteMap&) const override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void getFontSubset(SkPDFCanon*) override;
|
||||
#ifdef SK_DEBUG
|
||||
void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
|
||||
bool fPopulated;
|
||||
#endif
|
||||
bool populate(const SkPDFGlyphSet* subset,
|
||||
const SkAdvancedTypefaceMetrics& metrics);
|
||||
typedef SkPDFDict INHERITED;
|
||||
};
|
||||
|
||||
class SkPDFType1Font final : public SkPDFFont {
|
||||
public:
|
||||
SkPDFType1Font(const SkAdvancedTypefaceMetrics& info,
|
||||
sk_sp<SkTypeface> typeface,
|
||||
uint16_t glyphID,
|
||||
SkPDFCanon* canon);
|
||||
struct SkPDFType1Font final : public SkPDFFont {
|
||||
SkPDFType1Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&, SkPDFCanon*);
|
||||
virtual ~SkPDFType1Font() {}
|
||||
void getFontSubset(SkPDFCanon*) override {} // TODO(halcanary): implement
|
||||
};
|
||||
|
||||
class SkPDFType3Font final : public SkPDFFont {
|
||||
public:
|
||||
SkPDFType3Font(const SkAdvancedTypefaceMetrics& info,
|
||||
sk_sp<SkTypeface> typeface,
|
||||
SkAdvancedTypefaceMetrics::FontType fontType,
|
||||
uint16_t glyphID);
|
||||
struct SkPDFType3Font final : public SkPDFFont {
|
||||
SkPDFType3Font(SkPDFFont::Info, const SkAdvancedTypefaceMetrics&);
|
||||
virtual ~SkPDFType3Font() {}
|
||||
void emitObject(SkWStream*,
|
||||
const SkPDFObjNumMap&,
|
||||
const SkPDFSubstituteMap&) const override {
|
||||
SkDEBUGFAIL("should call getFontSubset!");
|
||||
}
|
||||
sk_sp<SkPDFObject> getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) override;
|
||||
void getFontSubset(SkPDFCanon*) override;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -143,58 +120,6 @@ static sk_sp<SkPDFArray> makeFontBBox(SkIRect glyphBBox, uint16_t emSize) {
|
||||
}
|
||||
} // 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
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -322,47 +247,46 @@ SkPDFFont* SkPDFFont::GetFontResource(SkPDFCanon* canon,
|
||||
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;
|
||||
switch (type) {
|
||||
case SkAdvancedTypefaceMetrics::kType1CID_Font:
|
||||
case SkAdvancedTypefaceMetrics::kTrueType_Font:
|
||||
SkASSERT(multibyte);
|
||||
font = sk_make_sp<SkPDFType0Font>(metrics,
|
||||
std::move(typeface),
|
||||
type);
|
||||
font = sk_make_sp<SkPDFType0Font>(std::move(info), metrics);
|
||||
break;
|
||||
case SkAdvancedTypefaceMetrics::kType1_Font:
|
||||
SkASSERT(!multibyte);
|
||||
font = sk_make_sp<SkPDFType1Font>(metrics,
|
||||
std::move(typeface),
|
||||
glyphID,
|
||||
canon);
|
||||
font = sk_make_sp<SkPDFType1Font>(std::move(info), metrics, canon);
|
||||
break;
|
||||
default:
|
||||
SkASSERT(!multibyte);
|
||||
// Type3 is our fallback font.
|
||||
font = sk_make_sp<SkPDFType3Font>(metrics,
|
||||
std::move(typeface),
|
||||
type,
|
||||
glyphID);
|
||||
font = sk_make_sp<SkPDFType3Font>(std::move(info), metrics);
|
||||
break;
|
||||
}
|
||||
canon->fFontMap.set(fontID, SkRef(font.get()));
|
||||
return font.release(); // TODO(halcanary) return sk_sp<SkPDFFont>.
|
||||
}
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFFont::getFontSubset(SkPDFCanon*, const SkPDFGlyphSet*) {
|
||||
return nullptr; // Default: no support.
|
||||
}
|
||||
|
||||
SkPDFFont::SkPDFFont(sk_sp<SkTypeface> typeface,
|
||||
SkAdvancedTypefaceMetrics::FontType fontType)
|
||||
SkPDFFont::SkPDFFont(SkPDFFont::Info info)
|
||||
: SkPDFDict("Font")
|
||||
, fTypeface(std::move(typeface))
|
||||
, fFirstGlyphID(1)
|
||||
, fFontType(fontType) {
|
||||
, fTypeface(std::move(info.fTypeface))
|
||||
, fGlyphUsage(info.fLastGlyphID + 1) // TODO(halcanary): Adjust mapping?
|
||||
, fFirstGlyphID(info.fFirstGlyphID)
|
||||
, fLastGlyphID(info.fLastGlyphID)
|
||||
, fFontType(info.fFontType) {
|
||||
SkASSERT(fTypeface);
|
||||
fLastGlyphID = SkToU16(fTypeface->countGlyphs() - 1);
|
||||
}
|
||||
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkPDFType0Font::SkPDFType0Font(const SkAdvancedTypefaceMetrics& info,
|
||||
sk_sp<SkTypeface> typeface,
|
||||
SkAdvancedTypefaceMetrics::FontType fontType)
|
||||
: SkPDFFont(std::move(typeface), fontType) {
|
||||
SkPDFType0Font::SkPDFType0Font(
|
||||
SkPDFFont::Info info,
|
||||
const SkAdvancedTypefaceMetrics& metrics)
|
||||
: SkPDFFont(std::move(info)) {
|
||||
SkDEBUGCODE(fPopulated = false);
|
||||
if (!can_subset(info)) {
|
||||
this->populate(nullptr, info);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
void SkPDFType0Font::emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(fPopulated);
|
||||
return INHERITED::emitObject(stream, objNumMap, substitutes);
|
||||
return INHERITED::emitObject(stream, objNumMap);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -488,8 +387,12 @@ static sk_sp<SkPDFObject> get_subset_font_stream(
|
||||
}
|
||||
#endif // SK_SFNTLY_SUBSETTER
|
||||
|
||||
bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
|
||||
const SkAdvancedTypefaceMetrics& metrics) {
|
||||
void SkPDFType0Font::getFontSubset(SkPDFCanon* canon) {
|
||||
const SkAdvancedTypefaceMetrics* metricsPtr =
|
||||
SkPDFFont::GetMetrics(this->typeface(), canon);
|
||||
SkASSERT(metricsPtr);
|
||||
if (!metricsPtr) { return; }
|
||||
const SkAdvancedTypefaceMetrics& metrics = *metricsPtr;
|
||||
SkASSERT(can_embed(metrics));
|
||||
SkAdvancedTypefaceMetrics::FontType type = this->getType();
|
||||
SkTypeface* face = this->typeface();
|
||||
@ -504,24 +407,22 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
|
||||
std::unique_ptr<SkStreamAsset> fontAsset(face->openStream(&ttcIndex));
|
||||
SkASSERT(fontAsset);
|
||||
if (!fontAsset) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
size_t fontSize = fontAsset->getLength();
|
||||
SkASSERT(fontSize > 0);
|
||||
if (fontSize == 0) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SK_SFNTLY_SUBSETTER
|
||||
if (can_subset(metrics) && subset) {
|
||||
if (can_subset(metrics)) {
|
||||
// Generate glyph id array. in format needed by sfntly
|
||||
SkTDArray<uint32_t> glyphIDs;
|
||||
if (subset) {
|
||||
if (!subset->has(0)) {
|
||||
glyphIDs.push(0); // Always include glyph 0.
|
||||
}
|
||||
subset->exportTo(&glyphIDs);
|
||||
if (!this->glyphUsage().has(0)) {
|
||||
glyphIDs.push(0); // Always include glyph 0.
|
||||
}
|
||||
this->glyphUsage().exportTo(&glyphIDs);
|
||||
sk_sp<SkPDFObject> subsetStream = get_subset_font_stream(
|
||||
std::move(fontAsset), glyphIDs, name.c_str());
|
||||
if (subsetStream) {
|
||||
@ -542,7 +443,7 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
|
||||
SkASSERT(fontData);
|
||||
SkASSERT(fontData->getLength() > 0);
|
||||
if (!fontData || 0 == fontData->getLength()) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontData));
|
||||
fontStream->dict()->insertName("Subtype", "CIDFontType0c");
|
||||
@ -574,11 +475,10 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
|
||||
|
||||
uint16_t emSize = metrics.fEmSize;
|
||||
int16_t defaultWidth = 0;
|
||||
const SkBitSet* bitSet = subset ? &subset->bitSet() : nullptr;
|
||||
{
|
||||
SkAutoGlyphCache glyphCache = vector_cache(face);
|
||||
sk_sp<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray(
|
||||
glyphCache.get(), bitSet, emSize, &defaultWidth);
|
||||
glyphCache.get(), &this->glyphUsage(), emSize, &defaultWidth);
|
||||
if (widths && widths->size() > 0) {
|
||||
newCIDFont->insertObject("W", std::move(widths));
|
||||
}
|
||||
@ -598,13 +498,13 @@ bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset,
|
||||
if (metrics.fGlyphToUnicode.count() > 0) {
|
||||
this->insertObjRef("ToUnicode",
|
||||
SkPDFMakeToUnicodeCmap(metrics.fGlyphToUnicode,
|
||||
subset,
|
||||
&this->glyphUsage(),
|
||||
multiByteGlyphs(),
|
||||
firstGlyphID(),
|
||||
lastGlyphID()));
|
||||
}
|
||||
SkDEBUGCODE(fPopulated = true);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -679,24 +579,22 @@ static void populate_type_1_font(SkPDFDict* font,
|
||||
font->insertObject("Encoding", std::move(encoding));
|
||||
}
|
||||
|
||||
SkPDFType1Font::SkPDFType1Font(const SkAdvancedTypefaceMetrics& info,
|
||||
sk_sp<SkTypeface> typeface,
|
||||
uint16_t glyphID,
|
||||
SkPDFType1Font::SkPDFType1Font(SkPDFFont::Info info,
|
||||
const SkAdvancedTypefaceMetrics& metrics,
|
||||
SkPDFCanon* canon)
|
||||
: SkPDFFont(std::move(typeface), SkAdvancedTypefaceMetrics::kType1_Font)
|
||||
: SkPDFFont(std::move(info))
|
||||
{
|
||||
SkFontID fontID = this->typeface()->uniqueID();
|
||||
sk_sp<SkPDFDict> fontDescriptor;
|
||||
if (SkPDFDict** ptr = canon->fFontDescriptors.find(fontID)) {
|
||||
fontDescriptor = sk_ref_sp(*ptr);
|
||||
} else {
|
||||
fontDescriptor = make_type1_font_descriptor(this->typeface(), info);
|
||||
fontDescriptor = make_type1_font_descriptor(this->typeface(), metrics);
|
||||
canon->fFontDescriptors.set(fontID, SkRef(fontDescriptor.get()));
|
||||
}
|
||||
this->insertObjRef("FontDescriptor", std::move(fontDescriptor));
|
||||
this->adjustGlyphRangeForSingleByteEncoding(glyphID);
|
||||
// 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());
|
||||
}
|
||||
|
||||
@ -738,7 +636,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
|
||||
SkPDFDict* font,
|
||||
SkTypeface* typeface,
|
||||
SkScalar emSize,
|
||||
const SkPDFGlyphSet* subset,
|
||||
const SkBitSet& subset,
|
||||
SkGlyphID firstGlyphID,
|
||||
SkGlyphID lastGlyphID) {
|
||||
SkASSERT(lastGlyphID >= firstGlyphID);
|
||||
@ -770,7 +668,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
|
||||
|
||||
sk_sp<SkPDFStream> emptyStream;
|
||||
for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) {
|
||||
bool skipGlyph = subset && gID != 0 && !subset->has(gID);
|
||||
bool skipGlyph = gID != 0 && !subset.has(gID);
|
||||
SkString characterName;
|
||||
SkScalar advance = 0.0f;
|
||||
SkIRect glyphBBox;
|
||||
@ -827,7 +725,7 @@ static void add_type3_font_info(SkPDFCanon* canon,
|
||||
if (metrics /* && metrics->fGlyphToUnicode.count() > 0 */) {
|
||||
font->insertObjRef("ToUnicode",
|
||||
SkPDFMakeToUnicodeCmap(metrics->fGlyphToUnicode,
|
||||
subset,
|
||||
&subset,
|
||||
false,
|
||||
firstGlyphID,
|
||||
lastGlyphID));
|
||||
@ -837,29 +735,20 @@ static void add_type3_font_info(SkPDFCanon* canon,
|
||||
font->insertObject("CharProcs", std::move(charProcs));
|
||||
}
|
||||
|
||||
SkPDFType3Font::SkPDFType3Font(const SkAdvancedTypefaceMetrics& info,
|
||||
sk_sp<SkTypeface> typeface,
|
||||
SkAdvancedTypefaceMetrics::FontType fontType,
|
||||
uint16_t glyphID)
|
||||
: SkPDFFont(std::move(typeface), fontType) {
|
||||
this->adjustGlyphRangeForSingleByteEncoding(glyphID);
|
||||
}
|
||||
SkPDFType3Font::SkPDFType3Font(SkPDFFont::Info info,
|
||||
const SkAdvancedTypefaceMetrics& metrics)
|
||||
: SkPDFFont(std::move(info)) {}
|
||||
|
||||
sk_sp<SkPDFObject> SkPDFType3Font::getFontSubset(SkPDFCanon* canon,
|
||||
const SkPDFGlyphSet* usage) {
|
||||
// All fonts are subset before serialization.
|
||||
// TODO(halcanary): all fonts should follow this pattern.
|
||||
void SkPDFType3Font::getFontSubset(SkPDFCanon* canon) {
|
||||
const SkAdvancedTypefaceMetrics* info =
|
||||
SkPDFFont::GetMetrics(this->typeface(), canon);
|
||||
SkASSERT(info);
|
||||
uint16_t emSize = info->fEmSize > 0 ? info->fEmSize : 1000;
|
||||
auto font = sk_make_sp<SkPDFDict>("Font");
|
||||
add_type3_font_info(canon, font.get(), this->typeface(), (SkScalar)emSize, usage,
|
||||
add_type3_font_info(canon, this, this->typeface(), (SkScalar)emSize,
|
||||
this->glyphUsage(),
|
||||
this->firstGlyphID(), this->lastGlyphID());
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SkPDFFont::CanEmbedTypeface(SkTypeface* typeface, SkPDFCanon* canon) {
|
||||
|
@ -18,48 +18,6 @@
|
||||
class SkPDFCanon;
|
||||
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
|
||||
A PDF Object class representing a font. The font may have resources
|
||||
attached to it in order to embed the font. SkPDFFonts are canonicalized
|
||||
@ -112,6 +70,10 @@ public:
|
||||
int glyphsToPDFFontEncodingCount(const SkGlyphID* glyphIDs,
|
||||
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
|
||||
* reference count of the object is incremented and it is the caller's
|
||||
* responsibility to unreference it when done. This is needed to
|
||||
@ -128,14 +90,10 @@ public:
|
||||
static const SkAdvancedTypefaceMetrics* GetMetrics(SkTypeface* typeface,
|
||||
SkPDFCanon* canon);
|
||||
|
||||
/** Subset the font based on usage set. Returns a SkPDFFont instance with
|
||||
* subset.
|
||||
* @param usage Glyph subset requested.
|
||||
* @return nullptr if font does not support subsetting, a new instance
|
||||
* of SkPDFFont otherwise.
|
||||
/** Subset the font based on current usage.
|
||||
* Must be called before emitObject().
|
||||
*/
|
||||
virtual sk_sp<SkPDFObject> getFontSubset(SkPDFCanon* canon,
|
||||
const SkPDFGlyphSet* usage);
|
||||
virtual void getFontSubset(SkPDFCanon*) = 0;
|
||||
|
||||
/**
|
||||
* Return false iff the typeface has its NotEmbeddable flag set.
|
||||
@ -145,23 +103,24 @@ public:
|
||||
|
||||
protected:
|
||||
// Common constructor to handle common members.
|
||||
SkPDFFont(sk_sp<SkTypeface> typeface,
|
||||
SkAdvancedTypefaceMetrics::FontType fontType);
|
||||
struct Info {
|
||||
sk_sp<SkTypeface> fTypeface;
|
||||
SkGlyphID fFirstGlyphID;
|
||||
SkGlyphID fLastGlyphID;
|
||||
SkAdvancedTypefaceMetrics::FontType fFontType;
|
||||
};
|
||||
SkPDFFont(Info);
|
||||
|
||||
SkGlyphID firstGlyphID() const { return fFirstGlyphID; }
|
||||
SkGlyphID lastGlyphID() const { return fLastGlyphID; }
|
||||
|
||||
const SkBitSet& glyphUsage() const { return fGlyphUsage; }
|
||||
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;
|
||||
|
||||
private:
|
||||
sk_sp<SkTypeface> fTypeface;
|
||||
SkBitSet fGlyphUsage;
|
||||
|
||||
// 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.
|
||||
|
@ -176,8 +176,7 @@ sk_sp<SkPDFDict> SkPDFGraphicState::MakeNoSmaskGraphicState() {
|
||||
|
||||
void SkPDFGraphicState::emitObject(
|
||||
SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
auto dict = sk_make_sp<SkPDFDict>("ExtGState");
|
||||
dict->insertName("Type", "ExtGState");
|
||||
|
||||
@ -207,5 +206,5 @@ void SkPDFGraphicState::emitObject(
|
||||
dict->insertScalar("ML", fStrokeMiter);
|
||||
dict->insertBool("SA", true); // SA = Auto stroke adjustment.
|
||||
dict->insertName("BM", as_blend_mode(xferMode));
|
||||
dict->emitObject(stream, objNumMap, substitutes);
|
||||
dict->emitObject(stream, objNumMap);
|
||||
}
|
||||
|
@ -31,8 +31,7 @@ public:
|
||||
// Override emitObject so that we can populate the dictionary on
|
||||
// demand.
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const override;
|
||||
const SkPDFObjNumMap& objNumMap) const override;
|
||||
|
||||
/** Get the graphic state for the passed SkPaint. The reference count of
|
||||
* the object is incremented and it is the caller's responsibility to
|
||||
|
@ -149,7 +149,7 @@ static void append_bfrange_section(const SkTDArray<BFRange>& bfrange,
|
||||
// one of them), the possible savings by aggressive optimization is 416KB
|
||||
// pre-compressed and does not provide enough motivation for implementation.
|
||||
void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
|
||||
const SkPDFGlyphSet* subset,
|
||||
const SkBitSet* subset,
|
||||
SkDynamicMemoryWStream* cmap,
|
||||
bool multiByteGlyphs,
|
||||
SkGlyphID firstGlyphID,
|
||||
@ -212,7 +212,7 @@ void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
|
||||
|
||||
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
|
||||
const SkTDArray<SkUnichar>& glyphToUnicode,
|
||||
const SkPDFGlyphSet* subset,
|
||||
const SkBitSet* subset,
|
||||
bool multiByteGlyphs,
|
||||
SkGlyphID firstGlyphID,
|
||||
SkGlyphID lastGlyphID) {
|
||||
|
@ -13,14 +13,14 @@
|
||||
|
||||
sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
|
||||
const SkTDArray<SkUnichar>& glyphToUnicode,
|
||||
const SkPDFGlyphSet* subset,
|
||||
const SkBitSet* subset,
|
||||
bool multiByteGlyphs,
|
||||
SkGlyphID firstGlyphID,
|
||||
SkGlyphID lastGlyphID);
|
||||
|
||||
// Exposed for unit testing.
|
||||
void SkPDFAppendCmapSections(const SkTDArray<SkUnichar>& glyphToUnicode,
|
||||
const SkPDFGlyphSet* subset,
|
||||
const SkBitSet* subset,
|
||||
SkDynamicMemoryWStream* cmap,
|
||||
bool multiByteGlyphs,
|
||||
SkGlyphID firstGlyphID,
|
||||
|
@ -168,12 +168,11 @@ class PDFXMLObject final : public SkPDFObject {
|
||||
public:
|
||||
PDFXMLObject(SkString xml) : fXML(std::move(xml)) {}
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& omap,
|
||||
const SkPDFSubstituteMap& smap) const override {
|
||||
const SkPDFObjNumMap& omap) const override {
|
||||
SkPDFDict dict("Metadata");
|
||||
dict.insertName("Subtype", "XML");
|
||||
dict.insertInt("Length", fXML.size());
|
||||
dict.emitObject(stream, omap, smap);
|
||||
dict.emitObject(stream, omap);
|
||||
static const char streamBegin[] = " stream\n";
|
||||
stream->write(streamBegin, strlen(streamBegin));
|
||||
// Do not compress this. The standard requires that a
|
||||
|
@ -113,8 +113,7 @@ static void write_name_escaped(SkWStream* o, const char* name) {
|
||||
}
|
||||
|
||||
void SkPDFUnion::emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
switch (fType) {
|
||||
case Type::kInt:
|
||||
stream->writeDecAsText(fIntValue);
|
||||
@ -147,20 +146,18 @@ void SkPDFUnion::emitObject(SkWStream* stream,
|
||||
pun(fSkString)->size());
|
||||
return;
|
||||
case Type::kObjRef:
|
||||
stream->writeDecAsText(objNumMap.getObjectNumber(
|
||||
substitutes.getSubstitute(fObject)));
|
||||
stream->writeDecAsText(objNumMap.getObjectNumber(fObject));
|
||||
stream->writeText(" 0 R"); // Generation number is always 0.
|
||||
return;
|
||||
case Type::kObject:
|
||||
fObject->emitObject(stream, objNumMap, substitutes);
|
||||
fObject->emitObject(stream, objNumMap);
|
||||
return;
|
||||
default:
|
||||
SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
|
||||
}
|
||||
}
|
||||
|
||||
void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
|
||||
const SkPDFSubstituteMap& substituteMap) const {
|
||||
void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap) const {
|
||||
switch (fType) {
|
||||
case Type::kInt:
|
||||
case Type::kColorComponent:
|
||||
@ -171,13 +168,11 @@ void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
|
||||
case Type::kNameSkS:
|
||||
case Type::kStringSkS:
|
||||
return; // These have no resources.
|
||||
case Type::kObjRef: {
|
||||
SkPDFObject* obj = substituteMap.getSubstitute(fObject);
|
||||
objNumMap->addObjectRecursively(obj, substituteMap);
|
||||
case Type::kObjRef:
|
||||
objNumMap->addObjectRecursively(fObject);
|
||||
return;
|
||||
}
|
||||
case Type::kObject:
|
||||
fObject->addResources(objNumMap, substituteMap);
|
||||
fObject->addResources(objNumMap);
|
||||
return;
|
||||
default:
|
||||
SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
|
||||
@ -253,13 +248,11 @@ SkPDFUnion SkPDFUnion::Object(sk_sp<SkPDFObject> objSp) {
|
||||
|
||||
#if 0 // Enable if needed.
|
||||
void SkPDFAtom::emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
fValue.emitObject(stream, objNumMap, substitutes);
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
fValue.emitObject(stream, objNumMap);
|
||||
}
|
||||
void SkPDFAtom::addResources(SkPDFObjNumMap* map,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
fValue.addResources(map, substitutes);
|
||||
void SkPDFAtom::addResources(SkPDFObjNumMap* map) const {
|
||||
fValue.addResources(map);
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
@ -282,12 +275,11 @@ void SkPDFArray::reserve(int length) {
|
||||
}
|
||||
|
||||
void SkPDFArray::emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(!fDumped);
|
||||
stream->writeText("[");
|
||||
for (int i = 0; i < fValues.count(); i++) {
|
||||
fValues[i].emitObject(stream, objNumMap, substitutes);
|
||||
fValues[i].emitObject(stream, objNumMap);
|
||||
if (i + 1 < fValues.count()) {
|
||||
stream->writeText(" ");
|
||||
}
|
||||
@ -295,11 +287,10 @@ void SkPDFArray::emitObject(SkWStream* stream,
|
||||
stream->writeText("]");
|
||||
}
|
||||
|
||||
void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
void SkPDFArray::addResources(SkPDFObjNumMap* catalog) const {
|
||||
SkASSERT(!fDumped);
|
||||
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,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
stream->writeText("<<");
|
||||
this->emitAll(stream, objNumMap, substitutes);
|
||||
this->emitAll(stream, objNumMap);
|
||||
stream->writeText(">>");
|
||||
}
|
||||
|
||||
void SkPDFDict::emitAll(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(!fDumped);
|
||||
for (int i = 0; i < fRecords.count(); i++) {
|
||||
fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
|
||||
fRecords[i].fKey.emitObject(stream, objNumMap);
|
||||
stream->writeText(" ");
|
||||
fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
|
||||
fRecords[i].fValue.emitObject(stream, objNumMap);
|
||||
if (i + 1 < fRecords.count()) {
|
||||
stream->writeText("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
void SkPDFDict::addResources(SkPDFObjNumMap* catalog) const {
|
||||
SkASSERT(!fDumped);
|
||||
for (int i = 0; i < fRecords.count(); i++) {
|
||||
fRecords[i].fKey.addResources(catalog, substitutes);
|
||||
fRecords[i].fValue.addResources(catalog, substitutes);
|
||||
fRecords[i].fKey.addResources(catalog);
|
||||
fRecords[i].fValue.addResources(catalog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,20 +451,17 @@ void SkPDFSharedStream::drop() {
|
||||
#ifdef SK_PDF_LESS_COMPRESSION
|
||||
void SkPDFSharedStream::emitObject(
|
||||
SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(fAsset);
|
||||
std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
|
||||
SkASSERT(dup && dup->hasLength());
|
||||
size_t length = dup->getLength();
|
||||
stream->writeText("<<");
|
||||
fDict.emitAll(stream, objNumMap, substitutes);
|
||||
fDict.emitAll(stream, objNumMap);
|
||||
stream->writeText("\n");
|
||||
SkPDFUnion::Name("Length").emitObject(
|
||||
stream, objNumMap, substitutes);
|
||||
SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
|
||||
stream->writeText(" ");
|
||||
SkPDFUnion::Int(length).emitObject(
|
||||
stream, objNumMap, substitutes);
|
||||
SkPDFUnion::Int(length).emitObject(stream, objNumMap);
|
||||
stream->writeText("\n>>stream\n");
|
||||
SkStreamCopy(stream, dup.get());
|
||||
stream->writeText("\nendstream");
|
||||
@ -484,8 +469,7 @@ void SkPDFSharedStream::emitObject(
|
||||
#else
|
||||
void SkPDFSharedStream::emitObject(
|
||||
SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(fAsset);
|
||||
SkDynamicMemoryWStream buffer;
|
||||
SkDeflateWStream deflateWStream(&buffer);
|
||||
@ -496,15 +480,15 @@ void SkPDFSharedStream::emitObject(
|
||||
deflateWStream.finalize();
|
||||
size_t length = buffer.bytesWritten();
|
||||
stream->writeText("<<");
|
||||
fDict.emitAll(stream, objNumMap, substitutes);
|
||||
fDict.emitAll(stream, objNumMap);
|
||||
stream->writeText("\n");
|
||||
SkPDFUnion::Name("Length").emitObject(stream, objNumMap, substitutes);
|
||||
SkPDFUnion::Name("Length").emitObject(stream, objNumMap);
|
||||
stream->writeText(" ");
|
||||
SkPDFUnion::Int(length).emitObject(stream, objNumMap, substitutes);
|
||||
SkPDFUnion::Int(length).emitObject(stream, objNumMap);
|
||||
stream->writeText("\n");
|
||||
SkPDFUnion::Name("Filter").emitObject(stream, objNumMap, substitutes);
|
||||
SkPDFUnion::Name("Filter").emitObject(stream, objNumMap);
|
||||
stream->writeText(" ");
|
||||
SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap, substitutes);
|
||||
SkPDFUnion::Name("FlateDecode").emitObject(stream, objNumMap);
|
||||
stream->writeText(">>");
|
||||
stream->writeText(" stream\n");
|
||||
buffer.writeToStream(stream);
|
||||
@ -513,9 +497,9 @@ void SkPDFSharedStream::emitObject(
|
||||
#endif
|
||||
|
||||
void SkPDFSharedStream::addResources(
|
||||
SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const {
|
||||
SkPDFObjNumMap* catalog) const {
|
||||
SkASSERT(fAsset);
|
||||
fDict.addResources(catalog, substitutes);
|
||||
fDict.addResources(catalog);
|
||||
}
|
||||
|
||||
|
||||
@ -534,10 +518,9 @@ SkPDFStream::SkPDFStream() {}
|
||||
|
||||
SkPDFStream::~SkPDFStream() {}
|
||||
|
||||
void SkPDFStream::addResources(
|
||||
SkPDFObjNumMap* catalog, const SkPDFSubstituteMap& substitutes) const {
|
||||
void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
|
||||
SkASSERT(fCompressedData);
|
||||
fDict.addResources(catalog, substitutes);
|
||||
fDict.addResources(catalog);
|
||||
}
|
||||
|
||||
void SkPDFStream::drop() {
|
||||
@ -546,10 +529,9 @@ void SkPDFStream::drop() {
|
||||
}
|
||||
|
||||
void SkPDFStream::emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const {
|
||||
const SkPDFObjNumMap& objNumMap) const {
|
||||
SkASSERT(fCompressedData);
|
||||
fDict.emitObject(stream, objNumMap, substitutes);
|
||||
fDict.emitObject(stream, objNumMap);
|
||||
// duplicate (a cheap operation) preserves const on fCompressedData.
|
||||
std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
|
||||
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) {
|
||||
if (fObjectNumbers.find(obj)) {
|
||||
return false;
|
||||
@ -622,10 +585,9 @@ bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj,
|
||||
const SkPDFSubstituteMap& subs) {
|
||||
void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
|
||||
if (obj && this->addObject(obj)) {
|
||||
obj->addResources(this, subs);
|
||||
obj->addResources(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
class SkData;
|
||||
class SkPDFObjNumMap;
|
||||
class SkPDFObject;
|
||||
class SkPDFSubstituteMap;
|
||||
class SkStreamAsset;
|
||||
class SkString;
|
||||
class SkWStream;
|
||||
@ -41,16 +40,14 @@ public:
|
||||
* @param stream The writable output stream to send the output to.
|
||||
*/
|
||||
virtual void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const = 0;
|
||||
const SkPDFObjNumMap& objNumMap) const = 0;
|
||||
|
||||
/**
|
||||
* Adds all transitive dependencies of this object to the
|
||||
* catalog. Implementations should respect the catalog's object
|
||||
* substitution map.
|
||||
*/
|
||||
virtual void addResources(SkPDFObjNumMap* catalog,
|
||||
const SkPDFSubstituteMap& substitutes) const {}
|
||||
virtual void addResources(SkPDFObjNumMap* catalog) const {}
|
||||
|
||||
/**
|
||||
* Release all resources associated with this SkPDFObject. It is
|
||||
@ -121,10 +118,8 @@ public:
|
||||
|
||||
/** These two non-virtual methods mirror SkPDFObject's
|
||||
corresponding virtuals. */
|
||||
void emitObject(SkWStream*,
|
||||
const SkPDFObjNumMap&,
|
||||
const SkPDFSubstituteMap&) const;
|
||||
void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const;
|
||||
void emitObject(SkWStream*, const SkPDFObjNumMap&) const;
|
||||
void addResources(SkPDFObjNumMap*) const;
|
||||
|
||||
bool isName() const;
|
||||
|
||||
@ -171,9 +166,8 @@ static_assert(sizeof(SkString) == sizeof(void*), "SkString_size");
|
||||
class SkPDFAtom final : public SkPDFObject {
|
||||
public:
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) final;
|
||||
void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
|
||||
const SkPDFObjNumMap& objNumMap) final;
|
||||
void addResources(SkPDFObjNumMap* const final;
|
||||
SkPDFAtom(SkPDFUnion&& v) : fValue(std::move(v) {}
|
||||
|
||||
private:
|
||||
@ -197,10 +191,8 @@ public:
|
||||
|
||||
// The SkPDFObject interface.
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const override;
|
||||
void addResources(SkPDFObjNumMap*,
|
||||
const SkPDFSubstituteMap&) const override;
|
||||
const SkPDFObjNumMap& objNumMap) const override;
|
||||
void addResources(SkPDFObjNumMap*) const override;
|
||||
void drop() override;
|
||||
|
||||
/** The size of the array.
|
||||
@ -247,10 +239,8 @@ public:
|
||||
|
||||
// The SkPDFObject interface.
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const override;
|
||||
void addResources(SkPDFObjNumMap*,
|
||||
const SkPDFSubstituteMap&) const override;
|
||||
const SkPDFObjNumMap& objNumMap) const override;
|
||||
void addResources(SkPDFObjNumMap*) const override;
|
||||
void drop() override;
|
||||
|
||||
/** The size of the dictionary.
|
||||
@ -282,8 +272,7 @@ public:
|
||||
/** Emit the dictionary, without the "<<" and ">>".
|
||||
*/
|
||||
void emitAll(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const;
|
||||
const SkPDFObjNumMap& objNumMap) const;
|
||||
|
||||
private:
|
||||
struct Record {
|
||||
@ -312,10 +301,8 @@ public:
|
||||
~SkPDFSharedStream();
|
||||
SkPDFDict* dict() { return &fDict; }
|
||||
void emitObject(SkWStream*,
|
||||
const SkPDFObjNumMap&,
|
||||
const SkPDFSubstituteMap&) const override;
|
||||
void addResources(SkPDFObjNumMap*,
|
||||
const SkPDFSubstituteMap&) const override;
|
||||
const SkPDFObjNumMap&) const override;
|
||||
void addResources(SkPDFObjNumMap*) const override;
|
||||
void drop() override;
|
||||
|
||||
private:
|
||||
@ -346,9 +333,8 @@ public:
|
||||
|
||||
// The SkPDFObject interface.
|
||||
void emitObject(SkWStream* stream,
|
||||
const SkPDFObjNumMap& objNumMap,
|
||||
const SkPDFSubstituteMap& substitutes) const override;
|
||||
void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
|
||||
const SkPDFObjNumMap& objNumMap) const override;
|
||||
void addResources(SkPDFObjNumMap*) const final;
|
||||
void drop() override;
|
||||
|
||||
protected:
|
||||
@ -383,9 +369,8 @@ public:
|
||||
|
||||
/** 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 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.
|
||||
* @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
|
||||
extern SkAtomic<int> gDrawImageCalls;
|
||||
extern SkAtomic<int> gJpegImageObjects;
|
||||
|
@ -16,11 +16,6 @@ SkBitSet::SkBitSet(int numberOfBits)
|
||||
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)
|
||||
: fBitData(source.fBitData.release())
|
||||
, fDwordCount(source.fDwordCount)
|
||||
@ -29,15 +24,15 @@ SkBitSet::SkBitSet(SkBitSet&& source)
|
||||
source.fBitCount = 0;
|
||||
}
|
||||
|
||||
SkBitSet& SkBitSet::operator=(const SkBitSet& rhs) {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
SkBitSet& SkBitSet::operator=(SkBitSet&& rhs) {
|
||||
if (this != &rhs) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,11 @@ public:
|
||||
/** NumberOfBits must be greater than zero.
|
||||
*/
|
||||
explicit SkBitSet(int numberOfBits);
|
||||
explicit SkBitSet(const SkBitSet& source);
|
||||
explicit SkBitSet(SkBitSet&&);
|
||||
SkBitSet(const SkBitSet&) = delete;
|
||||
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);
|
||||
|
||||
@ -39,6 +40,15 @@ public:
|
||||
*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.
|
||||
*/
|
||||
@ -46,6 +56,7 @@ public:
|
||||
uint32_t mask = 1 << (index & 31);
|
||||
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
|
||||
* bit count as source.
|
||||
@ -56,6 +67,7 @@ public:
|
||||
*/
|
||||
template<typename T>
|
||||
void exportTo(SkTDArray<T>* array) const {
|
||||
static_assert(std::is_integral<T>::value, "T is integral");
|
||||
SkASSERT(array);
|
||||
uint32_t* data = reinterpret_cast<uint32_t*>(fBitData.get());
|
||||
for (unsigned int i = 0; i < fDwordCount; ++i) {
|
||||
|
@ -70,7 +70,4 @@ DEF_TEST(BitSet, reporter) {
|
||||
set3.setBit(0, true);
|
||||
REPORTER_ASSERT(reporter, set2 == set3);
|
||||
set3.clearAll();
|
||||
set3 = set2;
|
||||
set2 = set2;
|
||||
REPORTER_ASSERT(reporter, set2 == set3);
|
||||
}
|
||||
|
@ -5,12 +5,14 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBitSet.h"
|
||||
#include "SkData.h"
|
||||
#include "SkPDFFont.h"
|
||||
#include "SkPDFMakeToUnicodeCmap.h"
|
||||
#include "SkStream.h"
|
||||
#include "Test.h"
|
||||
|
||||
static const int kMaximumGlyphCount = SK_MaxU16 + 1;
|
||||
|
||||
static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
|
||||
const char* buffer, size_t len) {
|
||||
sk_sp<SkData> data(stream.copyToData());
|
||||
@ -26,7 +28,7 @@ static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
|
||||
DEF_TEST(ToUnicode, reporter) {
|
||||
SkTDArray<SkUnichar> glyphToUnicode;
|
||||
SkTDArray<uint16_t> glyphsInSubset;
|
||||
SkPDFGlyphSet subset;
|
||||
SkBitSet subset(kMaximumGlyphCount);
|
||||
|
||||
glyphToUnicode.push(0); // 0
|
||||
glyphToUnicode.push(0); // 1
|
||||
@ -65,7 +67,7 @@ DEF_TEST(ToUnicode, reporter) {
|
||||
glyphToUnicode.push(0x1013);
|
||||
|
||||
SkDynamicMemoryWStream buffer;
|
||||
subset.set(glyphsInSubset.begin(), glyphsInSubset.count());
|
||||
subset.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
|
||||
SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF);
|
||||
|
||||
char expectedResult[] =
|
||||
@ -136,7 +138,7 @@ endbfrange\n";
|
||||
|
||||
glyphToUnicode.reset();
|
||||
glyphsInSubset.reset();
|
||||
SkPDFGlyphSet subset2;
|
||||
SkBitSet subset2(kMaximumGlyphCount);
|
||||
|
||||
// Test mapping:
|
||||
// I n s t a l
|
||||
@ -154,7 +156,7 @@ endbfrange\n";
|
||||
glyphsInSubset.push(0x57);
|
||||
|
||||
SkDynamicMemoryWStream buffer2;
|
||||
subset2.set(glyphsInSubset.begin(), glyphsInSubset.count());
|
||||
subset2.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
|
||||
SkPDFAppendCmapSections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff);
|
||||
|
||||
char expectedResult2[] =
|
||||
|
@ -31,21 +31,14 @@
|
||||
|
||||
#define DUMMY_TEXT "DCT compessed stream."
|
||||
|
||||
namespace {
|
||||
struct Catalog {
|
||||
SkPDFSubstituteMap substitutes;
|
||||
SkPDFObjNumMap numbers;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
static SkString emit_to_string(T& obj, Catalog* catPtr = nullptr) {
|
||||
Catalog catalog;
|
||||
static SkString emit_to_string(T& obj, SkPDFObjNumMap* catPtr = nullptr) {
|
||||
SkPDFObjNumMap catalog;
|
||||
SkDynamicMemoryWStream buffer;
|
||||
if (!catPtr) {
|
||||
catPtr = &catalog;
|
||||
}
|
||||
obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes);
|
||||
obj.emitObject(&buffer, *catPtr);
|
||||
SkString tmp(buffer.bytesWritten());
|
||||
buffer.copyTo(tmp.writable_str());
|
||||
return tmp;
|
||||
@ -148,9 +141,9 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
|
||||
sk_sp<SkPDFArray> a2(new SkPDFArray);
|
||||
a2->appendObjRef(a1);
|
||||
|
||||
Catalog catalog;
|
||||
catalog.numbers.addObject(a1.get());
|
||||
REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(a1.get()) == 1);
|
||||
SkPDFObjNumMap catalog;
|
||||
catalog.addObject(a1.get());
|
||||
REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
|
||||
|
||||
SkString result = emit_to_string(*a2, &catalog);
|
||||
// If appendObjRef misbehaves, then the result would
|
||||
@ -158,22 +151,6 @@ static void TestObjectRef(skiatest::Reporter* reporter) {
|
||||
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
|
||||
// http://code.google.com/p/skia/issues/detail?id=1083.
|
||||
// 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]]");
|
||||
|
||||
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
|
||||
Catalog catalog;
|
||||
catalog.numbers.addObject(referencedArray.get());
|
||||
REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
|
||||
SkPDFObjNumMap catalog;
|
||||
catalog.addObject(referencedArray.get());
|
||||
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
|
||||
referencedArray.get()) == 1);
|
||||
array->appendObjRef(std::move(referencedArray));
|
||||
|
||||
@ -347,9 +324,9 @@ static void TestPDFDict(skiatest::Reporter* reporter) {
|
||||
assert_emit_eq(reporter, *dict, "<</Type /DType>>");
|
||||
|
||||
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
|
||||
Catalog catalog;
|
||||
catalog.numbers.addObject(referencedArray.get());
|
||||
REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
|
||||
SkPDFObjNumMap catalog;
|
||||
catalog.addObject(referencedArray.get());
|
||||
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
|
||||
referencedArray.get()) == 1);
|
||||
dict->insertObjRef("n1", std::move(referencedArray));
|
||||
SkString result = emit_to_string(*dict, &catalog);
|
||||
@ -363,7 +340,6 @@ DEF_TEST(PDFPrimitives, reporter) {
|
||||
TestPDFStream(reporter);
|
||||
TestObjectNumberMap(reporter);
|
||||
TestObjectRef(reporter);
|
||||
TestSubstitute(reporter);
|
||||
test_issue1083();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user