📄 ftxgpos.c
字号:
FT_UShort index, property, first_pos; TTO_GPOSHeader* gpos = gpi->gpos; if ( buffer->in_pos >= buffer->in_length - 1 ) return TTO_Err_Not_Covered; /* Not enough glyphs in stream */ if ( context_length != 0xFFFF && context_length < 2 ) return TTO_Err_Not_Covered; if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) ) return error; error = Coverage_Index( &pp->Coverage, IN_CURGLYPH(), &index ); if ( error ) return error; /* second glyph */ first_pos = buffer->in_pos; (buffer->in_pos)++; while ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) ) { if ( error && error != TTO_Err_Not_Covered ) return error; if ( buffer->in_pos == buffer->in_length ) return TTO_Err_Not_Covered; (buffer->in_pos)++; } switch ( pp->PosFormat ) { case 1: error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer, first_pos, index, pp->ValueFormat1, pp->ValueFormat2 ); break; case 2: error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos, pp->ValueFormat1, pp->ValueFormat2 ); break; default: return TTO_Err_Invalid_GPOS_SubTable_Format; } /* adjusting the `next' glyph */ if ( pp->ValueFormat2 ) (buffer->in_pos)++; return error; } /* LookupType 3 */ /* CursivePosFormat1 */ FT_Error Load_CursivePos( TTO_CursivePos* cp, FT_Stream stream ) { FT_Error error; FT_Memory memory = stream->memory; FT_UShort n, m, count; FT_ULong cur_offset, new_offset, base_offset; TTO_EntryExitRecord* eer; base_offset = FILE_Pos(); if ( ACCESS_Frame( 4L ) ) return error; cp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &cp->Coverage, stream ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 2L ) ) goto Fail2; count = cp->EntryExitCount = GET_UShort(); FORGET_Frame(); cp->EntryExitRecord = NULL; if ( ALLOC_ARRAY( cp->EntryExitRecord, count, TTO_EntryExitRecord ) ) goto Fail2; eer = cp->EntryExitRecord; for ( n = 0; n < count; n++ ) { FT_ULong entry_offset; if ( ACCESS_Frame( 2L ) ) return error; entry_offset = new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &eer[n].EntryAnchor, stream ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } else eer[n].EntryAnchor.PosFormat = 0; if ( ACCESS_Frame( 2L ) ) return error; new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &eer[n].ExitAnchor, stream ) ) != TT_Err_Ok ) { if ( entry_offset ) Free_Anchor( &eer[n].EntryAnchor, memory ); goto Fail1; } (void)FILE_Seek( cur_offset ); } else eer[n].ExitAnchor.PosFormat = 0; } return TT_Err_Ok; Fail1: for ( m = 0; m < n; m++ ) { Free_Anchor( &eer[m].EntryAnchor, memory ); Free_Anchor( &eer[m].ExitAnchor, memory ); } FREE( eer ); Fail2: Free_Coverage( &cp->Coverage, memory ); return error; } void Free_CursivePos( TTO_CursivePos* cp, FT_Memory memory ) { FT_UShort n, count; TTO_EntryExitRecord* eer; if ( cp->EntryExitRecord ) { count = cp->EntryExitCount; eer = cp->EntryExitRecord; for ( n = 0; n < count; n++ ) { Free_Anchor( &eer[n].EntryAnchor, memory ); Free_Anchor( &eer[n].ExitAnchor, memory ); } FREE( eer ); } Free_Coverage( &cp->Coverage, memory ); } static FT_Error Lookup_CursivePos( GPOS_Instance* gpi, TTO_CursivePos* cp, OTL_Buffer buffer, FT_UShort flags, FT_UShort context_length ) { FT_UShort index, property; FT_Error error; TTO_GPOSHeader* gpos = gpi->gpos; TTO_EntryExitRecord* eer; FT_Pos entry_x, entry_y; FT_Pos exit_x, exit_y; if ( context_length != 0xFFFF && context_length < 1 ) { gpi->last = 0xFFFF; return TTO_Err_Not_Covered; } /* Glyphs not having the right GDEF properties will be ignored, i.e., gpi->last won't be reset (contrary to user defined properties). */ if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) ) return error; /* We don't handle mark glyphs here. According to Andrei, this isn't possible, but who knows... */ if ( property == MARK_GLYPH ) { gpi->last = 0xFFFF; return TTO_Err_Not_Covered; } error = Coverage_Index( &cp->Coverage, IN_CURGLYPH(), &index ); if ( error ) { gpi->last = 0xFFFF; return error; } if ( index >= cp->EntryExitCount ) return TTO_Err_Invalid_GPOS_SubTable; eer = &cp->EntryExitRecord[index]; /* Now comes the messiest part of the whole OpenType specification. At first glance, cursive connections seem easy to understand, but there are pitfalls! The reason is that the specs don't mention how to compute the advance values resp. glyph offsets. I was told it would be an omission, to be fixed in the next OpenType version... Again many thanks to Andrei Burago <andreib@microsoft.com> for clarifications. Consider the following example: | xadv1 | +---------+ | | +-----+--+ 1 | | | .| | | 0+--+------+ | 2 | | | 0+--------+ | xadv2 | glyph1: advance width = 12 anchor point = (3,1) glyph2: advance width = 11 anchor point = (9,4) LSB is 1 for both glyphs (so the boxes drawn above are glyph bboxes). Writing direction is R2L; `0' denotes the glyph's coordinate origin. Now the surprising part: The advance width of the *left* glyph (resp. of the *bottom* glyph) will be modified, no matter whether the writing direction is L2R or R2L (resp. T2B or B2T)! This assymetry is caused by the fact that the glyph's coordinate origin is always the lower left corner for all writing directions. Continuing the above example, we can compute the new (horizontal) advance width of glyph2 as 9 - 3 = 6 , and the new vertical offset of glyph2 as 1 - 4 = -3 . Vertical writing direction is far more complicated: a) Assuming that we recompute the advance height of the lower glyph: -- +---------+ -- | | +-----+--+ 1 | yadv1 | | .| | yadv2 | 0+--+------+ -- BSB1 -- | 2 | -- -- y_offset | | BSB2 -- 0+--------+ -- -- -- glyph1: advance height = 6 anchor point = (3,1) glyph2: advance height = 7 anchor point = (9,4) TSB is 1 for both glyphs; writing direction is T2B. BSB1 = yadv1 - (TSB1 + ymax1) BSB2 = yadv2 - (TSB2 + ymax2) y_offset = y2 - y1 vertical advance width of glyph2 = y_offset + BSB2 - BSB1 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1)) = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1) = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1 b) Assuming that we recompute the advance height of the upper glyph: -- -- +---------+ -- TSB1 -- -- | | TSB2 -- +-----+--+ 1 | yadv1 ymax1 | | .| | yadv2 | 0+--+------+ -- -- ymax2 | 2 | -- y_offset | | -- 0+--------+ -- -- glyph1: advance height = 6 anchor point = (3,1) glyph2: advance height = 7 anchor point = (9,4) TSB is 1 for both glyphs; writing direction is T2B. y_offset = y2 - y1 vertical advance width of glyph2 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2) = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2 Comparing a) with b) shows that b) is easier to compute. I'll wait for a reply from Andrei to see what should really be implemented... Since horizontal advance widths or vertical advance heights can be used alone but not together, no ambiguity occurs. */ if ( gpi->last == 0xFFFF ) goto end; /* Get_Anchor() returns TTO_Err_Not_Covered if there is no anchor table. */ error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH(), &entry_x, &entry_y ); if ( error == TTO_Err_Not_Covered ) goto end; if ( error ) return error; if ( gpi->r2l ) { POSITION( buffer->in_pos )->x_advance = entry_x - gpi->anchor_x; POSITION( buffer->in_pos )->new_advance = TRUE; } else { POSITION( gpi->last )->x_advance = gpi->anchor_x - entry_x; POSITION( gpi->last )->new_advance = TRUE; } if ( flags & RIGHT_TO_LEFT ) { POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos; POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y; } else { POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last; POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y; } end: error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH(), &exit_x, &exit_y ); if ( error == TTO_Err_Not_Covered ) gpi->last = 0xFFFF; else { gpi->last = buffer->in_pos; gpi->anchor_x = exit_x; gpi->anchor_y = exit_y; } if ( error ) return error; (buffer->in_pos)++; return TT_Err_Ok; } /* LookupType 4 */ /* BaseArray */ static FT_Error Load_BaseArray( TTO_BaseArray* ba, FT_UShort num_classes, FT_Stream stream ) { FT_Error error; FT_Memory memory = stream->memory; FT_UShort m, n, k, count; FT_ULong cur_offset, new_offset, base_offset; TTO_BaseRecord* br; TTO_Anchor* ban; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = ba->BaseCount = GET_UShort(); FORGET_Frame(); ba->BaseRecord = NULL; if ( ALLOC_ARRAY( ba->BaseRecord, count, TTO_BaseRecord ) ) return error; br = ba->BaseRecord; for ( m = 0; m < count; m++ ) { br[m].BaseAnchor = NULL; if ( ALLOC_ARRAY( br[m].BaseAnchor, num_classes, TTO_Anchor ) ) goto Fail; ban = br[m].BaseAnchor; for ( n = 0; n < num_classes; n++ ) { if ( ACCESS_Frame( 2L ) ) goto Fail0; new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &ban[n], stream ) ) != TT_Err_Ok ) goto Fail0; (void)FILE_Seek( cur_offset ); } continue; Fail0: for ( k = 0; k < n; k++ ) Free_Anchor( &ban[k], memory ); goto Fail; } return TT_Err_Ok; Fail: for ( k = 0; k < m; k++ ) { ban = br[k].BaseAnchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &ban[n], memory ); FREE( ban ); } FREE( br ); return error; } static void Free_BaseArray( TTO_BaseArray* ba, FT_UShort num_classes, FT_Memory memory ) { FT_UShort m, n, count; TTO_BaseRecord* br; TTO_Anchor* ban; if ( ba->BaseRecord ) { count = ba->BaseCount; br = ba->BaseRecord; for ( m = 0; m < count; m++ ) { ban = br[m].BaseAnchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &ban[n], memory ); FREE( ban ); } FREE( br ); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -