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