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