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