diff --git a/ChangeLog b/ChangeLog index b4f88d2fc..222cd72e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2002-10-05 David Turner + + * src/pfr/pfrsbit.h, src/pfr/pfrsbit.c, src/pfr/pfrload.c, + src/pfr/pfrgload.c, src/pfr/pfrobjs.c, src/pfr/pfrtypes.h, + Jamfile, src/base/ftobjs.c: adding support for embedded bitmaps to + the PFR driver, and rewriting its kerning loader / handler to use all + kerning pairs in a physical font (and not just the first item). + + * src/tools/docmaker/content.py, src/tools/docmaker/sources.py, + src/tools/docmaker/tohtml.py: fixing a few nasty bugs + + * src/sfnt/ttcmap0.c: the validator for format 4 sub-tables is + now capable of dealing with invalid "length" fields at the start + of the sub-table. This allows fonts like "mg______.ttf" (i.e. + Marriage) to return accurate charmaps. + 2002-10-05 Werner Lemberg * src/smooth/ftgrays.c (SUBPIXELS): Add cast to `TPos'. diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index bd07ac50a..4c6156007 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -433,6 +433,7 @@ /* disable scaling, hinting, and transformation */ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | + FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM; /* disable bitmap rendering */ @@ -466,7 +467,8 @@ /* XXX: This is really a temporary hack that should disappear */ /* promptly with FreeType 2.1! */ /* */ - if ( FT_HAS_FIXED_SIZES( face ) ) + if ( FT_HAS_FIXED_SIZES( face ) && + ( load_flags & FT_LOAD_NO_BITMAP ) == 0 ) { error = driver->clazz->load_glyph( slot, face->size, glyph_index, diff --git a/src/pfr/Jamfile b/src/pfr/Jamfile index e29c67470..73ffe4bc7 100644 --- a/src/pfr/Jamfile +++ b/src/pfr/Jamfile @@ -10,7 +10,7 @@ SubDirHdrs [ FT2_SubDir src pfr ] ; if $(FT2_MULTI) { - _sources = pfrdrivr pfrgload pfrload pfrobjs pfrcmap ; + _sources = pfrdrivr pfrgload pfrload pfrobjs pfrcmap pfrsbit ; } else { diff --git a/src/pfr/pfr.c b/src/pfr/pfr.c index 632820842..eb2c4edb7 100644 --- a/src/pfr/pfr.c +++ b/src/pfr/pfr.c @@ -24,5 +24,6 @@ #include "pfrcmap.c" #include "pfrobjs.c" #include "pfrdrivr.c" +#include "pfrsbit.c" /* END */ diff --git a/src/pfr/pfrgload.c b/src/pfr/pfrgload.c index dd8e114c7..cb5c60b88 100644 --- a/src/pfr/pfrgload.c +++ b/src/pfr/pfrgload.c @@ -17,6 +17,7 @@ #include "pfrgload.h" +#include "pfrsbit.h" #include "pfrload.h" /* for macro definitions */ #include FT_INTERNAL_DEBUG_H @@ -676,6 +677,9 @@ } + + + static FT_Error pfr_glyph_load_rec( PFR_Glyph glyph, FT_Stream stream, @@ -776,6 +780,9 @@ } + + + FT_LOCAL_DEF( FT_Error ) pfr_glyph_load( PFR_Glyph glyph, FT_Stream stream, diff --git a/src/pfr/pfrload.c b/src/pfr/pfrload.c index e6154bb0d..a165b6f0b 100644 --- a/src/pfr/pfrload.c +++ b/src/pfr/pfrload.c @@ -52,7 +52,7 @@ FT_Error error = 0; FT_Byte* p = *pp; FT_UInt num_items, item_type, item_size; - + PFR_CHECK( 1 ); num_items = PFR_NEXT_BYTE( p ); @@ -62,11 +62,11 @@ PFR_CHECK( 2 ); item_size = PFR_NEXT_BYTE( p ); item_type = PFR_NEXT_BYTE( p ); - + PFR_CHECK( item_size ); if ( item_list ) - { + { PFR_ExtraItem extra = item_list; @@ -76,19 +76,19 @@ { error = extra->parser( p, p + item_size, item_data ); if ( error ) goto Exit; - + break; } } } - + p += item_size; } Exit: *pp = p; return error; - + Too_Short: FT_ERROR(( "pfr_extra_items_parse: invalid extra items table\n" )); error = PFR_Err_Invalid_Table; @@ -204,17 +204,17 @@ FT_Error error; FT_UInt count; FT_UInt result = 0; - + if ( FT_STREAM_SEEK( section_offset ) || FT_READ_USHORT( count ) ) goto Exit; - + result = count; - + Exit: *acount = result; return error; - } + } FT_LOCAL_DEF( FT_Error ) @@ -252,30 +252,30 @@ FT_Byte* p; FT_Byte* limit; FT_UInt local; - + if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( size ) ) goto Exit; p = stream->cursor; limit = p + size; - + PFR_CHECK(13); - + log_font->matrix[0] = PFR_NEXT_LONG( p ); log_font->matrix[1] = PFR_NEXT_LONG( p ); log_font->matrix[2] = PFR_NEXT_LONG( p ); log_font->matrix[3] = PFR_NEXT_LONG( p ); - + flags = PFR_NEXT_BYTE( p ); - + local = 0; if ( flags & PFR_LOG_STROKE ) { local++; if ( flags & PFR_LOG_2BYTE_STROKE ) local++; - + if ( (flags & PFR_LINE_JOIN_MASK) == PFR_LINE_JOIN_MITER ) local += 3; } @@ -293,18 +293,18 @@ log_font->stroke_thickness = ( flags & PFR_LOG_2BYTE_STROKE ) ? PFR_NEXT_SHORT( p ) : PFR_NEXT_BYTE( p ); - + if ( ( flags & PFR_LINE_JOIN_MASK ) == PFR_LINE_JOIN_MITER ) log_font->miter_limit = PFR_NEXT_LONG( p ); } - + if ( flags & PFR_LOG_BOLD ) { log_font->bold_thickness = ( flags & PFR_LOG_2BYTE_BOLD ) ? PFR_NEXT_SHORT( p ) : PFR_NEXT_BYTE( p ); } - + if ( flags & PFR_LOG_EXTRA_ITEMS ) { error = pfr_extra_items_skip( &p, limit ); @@ -326,7 +326,7 @@ Exit: return error; - + Too_Short: FT_ERROR(( "pfr_log_font_load: invalid logical font table\n" )); error = PFR_Err_Invalid_Table; @@ -355,7 +355,7 @@ FT_UInt n, count, size1; FT_Error error = 0; - + PFR_CHECK( 5 ); p += 3; /* skip bctSize */ @@ -366,12 +366,12 @@ if ( phy_font->num_strikes + count > phy_font->max_strikes ) { FT_UInt new_max = (phy_font->num_strikes + count + 3) & -4; - + if ( FT_RENEW_ARRAY( phy_font->strikes, phy_font->num_strikes, new_max ) ) goto Exit; - + phy_font->max_strikes = new_max; } @@ -421,15 +421,15 @@ } phy_font->num_strikes += count; - + Exit: return error; - + Too_Short: error = PFR_Err_Invalid_Table; FT_ERROR(( "pfr_extra_item_load_bitmap_info: invalid bitmap info table\n" )); goto Exit; - } + } /* load font ID, i.e. name */ @@ -441,18 +441,18 @@ FT_Error error = 0; FT_Memory memory = phy_font->memory; FT_UInt len = (FT_UInt)( limit - p ); - + if ( phy_font->font_id != NULL ) goto Exit; - + if ( FT_ALLOC( phy_font->font_id, len+1 ) ) goto Exit; /* copy font ID name, and terminate it for safety */ FT_MEM_COPY( phy_font->font_id, p, len ); phy_font->font_id[len] = 0; - + Exit: return error; } @@ -472,35 +472,37 @@ if ( phy_font->vertical.stem_snaps != NULL ) goto Exit; - + PFR_CHECK( 1 ); count = PFR_NEXT_BYTE( p ); - + num_vert = count & 15; num_horz = count >> 4; count = num_vert + num_horz; - + PFR_CHECK( count * 2 ); - + if ( FT_NEW_ARRAY( snaps, count ) ) goto Exit; - + phy_font->vertical.stem_snaps = snaps; phy_font->horizontal.stem_snaps = snaps + num_vert; - + for ( ; count > 0; count--, snaps++ ) *snaps = FT_NEXT_SHORT( p ); - + Exit: return error; - + Too_Short: error = PFR_Err_Invalid_Table; FT_ERROR(( "pfr_exta_item_load_stem_snaps: invalid stem snaps table\n" )); goto Exit; - } + } + +#if 0 /* load kerning pair data */ FT_CALLBACK_DEF( FT_Error ) pfr_extra_item_load_kerning_pairs( FT_Byte* p, @@ -516,24 +518,25 @@ FT_Memory memory = phy_font->memory; + /* allocate a new kerning item */ /* XXX: there may be multiple extra items for kerning */ if ( phy_font->kern_pairs != NULL ) goto Exit; FT_TRACE2(( "pfr_extra_item_load_kerning_pairs()\n" )); - + PFR_CHECK( 4 ); num_pairs = PFR_NEXT_BYTE( p ); base_adj = PFR_NEXT_SHORT( p ); flags = PFR_NEXT_BYTE( p ); - + #ifndef PFR_CONFIG_NO_CHECKS count = 3; - + if ( flags & PFR_KERN_2BYTE_CHAR ) count += 2; - + if ( flags & PFR_KERN_2BYTE_ADJ ) count += 1; @@ -542,10 +545,10 @@ if ( FT_NEW_ARRAY( pairs, num_pairs ) ) goto Exit; - + phy_font->num_kern_pairs = num_pairs; phy_font->kern_pairs = pairs; - + for (count = num_pairs ; count > 0; count--, pairs++ ) { if ( flags & PFR_KERN_2BYTE_CHAR ) @@ -558,7 +561,7 @@ pairs->glyph1 = PFR_NEXT_BYTE( p ); pairs->glyph2 = PFR_NEXT_BYTE( p ); } - + if ( flags & PFR_KERN_2BYTE_ADJ ) pairs->kerning.x = base_adj + PFR_NEXT_SHORT( p ); else @@ -569,17 +572,111 @@ FT_TRACE2(( "kerning %d <-> %d : %ld\n", pairs->glyph1, pairs->glyph2, pairs->kerning.x )); } - + Exit: return error; - + Too_Short: error = PFR_Err_Invalid_Table; FT_ERROR(( "pfr_extra_item_load_kerning_pairs: " "invalid kerning pairs table\n" )); goto Exit; - } + } +#else + /* load kerning pair data */ + FT_CALLBACK_DEF( FT_Error ) + pfr_extra_item_load_kerning_pairs( FT_Byte* p, + FT_Byte* limit, + PFR_PhyFont phy_font ) + { + PFR_KernItem item; + FT_Error error = 0; + FT_Memory memory = phy_font->memory; + FT_TRACE2(( "pfr_extra_item_load_kerning_pairs()\n" )); + + if ( FT_NEW( item ) ) + goto Exit; + + PFR_CHECK( 4 ); + + item->pair_count = PFR_NEXT_BYTE( p ); + item->base_adj = PFR_NEXT_SHORT( p ); + item->flags = PFR_NEXT_BYTE( p ); + item->offset = phy_font->offset + ( p - phy_font->cursor ); + +#ifndef PFR_CONFIG_NO_CHECKS + item->pair_size = 3; + + if ( item->flags & PFR_KERN_2BYTE_CHAR ) + item->pair_size += 2; + + if ( item->flags & PFR_KERN_2BYTE_ADJ ) + item->pair_size += 1; + + PFR_CHECK( item->pair_count * item->pair_size ); +#endif + + /* load first and last pairs into the item to speed up */ + /* lookup later... */ + if ( item->pair_count > 0 ) + { + FT_UInt char1, char2; + FT_Byte* q; + + if ( item->flags & PFR_KERN_2BYTE_CHAR ) + { + q = p; + char1 = PFR_NEXT_USHORT(q); + char2 = PFR_NEXT_USHORT(q); + + item->pair1 = PFR_KERN_INDEX(char1,char2); + + q = p + item->pair_size*(item->pair_count-1); + char1 = PFR_NEXT_USHORT(q); + char2 = PFR_NEXT_USHORT(q); + + item->pair2 = PFR_KERN_INDEX(char1,char2); + } + else + { + q = p; + char1 = PFR_NEXT_BYTE(q); + char2 = PFR_NEXT_BYTE(q); + + item->pair1 = PFR_KERN_INDEX(char1,char2); + + q = p + item->pair_size*(item->pair_count-1); + char1 = PFR_NEXT_BYTE(q); + char2 = PFR_NEXT_BYTE(q); + + item->pair2 = PFR_KERN_INDEX(char1,char2); + } + + /* add new item to the current list */ + item->next = NULL; + *phy_font->kern_items_tail = item; + phy_font->kern_items_tail = &item->next; + phy_font->num_kern_pairs += item->pair_count; + } + else + { + /* empty item !! */ + FT_FREE( item ); + } + + Exit: + return error; + + Too_Short: + FT_FREE( item ); + + error = PFR_Err_Invalid_Table; + FT_ERROR(( "pfr_extra_item_load_kerning_pairs: " + "invalid kerning pairs table\n" )); + goto Exit; + } +#endif static const PFR_ExtraItemRec pfr_phy_font_extra_items[] = { @@ -607,7 +704,7 @@ FT_FREE( phy_font->strikes ); phy_font->num_strikes = 0; phy_font->max_strikes = 0; - + FT_FREE( phy_font->chars ); phy_font->num_chars = 0; phy_font->chars_offset = 0; @@ -615,10 +712,22 @@ FT_FREE( phy_font->blue_values ); phy_font->num_blue_values = 0; - FT_FREE( phy_font->kern_pairs ); + { + PFR_KernItem item, next; + + item = phy_font->kern_items; + while (item) + { + next = item->next; + FT_FREE( item ); + item = next; + } + phy_font->kern_items = NULL; + phy_font->kern_items_tail = NULL; + } phy_font->num_kern_pairs = 0; } - + FT_LOCAL_DEF( FT_Error ) pfr_phy_font_load( PFR_PhyFont phy_font, @@ -636,9 +745,14 @@ phy_font->memory = memory; phy_font->offset = offset; + phy_font->kern_items = NULL; + phy_font->kern_items_tail = &phy_font->kern_items; + if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( size ) ) goto Exit; + phy_font->cursor = stream->cursor; + p = stream->cursor; limit = p + size; @@ -664,15 +778,15 @@ { error = pfr_extra_items_parse( &p, limit, pfr_phy_font_extra_items, phy_font ); - + if ( error ) goto Fail; } - + /* skip the aux bytes */ PFR_CHECK( 3 ); num_aux = PFR_NEXT_ULONG( p ); - + PFR_CHECK( num_aux ); p += num_aux; @@ -684,7 +798,7 @@ phy_font->num_blue_values = count = PFR_NEXT_BYTE( p ); PFR_CHECK( count * 2 ); - + if ( FT_NEW_ARRAY( phy_font->blue_values, count ) ) goto Fail; @@ -713,19 +827,19 @@ Size = 1 + 1 + 2; if ( flags & PFR_PHY_2BYTE_CHARCODE ) Size += 1; - + if ( flags & PFR_PHY_PROPORTIONAL ) Size += 2; - + if ( flags & PFR_PHY_ASCII_CODE ) Size += 1; - + if ( flags & PFR_PHY_2BYTE_GPS_SIZE ) Size += 1; - + if ( flags & PFR_PHY_3BYTE_GPS_OFFSET ) Size += 1; - + PFR_CHECK( count * Size ); for ( n = 0; n < count; n++ ) @@ -763,9 +877,13 @@ Fail: FT_FRAME_EXIT(); + /* save position of bitmap info */ + phy_font->bct_offset = FT_STREAM_POS(); + phy_font->cursor = NULL; + Exit: return error; - + Too_Short: error = PFR_Err_Invalid_Table; FT_ERROR(( "pfr_phy_font_load: invalid physical font table\n" )); diff --git a/src/pfr/pfrobjs.c b/src/pfr/pfrobjs.c index 1ce257175..af123e966 100644 --- a/src/pfr/pfrobjs.c +++ b/src/pfr/pfrobjs.c @@ -20,6 +20,7 @@ #include "pfrload.h" #include "pfrgload.h" #include "pfrcmap.h" +#include "pfrsbit.h" #include FT_OUTLINE_H #include FT_INTERNAL_DEBUG_H @@ -129,7 +130,11 @@ else root->face_flags |= FT_FACE_FLAG_HORIZONTAL; - /* XXX: kerning and embedded bitmap support isn't there yet */ + if ( phy_font->num_strikes > 0 ) + root->face_flags |= FT_FACE_FLAG_FIXED_SIZES; + + if ( phy_font->num_kern_pairs > 0 ) + root->face_flags |= FT_FACE_FLAG_KERNING; root->family_name = phy_font->font_id; root->style_name = NULL; /* no style name in font file */ @@ -242,6 +247,14 @@ /* check that the glyph index is correct */ FT_ASSERT( gindex < face->phy_font.num_chars ); + /* try to load an embedded bitmap */ + if ( (load_flags & (FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP)) == 0 ) + { + error = pfr_slot_load_bitmap( slot, size, gindex ); + if ( error == 0 ) + goto Exit; + } + gchar = face->phy_font.chars + gindex; slot->root.format = FT_GLYPH_FORMAT_OUTLINE; outline->n_points = 0; @@ -325,6 +338,7 @@ metrics->horiBearingY = cbox.yMax - metrics->height; } + Exit: return error; } @@ -341,9 +355,7 @@ kerning pairs are sorted. We might want to sort it just to make sure */ -#undef PFR_KERN_INDEX -#define PFR_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 ) - +#if 0 /* find the kerning for a given glyph pair */ FT_LOCAL_DEF( FT_Error ) pfr_face_get_kerning( PFR_Face face, @@ -386,5 +398,87 @@ Exit: return 0; } +#else + /* find the kerning for a given glyph pair */ + FT_LOCAL_DEF( FT_Error ) + pfr_face_get_kerning( PFR_Face face, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ) + { + FT_Error error; + PFR_PhyFont phy_font = &face->phy_font; + PFR_KernItem item = phy_font->kern_items; + FT_UInt32 idx = PFR_KERN_INDEX( glyph1, glyph2 ); + + kerning->x = 0; + kerning->y = 0; + + /* find the kerning item containing our pair */ + while ( item ) + { + if ( item->pair1 <= idx && idx <= item->pair2 ) + goto Found_Item; + + item = item->next; + } + + /* not found */ + goto Exit; + + Found_Item: + { + /* perform simply binary search within the item */ + FT_UInt min, mid, max; + FT_Stream stream = face->root.stream; + FT_Byte* p; + + if ( FT_STREAM_SEEK( item->offset ) || + FT_FRAME_ENTER( item->pair_count * item->pair_size ) ) + goto Exit; + + min = 0; + max = item->pair_count; + while ( min < max ) + { + FT_UInt char1, char2, charcode; + + mid = (min + max) >> 1; + p = stream->cursor + mid*item->pair_size; + + if ( item->flags & PFR_KERN_2BYTE_CHAR ) + { + char1 = FT_NEXT_USHORT(p); + char2 = FT_NEXT_USHORT(p); + } + else + { + char1 = FT_NEXT_USHORT(p); + char2 = FT_NEXT_USHORT(p); + } + charcode = PFR_KERN_INDEX(char1,char2); + + if ( idx == charcode ) + { + if ( item->flags & PFR_KERN_2BYTE_ADJ ) + kerning->x = item->base_adj + FT_NEXT_SHORT(p); + else + kerning->x = item->base_adj + FT_NEXT_CHAR(p); + + break; + } + if ( idx > charcode ) + min = mid+1; + else + max = mid; + } + + FT_FRAME_EXIT(); + } + + Exit: + return 0; + } +#endif /* END */ diff --git a/src/pfr/pfrsbit.c b/src/pfr/pfrsbit.c new file mode 100644 index 000000000..63e2a5298 --- /dev/null +++ b/src/pfr/pfrsbit.c @@ -0,0 +1,629 @@ +#include "pfrsbit.h" +#include "pfrload.h" +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H + +#include "pfrerror.h" + +#undef FT_COMPONENT +#define FT_COMPONENT trace_pfr + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** PFR BIT WRITER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct PFR_BitWriter_ + { + FT_Byte* line; /* current line start */ + FT_Int pitch; /* line size in bytes */ + FT_Int width; /* width in pixels/bits */ + FT_Int rows; /* number of remaining rows to scan */ + FT_Int total; /* total number of bits to draw */ + + } PFR_BitWriterRec, *PFR_BitWriter; + + + static void + pfr_bitwriter_init( PFR_BitWriter writer, + FT_Bitmap* target, + FT_Bool decreasing ) + { + writer->line = target->buffer; + writer->pitch = target->pitch; + writer->width = target->width; + writer->rows = target->rows; + writer->total = writer->width * writer->rows; + + if ( !decreasing ) + { + writer->line += writer->pitch * ( target->rows-1 ); + writer->pitch = -writer->pitch; + } + } + + + static void + pfr_bitwriter_decode_bytes( PFR_BitWriter writer, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Int n, reload; + FT_Int left = writer->width; + FT_Byte* cur = writer->line; + FT_UInt mask = 0x80; + FT_UInt val = 0; + FT_UInt c = 0; + + n = (FT_Int)(limit - p)*8; + if ( n > writer->total ) + n = writer->total; + + reload = n & 7; + + for ( ; n > 0; n-- ) + { + if ( (n & 7) == reload ) + val = *p++; + + if ( val & 0x80 ) + c |= mask; + + val <<= 1; + mask >>= 1; + + if ( --left <= 0 ) + { + cur[0] = (FT_Byte) c; + left = writer->width; + mask = 0x80; + + writer->line += writer->pitch; + cur = writer->line; + c = 0; + } + else if ( mask == 0 ) + { + cur[0] = c; + mask = 0x80; + c = 0; + cur ++; + } + } + + if ( mask != 0x80 ) + cur[0] = c; + } + + + static void + pfr_bitwriter_decode_rle1( PFR_BitWriter writer, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Int n, phase, count, counts[2], reload; + FT_Int left = writer->width; + FT_Byte* cur = writer->line; + FT_UInt mask = 0x80; + FT_UInt c = 0; + + n = writer->total; + + phase = 1; + counts[0] = 0; + counts[1] = 0; + count = 0; + reload = 1; + + for ( ; n > 0; n-- ) + { + if ( reload ) + { + do + { + if ( phase ) + { + FT_Int v; + + if ( p >= limit ) + break; + + v = *p++; + counts[0] = (v >> 4); + counts[1] = (v & 15); + phase = 0; + count = counts[0]; + } + else + { + phase = 1; + count = counts[1]; + } + } + while ( count == 0 ); + } + + if ( phase ) + c |= mask; + + mask >>= 1; + + if ( --left <= 0 ) + { + cur[0] = (FT_Byte) c; + left = writer->width; + mask = 0x80; + + writer->line += writer->pitch; + cur = writer->line; + c = 0; + } + else if ( mask == 0 ) + { + cur[0] = c; + mask = 0x80; + c = 0; + cur ++; + } + + reload = ( --count <= 0 ); + } + + if ( mask != 0x80 ) + cur[0] = (FT_Byte) c; + } + + + static void + pfr_bitwriter_decode_rle2( PFR_BitWriter writer, + FT_Byte* p, + FT_Byte* limit ) + { + FT_Int n, phase, count, reload; + FT_Int left = writer->width; + FT_Byte* cur = writer->line; + FT_UInt mask = 0x80; + FT_UInt c = 0; + + n = writer->total; + + phase = 1; + count = 0; + reload = 1; + + for ( ; n > 0; n-- ) + { + if ( reload ) + { + do + { + if ( p >= limit ) + break; + + count = *p++; + phase = phase ^ 1; + } + while ( count == 0 ); + } + + if ( phase ) + c |= mask; + + mask >>= 1; + + if ( --left <= 0 ) + { + cur[0] = (FT_Byte) c; + c = 0; + mask = 0x80; + left = writer->width; + + writer->line += writer->pitch; + cur = writer->line; + } + else if ( mask == 0 ) + { + cur[0] = c; + c = 0; + mask = 0x80; + cur ++; + } + + reload = ( --count <= 0 ); + } + + if ( mask != 0x80 ) + cur[0] = (FT_Byte) c; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BITMAP DATA DECODING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + pfr_lookup_bitmap_data( FT_Byte* base, + FT_Byte* limit, + FT_Int count, + FT_Byte flags, + FT_UInt char_code, + FT_ULong* found_offset, + FT_ULong* found_size ) + { + FT_UInt left, right, char_len; + FT_Bool two = (flags & 1); + FT_Byte* buff; + + char_len = 4; + if ( two ) char_len += 1; + if ( flags & 2) char_len += 1; + if ( flags & 4) char_len += 1; + + left = 0; + right = count; + + while ( left < right ) + { + FT_UInt middle, code; + + middle = (left + right) >> 1; + buff = base + middle*char_len; + + /* check that we're not outside of the table */ + /* this is possible with broken fonts... */ + if ( buff + char_len > limit ) + goto Fail; + + if (two) code = PFR_NEXT_USHORT(buff); + else code = PFR_NEXT_BYTE(buff); + + if ( code == char_code ) + goto Found_It; + + if ( code < char_code ) + left = middle; + else + right = middle; + } + + Fail: + /* Not found */ + *found_size = 0; + *found_offset = 0; + return; + + Found_It: + if (flags & 2) *found_size = PFR_NEXT_USHORT(buff); + else *found_size = PFR_NEXT_BYTE(buff); + + if (flags & 4) *found_offset = PFR_NEXT_ULONG(buff); + else *found_offset = PFR_NEXT_USHORT(buff); + } + + + /* load bitmap metrics. "*padvance" must be set to the default value */ + /* before calling this function... */ + /* */ + static FT_Error + pfr_load_bitmap_metrics( FT_Byte** pdata, + FT_Byte* limit, + FT_Long scaled_advance, + FT_Long *axpos, + FT_Long *aypos, + FT_UInt *axsize, + FT_UInt *aysize, + FT_Long *aadvance, + FT_UInt *aformat ) + { + FT_Error error = 0; + FT_Byte flags; + FT_Char b; + FT_Byte* p = *pdata; + FT_Long xpos, ypos, advance; + FT_UInt xsize, ysize; + + PFR_CHECK(1); + flags = PFR_NEXT_BYTE(p); + + xpos = 0; + ypos = 0; + xsize = 0; + ysize = 0; + advance = 0; + + switch (flags & 3) + { + case 0: + PFR_CHECK(1); + b = PFR_NEXT_INT8(p); + xpos = b >> 4; + ypos = ((FT_Char)(b << 4)) >> 4; + break; + + case 1: + PFR_CHECK(2); + xpos = PFR_NEXT_INT8(p); + ypos = PFR_NEXT_INT8(p); + break; + + case 2: + PFR_CHECK(4); + xpos = PFR_NEXT_SHORT(p); + ypos = PFR_NEXT_SHORT(p); + break; + + case 3: + PFR_CHECK(6); + xpos = PFR_NEXT_LONG(p); + ypos = PFR_NEXT_LONG(p); + break; + + default: + ; + } + + flags >>= 2; + switch (flags & 3) + { + case 0: + /* blank image */ + xsize = 0; + ysize = 0; + break; + + case 1: + PFR_CHECK(1); + b = PFR_NEXT_BYTE(p); + xsize = (b >> 4) & 0xF; + ysize = b & 0xF; + break; + + case 2: + PFR_CHECK(2); + xsize = PFR_NEXT_BYTE(p); + ysize = PFR_NEXT_BYTE(p); + break; + + case 3: + PFR_CHECK(4); + xsize = PFR_NEXT_USHORT(p); + ysize = PFR_NEXT_USHORT(p); + break; + + default: + ; + } + + flags >>= 2; + switch (flags & 3) + { + case 0: + advance = scaled_advance; + break; + + case 1: + PFR_CHECK(1); + advance = PFR_NEXT_INT8(p) << 8; + break; + + case 2: + PFR_CHECK(2); + advance = PFR_NEXT_SHORT(p); + break; + + case 3: + PFR_CHECK(3); + advance = PFR_NEXT_LONG(p); + break; + + default: + ; + } + + *axpos = xpos; + *aypos = ypos; + *axsize = xsize; + *aysize = ysize; + *aadvance = advance; + *aformat = flags >> 2; + *pdata = p; + + Exit: + return error; + + Too_Short: + error = PFR_Err_Invalid_Table; + FT_ERROR(( "pfr_load_bitmap_metrics: invalid glyph data\n" )); + goto Exit; + } + + + static FT_Error + pfr_load_bitmap_bits( FT_Byte* p, + FT_Byte* limit, + FT_UInt format, + FT_UInt decreasing, + FT_Bitmap* target ) + { + FT_Error error = 0; + PFR_BitWriterRec writer; + + if ( target->rows > 0 && target->width > 0 ) + { + pfr_bitwriter_init( &writer, target, decreasing ); + + switch (format) + { + case 0: /* packed bits */ + pfr_bitwriter_decode_bytes( &writer, p, limit ); + break; + + case 1: /* RLE1 */ + pfr_bitwriter_decode_rle1( &writer, p, limit ); + break; + + case 2: /* RLE2 */ + pfr_bitwriter_decode_rle2( &writer, p, limit ); + break; + + default: + FT_ERROR(( "pfr_read_bitmap_data: invalid image type\n" )); + error = FT_Err_Invalid_File_Format; + } + } + + return error; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BITMAP LOADING *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + FT_LOCAL( FT_Error ) + pfr_slot_load_bitmap( PFR_Slot glyph, + PFR_Size size, + FT_UInt glyph_index ) + { + FT_Error error; + PFR_Face face = (PFR_Face) glyph->root.face; + FT_Stream stream = face->root.stream; + PFR_PhyFont phys = &face->phy_font; + FT_ULong gps_offset; + FT_ULong gps_size; + PFR_Char character; + PFR_Strike strike; + + character = &phys->chars[glyph_index]; + + /* Look-up a bitmap strike corresponding to the current */ + /* character dimensions */ + + { + FT_UInt n; + + strike = phys->strikes; + for ( n = 0; n < phys->num_strikes; n++ ) + { + if ( strike->x_ppm == (FT_UInt) size->root.metrics.x_ppem && + strike->y_ppm == (FT_UInt) size->root.metrics.y_ppem ) + { + goto Found_Strike; + } + strike++; + } + + /* couldn't find it */ + return FT_Err_Invalid_Argument; + } + + Found_Strike: + + /* Now lookup the glyph's position within the file */ + { + FT_UInt char_len; + + char_len = 4; + if ( strike->flags & 1 ) char_len += 1; + if ( strike->flags & 2 ) char_len += 1; + if ( strike->flags & 4 ) char_len += 1; + + /* Access data directly in the frame to speed lookups */ + if ( FT_STREAM_SEEK( phys->bct_offset + strike->bct_offset ) || + FT_FRAME_ENTER( char_len * strike->num_bitmaps ) ) + goto Exit; + + pfr_lookup_bitmap_data( stream->cursor, + stream->limit, + strike->num_bitmaps, + strike->flags, + character->char_code, + &gps_offset, + &gps_size ); + + FT_FRAME_EXIT(); + + if (gps_size == 0) + { + /* Could not find a bitmap program string for this glyph */ + error = FT_Err_Invalid_Argument; + goto Exit; + } + } + + /* get the bitmap metrics */ + { + FT_Long xpos, ypos, advance; + FT_UInt xsize, ysize, format; + FT_Byte* p; + + advance = FT_MulDiv( size->root.metrics.x_ppem << 8, + character->advance, + phys->metrics_resolution ); + + /* XXX: handle linearHoriAdvance correctly !! */ + + if ( FT_STREAM_SEEK( face->header.gps_section_offset + gps_offset ) || + FT_FRAME_ENTER( gps_size ) ) + goto Exit; + + p = stream->cursor; + error = pfr_load_bitmap_metrics( &p, stream->limit, + advance, + &xpos, &ypos, + &xsize, &ysize, + &advance, &format ); + if ( !error ) + { + glyph->root.format = FT_GLYPH_FORMAT_BITMAP; + + /* Set up glyph bitmap and metrics */ + glyph->root.bitmap.width = (FT_Int) xsize; + glyph->root.bitmap.rows = (FT_Int) ysize; + glyph->root.bitmap.pitch = (FT_Long)(xsize+7) >> 3; + glyph->root.bitmap.pixel_mode = FT_PIXEL_MODE_MONO; + + glyph->root.metrics.width = (FT_Long)xsize << 6; + glyph->root.metrics.height = (FT_Long)ysize << 6; + glyph->root.metrics.horiBearingX = xpos << 6; + glyph->root.metrics.horiBearingY = ypos << 6; + glyph->root.metrics.horiAdvance = ((advance >> 2) + 32) & -64; + glyph->root.metrics.vertBearingX = - glyph->root.metrics.width >> 1; + glyph->root.metrics.vertBearingY = 0; + glyph->root.metrics.vertAdvance = size->root.metrics.height; + + glyph->root.bitmap_left = xpos; + glyph->root.bitmap_top = ypos + ysize; + + /* Allocate and read bitmap data */ + { + FT_Memory memory = face->root.memory; + FT_Long len = glyph->root.bitmap.pitch*ysize; + + if ( !FT_ALLOC( glyph->root.bitmap.buffer, len ) ) + { + error = pfr_load_bitmap_bits( p, + stream->limit, + format, + (face->header.color_flags & 2), + &glyph->root.bitmap ); + } + } + } + FT_FRAME_EXIT(); + } + + Exit: + return error; + } diff --git a/src/pfr/pfrsbit.h b/src/pfr/pfrsbit.h new file mode 100644 index 000000000..49baa82ee --- /dev/null +++ b/src/pfr/pfrsbit.h @@ -0,0 +1,32 @@ +/***************************************************************************/ +/* */ +/* pfrsbit.c */ +/* */ +/* FreeType PFR bitmap loader */ +/* */ +/* Copyright 2002 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef __PFRSBIT_H__ +#define __PFRSBIT_H__ + +#include "pfrobjs.h" + +FT_BEGIN_HEADER + + FT_LOCAL( FT_Error ) + pfr_slot_load_bitmap( PFR_Slot glyph, + PFR_Size size, + FT_UInt glyph_index ); + +FT_END_HEADER + +#endif /* __PFR_SBIT_H__ */ diff --git a/src/pfr/pfrtypes.h b/src/pfr/pfrtypes.h index b3cd4dfb4..d76959861 100644 --- a/src/pfr/pfrtypes.h +++ b/src/pfr/pfrtypes.h @@ -52,19 +52,19 @@ FT_BEGIN_HEADER FT_UInt max_blue_values; FT_UInt max_x_orus; FT_UInt max_y_orus; - + FT_UInt phy_font_max_size_high; FT_UInt color_flags; - + FT_UInt32 bct_max_size; - FT_UInt32 bct_set_max_size; + FT_UInt32 bct_set_max_size; FT_UInt32 phy_bct_set_max_size; - + FT_UInt num_phy_fonts; FT_UInt max_vert_stem_snap; FT_UInt max_horz_stem_snap; FT_UInt max_chars; - + } PFR_HeaderRec, *PFR_Header; @@ -73,7 +73,7 @@ FT_BEGIN_HEADER { PFR_FLAG_BLACK_PIXEL = 1, PFR_FLAG_INVERT_BITMAP = 2 - + } PFR_HeaderFlags; @@ -83,16 +83,16 @@ FT_BEGIN_HEADER { FT_UInt32 size; FT_UInt32 offset; - + FT_Int32 matrix[4]; FT_UInt stroke_flags; FT_Int stroke_thickness; FT_Int bold_thickness; FT_Int32 miter_limit; - + FT_UInt32 phys_size; FT_UInt32 phys_offset; - + } PFR_LogFontRec, *PFR_LogFont; @@ -104,7 +104,7 @@ FT_BEGIN_HEADER PFR_LOG_2BYTE_STROKE = 8, PFR_LOG_STROKE = 4, PFR_LINE_JOIN_MASK = 3 - + } PFR_LogFlags; @@ -113,7 +113,7 @@ FT_BEGIN_HEADER PFR_LINE_JOIN_MITER = 0, PFR_LINE_JOIN_ROUND = 1, PFR_LINE_JOIN_BEVEL = 2 - + } PFR_LineJoinFlags; @@ -124,7 +124,7 @@ FT_BEGIN_HEADER PFR_BITMAP_3BYTE_OFFSET = 4, PFR_BITMAP_2BYTE_SIZE = 2, PFR_BITMAP_2BYTE_CHARCODE = 1 - + } PFR_BitmapFlags; @@ -133,7 +133,7 @@ FT_BEGIN_HEADER FT_UInt char_code; FT_UInt gps_size; FT_UInt32 gps_offset; - + } PFR_BitmapCharRec, *PFR_BitmapChar; @@ -153,17 +153,17 @@ FT_BEGIN_HEADER FT_UInt x_ppm; FT_UInt y_ppm; FT_UInt flags; - + FT_UInt32 gps_size; FT_UInt32 gps_offset; - + FT_UInt32 bct_size; FT_UInt32 bct_offset; - + /* optional */ FT_UInt num_bitmaps; PFR_BitmapChar bitmaps; - + } PFR_StrikeRec, *PFR_Strike; @@ -175,7 +175,7 @@ FT_BEGIN_HEADER FT_Int advance; FT_UInt gps_size; FT_UInt32 gps_offset; - + } PFR_CharRec, *PFR_Char; @@ -186,11 +186,27 @@ FT_BEGIN_HEADER FT_UInt standard; FT_UInt num_stem_snaps; FT_Int* stem_snaps; - + } PFR_DimensionRec, *PFR_Dimension; /************************************************************************/ + typedef struct PFR_KernItemRec_* PFR_KernItem; + typedef struct PFR_KernItemRec_ + { + PFR_KernItem next; + FT_UInt pair_count; + FT_UInt pair_size; + FT_Int base_adj; + FT_UInt flags; + FT_UInt32 offset; + FT_UInt32 pair1; + FT_UInt32 pair2; + + } PFR_KernItemRec; + +#define PFR_KERN_INDEX( g1, g2 ) ( ( (FT_UInt32)(g1) << 16 ) | (FT_UInt16)(g2) ) + typedef struct PFR_KernPairRec_ { FT_UInt glyph1; @@ -212,7 +228,7 @@ FT_BEGIN_HEADER FT_BBox bbox; FT_UInt flags; FT_UInt standard_advance; - + PFR_DimensionRec horizontal; PFR_DimensionRec vertical; @@ -221,18 +237,23 @@ FT_BEGIN_HEADER FT_UInt num_strikes; FT_UInt max_strikes; PFR_StrikeRec* strikes; - + FT_UInt num_blue_values; FT_Int *blue_values; FT_UInt blue_fuzz; FT_UInt blue_scale; - + FT_UInt num_chars; FT_UInt32 chars_offset; PFR_Char chars; FT_UInt num_kern_pairs; - PFR_KernPairRec *kern_pairs; + PFR_KernItem kern_items; + PFR_KernItem* kern_items_tail; + + /* not part of the spec, but used during load */ + FT_UInt32 bct_offset; + FT_Byte* cursor; } PFR_PhyFontRec, *PFR_PhyFont; @@ -254,7 +275,7 @@ FT_BEGIN_HEADER { PFR_KERN_2BYTE_ADJ = 0x01, PFR_KERN_2BYTE_CHAR = 0x02 - + } PFR_KernFlags; @@ -267,7 +288,7 @@ FT_BEGIN_HEADER PFR_GLYPH_1BYTE_XYCOUNT = 0x04, PFR_GLYPH_XCOUNT = 0x02, PFR_GLYPH_YCOUNT = 0x01 - + } PFR_GlyphFlags; @@ -276,7 +297,7 @@ FT_BEGIN_HEADER { FT_UInt org; FT_UInt cur; - + } PFR_CoordRec, *PFR_Coord; @@ -288,7 +309,7 @@ FT_BEGIN_HEADER FT_Int y_delta; FT_UInt32 gps_offset; FT_UInt gps_size; - + } PFR_SubGlyphRec, *PFR_SubGlyph; @@ -298,14 +319,14 @@ FT_BEGIN_HEADER PFR_SUBGLYPH_2BYTE_SIZE = 0x40, PFR_SUBGLYPH_YSCALE = 0x20, PFR_SUBGLYPH_XSCALE = 0x10 - + } PFR_SubGlyphFlags; typedef struct PFR_GlyphRec_ { FT_Byte format; - + FT_UInt num_x_control; FT_UInt num_y_control; FT_UInt max_xy_control; @@ -316,10 +337,10 @@ FT_BEGIN_HEADER FT_UInt num_subs; FT_UInt max_subs; PFR_SubGlyphRec* subs; - + FT_GlyphLoader loader; FT_Bool path_begun; - + } PFR_GlyphRec, *PFR_Glyph; diff --git a/src/sfnt/ttcmap0.c b/src/sfnt/ttcmap0.c index 5a024ec8d..136a6a1a7 100644 --- a/src/sfnt/ttcmap0.c +++ b/src/sfnt/ttcmap0.c @@ -597,10 +597,19 @@ FT_Byte *ends, *starts, *offsets, *deltas, *glyph_ids; FT_UInt num_segs; - - if ( table + length > valid->limit || length < 16 ) + /* in certain fonts, the 'length' field is invalid and goes */ + /* out of bound. We try to correct this here... */ + if ( length < 16 ) FT_INVALID_TOO_SHORT; + if ( table + length > valid->limit ) + { + if ( valid->level >= FT_VALIDATE_TIGHT ) + FT_INVALID_TOO_SHORT; + + length = (FT_UInt)( valid->limit - table ); + } + p = table + 6; num_segs = TT_NEXT_USHORT( p ); /* read segCountX2 */ diff --git a/src/tools/docmaker/content.py b/src/tools/docmaker/content.py index 52f7c1153..6d33b5db2 100644 --- a/src/tools/docmaker/content.py +++ b/src/tools/docmaker/content.py @@ -132,7 +132,7 @@ class DocField: m = re_code_end.match( l ) if m and len(m.group(1)) <= margin: # that's it, we finised the code sequence - code = DocCode( margin, cur_lines ) + code = DocCode( 0, cur_lines ) self.items.append( code ) margin = -1 cur_lines = [] @@ -289,9 +289,9 @@ class DocSection: self.description = block.get_markup_items( "description" ) self.order = block.get_markup_words( "order" ) return - + def reorder( self ): - + self.block_names = sort_order_list( self.block_names, self.order ) @@ -300,7 +300,7 @@ class ContentProcessor: def __init__( self ): """initialize a block content processor""" self.reset() - + self.sections = {} # dictionary of documentation sections self.section = None # current documentation section @@ -379,7 +379,7 @@ class ContentProcessor: blocks = source_processor.blocks count = len(blocks) for n in range(count): - + source = blocks[n] if source.content: # this is a documentation comment, we need to catch @@ -392,8 +392,8 @@ class ContentProcessor: m = m+1 doc_block = DocBlock( source, follow, self ) - - + + def finish( self ): # process all sections to extract their abstract, description @@ -430,13 +430,13 @@ class ContentProcessor: chap = DocChapter( None ) chap.sections = others self.chapters.append( chap ) - + class DocBlock: def __init__( self, source, follow, processor ): - + processor.reset() self.source = source @@ -451,8 +451,8 @@ class DocBlock: self.type = self.markups[0].tag except: pass - - + + # compute block name from first markup paragraph try: markup = self.markups[0] @@ -492,7 +492,7 @@ class DocBlock: # now strip the leading and trailing empty lines from the sources start = 0 end = len( source )-1 - + while start < end and not string.strip( source[start] ): start = start + 1 diff --git a/src/tools/docmaker/sources.py b/src/tools/docmaker/sources.py index 6961cd979..9828aa642 100644 --- a/src/tools/docmaker/sources.py +++ b/src/tools/docmaker/sources.py @@ -10,7 +10,7 @@ # "", "", etc.. # # the routines used to process the content of documentation blocks -# are not contained here, but in "doccontent.py" +# are not contained here, but in "content.py" # # the classes and methods found here only deal with text parsing # and basic documentation block extraction @@ -76,11 +76,11 @@ re_source_block_format1 = SourceBlockFormat( 1, start, column, start ) # # format 2 documentation comment blocks look like the following: # -# /************************************ +# /************************************ (at least 2 asterisks) +# * +# * +# * # * -# * -# * -# * # **/ (1 or more asterisks at the end) # # we define a few regular expressions here to detect them @@ -94,12 +94,12 @@ start = r''' column = r''' \s* # any number of whitespace \*{1} # followed by precisely one asterisk - (.*) # followed by anything (group1) + (.*) # then anything (group1) ''' end = r''' \s* # any number of whitespace - \*+/ # followed by at least on asterisk, then '/' + \*+/ # followed by at least one asterisk, then '/' ''' re_source_block_format2 = SourceBlockFormat( 2, start, column, end ) @@ -151,7 +151,7 @@ re_source_crossref = re.compile( r'(\W*)(\w*)' ) # # a list of reserved source keywords # -re_source_keywords = re.compile( '''( typedef | +re_source_keywords = re.compile( '''( typedef | struct | enum | union | @@ -232,18 +232,18 @@ class SourceBlock: # debugging only - not used in normal operations def dump( self ): - + if self.content: print "{{{content start---" for l in self.content: print l print "---content end}}}" return - + fmt = "" if self.format: fmt = repr(self.format.id) + " " - + for line in self.lines: print line @@ -281,22 +281,22 @@ class SourceProcessor: def parse_file( self, filename ): """parse a C source file, and adds its blocks to the processor's list""" - + self.reset() - + self.filename = filename - + fileinput.close() self.format = None self.lineno = 0 self.lines = [] for line in fileinput.input( filename ): - + # strip trailing newlines, important on Windows machines !! if line[-1] == '\012': line = line[0:-1] - + if self.format == None: self.process_normal_line( line ) @@ -304,25 +304,25 @@ class SourceProcessor: if self.format.end.match( line ): # that's a normal block end, add it to lines and # create a new block - self.lines.append( line ) + # self.lines.append( line ) self.add_block_lines() - + elif self.format.column.match( line ): # that's a normal column line, add it to 'lines' self.lines.append( line ) - + else: # humm.. this is an unexcepted block end, # create a new block, but don't process the line self.add_block_lines() - + # we need to process the line again self.process_normal_line( line ) - + # record the last lines self.add_block_lines() - + def process_normal_line( self, line ): """process a normal line and check if it's the start of a new block""" @@ -334,18 +334,18 @@ class SourceProcessor: self.lines.append( line ) - + def add_block_lines( self ): """add the current accumulated lines, and create a new block""" if self.lines != []: block = SourceBlock( self, self.filename, self.lineno, self.lines ) - + self.blocks.append( block ) self.format = None self.lines = [] - + # debugging only, not used in normal operations def dump( self ): """print all blocks in a processor""" diff --git a/src/tools/docmaker/tohtml.py b/src/tools/docmaker/tohtml.py index 414badc6f..75e5db068 100644 --- a/src/tools/docmaker/tohtml.py +++ b/src/tools/docmaker/tohtml.py @@ -273,29 +273,10 @@ class HtmlFormatter(Formatter): def print_html_field_list( self, fields ): print "" - - # compute the maximum length of each field name - # if it is - # - max = 0 for field in fields: - l = len( field.name ) - if l > max: - max = l - - head = "" - if max > 18: - head = "" - - for field in fields: - print head + field.name + inter + print "" print "
" - inter = "" - foot = "
" - inter = "
" - foot = "

" + field.name + "" self.print_html_items( field.items ) - print foot - + print "
" @@ -338,7 +319,7 @@ class HtmlFormatter(Formatter): count = len( self.block_index ) rows = (count + self.columns - 1)/self.columns - print "
" + print "
" for r in range(rows): line = "" for c in range(self.columns): @@ -416,9 +397,9 @@ class HtmlFormatter(Formatter): # print section synopsys print section_synopsis_header - print "
" + print "
" - maxwidth = 1 + maxwidth = 0 for b in section.blocks.values(): if len(b.name) > maxwidth: maxwidth = len(b.name)