| 1 | /**************************************************************************** |
| 2 | * |
| 3 | * cidparse.c |
| 4 | * |
| 5 | * CID-keyed Type1 parser (body). |
| 6 | * |
| 7 | * Copyright (C) 1996-2019 by |
| 8 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
| 9 | * |
| 10 | * This file is part of the FreeType project, and may only be used, |
| 11 | * modified, and distributed under the terms of the FreeType project |
| 12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
| 13 | * this file you indicate that you have read the license and |
| 14 | * understand and accept it fully. |
| 15 | * |
| 16 | */ |
| 17 | |
| 18 | |
| 19 | #include <ft2build.h> |
| 20 | #include FT_INTERNAL_DEBUG_H |
| 21 | #include FT_INTERNAL_OBJECTS_H |
| 22 | #include FT_INTERNAL_STREAM_H |
| 23 | |
| 24 | #include "cidparse.h" |
| 25 | |
| 26 | #include "ciderrs.h" |
| 27 | |
| 28 | |
| 29 | /************************************************************************** |
| 30 | * |
| 31 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
| 32 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
| 33 | * messages during execution. |
| 34 | */ |
| 35 | #undef FT_COMPONENT |
| 36 | #define FT_COMPONENT cidparse |
| 37 | |
| 38 | |
| 39 | /*************************************************************************/ |
| 40 | /*************************************************************************/ |
| 41 | /*************************************************************************/ |
| 42 | /***** *****/ |
| 43 | /***** INPUT STREAM PARSER *****/ |
| 44 | /***** *****/ |
| 45 | /*************************************************************************/ |
| 46 | /*************************************************************************/ |
| 47 | /*************************************************************************/ |
| 48 | |
| 49 | |
| 50 | #define STARTDATA "StartData" |
| 51 | #define STARTDATA_LEN ( sizeof ( STARTDATA ) - 1 ) |
| 52 | #define SFNTS "/sfnts" |
| 53 | #define SFNTS_LEN ( sizeof ( SFNTS ) - 1 ) |
| 54 | |
| 55 | |
| 56 | FT_LOCAL_DEF( FT_Error ) |
| 57 | cid_parser_new( CID_Parser* parser, |
| 58 | FT_Stream stream, |
| 59 | FT_Memory memory, |
| 60 | PSAux_Service psaux ) |
| 61 | { |
| 62 | FT_Error error; |
| 63 | FT_ULong base_offset, offset, ps_len; |
| 64 | FT_Byte *cur, *limit; |
| 65 | FT_Byte *arg1, *arg2; |
| 66 | |
| 67 | |
| 68 | FT_ZERO( parser ); |
| 69 | psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory ); |
| 70 | |
| 71 | parser->stream = stream; |
| 72 | |
| 73 | base_offset = FT_STREAM_POS(); |
| 74 | |
| 75 | /* first of all, check the font format in the header */ |
| 76 | if ( FT_FRAME_ENTER( 31 ) ) |
| 77 | goto Exit; |
| 78 | |
| 79 | if ( ft_strncmp( (char *)stream->cursor, |
| 80 | "%!PS-Adobe-3.0 Resource-CIDFont" , 31 ) ) |
| 81 | { |
| 82 | FT_TRACE2(( " not a CID-keyed font\n" )); |
| 83 | error = FT_THROW( Unknown_File_Format ); |
| 84 | } |
| 85 | |
| 86 | FT_FRAME_EXIT(); |
| 87 | if ( error ) |
| 88 | goto Exit; |
| 89 | |
| 90 | Again: |
| 91 | /* now, read the rest of the file until we find */ |
| 92 | /* `StartData' or `/sfnts' */ |
| 93 | { |
| 94 | /* |
| 95 | * The algorithm is as follows (omitting the case with less than 256 |
| 96 | * bytes to fill for simplicity). |
| 97 | * |
| 98 | * 1. Fill the buffer with 256 + STARTDATA_LEN bytes. |
| 99 | * |
| 100 | * 2. Search for the STARTDATA and SFNTS strings at positions |
| 101 | * buffer[0], buffer[1], ..., |
| 102 | * buffer[255 + STARTDATA_LEN - SFNTS_LEN]. |
| 103 | * |
| 104 | * 3. Move the last STARTDATA_LEN bytes to buffer[0]. |
| 105 | * |
| 106 | * 4. Fill the buffer with 256 bytes, starting at STARTDATA_LEN. |
| 107 | * |
| 108 | * 5. Repeat with step 2. |
| 109 | * |
| 110 | */ |
| 111 | FT_Byte buffer[256 + STARTDATA_LEN + 1]; |
| 112 | |
| 113 | /* values for the first loop */ |
| 114 | FT_ULong read_len = 256 + STARTDATA_LEN; |
| 115 | FT_ULong read_offset = 0; |
| 116 | FT_Byte* p = buffer; |
| 117 | |
| 118 | |
| 119 | for ( offset = FT_STREAM_POS(); ; offset += 256 ) |
| 120 | { |
| 121 | FT_ULong stream_len; |
| 122 | |
| 123 | |
| 124 | stream_len = stream->size - FT_STREAM_POS(); |
| 125 | |
| 126 | read_len = FT_MIN( read_len, stream_len ); |
| 127 | if ( FT_STREAM_READ( p, read_len ) ) |
| 128 | goto Exit; |
| 129 | |
| 130 | /* ensure that we do not compare with data beyond the buffer */ |
| 131 | p[read_len] = '\0'; |
| 132 | |
| 133 | limit = p + read_len - SFNTS_LEN; |
| 134 | |
| 135 | for ( p = buffer; p < limit; p++ ) |
| 136 | { |
| 137 | if ( p[0] == 'S' && |
| 138 | ft_strncmp( (char*)p, STARTDATA, STARTDATA_LEN ) == 0 ) |
| 139 | { |
| 140 | /* save offset of binary data after `StartData' */ |
| 141 | offset += (FT_ULong)( p - buffer ) + STARTDATA_LEN + 1; |
| 142 | goto Found; |
| 143 | } |
| 144 | else if ( p[1] == 's' && |
| 145 | ft_strncmp( (char*)p, SFNTS, SFNTS_LEN ) == 0 ) |
| 146 | { |
| 147 | offset += (FT_ULong)( p - buffer ) + SFNTS_LEN + 1; |
| 148 | goto Found; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | if ( read_offset + read_len < STARTDATA_LEN ) |
| 153 | { |
| 154 | FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" )); |
| 155 | error = FT_THROW( Invalid_File_Format ); |
| 156 | goto Exit; |
| 157 | } |
| 158 | |
| 159 | FT_MEM_MOVE( buffer, |
| 160 | buffer + read_offset + read_len - STARTDATA_LEN, |
| 161 | STARTDATA_LEN ); |
| 162 | |
| 163 | /* values for the next loop */ |
| 164 | read_len = 256; |
| 165 | read_offset = STARTDATA_LEN; |
| 166 | p = buffer + read_offset; |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | Found: |
| 171 | /* We have found the start of the binary data or the `/sfnts' token. */ |
| 172 | /* Now rewind and extract the frame corresponding to this PostScript */ |
| 173 | /* section. */ |
| 174 | |
| 175 | ps_len = offset - base_offset; |
| 176 | if ( FT_STREAM_SEEK( base_offset ) || |
| 177 | FT_FRAME_EXTRACT( ps_len, parser->postscript ) ) |
| 178 | goto Exit; |
| 179 | |
| 180 | parser->data_offset = offset; |
| 181 | parser->postscript_len = ps_len; |
| 182 | parser->root.base = parser->postscript; |
| 183 | parser->root.cursor = parser->postscript; |
| 184 | parser->root.limit = parser->root.cursor + ps_len; |
| 185 | parser->num_dict = -1; |
| 186 | |
| 187 | /* Finally, we check whether `StartData' or `/sfnts' was real -- */ |
| 188 | /* it could be in a comment or string. We also get the arguments */ |
| 189 | /* of `StartData' to find out whether the data is represented in */ |
| 190 | /* binary or hex format. */ |
| 191 | |
| 192 | arg1 = parser->root.cursor; |
| 193 | cid_parser_skip_PS_token( parser ); |
| 194 | cid_parser_skip_spaces ( parser ); |
| 195 | arg2 = parser->root.cursor; |
| 196 | cid_parser_skip_PS_token( parser ); |
| 197 | cid_parser_skip_spaces ( parser ); |
| 198 | |
| 199 | limit = parser->root.limit; |
| 200 | cur = parser->root.cursor; |
| 201 | |
| 202 | while ( cur <= limit - SFNTS_LEN ) |
| 203 | { |
| 204 | if ( parser->root.error ) |
| 205 | { |
| 206 | error = parser->root.error; |
| 207 | goto Exit; |
| 208 | } |
| 209 | |
| 210 | if ( cur[0] == 'S' && |
| 211 | cur <= limit - STARTDATA_LEN && |
| 212 | ft_strncmp( (char*)cur, STARTDATA, STARTDATA_LEN ) == 0 ) |
| 213 | { |
| 214 | if ( ft_strncmp( (char*)arg1, "(Hex)" , 5 ) == 0 ) |
| 215 | { |
| 216 | FT_Long tmp = ft_strtol( (const char *)arg2, NULL, 10 ); |
| 217 | |
| 218 | |
| 219 | if ( tmp < 0 ) |
| 220 | { |
| 221 | FT_ERROR(( "cid_parser_new: invalid length of hex data\n" )); |
| 222 | error = FT_THROW( Invalid_File_Format ); |
| 223 | } |
| 224 | else |
| 225 | parser->binary_length = (FT_ULong)tmp; |
| 226 | } |
| 227 | |
| 228 | goto Exit; |
| 229 | } |
| 230 | else if ( cur[1] == 's' && |
| 231 | ft_strncmp( (char*)cur, SFNTS, SFNTS_LEN ) == 0 ) |
| 232 | { |
| 233 | FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" )); |
| 234 | error = FT_THROW( Unknown_File_Format ); |
| 235 | goto Exit; |
| 236 | } |
| 237 | |
| 238 | cid_parser_skip_PS_token( parser ); |
| 239 | cid_parser_skip_spaces ( parser ); |
| 240 | arg1 = arg2; |
| 241 | arg2 = cur; |
| 242 | cur = parser->root.cursor; |
| 243 | } |
| 244 | |
| 245 | /* we haven't found the correct `StartData'; go back and continue */ |
| 246 | /* searching */ |
| 247 | FT_FRAME_RELEASE( parser->postscript ); |
| 248 | if ( !FT_STREAM_SEEK( offset ) ) |
| 249 | goto Again; |
| 250 | |
| 251 | Exit: |
| 252 | return error; |
| 253 | } |
| 254 | |
| 255 | |
| 256 | #undef STARTDATA |
| 257 | #undef STARTDATA_LEN |
| 258 | #undef SFNTS |
| 259 | #undef SFNTS_LEN |
| 260 | |
| 261 | |
| 262 | FT_LOCAL_DEF( void ) |
| 263 | cid_parser_done( CID_Parser* parser ) |
| 264 | { |
| 265 | /* always free the private dictionary */ |
| 266 | if ( parser->postscript ) |
| 267 | { |
| 268 | FT_Stream stream = parser->stream; |
| 269 | |
| 270 | |
| 271 | FT_FRAME_RELEASE( parser->postscript ); |
| 272 | } |
| 273 | parser->root.funcs.done( &parser->root ); |
| 274 | } |
| 275 | |
| 276 | |
| 277 | /* END */ |
| 278 | |