diff -r 000000000000 -r 6474c204b198 gfx/skia/trunk/src/pdf/SkPDFFont.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/skia/trunk/src/pdf/SkPDFFont.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1446 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +#include "SkData.h" +#include "SkFontHost.h" +#include "SkGlyphCache.h" +#include "SkPaint.h" +#include "SkPDFCatalog.h" +#include "SkPDFDevice.h" +#include "SkPDFFont.h" +#include "SkPDFFontImpl.h" +#include "SkPDFStream.h" +#include "SkPDFTypes.h" +#include "SkPDFUtils.h" +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkStream.h" +#include "SkTypefacePriv.h" +#include "SkTypes.h" +#include "SkUtils.h" + +#if defined (SK_SFNTLY_SUBSETTER) +#include SK_SFNTLY_SUBSETTER +#endif + +// PDF's notion of symbolic vs non-symbolic is related to the character set, not +// symbols vs. characters. Rarely is a font the right character set to call it +// non-symbolic, so always call it symbolic. (PDF 1.4 spec, section 5.7.1) +static const int kPdfSymbolic = 4; + +namespace { + +/////////////////////////////////////////////////////////////////////////////// +// File-Local Functions +/////////////////////////////////////////////////////////////////////////////// + +bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType, + size_t* size) { + // PFB sections have a two or six bytes header. 0x80 and a one byte + // section type followed by a four byte section length. Type one is + // an ASCII section (includes a length), type two is a binary section + // (includes a length) and type three is an EOF marker with no length. + const uint8_t* buf = *src; + if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType) { + return false; + } else if (buf[1] == 3) { + return true; + } else if (*len < 6) { + return false; + } + + *size = (size_t)buf[2] | ((size_t)buf[3] << 8) | ((size_t)buf[4] << 16) | + ((size_t)buf[5] << 24); + size_t consumed = *size + 6; + if (consumed > *len) { + return false; + } + *src = *src + consumed; + *len = *len - consumed; + return true; +} + +bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen, + size_t* dataLen, size_t* trailerLen) { + const uint8_t* srcPtr = src; + size_t remaining = size; + + return parsePFBSection(&srcPtr, &remaining, 1, headerLen) && + parsePFBSection(&srcPtr, &remaining, 2, dataLen) && + parsePFBSection(&srcPtr, &remaining, 1, trailerLen) && + parsePFBSection(&srcPtr, &remaining, 3, NULL); +} + +/* The sections of a PFA file are implicitly defined. The body starts + * after the line containing "eexec," and the trailer starts with 512 + * literal 0's followed by "cleartomark" (plus arbitrary white space). + * + * This function assumes that src is NUL terminated, but the NUL + * termination is not included in size. + * + */ +bool parsePFA(const char* src, size_t size, size_t* headerLen, + size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) { + const char* end = src + size; + + const char* dataPos = strstr(src, "eexec"); + if (!dataPos) { + return false; + } + dataPos += strlen("eexec"); + while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') && + dataPos < end) { + dataPos++; + } + *headerLen = dataPos - src; + + const char* trailerPos = strstr(dataPos, "cleartomark"); + if (!trailerPos) { + return false; + } + int zeroCount = 0; + for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) { + if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') { + continue; + } else if (*trailerPos == '0') { + zeroCount++; + } else { + return false; + } + } + if (zeroCount != 512) { + return false; + } + + *hexDataLen = trailerPos - src - *headerLen; + *trailerLen = size - *headerLen - *hexDataLen; + + // Verify that the data section is hex encoded and count the bytes. + int nibbles = 0; + for (; dataPos < trailerPos; dataPos++) { + if (isspace(*dataPos)) { + continue; + } + if (!isxdigit(*dataPos)) { + return false; + } + nibbles++; + } + *dataLen = (nibbles + 1) / 2; + + return true; +} + +int8_t hexToBin(uint8_t c) { + if (!isxdigit(c)) { + return -1; + } else if (c <= '9') { + return c - '0'; + } else if (c <= 'F') { + return c - 'A' + 10; + } else if (c <= 'f') { + return c - 'a' + 10; + } + return -1; +} + +SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen, + size_t* dataLen, size_t* trailerLen) { + // srcStream may be backed by a file or a unseekable fd, so we may not be + // able to use skip(), rewind(), or getMemoryBase(). read()ing through + // the input only once is doable, but very ugly. Furthermore, it'd be nice + // if the data was NUL terminated so that we can use strstr() to search it. + // Make as few copies as possible given these constraints. + SkDynamicMemoryWStream dynamicStream; + SkAutoTUnref staticStream; + SkData* data = NULL; + const uint8_t* src; + size_t srcLen; + if ((srcLen = srcStream->getLength()) > 0) { + staticStream.reset(new SkMemoryStream(srcLen + 1)); + src = (const uint8_t*)staticStream->getMemoryBase(); + if (srcStream->getMemoryBase() != NULL) { + memcpy((void *)src, srcStream->getMemoryBase(), srcLen); + } else { + size_t read = 0; + while (read < srcLen) { + size_t got = srcStream->read((void *)staticStream->getAtPos(), + srcLen - read); + if (got == 0) { + return NULL; + } + read += got; + staticStream->seek(read); + } + } + ((uint8_t *)src)[srcLen] = 0; + } else { + static const size_t kBufSize = 4096; + uint8_t buf[kBufSize]; + size_t amount; + while ((amount = srcStream->read(buf, kBufSize)) > 0) { + dynamicStream.write(buf, amount); + } + amount = 0; + dynamicStream.write(&amount, 1); // NULL terminator. + data = dynamicStream.copyToData(); + src = data->bytes(); + srcLen = data->size() - 1; + } + + // this handles releasing the data we may have gotten from dynamicStream. + // if data is null, it is a no-op + SkAutoDataUnref aud(data); + + if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) { + SkMemoryStream* result = + new SkMemoryStream(*headerLen + *dataLen + *trailerLen); + memcpy((char*)result->getAtPos(), src + 6, *headerLen); + result->seek(*headerLen); + memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen); + result->seek(*headerLen + *dataLen); + memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen, + *trailerLen); + result->rewind(); + return result; + } + + // A PFA has to be converted for PDF. + size_t hexDataLen; + if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen, + trailerLen)) { + SkMemoryStream* result = + new SkMemoryStream(*headerLen + *dataLen + *trailerLen); + memcpy((char*)result->getAtPos(), src, *headerLen); + result->seek(*headerLen); + + const uint8_t* hexData = src + *headerLen; + const uint8_t* trailer = hexData + hexDataLen; + size_t outputOffset = 0; + uint8_t dataByte = 0; // To hush compiler. + bool highNibble = true; + for (; hexData < trailer; hexData++) { + int8_t curNibble = hexToBin(*hexData); + if (curNibble < 0) { + continue; + } + if (highNibble) { + dataByte = curNibble << 4; + highNibble = false; + } else { + dataByte |= curNibble; + highNibble = true; + ((char *)result->getAtPos())[outputOffset++] = dataByte; + } + } + if (!highNibble) { + ((char *)result->getAtPos())[outputOffset++] = dataByte; + } + SkASSERT(outputOffset == *dataLen); + result->seek(*headerLen + outputOffset); + + memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen, + *trailerLen); + result->rewind(); + return result; + } + + return NULL; +} + +// scale from em-units to base-1000, returning as a SkScalar +SkScalar scaleFromFontUnits(int16_t val, uint16_t emSize) { + SkScalar scaled = SkIntToScalar(val); + if (emSize == 1000) { + return scaled; + } else { + return SkScalarMulDiv(scaled, 1000, emSize); + } +} + +void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box, + SkWStream* content) { + // Specify width and bounding box for the glyph. + SkPDFScalar::Append(width, content); + content->writeText(" 0 "); + content->writeDecAsText(box.fLeft); + content->writeText(" "); + content->writeDecAsText(box.fTop); + content->writeText(" "); + content->writeDecAsText(box.fRight); + content->writeText(" "); + content->writeDecAsText(box.fBottom); + content->writeText(" d1\n"); +} + +SkPDFArray* makeFontBBox(SkIRect glyphBBox, uint16_t emSize) { + SkPDFArray* bbox = new SkPDFArray; + bbox->reserve(4); + bbox->appendScalar(scaleFromFontUnits(glyphBBox.fLeft, emSize)); + bbox->appendScalar(scaleFromFontUnits(glyphBBox.fBottom, emSize)); + bbox->appendScalar(scaleFromFontUnits(glyphBBox.fRight, emSize)); + bbox->appendScalar(scaleFromFontUnits(glyphBBox.fTop, emSize)); + return bbox; +} + +SkPDFArray* appendWidth(const int16_t& width, uint16_t emSize, + SkPDFArray* array) { + array->appendScalar(scaleFromFontUnits(width, emSize)); + return array; +} + +SkPDFArray* appendVerticalAdvance( + const SkAdvancedTypefaceMetrics::VerticalMetric& advance, + uint16_t emSize, SkPDFArray* array) { + appendWidth(advance.fVerticalAdvance, emSize, array); + appendWidth(advance.fOriginXDisp, emSize, array); + appendWidth(advance.fOriginYDisp, emSize, array); + return array; +} + +template +SkPDFArray* composeAdvanceData( + SkAdvancedTypefaceMetrics::AdvanceMetric* advanceInfo, + uint16_t emSize, + SkPDFArray* (*appendAdvance)(const Data& advance, uint16_t emSize, + SkPDFArray* array), + Data* defaultAdvance) { + SkPDFArray* result = new SkPDFArray(); + for (; advanceInfo != NULL; advanceInfo = advanceInfo->fNext.get()) { + switch (advanceInfo->fType) { + case SkAdvancedTypefaceMetrics::WidthRange::kDefault: { + SkASSERT(advanceInfo->fAdvance.count() == 1); + *defaultAdvance = advanceInfo->fAdvance[0]; + break; + } + case SkAdvancedTypefaceMetrics::WidthRange::kRange: { + SkAutoTUnref advanceArray(new SkPDFArray()); + for (int j = 0; j < advanceInfo->fAdvance.count(); j++) + appendAdvance(advanceInfo->fAdvance[j], emSize, + advanceArray.get()); + result->appendInt(advanceInfo->fStartId); + result->append(advanceArray.get()); + break; + } + case SkAdvancedTypefaceMetrics::WidthRange::kRun: { + SkASSERT(advanceInfo->fAdvance.count() == 1); + result->appendInt(advanceInfo->fStartId); + result->appendInt(advanceInfo->fEndId); + appendAdvance(advanceInfo->fAdvance[0], emSize, result); + break; + } + } + } + return result; +} + +} // namespace + +static void append_tounicode_header(SkDynamicMemoryWStream* cmap, + uint16_t firstGlyphID, + uint16_t lastGlyphID) { + // 12 dict begin: 12 is an Adobe-suggested value. Shall not change. + // It's there to prevent old version Adobe Readers from malfunctioning. + const char* kHeader = + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n"; + cmap->writeText(kHeader); + + // The /CIDSystemInfo must be consistent to the one in + // SkPDFFont::populateCIDFont(). + // We can not pass over the system info object here because the format is + // different. This is not a reference object. + const char* kSysInfo = + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n"; + cmap->writeText(kSysInfo); + + // The CMapName must be consistent to /CIDSystemInfo above. + // /CMapType 2 means ToUnicode. + // Codespace range just tells the PDF processor the valid range. + const char* kTypeInfoHeader = + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n"; + cmap->writeText(kTypeInfoHeader); + + // e.g. "<0000> \n" + SkString range; + range.appendf("<%04X> <%04X>\n", firstGlyphID, lastGlyphID); + cmap->writeText(range.c_str()); + + const char* kTypeInfoFooter = "endcodespacerange\n"; + cmap->writeText(kTypeInfoFooter); +} + +static void append_cmap_footer(SkDynamicMemoryWStream* cmap) { + const char* kFooter = + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end"; + cmap->writeText(kFooter); +} + +struct BFChar { + uint16_t fGlyphId; + SkUnichar fUnicode; +}; + +struct BFRange { + uint16_t fStart; + uint16_t fEnd; + SkUnichar fUnicode; +}; + +static void append_bfchar_section(const SkTDArray& bfchar, + SkDynamicMemoryWStream* cmap) { + // PDF spec defines that every bf* list can have at most 100 entries. + for (int i = 0; i < bfchar.count(); i += 100) { + int count = bfchar.count() - i; + count = SkMin32(count, 100); + cmap->writeDecAsText(count); + cmap->writeText(" beginbfchar\n"); + for (int j = 0; j < count; ++j) { + cmap->writeText("<"); + cmap->writeHexAsText(bfchar[i + j].fGlyphId, 4); + cmap->writeText("> <"); + cmap->writeHexAsText(bfchar[i + j].fUnicode, 4); + cmap->writeText(">\n"); + } + cmap->writeText("endbfchar\n"); + } +} + +static void append_bfrange_section(const SkTDArray& bfrange, + SkDynamicMemoryWStream* cmap) { + // PDF spec defines that every bf* list can have at most 100 entries. + for (int i = 0; i < bfrange.count(); i += 100) { + int count = bfrange.count() - i; + count = SkMin32(count, 100); + cmap->writeDecAsText(count); + cmap->writeText(" beginbfrange\n"); + for (int j = 0; j < count; ++j) { + cmap->writeText("<"); + cmap->writeHexAsText(bfrange[i + j].fStart, 4); + cmap->writeText("> <"); + cmap->writeHexAsText(bfrange[i + j].fEnd, 4); + cmap->writeText("> <"); + cmap->writeHexAsText(bfrange[i + j].fUnicode, 4); + cmap->writeText(">\n"); + } + cmap->writeText("endbfrange\n"); + } +} + +// Generate and table according to PDF spec 1.4 and Adobe +// Technote 5014. +// The function is not static so we can test it in unit tests. +// +// Current implementation guarantees bfchar and bfrange entries do not overlap. +// +// Current implementation does not attempt aggresive optimizations against +// following case because the specification is not clear. +// +// 4 beginbfchar 1 beginbfchar +// <0003> <0013> <0020> <0014> +// <0005> <0015> to endbfchar +// <0007> <0017> 1 beginbfrange +// <0020> <0014> <0003> <0007> <0013> +// endbfchar endbfrange +// +// Adobe Technote 5014 said: "Code mappings (unlike codespace ranges) may +// overlap, but succeeding maps supersede preceding maps." +// +// In case of searching text in PDF, bfrange will have higher precedence so +// typing char id 0x0014 in search box will get glyph id 0x0004 first. However, +// the spec does not mention how will this kind of conflict being resolved. +// +// For the worst case (having 65536 continuous unicode and we use every other +// one of them), the possible savings by aggressive optimization is 416KB +// pre-compressed and does not provide enough motivation for implementation. + +// FIXME: this should be in a header so that it is separately testable +// ( see caller in tests/ToUnicode.cpp ) +void append_cmap_sections(const SkTDArray& glyphToUnicode, + const SkPDFGlyphSet* subset, + SkDynamicMemoryWStream* cmap, + bool multiByteGlyphs, + uint16_t firstGlyphID, + uint16_t lastGlyphID); + +void append_cmap_sections(const SkTDArray& glyphToUnicode, + const SkPDFGlyphSet* subset, + SkDynamicMemoryWStream* cmap, + bool multiByteGlyphs, + uint16_t firstGlyphID, + uint16_t lastGlyphID) { + if (glyphToUnicode.isEmpty()) { + return; + } + int glyphOffset = 0; + if (!multiByteGlyphs) { + glyphOffset = firstGlyphID - 1; + } + + SkTDArray bfcharEntries; + SkTDArray bfrangeEntries; + + BFRange currentRangeEntry = {0, 0, 0}; + bool rangeEmpty = true; + const int limit = + SkMin32(lastGlyphID + 1, glyphToUnicode.count()) - glyphOffset; + + for (int i = firstGlyphID - glyphOffset; i < limit + 1; ++i) { + bool inSubset = i < limit && + (subset == NULL || subset->has(i + glyphOffset)); + if (!rangeEmpty) { + // PDF spec requires bfrange not changing the higher byte, + // e.g. <1035> <10FF> <2222> is ok, but + // <1035> <1100> <2222> is no good + bool inRange = + i == currentRangeEntry.fEnd + 1 && + i >> 8 == currentRangeEntry.fStart >> 8 && + i < limit && + glyphToUnicode[i + glyphOffset] == + currentRangeEntry.fUnicode + i - currentRangeEntry.fStart; + if (!inSubset || !inRange) { + if (currentRangeEntry.fEnd > currentRangeEntry.fStart) { + bfrangeEntries.push(currentRangeEntry); + } else { + BFChar* entry = bfcharEntries.append(); + entry->fGlyphId = currentRangeEntry.fStart; + entry->fUnicode = currentRangeEntry.fUnicode; + } + rangeEmpty = true; + } + } + if (inSubset) { + currentRangeEntry.fEnd = i; + if (rangeEmpty) { + currentRangeEntry.fStart = i; + currentRangeEntry.fUnicode = glyphToUnicode[i + glyphOffset]; + rangeEmpty = false; + } + } + } + + // The spec requires all bfchar entries for a font must come before bfrange + // entries. + append_bfchar_section(bfcharEntries, cmap); + append_bfrange_section(bfrangeEntries, cmap); +} + +static SkPDFStream* generate_tounicode_cmap( + const SkTDArray& glyphToUnicode, + const SkPDFGlyphSet* subset, + bool multiByteGlyphs, + uint16_t firstGlyphID, + uint16_t lastGlyphID) { + SkDynamicMemoryWStream cmap; + if (multiByteGlyphs) { + append_tounicode_header(&cmap, firstGlyphID, lastGlyphID); + } else { + append_tounicode_header(&cmap, 1, lastGlyphID - firstGlyphID + 1); + } + append_cmap_sections(glyphToUnicode, subset, &cmap, multiByteGlyphs, + firstGlyphID, lastGlyphID); + append_cmap_footer(&cmap); + SkAutoTUnref cmapStream(new SkMemoryStream()); + cmapStream->setData(cmap.copyToData())->unref(); + return new SkPDFStream(cmapStream.get()); +} + +#if defined (SK_SFNTLY_SUBSETTER) +static void sk_delete_array(const void* ptr, size_t, void*) { + // Use C-style cast to cast away const and cast type simultaneously. + delete[] (unsigned char*)ptr; +} +#endif + +static int get_subset_font_stream(const char* fontName, + const SkTypeface* typeface, + const SkTDArray& subset, + SkPDFStream** fontStream) { + int ttcIndex; + SkAutoTUnref fontData(typeface->openStream(&ttcIndex)); + + int fontSize = fontData->getLength(); + +#if defined (SK_SFNTLY_SUBSETTER) + // Read font into buffer. + SkPDFStream* subsetFontStream = NULL; + SkTDArray originalFont; + originalFont.setCount(fontSize); + if (fontData->read(originalFont.begin(), fontSize) == (size_t)fontSize) { + unsigned char* subsetFont = NULL; + // sfntly requires unsigned int* to be passed in, as far as we know, + // unsigned int is equivalent to uint32_t on all platforms. + SK_COMPILE_ASSERT(sizeof(unsigned int) == sizeof(uint32_t), + unsigned_int_not_32_bits); + int subsetFontSize = SfntlyWrapper::SubsetFont(fontName, + originalFont.begin(), + fontSize, + subset.begin(), + subset.count(), + &subsetFont); + if (subsetFontSize > 0 && subsetFont != NULL) { + SkAutoDataUnref data(SkData::NewWithProc(subsetFont, + subsetFontSize, + sk_delete_array, + NULL)); + subsetFontStream = new SkPDFStream(data.get()); + fontSize = subsetFontSize; + } + } + if (subsetFontStream) { + *fontStream = subsetFontStream; + return fontSize; + } + fontData->rewind(); +#else + sk_ignore_unused_variable(fontName); + sk_ignore_unused_variable(subset); +#endif + + // Fail over: just embed the whole font. + *fontStream = new SkPDFStream(fontData.get()); + return fontSize; +} + +/////////////////////////////////////////////////////////////////////////////// +// 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::merge(const SkPDFGlyphSet& usage) { + fBitSet.orBits(usage.fBitSet); +} + +void SkPDFGlyphSet::exportTo(SkTDArray* glyphIDs) const { + fBitSet.exportTo(glyphIDs); +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFGlyphSetMap +/////////////////////////////////////////////////////////////////////////////// +SkPDFGlyphSetMap::FontGlyphSetPair::FontGlyphSetPair(SkPDFFont* font, + SkPDFGlyphSet* glyphSet) + : fFont(font), + fGlyphSet(glyphSet) { +} + +SkPDFGlyphSetMap::F2BIter::F2BIter(const SkPDFGlyphSetMap& map) { + reset(map); +} + +const SkPDFGlyphSetMap::FontGlyphSetPair* SkPDFGlyphSetMap::F2BIter::next() const { + if (fIndex >= fMap->count()) { + return NULL; + } + return &((*fMap)[fIndex++]); +} + +void SkPDFGlyphSetMap::F2BIter::reset(const SkPDFGlyphSetMap& map) { + fMap = &(map.fMap); + fIndex = 0; +} + +SkPDFGlyphSetMap::SkPDFGlyphSetMap() { +} + +SkPDFGlyphSetMap::~SkPDFGlyphSetMap() { + reset(); +} + +void SkPDFGlyphSetMap::merge(const SkPDFGlyphSetMap& usage) { + for (int i = 0; i < usage.fMap.count(); ++i) { + SkPDFGlyphSet* myUsage = getGlyphSetForFont(usage.fMap[i].fFont); + myUsage->merge(*(usage.fMap[i].fGlyphSet)); + } +} + +void SkPDFGlyphSetMap::reset() { + for (int i = 0; i < fMap.count(); ++i) { + delete fMap[i].fGlyphSet; // Should not be NULL. + } + 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; + } + } + fMap.append(); + index = fMap.count() - 1; + fMap[index].fFont = font; + fMap[index].fGlyphSet = new SkPDFGlyphSet(); + return fMap[index].fGlyphSet; +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFFont +/////////////////////////////////////////////////////////////////////////////// + +/* Font subset design: It would be nice to be able to subset fonts + * (particularly type 3 fonts), but it's a lot of work and not a priority. + * + * Resources are canonicalized and uniqueified by pointer so there has to be + * some additional state indicating which subset of the font is used. It + * must be maintained at the page granularity and then combined at the document + * granularity. a) change SkPDFFont to fill in its state on demand, kind of + * like SkPDFGraphicState. b) maintain a per font glyph usage class in each + * page/pdf device. c) in the document, retrieve the per font glyph usage + * from each page and combine it and ask for a resource with that subset. + */ + +SkPDFFont::~SkPDFFont() { + SkAutoMutexAcquire lock(CanonicalFontsMutex()); + int index = -1; + for (int i = 0 ; i < CanonicalFonts().count() ; i++) { + if (CanonicalFonts()[i].fFont == this) { + index = i; + } + } + + SkDEBUGCODE(int indexFound;) + SkASSERT(index == -1 || + (Find(fTypeface->uniqueID(), + fFirstGlyphID, + &indexFound) && + index == indexFound)); + if (index >= 0) { + CanonicalFonts().removeShuffle(index); + } + fResources.unrefAll(); +} + +void SkPDFFont::getResources(const SkTSet& knownResourceObjects, + SkTSet* newResourceObjects) { + GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); +} + +SkTypeface* SkPDFFont::typeface() { + return fTypeface.get(); +} + +SkAdvancedTypefaceMetrics::FontType SkPDFFont::getType() { + return fFontType; +} + +bool SkPDFFont::hasGlyph(uint16_t id) { + return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0; +} + +size_t SkPDFFont::glyphsToPDFFontEncoding(uint16_t* glyphIDs, + size_t numGlyphs) { + // A font with multibyte glyphs will support all glyph IDs in a single font. + if (this->multiByteGlyphs()) { + return numGlyphs; + } + + for (size_t i = 0; i < numGlyphs; i++) { + if (glyphIDs[i] == 0) { + continue; + } + if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) { + return i; + } + glyphIDs[i] -= (fFirstGlyphID - 1); + } + + return numGlyphs; +} + +// static +SkPDFFont* SkPDFFont::GetFontResource(SkTypeface* typeface, uint16_t glyphID) { + SkAutoMutexAcquire lock(CanonicalFontsMutex()); + + SkAutoResolveDefaultTypeface autoResolve(typeface); + typeface = autoResolve.get(); + + const uint32_t fontID = typeface->uniqueID(); + int relatedFontIndex; + if (Find(fontID, glyphID, &relatedFontIndex)) { + CanonicalFonts()[relatedFontIndex].fFont->ref(); + return CanonicalFonts()[relatedFontIndex].fFont; + } + + SkAutoTUnref fontMetrics; + SkPDFDict* relatedFontDescriptor = NULL; + if (relatedFontIndex >= 0) { + SkPDFFont* relatedFont = CanonicalFonts()[relatedFontIndex].fFont; + fontMetrics.reset(relatedFont->fontInfo()); + SkSafeRef(fontMetrics.get()); + relatedFontDescriptor = relatedFont->getFontDescriptor(); + + // This only is to catch callers who pass invalid glyph ids. + // If glyph id is invalid, then we will create duplicate entries + // for True Type fonts. + SkAdvancedTypefaceMetrics::FontType fontType = + fontMetrics.get() ? fontMetrics.get()->fType : + SkAdvancedTypefaceMetrics::kOther_Font; + + if (fontType == SkAdvancedTypefaceMetrics::kType1CID_Font || + fontType == SkAdvancedTypefaceMetrics::kTrueType_Font) { + CanonicalFonts()[relatedFontIndex].fFont->ref(); + return CanonicalFonts()[relatedFontIndex].fFont; + } + } else { + SkAdvancedTypefaceMetrics::PerGlyphInfo info; + info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo; + info = SkTBitOr( + info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo); +#if !defined (SK_SFNTLY_SUBSETTER) + info = SkTBitOr( + info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo); +#endif + fontMetrics.reset( + typeface->getAdvancedTypefaceMetrics(info, NULL, 0)); +#if defined (SK_SFNTLY_SUBSETTER) + if (fontMetrics.get() && + fontMetrics->fType != SkAdvancedTypefaceMetrics::kTrueType_Font) { + // Font does not support subsetting, get new info with advance. + info = SkTBitOr( + info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo); + fontMetrics.reset( + typeface->getAdvancedTypefaceMetrics(info, NULL, 0)); + } +#endif + } + + SkPDFFont* font = Create(fontMetrics.get(), typeface, glyphID, + relatedFontDescriptor); + FontRec newEntry(font, fontID, font->fFirstGlyphID); + CanonicalFonts().push(newEntry); + return font; // Return the reference new SkPDFFont() created. +} + +SkPDFFont* SkPDFFont::getFontSubset(const SkPDFGlyphSet*) { + return NULL; // Default: no support. +} + +// static +SkTDArray& SkPDFFont::CanonicalFonts() { + // This initialization is only thread safe with gcc. + static SkTDArray gCanonicalFonts; + return gCanonicalFonts; +} + +// static +SkBaseMutex& SkPDFFont::CanonicalFontsMutex() { + // This initialization is only thread safe with gcc, or when + // POD-style mutex initialization is used. + SK_DECLARE_STATIC_MUTEX(gCanonicalFontsMutex); + return gCanonicalFontsMutex; +} + +// static +bool SkPDFFont::Find(uint32_t fontID, uint16_t glyphID, int* index) { + // TODO(vandebo): Optimize this, do only one search? + FontRec search(NULL, fontID, glyphID); + *index = CanonicalFonts().find(search); + if (*index >= 0) { + return true; + } + search.fGlyphID = 0; + *index = CanonicalFonts().find(search); + return false; +} + +SkPDFFont::SkPDFFont(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface, + SkPDFDict* relatedFontDescriptor) + : SkPDFDict("Font"), + fTypeface(ref_or_default(typeface)), + fFirstGlyphID(1), + fLastGlyphID(info ? info->fLastGlyphID : 0), + fFontInfo(SkSafeRef(info)), + fDescriptor(SkSafeRef(relatedFontDescriptor)) { + if (info == NULL) { + fFontType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; + } else if (info->fMultiMaster) { + fFontType = SkAdvancedTypefaceMetrics::kOther_Font; + } else { + fFontType = info->fType; + } +} + +// static +SkPDFFont* SkPDFFont::Create(SkAdvancedTypefaceMetrics* info, + SkTypeface* typeface, uint16_t glyphID, + SkPDFDict* relatedFontDescriptor) { + SkAdvancedTypefaceMetrics::FontType type = + info ? info->fType : SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; + + if (info && info->fMultiMaster) { + NOT_IMPLEMENTED(true, true); + return new SkPDFType3Font(info, + typeface, + glyphID); + } + if (type == SkAdvancedTypefaceMetrics::kType1CID_Font || + type == SkAdvancedTypefaceMetrics::kTrueType_Font) { + SkASSERT(relatedFontDescriptor == NULL); + return new SkPDFType0Font(info, typeface); + } + if (type == SkAdvancedTypefaceMetrics::kType1_Font) { + return new SkPDFType1Font(info, + typeface, + glyphID, + relatedFontDescriptor); + } + + SkASSERT(type == SkAdvancedTypefaceMetrics::kCFF_Font || + type == SkAdvancedTypefaceMetrics::kOther_Font || + type == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font); + + return new SkPDFType3Font(info, typeface, glyphID); +} + +SkAdvancedTypefaceMetrics* SkPDFFont::fontInfo() { + return fFontInfo.get(); +} + +void SkPDFFont::setFontInfo(SkAdvancedTypefaceMetrics* info) { + if (info == NULL || info == fFontInfo.get()) { + return; + } + fFontInfo.reset(info); + SkSafeRef(info); +} + +uint16_t SkPDFFont::firstGlyphID() const { + return fFirstGlyphID; +} + +uint16_t SkPDFFont::lastGlyphID() const { + return fLastGlyphID; +} + +void SkPDFFont::setLastGlyphID(uint16_t glyphID) { + fLastGlyphID = glyphID; +} + +void SkPDFFont::addResource(SkPDFObject* object) { + SkASSERT(object != NULL); + fResources.push(object); + object->ref(); +} + +SkPDFDict* SkPDFFont::getFontDescriptor() { + return fDescriptor.get(); +} + +void SkPDFFont::setFontDescriptor(SkPDFDict* descriptor) { + fDescriptor.reset(descriptor); + SkSafeRef(descriptor); +} + +bool SkPDFFont::addCommonFontDescriptorEntries(int16_t defaultWidth) { + if (fDescriptor.get() == NULL) { + return false; + } + + const uint16_t emSize = fFontInfo->fEmSize; + + fDescriptor->insertName("FontName", fFontInfo->fFontName); + fDescriptor->insertInt("Flags", fFontInfo->fStyle | kPdfSymbolic); + fDescriptor->insertScalar("Ascent", + scaleFromFontUnits(fFontInfo->fAscent, emSize)); + fDescriptor->insertScalar("Descent", + scaleFromFontUnits(fFontInfo->fDescent, emSize)); + fDescriptor->insertScalar("StemV", + scaleFromFontUnits(fFontInfo->fStemV, emSize)); + fDescriptor->insertScalar("CapHeight", + scaleFromFontUnits(fFontInfo->fCapHeight, emSize)); + fDescriptor->insertInt("ItalicAngle", fFontInfo->fItalicAngle); + fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox, + fFontInfo->fEmSize))->unref(); + + if (defaultWidth > 0) { + fDescriptor->insertScalar("MissingWidth", + scaleFromFontUnits(defaultWidth, emSize)); + } + return true; +} + +void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(int16_t glyphID) { + // Single byte glyph encoding supports a max of 255 glyphs. + fFirstGlyphID = glyphID - (glyphID - 1) % 255; + if (fLastGlyphID > fFirstGlyphID + 255 - 1) { + fLastGlyphID = fFirstGlyphID + 255 - 1; + } +} + +bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const { + if (fFontID != b.fFontID) { + return false; + } + if (fFont != NULL && b.fFont != NULL) { + return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID && + fFont->fLastGlyphID == b.fFont->fLastGlyphID; + } + if (fGlyphID == 0 || b.fGlyphID == 0) { + return true; + } + + if (fFont != NULL) { + return fFont->fFirstGlyphID <= b.fGlyphID && + b.fGlyphID <= fFont->fLastGlyphID; + } else if (b.fFont != NULL) { + return b.fFont->fFirstGlyphID <= fGlyphID && + fGlyphID <= b.fFont->fLastGlyphID; + } + return fGlyphID == b.fGlyphID; +} + +SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID) + : fFont(font), + fFontID(fontID), + fGlyphID(glyphID) { +} + +void SkPDFFont::populateToUnicodeTable(const SkPDFGlyphSet* subset) { + if (fFontInfo == NULL || fFontInfo->fGlyphToUnicode.begin() == NULL) { + return; + } + SkAutoTUnref pdfCmap( + generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset, + multiByteGlyphs(), firstGlyphID(), + lastGlyphID())); + addResource(pdfCmap.get()); + insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref(); +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFType0Font +/////////////////////////////////////////////////////////////////////////////// + +SkPDFType0Font::SkPDFType0Font(SkAdvancedTypefaceMetrics* info, + SkTypeface* typeface) + : SkPDFFont(info, typeface, NULL) { + SkDEBUGCODE(fPopulated = false); +} + +SkPDFType0Font::~SkPDFType0Font() {} + +SkPDFFont* SkPDFType0Font::getFontSubset(const SkPDFGlyphSet* subset) { + SkPDFType0Font* newSubset = new SkPDFType0Font(fontInfo(), typeface()); + newSubset->populate(subset); + return newSubset; +} + +#ifdef SK_DEBUG +void SkPDFType0Font::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + SkASSERT(fPopulated); + return INHERITED::emitObject(stream, catalog, indirect); +} +#endif + +bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset) { + insertName("Subtype", "Type0"); + insertName("BaseFont", fontInfo()->fFontName); + insertName("Encoding", "Identity-H"); + + SkAutoTUnref newCIDFont( + new SkPDFCIDFont(fontInfo(), typeface(), subset)); + addResource(newCIDFont.get()); + SkAutoTUnref descendantFonts(new SkPDFArray()); + descendantFonts->append(new SkPDFObjRef(newCIDFont.get()))->unref(); + insert("DescendantFonts", descendantFonts.get()); + + populateToUnicodeTable(subset); + + SkDEBUGCODE(fPopulated = true); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFCIDFont +/////////////////////////////////////////////////////////////////////////////// + +SkPDFCIDFont::SkPDFCIDFont(SkAdvancedTypefaceMetrics* info, + SkTypeface* typeface, const SkPDFGlyphSet* subset) + : SkPDFFont(info, typeface, NULL) { + populate(subset); +} + +SkPDFCIDFont::~SkPDFCIDFont() {} + +bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth, + const SkTDArray* subset) { + SkAutoTUnref descriptor(new SkPDFDict("FontDescriptor")); + setFontDescriptor(descriptor.get()); + addResource(descriptor.get()); + + switch (getType()) { + case SkAdvancedTypefaceMetrics::kTrueType_Font: { + SkASSERT(subset); + // Font subsetting + SkPDFStream* rawStream = NULL; + int fontSize = get_subset_font_stream(fontInfo()->fFontName.c_str(), + typeface(), + *subset, + &rawStream); + SkASSERT(fontSize); + SkASSERT(rawStream); + SkAutoTUnref fontStream(rawStream); + addResource(fontStream.get()); + + fontStream->insertInt("Length1", fontSize); + descriptor->insert("FontFile2", + new SkPDFObjRef(fontStream.get()))->unref(); + break; + } + case SkAdvancedTypefaceMetrics::kCFF_Font: + case SkAdvancedTypefaceMetrics::kType1CID_Font: { + int ttcIndex; + SkAutoTUnref fontData(typeface()->openStream(&ttcIndex)); + SkAutoTUnref fontStream( + new SkPDFStream(fontData.get())); + addResource(fontStream.get()); + + if (getType() == SkAdvancedTypefaceMetrics::kCFF_Font) { + fontStream->insertName("Subtype", "Type1C"); + } else { + fontStream->insertName("Subtype", "CIDFontType0c"); + } + descriptor->insert("FontFile3", + new SkPDFObjRef(fontStream.get()))->unref(); + break; + } + default: + SkASSERT(false); + } + + insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref(); + return addCommonFontDescriptorEntries(defaultWidth); +} + +bool SkPDFCIDFont::populate(const SkPDFGlyphSet* subset) { + // Generate new font metrics with advance info for true type fonts. + if (fontInfo()->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) { + // Generate glyph id array. + SkTDArray glyphIDs; + if (subset) { + // Always include glyph 0. + if (!subset->has(0)) { + glyphIDs.push(0); + } + subset->exportTo(&glyphIDs); + } + + SkAdvancedTypefaceMetrics::PerGlyphInfo info; + info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo; + info = SkTBitOr( + info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo); + uint32_t* glyphs = (glyphIDs.count() == 0) ? NULL : glyphIDs.begin(); + uint32_t glyphsCount = glyphs ? glyphIDs.count() : 0; + SkAutoTUnref fontMetrics( + typeface()->getAdvancedTypefaceMetrics(info, glyphs, glyphsCount)); + setFontInfo(fontMetrics.get()); + addFontDescriptor(0, &glyphIDs); + } else { + // Other CID fonts + addFontDescriptor(0, NULL); + } + + insertName("BaseFont", fontInfo()->fFontName); + + if (getType() == SkAdvancedTypefaceMetrics::kType1CID_Font) { + insertName("Subtype", "CIDFontType0"); + } else if (getType() == SkAdvancedTypefaceMetrics::kTrueType_Font) { + insertName("Subtype", "CIDFontType2"); + insertName("CIDToGIDMap", "Identity"); + } else { + SkASSERT(false); + } + + SkAutoTUnref sysInfo(new SkPDFDict); + sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref(); + sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref(); + sysInfo->insertInt("Supplement", 0); + insert("CIDSystemInfo", sysInfo.get()); + + if (fontInfo()->fGlyphWidths.get()) { + int16_t defaultWidth = 0; + SkAutoTUnref widths( + composeAdvanceData(fontInfo()->fGlyphWidths.get(), + fontInfo()->fEmSize, &appendWidth, + &defaultWidth)); + if (widths->size()) + insert("W", widths.get()); + if (defaultWidth != 0) { + insertScalar("DW", scaleFromFontUnits(defaultWidth, + fontInfo()->fEmSize)); + } + } + if (fontInfo()->fVerticalMetrics.get()) { + struct SkAdvancedTypefaceMetrics::VerticalMetric defaultAdvance; + defaultAdvance.fVerticalAdvance = 0; + defaultAdvance.fOriginXDisp = 0; + defaultAdvance.fOriginYDisp = 0; + SkAutoTUnref advances( + composeAdvanceData(fontInfo()->fVerticalMetrics.get(), + fontInfo()->fEmSize, &appendVerticalAdvance, + &defaultAdvance)); + if (advances->size()) + insert("W2", advances.get()); + if (defaultAdvance.fVerticalAdvance || + defaultAdvance.fOriginXDisp || + defaultAdvance.fOriginYDisp) { + insert("DW2", appendVerticalAdvance(defaultAdvance, + fontInfo()->fEmSize, + new SkPDFArray))->unref(); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFType1Font +/////////////////////////////////////////////////////////////////////////////// + +SkPDFType1Font::SkPDFType1Font(SkAdvancedTypefaceMetrics* info, + SkTypeface* typeface, + uint16_t glyphID, + SkPDFDict* relatedFontDescriptor) + : SkPDFFont(info, typeface, relatedFontDescriptor) { + populate(glyphID); +} + +SkPDFType1Font::~SkPDFType1Font() {} + +bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) { + if (getFontDescriptor() != NULL) { + SkPDFDict* descriptor = getFontDescriptor(); + addResource(descriptor); + insert("FontDescriptor", new SkPDFObjRef(descriptor))->unref(); + return true; + } + + SkAutoTUnref descriptor(new SkPDFDict("FontDescriptor")); + setFontDescriptor(descriptor.get()); + + int ttcIndex; + size_t header SK_INIT_TO_AVOID_WARNING; + size_t data SK_INIT_TO_AVOID_WARNING; + size_t trailer SK_INIT_TO_AVOID_WARNING; + SkAutoTUnref rawFontData(typeface()->openStream(&ttcIndex)); + SkStream* fontData = handleType1Stream(rawFontData.get(), &header, &data, + &trailer); + if (fontData == NULL) { + return false; + } + SkAutoTUnref fontStream(new SkPDFStream(fontData)); + addResource(fontStream.get()); + fontStream->insertInt("Length1", header); + fontStream->insertInt("Length2", data); + fontStream->insertInt("Length3", trailer); + descriptor->insert("FontFile", new SkPDFObjRef(fontStream.get()))->unref(); + + addResource(descriptor.get()); + insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref(); + + return addCommonFontDescriptorEntries(defaultWidth); +} + +bool SkPDFType1Font::populate(int16_t glyphID) { + SkASSERT(!fontInfo()->fVerticalMetrics.get()); + SkASSERT(fontInfo()->fGlyphWidths.get()); + + adjustGlyphRangeForSingleByteEncoding(glyphID); + + int16_t defaultWidth = 0; + const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = NULL; + const SkAdvancedTypefaceMetrics::WidthRange* widthEntry; + for (widthEntry = fontInfo()->fGlyphWidths.get(); + widthEntry != NULL; + widthEntry = widthEntry->fNext.get()) { + switch (widthEntry->fType) { + case SkAdvancedTypefaceMetrics::WidthRange::kDefault: + defaultWidth = widthEntry->fAdvance[0]; + break; + case SkAdvancedTypefaceMetrics::WidthRange::kRun: + SkASSERT(false); + break; + case SkAdvancedTypefaceMetrics::WidthRange::kRange: + SkASSERT(widthRangeEntry == NULL); + widthRangeEntry = widthEntry; + break; + } + } + + if (!addFontDescriptor(defaultWidth)) { + return false; + } + + insertName("Subtype", "Type1"); + insertName("BaseFont", fontInfo()->fFontName); + + addWidthInfoFromRange(defaultWidth, widthRangeEntry); + + SkAutoTUnref encoding(new SkPDFDict("Encoding")); + insert("Encoding", encoding.get()); + + SkAutoTUnref encDiffs(new SkPDFArray); + encoding->insert("Differences", encDiffs.get()); + + encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2); + encDiffs->appendInt(1); + for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) { + encDiffs->appendName(fontInfo()->fGlyphNames->get()[gID].c_str()); + } + + return true; +} + +void SkPDFType1Font::addWidthInfoFromRange( + int16_t defaultWidth, + const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) { + SkAutoTUnref widthArray(new SkPDFArray()); + int firstChar = 0; + if (widthRangeEntry) { + const uint16_t emSize = fontInfo()->fEmSize; + int startIndex = firstGlyphID() - widthRangeEntry->fStartId; + int endIndex = startIndex + lastGlyphID() - firstGlyphID() + 1; + if (startIndex < 0) + startIndex = 0; + if (endIndex > widthRangeEntry->fAdvance.count()) + endIndex = widthRangeEntry->fAdvance.count(); + if (widthRangeEntry->fStartId == 0) { + appendWidth(widthRangeEntry->fAdvance[0], emSize, widthArray.get()); + } else { + firstChar = startIndex + widthRangeEntry->fStartId; + } + for (int i = startIndex; i < endIndex; i++) { + appendWidth(widthRangeEntry->fAdvance[i], emSize, widthArray.get()); + } + } else { + appendWidth(defaultWidth, 1000, widthArray.get()); + } + insertInt("FirstChar", firstChar); + insertInt("LastChar", firstChar + widthArray->size() - 1); + insert("Widths", widthArray.get()); +} + +/////////////////////////////////////////////////////////////////////////////// +// class SkPDFType3Font +/////////////////////////////////////////////////////////////////////////////// + +SkPDFType3Font::SkPDFType3Font(SkAdvancedTypefaceMetrics* info, + SkTypeface* typeface, + uint16_t glyphID) + : SkPDFFont(info, typeface, NULL) { + populate(glyphID); +} + +SkPDFType3Font::~SkPDFType3Font() {} + +bool SkPDFType3Font::populate(int16_t glyphID) { + SkPaint paint; + paint.setTypeface(typeface()); + paint.setTextSize(1000); + SkAutoGlyphCache autoCache(paint, NULL, NULL); + SkGlyphCache* cache = autoCache.getCache(); + // If fLastGlyphID isn't set (because there is not fFontInfo), look it up. + if (lastGlyphID() == 0) { + setLastGlyphID(cache->getGlyphCount() - 1); + } + + adjustGlyphRangeForSingleByteEncoding(glyphID); + + insertName("Subtype", "Type3"); + // Flip about the x-axis and scale by 1/1000. + SkMatrix fontMatrix; + fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000)); + insert("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix))->unref(); + + SkAutoTUnref charProcs(new SkPDFDict); + insert("CharProcs", charProcs.get()); + + SkAutoTUnref encoding(new SkPDFDict("Encoding")); + insert("Encoding", encoding.get()); + + SkAutoTUnref encDiffs(new SkPDFArray); + encoding->insert("Differences", encDiffs.get()); + encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2); + encDiffs->appendInt(1); + + SkAutoTUnref widthArray(new SkPDFArray()); + + SkIRect bbox = SkIRect::MakeEmpty(); + for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) { + SkString characterName; + characterName.printf("gid%d", gID); + encDiffs->appendName(characterName.c_str()); + + const SkGlyph& glyph = cache->getGlyphIDMetrics(gID); + widthArray->appendScalar(SkFixedToScalar(glyph.fAdvanceX)); + SkIRect glyphBBox = SkIRect::MakeXYWH(glyph.fLeft, glyph.fTop, + glyph.fWidth, glyph.fHeight); + bbox.join(glyphBBox); + + SkDynamicMemoryWStream content; + setGlyphWidthAndBoundingBox(SkFixedToScalar(glyph.fAdvanceX), glyphBBox, + &content); + const SkPath* path = cache->findPath(glyph); + if (path) { + SkPDFUtils::EmitPath(*path, paint.getStyle(), &content); + SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(), + &content); + } + SkAutoTUnref glyphStream(new SkMemoryStream()); + glyphStream->setData(content.copyToData())->unref(); + + SkAutoTUnref glyphDescription( + new SkPDFStream(glyphStream.get())); + addResource(glyphDescription.get()); + charProcs->insert(characterName.c_str(), + new SkPDFObjRef(glyphDescription.get()))->unref(); + } + + insert("FontBBox", makeFontBBox(bbox, 1000))->unref(); + insertInt("FirstChar", 1); + insertInt("LastChar", lastGlyphID() - firstGlyphID() + 1); + insert("Widths", widthArray.get()); + insertName("CIDToGIDMap", "Identity"); + + populateToUnicodeTable(NULL); + return true; +}