diff options
Diffstat (limited to 'gfx/skia/skia/src/pdf/SkPDFType1Font.cpp')
-rw-r--r-- | gfx/skia/skia/src/pdf/SkPDFType1Font.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/pdf/SkPDFType1Font.cpp b/gfx/skia/skia/src/pdf/SkPDFType1Font.cpp new file mode 100644 index 0000000000..da17281ef4 --- /dev/null +++ b/gfx/skia/skia/src/pdf/SkPDFType1Font.cpp @@ -0,0 +1,339 @@ +// Copyright 2019 Google LLC. +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#include "src/pdf/SkPDFType1Font.h" + +#include "include/private/base/SkTemplates.h" +#include "include/private/base/SkTo.h" +#include "src/core/SkStrike.h" +#include "src/core/SkStrikeSpec.h" + +#include <ctype.h> + +using namespace skia_private; + +/* + "A standard Type 1 font program, as described in the Adobe Type 1 + Font Format specification, consists of three parts: a clear-text + portion (written using PostScript syntax), an encrypted portion, and + a fixed-content portion. The fixed-content portion contains 512 + ASCII zeros followed by a cleartomark operator, and perhaps followed + by additional data. Although the encrypted portion of a standard + Type 1 font may be in binary or ASCII hexadecimal format, PDF + supports only the binary format." +*/ +static 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; +} + +static 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, nullptr); +} + +/* 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. + * + */ +static 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; + } + // isxdigit() is locale-sensitive https://bugs.skia.org/8285 + if (nullptr == strchr("0123456789abcdefABCDEF", *dataPos)) { + return false; + } + nibbles++; + } + *dataLen = (nibbles + 1) / 2; + + return true; +} + +static 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; +} + +static sk_sp<SkData> convert_type1_font_stream(std::unique_ptr<SkStreamAsset> srcStream, + size_t* headerLen, + size_t* dataLen, + size_t* trailerLen) { + size_t srcLen = srcStream ? srcStream->getLength() : 0; + SkASSERT(srcLen); + if (!srcLen) { + return nullptr; + } + // Flatten and Nul-terminate the source stream so that we can use + // strstr() to search it. + AutoTMalloc<uint8_t> sourceBuffer(SkToInt(srcLen + 1)); + (void)srcStream->read(sourceBuffer.get(), srcLen); + sourceBuffer[SkToInt(srcLen)] = 0; + const uint8_t* src = sourceBuffer.get(); + + if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) { + static const int kPFBSectionHeaderLength = 6; + const size_t length = *headerLen + *dataLen + *trailerLen; + SkASSERT(length > 0); + SkASSERT(length + (2 * kPFBSectionHeaderLength) <= srcLen); + + sk_sp<SkData> data(SkData::MakeUninitialized(length)); + + const uint8_t* const srcHeader = src + kPFBSectionHeaderLength; + // There is a six-byte section header before header and data + // (but not trailer) that we're not going to copy. + const uint8_t* const srcData = srcHeader + *headerLen + kPFBSectionHeaderLength; + const uint8_t* const srcTrailer = srcData + *headerLen; + + uint8_t* const resultHeader = (uint8_t*)data->writable_data(); + uint8_t* const resultData = resultHeader + *headerLen; + uint8_t* const resultTrailer = resultData + *dataLen; + + SkASSERT(resultTrailer + *trailerLen == resultHeader + length); + + memcpy(resultHeader, srcHeader, *headerLen); + memcpy(resultData, srcData, *dataLen); + memcpy(resultTrailer, srcTrailer, *trailerLen); + + return data; + } + + // A PFA has to be converted for PDF. + size_t hexDataLen; + if (!parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen, + trailerLen)) { + return nullptr; + } + const size_t length = *headerLen + *dataLen + *trailerLen; + SkASSERT(length > 0); + auto data = SkData::MakeUninitialized(length); + uint8_t* buffer = (uint8_t*)data->writable_data(); + + memcpy(buffer, src, *headerLen); + uint8_t* const resultData = &(buffer[*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; + resultData[outputOffset++] = dataByte; + } + } + if (!highNibble) { + resultData[outputOffset++] = dataByte; + } + SkASSERT(outputOffset == *dataLen); + + uint8_t* const resultTrailer = &(buffer[SkToInt(*headerLen + outputOffset)]); + memcpy(resultTrailer, src + *headerLen + hexDataLen, *trailerLen); + return data; +} + +inline static bool can_embed(const SkAdvancedTypefaceMetrics& metrics) { + return !SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag); +} + +inline static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { + return emSize == 1000 ? scaled : scaled * 1000 / emSize; +} + +static SkPDFIndirectReference make_type1_font_descriptor(SkPDFDocument* doc, + const SkTypeface* typeface, + const SkAdvancedTypefaceMetrics* info) { + SkPDFDict descriptor("FontDescriptor"); + uint16_t emSize = SkToU16(typeface->getUnitsPerEm()); + if (info) { + SkPDFFont::PopulateCommonFontDescriptor(&descriptor, *info, emSize, 0); + if (can_embed(*info)) { + 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; + std::unique_ptr<SkStreamAsset> rawFontData = typeface->openStream(&ttcIndex); + sk_sp<SkData> fontData = convert_type1_font_stream(std::move(rawFontData), + &header, &data, &trailer); + if (fontData) { + std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict(); + dict->insertInt("Length1", header); + dict->insertInt("Length2", data); + dict->insertInt("Length3", trailer); + auto fontStream = SkMemoryStream::Make(std::move(fontData)); + descriptor.insertRef("FontFile", + SkPDFStreamOut(std::move(dict), std::move(fontStream), + doc, SkPDFSteamCompressionEnabled::Yes)); + } + } + } + return doc->emit(descriptor); +} + + +static const std::vector<SkString>& type_1_glyphnames(SkPDFDocument* canon, + const SkTypeface* typeface) { + SkTypefaceID typefaceID = typeface->uniqueID(); + const std::vector<SkString>* glyphNames = canon->fType1GlyphNames.find(typefaceID); + if (!glyphNames) { + std::vector<SkString> names(typeface->countGlyphs()); + SkPDFFont::GetType1GlyphNames(*typeface, names.data()); + glyphNames = canon->fType1GlyphNames.set(typefaceID, std::move(names)); + } + SkASSERT(glyphNames); + return *glyphNames; +} + +static SkPDFIndirectReference type1_font_descriptor(SkPDFDocument* doc, + const SkTypeface* typeface) { + SkTypefaceID typefaceID = typeface->uniqueID(); + if (SkPDFIndirectReference* ptr = doc->fFontDescriptors.find(typefaceID)) { + return *ptr; + } + const SkAdvancedTypefaceMetrics* info = SkPDFFont::GetMetrics(typeface, doc); + auto fontDescriptor = make_type1_font_descriptor(doc, typeface, info); + doc->fFontDescriptors.set(typefaceID, fontDescriptor); + return fontDescriptor; +} + + +void SkPDFEmitType1Font(const SkPDFFont& pdfFont, SkPDFDocument* doc) { + SkTypeface* typeface = pdfFont.typeface(); + const std::vector<SkString>& glyphNames = type_1_glyphnames(doc, typeface); + SkGlyphID firstGlyphID = pdfFont.firstGlyphID(); + SkGlyphID lastGlyphID = pdfFont.lastGlyphID(); + + SkPDFDict font("Font"); + font.insertRef("FontDescriptor", type1_font_descriptor(doc, typeface)); + font.insertName("Subtype", "Type1"); + if (const SkAdvancedTypefaceMetrics* info = SkPDFFont::GetMetrics(typeface, doc)) { + font.insertName("BaseFont", info->fPostScriptName); + } + + // glyphCount not including glyph 0 + unsigned glyphCount = 1 + lastGlyphID - firstGlyphID; + SkASSERT(glyphCount > 0 && glyphCount <= 255); + font.insertInt("FirstChar", (size_t)0); + font.insertInt("LastChar", (size_t)glyphCount); + { + int emSize; + auto widths = SkPDFMakeArray(); + + int glyphRangeSize = lastGlyphID - firstGlyphID + 2; + AutoTArray<SkGlyphID> glyphIDs{glyphRangeSize}; + glyphIDs[0] = 0; + for (unsigned gId = firstGlyphID; gId <= lastGlyphID; gId++) { + glyphIDs[gId - firstGlyphID + 1] = gId; + } + SkStrikeSpec strikeSpec = SkStrikeSpec::MakePDFVector(*typeface, &emSize); + SkBulkGlyphMetrics metrics{strikeSpec}; + auto glyphs = metrics.glyphs(SkSpan(glyphIDs.get(), glyphRangeSize)); + for (int i = 0; i < glyphRangeSize; ++i) { + widths->appendScalar(from_font_units(glyphs[i]->advanceX(), SkToU16(emSize))); + } + font.insertObject("Widths", std::move(widths)); + } + auto encDiffs = SkPDFMakeArray(); + encDiffs->reserve(lastGlyphID - firstGlyphID + 3); + encDiffs->appendInt(0); + + SkASSERT(glyphNames.size() > lastGlyphID); + const SkString unknown("UNKNOWN"); + encDiffs->appendName(glyphNames[0].isEmpty() ? unknown : glyphNames[0]); + for (int gID = firstGlyphID; gID <= lastGlyphID; gID++) { + encDiffs->appendName(glyphNames[gID].isEmpty() ? unknown : glyphNames[gID]); + } + + auto encoding = SkPDFMakeDict("Encoding"); + encoding->insertObject("Differences", std::move(encDiffs)); + font.insertObject("Encoding", std::move(encoding)); + + doc->emit(font, pdfFont.indirectReference()); +} |