1/****************************************************************************
2 *
3 * gxvmort2.c
4 *
5 * TrueTypeGX/AAT mort table validation
6 * body for type2 (Ligature Substitution) subtable.
7 *
8 * Copyright (C) 2005-2023 by
9 * suzuki toshiya, Masatake YAMATO, Red Hat K.K.,
10 * David Turner, Robert Wilhelm, and Werner Lemberg.
11 *
12 * This file is part of the FreeType project, and may only be used,
13 * modified, and distributed under the terms of the FreeType project
14 * license, LICENSE.TXT. By continuing to use, modify, or distribute
15 * this file you indicate that you have read the license and
16 * understand and accept it fully.
17 *
18 */
19
20/****************************************************************************
21 *
22 * gxvalid is derived from both gxlayout module and otvalid module.
23 * Development of gxlayout is supported by the Information-technology
24 * Promotion Agency(IPA), Japan.
25 *
26 */
27
28
29#include "gxvmort.h"
30
31
32 /**************************************************************************
33 *
34 * The macro FT_COMPONENT is used in trace mode. It is an implicit
35 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
36 * messages during execution.
37 */
38#undef FT_COMPONENT
39#define FT_COMPONENT gxvmort
40
41
42 typedef struct GXV_mort_subtable_type2_StateOptRec_
43 {
44 FT_UShort ligActionTable;
45 FT_UShort componentTable;
46 FT_UShort ligatureTable;
47 FT_UShort ligActionTable_length;
48 FT_UShort componentTable_length;
49 FT_UShort ligatureTable_length;
50
51 } GXV_mort_subtable_type2_StateOptRec,
52 *GXV_mort_subtable_type2_StateOptRecData;
53
54#define GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE \
55 ( GXV_STATETABLE_HEADER_SIZE + 2 + 2 + 2 )
56
57
58 static void
59 gxv_mort_subtable_type2_opttable_load( FT_Bytes table,
60 FT_Bytes limit,
61 GXV_Validator gxvalid )
62 {
63 FT_Bytes p = table;
64 GXV_mort_subtable_type2_StateOptRecData optdata =
65 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata;
66
67
68 GXV_LIMIT_CHECK( 2 + 2 + 2 );
69 optdata->ligActionTable = FT_NEXT_USHORT( p );
70 optdata->componentTable = FT_NEXT_USHORT( p );
71 optdata->ligatureTable = FT_NEXT_USHORT( p );
72
73 GXV_TRACE(( "offset to ligActionTable=0x%04x\n",
74 optdata->ligActionTable ));
75 GXV_TRACE(( "offset to componentTable=0x%04x\n",
76 optdata->componentTable ));
77 GXV_TRACE(( "offset to ligatureTable=0x%04x\n",
78 optdata->ligatureTable ));
79 }
80
81
82 static void
83 gxv_mort_subtable_type2_subtable_setup( FT_UShort table_size,
84 FT_UShort classTable,
85 FT_UShort stateArray,
86 FT_UShort entryTable,
87 FT_UShort *classTable_length_p,
88 FT_UShort *stateArray_length_p,
89 FT_UShort *entryTable_length_p,
90 GXV_Validator gxvalid )
91 {
92 FT_UShort o[6];
93 FT_UShort *l[6];
94 FT_UShort buff[7];
95
96 GXV_mort_subtable_type2_StateOptRecData optdata =
97 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata;
98
99
100 GXV_NAME_ENTER( "subtable boundaries setup" );
101
102 o[0] = classTable;
103 o[1] = stateArray;
104 o[2] = entryTable;
105 o[3] = optdata->ligActionTable;
106 o[4] = optdata->componentTable;
107 o[5] = optdata->ligatureTable;
108 l[0] = classTable_length_p;
109 l[1] = stateArray_length_p;
110 l[2] = entryTable_length_p;
111 l[3] = &(optdata->ligActionTable_length);
112 l[4] = &(optdata->componentTable_length);
113 l[5] = &(optdata->ligatureTable_length);
114
115 gxv_set_length_by_ushort_offset( o, l, buff, 6, table_size, gxvalid );
116
117 GXV_TRACE(( "classTable: offset=0x%04x length=0x%04x\n",
118 classTable, *classTable_length_p ));
119 GXV_TRACE(( "stateArray: offset=0x%04x length=0x%04x\n",
120 stateArray, *stateArray_length_p ));
121 GXV_TRACE(( "entryTable: offset=0x%04x length=0x%04x\n",
122 entryTable, *entryTable_length_p ));
123 GXV_TRACE(( "ligActionTable: offset=0x%04x length=0x%04x\n",
124 optdata->ligActionTable,
125 optdata->ligActionTable_length ));
126 GXV_TRACE(( "componentTable: offset=0x%04x length=0x%04x\n",
127 optdata->componentTable,
128 optdata->componentTable_length ));
129 GXV_TRACE(( "ligatureTable: offset=0x%04x length=0x%04x\n",
130 optdata->ligatureTable,
131 optdata->ligatureTable_length ));
132
133 GXV_EXIT;
134 }
135
136
137 static void
138 gxv_mort_subtable_type2_ligActionOffset_validate(
139 FT_Bytes table,
140 FT_UShort ligActionOffset,
141 GXV_Validator gxvalid )
142 {
143 /* access ligActionTable */
144 GXV_mort_subtable_type2_StateOptRecData optdata =
145 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata;
146
147 FT_Bytes lat_base = table + optdata->ligActionTable;
148 FT_Bytes p = table + ligActionOffset;
149 FT_Bytes lat_limit = lat_base + optdata->ligActionTable;
150
151
152 GXV_32BIT_ALIGNMENT_VALIDATE( ligActionOffset );
153 if ( p < lat_base )
154 {
155 GXV_TRACE(( "too short offset 0x%04x: p < lat_base (%ld byte rewind)\n",
156 ligActionOffset, lat_base - p ));
157
158 /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */
159 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
160 }
161 else if ( lat_limit < p )
162 {
163 GXV_TRACE(( "too large offset 0x%04x: lat_limit < p (%ld byte overrun)\n",
164 ligActionOffset, p - lat_limit ));
165
166 /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */
167 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
168 }
169 else
170 {
171 /* validate entry in ligActionTable */
172 FT_ULong lig_action;
173#ifdef GXV_LOAD_UNUSED_VARS
174 FT_UShort last;
175 FT_UShort store;
176#endif
177 FT_ULong offset;
178
179
180 lig_action = FT_NEXT_ULONG( p );
181#ifdef GXV_LOAD_UNUSED_VARS
182 last = (FT_UShort)( ( lig_action >> 31 ) & 1 );
183 store = (FT_UShort)( ( lig_action >> 30 ) & 1 );
184#endif
185
186 /* Apple spec defines this offset as a word offset */
187 offset = lig_action & 0x3FFFFFFFUL;
188 if ( offset * 2 < optdata->ligatureTable )
189 {
190 GXV_TRACE(( "too short offset 0x%08lx:"
191 " 2 x offset < ligatureTable (%lu byte rewind)\n",
192 offset, optdata->ligatureTable - offset * 2 ));
193
194 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
195 } else if ( offset * 2 >
196 optdata->ligatureTable + optdata->ligatureTable_length )
197 {
198 GXV_TRACE(( "too long offset 0x%08lx:"
199 " 2 x offset > ligatureTable + ligatureTable_length"
200 " (%lu byte overrun)\n",
201 offset,
202 optdata->ligatureTable + optdata->ligatureTable_length
203 - offset * 2 ));
204
205 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
206 }
207 }
208 }
209
210
211 static void
212 gxv_mort_subtable_type2_entry_validate(
213 FT_Byte state,
214 FT_UShort flags,
215 GXV_StateTable_GlyphOffsetCPtr glyphOffset_p,
216 FT_Bytes table,
217 FT_Bytes limit,
218 GXV_Validator gxvalid )
219 {
220#ifdef GXV_LOAD_UNUSED_VARS
221 FT_UShort setComponent;
222 FT_UShort dontAdvance;
223#endif
224 FT_UShort offset;
225
226 FT_UNUSED( state );
227 FT_UNUSED( glyphOffset_p );
228 FT_UNUSED( limit );
229
230
231#ifdef GXV_LOAD_UNUSED_VARS
232 setComponent = (FT_UShort)( ( flags >> 15 ) & 1 );
233 dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
234#endif
235
236 offset = (FT_UShort)( flags & 0x3FFFU );
237
238 if ( 0 < offset )
239 gxv_mort_subtable_type2_ligActionOffset_validate( table, offset,
240 gxvalid );
241 }
242
243
244 static void
245 gxv_mort_subtable_type2_ligatureTable_validate( FT_Bytes table,
246 GXV_Validator gxvalid )
247 {
248 GXV_mort_subtable_type2_StateOptRecData optdata =
249 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata;
250
251 FT_Bytes p = table + optdata->ligatureTable;
252 FT_Bytes limit = table + optdata->ligatureTable
253 + optdata->ligatureTable_length;
254
255
256 GXV_NAME_ENTER( "mort chain subtable type2 - substitutionTable" );
257 if ( 0 != optdata->ligatureTable )
258 {
259 /* Apple does not give specification of ligatureTable format */
260 while ( p < limit )
261 {
262 FT_UShort lig_gid;
263
264
265 GXV_LIMIT_CHECK( 2 );
266 lig_gid = FT_NEXT_USHORT( p );
267
268 if ( gxvalid->face->num_glyphs < lig_gid )
269 GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID );
270 }
271 }
272 GXV_EXIT;
273 }
274
275
276 FT_LOCAL_DEF( void )
277 gxv_mort_subtable_type2_validate( FT_Bytes table,
278 FT_Bytes limit,
279 GXV_Validator gxvalid )
280 {
281 FT_Bytes p = table;
282
283 GXV_mort_subtable_type2_StateOptRec lig_rec;
284
285
286 GXV_NAME_ENTER( "mort chain subtable type2 (Ligature Substitution)" );
287
288 GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE );
289
290 gxvalid->statetable.optdata =
291 &lig_rec;
292 gxvalid->statetable.optdata_load_func =
293 gxv_mort_subtable_type2_opttable_load;
294 gxvalid->statetable.subtable_setup_func =
295 gxv_mort_subtable_type2_subtable_setup;
296 gxvalid->statetable.entry_glyphoffset_fmt =
297 GXV_GLYPHOFFSET_NONE;
298 gxvalid->statetable.entry_validate_func =
299 gxv_mort_subtable_type2_entry_validate;
300
301 gxv_StateTable_validate( p, limit, gxvalid );
302
303 p += gxvalid->subtable_length;
304 gxv_mort_subtable_type2_ligatureTable_validate( table, gxvalid );
305
306 gxvalid->subtable_length = (FT_ULong)( p - table );
307
308 GXV_EXIT;
309 }
310
311
312/* END */
313