1/****************************************************************************
2 *
3 * gxvmorx2.c
4 *
5 * TrueTypeGX/AAT morx 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 "gxvmorx.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 gxvmorx
40
41
42 typedef struct GXV_morx_subtable_type2_StateOptRec_
43 {
44 FT_ULong ligActionTable;
45 FT_ULong componentTable;
46 FT_ULong ligatureTable;
47 FT_ULong ligActionTable_length;
48 FT_ULong componentTable_length;
49 FT_ULong ligatureTable_length;
50
51 } GXV_morx_subtable_type2_StateOptRec,
52 *GXV_morx_subtable_type2_StateOptRecData;
53
54
55#define GXV_MORX_SUBTABLE_TYPE2_HEADER_SIZE \
56 ( GXV_XSTATETABLE_HEADER_SIZE + 4 + 4 + 4 )
57
58
59 static void
60 gxv_morx_subtable_type2_opttable_load( FT_Bytes table,
61 FT_Bytes limit,
62 GXV_Validator gxvalid )
63 {
64 FT_Bytes p = table;
65
66 GXV_morx_subtable_type2_StateOptRecData optdata =
67 (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata;
68
69
70 GXV_LIMIT_CHECK( 4 + 4 + 4 );
71 optdata->ligActionTable = FT_NEXT_ULONG( p );
72 optdata->componentTable = FT_NEXT_ULONG( p );
73 optdata->ligatureTable = FT_NEXT_ULONG( p );
74
75 GXV_TRACE(( "offset to ligActionTable=0x%08lx\n",
76 optdata->ligActionTable ));
77 GXV_TRACE(( "offset to componentTable=0x%08lx\n",
78 optdata->componentTable ));
79 GXV_TRACE(( "offset to ligatureTable=0x%08lx\n",
80 optdata->ligatureTable ));
81 }
82
83
84 static void
85 gxv_morx_subtable_type2_subtable_setup( FT_ULong table_size,
86 FT_ULong classTable,
87 FT_ULong stateArray,
88 FT_ULong entryTable,
89 FT_ULong* classTable_length_p,
90 FT_ULong* stateArray_length_p,
91 FT_ULong* entryTable_length_p,
92 GXV_Validator gxvalid )
93 {
94 FT_ULong o[6];
95 FT_ULong* l[6];
96 FT_ULong buff[7];
97
98 GXV_morx_subtable_type2_StateOptRecData optdata =
99 (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata;
100
101
102 GXV_NAME_ENTER( "subtable boundaries setup" );
103
104 o[0] = classTable;
105 o[1] = stateArray;
106 o[2] = entryTable;
107 o[3] = optdata->ligActionTable;
108 o[4] = optdata->componentTable;
109 o[5] = optdata->ligatureTable;
110 l[0] = classTable_length_p;
111 l[1] = stateArray_length_p;
112 l[2] = entryTable_length_p;
113 l[3] = &(optdata->ligActionTable_length);
114 l[4] = &(optdata->componentTable_length);
115 l[5] = &(optdata->ligatureTable_length);
116
117 gxv_set_length_by_ulong_offset( o, l, buff, 6, table_size, gxvalid );
118
119 GXV_TRACE(( "classTable: offset=0x%08lx length=0x%08lx\n",
120 classTable, *classTable_length_p ));
121 GXV_TRACE(( "stateArray: offset=0x%08lx length=0x%08lx\n",
122 stateArray, *stateArray_length_p ));
123 GXV_TRACE(( "entryTable: offset=0x%08lx length=0x%08lx\n",
124 entryTable, *entryTable_length_p ));
125 GXV_TRACE(( "ligActionTable: offset=0x%08lx length=0x%08lx\n",
126 optdata->ligActionTable,
127 optdata->ligActionTable_length ));
128 GXV_TRACE(( "componentTable: offset=0x%08lx length=0x%08lx\n",
129 optdata->componentTable,
130 optdata->componentTable_length ));
131 GXV_TRACE(( "ligatureTable: offset=0x%08lx length=0x%08lx\n",
132 optdata->ligatureTable,
133 optdata->ligatureTable_length ));
134
135 GXV_EXIT;
136 }
137
138
139#define GXV_MORX_LIGACTION_ENTRY_SIZE 4
140
141
142 static void
143 gxv_morx_subtable_type2_ligActionIndex_validate(
144 FT_Bytes table,
145 FT_UShort ligActionIndex,
146 GXV_Validator gxvalid )
147 {
148 /* access ligActionTable */
149 GXV_morx_subtable_type2_StateOptRecData optdata =
150 (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata;
151
152 FT_Bytes lat_base = table + optdata->ligActionTable;
153 FT_Bytes p = lat_base +
154 ligActionIndex * GXV_MORX_LIGACTION_ENTRY_SIZE;
155 FT_Bytes lat_limit = lat_base + optdata->ligActionTable;
156
157
158 if ( p < lat_base )
159 {
160 GXV_TRACE(( "p < lat_base (%ld byte rewind)\n", lat_base - p ));
161 FT_INVALID_OFFSET;
162 }
163 else if ( lat_limit < p )
164 {
165 GXV_TRACE(( "lat_limit < p (%ld byte overrun)\n", p - lat_limit ));
166 FT_INVALID_OFFSET;
167 }
168
169 {
170 /* validate entry in ligActionTable */
171 FT_ULong lig_action;
172#ifdef GXV_LOAD_UNUSED_VARS
173 FT_UShort last;
174 FT_UShort store;
175#endif
176 FT_ULong offset;
177 FT_Long gid_limit;
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 offset = lig_action & 0x3FFFFFFFUL;
187
188 /* this offset is 30-bit signed value to add to GID */
189 /* it is different from the location offset in mort */
190 if ( ( offset & 0x3FFF0000UL ) == 0x3FFF0000UL )
191 { /* negative offset */
192 gid_limit = gxvalid->face->num_glyphs -
193 (FT_Long)( offset & 0x0000FFFFUL );
194 if ( gid_limit > 0 )
195 return;
196
197 GXV_TRACE(( "ligature action table includes"
198 " too negative offset moving all GID"
199 " below defined range: 0x%04lx\n",
200 offset & 0xFFFFU ));
201 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
202 }
203 else if ( ( offset & 0x3FFF0000UL ) == 0x00000000UL )
204 { /* positive offset */
205 if ( (FT_Long)offset < gxvalid->face->num_glyphs )
206 return;
207
208 GXV_TRACE(( "ligature action table includes"
209 " too large offset moving all GID"
210 " over defined range: 0x%04lx\n",
211 offset & 0xFFFFU ));
212 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
213 }
214
215 GXV_TRACE(( "ligature action table includes"
216 " invalid offset to add to 16-bit GID:"
217 " 0x%08lx\n", offset ));
218 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET );
219 }
220 }
221
222
223 static void
224 gxv_morx_subtable_type2_entry_validate(
225 FT_UShort state,
226 FT_UShort flags,
227 GXV_StateTable_GlyphOffsetCPtr glyphOffset_p,
228 FT_Bytes table,
229 FT_Bytes limit,
230 GXV_Validator gxvalid )
231 {
232#ifdef GXV_LOAD_UNUSED_VARS
233 FT_UShort setComponent;
234 FT_UShort dontAdvance;
235 FT_UShort performAction;
236#endif
237 FT_UShort reserved;
238 FT_UShort ligActionIndex;
239
240 FT_UNUSED( state );
241 FT_UNUSED( limit );
242
243
244#ifdef GXV_LOAD_UNUSED_VARS
245 setComponent = (FT_UShort)( ( flags >> 15 ) & 1 );
246 dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
247 performAction = (FT_UShort)( ( flags >> 13 ) & 1 );
248#endif
249
250 reserved = (FT_UShort)( flags & 0x1FFF );
251 ligActionIndex = glyphOffset_p->u;
252
253 if ( reserved > 0 )
254 GXV_TRACE(( " reserved 14bit is non-zero\n" ));
255
256 if ( 0 < ligActionIndex )
257 gxv_morx_subtable_type2_ligActionIndex_validate(
258 table, ligActionIndex, gxvalid );
259 }
260
261
262 static void
263 gxv_morx_subtable_type2_ligatureTable_validate( FT_Bytes table,
264 GXV_Validator gxvalid )
265 {
266 GXV_morx_subtable_type2_StateOptRecData optdata =
267 (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata;
268
269 FT_Bytes p = table + optdata->ligatureTable;
270 FT_Bytes limit = table + optdata->ligatureTable
271 + optdata->ligatureTable_length;
272
273
274 GXV_NAME_ENTER( "morx chain subtable type2 - substitutionTable" );
275
276 if ( 0 != optdata->ligatureTable )
277 {
278 /* Apple does not give specification of ligatureTable format */
279 while ( p < limit )
280 {
281 FT_UShort lig_gid;
282
283
284 GXV_LIMIT_CHECK( 2 );
285 lig_gid = FT_NEXT_USHORT( p );
286 if ( lig_gid < gxvalid->face->num_glyphs )
287 GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID );
288 }
289 }
290
291 GXV_EXIT;
292 }
293
294
295 FT_LOCAL_DEF( void )
296 gxv_morx_subtable_type2_validate( FT_Bytes table,
297 FT_Bytes limit,
298 GXV_Validator gxvalid )
299 {
300 FT_Bytes p = table;
301
302 GXV_morx_subtable_type2_StateOptRec lig_rec;
303
304
305 GXV_NAME_ENTER( "morx chain subtable type2 (Ligature Substitution)" );
306
307 GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE2_HEADER_SIZE );
308
309 gxvalid->xstatetable.optdata =
310 &lig_rec;
311 gxvalid->xstatetable.optdata_load_func =
312 gxv_morx_subtable_type2_opttable_load;
313 gxvalid->xstatetable.subtable_setup_func =
314 gxv_morx_subtable_type2_subtable_setup;
315 gxvalid->xstatetable.entry_glyphoffset_fmt =
316 GXV_GLYPHOFFSET_USHORT;
317 gxvalid->xstatetable.entry_validate_func =
318 gxv_morx_subtable_type2_entry_validate;
319
320 gxv_XStateTable_validate( p, limit, gxvalid );
321
322#if 0
323 p += gxvalid->subtable_length;
324#endif
325 gxv_morx_subtable_type2_ligatureTable_validate( table, gxvalid );
326
327 GXV_EXIT;
328 }
329
330
331/* END */
332