1 /*******************************************************************
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 ******************************************************************/
29 #include "ttload.h" /* For the macros */
32 /* Required by the tracing mode */
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
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
,
61 DEFINE_LOAD_LOCALS( input
->stream
);
66 if ( ACCESS_Frame( 8L ) )
69 num_pairs
= GET_UShort();
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 */
79 if ( ALLOC_ARRAY( kern0
->pairs
, num_pairs
, TT_Kern_0_Pair
) )
82 if ( ACCESS_Frame( num_pairs
* 6L ) )
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
)
95 error
= TT_Err_Invalid_Kerning_Table
;
102 /* we're ok, set the pairs count */
103 kern0
->nPairs
= num_pairs
;
108 FREE( kern0
->pairs
);
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
,
138 DEFINE_LOAD_LOCALS( input
->stream
);
142 UShort left_offset
, right_offset
, array_offset
;
144 UShort left_max
, right_max
, n
;
147 /* record the table offset */
148 table_base
= FILE_Pos();
150 if ( ACCESS_Frame( 8L ) )
153 kern2
->rowWidth
= GET_UShort();
154 left_offset
= GET_UShort();
155 right_offset
= GET_UShort();
156 array_offset
= GET_UShort();
160 /* first load left and right glyph classes */
162 if ( FILE_Seek( table_base
+ left_offset
) ||
166 kern2
->leftClass
.firstGlyph
= GET_UShort();
167 kern2
->leftClass
.nGlyphs
= GET_UShort();
171 if ( ALLOC_ARRAY( kern2
->leftClass
.classes
,
172 kern2
->leftClass
.nGlyphs
,
176 /* load left offsets */
178 if ( ACCESS_Frame( kern2
->leftClass
.nGlyphs
* 2L ) )
181 for ( n
= 0; n
< kern2
->leftClass
.nGlyphs
; n
++ )
182 kern2
->leftClass
.classes
[n
] = GET_UShort();
188 if ( FILE_Seek( table_base
+ right_offset
) ||
192 kern2
->rightClass
.firstGlyph
= GET_UShort();
193 kern2
->rightClass
.nGlyphs
= GET_UShort();
197 if ( ALLOC_ARRAY( kern2
->rightClass
.classes
,
198 kern2
->rightClass
.nGlyphs
,
202 /* load right offsets */
204 if ( ACCESS_Frame( kern2
->rightClass
.nGlyphs
* 2L ) )
207 for ( n
= 0; n
< kern2
->rightClass
.nGlyphs
; n
++ )
208 kern2
->rightClass
.classes
[n
] = GET_UShort();
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
) )
231 if ( ACCESS_Frame( array_size
) )
234 for ( n
= 0; n
< array_size
/2; n
++ )
235 kern2
->array
[n
] = GET_Short();
244 FREE( kern2
->array
);
247 FREE( kern2
->rightClass
.classes
);
248 kern2
->rightClass
.nGlyphs
= 0;
251 FREE( kern2
->leftClass
.classes
);
252 kern2
->leftClass
.nGlyphs
= 0;
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
,
281 DEFINE_LOAD_LOCALS( face
->stream
);
283 TT_Kerning
* kern
= (TT_Kerning
*)ext
;
287 TT_Kern_Subtable
* sub
;
294 /* Now load the kerning directory. We're called from the face */
295 /* constructor. We thus need not use the stream. */
301 table
= TT_LookUp_Table( face
, TTAG_kern
);
303 return TT_Err_Ok
; /* The table is optional */
305 if ( FILE_Seek( face
->dirTables
[table
].Offset
) ||
309 kern
->version
= GET_UShort();
310 num_tables
= GET_UShort();
314 /* we don't set kern->nTables until we have allocated the array */
316 if ( ALLOC_ARRAY( kern
->tables
, num_tables
, TT_Kern_Subtable
) )
319 kern
->nTables
= num_tables
;
321 /* now load the directory entries, but do _not_ load the tables ! */
325 for ( table
= 0; table
< num_tables
; table
++ )
327 if ( ACCESS_Frame( 6L ) )
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();
338 sub
->offset
= FILE_Pos();
340 /* now skip to the next table */
342 if ( FILE_Skip( sub
->length
) )
348 /* that's fine, leave now */
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
,
372 TT_Kerning
* kern
= (TT_Kerning
*)ext
;
373 TT_Kern_Subtable
* sub
;
381 if ( kern
->nTables
== 0 )
382 return TT_Err_Ok
; /* no tables to release */
384 /* scan the table directory and release loaded entries */
387 for ( n
= 0; n
< kern
->nTables
; n
++ )
391 switch ( sub
->format
)
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;
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;
415 ; /* invalid subtable format - do nothing */
428 FREE( kern
->tables
);
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
452 ******************************************************************/
455 TT_Error
TT_Get_Kerning_Directory( TT_Face face
,
456 TT_Kerning
* directory
)
458 PFace faze
= HANDLE_Face( face
);
464 return TT_Err_Invalid_Face_Handle
;
466 /* copy directory header */
467 error
= TT_Extension_Get( faze
, KERNING_ID
, (void**)&kerning
);
469 *directory
= *kerning
;
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
488 ******************************************************************/
491 TT_Error
TT_Load_Kerning_Table( TT_Face face
,
492 TT_UShort kern_index
)
498 TT_Kern_Subtable
* sub
;
501 PFace faze
= HANDLE_Face( face
);
504 return TT_Err_Invalid_Face_Handle
;
506 error
= TT_Extension_Get( faze
, KERNING_ID
, (void**)&kern
);
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
) )
525 if ( FILE_Seek( sub
->offset
) )
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
);
538 DONE_Stream( stream
);
545 TT_Error
TT_Init_Kerning_Extension( TT_Engine engine
)
547 PEngine_Instance _engine
= HANDLE_Engine( engine
);
553 return TT_Err_Invalid_Engine
;
555 error
= TT_Register_Extension( _engine
,
557 sizeof ( TT_Kerning
),