contrib/OWB: add correct SDL dependency, fix compilers used
[AROS-Contrib.git] / freetype1 / lib / extend / ftxkern.c
blobe2704ae4e6894536aa08f03f330aeb10f1a8d97e
1 /*******************************************************************
3 * ftxkern.c 1.0
5 * Kerning support extension.
7 * Copyright 1996-1999 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
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.
17 * The kerning support is currently part of the engine extensions.
19 ******************************************************************/
21 #include "ftxkern.h"
23 #include "ttextend.h"
24 #include "tttypes.h"
25 #include "ttdebug.h"
26 #include "ttmemory.h"
27 #include "ttfile.h"
28 #include "ttobjs.h"
29 #include "ttload.h" /* For the macros */
30 #include "tttags.h"
32 /* Required by the tracing mode */
33 #undef TT_COMPONENT
34 #define TT_COMPONENT trace_any
36 #define KERNING_ID Build_Extension_ID( 'k', 'e', 'r', 'n' )
39 /*******************************************************************
41 * Function : SubTable_Load_0
43 * Description : Loads a format 0 kerning subtable data.
45 * Input : kern0 pointer to the kerning subtable
47 * Output : error code
49 * Notes : - Assumes that the stream is already `used'
51 * - the file cursor must be set by the caller
53 * - in case of error, the function _must_ destroy
54 * the data it allocates!
56 ******************************************************************/
58 static TT_Error Subtable_Load_0( TT_Kern_0* kern0,
59 PFace input )
61 DEFINE_LOAD_LOCALS( input->stream );
63 UShort num_pairs, n;
66 if ( ACCESS_Frame( 8L ) )
67 return error;
69 num_pairs = GET_UShort();
70 kern0->nPairs = 0;
71 kern0->searchRange = GET_UShort();
72 kern0->entrySelector = GET_UShort();
73 kern0->rangeShift = GET_UShort();
75 /* we only set kern0->nPairs if the subtable has been loaded */
77 FORGET_Frame();
79 if ( ALLOC_ARRAY( kern0->pairs, num_pairs, TT_Kern_0_Pair ) )
80 return error;
82 if ( ACCESS_Frame( num_pairs * 6L ) )
83 goto Fail;
85 for ( n = 0; n < num_pairs; n++ )
87 kern0->pairs[n].left = GET_UShort();
88 kern0->pairs[n].right = GET_UShort();
89 kern0->pairs[n].value = GET_UShort();
91 if ( kern0->pairs[n].left >= input->numGlyphs ||
92 kern0->pairs[n].right >= input->numGlyphs )
94 FORGET_Frame();
95 error = TT_Err_Invalid_Kerning_Table;
96 goto Fail;
100 FORGET_Frame();
102 /* we're ok, set the pairs count */
103 kern0->nPairs = num_pairs;
105 return TT_Err_Ok;
107 Fail:
108 FREE( kern0->pairs );
109 return error;
113 /*******************************************************************
115 * Function : SubTable_Load_2
117 * Description : Loads a format 2 kerning subtable data.
119 * Input : kern2 pointer to the kerning subtable
120 * length subtable length. This is required as
121 * the subheader doesn't give any indication
122 * of the size of the `array' table.
124 * Output : error code
126 * Notes : - Assumes that the stream is already `used'
128 * - the file cursor must be set by the caller
130 * - in case of error, the function _must_ destroy
131 * the data it allocates!
133 ******************************************************************/
135 static TT_Error Subtable_Load_2( TT_Kern_2* kern2,
136 PFace input )
138 DEFINE_LOAD_LOCALS( input->stream );
140 Long table_base;
142 UShort left_offset, right_offset, array_offset;
143 ULong array_size;
144 UShort left_max, right_max, n;
147 /* record the table offset */
148 table_base = FILE_Pos();
150 if ( ACCESS_Frame( 8L ) )
151 return error;
153 kern2->rowWidth = GET_UShort();
154 left_offset = GET_UShort();
155 right_offset = GET_UShort();
156 array_offset = GET_UShort();
158 FORGET_Frame();
160 /* first load left and right glyph classes */
162 if ( FILE_Seek( table_base + left_offset ) ||
163 ACCESS_Frame( 4L ) )
164 return error;
166 kern2->leftClass.firstGlyph = GET_UShort();
167 kern2->leftClass.nGlyphs = GET_UShort();
169 FORGET_Frame();
171 if ( ALLOC_ARRAY( kern2->leftClass.classes,
172 kern2->leftClass.nGlyphs,
173 UShort ) )
174 return error;
176 /* load left offsets */
178 if ( ACCESS_Frame( kern2->leftClass.nGlyphs * 2L ) )
179 goto Fail_Left;
181 for ( n = 0; n < kern2->leftClass.nGlyphs; n++ )
182 kern2->leftClass.classes[n] = GET_UShort();
184 FORGET_Frame();
186 /* right class */
188 if ( FILE_Seek( table_base + right_offset ) ||
189 ACCESS_Frame( 4L ) )
190 goto Fail_Left;
192 kern2->rightClass.firstGlyph = GET_UShort();
193 kern2->rightClass.nGlyphs = GET_UShort();
195 FORGET_Frame();
197 if ( ALLOC_ARRAY( kern2->rightClass.classes,
198 kern2->rightClass.nGlyphs,
199 UShort ) )
200 goto Fail_Left;
202 /* load right offsets */
204 if ( ACCESS_Frame( kern2->rightClass.nGlyphs * 2L ) )
205 goto Fail_Right;
207 for ( n = 0; n < kern2->rightClass.nGlyphs; n++ )
208 kern2->rightClass.classes[n] = GET_UShort();
210 FORGET_Frame();
212 /* Now load the kerning array. We don't have its size, we */
213 /* must compute it from what we know. */
215 /* We thus compute the maximum left and right offsets and */
216 /* add them to get the array size. */
218 left_max = right_max = 0;
220 for ( n = 0; n < kern2->leftClass.nGlyphs; n++ )
221 left_max = MAX( left_max, kern2->leftClass.classes[n] );
223 for ( n = 0; n < kern2->rightClass.nGlyphs; n++ )
224 right_max = MAX( right_max, kern2->leftClass.classes[n] );
226 array_size = left_max + right_max + 2;
228 if ( ALLOC( kern2->array, array_size ) )
229 goto Fail_Right;
231 if ( ACCESS_Frame( array_size ) )
232 goto Fail_Array;
234 for ( n = 0; n < array_size/2; n++ )
235 kern2->array[n] = GET_Short();
237 FORGET_Frame();
239 /* we're good now */
241 return TT_Err_Ok;
243 Fail_Array:
244 FREE( kern2->array );
246 Fail_Right:
247 FREE( kern2->rightClass.classes );
248 kern2->rightClass.nGlyphs = 0;
250 Fail_Left:
251 FREE( kern2->leftClass.classes );
252 kern2->leftClass.nGlyphs = 0;
254 return error;
258 /*******************************************************************
260 * Function : Kerning_Create
262 * Description : Creates the kerning directory if a face is
263 * loaded. The tables however are loaded on
264 * demand to save space.
266 * Input : face pointer to the parent face object
267 * kern pointer to the extension's kerning field
269 * Output : error code
271 * Notes : as in all constructors, the memory allocated isn't
272 * released in case of failure. Rather, the task is left
273 * to the destructor (which is called if an error
274 * occurs during the loading of a face).
276 ******************************************************************/
278 static TT_Error Kerning_Create( void* ext,
279 PFace face )
281 DEFINE_LOAD_LOCALS( face->stream );
283 TT_Kerning* kern = (TT_Kerning*)ext;
284 UShort num_tables;
285 Long table;
287 TT_Kern_Subtable* sub;
290 /* by convention */
291 if ( !kern )
292 return TT_Err_Ok;
294 /* Now load the kerning directory. We're called from the face */
295 /* constructor. We thus need not use the stream. */
297 kern->version = 0;
298 kern->nTables = 0;
299 kern->tables = NULL;
301 table = TT_LookUp_Table( face, TTAG_kern );
302 if ( table < 0 )
303 return TT_Err_Ok; /* The table is optional */
305 if ( FILE_Seek( face->dirTables[table].Offset ) ||
306 ACCESS_Frame( 4L ) )
307 return error;
309 kern->version = GET_UShort();
310 num_tables = GET_UShort();
312 FORGET_Frame();
314 /* we don't set kern->nTables until we have allocated the array */
316 if ( ALLOC_ARRAY( kern->tables, num_tables, TT_Kern_Subtable ) )
317 return error;
319 kern->nTables = num_tables;
321 /* now load the directory entries, but do _not_ load the tables ! */
323 sub = kern->tables;
325 for ( table = 0; table < num_tables; table++ )
327 if ( ACCESS_Frame( 6L ) )
328 return error;
330 sub->loaded = FALSE; /* redundant, but good to see */
331 sub->version = GET_UShort();
332 sub->length = GET_UShort() - 6; /* substract header length */
333 sub->format = GET_Byte();
334 sub->coverage = GET_Byte();
336 FORGET_Frame();
338 sub->offset = FILE_Pos();
340 /* now skip to the next table */
342 if ( FILE_Skip( sub->length ) )
343 return error;
345 sub++;
348 /* that's fine, leave now */
350 return TT_Err_Ok;
354 /*******************************************************************
356 * Function : Kerning_Destroy
358 * Description : Destroys all kerning information.
360 * Input : kern pointer to the extension's kerning field
362 * Output : error code
364 * Notes : This function is a destructor; it must be able
365 * to destroy partially built tables.
367 ******************************************************************/
369 static TT_Error Kerning_Destroy( void* ext,
370 PFace face )
372 TT_Kerning* kern = (TT_Kerning*)ext;
373 TT_Kern_Subtable* sub;
374 UShort n;
377 /* by convention */
378 if ( !kern )
379 return TT_Err_Ok;
381 if ( kern->nTables == 0 )
382 return TT_Err_Ok; /* no tables to release */
384 /* scan the table directory and release loaded entries */
386 sub = kern->tables;
387 for ( n = 0; n < kern->nTables; n++ )
389 if ( sub->loaded )
391 switch ( sub->format )
393 case 0:
394 FREE( sub->t.kern0.pairs );
395 sub->t.kern0.nPairs = 0;
396 sub->t.kern0.searchRange = 0;
397 sub->t.kern0.entrySelector = 0;
398 sub->t.kern0.rangeShift = 0;
399 break;
401 case 2:
402 FREE( sub->t.kern2.leftClass.classes );
403 sub->t.kern2.leftClass.firstGlyph = 0;
404 sub->t.kern2.leftClass.nGlyphs = 0;
406 FREE( sub->t.kern2.rightClass.classes );
407 sub->t.kern2.rightClass.firstGlyph = 0;
408 sub->t.kern2.rightClass.nGlyphs = 0;
410 FREE( sub->t.kern2.array );
411 sub->t.kern2.rowWidth = 0;
412 break;
414 default:
415 ; /* invalid subtable format - do nothing */
418 sub->loaded = FALSE;
419 sub->version = 0;
420 sub->offset = 0;
421 sub->length = 0;
422 sub->coverage = 0;
423 sub->format = 0;
425 sub++;
428 FREE( kern->tables );
429 kern->nTables = 0;
431 return TT_Err_Ok;
435 /*******************************************************************
437 * Function : TT_Get_Kerning_Directory
439 * Description : Returns a given face's kerning directory.
441 * Input : face handle to the face object
442 * directory pointer to client's target directory
444 * Output : error code
446 * Notes : The kerning table directory is loaded with the face
447 * through the extension constructor. However, the kerning
448 * tables themselves are only loaded on demand, as they
449 * may represent a lot of data, unneeded by most uses of
450 * the engine.
452 ******************************************************************/
454 EXPORT_FUNC
455 TT_Error TT_Get_Kerning_Directory( TT_Face face,
456 TT_Kerning* directory )
458 PFace faze = HANDLE_Face( face );
459 TT_Error error;
460 TT_Kerning* kerning;
463 if ( !faze )
464 return TT_Err_Invalid_Face_Handle;
466 /* copy directory header */
467 error = TT_Extension_Get( faze, KERNING_ID, (void**)&kerning );
468 if ( !error )
469 *directory = *kerning;
471 return error;
475 /*******************************************************************
477 * Function : TT_Load_Kerning_Table
479 * Description : Loads a kerning table intro memory.
481 * Input : face face handle
482 * kern_index index in the face's kerning directory
484 * Output : error code
486 * Notes :
488 ******************************************************************/
490 EXPORT_FUNC
491 TT_Error TT_Load_Kerning_Table( TT_Face face,
492 TT_UShort kern_index )
494 TT_Error error;
495 TT_Stream stream;
497 TT_Kerning* kern;
498 TT_Kern_Subtable* sub;
501 PFace faze = HANDLE_Face( face );
503 if ( !faze )
504 return TT_Err_Invalid_Face_Handle;
506 error = TT_Extension_Get( faze, KERNING_ID, (void**)&kern );
507 if ( error )
508 return error;
510 if ( kern->nTables == 0 )
511 return TT_Err_Table_Missing;
513 if ( kern_index >= kern->nTables )
514 return TT_Err_Invalid_Argument;
516 sub = kern->tables + kern_index;
518 if ( sub->format != 0 && sub->format != 2 )
519 return TT_Err_Invalid_Kerning_Table_Format;
521 /* now access stream */
522 if ( USE_Stream( faze->stream, stream ) )
523 return error;
525 if ( FILE_Seek( sub->offset ) )
526 goto Fail;
528 if ( sub->format == 0 )
529 error = Subtable_Load_0( &sub->t.kern0, faze );
530 else if ( sub->format == 2 )
531 error = Subtable_Load_2( &sub->t.kern2, faze );
533 if ( !error )
534 sub->loaded = TRUE;
536 Fail:
537 /* release stream */
538 DONE_Stream( stream );
540 return error;
544 EXPORT_FUNC
545 TT_Error TT_Init_Kerning_Extension( TT_Engine engine )
547 PEngine_Instance _engine = HANDLE_Engine( engine );
549 TT_Error error;
552 if ( !_engine )
553 return TT_Err_Invalid_Engine;
555 error = TT_Register_Extension( _engine,
556 KERNING_ID,
557 sizeof ( TT_Kerning ),
558 Kerning_Create,
559 Kerning_Destroy );
560 return error;
564 /* END */