1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkPDFConvertType1FontStream.h"
9#include "SkTemplates.h"
10
11#include <ctype.h>
12
13static bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
14                            size_t* size) {
15    // PFB sections have a two or six bytes header. 0x80 and a one byte
16    // section type followed by a four byte section length.  Type one is
17    // an ASCII section (includes a length), type two is a binary section
18    // (includes a length) and type three is an EOF marker with no length.
19    const uint8_t* buf = *src;
20    if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType) {
21        return false;
22    } else if (buf[1] == 3) {
23        return true;
24    } else if (*len < 6) {
25        return false;
26    }
27
28    *size = (size_t)buf[2] | ((size_t)buf[3] << 8) | ((size_t)buf[4] << 16) |
29            ((size_t)buf[5] << 24);
30    size_t consumed = *size + 6;
31    if (consumed > *len) {
32        return false;
33    }
34    *src = *src + consumed;
35    *len = *len - consumed;
36    return true;
37}
38
39static bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen,
40                     size_t* dataLen, size_t* trailerLen) {
41    const uint8_t* srcPtr = src;
42    size_t remaining = size;
43
44    return parsePFBSection(&srcPtr, &remaining, 1, headerLen) &&
45           parsePFBSection(&srcPtr, &remaining, 2, dataLen) &&
46           parsePFBSection(&srcPtr, &remaining, 1, trailerLen) &&
47           parsePFBSection(&srcPtr, &remaining, 3, nullptr);
48}
49
50/* The sections of a PFA file are implicitly defined.  The body starts
51 * after the line containing "eexec," and the trailer starts with 512
52 * literal 0's followed by "cleartomark" (plus arbitrary white space).
53 *
54 * This function assumes that src is NUL terminated, but the NUL
55 * termination is not included in size.
56 *
57 */
58static bool parsePFA(const char* src, size_t size, size_t* headerLen,
59                     size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) {
60    const char* end = src + size;
61
62    const char* dataPos = strstr(src, "eexec");
63    if (!dataPos) {
64        return false;
65    }
66    dataPos += strlen("eexec");
67    while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') &&
68            dataPos < end) {
69        dataPos++;
70    }
71    *headerLen = dataPos - src;
72
73    const char* trailerPos = strstr(dataPos, "cleartomark");
74    if (!trailerPos) {
75        return false;
76    }
77    int zeroCount = 0;
78    for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) {
79        if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') {
80            continue;
81        } else if (*trailerPos == '0') {
82            zeroCount++;
83        } else {
84            return false;
85        }
86    }
87    if (zeroCount != 512) {
88        return false;
89    }
90
91    *hexDataLen = trailerPos - src - *headerLen;
92    *trailerLen = size - *headerLen - *hexDataLen;
93
94    // Verify that the data section is hex encoded and count the bytes.
95    int nibbles = 0;
96    for (; dataPos < trailerPos; dataPos++) {
97        if (isspace(*dataPos)) {
98            continue;
99        }
100        if (!isxdigit(*dataPos)) {
101            return false;
102        }
103        nibbles++;
104    }
105    *dataLen = (nibbles + 1) / 2;
106
107    return true;
108}
109
110static int8_t hexToBin(uint8_t c) {
111    if (!isxdigit(c)) {
112        return -1;
113    } else if (c <= '9') {
114        return c - '0';
115    } else if (c <= 'F') {
116        return c - 'A' + 10;
117    } else if (c <= 'f') {
118        return c - 'a' + 10;
119    }
120    return -1;
121}
122
123sk_sp<SkData> SkPDFConvertType1FontStream(
124        std::unique_ptr<SkStreamAsset> srcStream, size_t* headerLen,
125        size_t* dataLen, size_t* trailerLen) {
126    size_t srcLen = srcStream ? srcStream->getLength() : 0;
127    SkASSERT(srcLen);
128    if (!srcLen) {
129        return nullptr;
130    }
131    // Flatten and Nul-terminate the source stream so that we can use
132    // strstr() to search it.
133    SkAutoTMalloc<uint8_t> sourceBuffer(SkToInt(srcLen + 1));
134    (void)srcStream->read(sourceBuffer.get(), srcLen);
135    sourceBuffer[SkToInt(srcLen)] = 0;
136    const uint8_t* src = sourceBuffer.get();
137
138    if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) {
139        static const int kPFBSectionHeaderLength = 6;
140        const size_t length = *headerLen + *dataLen + *trailerLen;
141        SkASSERT(length > 0);
142        SkASSERT(length + (2 * kPFBSectionHeaderLength) <= srcLen);
143
144        sk_sp<SkData> data(SkData::MakeUninitialized(length));
145
146        const uint8_t* const srcHeader = src + kPFBSectionHeaderLength;
147        // There is a six-byte section header before header and data
148        // (but not trailer) that we're not going to copy.
149        const uint8_t* const srcData = srcHeader + *headerLen + kPFBSectionHeaderLength;
150        const uint8_t* const srcTrailer = srcData + *headerLen;
151
152        uint8_t* const resultHeader = (uint8_t*)data->writable_data();
153        uint8_t* const resultData = resultHeader + *headerLen;
154        uint8_t* const resultTrailer = resultData + *dataLen;
155
156        SkASSERT(resultTrailer + *trailerLen == resultHeader + length);
157
158        memcpy(resultHeader,  srcHeader,  *headerLen);
159        memcpy(resultData,    srcData,    *dataLen);
160        memcpy(resultTrailer, srcTrailer, *trailerLen);
161
162        return data;
163    }
164
165    // A PFA has to be converted for PDF.
166    size_t hexDataLen;
167    if (!parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen,
168                 trailerLen)) {
169        return nullptr;
170    }
171    const size_t length = *headerLen + *dataLen + *trailerLen;
172    SkASSERT(length > 0);
173    auto data = SkData::MakeUninitialized(length);
174    uint8_t* buffer = (uint8_t*)data->writable_data();
175
176    memcpy(buffer, src, *headerLen);
177    uint8_t* const resultData = &(buffer[*headerLen]);
178
179    const uint8_t* hexData = src + *headerLen;
180    const uint8_t* trailer = hexData + hexDataLen;
181    size_t outputOffset = 0;
182    uint8_t dataByte = 0;  // To hush compiler.
183    bool highNibble = true;
184    for (; hexData < trailer; hexData++) {
185        int8_t curNibble = hexToBin(*hexData);
186        if (curNibble < 0) {
187            continue;
188        }
189        if (highNibble) {
190            dataByte = curNibble << 4;
191            highNibble = false;
192        } else {
193            dataByte |= curNibble;
194            highNibble = true;
195            resultData[outputOffset++] = dataByte;
196        }
197    }
198    if (!highNibble) {
199        resultData[outputOffset++] = dataByte;
200    }
201    SkASSERT(outputOffset == *dataLen);
202
203    uint8_t* const resultTrailer = &(buffer[SkToInt(*headerLen + outputOffset)]);
204    memcpy(resultTrailer, src + *headerLen + hexDataLen, *trailerLen);
205    return data;
206}
207