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 | |