| Index: src/pdf/SkPDFConvertType1FontStream.cpp
|
| diff --git a/src/pdf/SkPDFConvertType1FontStream.cpp b/src/pdf/SkPDFConvertType1FontStream.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d75da5c787124571c0072040b4966ba7e96d73ea
|
| --- /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;
|
| +}
|
|
|