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