diff options
author | halcanary <halcanary@google.com> | 2016-08-09 13:04:34 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-08-09 13:04:34 -0700 |
commit | 8eccc308c8adcdf26ffc7c4dd538b71f33c6f22b (patch) | |
tree | a05b3bfa26ccf05e733b0776a0e1fe788fad5528 /src/pdf/SkPDFConvertType1FontStream.cpp | |
parent | 9bc1187249f27ffed448b49ad75fd226d8181241 (diff) |
SkPDF: SkPDFFont organization changes.
SkPDFFont:
- SkPDFType1Font::populate() encode advances correctly.
- break out logically independent code into new files:
* SkPDFConvertType1FontStream
* SkPDFMakeToUnicodeCmap
SkPDFFont.cpp is now 380 lines smaller.
Expose `SkPDFAppendCmapSections()` for testing.
SkPDFFontImpl.h
- Fold into SkPDFFont.
SkPDFConvertType1FontStream:
- Now assume given a SkStreamAsset
SkPDFFont:
- AdvanceMetric now hidden in a anonymous namespace.
No public API changes.
TBR=reed@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2221163002
Review-Url: https://codereview.chromium.org/2221163002
Diffstat (limited to 'src/pdf/SkPDFConvertType1FontStream.cpp')
-rw-r--r-- | src/pdf/SkPDFConvertType1FontStream.cpp | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/pdf/SkPDFConvertType1FontStream.cpp b/src/pdf/SkPDFConvertType1FontStream.cpp new file mode 100644 index 0000000000..d75da5c787 --- /dev/null +++ b/src/pdf/SkPDFConvertType1FontStream.cpp @@ -0,0 +1,205 @@ +/* + * 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 "SkPDFConvertType1FontStream.h" + +#include <ctype.h> + +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; + } + if (!isxdigit(*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; +} + +sk_sp<SkData> SkPDFConvertType1FontStream( + 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. + SkAutoTMalloc<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; +} |