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; |
+} |