From: kramm Date: Fri, 23 Nov 2001 15:58:13 +0000 (+0000) Subject: initial revision X-Git-Tag: release-0-2-1~113 X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=commitdiff_plain;h=92c5f229fdf4d1a1d1efd8205dae033e70d07a1a initial revision --- diff --git a/pdf2swf/ttf2pt1/COPYRIGHT b/pdf2swf/ttf2pt1/COPYRIGHT new file mode 100644 index 0000000..6e130fb --- /dev/null +++ b/pdf2swf/ttf2pt1/COPYRIGHT @@ -0,0 +1,83 @@ +The following copyright notice applies to all the files provided +in this distribution unless explicitly noted otherwise +(the most notable exception being t1asm.c). + + Copyright (c) 1997-2001 by the AUTHORS: + Andrew Weeks + Frank M. Siegert + Mark Heath + Thomas Henlich + Sergey Babkin , + Turgut Uyar + Rihardas Hepas + Szalay Tamas + Johan Vromans + Petr Titera + Lei Wang + Chen Xiangyang + Zvezdan Petkovic + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the TTF2PT1 Project + and its contributors. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +For the approximate list of the AUTHORS' responsibilities see the +project history. + +Other contributions to the project are: + +Turgut Uyar + The Unicode translation table for the Turkish language. + +Rihardas Hepas + The Unicode translation table for the Baltic languages. + +Szalay Tamas + The Unicode translation table for the Central European languages. + +Johan Vromans + The RPM file. + +Petr Titera + The Unicode map format with names, the forced Unicode option. + +Frank M. Siegert + Port to Windows + +Lei Wang +Chen Xiangyang + Translation maps for Chinese fonts. + +Zvezdan Petkovic + The Unicode translation tables for the Cyrillic alphabet. + +I. Lee Hetherington + The Type1 assembler (from the package 't1utils'), its full copyright + notice: + Copyright (c) 1992 by I. Lee Hetherington, all rights reserved. + Permission is hereby granted to use, modify, and distribute this program + for any purpose provided this copyright notice and the one below remain + intact. + diff --git a/pdf2swf/ttf2pt1/Makefile.in b/pdf2swf/ttf2pt1/Makefile.in new file mode 100644 index 0000000..42343df --- /dev/null +++ b/pdf2swf/ttf2pt1/Makefile.in @@ -0,0 +1,7 @@ +all: + gcc -c ft.c + gcc -c ttf.c + gcc -c pt1.c + gcc -c ttf2pt1.c + gcc -c t1asm.c + ar cru ttf2pt1.a ft.o ttf.o pt1.o ttf2pt1.o t1asm.o diff --git a/pdf2swf/ttf2pt1/byteorder.h b/pdf2swf/ttf2pt1/byteorder.h new file mode 100644 index 0000000..c139817 --- /dev/null +++ b/pdf2swf/ttf2pt1/byteorder.h @@ -0,0 +1,24 @@ +/* + * see COPYRIGHT + */ + +/* This defines the macroes ntohs and ntohl, which convert short and long + ints from network order (used on 68000 chips, and in TrueType font + files) to whatever order your computer uses. #define _BIG_ENDIAN or not + to control which set of definitions apply. If you don't know, try both. If + you have a peculiar machine you're on your own. +*/ + +#if defined(_BIG_ENDIAN) +#define ntohl(x) (x) +#define ntohs(x) (x) +#else +#define ntohs(x) \ + ((USHORT)((((USHORT)(x) & 0x00ff) << 8) | \ + (((USHORT)(x) & 0xff00) >> 8))) +#define ntohl(x) \ + ((ULONG)((((ULONG)(x) & 0x000000ffU) << 24) | \ + (((ULONG)(x) & 0x0000ff00U) << 8) | \ + (((ULONG)(x) & 0x00ff0000U) >> 8) | \ + (((ULONG)(x) & 0xff000000U) >> 24))) +#endif diff --git a/pdf2swf/ttf2pt1/ft.c b/pdf2swf/ttf2pt1/ft.c new file mode 100644 index 0000000..9b65785 --- /dev/null +++ b/pdf2swf/ttf2pt1/ft.c @@ -0,0 +1,636 @@ +/* + * The font parser using the FreeType library version 2. + * + * see COPYRIGHT + * + */ + +#ifdef USE_FREETYPE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pt1.h" +#include "global.h" + +/* prototypes of call entries */ +static void openfont(char *fname, char *arg); +static void closefont( void); +static int getnglyphs ( void); +static int glnames( GLYPH *glyph_list); +static void glmetrics( GLYPH *glyph_list); +static int glenc( GLYPH *glyph_list, int *encoding, int *unimap); +static void fnmetrics( struct font_metrics *fm); +static void glpath( int glyphno, GLYPH *glyph_list); +static void kerning( GLYPH *glyph_list); + +/* globals */ + +/* front-end descriptor */ +struct frontsw freetype_sw = { + /*name*/ "ft", + /*descr*/ "based on the FreeType library", + /*suffix*/ { "ttf", "otf", "pfa", "pfb" }, + /*open*/ openfont, + /*close*/ closefont, + /*nglyphs*/ getnglyphs, + /*glnames*/ glnames, + /*glmetrics*/ glmetrics, + /*glenc*/ glenc, + /*fnmetrics*/ fnmetrics, + /*glpath*/ glpath, + /*kerning*/ kerning, +}; + +/* statics */ + +static char * dupcnstring( char *s, int len); + +static FT_Library library; +static FT_Face face; + +static int enc_type, enc_found; + +/* SFNT functions do not seem to be included by default in FT2beta8 */ +#define ENABLE_SFNT + +/* + * Open font and prepare to return information to the main driver. + * May print error and warning messages. + * Exit on error. + */ + +static void +openfont( + char *fname, + char *arg /* unused now */ +) +{ + FT_Error error; + + if( FT_Init_FreeType( &library ) ) { + fprintf(stderr, "** FreeType initialization failed\n"); + exit(1); + } + + if( error = FT_New_Face( library, fname, 0, &face ) ) { + if ( error == FT_Err_Unknown_File_Format ) + fprintf(stderr, "**** %s has format unknown to FreeType\n", fname); + else + fprintf(stderr, "**** Cannot access %s ****\n", fname); + exit(1); + } + + if(FT_HAS_FIXED_SIZES(face)) { + WARNING_1 fprintf(stderr, "Font contains bitmaps\n"); + } + if(FT_HAS_MULTIPLE_MASTERS(face)) { + WARNING_1 fprintf(stderr, "Font contains multiple masters, using default\n"); + } + + if(ISDBG(FT)) fprintf(stderr," %d units per EM\n", face->units_per_EM); + + enc_found = 0; +} + +/* + * Close font. + * Exit on error. + */ + +static void +closefont( + void +) +{ + if( FT_Done_Face(face) ) { + WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n"); + } + if( FT_Done_FreeType(library) ) { + WARNING_1 fprintf(stderr, "Errors when stopping FreeType, ignored\n"); + } +} + +/* + * Get the number of glyphs in font. + */ + +static int +getnglyphs ( + void +) +{ + if(ISDBG(FT)) fprintf(stderr, "%d glyphs in font\n", face->num_glyphs); + return (int)face->num_glyphs; +} + +/* + * Get the names of the glyphs. + * Returns 0 if the names were assigned, non-zero if the font + * provides no glyph names. + */ + +static int +glnames( + GLYPH *glyph_list +) +{ +#define MAX_NAMELEN 1024 + unsigned char bf[1024]; + int i; + + if( ! FT_HAS_GLYPH_NAMES(face) ) { + WARNING_1 fprintf(stderr, "Font has no glyph names\n"); + return 1; + } + + for(i=0; i < face->num_glyphs; i++) { + if( FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0 ) { + sprintf(bf, "_%d", i); + WARNING_2 fprintf(stderr, + "**** Glyph No. %d has no postscript name, becomes %s ****\n", + i, bf); + } + glyph_list[i].name = strdup(bf); + if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, bf); + if (glyph_list[i].name == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + } + return 0; +} + +/* + * Get the metrics of the glyphs. + */ + +static void +glmetrics( + GLYPH *glyph_list +) +{ + GLYPH *g; + int i; + FT_Glyph_Metrics *met; + FT_BBox bbox; + FT_Glyph gly; + + for(i=0; i < face->num_glyphs; i++) { + g = &(glyph_list[i]); + + if( FT_Load_Glyph(face, i, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE) ) { + fprintf(stderr, "Can't load glyph %s, skipped\n", g->name); + continue; + } + + met = &face->glyph->metrics; + + if(FT_HAS_HORIZONTAL(face)) { + g->width = met->horiAdvance; + g->lsb = met->horiBearingX; + } else { + WARNING_2 fprintf(stderr, "Glyph %s has no horizontal metrics, guessed them\n", g->name); + g->width = met->width; + g->lsb = 0; + } + + if( FT_Get_Glyph(face->glyph, &gly) ) { + fprintf(stderr, "Can't access glyph %s bbox, skipped\n", g->name); + continue; + } + + FT_Glyph_Get_CBox(gly, ft_glyph_bbox_unscaled, &bbox); + g->xMin = bbox.xMin; + g->yMin = bbox.yMin; + g->xMax = bbox.xMax; + g->yMax = bbox.yMax; + + g->ttf_pathlen = face->glyph->outline.n_points; + } +} + +/* + * Get the original encoding of the font. + * Returns 1 for if the original encoding is Unicode, 2 if the + * original encoding is other 16-bit, 0 if 8-bit. + */ + +static int +glenc( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + int i, e; + unsigned code; + + if(ISDBG(FT)) + for(e=0; e < face->num_charmaps; e++) { + fprintf(stderr, "found encoding pid=%d eid=%d\n", + face->charmaps[e]->platform_id, + face->charmaps[e]->encoding_id); + } + + if(enc_found) + goto populate_map; + + enc_type = 0; + + /* first check for an explicit PID/EID */ + + if(force_pid != -1) { + for(e=0; e < face->num_charmaps; e++) { + if(face->charmaps[e]->platform_id == force_pid + && face->charmaps[e]->encoding_id == force_eid) { + WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n", + force_pid, force_eid); + if( FT_Set_Charmap(face, face->charmaps[e]) ) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + exit(1); + } + enc_type = 1; + goto populate_map; + } + } + fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n", + force_pid, force_eid); + exit(1); + } + + /* next check for a direct Adobe mapping */ + + if(!forcemap) { + for(e=0; e < face->num_charmaps; e++) { + if(face->charmaps[e]->encoding == ft_encoding_adobe_custom) { + WARNING_1 fputs("Found Adobe Custom Encoding\n", stderr); + if( FT_Set_Charmap(face, face->charmaps[e]) ) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + exit(1); + } + goto populate_map; + } + } + } + + for(e=0; e < face->num_charmaps; e++) { + if(face->charmaps[e]->platform_id == 3) { + switch(face->charmaps[e]->encoding_id) { + case 0: + WARNING_1 fputs("Found Symbol Encoding\n", stderr); + break; + case 1: + WARNING_1 fputs("Found Unicode Encoding\n", stderr); + enc_type = 1; + break; + default: + WARNING_1 { + fprintf(stderr, + "****MS Encoding ID %d not supported****\n", + face->charmaps[e]->encoding_id); + fputs("Treating it like Symbol encoding\n", stderr); + } + break; + } + break; + } + } + if(e >= face->num_charmaps) { + WARNING_1 fputs("No Microsoft encoding, using first encoding available\n", stderr); + e = 0; + } + + if( FT_Set_Charmap(face, face->charmaps[e]) ) { + fprintf(stderr, "**** Cannot set charmap in FreeType ****\n"); + exit(1); + } + +populate_map: + enc_found = 1; + for(i=0; iitalic_angle = 0.0; /* FreeType hides the angle */ + fm->underline_position = face->underline_position; + fm->underline_thickness = face->underline_thickness; + fm->is_fixed_pitch = FT_IS_FIXED_WIDTH(face); + + fm->ascender = face->ascender; + fm->descender = face->descender; + + fm->units_per_em = face->units_per_EM; + + fm->bbox[0] = face->bbox.xMin; + fm->bbox[1] = face->bbox.yMin; + fm->bbox[2] = face->bbox.xMax; + fm->bbox[3] = face->bbox.yMax; + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_COPYRIGHT, &sn) ) +#endif /* ENABLE_SFNT */ + fm->name_copyright = ""; +#ifdef ENABLE_SFNT + else + fm->name_copyright = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + + fm->name_family = face->family_name; + + fm->name_style = face->style_name; + if(fm->name_style == NULL) + fm->name_style = ""; + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_FULL_NAME, &sn) ) +#endif /* ENABLE_SFNT */ + { + int len; + + len = strlen(fm->name_family) + strlen(fm->name_style) + 2; + if(( fm->name_full = malloc(len) )==NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + strcpy(fm->name_full, fm->name_family); + if(strlen(fm->name_style) != 0) { + strcat(fm->name_full, " "); + strcat(fm->name_full, fm->name_style); + } + } +#ifdef ENABLE_SFNT + else + fm->name_full = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_VERSION_STRING, &sn) ) +#endif /* ENABLE_SFNT */ + fm->name_version = "1.0"; +#ifdef ENABLE_SFNT + else + fm->name_version = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + +#ifdef ENABLE_SFNT + if( FT_Get_Sfnt_Name(face, TT_NAME_ID_PS_NAME , &sn) ) { +#endif /* ENABLE_SFNT */ + if(( fm->name_ps = strdup(fm->name_full) )==NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } +#ifdef ENABLE_SFNT + } else + fm->name_ps = dupcnstring(sn.string, sn.string_len); +#endif /* ENABLE_SFNT */ + + /* guess the boldness from the font names */ + fm->force_bold=0; + + fieldstocheck[0] = fm->name_style; + fieldstocheck[1] = fm->name_full; + fieldstocheck[2] = fm->name_ps; + + for(i=0; !fm->force_bold && iforce_bold=1; + break; + } + } + } +} + +/* + * Functions to decompose the outlines + */ + +static GLYPH *curg; +static double lastx, lasty; + +static int +outl_moveto( + FT_Vector *to, + void *unused +) +{ + double tox, toy; + + tox = fscale((double)to->x); toy = fscale((double)to->y); + + /* FreeType does not do explicit closepath() */ + if(curg->lastentry) { + g_closepath(curg); + } + fg_rmoveto(curg, tox, toy); + lastx = tox; lasty = toy; + + return 0; +} + +static int +outl_lineto( + FT_Vector *to, + void *unused +) +{ + double tox, toy; + + tox = fscale((double)to->x); toy = fscale((double)to->y); + + fg_rlineto(curg, tox, toy); + lastx = tox; lasty = toy; + + return 0; +} + +static int +outl_conicto( + FT_Vector *control1, + FT_Vector *to, + void *unused +) +{ + double c1x, c1y, tox, toy; + + c1x = fscale((double)control1->x); c1y = fscale((double)control1->y); + tox = fscale((double)to->x); toy = fscale((double)to->y); + + fg_rrcurveto(curg, + (lastx + 2.0 * c1x) / 3.0, (lasty + 2.0 * c1y) / 3.0, + (2.0 * c1x + tox) / 3.0, (2.0 * c1y + toy) / 3.0, + tox, toy ); + lastx = tox; lasty = toy; + + return 0; +} + +static int +outl_cubicto( + FT_Vector *control1, + FT_Vector *control2, + FT_Vector *to, + void *unused +) +{ + double c1x, c1y, c2x, c2y, tox, toy; + + c1x = fscale((double)control1->x); c1y = fscale((double)control1->y); + c2x = fscale((double)control2->x); c2y = fscale((double)control2->y); + tox = fscale((double)to->x); toy = fscale((double)to->y); + + fg_rrcurveto(curg, c1x, c1y, c2x, c2y, tox, toy); + lastx = tox; lasty = toy; + + return 0; +} + +static FT_Outline_Funcs ft_outl_funcs = { + outl_moveto, + outl_lineto, + outl_conicto, + outl_cubicto, + 0, + 0 +}; + +/* + * Get the path of contrours for a glyph. + */ + +static void +glpath( + int glyphno, + GLYPH *glyf_list +) +{ + FT_Outline *ol; + + curg = &glyf_list[glyphno]; + + if( FT_Load_Glyph(face, glyphno, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE|FT_LOAD_NO_HINTING) + || face->glyph->format != ft_glyph_format_outline ) { + fprintf(stderr, "Can't load glyph %s, skipped\n", curg->name); + return; + } + + ol = &face->glyph->outline; + lastx = 0.0; lasty = 0.0; + + if( FT_Outline_Decompose(ol, &ft_outl_funcs, NULL) ) { + fprintf(stderr, "Can't decompose outline of glyph %s, skipped\n", curg->name); + return; + } + + /* FreeType does not do explicit closepath() */ + if(curg->lastentry) { + g_closepath(curg); + } + + if(ol->flags & ft_outline_reverse_fill) { + assertpath(curg->entries, __FILE__, __LINE__, curg->name); + reversepaths(curg); + } +} + +/* + * Get the kerning data. + */ + +static void +kerning( + GLYPH *glyph_list +) +{ + int i, j, n; + int nglyphs = face->num_glyphs; + FT_Vector k; + GLYPH *gl; + + if( nglyphs == 0 || !FT_HAS_KERNING(face) ) { + WARNING_1 fputs("No Kerning data\n", stderr); + return; + } + + for(i=0; i= 1) +#define WARNING_2 if(warnlevel >= 2) +#define WARNING_3 if(warnlevel >= 3) +#define WARNING_4 if(warnlevel >= 4) + +/* + * Bitmap control macros + */ + +#define BITMAP_BYTES(size) (((size)+7)>>3) +#define DEF_BITMAP(name, size) unsigned char name[BITMAP_BYTES(size)] +#define SET_BITMAP(name, bit) ( name[(bit)>>3] |= (1<<((bit)&7)) ) +#define CLR_BITMAP(name, bit) ( name[(bit)>>3] &= ~(1<<((bit)&7)) ) +#define IS_BITMAP(name, bit) ( name[(bit)>>3] & (1<<((bit)&7)) ) + +/* debugging */ + +/* debug flags */ +#define DEBUG_UNICODE 0x00000001 /* unicode to 8-bit code conversion */ +#define DEBUG_MAINSTEMS 0x00000002 /* glyph-wide main stem generation */ +#define DEBUG_SUBSTEMS 0x00000004 /* substituted stem generation */ +#define DEBUG_STEMS (DEBUG_MAINSTEMS|DEBUG_SUBSTEMS) +#define DEBUG_REVERSAL 0x00000008 /* reversal of the paths */ +#define DEBUG_FIXCVDIR 0x00000010 /* fixcvdir() */ +#define DEBUG_STEMOVERLAP 0x00000020 /* stemoverlap() */ +#define DEBUG_BLUESTEMS 0x00000040 /* markbluestems() */ +#define DEBUG_STRAIGHTEN 0x00000080 /* markbluestems() */ +#define DEBUG_EXTMAP 0x00000100 /* parsing of external map */ +#define DEBUG_TOINT 0x00000200 /* conversion of path to integer */ +#define DEBUG_BUILDG 0x00000400 /* building of glyph path */ +#define DEBUG_QUAD 0x00000800 /* splitting curves by quadrants */ +#define DEBUG_SQEQ 0x00001000 /* square equation solver */ +#define DEBUG_COMPOSITE 0x00002000 /* handling of composite glyphs */ +#define DEBUG_FCONCISE 0x00004000 /* normalization of curves */ +#define DEBUG_FT 0x00008000 /* FreeType front-end */ +#define DEBUG_DISABLED 0x80000000 /* special flag: temporary disable debugging */ + +/* at what we want to look now */ +#ifndef DEBUG +# define DEBUG (0) +#endif + +/* uncomment the next line if debugging data is wanted for one glyph only */ +/* #define DBG_GLYPH "_517" /* */ + +#if DEBUG==0 +# define ISDBG(name) (0) +# define ENABLEDBG(condition) (0) +# define DISABLEDBG(condition) (0) +#else + extern int debug; /* collection of the flags */ +/* this ISDBG will only work on ANSI C, not K&R */ +# define ISDBG(name) ( (debug & DEBUG_DISABLED) ? 0 : (debug & (DEBUG_##name)) ) +# define ENABLEDBG(condition) ( (condition) ? (debug&=~DEBUG_DISABLED) : 0 ) +# define DISABLEDBG(condition) ( (condition) ? (debug|=DEBUG_DISABLED) : 0 ) +#endif + +#ifdef DBG_GLYPH +# define DBG_TO_GLYPH(g) DISABLEDBG( strcmp( (g)->name, DBG_GLYPH ) ) +# define DBG_FROM_GLYPH(g) ENABLEDBG(1) +#else +# define DBG_TO_GLYPH(g) (0) +# define DBG_FROM_GLYPH(g) (0) +#endif + +/* prototypes */ +int iscale( int val); +double fscale( double val); +int unicode_rev_lookup( int unival); + +/* global metrics for a font */ + +struct font_metrics { + /* post */ + double italic_angle; + short underline_position; + short underline_thickness; + short is_fixed_pitch; + + /* hhea */ + short ascender; + short descender; + + /* head */ + unsigned short units_per_em; + short bbox[4]; + + /* name */ + char *name_copyright; + char *name_family; + char *name_style; + char *name_full; + char *name_version; + char *name_ps; + + /* other */ + int force_bold; +}; + +/* size of the encoding table - glyphs beyond 255 are actually unnumbered */ + +#define ENCTABSZ 1024 + +/* switch table structure for front-ends */ + +#define MAXSUFFIX 10 + +struct frontsw { + char *name; /* name of the front end */ + char *descr; /* description of the front end */ + char *suffix[MAXSUFFIX]; /* possible file name suffixes */ + + void (*open)(char *fname, char *arg); /* open font file */ + void (*close)(void); /* close font file */ + int (*nglyphs)(void); /* get the number of glyphs */ + int (*glnames)(GLYPH *glyphs); /* get the names of glyphs */ + void (*glmetrics)(GLYPH *glyphs); /* get the metrics of glyphs */ + int (*glenc)(GLYPH *glyphs, int *enc, int *unimap); /* get the encoding */ + void (*fnmetrics)(struct font_metrics *fm); /* get the font metrics */ + void (*glpath)(int glyphno, GLYPH *glyphs); /* get the glyph path */ + void (*kerning)(GLYPH *glyph_list); /* extract the kerning data */ +}; diff --git a/pdf2swf/ttf2pt1/pt1.c b/pdf2swf/ttf2pt1/pt1.c new file mode 100644 index 0000000..0099324 --- /dev/null +++ b/pdf2swf/ttf2pt1/pt1.c @@ -0,0 +1,5966 @@ +/* + * see COPYRIGHT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WINDOWS +# include +# include +#else +# include "windows.h" +#endif + +#include "ttf.h" +#include "pt1.h" +#include "global.h" + +/* big and small values for comparisons */ +#define FBIGVAL (1e20) +#define FEPS (100000./FBIGVAL) + +int stdhw, stdvw; /* dominant stems widths */ +int stemsnaph[12], stemsnapv[12]; /* most typical stem width */ + +int bluevalues[14]; +int nblues; +int otherblues[10]; +int notherb; +int bbox[4]; /* the FontBBox array */ +double italic_angle; + +GLYPH *glyph_list; +int encoding[ENCTABSZ]; /* inverse of glyph[].char_no */ +int kerning_pairs = 0; + +/* prototypes */ +static int isign( int x); +static int fsign( double x); +static void fixcvdir( GENTRY * ge, int dir); +static void fixcvends( GENTRY * ge); +static int fgetcvdir( GENTRY * ge); +static int igetcvdir( GENTRY * ge); +static int fiszigzag( GENTRY *ge); +static int iiszigzag( GENTRY *ge); +static GENTRY * freethisge( GENTRY *ge); +static void addgeafter( GENTRY *oge, GENTRY *nge ); +static GENTRY * newgentry( int flags); +static void debugstems( char *name, STEM * hstems, int nhs, STEM * vstems, int nvs); +static int addbluestems( STEM *s, int n); +static void sortstems( STEM * s, int n); +static int stemoverlap( STEM * s1, STEM * s2); +static int steminblue( STEM *s); +static void markbluestems( STEM *s, int nold); +static int joinmainstems( STEM * s, int nold, int useblues); +static void joinsubstems( STEM * s, short *pairs, int nold, int useblues); +static void fixendpath( GENTRY *ge); +static void fdelsmall( GLYPH *g, double minlen); +static double fcvarea( GENTRY *ge); +static int fckjoinedcv( GLYPH *g, double t, GENTRY *nge, + GENTRY *old1, GENTRY *old2, double k); +static double fcvval( GENTRY *ge, int axis, double t); +static double fclosegap( GENTRY *from, GENTRY *to, int axis, + double gap, double *ret); + +static int +isign( + int x +) +{ + if (x > 0) + return 1; + else if (x < 0) + return -1; + else + return 0; +} + +static int +fsign( + double x +) +{ + if (x > 0.0) + return 1; + else if (x < 0.0) + return -1; + else + return 0; +} + +static GENTRY * +newgentry( + int flags +) +{ + GENTRY *ge; + + ge = calloc(1, sizeof(GENTRY)); + + if (ge == 0) { + fprintf(stderr, "***** Memory allocation error *****\n"); + exit(255); + } + ge->stemid = -1; + ge->flags = flags; + /* the rest is set to 0 by calloc() */ + return ge; +} + +/* + * Routines to print out Postscript functions with optimization + */ + +void +rmoveto( + int dx, + int dy +) +{ + if (optimize && dx == 0) + fprintf(pfa_file, "%d vmoveto\n", dy); + else if (optimize && dy == 0) + fprintf(pfa_file, "%d hmoveto\n", dx); + else + fprintf(pfa_file, "%d %d rmoveto\n", dx, dy); +} + +void +rlineto( + int dx, + int dy +) +{ + if (optimize && dx == 0 && dy == 0) /* for special pathologic + * case */ + return; + else if (optimize && dx == 0) + fprintf(pfa_file, "%d vlineto\n", dy); + else if (optimize && dy == 0) + fprintf(pfa_file, "%d hlineto\n", dx); + else + fprintf(pfa_file, "%d %d rlineto\n", dx, dy); +} + +void +rrcurveto( + int dx1, + int dy1, + int dx2, + int dy2, + int dx3, + int dy3 +) +{ + /* first two ifs are for crazy cases that occur surprisingly often */ + if (optimize && dx1 == 0 && dx2 == 0 && dx3 == 0) + rlineto(0, dy1 + dy2 + dy3); + else if (optimize && dy1 == 0 && dy2 == 0 && dy3 == 0) + rlineto(dx1 + dx2 + dx3, 0); + else if (optimize && dy1 == 0 && dx3 == 0) + fprintf(pfa_file, "%d %d %d %d hvcurveto\n", + dx1, dx2, dy2, dy3); + else if (optimize && dx1 == 0 && dy3 == 0) + fprintf(pfa_file, "%d %d %d %d vhcurveto\n", + dy1, dx2, dy2, dx3); + else + fprintf(pfa_file, "%d %d %d %d %d %d rrcurveto\n", + dx1, dy1, dx2, dy2, dx3, dy3); +} + +void +closepath(void) +{ + fprintf(pfa_file, "closepath\n"); +} + +/* + * Many of the path processing routines exist (or will exist) in + * both floating-point and integer version. Fimally most of the + * processing will go in floating point and the integer processing + * will become legacy. + * The names of floating routines start with f, names of integer + * routines start with i, and those old routines existing in one + * version only have no such prefix at all. + */ + +/* +** Routine that checks integrity of the path, for debugging +*/ + +void +assertpath( + GENTRY * from, + char *file, + int line, + char *name +) +{ + GENTRY *first, *pe, *ge; + int isfloat; + + if(from==0) + return; + isfloat = (from->flags & GEF_FLOAT); + pe = from->prev; + for (ge = from; ge != 0; pe = ge, ge = ge->next) { + if( (ge->flags & GEF_FLOAT) ^ isfloat ) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "float flag changes from %s to %s at 0x%p (type %c, prev type %c)\n", + (isfloat ? "TRUE" : "FALSE"), (isfloat ? "FALSE" : "TRUE"), ge, ge->type, pe->type); + abort(); + } + if (pe != ge->prev) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "unidirectional chain 0x%x -next-> 0x%x -prev-> 0x%x \n", + pe, ge, ge->prev); + abort(); + } + + switch(ge->type) { + case GE_MOVE: + break; + case GE_PATH: + if (ge->prev == 0) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "empty path at 0x%x \n", ge); + abort(); + } + break; + case GE_LINE: + case GE_CURVE: + if(ge->frwd->bkwd != ge) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "unidirectional chain 0x%x -frwd-> 0x%x -bkwd-> 0x%x \n", + ge, ge->frwd, ge->frwd->bkwd); + abort(); + } + if(ge->prev->type == GE_MOVE) { + first = ge; + if(ge->bkwd->next->type != GE_PATH) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "broken first backlink 0x%x -bkwd-> 0x%x -next-> 0x%x \n", + ge, ge->bkwd, ge->bkwd->next); + abort(); + } + } + if(ge->next->type == GE_PATH) { + if(ge->frwd != first) { + fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name); + fprintf(stderr, "broken loop 0x%x -...-> 0x%x -frwd-> 0x%x \n", + first, ge, ge->frwd); + abort(); + } + } + break; + } + + } +} + +void +assertisfloat( + GLYPH *g, + char *msg +) +{ + if( !(g->flags & GF_FLOAT) ) { + fprintf(stderr, "**! Glyph %s is not float: %s\n", g->name, msg); + abort(); + } + if(g->lastentry) { + if( !(g->lastentry->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! Glyphs %s last entry is int: %s\n", g->name, msg); + abort(); + } + } +} + +void +assertisint( + GLYPH *g, + char *msg +) +{ + if( (g->flags & GF_FLOAT) ) { + fprintf(stderr, "**! Glyph %s is not int: %s\n", g->name, msg); + abort(); + } + if(g->lastentry) { + if( (g->lastentry->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! Glyphs %s last entry is float: %s\n", g->name, msg); + abort(); + } + } +} + + +/* + * Routines to save the generated data about glyph + */ + +void +fg_rmoveto( + GLYPH * g, + double x, + double y) +{ + GENTRY *oge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: f rmoveto(%g, %g)\n", g->name, x, y); + + assertisfloat(g, "adding float MOVE"); + + if ((oge = g->lastentry) != 0) { + if (oge->type == GE_MOVE) { /* just eat up the first move */ + oge->fx3 = x; + oge->fy3 = y; + } else if (oge->type == GE_LINE || oge->type == GE_CURVE) { + fprintf(stderr, "Glyph %s: MOVE in middle of path\n", g->name); + } else { + GENTRY *nge; + + nge = newgentry(GEF_FLOAT); + nge->type = GE_MOVE; + nge->fx3 = x; + nge->fy3 = y; + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } + } else { + GENTRY *nge; + + nge = newgentry(GEF_FLOAT); + nge->type = GE_MOVE; + nge->fx3 = x; + nge->fy3 = y; + nge->bkwd = (GENTRY*)&g->entries; + g->entries = g->lastentry = nge; + } + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +void +fg_rlineto( + GLYPH * g, + double x, + double y) +{ + GENTRY *oge, *nge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: f rlineto(%g, %g)\n", g->name, x, y); + + assertisfloat(g, "adding float LINE"); + + nge = newgentry(GEF_FLOAT); + nge->type = GE_LINE; + nge->fx3 = x; + nge->fy3 = y; + + if ((oge = g->lastentry) != 0) { + if (x == oge->fx3 && y == oge->fy3) { /* empty line */ + /* ignore it or we will get in troubles later */ + free(nge); + return; + } + if (g->path == 0) { + g->path = nge; + nge->bkwd = nge->frwd = nge; + } else { + oge->frwd = nge; + nge->bkwd = oge; + g->path->bkwd = nge; + nge->frwd = g->path; + } + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } else { + WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name); + free(nge); + } + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +void +fg_rrcurveto( + GLYPH * g, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3) +{ + GENTRY *oge, *nge; + + oge = g->lastentry; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: f rrcurveto(%g, %g, %g, %g, %g, %g)\n" + ,g->name, x1, y1, x2, y2, x3, y3); + + assertisfloat(g, "adding float CURVE"); + + if (oge && oge->fx3 == x1 && x1 == x2 && x2 == x3) /* check if it's + * actually a line */ + fg_rlineto(g, x1, y3); + else if (oge && oge->fy3 == y1 && y1 == y2 && y2 == y3) + fg_rlineto(g, x3, y1); + else { + nge = newgentry(GEF_FLOAT); + nge->type = GE_CURVE; + nge->fx1 = x1; + nge->fy1 = y1; + nge->fx2 = x2; + nge->fy2 = y2; + nge->fx3 = x3; + nge->fy3 = y3; + + if (oge != 0) { + if (x3 == oge->fx3 && y3 == oge->fy3) { + free(nge); /* consider this curve empty */ + /* ignore it or we will get in troubles later */ + return; + } + if (g->path == 0) { + g->path = nge; + nge->bkwd = nge->frwd = nge; + } else { + oge->frwd = nge; + nge->bkwd = oge; + g->path->bkwd = nge; + nge->frwd = g->path; + } + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + } else { + WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name); + free(nge); + } + } + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +void +g_closepath( + GLYPH * g +) +{ + GENTRY *oge, *nge; + + if (ISDBG(BUILDG)) + fprintf(stderr, "%s: closepath\n", g->name); + + oge = g->lastentry; + + if (g->path == 0) { + WARNING_1 fprintf(stderr, "Warning: **** closepath on empty path in glyph \"%s\" ****\n", + g->name); + if (oge == 0) { + WARNING_1 fprintf(stderr, "No previois entry\n"); + } else { + WARNING_1 fprintf(stderr, "Previous entry type: %c\n", oge->type); + if (oge->type == GE_MOVE) { + g->lastentry = oge->prev; + if (oge->prev == 0) + g->entries = 0; + } + } + return; + } + + nge = newgentry(oge->flags & GEF_FLOAT); /* keep the same type */ + nge->type = GE_PATH; + + g->path = 0; + + oge->next = nge; + nge->prev = oge; + g->lastentry = nge; + + if (0 && ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); +} + +/* + * * SB * Routines to smooth and fix the glyphs + */ + +/* +** we don't want to see the curves with coinciding middle and +** outer points +*/ + +static void +fixcvends( + GENTRY * ge +) +{ + int dx, dy; + int x0, y0, x1, y1, x2, y2, x3, y3; + + if (ge->type != GE_CURVE) + return; + + if(ge->flags & GEF_FLOAT) { + fprintf(stderr, "**! fixcvends(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + x0 = ge->prev->ix3; + y0 = ge->prev->iy3; + x1 = ge->ix1; + y1 = ge->iy1; + x2 = ge->ix2; + y2 = ge->iy2; + x3 = ge->ix3; + y3 = ge->iy3; + + + /* look at the start of the curve */ + if (x1 == x0 && y1 == y0) { + dx = x2 - x1; + dy = y2 - y1; + + if (dx == 0 && dy == 0 + || x2 == x3 && y2 == y3) { + /* Oops, we actually have a straight line */ + /* + * if it's small, we hope that it will get optimized + * later + */ + if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) { + ge->ix1 = x3; + ge->iy1 = y3; + ge->ix2 = x0; + ge->iy2 = y0; + } else {/* just make it a line */ + ge->type = GE_LINE; + } + } else { + if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very + * small */ + ge->ix1 = x2; + ge->iy1 = y2; + } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */ + ge->ix1 += dx / 2; + ge->iy1 += dy / 2; + } else { + ge->ix1 += dx / 4; + ge->iy1 += dy / 4; + } + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) { + if (abs(x3 - x0) * abs(ge->iy1 - y0) > abs(y3 - y0) * abs(ge->ix1 - x0)) + ge->ix1 += isign(dx); + } else { + if (abs(x3 - x0) * abs(ge->iy1 - y0) < abs(y3 - y0) * abs(ge->ix1 - x0)) + ge->iy1 += isign(dy); + } + + ge->ix2 += (x3 - x2) / 8; + ge->iy2 += (y3 - y2) / 8; + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(y3 - y2) < abs(y3 - y0) * abs(x3 - x2)) { + if (abs(x3 - x0) * abs(y3 - ge->iy2) > abs(y3 - y0) * abs(x3 - ge->ix2)) + ge->iy1 -= isign(y3 - y2); + } else { + if (abs(x3 - x0) * abs(y3 - ge->iy2) < abs(y3 - y0) * abs(x3 - ge->ix2)) + ge->ix1 -= isign(x3 - x2); + } + + } + } else if (x2 == x3 && y2 == y3) { + dx = x1 - x2; + dy = y1 - y2; + + if (dx == 0 && dy == 0) { + /* Oops, we actually have a straight line */ + /* + * if it's small, we hope that it will get optimized + * later + */ + if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) { + ge->ix1 = x3; + ge->iy1 = y3; + ge->ix2 = x0; + ge->iy2 = y0; + } else {/* just make it a line */ + ge->type = GE_LINE; + } + } else { + if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very + * small */ + ge->ix2 = x1; + ge->iy2 = y1; + } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */ + ge->ix2 += dx / 2; + ge->iy2 += dy / 2; + } else { + ge->ix2 += dx / 4; + ge->iy2 += dy / 4; + } + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) { + if (abs(x3 - x0) * abs(ge->iy2 - y3) > abs(y3 - y0) * abs(ge->ix2 - x3)) + ge->ix2 += isign(dx); + } else { + if (abs(x3 - x0) * abs(ge->iy2 - y3) < abs(y3 - y0) * abs(ge->ix2 - x3)) + ge->iy2 += isign(dy); + } + + ge->ix1 += (x0 - x1) / 8; + ge->iy1 += (y0 - y1) / 8; + /* make sure that it's still on the same side */ + if (abs(x3 - x0) * abs(y0 - y1) < abs(y3 - y0) * abs(x0 - x1)) { + if (abs(x3 - x0) * abs(y0 - ge->iy1) > abs(y3 - y0) * abs(x0 - ge->ix1)) + ge->iy1 -= isign(y0 - y1); + } else { + if (abs(x3 - x0) * abs(y0 - ge->iy1) < abs(y3 - y0) * abs(x0 - ge->ix1)) + ge->ix1 -= isign(x0 - x1); + } + + } + } +} + +/* if we have any curves that are in fact flat but +** are not horizontal nor vertical, substitute +** them also with lines +*/ + +void +flattencurves( + GLYPH * g +) +{ + GENTRY *ge; + int x0, y0, x1, y1, x2, y2, x3, y3; + + assertisint(g, "flattencurves INT"); + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + x0 = ge->prev->ix3; + y0 = ge->prev->iy3; + x1 = ge->ix1; + y1 = ge->iy1; + x2 = ge->ix2; + y2 = ge->iy2; + x3 = ge->ix3; + y3 = ge->iy3; + + if ((x1 - x0) * (y2 - y1) == (x2 - x1) * (y1 - y0) + && (x1 - x0) * (y3 - y2) == (x3 - x2) * (y1 - y0)) { + ge->type = GE_LINE; + } + } +} + +/* +** After transformations we want to make sure that the resulting +** curve is going in the same quadrant as the original one, +** because rounding errors introduced during transformations +** may make the result completeley wrong. +** +** `dir' argument describes the direction of the original curve, +** it is the superposition of two values for the front and +** rear ends of curve: +** +** >EQUAL - goes over the line connecting the ends +** =EQUAL - coincides with the line connecting the ends +** flags & GEF_FLOAT) { + fprintf(stderr, "**! fixcvdir(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + fdir = (dir & CVDIR_FRONT) - CVDIR_FEQUAL; + if ((dir & CVDIR_REAR) == CVDIR_RSAME) + rdir = fdir; /* we need only isign, exact value doesn't matter */ + else + rdir = (dir & CVDIR_REAR) - CVDIR_REQUAL; + + fixcvends(ge); + + c = isign(ge->ix3 - ge->prev->ix3); /* note the direction of + * curve */ + d = isign(ge->iy3 - ge->prev->iy3); + + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + + changed = 1; + while (changed) { + if (ISDBG(FIXCVDIR)) { + /* for debugging */ + fprintf(stderr, "fixcvdir %d %d (%d %d %d %d %d %d) %f %f %f\n", + fdir, rdir, + ge->ix1 - ge->prev->ix3, + ge->iy1 - ge->prev->iy3, + ge->ix2 - ge->ix1, + ge->iy2 - ge->iy1, + ge->ix3 - ge->ix2, + ge->iy3 - ge->iy2, + kk1, kk, kk2); + } + changed = 0; + + if (fdir > 0) { + if (kk1 > kk) { /* the front end has problems */ + if (c * (ge->ix1 - ge->prev->ix3) > 0) { + ge->ix1 -= c; + changed = 1; + } if (d * (ge->iy2 - ge->iy1) > 0) { + ge->iy1 += d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } else if (fdir < 0) { + if (kk1 < kk) { /* the front end has problems */ + if (c * (ge->ix2 - ge->ix1) > 0) { + ge->ix1 += c; + changed = 1; + } if (d * (ge->iy1 - ge->prev->iy3) > 0) { + ge->iy1 -= d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } + if (rdir > 0) { + if (kk2 < kk) { /* the rear end has problems */ + if (c * (ge->ix2 - ge->ix1) > 0) { + ge->ix2 -= c; + changed = 1; + } if (d * (ge->iy3 - ge->iy2) > 0) { + ge->iy2 += d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } else if (rdir < 0) { + if (kk2 > kk) { /* the rear end has problems */ + if (c * (ge->ix3 - ge->ix2) > 0) { + ge->ix2 += c; + changed = 1; + } if (d * (ge->iy2 - ge->iy1) > 0) { + ge->iy2 -= d; + changed = 1; + } + /* recalculate the coefficients */ + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + } + } + } + fixcvends(ge); +} + +/* Get the directions of ends of curve for further usage */ + +/* expects that the previous element is also float */ + +static int +fgetcvdir( + GENTRY * ge +) +{ + double a, b; + double k, k1, k2; + int dir = 0; + + if( !(ge->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! fgetcvdir(0x%x) on int entry, ABORT\n", ge); + abort(); /* dump core */ + } + + a = ge->fy3 - ge->prev->fy3; + b = ge->fx3 - ge->prev->fx3; + k = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a)); + a = ge->fy1 - ge->prev->fy3; + b = ge->fx1 - ge->prev->fx3; + k1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a)); + a = ge->fy3 - ge->fy2; + b = ge->fx3 - ge->fx2; + k2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a)); + + if (k1 < k) + dir |= CVDIR_FUP; + else if (k1 > k) + dir |= CVDIR_FDOWN; + else + dir |= CVDIR_FEQUAL; + + if (k2 > k) + dir |= CVDIR_RUP; + else if (k2 < k) + dir |= CVDIR_RDOWN; + else + dir |= CVDIR_REQUAL; + + return dir; +} + + +/* expects that the previous element is also int */ + +static int +igetcvdir( + GENTRY * ge +) +{ + int a, b; + double k, k1, k2; + int dir = 0; + + if(ge->flags & GEF_FLOAT) { + fprintf(stderr, "**! igetcvdir(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + a = ge->iy3 - ge->prev->iy3; + b = ge->ix3 - ge->prev->ix3; + k = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + k1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + k2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a)); + + if (k1 < k) + dir |= CVDIR_FUP; + else if (k1 > k) + dir |= CVDIR_FDOWN; + else + dir |= CVDIR_FEQUAL; + + if (k2 > k) + dir |= CVDIR_RUP; + else if (k2 < k) + dir |= CVDIR_RDOWN; + else + dir |= CVDIR_REQUAL; + + return dir; +} + +#if 0 +/* a function just to test the work of fixcvdir() */ +static void +testfixcvdir( + GLYPH * g +) +{ + GENTRY *ge; + int dir; + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_CURVE) { + dir = igetcvdir(ge); + fixcvdir(ge, dir); + } + } +} +#endif + +static int +iround( + double val +) +{ + return (int) (val > 0 ? val + 0.5 : val - 0.5); +} + +/* for debugging - dump the glyph + * mark with a star the entries from start to end inclusive + * (start == NULL means don't mark any, end == NULL means to the last) + */ + +void +dumppaths( + GLYPH *g, + GENTRY *start, + GENTRY *end +) +{ + GENTRY *ge; + int i; + char mark=' '; + + fprintf(stderr, "Glyph %s:\n", g->name); + + /* now do the conversion */ + for(ge = g->entries; ge != 0; ge = ge->next) { + if(ge == start) + mark = '*'; + fprintf(stderr, " %c %8x", mark, ge); + switch(ge->type) { + case GE_MOVE: + case GE_LINE: + if(ge->flags & GEF_FLOAT) + fprintf(stderr," %c float (%g, %g)\n", ge->type, ge->fx3, ge->fy3); + else + fprintf(stderr," %c int (%d, %d)\n", ge->type, ge->ix3, ge->iy3); + break; + case GE_CURVE: + if(ge->flags & GEF_FLOAT) { + fprintf(stderr," C float "); + for(i=0; i<3; i++) + fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]); + fprintf(stderr,"\n"); + } else { + fprintf(stderr," C int "); + for(i=0; i<3; i++) + fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); + fprintf(stderr,"\n"); + } + break; + default: + fprintf(stderr, " %c\n", ge->type); + break; + } + if(ge == end) + mark = ' '; + } +} + +/* + * Routine that converts all entries in the path from float to int + */ + +void +pathtoint( + GLYPH *g +) +{ + GENTRY *ge; + int x[3], y[3]; + int i; + + + if(ISDBG(TOINT)) + fprintf(stderr, "TOINT: glyph %s\n", g->name); + assertisfloat(g, "converting path to int\n"); + + fdelsmall(g, 1.0); /* get rid of sub-pixel contours */ + assertpath(g->entries, __FILE__, __LINE__, g->name); + + /* 1st pass, collect the directions of the curves: have + * to do that in advance, while everyting is float + */ + for(ge = g->entries; ge != 0; ge = ge->next) { + if( !(ge->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! glyphs %s has int entry, found in conversion to int\n", + g->name); + exit(1); + } + if(ge->type == GE_CURVE) { + ge->dir = fgetcvdir(ge); + } + } + + /* now do the conversion */ + for(ge = g->entries; ge != 0; ge = ge->next) { + switch(ge->type) { + case GE_MOVE: + case GE_LINE: + if(ISDBG(TOINT)) + fprintf(stderr," %c float x=%g y=%g\n", ge->type, ge->fx3, ge->fy3); + x[0] = iround(ge->fx3); + y[0] = iround(ge->fy3); + for(i=0; i<3; i++) { /* put some valid values everywhere, for convenience */ + ge->ixn[i] = x[0]; + ge->iyn[i] = y[0]; + } + if(ISDBG(TOINT)) + fprintf(stderr," int x=%d y=%d\n", ge->ix3, ge->iy3); + break; + case GE_CURVE: + if(ISDBG(TOINT)) + fprintf(stderr," %c float ", ge->type); + + for(i=0; i<3; i++) { + if(ISDBG(TOINT)) + fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]); + x[i] = iround(ge->fxn[i]); + y[i] = iround(ge->fyn[i]); + } + + if(ISDBG(TOINT)) + fprintf(stderr,"\n int "); + + for(i=0; i<3; i++) { + ge->ixn[i] = x[i]; + ge->iyn[i] = y[i]; + if(ISDBG(TOINT)) + fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); + } + ge->flags &= ~GEF_FLOAT; /* for fixcvdir */ + fixcvdir(ge, ge->dir); + + if(ISDBG(TOINT)) { + fprintf(stderr,"\n fixed "); + for(i=0; i<3; i++) + fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]); + fprintf(stderr,"\n"); + } + + break; + } + ge->flags &= ~GEF_FLOAT; + } + g->flags &= ~GF_FLOAT; +} + + +/* check whether we can fix up the curve to change its size by (dx,dy) */ +/* 0 means NO, 1 means YES */ + +/* for float: if scaling would be under 10% */ + +int +fcheckcv( + GENTRY * ge, + double dx, + double dy +) +{ + if( !(ge->flags & GEF_FLOAT) ) { + fprintf(stderr, "**! fcheckcv(0x%x) on int entry, ABORT\n", ge); + abort(); /* dump core */ + } + + if (ge->type != GE_CURVE) + return 0; + + if( fabs(ge->fx3 - ge->prev->fx3) < fabs(dx) * 10 ) + return 0; + + if( fabs(ge->fy3 - ge->prev->fy3) < fabs(dy) * 10 ) + return 0; + + return 1; +} + +/* for int: if won't create new zigzags at the ends */ + +int +icheckcv( + GENTRY * ge, + int dx, + int dy +) +{ + int xdep, ydep; + + if(ge->flags & GEF_FLOAT) { + fprintf(stderr, "**! icheckcv(0x%x) on floating entry, ABORT\n", ge); + abort(); /* dump core */ + } + + if (ge->type != GE_CURVE) + return 0; + + xdep = ge->ix3 - ge->prev->ix3; + ydep = ge->iy3 - ge->prev->iy3; + + if (ge->type == GE_CURVE + && (xdep * (xdep + dx)) > 0 + && (ydep * (ydep + dy)) > 0) { + return 1; + } else + return 0; +} + +/* float connect the ends of open contours */ + +void +fclosepaths( + GLYPH * g +) +{ + GENTRY *ge, *fge, *xge, *nge; + int i; + + assertisfloat(g, "fclosepaths float\n"); + + for (xge = g->entries; xge != 0; xge = xge->next) { + if( xge->type != GE_PATH ) + continue; + + ge = xge->prev; + if(ge == 0 || ge->type != GE_LINE && ge->type!= GE_CURVE) { + fprintf(stderr, "**! Glyph %s got empty path\n", + g->name); + exit(1); + } + + fge = ge->frwd; + if (fge->prev == 0 || fge->prev->type != GE_MOVE) { + fprintf(stderr, "**! Glyph %s got strange beginning of path\n", + g->name); + exit(1); + } + fge = fge->prev; + if (fge->fx3 != ge->fx3 || fge->fy3 != ge->fy3) { + /* we have to fix this open path */ + + WARNING_4 fprintf(stderr, "Glyph %s got path open by dx=%g dy=%g\n", + g->name, fge->fx3 - ge->fx3, fge->fy3 - ge->fy3); + + + /* add a new line */ + nge = newgentry(GEF_FLOAT); + (*nge) = (*ge); + nge->fx3 = fge->fx3; + nge->fy3 = fge->fy3; + nge->type = GE_LINE; + + addgeafter(ge, nge); + + if (fabs(ge->fx3 - fge->fx3) <= 2 && fabs(ge->fy3 - fge->fy3) <= 2) { + /* + * small change, try to get rid of the new entry + */ + + double df[2]; + + for(i=0; i<2; i++) { + df[i] = ge->fpoints[i][2] - fge->fpoints[i][2]; + df[i] = fclosegap(nge, nge, i, df[i], NULL); + } + + if(df[0] == 0. && df[1] == 0.) { + /* closed gap successfully, remove the added entry */ + freethisge(nge); + } + } + } + } +} + +void +smoothjoints( + GLYPH * g +) +{ + GENTRY *ge, *ne; + int dx1, dy1, dx2, dy2, k; + int dir; + + return; /* this stuff seems to create problems */ + + assertisint(g, "smoothjoints int"); + + if (g->entries == 0) /* nothing to do */ + return; + + for (ge = g->entries->next; ge != 0; ge = ge->next) { + ne = ge->frwd; + + /* + * although there should be no one-line path * and any path + * must end with CLOSEPATH, * nobody can say for sure + */ + + if (ge == ne || ne == 0) + continue; + + /* now handle various joints */ + + if (ge->type == GE_LINE && ne->type == GE_LINE) { + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + dx2 = ne->ix3 - ge->ix3; + dy2 = ne->iy3 - ge->iy3; + + /* check whether they have the same direction */ + /* and the same slope */ + /* then we can join them into one line */ + + if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 && dx1 * dy2 == dy1 * dx2) { + /* extend the previous line */ + ge->ix3 = ne->ix3; + ge->iy3 = ne->iy3; + + /* and get rid of the next line */ + freethisge(ne); + } + } else if (ge->type == GE_LINE && ne->type == GE_CURVE) { + fixcvends(ne); + + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + dx2 = ne->ix1 - ge->ix3; + dy2 = ne->iy1 - ge->iy3; + + /* if the line is nearly horizontal and we can fix it */ + if (dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0 + && icheckcv(ne, 0, -dy1) + && abs(dy1) <= 4) { + dir = igetcvdir(ne); + ge->iy3 -= dy1; + ne->iy1 -= dy1; + fixcvdir(ne, dir); + if (ge->next != ne) + ne->prev->iy3 -= dy1; + dy1 = 0; + } else if (dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0 + && icheckcv(ne, -dx1, 0) + && abs(dx1) <= 4) { + /* the same but vertical */ + dir = igetcvdir(ne); + ge->ix3 -= dx1; + ne->ix1 -= dx1; + fixcvdir(ne, dir); + if (ge->next != ne) + ne->prev->ix3 -= dx1; + dx1 = 0; + } + /* + * if line is horizontal and curve begins nearly + * horizontally + */ + if (dy1 == 0 && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0) { + dir = igetcvdir(ne); + ne->iy1 -= dy2; + fixcvdir(ne, dir); + dy2 = 0; + } else if (dx1 == 0 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0) { + /* the same but vertical */ + dir = igetcvdir(ne); + ne->ix1 -= dx2; + fixcvdir(ne, dir); + dx2 = 0; + } + } else if (ge->type == GE_CURVE && ne->type == GE_LINE) { + fixcvends(ge); + + dx1 = ge->ix3 - ge->ix2; + dy1 = ge->iy3 - ge->iy2; + dx2 = ne->ix3 - ge->ix3; + dy2 = ne->iy3 - ge->iy3; + + /* if the line is nearly horizontal and we can fix it */ + if (dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0 + && icheckcv(ge, 0, dy2) + && abs(dy2) <= 4) { + dir = igetcvdir(ge); + ge->iy3 += dy2; + ge->iy2 += dy2; + fixcvdir(ge, dir); + if (ge->next != ne) + ne->prev->iy3 += dy2; + dy2 = 0; + } else if (dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0 + && icheckcv(ge, dx2, 0) + && abs(dx2) <= 4) { + /* the same but vertical */ + dir = igetcvdir(ge); + ge->ix3 += dx2; + ge->ix2 += dx2; + fixcvdir(ge, dir); + if (ge->next != ne) + ne->prev->ix3 += dx2; + dx2 = 0; + } + /* + * if line is horizontal and curve ends nearly + * horizontally + */ + if (dy2 == 0 && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0) { + dir = igetcvdir(ge); + ge->iy2 += dy1; + fixcvdir(ge, dir); + dy1 = 0; + } else if (dx2 == 0 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0) { + /* the same but vertical */ + dir = igetcvdir(ge); + ge->ix2 += dx1; + fixcvdir(ge, dir); + dx1 = 0; + } + } else if (ge->type == GE_CURVE && ne->type == GE_CURVE) { + fixcvends(ge); + fixcvends(ne); + + dx1 = ge->ix3 - ge->ix2; + dy1 = ge->iy3 - ge->iy2; + dx2 = ne->ix1 - ge->ix3; + dy2 = ne->iy1 - ge->iy3; + + /* + * check if we have a rather smooth joint at extremal + * point + */ + /* left or right extremal point */ + if (abs(dx1) <= 4 && abs(dx2) <= 4 + && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0 + && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0 + && (ge->iy3 < ge->prev->iy3 && ne->iy3 < ge->iy3 + || ge->iy3 > ge->prev->iy3 && ne->iy3 > ge->iy3) + && (ge->ix3 - ge->prev->ix3) * (ne->ix3 - ge->ix3) < 0 + ) { + dir = igetcvdir(ge); + ge->ix2 += dx1; + dx1 = 0; + fixcvdir(ge, dir); + dir = igetcvdir(ne); + ne->ix1 -= dx2; + dx2 = 0; + fixcvdir(ne, dir); + } + /* top or down extremal point */ + else if (abs(dy1) <= 4 && abs(dy2) <= 4 + && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0 + && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0 + && (ge->ix3 < ge->prev->ix3 && ne->ix3 < ge->ix3 + || ge->ix3 > ge->prev->ix3 && ne->ix3 > ge->ix3) + && (ge->iy3 - ge->prev->iy3) * (ne->iy3 - ge->iy3) < 0 + ) { + dir = igetcvdir(ge); + ge->iy2 += dy1; + dy1 = 0; + fixcvdir(ge, dir); + dir = igetcvdir(ne); + ne->iy1 -= dy2; + dy2 = 0; + fixcvdir(ne, dir); + } + /* or may be we just have a smooth junction */ + else if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 + && 10 * abs(k = abs(dx1 * dy2) - abs(dy1 * dx2)) < (abs(dx1 * dy2) + abs(dy1 * dx2))) { + int tries[6][4]; + int results[6]; + int i, b; + + /* build array of changes we are going to try */ + /* uninitalized entries are 0 */ + if (k > 0) { + static int t1[6][4] = { + {0, 0, 0, 0}, + {-1, 0, 1, 0}, + {-1, 0, 0, 1}, + {0, -1, 1, 0}, + {0, -1, 0, 1}, + {-1, -1, 1, 1}}; + memcpy(tries, t1, sizeof tries); + } else { + static int t1[6][4] = { + {0, 0, 0, 0}, + {1, 0, -1, 0}, + {1, 0, 0, -1}, + {0, 1, -1, 0}, + {0, 1, 0, -1}, + {1, 1, -1, -1}}; + memcpy(tries, t1, sizeof tries); + } + + /* now try the changes */ + results[0] = abs(k); + for (i = 1; i < 6; i++) { + results[i] = abs((abs(dx1) + tries[i][0]) * (abs(dy2) + tries[i][1]) - + (abs(dy1) + tries[i][2]) * (abs(dx2) + tries[i][3])); + } + + /* and find the best try */ + k = abs(k); + b = 0; + for (i = 1; i < 6; i++) + if (results[i] < k) { + k = results[i]; + b = i; + } + /* and finally apply it */ + if (dx1 < 0) + tries[b][0] = -tries[b][0]; + if (dy2 < 0) + tries[b][1] = -tries[b][1]; + if (dy1 < 0) + tries[b][2] = -tries[b][2]; + if (dx2 < 0) + tries[b][3] = -tries[b][3]; + + dir = igetcvdir(ge); + ge->ix2 -= tries[b][0]; + ge->iy2 -= tries[b][2]; + fixcvdir(ge, dir); + dir = igetcvdir(ne); + ne->ix1 += tries[b][3]; + ne->iy1 += tries[b][1]; + fixcvdir(ne, dir); + } + } + } +} + +/* debugging: print out stems of a glyph */ +static void +debugstems( + char *name, + STEM * hstems, + int nhs, + STEM * vstems, + int nvs +) +{ + int i; + + fprintf(pfa_file, "%% %s\n", name); + fprintf(pfa_file, "%% %d horizontal stems:\n", nhs); + for (i = 0; i < nhs; i++) + fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c%c%c\n", i, hstems[i].value, + hstems[i].from, hstems[i].to, + ((hstems[i].flags & ST_UP) ? 'U' : 'D'), + ((hstems[i].flags & ST_END) ? 'E' : '-'), + ((hstems[i].flags & ST_FLAT) ? 'F' : '-'), + ((hstems[i].flags & ST_ZONE) ? 'Z' : ' '), + ((hstems[i].flags & ST_TOPZONE) ? 'T' : ' ')); + fprintf(pfa_file, "%% %d vertical stems:\n", nvs); + for (i = 0; i < nvs; i++) + fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c\n", i, vstems[i].value, + vstems[i].from, vstems[i].to, + ((vstems[i].flags & ST_UP) ? 'U' : 'D'), + ((vstems[i].flags & ST_END) ? 'E' : '-'), + ((vstems[i].flags & ST_FLAT) ? 'F' : '-')); +} + +/* add pseudo-stems for the limits of the Blue zones to the stem array */ +static int +addbluestems( + STEM *s, + int n +) +{ + int i; + + for(i=0; i + (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT) + ) + continue; + } else { + if( + (s[i].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT) + < + (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT) + ) + continue; + } + } + } + x = s[j]; + s[j] = s[i]; + s[i] = x; + } +} + +/* check whether two stem borders overlap */ + +static int +stemoverlap( + STEM * s1, + STEM * s2 +) +{ + int result; + + if (s1->from <= s2->from && s1->to >= s2->from + || s2->from <= s1->from && s2->to >= s1->from) + result = 1; + else + result = 0; + + if (ISDBG(STEMOVERLAP)) + fprintf(pfa_file, "%% overlap %d(%d..%d)x%d(%d..%d)=%d\n", + s1->value, s1->from, s1->to, s2->value, s2->from, s2->to, result); + return result; +} + +/* + * check if the stem [border] is in an appropriate blue zone + * (currently not used) + */ + +static int +steminblue( + STEM *s +) +{ + int i, val; + + val=s->value; + if(s->flags & ST_UP) { + /* painted size up, look at lower zones */ + if(nblues>=2 && val>=bluevalues[0] && val<=bluevalues[1] ) + return 1; + for(i=0; i=otherblues[i] && val<=otherblues[i+1] ) + return 1; + } + } else { + /* painted side down, look at upper zones */ + for(i=2; i=bluevalues[i] && val<=bluevalues[i+1] ) + return 1; + } + } + + return 0; +} + +/* mark the outermost stem [borders] in the blue zones */ + +static void +markbluestems( + STEM *s, + int nold +) +{ + int i, j, a, b, c; + /* + * traverse the list of Blue Values, mark the lowest upper + * stem in each bottom zone and the topmost lower stem in + * each top zone with ST_BLUE + */ + + /* top zones */ + for(i=2; i=0; j--) { + if( s[j].flags & (ST_ZONE|ST_UP|ST_END) ) + continue; + c=s[j].value; + if(c=0 && s[j].value==c + && (s[j].flags & (ST_UP|ST_ZONE))==0 ; j--) + s[j].flags |= ST_BLUE; + break; + } + } + } + /* baseline */ + if(nblues>=2) { + a=bluevalues[0]; b=bluevalues[1]; + for(j=0; jb) /* too high */ + break; + if(c>=a) { /* found the lowest stem border */ + /* mark all the stems with the same value */ + if(ISDBG(BLUESTEMS)) + fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value); + /* include ST_END values */ + while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 ) + j--; + s[j].flags |= ST_BLUE; + for(j++; jb) /* too high */ + break; + if(c>=a) { /* found the lowest stem border */ + /* mark all the stems with the same value */ + if(ISDBG(BLUESTEMS)) + fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value); + /* include ST_END values */ + while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 ) + j--; + s[j].flags |= ST_BLUE; + for(j++; j=b) { /* have no free space */ + for(j=nold; j>=b; j--) /* make free space */ + s[j]=s[j-1]; + b++; + nold++; + } + s[nnew]=s[a]; + s[nnew].flags &= ~(ST_UP|ST_BLUE); + nnew++; + i=b-1; + } else { + s[nnew++]=s[c]; + i=c; /* skip up to this point */ + } + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d U BLUE\n", + s[nnew-2].value, s[nnew-1].value); + } else { + if (nstack >= MAX_STACK) { + WARNING_1 fprintf(stderr, "Warning: **** converter's stem stack overflow ****\n"); + nstack = 0; + } + stack[nstack++] = s[i]; + } + } else if(s[i].flags & ST_BLUE) { + /* again, we just HAVE to use this value */ + if (readystem) + nnew += 2; + readystem=0; + + /* remember the list of Blue zone stems with the same value */ + for(a=i, i++; i= 0; i--) { + if( (stack[i].flags & ST_UP)==0 ) { + if( (stack[i].flags & (ST_ZONE|ST_TOPZONE))==ST_ZONE ) + break; + else + continue; + } + for(j=a; j=0; j-=2) { + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% ?stem %d...%d -- %d\n", + s[j].value, s[j+1].value, stack[c].value); + if(s[j+1].value < stack[c].value) /* no conflict */ + break; + if(s[j].flags & ST_BLUE) { + /* oops, we don't want to spoil other blue zones */ + stack[c].value=s[j+1].value+1; + break; + } + if( (s[j].flags|s[j+1].flags) & ST_END ) { + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% -stem %d...%d p=1\n", + s[j].value, s[j+1].value); + continue; /* pri==1, silently discard it */ + } + /* we want to discard no nore than 2 stems of pri>=2 */ + if( ++readystem > 2 ) { + /* change our stem to not conflict */ + stack[c].value=s[j+1].value+1; + break; + } else { + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% -stem %d...%d p>=2\n", + s[j].value, s[j+1].value); + continue; + } + } + nnew=j+2; + /* add this stem */ + if(nnew>=b-1) { /* have no free space */ + for(j=nold; j>=b-1; j--) /* make free space */ + s[j]=s[j-1]; + b++; + nold++; + } + s[nnew++]=stack[c]; + s[nnew++]=s[b-1]; + /* clean up the stack */ + nstack=sbottom=0; + readystem=0; + /* set the next position to search */ + i=b-1; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d D BLUE\n", + s[nnew-2].value, s[nnew-1].value); + } else if (nstack > 0) { + + /* + * check whether our stem overlaps with anything in + * stack + */ + for (j = nstack - 1; j >= sbottom; j--) { + if (s[i].value <= stack[j].value) + break; + if (stack[j].flags & ST_ZONE) + continue; + + if ((s[i].flags & ST_END) + || (stack[j].flags & ST_END)) + pri = 1; + else if ((s[i].flags & ST_FLAT) + || (stack[j].flags & ST_FLAT)) + pri = 3; + else + pri = 2; + + if (pri < readystem && s[nnew + 1].value >= stack[j].value + || !stemoverlap(&stack[j], &s[i])) + continue; + + if (readystem > 1 && s[nnew + 1].value < stack[j].value) { + nnew += 2; + readystem = 0; + nlps = 0; + } + /* + * width of the previous stem (if it's + * present) + */ + w1 = s[nnew + 1].value - s[nnew].value; + + /* width of this stem */ + w2 = s[i].value - stack[j].value; + + if (readystem == 0) { + /* nothing yet, just add a new stem */ + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + readystem = pri; + if (pri == 1) + nlps = 1; + else if (pri == 2) + sbottom = j; + else { + sbottom = j + 1; + while (sbottom < nstack + && stack[sbottom].value <= stack[j].value) + sbottom++; + } + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } else if (pri == 1) { + if (stack[j].value > s[nnew + 1].value) { + /* + * doesn't overlap with the + * previous one + */ + nnew += 2; + nlps++; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } else if (w2 < w1) { + /* is narrower */ + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d %d->%d\n", + stack[j].value, s[i].value, pri, nlps, w1, w2); + } + } else if (pri == 2) { + if (readystem == 2) { + /* choose the narrower stem */ + if (w1 > w2) { + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + sbottom = j; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + /* else readystem==1 */ + } else if (stack[j].value > s[nnew + 1].value) { + /* + * value doesn't overlap with + * the previous one + */ + nnew += 2; + nlps = 0; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + sbottom = j; + readystem = pri; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } else if (nlps == 1 + || stack[j].value > s[nnew - 1].value) { + /* + * we can replace the top + * stem + */ + nlps = 0; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + readystem = pri; + sbottom = j; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + } else if (readystem == 3) { /* that means also + * pri==3 */ + /* choose the narrower stem */ + if (w1 > w2) { + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + sbottom = j + 1; + while (sbottom < nstack + && stack[sbottom].value <= stack[j].value) + sbottom++; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + } else if (pri == 3) { + /* + * we can replace as many stems as + * neccessary + */ + nnew += 2; + while (nnew > 0 && s[nnew - 1].value >= stack[j].value) { + nnew -= 2; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% -stem %d..%d\n", + s[nnew].value, s[nnew + 1].value); + } + nlps = 0; + s[nnew] = stack[j]; + s[nnew + 1] = s[i]; + readystem = pri; + sbottom = j + 1; + while (sbottom < nstack + && stack[sbottom].value <= stack[j].value) + sbottom++; + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n", + stack[j].value, s[i].value, pri, nlps); + } + } + } + } + if (readystem) + nnew += 2; + + /* change the 1-pixel-wide stems to 20-pixel-wide stems if possible + * the constant 20 is recommended in the Type1 manual + */ + if(useblues) { + for(i=0; ii+2 && s[i+2].value0 && s[i-1].value>s[i].value-22) + s[i].value=s[i-1].value+2; /* compensate for fuzziness */ + else + s[i].value-=20; + } + } + } + /* make sure that no stem it stretched between + * a top zone and a bottom zone + */ + if(useblues) { + for(i=0; i=s[i].value && c<=s[i+1].value && c=2) { + c=bluevalues[1]; + if( c>=s[i].value && c<=s[i+1].value && c>b ) + b=c; + } + for(j=1; j=s[i].value && c<=s[i+1].value && c>b ) + b=c; + } + if( a!=10000 && b!= -10000 ) { /* it is stretched */ + /* split the stem into 2 ghost stems */ + for(j=nnew+1; j>i+1; j--) /* make free space */ + s[j]=s[j-2]; + nnew+=2; + + if(s[i].value+22 >= a) + s[i+1].value=a-2; /* leave space for fuzziness */ + else + s[i+1].value=s[i].value+20; + + if(s[i+3].value-22 <= b) + s[i+2].value=b+2; /* leave space for fuzziness */ + else + s[i+2].value=s[i+3].value-20; + + i+=2; + } + } + } + /* look for triple stems */ + for (i = 0; i < nnew; i += 2) { + if (nnew - i >= 6) { + a = s[i].value + s[i + 1].value; + b = s[i + 2].value + s[i + 3].value; + c = s[i + 4].value + s[i + 5].value; + + w1 = s[i + 1].value - s[i].value; + w2 = s[i + 3].value - s[i + 2].value; + w3 = s[i + 5].value - s[i + 4].value; + + fw = w3 - w1; /* fuzz in width */ + fd = ((c - b) - (b - a)); /* fuzz in distance + * (doubled) */ + + /* we are able to handle some fuzz */ + /* + * it doesn't hurt if the declared stem is a bit + * narrower than actual unless it's an edge in + * a blue zone + */ + if (abs(abs(fd) - abs(fw)) * 5 < w2 + && abs(fw) * 20 < (w1 + w3)) { /* width dirrerence <10% */ + + if(useblues) { /* check that we don't disturb any blue stems */ + j=c; k=a; + if (fw > 0) { + if (fd > 0) { + if( s[i+5].flags & ST_BLUE ) + continue; + j -= fw; + } else { + if( s[i+4].flags & ST_BLUE ) + continue; + j += fw; + } + } else if(fw < 0) { + if (fd > 0) { + if( s[i+1].flags & ST_BLUE ) + continue; + k -= fw; + } else { + if( s[i].flags & ST_BLUE ) + continue; + k += fw; + } + } + pri = ((j - b) - (b - k)); + + if (pri > 0) { + if( s[i+2].flags & ST_BLUE ) + continue; + } else if(pri < 0) { + if( s[i+3].flags & ST_BLUE ) + continue; + } + } + + /* + * first fix up the width of 1st and 3rd + * stems + */ + if (fw > 0) { + if (fd > 0) { + s[i + 5].value -= fw; + c -= fw; + } else { + s[i + 4].value += fw; + c += fw; + } + } else { + if (fd > 0) { + s[i + 1].value -= fw; + a -= fw; + } else { + s[i].value += fw; + a += fw; + } + } + fd = ((c - b) - (b - a)); + + if (fd > 0) { + s[i + 2].value += abs(fd) / 2; + } else { + s[i + 3].value -= abs(fd) / 2; + } + + s[i].flags |= ST_3; + i += 4; + } + } + } + + return (nnew & ~1); /* number of lines must be always even */ +} + +/* + * these macros and function allow to set the base stem, + * check that it's not empty and subtract another stem + * from the base stem (possibly dividing it into multiple parts) + */ + +/* pairs for pieces of the base stem */ +static short xbstem[MAX_STEMS*2]; +/* index of the last point */ +static int xblast= -1; + +#define setbasestem(from, to) \ + (xbstem[0]=from, xbstem[1]=to, xblast=1) +#define isbaseempty() (xblast<=0) + +/* returns 1 if was overlapping, 0 otherwise */ +static int +subfrombase( + int from, + int to +) +{ + int a, b; + int i, j; + + if(isbaseempty()) + return 0; + + /* handle the simple case simply */ + if(from > xbstem[xblast] || to < xbstem[0]) + return 0; + + /* the binary search may be more efficient */ + /* but for now the linear search is OK */ + for(b=1; from > xbstem[b]; b+=2) {} /* result: from <= xbstem[b] */ + for(a=xblast-1; to < xbstem[a]; a-=2) {} /* result: to >= xbstem[a] */ + + /* now the interesting examples are: + * (it was hard for me to understand, so I looked at the examples) + * 1 + * a|-----| |-----|b |-----| |-----| + * f|-----|t + * 2 + * a|-----|b |-----| |-----| |-----| + * f|--|t + * 3 + * a|-----|b |-----| |-----| |-----| + * f|-----|t + * 4 + * |-----|b a|-----| |-----| |-----| + * f|------------|t + * 5 + * |-----| |-----|b |-----| a|-----| + * f|-----------------------------|t + * 6 + * |-----|b |-----| |-----| a|-----| + * f|--------------------------------------------------|t + * 7 + * |-----|b |-----| a|-----| |-----| + * f|--------------------------|t + */ + + if(a < b-1) /* hits a gap - example 1 */ + return 0; + + /* now the subtraction itself */ + + if(a==b-1 && from > xbstem[a] && to < xbstem[b]) { + /* overlaps with only one subrange and splits it - example 2 */ + j=xblast; i=(xblast+=2); + while(j>=b) + xbstem[i--]=xbstem[j--]; + xbstem[b]=from-1; + xbstem[b+1]=to+1; + return 1; + /* becomes + * 2a + * a|b || |-----| |-----| |-----| + * f|--|t + */ + } + + if(xbstem[b-1] < from) { + /* cuts the back of this subrange - examples 3, 4, 7 */ + xbstem[b] = from-1; + b+=2; + /* becomes + * 3a + * a|----| |-----|b |-----| |-----| + * f|-----|t + * 4a + * |---| a|-----|b |-----| |-----| + * f|------------|t + * 7a + * |---| |-----|b a|-----| |-----| + * f|--------------------------|t + */ + } + + if(xbstem[a+1] > to) { + /* cuts the front of this subrange - examples 4a, 5, 7a */ + xbstem[a] = to+1; + a-=2; + /* becomes + * 4b + * a|---| |---|b |-----| |-----| + * f|------------|t + * 5b + * |-----| |-----|b a|-----| || + * f|-----------------------------|t + * 7b + * |---| a|-----|b || |-----| + * f|--------------------------|t + */ + } + + if(a < b-1) /* now after modification it hits a gap - examples 3a, 4b */ + return 1; /* because we have removed something */ + + /* now remove the subranges completely covered by the new stem */ + /* examples 5b, 6, 7b */ + i=b-1; j=a+2; + /* positioned as: + * 5b i j + * |-----| |-----|b a|-----| || + * f|-----------------------------|t + * 6 i xblast j + * |-----|b |-----| |-----| a|-----| + * f|--------------------------------------------------|t + * 7b i j + * |---| a|-----|b || |-----| + * f|--------------------------|t + */ + while(j <= xblast) + xbstem[i++]=xbstem[j++]; + xblast=i-1; + return 1; +} + +/* for debugging */ +static void +printbasestem(void) +{ + int i; + + printf("( "); + for(i=0; i lastpri + && ( lastpri==1 || s[j].value-v<20 || (s[x].value-v)*2 >= s[j].value-v ) ) { + lastpri=pri; + x=j; + } + } + } else { + for(j=i-1; j>=0; j--) { + if(mx[i][j]==0) + continue; + + if( (f | s[j].flags) & ST_END ) + pri=1; + else if( (f | s[j].flags) & ST_FLAT ) + pri=3; + else + pri=2; + + if(lastpri==0 + || pri > lastpri + && ( lastpri==1 || v-s[j].value<20 || (v-s[x].value)*2 >= v-s[j].value ) ) { + lastpri=pri; + x=j; + } + } + } + if(x== -1 && mx[i][i]) + pairs[i]=i; /* a special case */ + else + pairs[i]=x; + } + + if(ISDBG(SUBSTEMS)) { + for(i=0; i0) + fprintf(pfa_file, "%% %d...%d (%d x %d)\n", s[i].value, s[j].value, i, j); + } + } +} + +/* + * Make all the stems originating at the same value get the + * same width. Without this the rasterizer may move the dots + * randomly up or down by one pixel, and that looks bad. + * The prioritisation is the same as in findstemat(). + */ +static void +uniformstems( + STEM * s, + short *pairs, + int ns +) +{ + int i, j, from, to, val, dir; + int pri, prevpri[2], wd, prevwd[2], prevbest[2]; + + for(from=0; from prevpri[dir] || wd= 0) { + if(ISDBG(SUBSTEMS)) { + fprintf(stderr, "at %d (%s %d) pair %d->%d(%d)\n", i, + (dir ? "UP":"DOWN"), s[i].value, pairs[i], prevbest[dir], + s[prevbest[dir]].value); + } + pairs[i] = prevbest[dir]; + } + } + } +} + +/* + * Find the best stem in the array at the specified (value, origin), + * related to the entry ge. + * Returns its index in the array sp, -1 means "none". + * prevbest is the result for the other end of the line, we must + * find something better than it or leave it as it is. + */ +static int +findstemat( + int value, + int origin, + GENTRY *ge, + STEM *sp, + short *pairs, + int ns, + int prevbest /* -1 means "none" */ +) +{ + int i, min, max; + int v, si; + int pri, prevpri; /* priority, 0 = has ST_END, 1 = no ST_END */ + int wd, prevwd; /* stem width */ + + si= -1; /* nothing yet */ + + /* stems are ordered by value, binary search */ + min=0; max=ns; /* min <= i < max */ + while( min < max ) { + i=(min+max)/2; + v=sp[i].value; + if(vvalue) + max=i; + else { + si=i; /* temporary value */ + break; + } + } + + if( si < 0 ) /* found nothing this time */ + return prevbest; + + /* find the priority of the prevbest */ + /* we expect that prevbest has a pair */ + if(prevbest>=0) { + i=pairs[prevbest]; + prevpri=1; + if( (sp[prevbest].flags | sp[i].flags) & ST_END ) + prevpri=0; + prevwd=abs(sp[i].value-value); + } + + /* stems are not ordered by origin, so now do the linear search */ + + while( si>0 && sp[si-1].value==value ) /* find the first one */ + si--; + + for(; siprevpri + || pri==prevpri && prevwd==0 || wd!=0 && wdprev->ix3; + y=ge->prev->iy3; + + if(*nextvsi == -2) + si[SI_VP]=findstemat(x, y, ge, vs, vpairs, nvs, -1); + else { + si[SI_VP]= *nextvsi; *nextvsi= -2; + } + if(*nexthsi == -2) + si[SI_HP]=findstemat(y, x, ge, hs, hpairs, nhs, -1); + else { + si[SI_HP]= *nexthsi; *nexthsi= -2; + } + + /* + * For the horizontal lines we make sure that both + * ends of the line have the same horizontal stem, + * and the same thing for vertical lines and stems. + * In both cases we enforce the stem for the next entry. + * Otherwise unpleasant effects may arise. + */ + + if(ge->type==GE_LINE) { + if(ge->ix3==x) { /* vertical line */ + *nextvsi=si[SI_VP]=findstemat(x, ge->iy3, ge->frwd, vs, vpairs, nvs, si[SI_VP]); + } else if(ge->iy3==y) { /* horizontal line */ + *nexthsi=si[SI_HP]=findstemat(y, ge->ix3, ge->frwd, hs, hpairs, nhs, si[SI_HP]); + } + } + + if(si[SI_VP]+si[SI_HP] == -2) /* no stems, leave it alone */ + return 0; + + /* build the array of relevant bounds */ + nr=0; + for(i=0; i< sizeof(si) / sizeof(si[0]); i++) { + STEM *sp; + short *pairs; + int step; + int f; + int nzones, firstzone, binzone, einzone; + int btype, etype; + + if(si[i] < 0) + continue; + + if(i r[nr].high) { + j=r[nr].low; r[nr].low=r[nr].high; r[nr].high=j; + step= -1; + } else { + step=1; + } + + /* handle the interaction with Blue Zones */ + + if(i>=SI_HP) { /* only for horizontal stems */ + if(si[i]==pairs[si[i]]) { + /* special case, the outermost stem in the + * Blue Zone without a pair, simulate it to 20-pixel + */ + if(sp[ si[i] ].flags & ST_UP) { + r[nr].high+=20; + for(j=si[i]+1; j sp[j].value-2) + r[nr].high=sp[j].value-2; + break; + } + } else { + r[nr].low-=20; + for(j=si[i]-1; j>=0; j--) + if( (sp[j].flags & (ST_ZONE|ST_TOPZONE)) + == (ST_ZONE) ) { + if(r[nr].low < sp[j].value+2) + r[nr].low=sp[j].value+2; + break; + } + } + } + + /* check that the stem borders don't end up in + * different Blue Zones */ + f=sp[ si[i] ].flags; + nzones=0; einzone=binzone=0; + for(j=si[i]; j!=pairs[ si[i] ]; j+=step) { + if( (sp[j].flags & ST_ZONE)==0 ) + continue; + /* if see a zone border going in the same direction */ + if( ((f ^ sp[j].flags) & ST_UP)==0 ) { + if( ++nzones == 1 ) { + firstzone=sp[j].value; /* remember the first one */ + etype=sp[j].flags & ST_TOPZONE; + } + einzone=1; + + } else { /* the opposite direction */ + if(nzones==0) { /* beginning is in a blue zone */ + binzone=1; + btype=sp[j].flags & ST_TOPZONE; + } + einzone=0; + } + } + + /* beginning and end are in Blue Zones of different types */ + if( binzone && einzone && (btype ^ etype)!=0 ) { + if( sp[si[i]].flags & ST_UP ) { + if(firstzone > r[nr].low+22) + r[nr].high=r[nr].low+20; + else + r[nr].high=firstzone-2; + } else { + if(firstzone < r[nr].high-22) + r[nr].low=r[nr].high-20; + else + r[nr].low=firstzone+2; + } + } + } + + if(ISDBG(SUBSTEMS)) + fprintf(pfa_file, "%% at(%d,%d)[%d,%d] %d..%d %c (%d x %d)\n", x, y, i, nr, + r[nr].low, r[nr].high, r[nr].isvert ? 'v' : 'h', + si[i], pairs[si[i]]); + + nr++; + } + + /* now try to find a group */ + conflict=0; /* no conflicts found yet */ + for(j=0; j= lb ) { + if( r[j].low == lb && r[j].high == hb ) /* coincides */ + r[j].already=1; + else + conflict=1; + } + + if(conflict) + break; + } + + if(conflict) { /* nope, check all the groups */ + for(j=0; j= lb ) { + if( r[j].low == lb && r[j].high == hb ) /* coincides */ + r[j].already=1; + else + conflict=1; + } + + if(conflict) + i=egp[grp]-1; /* fast forward to the next group */ + } + } + + /* do we have any empty group ? */ + if(conflict && grp < NSTEMGRP-1) { + grp++; conflict=0; + for(j=0; j 0) { + for(i=egp[NSTEMGRP-1]-1; i>=egp[grp]; i--) + s[i+rexpand]=s[i]; + for(i=0; istemid = gssentry_lastgrp = grp; + return 0; +} + +/* + * Create the groups of substituted stems from the list. + * Each group will be represented by a subroutine in the Subs + * array. + */ + +static void +groupsubstems( + GLYPH *g, + STEM *hs, /* horizontal stems, sorted by value */ + short *hpairs, + int nhs, + STEM *vs, /* vertical stems, sorted by value */ + short *vpairs, + int nvs +) +{ + GENTRY *ge; + int i, j; + + /* temporary storage */ + STEMBOUNDS s[MAX_STEMS*2]; + /* indexes in there, pointing past the end each stem group */ + short egp[NSTEMGRP]; + + int nextvsi, nexthsi; /* -2 means "check by yourself" */ + + for(i=0; ientries; ge != 0; ge = ge->next) { + if(ge->type!=GE_LINE && ge->type!=GE_CURVE) { + nextvsi=nexthsi= -2; /* next path is independent */ + continue; + } + + if( gssentry(ge, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) { + WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n", + g->name, NSTEMGRP); + /* it's better to have no substituted hints at all than have only part */ + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid= -1; + g->nsg=0; /* just to be safe, already is 0 by initialization */ + return; + } + + /* + * handle the last vert/horiz line of the path specially, + * correct the hint for the first entry of the path + */ + if(ge->frwd != ge->next && (nextvsi != -2 || nexthsi != -2) ) { + if( gssentry(ge->frwd, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) { + WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n", + g->name, NSTEMGRP); + /* it's better to have no substituted hints at all than have only part */ + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid= -1; + g->nsg=0; /* just to be safe, already is 0 by initialization */ + return; + } + } + + } + + /* find the index of the first empty group - same as the number of groups */ + if(egp[0]>0) { + for(i=1; insg=i; + } else + g->nsg=0; + + if(ISDBG(SUBSTEMS)) { + fprintf(pfa_file, "%% %d substem groups (%d %d %d)\n", g->nsg, + g->nsg>1 ? egp[g->nsg-2] : -1, + g->nsg>0 ? egp[g->nsg-1] : -1, + g->nsgnsg] : -1 ); + j=0; + for(i=0; insg; i++) { + fprintf(pfa_file, "%% grp %3d: ", i); + for(; jnsg==1) { /* it would be the same as the main stems */ + /* so erase it */ + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid= -1; + g->nsg=0; + } + + if(g->nsg>0) { + if( (g->nsbs=malloc(g->nsg * sizeof (egp[0]))) == 0 ) { + fprintf(stderr, "**** not enough memory for substituted hints ****\n"); + exit(255); + } + memmove(g->nsbs, egp, g->nsg * sizeof(short)); + if( (g->sbstems=malloc(egp[g->nsg-1] * sizeof (s[0]))) == 0 ) { + fprintf(stderr, "**** not enough memory for substituted hints ****\n"); + exit(255); + } + memmove(g->sbstems, s, egp[g->nsg-1] * sizeof(s[0])); + } +} + +void +buildstems( + GLYPH * g +) +{ + STEM hs[MAX_STEMS], vs[MAX_STEMS]; /* temporary working + * storage */ + short hs_pairs[MAX_STEMS], vs_pairs[MAX_STEMS]; /* best pairs for these stems */ + STEM *sp; + GENTRY *ge, *nge, *pge; + int nx, ny; + int ovalue; + int totals, grp, lastgrp; + + assertisint(g, "buildstems int"); + + g->nhs = g->nvs = 0; + memset(hs, 0, sizeof hs); + memset(vs, 0, sizeof vs); + + /* first search the whole character for possible stem points */ + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_CURVE) { + + /* + * SURPRISE! + * We consider the stems bound by the + * H/V ends of the curves as flat ones. + * + * But we don't include the point on the + * other end into the range. + */ + + /* first check the beginning of curve */ + /* if it is horizontal, add a hstem */ + if (ge->iy1 == ge->prev->iy3) { + hs[g->nhs].value = ge->iy1; + + if (ge->ix1 < ge->prev->ix3) + hs[g->nhs].flags = ST_FLAT | ST_UP; + else + hs[g->nhs].flags = ST_FLAT; + + hs[g->nhs].origin = ge->prev->ix3; + hs[g->nhs].ge = ge; + + if (ge->ix1 < ge->prev->ix3) { + hs[g->nhs].from = ge->ix1+1; + hs[g->nhs].to = ge->prev->ix3; + if(hs[g->nhs].from > hs[g->nhs].to) + hs[g->nhs].from--; + } else { + hs[g->nhs].from = ge->prev->ix3; + hs[g->nhs].to = ge->ix1-1; + if(hs[g->nhs].from > hs[g->nhs].to) + hs[g->nhs].to++; + } + if (ge->ix1 != ge->prev->ix3) + g->nhs++; + } + /* if it is vertical, add a vstem */ + else if (ge->ix1 == ge->prev->ix3) { + vs[g->nvs].value = ge->ix1; + + if (ge->iy1 > ge->prev->iy3) + vs[g->nvs].flags = ST_FLAT | ST_UP; + else + vs[g->nvs].flags = ST_FLAT; + + vs[g->nvs].origin = ge->prev->iy3; + vs[g->nvs].ge = ge; + + if (ge->iy1 < ge->prev->iy3) { + vs[g->nvs].from = ge->iy1+1; + vs[g->nvs].to = ge->prev->iy3; + if(vs[g->nvs].from > vs[g->nvs].to) + vs[g->nvs].from--; + } else { + vs[g->nvs].from = ge->prev->iy3; + vs[g->nvs].to = ge->iy1-1; + if(vs[g->nvs].from > vs[g->nvs].to) + vs[g->nvs].to++; + } + + if (ge->iy1 != ge->prev->iy3) + g->nvs++; + } + /* then check the end of curve */ + /* if it is horizontal, add a hstem */ + if (ge->iy3 == ge->iy2) { + hs[g->nhs].value = ge->iy3; + + if (ge->ix3 < ge->ix2) + hs[g->nhs].flags = ST_FLAT | ST_UP; + else + hs[g->nhs].flags = ST_FLAT; + + hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if (ge->ix3 < ge->ix2) { + hs[g->nhs].from = ge->ix3; + hs[g->nhs].to = ge->ix2-1; + if( hs[g->nhs].from > hs[g->nhs].to ) + hs[g->nhs].to++; + } else { + hs[g->nhs].from = ge->ix2+1; + hs[g->nhs].to = ge->ix3; + if( hs[g->nhs].from > hs[g->nhs].to ) + hs[g->nhs].from--; + } + + if (ge->ix3 != ge->ix2) + g->nhs++; + } + /* if it is vertical, add a vstem */ + else if (ge->ix3 == ge->ix2) { + vs[g->nvs].value = ge->ix3; + + if (ge->iy3 > ge->iy2) + vs[g->nvs].flags = ST_FLAT | ST_UP; + else + vs[g->nvs].flags = ST_FLAT; + + vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if (ge->iy3 < ge->iy2) { + vs[g->nvs].from = ge->iy3; + vs[g->nvs].to = ge->iy2-1; + if( vs[g->nvs].from > vs[g->nvs].to ) + vs[g->nvs].to++; + } else { + vs[g->nvs].from = ge->iy2+1; + vs[g->nvs].to = ge->iy3; + if( vs[g->nvs].from > vs[g->nvs].to ) + vs[g->nvs].from--; + } + + if (ge->iy3 != ge->iy2) + g->nvs++; + } else { + + /* + * check the end of curve for a not smooth + * local extremum + */ + nge = ge->frwd; + + if (nge == 0) + continue; + else if (nge->type == GE_LINE) { + nx = nge->ix3; + ny = nge->iy3; + } else if (nge->type == GE_CURVE) { + nx = nge->ix1; + ny = nge->iy1; + } else + continue; + + /* check for vertical extremums */ + if (ge->iy3 > ge->iy2 && ge->iy3 > ny + || ge->iy3 < ge->iy2 && ge->iy3 < ny) { + hs[g->nhs].value = ge->iy3; + hs[g->nhs].from + = hs[g->nhs].to + = hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if (ge->ix3 < ge->ix2 + || nx < ge->ix3) + hs[g->nhs].flags = ST_UP; + else + hs[g->nhs].flags = 0; + + if (ge->ix3 != ge->ix2 || nx != ge->ix3) + g->nhs++; + } + /* + * the same point may be both horizontal and + * vertical extremum + */ + /* check for horizontal extremums */ + if (ge->ix3 > ge->ix2 && ge->ix3 > nx + || ge->ix3 < ge->ix2 && ge->ix3 < nx) { + vs[g->nvs].value = ge->ix3; + vs[g->nvs].from + = vs[g->nvs].to + = vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if (ge->iy3 > ge->iy2 + || ny > ge->iy3) + vs[g->nvs].flags = ST_UP; + else + vs[g->nvs].flags = 0; + + if (ge->iy3 != ge->iy2 || ny != ge->iy3) + g->nvs++; + } + } + + } else if (ge->type == GE_LINE) { + nge = ge->frwd; + + /* if it is horizontal, add a hstem */ + /* and the ends as vstems if they brace the line */ + if (ge->iy3 == ge->prev->iy3 + && ge->ix3 != ge->prev->ix3) { + hs[g->nhs].value = ge->iy3; + if (ge->ix3 < ge->prev->ix3) { + hs[g->nhs].flags = ST_FLAT | ST_UP; + hs[g->nhs].from = ge->ix3; + hs[g->nhs].to = ge->prev->ix3; + } else { + hs[g->nhs].flags = ST_FLAT; + hs[g->nhs].from = ge->prev->ix3; + hs[g->nhs].to = ge->ix3; + } + hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + pge = ge->bkwd; + + /* add beginning as vstem */ + vs[g->nvs].value = pge->ix3; + vs[g->nvs].origin + = vs[g->nvs].from + = vs[g->nvs].to = pge->iy3; + vs[g->nvs].ge = ge; + + if(pge->type==GE_CURVE) + ovalue=pge->iy2; + else + ovalue=pge->prev->iy3; + + if (pge->iy3 > ovalue) + vs[g->nvs].flags = ST_UP | ST_END; + else if (pge->iy3 < ovalue) + vs[g->nvs].flags = ST_END; + else + vs[g->nvs].flags = 0; + + if( vs[g->nvs].flags != 0 ) + g->nvs++; + + /* add end as vstem */ + vs[g->nvs].value = ge->ix3; + vs[g->nvs].origin + = vs[g->nvs].from + = vs[g->nvs].to = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if(nge->type==GE_CURVE) + ovalue=nge->iy1; + else + ovalue=nge->iy3; + + if (ovalue > ge->iy3) + vs[g->nvs].flags = ST_UP | ST_END; + else if (ovalue < ge->iy3) + vs[g->nvs].flags = ST_END; + else + vs[g->nvs].flags = 0; + + if( vs[g->nvs].flags != 0 ) + g->nvs++; + + g->nhs++; + } + /* if it is vertical, add a vstem */ + /* and the ends as hstems if they brace the line */ + else if (ge->ix3 == ge->prev->ix3 + && ge->iy3 != ge->prev->iy3) { + vs[g->nvs].value = ge->ix3; + if (ge->iy3 > ge->prev->iy3) { + vs[g->nvs].flags = ST_FLAT | ST_UP; + vs[g->nvs].from = ge->prev->iy3; + vs[g->nvs].to = ge->iy3; + } else { + vs[g->nvs].flags = ST_FLAT; + vs[g->nvs].from = ge->iy3; + vs[g->nvs].to = ge->prev->iy3; + } + vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + pge = ge->bkwd; + + /* add beginning as hstem */ + hs[g->nhs].value = pge->iy3; + hs[g->nhs].origin + = hs[g->nhs].from + = hs[g->nhs].to = pge->ix3; + hs[g->nhs].ge = ge; + + if(pge->type==GE_CURVE) + ovalue=pge->ix2; + else + ovalue=pge->prev->ix3; + + if (pge->ix3 < ovalue) + hs[g->nhs].flags = ST_UP | ST_END; + else if (pge->ix3 > ovalue) + hs[g->nhs].flags = ST_END; + else + hs[g->nhs].flags = 0; + + if( hs[g->nhs].flags != 0 ) + g->nhs++; + + /* add end as hstem */ + hs[g->nhs].value = ge->iy3; + hs[g->nhs].origin + = hs[g->nhs].from + = hs[g->nhs].to = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if(nge->type==GE_CURVE) + ovalue=nge->ix1; + else + ovalue=nge->ix3; + + if (ovalue < ge->ix3) + hs[g->nhs].flags = ST_UP | ST_END; + else if (ovalue > ge->ix3) + hs[g->nhs].flags = ST_END; + else + hs[g->nhs].flags = 0; + + if( hs[g->nhs].flags != 0 ) + g->nhs++; + + g->nvs++; + } + /* + * check the end of line for a not smooth local + * extremum + */ + nge = ge->frwd; + + if (nge == 0) + continue; + else if (nge->type == GE_LINE) { + nx = nge->ix3; + ny = nge->iy3; + } else if (nge->type == GE_CURVE) { + nx = nge->ix1; + ny = nge->iy1; + } else + continue; + + /* check for vertical extremums */ + if (ge->iy3 > ge->prev->iy3 && ge->iy3 > ny + || ge->iy3 < ge->prev->iy3 && ge->iy3 < ny) { + hs[g->nhs].value = ge->iy3; + hs[g->nhs].from + = hs[g->nhs].to + = hs[g->nhs].origin = ge->ix3; + hs[g->nhs].ge = ge->frwd; + + if (ge->ix3 < ge->prev->ix3 + || nx < ge->ix3) + hs[g->nhs].flags = ST_UP; + else + hs[g->nhs].flags = 0; + + if (ge->ix3 != ge->prev->ix3 || nx != ge->ix3) + g->nhs++; + } + /* + * the same point may be both horizontal and vertical + * extremum + */ + /* check for horizontal extremums */ + if (ge->ix3 > ge->prev->ix3 && ge->ix3 > nx + || ge->ix3 < ge->prev->ix3 && ge->ix3 < nx) { + vs[g->nvs].value = ge->ix3; + vs[g->nvs].from + = vs[g->nvs].to + = vs[g->nvs].origin = ge->iy3; + vs[g->nvs].ge = ge->frwd; + + if (ge->iy3 > ge->prev->iy3 + || ny > ge->iy3) + vs[g->nvs].flags = ST_UP; + else + vs[g->nvs].flags = 0; + + if (ge->iy3 != ge->prev->iy3 || ny != ge->iy3) + g->nvs++; + } + } + } + + g->nhs=addbluestems(hs, g->nhs); + sortstems(hs, g->nhs); + sortstems(vs, g->nvs); + + if (ISDBG(STEMS)) + debugstems(g->name, hs, g->nhs, vs, g->nvs); + + /* find the stems interacting with the Blue Zones */ + markbluestems(hs, g->nhs); + + if(subhints) { + if (ISDBG(SUBSTEMS)) + fprintf(pfa_file, "%% %s: joining subst horizontal stems\n", g->name); + joinsubstems(hs, hs_pairs, g->nhs, 1); + uniformstems(hs, hs_pairs, g->nhs); + + if (ISDBG(SUBSTEMS)) + fprintf(pfa_file, "%% %s: joining subst vertical stems\n", g->name); + joinsubstems(vs, vs_pairs, g->nvs, 0); + + groupsubstems(g, hs, hs_pairs, g->nhs, vs, vs_pairs, g->nvs); + } + + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% %s: joining main horizontal stems\n", g->name); + g->nhs = joinmainstems(hs, g->nhs, 1); + if (ISDBG(MAINSTEMS)) + fprintf(pfa_file, "%% %s: joining main vertical stems\n", g->name); + g->nvs = joinmainstems(vs, g->nvs, 0); + + if (ISDBG(MAINSTEMS)) + debugstems(g->name, hs, g->nhs, vs, g->nvs); + + if(g->nhs > 0) { + if ((sp = malloc(sizeof(STEM) * g->nhs)) == 0) { + fprintf(stderr, "**** not enough memory for hints ****\n"); + exit(255); + } + g->hstems = sp; + memcpy(sp, hs, sizeof(STEM) * g->nhs); + } else + g->hstems = 0; + + if(g->nvs > 0) { + if ((sp = malloc(sizeof(STEM) * g->nvs)) == 0) { + fprintf(stderr, "**** not enough memory for hints ****\n"); + exit(255); + } + g->vstems = sp; + memcpy(sp, vs, sizeof(STEM) * g->nvs); + } else + g->vstems = 0; + + /* now check that the stems won't overflow the interpreter's stem stack: + * some interpreters (like X11) push the stems on each change into + * stack and pop them only after the whole glyphs is completed. + */ + + totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */ + lastgrp = -1; + + for (ge = g->entries; ge != 0; ge = ge->next) { + grp=ge->stemid; + if(grp >= 0 && grp != lastgrp) { + if(grp==0) + totals += g->nsbs[0]; + else + totals += g->nsbs[grp] - g->nsbs[grp-1]; + + lastgrp = grp; + } + } + + /* be on the safe side, check for >= , not > */ + if(totals >= max_stemdepth) { /* oops, too deep */ + WARNING_2 { + fprintf(stderr, "Warning: glyph %s needs hint stack depth %d\n", g->name, totals); + fprintf(stderr, " (limit %d): removed the substituted hints from it\n", max_stemdepth); + } + if(g->nsg > 0) { + for (ge = g->entries; ge != 0; ge = ge->next) + ge->stemid = -1; + free(g->sbstems); g->sbstems = 0; + free(g->nsbs); g->nsbs = 0; + g->nsg = 0; + } + } + + /* now check if there are too many main stems */ + totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */ + if(totals >= max_stemdepth) { + /* even worse, too much of non-substituted stems */ + WARNING_2 { + fprintf(stderr, "Warning: glyph %s has %d main hints\n", g->name, totals); + fprintf(stderr, " (limit %d): removed the hints from it\n", max_stemdepth); + } + if(g->vstems) { + free(g->vstems); g->vstems = 0; g->nvs = 0; + } + if(g->hstems) { + free(g->hstems); g->hstems = 0; g->nhs = 0; + } + } +} + +/* convert weird curves that are close to lines into lines. +*/ + +void +fstraighten( + GLYPH * g +) +{ + GENTRY *ge, *pge, *nge, *ige; + double df; + int dir; + double iln, oln; + int svdir, i, o; + + for (ige = g->entries; ige != 0; ige = ige->next) { + if (ige->type != GE_CURVE) + continue; + + ge = ige; + pge = ge->bkwd; + nge = ge->frwd; + + df = 0.; + + /* look for vertical then horizontal */ + for(i=0; i<2; i++) { + o = !i; /* other axis */ + + iln = fabs(ge->fpoints[i][2] - pge->fpoints[i][2]); + oln = fabs(ge->fpoints[o][2] - pge->fpoints[o][2]); + /* + * if current curve is almost a vertical line, and it + * doesn't begin or end horizontally (and the prev/next + * line doesn't join smoothly ?) + */ + if( oln < 1. + || ge->fpoints[o][2] == ge->fpoints[o][1] + || ge->fpoints[o][0] == pge->fpoints[o][2] + || iln > 2. + || iln > 1. && iln/oln > 0.1 ) + continue; + + + if(ISDBG(STRAIGHTEN)) + fprintf(stderr,"** straighten almost %s\n", (i? "horizontal":"vertical")); + + df = ge->fpoints[i][2] - pge->fpoints[i][2]; + dir = fsign(ge->fpoints[o][2] - pge->fpoints[o][2]); + ge->type = GE_LINE; + + /* + * suck in all the sequence of such almost lines + * going in the same direction but not deviating + * too far from vertical + */ + iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]); + oln = nge->fpoints[o][2] - ge->fpoints[o][2]; + + while (fabs(df) <= 5 && nge->type == GE_CURVE + && dir == fsign(oln) /* that also gives oln != 0 */ + && iln <= 2. + && ( iln <= 1. || iln/fabs(oln) <= 0.1 ) ) { + ge->fx3 = nge->fx3; + ge->fy3 = nge->fy3; + + if(ISDBG(STRAIGHTEN)) + fprintf(stderr,"** straighten collapsing %s\n", (i? "horizontal":"vertical")); + freethisge(nge); + fixendpath(ge); + pge = ge->bkwd; + nge = ge->frwd; + + df = ge->fpoints[i][2] - pge->fpoints[i][2]; + + iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]); + oln = nge->fpoints[o][2] - ge->fpoints[o][2]; + } + + /* now check what do we have as previous/next line */ + + if(ge != pge) { + if( pge->type == GE_LINE && pge->fpoints[i][2] == pge->prev->fpoints[i][2] + && fabs(pge->fpoints[o][2] != pge->prev->fpoints[o][2]) ) { + if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with previous 0x%x 0x%x\n", pge, ge); + /* join the previous line with current */ + pge->fx3 = ge->fx3; + pge->fy3 = ge->fy3; + + ige = freethisge(ge)->prev; /* keep the iterator valid */ + ge = pge; + fixendpath(ge); + pge = ge->bkwd; + } + } + + if(ge != nge) { + if (nge->type == GE_LINE && nge->fpoints[i][2] == ge->fpoints[i][2] + && fabs(nge->fpoints[o][2] != ge->fpoints[o][2]) ) { + if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with next 0x%x 0x%x\n", ge, nge); + /* join the next line with current */ + ge->fx3 = nge->fx3; + ge->fy3 = nge->fy3; + + freethisge(nge); + fixendpath(ge); + pge = ge->bkwd; + nge = ge->frwd; + + } + } + + if(ge != pge) { + /* try to align the lines if neccessary */ + if(df != 0.) + fclosegap(ge, ge, i, df, NULL); + } else { + /* contour consists of only one line, get rid of it */ + ige = freethisge(ge)->prev; /* keep the iterator valid */ + } + + break; /* don't bother looking at the other axis */ + } + } +} + +/* solve a square equation, + * returns the number of solutions found, the solutions + * are stored in res which should point to array of two doubles. + * min and max limit the area for solutions + */ + +static int +fsqequation( + double a, + double b, + double c, + double *res, + double min, + double max +) +{ + double D; + int n; + + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq(%g,%g,%g) [%g;%g]\n", a, b, c, min, max); + + if(fabs(a) < 0.000001) { /* if a linear equation */ + n=0; + if(fabs(b) < 0.000001) /* not an equation at all */ + return 0; + res[0] = -c/b; + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: linear t=%g\n", res[0]); + if(res[0] >= min && res[0] <= max) + n++; + return n; + } + + D = b*b - 4.0*a*c; + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: D=%g\n", D); + if(D<0) + return 0; + + D = sqrt(D); + + n=0; + res[0] = (-b+D) / (2*a); + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t1=%g\n", res[0]); + if(res[0] >= min && res[0] <= max) + n++; + + res[n] = (-b-D) / (2*a); + if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t2=%g\n", res[n]); + if(res[n] >= min && res[n] <= max) + n++; + + /* return 2nd solution only if it's different enough */ + if(n==2 && fabs(res[0]-res[1])<0.000001) + n=1; + + return n; +} + +/* check that the curves don't cross quadrant boundary */ +/* (float) */ + +/* + Here we make sure that the curve does not continue past + horizontal or vertical extremums. The horizontal points are + explained, vertical points are by analogy. + + The horizontal points are where the derivative + dy/dx is equal to 0. But the Bezier curves are defined by + parametric formulas + x=fx(t) + y=fy(t) + so finding this derivative is complicated. + Also even if we find some point (x,y) splitting at this point + is far not obvious. Fortunately we can use dy/dt = 0 instead, + this gets to a rather simple square equation and splitting + at a known value of t is simple. + + The formulas are: + + y = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 + y = (-A+3*B-3*C+D)*t^3 + (3*A-6*B+3*C)*t^2 + (-3*A+3*B)*t + A + dy/dt = 3*(-A+3*B-3*C+D)*t^2 + 2*(3*A-6*B+3*C)*t + (-3*A+3*B) + */ + +void +ffixquadrants( + GLYPH *g +) +{ + GENTRY *ge, *nge; + int i, j, np, oldnp; + double sp[5]; /* split points, last one empty */ + char dir[5]; /* for debugging, direction by which split happened */ + double a, b, *pts; /* points of a curve */ + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + doagain: + np = 0; /* no split points yet */ + if(ISDBG(QUAD)) { + fprintf(stderr, "%s: trying 0x%x (%g %g) (%g %g) (%g %g) (%g %g)\n ", g->name, + ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2, + ge->fx3, ge->fy3); + } + for(i=0; i<2; i++) { /* first for x then for y */ + /* find the cooridnates of control points */ + a = ge->prev->fpoints[i][2]; + pts = &ge->fpoints[i][0]; + + oldnp = np; + np += fsqequation( + 3.0*(-a + 3.0*pts[0] - 3.0*pts[1] + pts[2]), + 6.0*(a - 2.0*pts[0] + pts[1]), + 3.0*(-a + pts[0]), + &sp[np], + 0.0, 1.0); /* XXX range is [0;1] */ + + if(np == oldnp) + continue; + + if(ISDBG(QUAD)) + fprintf(stderr, "%s: 0x%x: %d pts(%c): ", + g->name, ge, np-oldnp, i? 'y':'x'); + + /* remove points that are too close to the ends + * because hor/vert ends are permitted, also + * if the split point is VERY close to the ends + * but not exactly then just flatten it and check again. + */ + for(j = oldnp; jfpoints[i][0] != ge->prev->fpoints[i][2]) { + ge->fpoints[i][0] = ge->prev->fpoints[i][2]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened at front\n"); + goto doagain; + } + if( ge->fpoints[i][1] != ge->fpoints[i][0] + && fsign(ge->fpoints[i][2] - ge->fpoints[i][1]) + != fsign(ge->fpoints[i][1] - ge->fpoints[i][0]) ) { + ge->fpoints[i][1] = ge->fpoints[i][0]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at front\n"); + goto doagain; + } + sp[j] = sp[j+1]; np--; j--; + if(ISDBG(QUAD)) fprintf(stderr, "(front flat) "); + } else if(sp[j] > 0.97) { /* rear end of curve */ + if(ge->fpoints[i][1] != ge->fpoints[i][2]) { + ge->fpoints[i][1] = ge->fpoints[i][2]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened at rear\n"); + goto doagain; + } + if( ge->fpoints[i][0] != ge->fpoints[i][1] + && fsign(ge->prev->fpoints[i][2] - ge->fpoints[i][0]) + != fsign(ge->fpoints[i][0] - ge->fpoints[i][1]) ) { + ge->fpoints[i][0] = ge->fpoints[i][1]; + if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at rear\n"); + goto doagain; + } + sp[j] = sp[j+1]; np--; j--; + if(ISDBG(QUAD)) fprintf(stderr, "(rear flat) "); + } + } + if(ISDBG(QUAD)) fprintf(stderr, "\n"); + } + + if(np==0) /* no split points, leave it alone */ + continue; + + if(ISDBG(QUAD)) { + fprintf(stderr, "%s: splitting 0x%x (%g %g) (%g %g) (%g %g) (%g %g) at %d points\n ", g->name, + ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2, + ge->fx3, ge->fy3, np); + for(i=0; i sp[j]) { + a = sp[i]; sp[i] = sp[j]; sp[j] = a; + } + + /* now finally do the split on each point */ + for(j=0; jfpoints[i][0]; /* get the middle points */ + b = ge->fpoints[i][1]; + + /* calculate new internal points */ + c = SPLIT(a, b); + + ge->fpoints[i][0] = SPLIT(ge->prev->fpoints[i][2], a); + ge->fpoints[i][1] = SPLIT(ge->fpoints[i][0], c); + + nge->fpoints[i][1] = SPLIT(b, nge->fpoints[i][2]); + nge->fpoints[i][0] = SPLIT(c, nge->fpoints[i][1]); + + ge->fpoints[i][2] = SPLIT(ge->fpoints[i][1], + + nge->fpoints[i][0]); + } +#undef SPLIT + + addgeafter(ge, nge); + + /* go to the next part, adjust remaining points */ + ge = nge; + for(i=j+1; itype != GE_CURVE) + return 0; + + a = ge->iy2 - ge->iy1; + b = ge->ix2 - ge->ix1; + k = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a); + a = ge->iy1 - ge->prev->iy3; + b = ge->ix1 - ge->prev->ix3; + k1 = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a); + a = ge->iy3 - ge->iy2; + b = ge->ix3 - ge->ix2; + k2 = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a); + + /* if the curve is not a zigzag */ + if (k1 >= k && k2 <= k || k1 <= k && k2 >= k) + return 0; + else + return 1; +} + +/* check if a curve is a zigzag - floating */ + +static int +fiszigzag( + GENTRY *ge +) +{ + double k, k1, k2; + double a, b; + + if (ge->type != GE_CURVE) + return 0; + + a = fabs(ge->fy2 - ge->fy1); + b = fabs(ge->fx2 - ge->fx1); + k = a < FEPS ? (b fy1 - ge->prev->fy3); + b = fabs(ge->fx1 - ge->prev->fx3); + k1 = a < FEPS ? (b < FEPS ? 1. : FBIGVAL) : b / a; + a = fabs(ge->fy3 - ge->fy2); + b = fabs(ge->fx3 - ge->fx2); + k2 = a < FEPS ? (b = k && k2 <= k || k1 <= k && k2 >= k) + return 0; + else + return 1; +} + +/* split the zigzag-like curves into two parts */ + +void +fsplitzigzags( + GLYPH * g +) +{ + GENTRY *ge, *nge; + double a, b, c, d; + + assertisfloat(g, "splitting zigzags"); + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + /* if the curve is not a zigzag */ + if ( !fiszigzag(ge) ) { + continue; + } + + /* split the curve by t=0.5 */ + nge = newgentry(GEF_FLOAT); + (*nge) = (*ge); + nge->type = GE_CURVE; + + a = ge->prev->fx3; + b = ge->fx1; + c = ge->fx2; + d = ge->fx3; + nge->fx3 = d; + nge->fx2 = (c + d) / 2.; + nge->fx1 = (b + 2. * c + d) / 4.; + ge->fx3 = (a + b * 3. + c * 3. + d) / 8.; + ge->fx2 = (a + 2. * b + c) / 4.; + ge->fx1 = (a + b) / 2.; + + a = ge->prev->fy3; + b = ge->fy1; + c = ge->fy2; + d = ge->fy3; + nge->fy3 = d; + nge->fy2 = (c + d) / 2.; + nge->fy1 = (b + 2. * c + d) / 4.; + ge->fy3 = (a + b * 3. + c * 3. + d) / 8.; + ge->fy2 = (a + 2. * b + c) / 4.; + ge->fy1 = (a + b) / 2.; + + addgeafter(ge, nge); + } +} + +/* free this GENTRY, returns what was ge->next + * (ge must be of type GE_LINE or GE_CURVE) + * works on both float and int entries + */ + +static GENTRY * +freethisge( + GENTRY *ge +) +{ + GENTRY *xge; + + if (ge->bkwd != ge->prev) { + /* at beginning of the contour */ + + xge = ge->bkwd; + if(xge == ge) { /* was the only line in contour */ + /* remove the contour completely */ + /* prev is GE_MOVE, next is GE_PATH, remove them all */ + + /* may be the first contour, then ->bkwd points to ge->entries */ + if(ge->prev->prev == 0) + *(GENTRY **)(ge->prev->bkwd) = ge->next->next; + else + ge->prev->prev->next = ge->next->next; + + if(ge->next->next) { + ge->next->next->prev = ge->prev->prev; + ge->next->next->bkwd = ge->prev->bkwd; + } + + xge = ge->next->next; + free(ge->prev); free(ge->next); free(ge); + return xge; + } + + /* move the start point of the contour */ + if(ge->flags & GEF_FLOAT) { + ge->prev->fx3 = xge->fx3; + ge->prev->fy3 = xge->fy3; + } else { + ge->prev->ix3 = xge->ix3; + ge->prev->iy3 = xge->iy3; + } + } else if(ge->frwd != ge->next) { + /* at end of the contour */ + + xge = ge->frwd->prev; + /* move the start point of the contour */ + if(ge->flags & GEF_FLOAT) { + xge->fx3 = ge->bkwd->fx3; + xge->fy3 = ge->bkwd->fy3; + } else { + xge->ix3 = ge->bkwd->ix3; + xge->iy3 = ge->bkwd->iy3; + } + } + + ge->prev->next = ge->next; + ge->next->prev = ge->prev; + ge->bkwd->frwd = ge->frwd; + ge->frwd->bkwd = ge->bkwd; + + xge = ge->next; + free(ge); + return xge; +} + +/* inserts a new gentry (LINE or CURVE) after another (MOVE + * or LINE or CURVE) + * corrects the first GE_MOVE if neccessary + */ + +static void +addgeafter( + GENTRY *oge, /* after this */ + GENTRY *nge /* insert this */ +) +{ + if(oge->type == GE_MOVE) { + /* insert before next */ + if(oge->next->type == GE_PATH) { + /* first and only GENTRY in path */ + nge->frwd = nge->bkwd = nge; + } else { + nge->frwd = oge->next; + nge->bkwd = oge->next->bkwd; + oge->next->bkwd->frwd = nge; + oge->next->bkwd = nge; + } + } else { + nge->frwd = oge->frwd; + nge->bkwd = oge; + oge->frwd->bkwd = nge; + oge->frwd = nge; + } + + nge->next = oge->next; + nge->prev = oge; + oge->next->prev = nge; + oge->next = nge; + + if(nge->frwd->prev->type == GE_MOVE) { + /* fix up the GE_MOVE entry */ + if(nge->flags & GEF_FLOAT) { + nge->frwd->prev->fx3 = nge->fx3; + nge->frwd->prev->fy3 = nge->fy3; + } else { + nge->frwd->prev->ix3 = nge->ix3; + nge->frwd->prev->iy3 = nge->iy3; + } + } +} + +/* + * Check if this GENTRY happens to be at the end of path + * and fix the first MOVETO accordingly + * handles both int and float + */ + +static void +fixendpath( + GENTRY *ge +) +{ + GENTRY *mge; + + mge = ge->frwd->prev; + if(mge->type == GE_MOVE) { + if(ge->flags & GEF_FLOAT) { + mge->fx3 = ge->fx3; + mge->fy3 = ge->fy3; + } else { + mge->ix3 = ge->ix3; + mge->iy3 = ge->iy3; + } + } +} + +/* + * This function adjusts the rest of path (the part from...to is NOT changed) + * to cover the specified gap by the specified axis (0 - X, 1 - Y). + * Gap is counted in direction (end_of_to - beginning_of_from). + * Returns by how much the gap was not closed (0.0 if it was fully closed). + * Ret contains by how much the first and last points of [from...to] + * were moved to bring them in consistence to the rest of the path. + * If ret==NULL then this info is not returned. + */ + +static double +fclosegap( + GENTRY *from, + GENTRY *to, + int axis, + double gap, + double *ret +) +{ +#define TIMESLARGER 10. /* how many times larger must be a curve to not change too much */ + double rm[2]; + double oldpos[2]; + double times, limit, df, dx; + int j, k; + GENTRY *xge, *pge, *nge, *bge[2]; + + /* remember the old points to calculate ret */ + oldpos[0] = from->prev->fpoints[axis][2]; + oldpos[1] = to->fpoints[axis][2]; + + rm[0] = rm[1] = gap / 2. ; + + bge[0] = from; /* this is convenient for iterations */ + bge[1] = to; + + /* first try to modify large curves but if have none then settle for small */ + for(times = (TIMESLARGER-1); times > 0.1; times /= 2. ) { + + if(rm[0]+rm[1] == 0.) + break; + + /* iterate in both directions, backwards then forwards */ + for(j = 0; j<2; j++) { + + if(rm[j] == 0.) /* if this direction is exhausted */ + continue; + + limit = fabs(rm[j]) * (1.+times); + + for(xge = bge[j]->cntr[j]; xge != bge[!j]; xge = xge->cntr[j]) { + dx = xge->fpoints[axis][2] - xge->prev->fpoints[axis][2]; + df = fabs(dx) - limit; + if( df <= FEPS ) /* curve is too small to change */ + continue; + + if( df >= fabs(rm[j]) ) + df = rm[j]; + else + df *= fsign(rm[j]); /* we may cover this part of rm */ + + rm[j] -= df; + limit = fabs(rm[j]) * (1.+times); + + if(xge->type == GE_CURVE) { /* correct internal points */ + double scale = ((dx+df) / dx) - 1.; + double base; + + if(j) + base = xge->fpoints[axis][2]; + else + base = xge->prev->fpoints[axis][2]; + + for(k = 0; k<2; k++) + xge->fpoints[axis][k] += scale * + (xge->fpoints[axis][k] - base); + } + + /* move all the intermediate lines */ + if(j) { + df = -df; /* absolute direction */ + pge = bge[1]->bkwd; + nge = xge->bkwd; + } else { + xge->fpoints[axis][2] += df; + pge = bge[0]; + nge = xge->frwd; + } + while(nge != pge) { + if(nge->type == GE_CURVE) { + nge->fpoints[axis][0] +=df; + nge->fpoints[axis][1] +=df; + } + nge->fpoints[axis][2] += df; + if(nge->next != nge->frwd) { /* last entry of contour */ + nge->frwd->prev->fpoints[axis][2] += df; + } + nge = nge->cntr[!j]; + } + + if(rm[j] == 0.) + break; + } + } + } + + /* find the difference */ + oldpos[0] -= from->prev->fpoints[axis][2]; + oldpos[1] -= to->fpoints[axis][2]; + + if(ret) { + ret[0] = oldpos[0] - from->prev->fpoints[axis][2]; + ret[1] = oldpos[1] - to->fpoints[axis][2]; + } + +#if 0 + if( rm[0]+rm[1] != gap - oldpos[1] + oldpos[0]) { + fprintf(stderr, "** gap=%g rm[0]=%g rm[1]=%g o[0]=%g o[1]=%g rg=%g og=%g\n", + gap, rm[0], rm[1], oldpos[0], oldpos[1], rm[0]+rm[1], + gap - oldpos[1] + oldpos[0]); + } +#endif + + return rm[0]+rm[1]; +#undef TIMESLARGER +} + +/* remove the lines or curves smaller or equal to the size limit */ + +static void +fdelsmall( + GLYPH *g, + double minlen +) +{ + GENTRY *ge, *nge, *pge, *xge, *next; + int i, k; + double dx, dy, d2, d2m; + double minlen2; +#define TIMESLARGER 10. /* how much larger must be a curve to not change too much */ + + minlen2 = minlen*minlen; + + for (ge = g->entries; ge != 0; ge = next) { + next = ge->next; + + if (ge->type != GE_CURVE && ge->type != GE_LINE) + continue; + + d2m = 0; + for(i= (ge->type==GE_CURVE? 0: 2); i<3; i++) { + dx = ge->fxn[i] - ge->prev->fx3; + dy = ge->fyn[i] - ge->prev->fy3; + d2 = dx*dx + dy*dy; + if(d2m < d2) + d2m = d2; + } + + if( d2m > minlen2 ) { /* line is not too small */ + /* XXX add more normalization here */ + continue; + } + + /* if the line is too small */ + + /* check forwards if we have a whole sequence of them */ + nge = ge; + for(xge = ge->frwd; xge != ge; xge = xge->frwd) { + d2m = 0; + for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) { + dx = xge->fxn[i] - xge->prev->fx3; + dy = xge->fyn[i] - xge->prev->fy3; + d2 = dx*dx + dy*dy; + if(d2m < d2) + d2m = d2; + } + if( d2m > minlen2 ) /* line is not too small */ + break; + nge = xge; + if(next == nge) /* move the next step past this sequence */ + next = next->next; + } + + /* check backwards if we have a whole sequence of them */ + pge = ge; + for(xge = ge->bkwd; xge != ge; xge = xge->bkwd) { + d2m = 0; + for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) { + dx = xge->fxn[i] - xge->prev->fx3; + dy = xge->fyn[i] - xge->prev->fy3; + d2 = dx*dx + dy*dy; + if(d2m < d2) + d2m = d2; + } + if( d2m > minlen2 ) /* line is not too small */ + break; + pge = xge; + } + + /* now we have a sequence of small fragments in pge...nge (inclusive) */ + + if(ISDBG(FCONCISE)) { + fprintf(stderr, "glyph %s has very small fragments(%x..%x..%x)\n", + g->name, pge, ge, nge); + dumppaths(g, pge, nge); + } + + /* reduce whole sequence to one part and remember the middle point */ + if(pge != nge) { + while(1) { + xge = pge->frwd; + if(xge == nge) { + pge->fx1 = pge->fx2 = pge->fx3; + pge->fx3 = nge->fx3; + pge->fy1 = pge->fy2 = pge->fy3; + pge->fy3 = nge->fy3; + pge->type = GE_CURVE; + freethisge(nge); + break; + } + if(xge == nge->bkwd) { + pge->fx1 = pge->fx2 = (pge->fx3+xge->fx3)/2.; + pge->fx3 = nge->fx3; + pge->fy1 = pge->fy2 = (pge->fy3+xge->fy3)/2.; + pge->fy3 = nge->fy3; + pge->type = GE_CURVE; + freethisge(nge); + freethisge(xge); + break; + } + freethisge(pge); pge = xge; + xge = nge->bkwd; freethisge(nge); nge = xge; + } + } + ge = pge; + + /* check if the whole sequence is small */ + dx = ge->fx3 - ge->prev->fx3; + dy = ge->fy3 - ge->prev->fy3; + d2 = dx*dx + dy*dy; + + if( d2 > minlen2 ) { /* no, it is not */ + double b, d; + + WARNING_3 fprintf(stderr, "glyph %s had a sequence of fragments < %g points each, reduced to one curve\n", + g->name, minlen); + + /* check that we did not create a monstrosity spanning quadrants */ + if(fsign(ge->fx1 - ge->prev->fx1) * fsign(ge->fx3 - ge->fx1) < 0 + || fsign(ge->fy1 - ge->prev->fy1) * fsign(ge->fy3 - ge->fy1) < 0 ) { + /* yes, we did; are both parts of this thing big enough ? */ + dx = ge->fx1 - ge->prev->fx3; + dy = ge->fy1 - ge->prev->fy3; + d2 = dx*dx + dy*dy; + + dx = ge->fx3 - ge->fx1; + dy = ge->fy3 - ge->fy1; + d2m = dx*dx + dy*dy; + + if(d2 > minlen2 && d2m > minlen2) { /* make two straights */ + nge = newgentry(GEF_FLOAT); + *nge = *ge; + + for(i=0; i<2; i++) { + ge->fpoints[i][2] = ge->fpoints[i][0]; + b = nge->fpoints[i][0]; + d = nge->fpoints[i][2] - b; + nge->fpoints[i][0] = b + 0.1*d; + nge->fpoints[i][1] = b + 0.9*d; + } + } + for(i=0; i<2; i++) { /* make one straight or first of two straights */ + b = ge->prev->fpoints[i][2]; + d = ge->fpoints[i][2] - b; + ge->fpoints[i][0] = b + 0.1*d; + ge->fpoints[i][1] = b + 0.9*d; + } + } + continue; + } + + if(ge->frwd == ge) { /* points to itself, just remove the path completely */ + WARNING_3 fprintf(stderr, "glyph %s had a path made of fragments < %g points each, removed\n", + g->name, minlen); + + next = freethisge(ge); + continue; + } + + /* now close the gap by x and y */ + for(i=0; i<2; i++) { + double gap; + + gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; + if( fclosegap(ge, ge, i, gap, NULL) != 0.0 ) { + double scale, base; + + /* not good, as the last resort just scale the next line */ + gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2]; + + if(ISDBG(FCONCISE)) + fprintf(stderr, " last resort on %c: closing next by %g\n", + (i==0 ? 'x' : 'y'), gap); + + nge = ge->frwd; + base = nge->fpoints[i][2]; + dx = ge->fpoints[i][2] - base; + if(fabs(dx) < FEPS) + continue; + + scale = ((dx-gap) / dx); + + if(nge->type == GE_CURVE) + for(k = 0; k<2; k++) + nge->fpoints[i][k] = base + + scale * (nge->fpoints[i][k] - base); + + ge->fpoints[i][2] -= gap; + } + } + + /* OK, the gap is closed - remove this useless GENTRY */ + freethisge(ge); + } +#undef TIMESLARGER +} + +/* normalize curves to the form where their ends + * can be safely used as derivatives + */ + +static void +fnormalizec( + GLYPH * g +) +{ + GENTRY *ge; + int midsame, frontsame, rearsame, i; + double d, b; + + assertisfloat(g, "normalizing curves"); + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + midsame = (fabs(ge->fx1-ge->fx2)fy1-ge->fy2)fx1-ge->prev->fx3)fy1-ge->prev->fy3)fx3-ge->fx2)fy3-ge->fy2)prev->fpoints[i][2]; + d = ge->fpoints[i][2] - b; + ge->fpoints[i][0] = b + 0.1*d; + ge->fpoints[i][1] = b + 0.9*d; + } + } else if(frontsame) { + for(i=0; i<2; i++) { + b = ge->prev->fpoints[i][2]; + d = ge->fpoints[i][1] - b; + ge->fpoints[i][0] = b + 0.01*d; + } + } else if(rearsame) { + for(i=0; i<2; i++) { + b = ge->fpoints[i][2]; + d = ge->fpoints[i][0] - b; + ge->fpoints[i][1] = b + 0.01*d; + } + } else + continue; + + if(ISDBG(FCONCISE)) fprintf(stderr, "glyph %g, normalized entry %x\n", g->name, ge); + } +} + +/* find the point where two rays continuing vectors cross + * rays are defined as beginning of curve1 and end of curve 2 + * returns 1 if they cross, 0 if they don't + * If they cross returns the maximal scales for both vectors. + * Expects that the curves are normalized. + */ + +static int +fcrossrays( + GENTRY *ge1, + GENTRY *ge2, + double *max1, + double *max2 +) +{ + struct ray { + double x1, y1, x2, y2; + int isvert; + double k, b; /* lines are represented as y = k*x + b */ + double *maxp; + } ray [3]; + double x, y; + int i; + + ray[0].x1 = ge1->prev->fx3; + ray[0].y1 = ge1->prev->fy3; + ray[0].x2 = ge1->fx1; + ray[0].y2 = ge1->fy1; + ray[0].maxp = max1; + + ray[1].x1 = ge2->fx3; + ray[1].y1 = ge2->fy3; + ray[1].x2 = ge2->fx2; + ray[1].y2 = ge2->fy2; + ray[1].maxp = max2; + + for(i=0; i<2; i++) { + if(ray[i].x1 == ray[i].x2) + ray[i].isvert = 1; + else { + ray[i].isvert = 0; + ray[i].k = (ray[i].y2 - ray[i].y1) / (ray[i].x2 - ray[i].x1); + ray[i].b = ray[i].y2 - ray[i].k * ray[i].x2; + } + } + + if(ray[0].isvert && ray[1].isvert) { + if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: both vertical\n"); + return 0; /* both vertical, don't cross */ + } + + if(ray[1].isvert) { + ray[2] = ray[0]; /* exchange them */ + ray[0] = ray[1]; + ray[1] = ray[2]; + } + + if(ray[0].isvert) { + x = ray[0].x1; + } else { + if( fabs(ray[0].k - ray[1].k) < FEPS) { + if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: parallel lines, k = %g, %g\n", + ray[0].k, ray[1].k); + return 0; /* parallel lines */ + } + x = (ray[1].b - ray[0].b) / (ray[0].k - ray[1].k) ; + } + y = ray[1].k * x + ray[1].b; + + for(i=0; i<2; i++) { + if(ray[i].isvert) + *ray[i].maxp = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1); + else + *ray[i].maxp = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1); + /* check if wrong sides of rays cross */ + if( *ray[i].maxp < 0 ) { + if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: scale=%g @(%g,%g) (%g,%g)<-(%g,%g)\n", + *ray[i].maxp, x, y, ray[i].x2, ray[i].y2, ray[i].x1, ray[i].y1); + return 0; + } + } + return 1; +} + +/* find the area covered by the curve + * (limited by the projections to the X axis) + */ + +static double +fcvarea( + GENTRY *ge +) +{ + double Ly, My, Ny, Py, Qx, Rx, Sx; + double area; + + /* y = Ly*t^3 + My*t^2 + Ny*t + Py */ + Ly = -ge->prev->fy3 + 3*(ge->fy1 - ge->fy2) + ge->fy3; + My = 3*ge->prev->fy3 - 6*ge->fy1 + 3*ge->fy2; + Ny = 3*(-ge->prev->fy3 + ge->fy1); + Py = ge->prev->fy3; + + /* dx/dt = Qx*t^2 + Rx*t + Sx */ + Qx = 3*(-ge->prev->fx3 + 3*(ge->fx1 - ge->fx2) + ge->fx3); + Rx = 6*(ge->prev->fx3 - 2*ge->fx1 + ge->fx2); + Sx = 3*(-ge->prev->fx3 + ge->fx1); + + /* area is integral[from 0 to 1]( y(t) * dx(t)/dt *dt) */ + area = 1./6.*(Ly*Qx) + 1./5.*(Ly*Rx + My*Qx) + + 1./4.*(Ly*Sx + My*Rx + Ny*Qx) + 1./3.*(My*Sx + Ny*Rx + Py*Qx) + + 1./2.*(Ny*Sx + Py*Rx) + Py*Sx; + + return area; +} + +/* find the value of point on the curve at the given parameter t, + * along the given axis (0 - X, 1 - Y). + */ + +static double +fcvval( + GENTRY *ge, + int axis, + double t +) +{ + double t2, mt, mt2; + + /* val = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 */ + t2 = t*t; + mt = 1-t; + mt2 = mt*mt; + + return ge->prev->fpoints[axis][2]*mt2*mt + + 3*(ge->fpoints[axis][0]*mt2*t + ge->fpoints[axis][1]*mt*t2) + + ge->fpoints[axis][2]*t*t2; +} + +/* Check that the new curve has the point identified by the + * parameter t reasonably close to the corresponding point + * in the old pair of curves which were joined in proportion k. + * If old2 is NULL then just compare nge and old1 at the point t. + * Returns 0 if OK, 1 if it's too far. + */ + +static int +fckjoinedcv( + GLYPH *g, + double t, + GENTRY *nge, + GENTRY *old1, + GENTRY *old2, + double k +) +{ + GENTRY *oge; + double ot; + double off; + double lim; + int i; + + if(old2 == 0) { + oge = old1; + ot = t; + } else if(t <= k && k!=0.) { + oge = old1; + ot = t/k; + } else { + oge = old2; + ot = (t-k) / (1.-k); + } + + if(ISDBG(FCONCISE)) + fprintf(stderr, "%s: t=%g ot=%g (%x) ", g->name, t, ot, oge); + + for(i=0; i<2; i++) { + /* permitted tolerance is 5% */ + lim = fabs(nge->fpoints[i][2] - nge->prev->fpoints[i][2])*0.05; + + if(lim < 3.) + lim = 3.; /* for small curves the tolerance is higher */ + if(lim > 10.) + lim = 10.; /* for big curves the tolerance is limited anyway */ + + off = fabs(fcvval(nge, i, t) - fcvval(oge, i, ot)); + + if(off > lim) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "out of range d%c=%.2f(%.2f)\n", + (i==0 ? 'X' : 'Y'), off, lim); + return 1; + } + + if(ISDBG(FCONCISE)) + fprintf(stderr, "valid d%c=%.2f(%.2f) ", (i==0 ? 'X' : 'Y'), off, lim); + } + if(ISDBG(FCONCISE)) + fprintf(stderr, "\n"); + return 0; +} + +/* force conciseness: substitute 2 or more curves going in the +** same quadrant with one curve +** in floating point +*/ + +void +fforceconcise( + GLYPH * g +) +{ + GENTRY *ge, *nge; + GENTRY tge; + double firstlen, lastlen, sumlen, scale; + double dxw1, dyw1, dxw2, dyw2; + double dxb1, dyb1, dxe1, dye1; + double dxb2, dyb2, dxe2, dye2; + double maxsc1, maxsc2; + int i; + + assertisfloat(g, "enforcing conciseness"); + + fdelsmall(g, 0.05); + assertpath(g->entries, __FILE__, __LINE__, g->name); + fnormalizec(g); + + + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_CURVE) + continue; + + /* the whole direction of curve */ + dxw1 = ge->fx3 - ge->prev->fx3; + dyw1 = ge->fy3 - ge->prev->fy3; + + while (1) { + /* the whole direction of curve */ + dxw1 = ge->fx3 - ge->prev->fx3; + dyw1 = ge->fy3 - ge->prev->fy3; + + /* directions of ends of curve */ + dxb1 = ge->fx1 - ge->prev->fx3; + dyb1 = ge->fy1 - ge->prev->fy3; + dxe1 = ge->fx3 - ge->fx2; + dye1 = ge->fy3 - ge->fy2; + + nge = ge->frwd; + + if (nge->type != GE_CURVE) + break; + + dxw2 = nge->fx3 - ge->fx3; + dyw2 = nge->fy3 - ge->fy3; + + dxb2 = nge->fx1 - ge->fx3; + dyb2 = nge->fy1 - ge->fy3; + dxe2 = nge->fx3 - nge->fx2; + dye2 = nge->fy3 - nge->fy2; + + /* if curve changes direction */ + if (fsign(dxw1) != fsign(dxw2) || fsign(dyw1) != fsign(dyw2)) + break; + + /* if the arch is going in other direction */ + if (fsign(fabs(dxb1 * dyw1) - fabs(dyb1 * dxw1)) + * fsign(fabs(dxe2 * dyw2) - fabs(dye2 * dxw2)) > 0) + break; + + /* get possible scale limits within which we won't cross quadrants */ + if( fcrossrays(ge, nge, &maxsc1, &maxsc2) == 0 ) { + if(ISDBG(FCONCISE)) { + fprintf(stderr, "glyph %s has curves with strange ends\n", g->name); + dumppaths(g, ge, nge); + } + break; + } + + if(maxsc1 < 1. || maxsc2 < 1. ) /* would create a zigzag */ + break; + + ge->dir = fgetcvdir(ge); + nge->dir = fgetcvdir(nge); + + if( ((ge->dir&CVDIR_FRONT)-CVDIR_FEQUAL) * ((nge->dir&CVDIR_REAR)-CVDIR_REQUAL) < 0 ) + /* would create a zigzag */ + break; + + firstlen = sqrt( dxe1*dxe1 + dye1*dye1 ); + lastlen = sqrt( dxb2*dxb2 + dyb2*dyb2 ); + sumlen = firstlen + lastlen; + + /* check the scale limits */ + if( sumlen/firstlen > maxsc1 || sumlen/lastlen > maxsc2 ) { + if(ISDBG(FCONCISE)) + fprintf(stderr, "%s: %x, %x would be crossing in forceconcise\n", + g->name, ge, nge); + break; + } + + /* OK, it seems like we can attempt to join these two curves */ + tge.flags = ge->flags; + tge.prev = ge->prev; + tge.fx1 = ge->fx1; + tge.fy1 = ge->fy1; + tge.fx2 = nge->fx2; + tge.fy2 = nge->fy2; + tge.fx3 = nge->fx3; + tge.fy3 = nge->fy3; + + dxb1 = tge.fx1 - tge.prev->fx3; + dyb1 = tge.fy1 - tge.prev->fy3; + dxe1 = tge.fx3 - tge.fx2; + dye1 = tge.fy3 - tge.fy2; + + /* scale the first segment */ + scale = sumlen / firstlen; + tge.fx1 = tge.prev->fx3 + scale * dxb1; + tge.fy1 = tge.prev->fy3 + scale * dyb1; + + /* scale the last segment */ + scale = sumlen / lastlen; + tge.fx2 = tge.fx3 - scale * dxe1; + tge.fy2 = tge.fy3 - scale * dye1; + + /* now check if we got something sensible */ + + /* check if some important points is too far from original */ + scale = firstlen / sumlen; + { + double pts[4] = { 0./*will be replaced*/, 0.5, 0.25, 0.75 }; + int i, bad; + + pts[0] = scale; + bad = 0; + + for(i=0; ifxn[i] = tge.fxn[i]; + ge->fyn[i] = tge.fyn[i]; + } + + freethisge(nge); + } + } +} + +void +print_glyph( + int glyphno +) +{ + GLYPH *g; + GENTRY *ge; + int x = 0, y = 0; + int i; + int grp, lastgrp= -1; + + g = &glyph_list[glyphno]; + + fprintf(pfa_file, "/%s { \n", g->name); + + /* consider widths >MAXLEGALWIDTH as bugs */ + if( g->scaledwidth <= MAXLEGALWIDTH ) { + fprintf(pfa_file, "0 %d hsbw\n", g->scaledwidth); + } else { + fprintf(pfa_file, "0 1000 hsbw\n"); + WARNING_2 fprintf(stderr, "glyph %s: width %d seems to be buggy, set to 1000\n", + g->name, g->scaledwidth); + } + +#if 0 + fprintf(pfa_file, "%% contours: "); + for (i = 0; i < g->ncontours; i++) + fprintf(pfa_file, "%s(%d,%d) ", (g->contours[i].direction == DIR_OUTER ? "out" : "in"), + g->contours[i].xofmin, g->contours[i].ymin); + fprintf(pfa_file, "\n"); + + if (g->rymin < 5000) + fprintf(pfa_file, "%d lower%s\n", g->rymin, (g->flatymin ? "flat" : "curve")); + if (g->rymax > -5000) + fprintf(pfa_file, "%d upper%s\n", g->rymax, (g->flatymax ? "flat" : "curve")); +#endif + + if (g->hstems) + for (i = 0; i < g->nhs; i += 2) { + if (g->hstems[i].flags & ST_3) { + fprintf(pfa_file, "%d %d %d %d %d %d hstem3\n", + g->hstems[i].value, + g->hstems[i + 1].value - g->hstems[i].value, + g->hstems[i + 2].value, + g->hstems[i + 3].value - g->hstems[i + 2].value, + g->hstems[i + 4].value, + g->hstems[i + 5].value - g->hstems[i + 4].value + ); + i += 4; + } else { + fprintf(pfa_file, "%d %d hstem\n", g->hstems[i].value, + g->hstems[i + 1].value - g->hstems[i].value); + } + } + + if (g->vstems) + for (i = 0; i < g->nvs; i += 2) { + if (g->vstems[i].flags & ST_3) { + fprintf(pfa_file, "%d %d %d %d %d %d vstem3\n", + g->vstems[i].value, + g->vstems[i + 1].value - g->vstems[i].value, + g->vstems[i + 2].value, + g->vstems[i + 3].value - g->vstems[i + 2].value, + g->vstems[i + 4].value, + g->vstems[i + 5].value - g->vstems[i + 4].value + ); + i += 4; + } else { + fprintf(pfa_file, "%d %d vstem\n", g->vstems[i].value, + g->vstems[i + 1].value - g->vstems[i].value); + } + } + + for (ge = g->entries; ge != 0; ge = ge->next) { + if(g->nsg>0) { + grp=ge->stemid; + if(grp >= 0 && grp != lastgrp) { + fprintf(pfa_file, "%d 4 callsubr\n", grp+g->firstsubr); + lastgrp=grp; + } + } + + switch (ge->type) { + case GE_MOVE: + if (absolute) + fprintf(pfa_file, "%d %d amoveto\n", ge->ix3, ge->iy3); + else + rmoveto(ge->ix3 - x, ge->iy3 - y); + if (0) + fprintf(stderr, "Glyph %s: print moveto(%d, %d)\n", + g->name, ge->ix3, ge->iy3); + x = ge->ix3; + y = ge->iy3; + break; + case GE_LINE: + if (absolute) + fprintf(pfa_file, "%d %d alineto\n", ge->ix3, ge->iy3); + else + rlineto(ge->ix3 - x, ge->iy3 - y); + x = ge->ix3; + y = ge->iy3; + break; + case GE_CURVE: + if (absolute) + fprintf(pfa_file, "%d %d %d %d %d %d arcurveto\n", + ge->ix1, ge->iy1, ge->ix2, ge->iy2, ge->ix3, ge->iy3); + else + rrcurveto(ge->ix1 - x, ge->iy1 - y, + ge->ix2 - ge->ix1, ge->iy2 - ge->iy1, + ge->ix3 - ge->ix2, ge->iy3 - ge->iy2); + x = ge->ix3; + y = ge->iy3; + break; + case GE_PATH: + closepath(); + break; + default: + WARNING_1 fprintf(stderr, "**** Glyph %s: unknown entry type '%c'\n", + g->name, ge->type); + break; + } + } + + fprintf(pfa_file, "endchar } ND\n"); +} + +/* print the subroutines for this glyph, returns the number of them */ +int +print_glyph_subs( + int glyphno, + int startid /* start numbering subroutines from this id */ +) +{ + GLYPH *g; + int i, grp; + + g = &glyph_list[glyphno]; + + if(!hints || !subhints || g->nsg<1) + return 0; + + g->firstsubr=startid; + +#if 0 + fprintf(pfa_file, "%% %s %d\n", g->name, g->nsg); +#endif + for(grp=0; grpnsg; grp++) { + fprintf(pfa_file, "dup %d {\n", startid++); + for(i= (grp==0)? 0 : g->nsbs[grp-1]; insbs[grp]; i++) + fprintf(pfa_file, "\t%d %d %cstem\n", g->sbstems[i].low, + g->sbstems[i].high-g->sbstems[i].low, + g->sbstems[i].isvert ? 'v' : 'h'); + fprintf(pfa_file, "\treturn\n\t} NP\n"); + } + + return g->nsg; +} + +void +print_glyph_metrics( + int code, + int glyphno +) +{ + GLYPH *g; + + g = &glyph_list[glyphno]; + + if(transform) + fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n", + code, g->scaledwidth, g->name, + iscale(g->xMin), iscale(g->yMin), iscale(g->xMax), iscale(g->yMax)); + else + fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n", + code, g->scaledwidth, g->name, + g->xMin, g->yMin, g->xMax, g->yMax); +} + +/* + SB: + An important note about the BlueValues. + + The Adobe documentation says that the maximal width of a Blue zone + is connected to the value of BlueScale, which is by default 0.039625. + The BlueScale value defines, at which point size the overshoot + suppression be disabled. + + The formula for it that is given in the manual is: + + BlueScale=point_size/240, for a 300dpi device + + that makes us wonder what is this 240 standing for. Incidentally + 240=72*1000/300, where 72 is the relation between inches and points, + 1000 is the size of the glyph matrix, and 300dpi is the resolution of + the output device. Knowing that we can recalculate the formula for + the font size in pixels rather than points: + + BlueScale=pixel_size/1000 + + That looks a lot simpler than the original formula, does not it ? + And the limitation about the maximal width of zone also looks + a lot simpler after the transformation: + + max_width < 1000/pixel_size + + that ensures that even at the maximal pixel size when the overshoot + suppression is disabled the zone width will be less than one pixel. + This is important, failure to comply to this limit will result in + really ugly fonts (been there, done that). But knowing the formula + for the pixel width, we see that in fact we can use the maximal width + of 24, not 23 as specified in the manual. + +*/ + +#define MAXBLUEWIDTH (24) + +/* + * Find the indexes of the most frequent values + * in the hystogram, sort them in ascending order, and save which one + * was the best one (if asked). + * Returns the number of values found (may be less than maximal because + * we ignore the zero values) + */ + +#define MAXHYST (2000) /* size of the hystogram */ +#define HYSTBASE 500 + +static int +besthyst( + int *hyst, /* the hystogram */ + int base, /* the base point of the hystogram */ + int *best, /* the array for indexes of best values */ + int nbest, /* its allocated size */ + int width, /* minimal difference between indexes */ + int *bestindp /* returned top point */ +) +{ + unsigned char hused[MAXHYST / 8 + 1]; + int i, max, j, w, last = 0; + int nf = 0; + + width--; + + memset(hused, 0 , sizeof hused); + + max = 1; + for (i = 0; i < nbest && max != 0; i++) { + best[i] = 0; + max = 0; + for (j = 1; j < MAXHYST - 1; j++) { + w = hyst[j]; + + if (w > max && (hused[j>>3] & (1 << (j & 0x07))) == 0) { + best[i] = j; + max = w; + } + } + if (max != 0) { + if (max < last/2) { + /* do not pick the too low values */ + break; + } + for (j = best[i] - width; j <= best[i] + width; j++) { + if (j >= 0 && j < MAXHYST) + hused[j >> 3] |= (1 << (j & 0x07)); + } + last = max; + best[i] -= base; + nf = i + 1; + } + } + + if (bestindp) + *bestindp = best[0]; + + /* sort the indexes in ascending order */ + for (i = 0; i < nf; i++) { + for (j = i + 1; j < nf; j++) + if (best[j] < best[i]) { + w = best[i]; + best[i] = best[j]; + best[j] = w; + } + } + + return nf; +} + +/* + * Find the next best Blue zone in the hystogram. + * Return the weight of the found zone. + */ + +static int +bestblue( + short *zhyst, /* the zones hystogram */ + short *physt, /* the points hystogram */ + short *ozhyst, /* the other zones hystogram */ + int *bluetab /* where to put the found zone */ +) +{ + int i, j, w, max, ind, first, last; + + /* find the highest point in the zones hystogram */ + /* if we have a plateau, take its center */ + /* if we have multiple peaks, take the first one */ + + max = -1; + first = last = -10; + for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { + w = zhyst[i]; + if (w > max) { + first = last = i; + max = w; + } else if (w == max) { + if (last == i - 1) + last = i; + } + } + ind = (first + last) / 2; + + if (max == 0) /* no zones left */ + return 0; + + /* now we reuse `first' and `last' as inclusive borders of the zone */ + first = ind; + last = ind + (MAXBLUEWIDTH - 1); + + /* our maximal width is far too big, so we try to make it narrower */ + w = max; + j = (w & 1); /* a pseudo-random bit */ + while (1) { + while (physt[first] == 0) + first++; + while (physt[last] == 0) + last--; + if (last - first < (MAXBLUEWIDTH * 2 / 3) || (max - w) * 10 > max) + break; + + if (physt[first] < physt[last] + || physt[first] == physt[last] && j) { + if (physt[first] * 20 > w) /* if weight is >5%, + * stop */ + break; + w -= physt[first]; + first++; + j = 0; + } else { + if (physt[last] * 20 > w) /* if weight is >5%, + * stop */ + break; + w -= physt[last]; + last--; + j = 1; + } + } + + /* save our zone */ + bluetab[0] = first - HYSTBASE; + bluetab[1] = last - HYSTBASE; + + /* invalidate all the zones overlapping with this one */ + /* the constant of 2 is determined by the default value of BlueFuzz */ + for (i = first - (MAXBLUEWIDTH - 1) - 2; i <= last + 2; i++) + if (i >= 0 && i < MAXHYST) { + zhyst[i] = 0; + ozhyst[i] = 0; + } + return w; +} + +/* + * Try to find the Blue Values, bounding box and italic angle + */ + +void +findblues(void) +{ + /* hystograms for upper and lower zones */ + short hystl[MAXHYST]; + short hystu[MAXHYST]; + short zuhyst[MAXHYST]; + short zlhyst[MAXHYST]; + int nchars; + int i, j, k, w, max; + GENTRY *ge; + GLYPH *g; + double ang; + + /* find the lowest and highest points of glyphs */ + /* and by the way build the values for FontBBox */ + /* and build the hystogram for the ItalicAngle */ + + /* re-use hystl for the hystogram of italic angle */ + + bbox[0] = bbox[1] = 5000; + bbox[2] = bbox[3] = -5000; + + for (i = 0; i < MAXHYST; i++) + hystl[i] = 0; + + nchars = 0; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if (g->flags & GF_USED) { + nchars++; + + g->rymin = 5000; + g->rymax = -5000; + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_LINE) { + + j = ge->iy3 - ge->prev->iy3; + k = ge->ix3 - ge->prev->ix3; + if (j > 0) + ang = atan2(-k, j) * 180.0 / M_PI; + else + ang = atan2(k, -j) * 180.0 / M_PI; + + k /= 100; + j /= 100; + if (ang > -45.0 && ang < 45.0) { + /* + * be careful to not overflow + * the counter + */ + hystl[HYSTBASE + (int) (ang * 10.0)] += (k * k + j * j) / 4; + } + if (ge->iy3 == ge->prev->iy3) { + if (ge->iy3 <= g->rymin) { + g->rymin = ge->iy3; + g->flatymin = 1; + } + if (ge->iy3 >= g->rymax) { + g->rymax = ge->iy3; + g->flatymax = 1; + } + } else { + if (ge->iy3 < g->rymin) { + g->rymin = ge->iy3; + g->flatymin = 0; + } + if (ge->iy3 > g->rymax) { + g->rymax = ge->iy3; + g->flatymax = 0; + } + } + } else if (ge->type == GE_CURVE) { + if (ge->iy3 < g->rymin) { + g->rymin = ge->iy3; + g->flatymin = 0; + } + if (ge->iy3 > g->rymax) { + g->rymax = ge->iy3; + g->flatymax = 0; + } + } + if (ge->type == GE_LINE || ge->type == GE_CURVE) { + if (ge->ix3 < bbox[0]) + bbox[0] = ge->ix3; + if (ge->ix3 > bbox[2]) + bbox[2] = ge->ix3; + if (ge->iy3 < bbox[1]) + bbox[1] = ge->iy3; + if (ge->iy3 > bbox[3]) + bbox[3] = ge->iy3; + } + } + } + } + + /* get the most popular angle */ + max = 0; + w = 0; + for (i = 0; i < MAXHYST; i++) { + if (hystl[i] > w) { + w = hystl[i]; + max = i; + } + } + ang = (double) (max - HYSTBASE) / 10.0; + WARNING_2 fprintf(stderr, "Guessed italic angle: %f\n", ang); + if (italic_angle == 0.0) + italic_angle = ang; + + /* build the hystogram of the lower points */ + for (i = 0; i < MAXHYST; i++) + hystl[i] = 0; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if ((g->flags & GF_USED) + && g->rymin + HYSTBASE >= 0 && g->rymin < MAXHYST - HYSTBASE) { + hystl[g->rymin + HYSTBASE]++; + } + } + + /* build the hystogram of the upper points */ + for (i = 0; i < MAXHYST; i++) + hystu[i] = 0; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if ((g->flags & GF_USED) + && g->rymax + HYSTBASE >= 0 && g->rymax < MAXHYST - HYSTBASE) { + hystu[g->rymax + HYSTBASE]++; + } + } + + /* build the hystogram of all the possible lower zones with max width */ + for (i = 0; i < MAXHYST; i++) + zlhyst[i] = 0; + + for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { + for (j = 0; j < MAXBLUEWIDTH; j++) + zlhyst[i] += hystl[i + j]; + } + + /* build the hystogram of all the possible upper zones with max width */ + for (i = 0; i < MAXHYST; i++) + zuhyst[i] = 0; + + for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) { + for (j = 0; j < MAXBLUEWIDTH; j++) + zuhyst[i] += hystu[i + j]; + } + + /* find the baseline */ + w = bestblue(zlhyst, hystl, zuhyst, &bluevalues[0]); + if (0) + fprintf(stderr, "BaselineBlue zone %d%% %d...%d\n", w * 100 / nchars, + bluevalues[0], bluevalues[1]); + + if (w == 0) /* no baseline, something weird */ + return; + + /* find the upper zones */ + for (nblues = 2; nblues < 14; nblues += 2) { + w = bestblue(zuhyst, hystu, zlhyst, &bluevalues[nblues]); + + if (0) + fprintf(stderr, "Blue zone %d%% %d...%d\n", w * 100 / nchars, + bluevalues[nblues], bluevalues[nblues+1]); + + if (w * 20 < nchars) + break; /* don't save this zone */ + } + + /* find the lower zones */ + for (notherb = 0; notherb < 10; notherb += 2) { + w = bestblue(zlhyst, hystl, zuhyst, &otherblues[notherb]); + + if (0) + fprintf(stderr, "OtherBlue zone %d%% %d...%d\n", w * 100 / nchars, + otherblues[notherb], otherblues[notherb+1]); + + + if (w * 20 < nchars) + break; /* don't save this zone */ + } + +} + +/* + * Find the actual width of the glyph and modify the + * description to reflect it. Not guaranteed to do + * any good, may make character spacing too wide. + */ + +void +docorrectwidth(void) +{ + int i; + GENTRY *ge; + GLYPH *g; + int xmin, xmax; + int maxwidth, minsp; + + /* enforce this minimal spacing, + * we limit the amount of the enforced spacing to avoid + * spacing the bold wonts too widely + */ + minsp = (stdhw>60 || stdhw<10)? 60 : stdhw; + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + g->oldwidth=g->scaledwidth; /* save the old width, will need for AFM */ + + if (correctwidth && g->flags & GF_USED) { + xmin = 5000; + xmax = -5000; + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type != GE_LINE && ge->type != GE_CURVE) + continue; + + if (ge->ix3 <= xmin) { + xmin = ge->ix3; + } + if (ge->ix3 >= xmax) { + xmax = ge->ix3; + } + } + + maxwidth=xmax+minsp; + if( g->scaledwidth < maxwidth ) { + g->scaledwidth = maxwidth; + WARNING_3 fprintf(stderr, "glyph %s: extended from %d to %d\n", + g->name, g->oldwidth, g->scaledwidth ); + } + } + } + +} + +/* + * Try to find the typical stem widths + */ + +void +stemstatistics(void) +{ +#define MINDIST 10 /* minimal distance between the widths */ + int hyst[MAXHYST+MINDIST*2]; + int best[12]; + int i, j, k, w; + int nchars; + int ns; + STEM *s; + GLYPH *g; + + /* start with typical stem width */ + + nchars=0; + + /* build the hystogram of horizontal stem widths */ + memset(hyst, 0, sizeof hyst); + + for (i = 0, g = glyph_list; i < numglyphs; i++, g++) { + if (g->flags & GF_USED) { + nchars++; + s = g->hstems; + for (j = 0; j < g->nhs; j += 2) { + if ((s[j].flags | s[j + 1].flags) & ST_END) + continue; + w = s[j + 1].value - s[j].value+1; + if(w==20) /* split stems should not be counted */ + continue; + if (w > 0 && w < MAXHYST - 1) { + /* + * handle some fuzz present in + * converted fonts + */ + hyst[w+MINDIST] += MINDIST-1; + for(k=1; kflags & GF_USED) { + s = g->vstems; + for (j = 0; j < g->nvs; j += 2) { + if ((s[j].flags | s[j + 1].flags) & ST_END) + continue; + w = s[j + 1].value - s[j].value+1; + if (w > 0 && w < MAXHYST - 1) { + /* + * handle some fuzz present in + * converted fonts + */ + hyst[w+MINDIST] += MINDIST-1; + for(k=1; knext) { + if(ge->type == GE_LINE || ge->type == GE_CURVE) { + if (ISDBG(REVERSAL)) + fprintf(stderr, "reverse path 0x%x <- 0x%x, 0x%x\n", ge, ge->prev, ge->bkwd); + + /* cut out the path itself */ + pge = ge->prev; /* GE_MOVE */ + if (pge == 0) { + fprintf(stderr, "**! No MOVE before line !!! Fatal. ****\n"); + exit(1); + } + nge = ge->bkwd->next; /* GE_PATH */ + pge->next = nge; + nge->prev = pge; + ge->bkwd->next = 0; /* mark end of chain */ + + /* remember the starting point */ + if(ge->flags & GEF_FLOAT) { + flast[0] = pge->fx3; + flast[1] = pge->fy3; + } else { + ilast[0] = pge->ix3; + ilast[1] = pge->iy3; + } + + /* then reinsert them in backwards order */ + for(cur = ge; cur != 0; cur = next ) { + next = cur->next; /* or addgeafter() will screw it up */ + if(cur->flags & GEF_FLOAT) { + for(i=0; i<2; i++) { + /* reverse the direction of path element */ + f = cur->fpoints[i][0]; + cur->fpoints[i][0] = cur->fpoints[i][1]; + cur->fpoints[i][1] = f; + f = flast[i]; + flast[i] = cur->fpoints[i][2]; + cur->fpoints[i][2] = f; + } + } else { + for(i=0; i<2; i++) { + /* reverse the direction of path element */ + n = cur->ipoints[i][0]; + cur->ipoints[i][0] = cur->ipoints[i][1]; + cur->ipoints[i][1] = n; + n = ilast[i]; + ilast[i] = cur->ipoints[i][2]; + cur->ipoints[i][2] = n; + } + } + addgeafter(pge, cur); + } + + /* restore the starting point */ + if(ge->flags & GEF_FLOAT) { + pge->fx3 = flast[0]; + pge->fy3 = flast[1]; + } else { + pge->ix3 = ilast[0]; + pge->iy3 = ilast[1]; + } + + ge = nge; + } + + } +} + +void +reversepaths( + GLYPH * g +) +{ + reversepathsfromto(g->entries, NULL); +} + +/* add a kerning pair information, scales the value */ + +void +addkernpair( + unsigned id1, + unsigned id2, + int unscval +) +{ + static unsigned char *bits = 0; + static int lastid; + GLYPH *g = &glyph_list[id1]; + int i, n; + struct kern *p; + + if(unscval == 0 || id1 >= numglyphs || id2 >= numglyphs) + return; + + if( (glyph_list[id1].flags & GF_USED)==0 + || (glyph_list[id2].flags & GF_USED)==0 ) + return; + + if(bits == 0) { + bits = calloc( BITMAP_BYTES(numglyphs), 1); + if (bits == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + lastid = id1; + } + + if(lastid != id1) { + /* refill the bitmap cache */ + memset(bits, 0,BITMAP_BYTES(numglyphs)); + p = g->kern; + for(i=g->kerncount; i>0; i--) { + n = (p++)->id; + SET_BITMAP(bits, n); + } + lastid = id1; + } + + if(IS_BITMAP(bits, id2)) + return; /* duplicate */ + + if(g->kerncount <= g->kernalloc) { + g->kernalloc += 8; + p = realloc(g->kern, sizeof(struct kern) * g->kernalloc); + if(p == 0) { + fprintf (stderr, "** realloc failed, kerning data will be incomplete\n"); + } + g->kern = p; + } + + SET_BITMAP(bits, id2); + p = &g->kern[g->kerncount]; + p->id = id2; + p->val = iscale(unscval) - (g->scaledwidth - g->oldwidth); + g->kerncount++; + kerning_pairs++; +} + +/* print out the kerning information */ + +void +print_kerning( + FILE *afm_file +) +{ + int i, j, n; + GLYPH *g; + struct kern *p; + + if( kerning_pairs == 0 ) + return; + + fprintf(afm_file, "StartKernData\n"); + fprintf(afm_file, "StartKernPairs %hd\n", kerning_pairs); + + for(i=0; iflags & GF_USED) ==0) + continue; + p = g->kern; + for(j=g->kerncount; j>0; j--, p++) { + fprintf(afm_file, "KPX %s %s %d\n", g->name, + glyph_list[ p->id ].name, p->val ); + } + } + + fprintf(afm_file, "EndKernPairs\n"); + fprintf(afm_file, "EndKernData\n"); +} + + +#if 0 + +/* +** This function is commented out because the information +** collected by it is not used anywhere else yet. Now +** it only collects the directions of contours. And the +** direction of contours gets fixed already in draw_glyf(). +** +*********************************************** +** +** Here we expect that the paths are already closed. +** We also expect that the contours do not intersect +** and that curves doesn't cross any border of quadrant. +** +** Find which contours go inside which and what is +** their proper direction. Then fix the direction +** to make it right. +** +*/ + +#define MAXCONT 1000 + +void +fixcontours( + GLYPH * g +) +{ + CONTOUR cont[MAXCONT]; + short ymax[MAXCONT]; /* the highest point */ + short xofmax[MAXCONT]; /* X-coordinate of any point + * at ymax */ + short ymin[MAXCONT]; /* the lowest point */ + short xofmin[MAXCONT]; /* X-coordinate of any point + * at ymin */ + short count[MAXCONT]; /* count of lines */ + char dir[MAXCONT]; /* in which direction they must go */ + GENTRY *start[MAXCONT], *minptr[MAXCONT], *maxptr[MAXCONT]; + int ncont; + int i; + int dx1, dy1, dx2, dy2; + GENTRY *ge, *nge; + + /* find the contours and their most upper/lower points */ + ncont = 0; + ymax[0] = -5000; + ymin[0] = 5000; + for (ge = g->entries; ge != 0; ge = ge->next) { + if (ge->type == GE_LINE || ge->type == GE_CURVE) { + if (ge->iy3 > ymax[ncont]) { + ymax[ncont] = ge->iy3; + xofmax[ncont] = ge->ix3; + maxptr[ncont] = ge; + } + if (ge->iy3 < ymin[ncont]) { + ymin[ncont] = ge->iy3; + xofmin[ncont] = ge->ix3; + minptr[ncont] = ge; + } + } + if (ge->frwd != ge->next) { + start[ncont++] = ge->frwd; + ymax[ncont] = -5000; + ymin[ncont] = 5000; + } + } + + /* determine the directions of contours */ + for (i = 0; i < ncont; i++) { + ge = minptr[i]; + nge = ge->frwd; + + if (ge->type == GE_CURVE) { + dx1 = ge->ix3 - ge->ix2; + dy1 = ge->iy3 - ge->iy2; + + if (dx1 == 0 && dy1 == 0) { /* a pathological case */ + dx1 = ge->ix3 - ge->ix1; + dy1 = ge->iy3 - ge->iy1; + } + if (dx1 == 0 && dy1 == 0) { /* a more pathological + * case */ + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + } + } else { + dx1 = ge->ix3 - ge->prev->ix3; + dy1 = ge->iy3 - ge->prev->iy3; + } + if (nge->type == GE_CURVE) { + dx2 = ge->ix3 - nge->ix1; + dy2 = ge->iy3 - nge->iy1; + if (dx1 == 0 && dy1 == 0) { /* a pathological case */ + dx2 = ge->ix3 - nge->ix2; + dy2 = ge->iy3 - nge->iy2; + } + if (dx1 == 0 && dy1 == 0) { /* a more pathological + * case */ + dx2 = ge->ix3 - nge->ix3; + dy2 = ge->iy3 - nge->iy3; + } + } else { + dx2 = ge->ix3 - nge->ix3; + dy2 = ge->iy3 - nge->iy3; + } + + /* compare angles */ + cont[i].direction = DIR_INNER; + if (dy1 == 0) { + if (dx1 < 0) + cont[i].direction = DIR_OUTER; + } else if (dy2 == 0) { + if (dx2 > 0) + cont[i].direction = DIR_OUTER; + } else if (dx2 * dy1 < dx1 * dy2) + cont[i].direction = DIR_OUTER; + + cont[i].ymin = ymin[i]; + cont[i].xofmin = xofmin[i]; + } + + /* save the information that may be needed further */ + g->ncontours = ncont; + if (ncont > 0) { + g->contours = malloc(sizeof(CONTOUR) * ncont); + if (g->contours == 0) { + fprintf(stderr, "***** Memory allocation error *****\n"); + exit(255); + } + memcpy(g->contours, cont, sizeof(CONTOUR) * ncont); + } +} + +#endif + +/* + * + */ + diff --git a/pdf2swf/ttf2pt1/pt1.h b/pdf2swf/ttf2pt1/pt1.h new file mode 100644 index 0000000..7540531 --- /dev/null +++ b/pdf2swf/ttf2pt1/pt1.h @@ -0,0 +1,234 @@ +/* + * see COPYRIGHT + */ + + +/* glyph entry, one drawing command */ +typedef struct gentry { + /* this list links all GENTRYs of a GLYPH sequentially */ + struct gentry *next; /* double linked list */ + struct gentry *prev; + + /* this list links all GENTRYs of one contour - + * of types GE_LINE and GE_CURVE only + * bkwd is also reused: in the very first entry (normally + * of type GE_MOVE) it points to g->entries + */ + struct gentry *cntr[2]; /* double-linked circular list */ +/* convenience handles */ +#define bkwd cntr[0] +#define frwd cntr[1] + + union { + struct { + int val[2][3]; /* integer values */ + } i; + struct { + double val[2][3]; /* floating values */ + } f; + } points; /* absolute values, NOT deltas */ +/* convenience handles */ +#define ipoints points.i.val +#define fpoints points.f.val +#define ixn ipoints[0] +#define iyn ipoints[1] +#define fxn fpoints[0] +#define fyn fpoints[1] +#define ix1 ixn[0] +#define ix2 ixn[1] +#define ix3 ixn[2] +#define iy1 iyn[0] +#define iy2 iyn[1] +#define iy3 iyn[2] +#define fx1 fxn[0] +#define fx2 fxn[1] +#define fx3 fxn[2] +#define fy1 fyn[0] +#define fy2 fyn[1] +#define fy3 fyn[2] + + char flags; +#define GEF_FLOAT 0x02 /* entry contains floating point data */ + + unsigned char dir; /* used to temporarily store the values for + * the directions of the ends of curves */ +/* front end */ +#define CVDIR_FUP 0x02 /* goes over the line connecting the ends */ +#define CVDIR_FEQUAL 0x01 /* coincides with the line connecting the + * ends */ +#define CVDIR_FDOWN 0x00 /* goes under the line connecting the ends */ +#define CVDIR_FRONT 0x0F /* mask of all front directions */ +/* rear end */ +#define CVDIR_RSAME 0x30 /* is the same as for the front end */ +#define CVDIR_RUP 0x20 /* goes over the line connecting the ends */ +#define CVDIR_REQUAL 0x10 /* coincides with the line connecting the + * ends */ +#define CVDIR_RDOWN 0x00 /* goes under the line connecting the ends */ +#define CVDIR_REAR 0xF0 /* mask of all rear directions */ + + signed char stemid; /* connection to the substituted stem group */ + char type; +#define GE_HSBW 'B' +#define GE_MOVE 'M' +#define GE_LINE 'L' +#define GE_CURVE 'C' +#define GE_PATH 'P' +} GENTRY; + +/* stem structure, describes one [hv]stem */ +/* acually, it describes one border of a stem */ +/* the whole stem is a pair of these structures */ + +typedef struct stem { + short value; /* value of X or Y coordinate */ + short origin; /* point of origin for curve stems */ + GENTRY *ge; /* entry that has (value, origin) as its first dot */ + /* also for all the stems the couple (value, origin) + * is used to determine whether a stem is relevant for a + * line, it's considered revelant if this tuple is + * equal to any of the ends of the line. + * ge is also used to resolve ambiguity if there is more than + * one line going through certain pointi, it is used to + * distinguish these lines. + */ + + short from, to; /* values of other coordinate between + * which this stem is valid */ + + short flags; + /* ordering of ST_END, ST_FLAT, ST_ZONE is IMPORTANT for sorting */ +#define ST_END 0x01 /* end of line, lowest priority */ +#define ST_FLAT 0x02 /* stem is defined by a flat line, not a + * curve */ +#define ST_ZONE 0x04 /* pseudo-stem, the limit of a blue zone */ +#define ST_UP 0x08 /* the black area is to up or right from + * value */ +#define ST_3 0x20 /* first stem of [hv]stem3 */ +#define ST_BLUE 0x40 /* stem is in blue zone */ +#define ST_TOPZONE 0x80 /* 1 - top zone, 0 - bottom zone */ +#define ST_VERT 0x100 /* vertical stem (used in substitutions) */ +} STEM; + +#define MAX_STEMS 2000 /* we can't have more stems than path + * elements (or hope so) */ +#define NSTEMGRP 50 /* maximal number of the substituted stem groups */ + +/* structure for economical representation of the + * substituted stems + */ + +typedef struct stembounds { + short low; /* low bound */ + short high; /* high bound */ + char isvert; /* 1 - vertical, 0 - horizontal */ + char already; /* temp. flag: is aleready included */ +} STEMBOUNDS; + +struct kern { + unsigned id; /* ID of the second glyph */ + int val; /* kerning value */ +}; + +typedef struct contour { + short ymin, xofmin; + short inside; /* inside which contour */ + char direction; +#define DIR_OUTER 1 +#define DIR_INNER 0 +} CONTOUR; + +typedef struct glyph { + int char_no;/* Encoding of glyph */ + int orig_code;/* code of glyph in the font's original encoding */ + char *name; /* Postscript name of glyph */ + int xMin, yMin, xMax, yMax; /* values from TTF dictionary */ + int lsb; /* left sidebearing */ + int ttf_pathlen; /* total length of TTF paths */ + short width; + short flags; +#define GF_USED 0x0001 /* whether is this glyph used in T1 font */ +#define GF_FLOAT 0x0002 /* thys glyph contains floating point entries */ + + GENTRY *entries;/* doube linked list of entries */ + GENTRY *lastentry; /* the last inserted entry */ + GENTRY *path; /* beggining of the last path */ + int oldwidth; /* actually also scaled */ + int scaledwidth; +#define MAXLEGALWIDTH 10000 + + struct kern *kern; /* kerning data */ + int kerncount; /* number of kerning pairs */ + int kernalloc; /* for how many pairs we have space */ + + STEM *hstems; /* global horiz. and vert. stems */ + STEM *vstems; + int nhs, nvs; /* numbers of stems */ + + STEMBOUNDS *sbstems; /* substituted stems for all the groups */ + short *nsbs; /* indexes of the group ends in the common array */ + int nsg; /* actual number of the stem groups */ + int firstsubr; /* first substistuted stems subroutine number */ + + CONTOUR *contours; /* it is not used now */ + int ncontours; + + int rymin, rymax; /* real values */ + /* do we have flat surfaces on top/bottom */ + char flatymin, flatymax; + +} GLYPH; + +extern int stdhw, stdvw; /* dominant stems widths */ +extern int stemsnaph[12], stemsnapv[12]; /* most typical stem width */ + +extern int bluevalues[14]; +extern int nblues; +extern int otherblues[10]; +extern int notherb; +extern int bbox[4]; /* the FontBBox array */ +extern double italic_angle; + +extern GLYPH *glyph_list; +extern int encoding[]; /* inverse of glyph[].char_no */ + +/* prototypes of functions */ +void rmoveto( int dx, int dy); +void rlineto( int dx, int dy); +void rrcurveto( int dx1, int dy1, int dx2, int dy2, int dx3, int dy3); +void assertpath( GENTRY * from, char *file, int line, char *name); + +void fg_rmoveto( GLYPH * g, double x, double y); +void ig_rmoveto( GLYPH * g, int x, int y); +void fg_rlineto( GLYPH * g, double x, double y); +void ig_rlineto( GLYPH * g, int x, int y); +void fg_rrcurveto( GLYPH * g, double x1, double y1, + double x2, double y2, double x3, double y3); +void ig_rrcurveto( GLYPH * g, int x1, int y1, + int x2, int y2, int x3, int y3); +void g_closepath( GLYPH * g); + +void pathtoint( GLYPH *g); +void ffixquadrants( GLYPH *g); +void flattencurves( GLYPH * g); +int checkcv( GENTRY * ge, int dx, int dy); +void iclosepaths( GLYPH * g); +void fclosepaths( GLYPH * g); +void smoothjoints( GLYPH * g); +void buildstems( GLYPH * g); +void fstraighten( GLYPH * g); +void istraighten( GLYPH * g, int zigonly); +void isplitzigzags( GLYPH * g); +void fsplitzigzags( GLYPH * g); +void fforceconcise( GLYPH * g); +void iforceconcise( GLYPH * g); +void reversepathsfromto( GENTRY * from, GENTRY * to); +void reversepaths( GLYPH * g); +void dumppaths( GLYPH * g, GENTRY *start, GENTRY *end); +void print_glyph( int glyphno); +int print_glyph_subs( int glyphno, int startid); +void print_glyph_metrics( int code, int glyphno); +void findblues(void); +void stemstatistics(void); +void docorrectwidth(void); +void addkernpair( unsigned id1, unsigned id2, int unscval); +void print_kerning( FILE *afm_file); diff --git a/pdf2swf/ttf2pt1/runt1asm.c b/pdf2swf/ttf2pt1/runt1asm.c new file mode 100644 index 0000000..58c4cad --- /dev/null +++ b/pdf2swf/ttf2pt1/runt1asm.c @@ -0,0 +1,61 @@ +/* + * Wrap-around code to either compile in t1asm or call it externally + * + * Copyright (C) 2000 by Sergey Babkin + * Copyright (C) 2000 by The TTF2PT1 Project + * + * See COPYRIGHT for full license + */ + +#ifdef EXTERNAL_T1ASM + +#include +#include + +FILE *ifp; +FILE *ofp; + +int +runt1asm( + int pfbflag +) +{ + char *cmd; + int id, od; + int error; + + /* first make a copy in case some of then is already stdin/stdout */ + if(( id = dup(fileno(ifp)) )<0) { + perror("** Re-opening input file for t1asm"); + exit(1); + } + if(( od = dup(fileno(ofp)) )<0) { + perror("** Re-opening output file for t1asm"); + exit(1); + } + fclose(ifp); fclose(ofp); + close(0); + if(( dup(id) )!=0) { + perror("** Re-directing input file for t1asm"); + exit(1); + } + close(1); + if(( dup(od) )!=1) { + perror("** Re-directing output file for t1asm"); + exit(1); + } + close(id); close(od); + + if(pfbflag) + error = execlp("t1asm", "t1asm", "-b", NULL); + else + error = execlp("t1asm", "t1asm", NULL); + + perror("** Calling t1asm"); + + exit(1); +} + +#else +# include "t1asm.c" +#endif diff --git a/pdf2swf/ttf2pt1/t1asm.c b/pdf2swf/ttf2pt1/t1asm.c new file mode 100644 index 0000000..d9c3061 --- /dev/null +++ b/pdf2swf/ttf2pt1/t1asm.c @@ -0,0 +1,604 @@ +/* t1asm + * + * This program `assembles' Adobe Type-1 font programs in pseudo-PostScript + * form into either PFB or PFA format. The human readable/editable input is + * charstring- and eexec-encrypted as specified in the `Adobe Type 1 Font + * Format' version 1.1 (the `black book'). There is a companion program, + * t1disasm, which `disassembles' PFB and PFA files into a pseudo-PostScript + * file. + * + * Copyright (c) 1992 by I. Lee Hetherington, all rights reserved. + * + * Permission is hereby granted to use, modify, and distribute this program + * for any purpose provided this copyright notice and the one below remain + * intact. + * + * I. Lee Hetherington (ilh@lcs.mit.edu) + * + * Revision 1.2 92/05/22 11:54:45 ilh + * Fixed bug where integers larger than 32000 could not be encoded in + * charstrings. Now integer range is correct for four-byte + * twos-complement integers: -(1<<31) <= i <= (1<<31)-1. Bug detected by + * Piet Tutelaers (rcpt@urc.tue.nl). + * + * Revision 1.1 92/05/22 11:48:46 ilh + * initial version + * + * Ported to Microsoft C/C++ Compiler and MS-DOS operating system by + * Kai-Uwe Herbing (herbing@netmbx.netmbx.de) on June 12, 1992. Code + * specific to the MS-DOS version is encapsulated with #ifdef _MSDOS + * ... #endif, where _MSDOS is an identifier, which is automatically + * defined, if you compile with the Microsoft C/C++ Compiler. + * + */ + +#ifndef lint +static char copyright[] = + "@(#) Copyright (c) 1992 by I. Lee Hetherington, all rights reserved."; +#ifdef _MSDOS +static char portnotice[] = + "@(#) Ported to MS-DOS by Kai-Uwe Herbing (herbing@netmbx.netmbx.de)."; +#endif +#endif + +/* Note: this is ANSI C. */ + +#ifdef _MSDOS + #include + #include + #include +#endif +#include +#include +#include +#include +#include + +#ifdef WINDOWS +# ifdef STANDALONE +# define WINDOWS_FUNCTIONS +# include "windows.h" +# endif +#endif + +/* int32 must be at least 32-bit and uint16 must be at least 16-bit */ +#if INT_MAX >= 0x7FFFFFFFUL +typedef int int32; +#else +typedef long int32; +#endif +#if USHRT_MAX >= 0xFFFFUL +typedef unsigned short uint16; +#else +typedef unsigned int uint16; +#endif + +#define LINESIZE 256 + +#define MAXBLOCKLEN ((1L<<17)-6) +#define MINBLOCKLEN ((1L<<8)-6) + +#define MARKER 128 +#define ASCII 1 +#define BINARY 2 +#define DONE 3 + +typedef unsigned char byte; + +/* must be visible from outside */ +FILE *ifp; +FILE *ofp; + +/* flags */ +static int pfb = 0; +static int active = 0; +static int start_charstring = 0; +static int in_eexec = 0; + +static char line[LINESIZE]; + +/* lenIV and charstring start command */ +static int lenIV = 4; +static char cs_start[10]; + +/* for charstring buffering */ +static byte charstring_buf[65535]; +static byte *charstring_bp; + +/* for PFB block buffering */ +static byte blockbuf[MAXBLOCKLEN]; +static int32 blocklen = MAXBLOCKLEN; +static int32 blockpos = -1; +static int blocktyp = ASCII; + +/* decryption stuff */ +static uint16 er, cr; +static uint16 c1 = 52845, c2 = 22719; + +/* table of charstring commands */ +static struct command { + char *name; + int one, two; +} command_table[] = { + { "callothersubr", 12, 16 }, + { "callsubr", 10, -1 }, + { "closepath", 9, -1 }, + { "div", 12, 12 }, + { "dotsection", 12, 0 }, + { "endchar", 14, -1 }, + { "hlineto", 6, -1 }, + { "hmoveto", 22, -1 }, + { "hsbw", 13, -1 }, + { "hstem", 1, -1 }, + { "hstem3", 12, 2 }, + { "hvcurveto", 31, -1 }, + { "pop", 12, 17 }, + { "return", 11, -1 }, + { "rlineto", 5, -1 }, + { "rmoveto", 21, -1 }, + { "rrcurveto", 8, -1 }, + { "sbw", 12, 7 }, + { "seac", 12, 6 }, + { "setcurrentpoint", 12, 33 }, + { "vhcurveto", 30, -1 }, + { "vlineto", 7, -1 }, + { "vmoveto", 4, -1 }, + { "vstem", 3, -1 }, + { "vstem3", 12, 1 }, +}; /* alphabetical */ + +/* Two separate encryption functions because eexec and charstring encryption + must proceed in parallel. */ + +static byte eencrypt(byte plain) +{ + byte cipher; + + cipher = (byte) (plain ^ (er >> 8)); + er = (uint16) ((cipher + er) * c1 + c2); + return cipher; +} + +static byte cencrypt(byte plain) +{ + byte cipher; + + cipher = (byte) (plain ^ (cr >> 8)); + cr = (uint16) ((cipher + cr) * c1 + c2); + return cipher; +} + +/* This function flushes a buffered PFB block. */ + +static void output_block() +{ + int32 i; + + /* output four-byte block length */ + fputc((int) (blockpos & 0xff), ofp); + fputc((int) ((blockpos >> 8) & 0xff), ofp); + fputc((int) ((blockpos >> 16) & 0xff), ofp); + fputc((int) ((blockpos >> 24) & 0xff), ofp); + + /* output block data */ + for (i = 0; i < blockpos; i++) + fputc(blockbuf[i], ofp); + + /* mark block buffer empty and uninitialized */ + blockpos = -1; +} + +/* This function outputs a single byte. If output is in PFB format then output + is buffered through blockbuf[]. If output is in PFA format, then output + will be hexadecimal if in_eexec is set, ASCII otherwise. */ + +static void output_byte(byte b) +{ + static char *hexchar = "0123456789ABCDEF"; + static int hexcol = 0; + + if (pfb) { + /* PFB */ + if (blockpos < 0) { + fputc(MARKER, ofp); + fputc(blocktyp, ofp); + blockpos = 0; + } + blockbuf[blockpos++] = b; + if (blockpos == blocklen) + output_block(); + } else { + /* PFA */ + if (in_eexec) { + /* trim hexadecimal lines to 64 columns */ + if (hexcol >= 64) { + fputc('\n', ofp); + hexcol = 0; + } + fputc(hexchar[(b >> 4) & 0xf], ofp); + fputc(hexchar[b & 0xf], ofp); + hexcol += 2; + } else { + fputc(b, ofp); + } + } +} + +/* This function outputs a byte through possible eexec encryption. */ + +static void eexec_byte(byte b) +{ + if (in_eexec) + output_byte(eencrypt(b)); + else + output_byte(b); +} + +/* This function outputs a null-terminated string through possible eexec + encryption. */ + +static void eexec_string(char *string) +{ + while (*string) + eexec_byte((byte) *string++); +} + +/* This function gets ready for the eexec-encrypted data. If output is in + PFB format then flush current ASCII block and get ready for binary block. + We start encryption with four random (zero) bytes. */ + +static void eexec_start() +{ + eexec_string(line); + if (pfb) { + output_block(); + blocktyp = BINARY; + } + + in_eexec = 1; + er = 55665; + eexec_byte(0); + eexec_byte(0); + eexec_byte(0); + eexec_byte(0); +} + +/* This function wraps-up the eexec-encrypted data. + If output is in PFB format then this entails flushing binary block and + starting an ASCII block. */ + +static void eexec_end() +{ + int i, j; + + if (pfb) { + output_block(); + blocktyp = ASCII; + } else { + fputc('\n', ofp); + } + in_eexec = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 64; j++) + eexec_byte('0'); + eexec_byte('\n'); + } +#if 0 + eexec_string("cleartomark\n"); +#endif +} + +/* This function writes ASCII trailer. + If output is in PFB format then this entails flushing binary block and + starting an ASCII block. */ + +static void file_end() +{ + if (pfb) { + output_block(); + fputc(MARKER, ofp); + fputc(DONE, ofp); + } +} +/* This function returns an input line of characters. A line is terminated by + length (including terminating null) greater than LINESIZE, a newline \n, or + when active (looking for charstrings) by '{'. When terminated by a newline + the newline is put into line[]. When terminated by '{', the '{' is not put + into line[], and the flag start_charstring is set to 1. */ + +static void t1asm_getline() +{ + int c; + char *p = line; + int comment = 0; + + start_charstring = 0; + while (p < line + LINESIZE) { + c = fgetc(ifp); + if (c == EOF) + break; + if (c == '%') + comment = 1; + if (active && !comment && c == '{') { + start_charstring = 1; + break; + } + *p++ = (char) c; + if (c == '\n') + break; + } + *p = '\0'; +} + +/* This function is used by the binary search, bsearch(), for command names in + the command table. */ + +static int command_compare(const void *key, const void *item) +{ + return strcmp((char *) key, ((struct command *) item)->name); +} + +/* This function returns 1 if the string is an integer and 0 otherwise. */ + +static int is_integer(char *string) +{ + if (isdigit(string[0]) || string[0] == '-' || string[0] == '+') { + while (*++string && isdigit(*string)) + ; /* deliberately empty */ + if (!*string) + return 1; + } + return 0; +} + +/* This function initializes charstring encryption. Note that this is called + at the beginning of every charstring. */ + +static void charstring_start() +{ + int i; + + charstring_bp = charstring_buf; + cr = 4330; + for (i = 0; i < lenIV; i++) + *charstring_bp++ = cencrypt((byte) 0); +} + +/* This function encrypts and buffers a single byte of charstring data. */ + +static void charstring_byte(int v) +{ + byte b = (byte) (v & 0xff); + + if (charstring_bp - charstring_buf > sizeof(charstring_buf)) { + fprintf(stderr, "error: charstring_buf full (%d bytes)\n", + sizeof(charstring_buf)); + exit(1); + } + *charstring_bp++ = cencrypt(b); +} + +/* This function outputs buffered, encrypted charstring data through possible + eexec encryption. */ + +static void charstring_end() +{ + byte *bp; + + sprintf(line, "%d ", charstring_bp - charstring_buf); + eexec_string(line); + sprintf(line, "%s ", cs_start); + eexec_string(line); + for (bp = charstring_buf; bp < charstring_bp; bp++) + eexec_byte(*bp); +} + +/* This function generates the charstring representation of an integer. */ + +static void charstring_int(int num) +{ + int x; + + if (num >= -107 && num <= 107) { + charstring_byte(num + 139); + } else if (num >= 108 && num <= 1131) { + x = num - 108; + charstring_byte(x / 256 + 247); + charstring_byte(x % 256); + } else if (num >= -1131 && num <= -108) { + x = abs(num) - 108; + charstring_byte(x / 256 + 251); + charstring_byte(x % 256); + } else if (num >= (-2147483647-1) && num <= 2147483647) { + charstring_byte(255); + charstring_byte(num >> 24); + charstring_byte(num >> 16); + charstring_byte(num >> 8); + charstring_byte(num); + } else { + fprintf(stderr, + "error: cannot format the integer %d, too large\n", num); + exit(1); + } +} + +/* This function parses an entire charstring into integers and commands, + outputting bytes through the charstring buffer. */ + +static void parse_charstring() +{ + struct command *cp; + + charstring_start(); + while (fscanf(ifp, "%s", line) == 1) { + if (line[0] == '%') { + /* eat comment to end of line */ + while (fgetc(ifp) != '\n' && !feof(ifp)) + ; /* deliberately empty */ + continue; + } + if (line[0] == '}') + break; + if (is_integer(line)) { + charstring_int(atoi(line)); + } else { + cp = (struct command *) + bsearch((void *) line, (void *) command_table, + sizeof(command_table) / sizeof(struct command), + sizeof(struct command), + command_compare); + if (cp) { + charstring_byte(cp->one); + if (cp->two >= 0) + charstring_byte(cp->two); + } else { + fprintf(stderr, "error: cannot use `%s' in charstring\n",line); + exit(1); + } + } + } + charstring_end(); +} + +static void usage() +{ + fprintf(stderr, + "usage: t1asm [-b] [-l block-length] [input [output]]\n"); + fprintf(stderr, + "\n-b means output in PFB format, otherwise PFA format.\n"); + fprintf(stderr, + "The block length applies to the length of blocks in the\n"); + fprintf(stderr, + "PFB output file; the default is to use the largest possible.\n"); + exit(1); +} + +static void print_banner() +{ + static char rcs_revision[] = ""; /* removed RCS */ + static char revision[20]; + + if (sscanf(rcs_revision, "$Revision: %19s", revision) != 1) + revision[0] = '\0'; + fprintf(stderr, "This is t1asm %s.\n", revision); +} + +#ifdef STANDALONE +int main(int argc, char **argv) +{ + char *p, *q, *r; + int c; + + extern char *optarg; + extern int optind; + + ifp = stdin; + ofp = stdout; + + print_banner(); + + /* interpret command line arguments using getopt */ + while ((c = getopt(argc, argv, "bl:")) != -1) + switch (c) { + case 'b': + pfb = 1; + break; + case 'l': + blocklen = atoi(optarg); + if (blocklen < MINBLOCKLEN) { + blocklen = MINBLOCKLEN; + fprintf(stderr, + "warning: using minimum block length of %d\n", + blocklen); + } else if (blocklen > MAXBLOCKLEN) { + blocklen = MAXBLOCKLEN; + fprintf(stderr, + "warning: using maximum block length of %d\n", + blocklen); + } + break; + default: + usage(); + break; + } + if (argc - optind > 2) + usage(); + + /* possibly open input & output files */ + if (argc - optind >= 1) { + ifp = fopen(argv[optind], "r"); + if (!ifp) { + fprintf(stderr, "error: cannot open %s for reading\n", argv[1]); + exit(1); + } + } + if (argc - optind >= 2) { + ofp = fopen(argv[optind + 1], "w"); + if (!ofp) { + fprintf(stderr, "error: cannot open %s for writing\n", argv[2]); + exit(1); + } + } + +#else +int runt1asm(int pfbflag) +{ + char *p, *q, *r; + + pfb = pfbflag; +#endif + + #ifdef _MSDOS + /* If we are processing a PFB (binary) output */ + /* file, we must set its file mode to binary. */ + if (pfb) + _setmode(_fileno(ofp), _O_BINARY); + #endif + + /* Finally, we loop until no more input. Some special things to look for + are the `currentfile eexec' line, the beginning of the `/Subrs' + definition, the definition of `/lenIV', and the definition of the + charstring start command which has `...string currentfile...' in it. */ + + while (!feof(ifp) && !ferror(ifp)) { + t1asm_getline(); + if (strcmp(line, "currentfile eexec\n") == 0) { + eexec_start(); + continue; + } else if (strstr(line, "/Subrs") && isspace(line[6])) { + active = 1; + } else if ((p = strstr(line, "/lenIV"))) { + sscanf(p, "%*s %d", &lenIV); + } else if ((p = strstr(line, "string currentfile"))) { + /* locate the name of the charstring start command */ + *p = '\0'; /* damage line[] */ + q = strrchr(line, '/'); + if (q) { + r = cs_start; + ++q; + while (!isspace(*q) && *q != '{') + *r++ = *q++; + *r = '\0'; + } + *p = 's'; /* repair line[] */ + } + /* output line data */ + eexec_string(line); + if ((p = strstr(line, "currentfile closefile"))) { + eexec_end(); + } + if (start_charstring) { + if (!cs_start[0]) { + fprintf(stderr, "error: couldn't find charstring start command\n"); + exit(1); + } + parse_charstring(); + } + } + file_end(); + + fclose(ifp); + fclose(ofp); + + return 0; +} diff --git a/pdf2swf/ttf2pt1/ttf.c b/pdf2swf/ttf2pt1/ttf.c new file mode 100644 index 0000000..c59b2f7 --- /dev/null +++ b/pdf2swf/ttf2pt1/ttf.c @@ -0,0 +1,1479 @@ +/* + * True Type Font to Adobe Type 1 font converter + * By Mark Heath + * Based on ttf2pfa by Andrew Weeks + * With help from Frank M. Siegert + * + * see COPYRIGHT + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WINDOWS +# include +# include +#else +# include "windows.h" +#endif + +#include "ttf.h" +#include "pt1.h" +#include "global.h" + +/* prototypes of call entries */ +static void openfont(char *fname, char *arg); +static void closefont( void); +static int getnglyphs ( void); +static int glnames( GLYPH *glyph_list); +static void glmetrics( GLYPH *glyph_list); +static int glenc( GLYPH *glyph_list, int *encoding, int *unimap); +static void fnmetrics( struct font_metrics *fm); +static void glpath( int glyphno, GLYPH *glyph_list); +static void kerning( GLYPH *glyph_list); + +/* globals */ + +/* front-end descriptor */ +struct frontsw ttf_sw = { + /*name*/ "ttf", + /*descr*/ "built-in TTF support", + /*suffix*/ { "ttf" }, + /*open*/ openfont, + /*close*/ closefont, + /*nglyphs*/ getnglyphs, + /*glnames*/ glnames, + /*glmetrics*/ glmetrics, + /*glenc*/ glenc, + /*fnmetrics*/ fnmetrics, + /*glpath*/ glpath, + /*kerning*/ kerning, +}; + +/* statics */ + +static FILE *ttf_file; +static int ttf_nglyphs, long_offsets; + +static TTF_DIRECTORY *directory; +static TTF_DIR_ENTRY *dir_entry; +static char *filebuffer; +static char *filebuffer_end; +static TTF_NAME *name_table = NULL; +static TTF_NAME_REC *name_record; +static TTF_HEAD *head_table = NULL; +static TTF_HHEA *hhea_table = NULL; +static TTF_KERN *kern_table = NULL; +static TTF_CMAP *cmap_table = NULL; +static LONGHORMETRIC *hmtx_table = NULL; +static TTF_GLYF *glyf_table; +static BYTE *glyf_start = NULL; +static TTF_MAXP *maxp_table = NULL; +static TTF_POST_HEAD *post_table = NULL; +static union { + USHORT *sp; + ULONG *lp; +} loca_table; +#define short_loca_table loca_table.sp +#define long_loca_table loca_table.lp + +static short cmap_n_segs; +static USHORT *cmap_seg_start, *cmap_seg_end; +static short *cmap_idDelta, *cmap_idRangeOffset; +static TTF_CMAP_FMT0 *encoding0; +static int enc_type; + +static char name_buffer[2000]; +static char *name_fields[8]; + +static int enc_found_ms, enc_found_mac; + +static char *mac_glyph_names[258] = { + ".notdef", ".null", "CR", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "Adieresis", + "Aring", "Ccedilla", "Eacute", "Ntilde", + "Odieresis", "Udieresis", "aacute", "agrave", + "acircumflex", "adieresis", "atilde", "aring", + "ccedilla", "eacute", "egrave", "ecircumflex", + "edieresis", "iacute", "igrave", "icircumflex", + "idieresis", "ntilde", "oacute", "ograve", + "ocircumflex", "odieresis", "otilde", "uacute", + "ugrave", "ucircumflex", "udieresis", "dagger", + "degree", "cent", "sterling", "section", + "bullet", "paragraph", "germandbls", "registered", + "copyright", "trademark", "acute", "dieresis", + "notequal", "AE", "Oslash", "infinity", + "plusminus", "lessequal", "greaterequal", "yen", + "mu", "partialdiff", "summation", "product", + "pi", "integral", "ordfeminine", "ordmasculine", + "Omega", "ae", "oslash", "questiondown", + "exclamdown", "logicalnot", "radical", "florin", + "approxequal", "increment", "guillemotleft", "guillemotright", + "ellipsis", "nbspace", "Agrave", "Atilde", + "Otilde", "OE", "oe", "endash", + "emdash", "quotedblleft", "quotedblright", "quoteleft", + "quoteright", "divide", "lozenge", "ydieresis", + "Ydieresis", "fraction", "currency", "guilsinglleft", + "guilsinglright", "fi", "fl", "daggerdbl", + "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", + "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", + "Egrave", "Iacute", "Icircumflex", "Idieresis", + "Igrave", "Oacute", "Ocircumflex", "applelogo", + "Ograve", "Uacute", "Ucircumflex", "Ugrave", + "dotlessi", "circumflex", "tilde", "macron", + "breve", "dotaccent", "ring", "cedilla", + "hungarumlaut", "ogonek", "caron", "Lslash", + "lslash", "Scaron", "scaron", "Zcaron", + "zcaron", "brokenbar", "Eth", "eth", + "Yacute", "yacute", "Thorn", "thorn", + "minus", "multiply", "onesuperior", "twosuperior", + "threesuperior", "onehalf", "onequarter", "threequarters", + "franc", "Gbreve", "gbreve", "Idot", + "Scedilla", "scedilla", "Cacute", "cacute", + "Ccaron", "ccaron", "dmacron" +}; + +/* other prototypes */ +static void draw_composite_glyf( GLYPH *g, GLYPH *glyph_list, int glyphno, + double *matrix, int level); +static void draw_simple_glyf( GLYPH *g, GLYPH *glyph_list, int glyphno, + double *matrix); +static double f2dot14( short x); + +/* get the TTF description table address and length for this index */ + +static void +get_glyf_table( + int glyphno, + TTF_GLYF **tab, + int *len +) +{ + if(tab!=NULL) { + if (long_offsets) { + *tab = (TTF_GLYF *) (glyf_start + ntohl(long_loca_table[glyphno])); + } else { + *tab = (TTF_GLYF *) (glyf_start + (ntohs(short_loca_table[glyphno]) << 1)); + } + } + if(len!=NULL) { + if (long_offsets) { + *len = ntohl(long_loca_table[glyphno + 1]) - ntohl(long_loca_table[glyphno]); + } else { + *len = (ntohs(short_loca_table[glyphno + 1]) - ntohs(short_loca_table[glyphno])) << 1; + } + } +} + +static void +handle_name(void) +{ + int j, k, lang, len, platform; + char *p, *string_area; + char *nbp = name_buffer; + int found3 = 0; + + string_area = (char *) name_table + ntohs(name_table->offset); + name_record = &(name_table->nameRecords); + + for (j = 0; j < 8; j++) { + name_fields[j] = ""; + } + + for (j = 0; j < ntohs(name_table->numberOfNameRecords); j++) { + + platform = ntohs(name_record->platformID); + + if (platform == 3) { + + found3 = 1; + lang = ntohs(name_record->languageID) & 0xff; + len = ntohs(name_record->stringLength); + if (lang == 0 || lang == 9) { + k = ntohs(name_record->nameID); + if (k < 8) { + name_fields[k] = nbp; + + p = string_area + ntohs(name_record->stringOffset); + for (k = 0; k < len; k++) { + if (p[k] != '\0') { + if (p[k] == '(') { + *nbp = '['; + } else if (p[k] == ')') { + *nbp = ']'; + } else { + *nbp = p[k]; + } + nbp++; + } + } + *nbp = '\0'; + nbp++; + } + } + } + name_record++; + } + + string_area = (char *) name_table + ntohs(name_table->offset); + name_record = &(name_table->nameRecords); + + if (!found3) { + for (j = 0; j < ntohs(name_table->numberOfNameRecords); j++) { + + platform = ntohs(name_record->platformID); + + if (platform == 1) { + + found3 = 1; + lang = ntohs(name_record->languageID) & 0xff; + len = ntohs(name_record->stringLength); + if (lang == 0 || lang == 9) { + k = ntohs(name_record->nameID); + if (k < 8) { + name_fields[k] = nbp; + + p = string_area + ntohs(name_record->stringOffset); + for (k = 0; k < len; k++) { + if (p[k] != '\0') { + if (p[k] == '(') { + *nbp = '['; + } else if (p[k] == ')') { + *nbp = ']'; + } else { + *nbp = p[k]; + } + nbp++; + } + } + *nbp = '\0'; + nbp++; + } + } + } + name_record++; + } + } + if (!found3) { + fprintf(stderr, "**** Cannot decode font name fields ****\n"); + exit(1); + } + if (name_fields[4][0] == 0) { /* Full Name empty, use Family Name */ + name_fields[4] = name_fields[1]; + } + if (name_fields[6][0] == 0) { /* Font Name empty, use Full Name */ + name_fields[6] = name_fields[4]; + if (name_fields[6][0] == 0) { /* oops, empty again */ + WARNING_1 fprintf(stderr, "Font name is unknown, setting to \"Unknown\"\n"); + name_fields[6] = "Unknown"; + } + } + p = name_fields[6]; + /* must not start with a digit */ + if(isdigit(*p)) + *p+= 'A'-'0'; /* change to a letter */ + while (*p != '\0') { + if (!isalnum(*p) || *p=='_') { + *p = '-'; + } + p++; + } +} + +static void +handle_head(void) +{ + long_offsets = ntohs(head_table->indexToLocFormat); + if (long_offsets != 0 && long_offsets != 1) { + fprintf(stderr, "**** indexToLocFormat wrong ****\n"); + exit(1); + } +} + +/* limit the recursion level to avoid cycles */ +#define MAX_COMPOSITE_LEVEL 20 + +static void +draw_composite_glyf( + GLYPH *g, + GLYPH *glyph_list, + int glyphno, + double *orgmatrix, + int level +) +{ + int len; + short ncontours; + USHORT flagbyte, glyphindex; + double arg1, arg2; + BYTE *ptr; + char *bptr; + SHORT *sptr; + double matrix[6], newmatrix[6]; + + get_glyf_table(glyphno, &glyf_table, &len); + + if(len<=0) /* nothing to do */ + return; + + ncontours = ntohs(glyf_table->numberOfContours); + if (ncontours >= 0) { /* simple case */ + draw_simple_glyf(g, glyph_list, glyphno, orgmatrix); + return; + } + + if(ISDBG(COMPOSITE) && level ==0) + fprintf(stderr, "* %s [ %.2f %.2f %.2f %.2f %.2f %.2f ]\n", g->name, + orgmatrix[0], orgmatrix[1], orgmatrix[2], orgmatrix[3], + orgmatrix[4], orgmatrix[5]); + + /* complex case */ + if(level >= MAX_COMPOSITE_LEVEL) { + WARNING_1 fprintf(stderr, + "*** Glyph %s: stopped (possibly infinite) recursion at depth %d\n", + g->name, level); + return; + } + + ptr = ((BYTE *) glyf_table + sizeof(TTF_GLYF)); + sptr = (SHORT *) ptr; + do { + flagbyte = ntohs(*sptr); + sptr++; + glyphindex = ntohs(*sptr); + sptr++; + + if (flagbyte & ARG_1_AND_2_ARE_WORDS) { + arg1 = (short)ntohs(*sptr); + sptr++; + arg2 = (short)ntohs(*sptr); + sptr++; + } else { + bptr = (char *) sptr; + arg1 = (signed char) bptr[0]; + arg2 = (signed char) bptr[1]; + sptr++; + } + matrix[1] = matrix[2] = 0.0; + + if (flagbyte & WE_HAVE_A_SCALE) { + matrix[0] = matrix[3] = f2dot14(*sptr); + sptr++; + } else if (flagbyte & WE_HAVE_AN_X_AND_Y_SCALE) { + matrix[0] = f2dot14(*sptr); + sptr++; + matrix[3] = f2dot14(*sptr); + sptr++; + } else if (flagbyte & WE_HAVE_A_TWO_BY_TWO) { + matrix[0] = f2dot14(*sptr); + sptr++; + matrix[1] = f2dot14(*sptr); + sptr++; + matrix[2] = f2dot14(*sptr); + sptr++; + matrix[3] = f2dot14(*sptr); + sptr++; + } else { + matrix[0] = matrix[3] = 1.0; + } + + /* + * See * + * http://fonts.apple.com/TTRefMan/RM06/Chap6g + * lyf.html * matrix[0,1,2,3,4,5]=a,b,c,d,m,n + */ + + if (fabs(matrix[0]) > fabs(matrix[1])) + matrix[4] = fabs(matrix[0]); + else + matrix[4] = fabs(matrix[1]); + if (fabs(fabs(matrix[0]) - fabs(matrix[2])) <= 33. / 65536.) + matrix[4] *= 2.0; + + if (fabs(matrix[2]) > fabs(matrix[3])) + matrix[5] = fabs(matrix[2]); + else + matrix[5] = fabs(matrix[3]); + if (fabs(fabs(matrix[2]) - fabs(matrix[3])) <= 33. / 65536.) + matrix[5] *= 2.0; + + /* + * fprintf (stderr,"Matrix Opp %hd + * %hd\n",arg1,arg2); + */ +#if 0 + fprintf(stderr, "Matrix: %f %f %f %f %f %f\n", + matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5]); + fprintf(stderr, "Offset: %f %f (%s)\n", + arg1, arg2, + ((flagbyte & ARGS_ARE_XY_VALUES) ? "XY" : "index")); +#endif + + if (flagbyte & ARGS_ARE_XY_VALUES) { + matrix[4] *= arg1; + matrix[5] *= arg2; + } else { + WARNING_1 fprintf(stderr, + "*** Glyph %s: reusing scale from another glyph is unsupported\n", + g->name); + /* + * must extract values from a glyph + * but it seems to be too much pain + * and it's not clear now that it + * would be really used in any + * interesting font + */ + } + + /* at this point arg1,arg2 contain what logically should be matrix[4,5] */ + + /* combine matrices */ + + newmatrix[0] = orgmatrix[0]*matrix[0] + orgmatrix[2]*matrix[1]; + newmatrix[1] = orgmatrix[0]*matrix[2] + orgmatrix[2]*matrix[3]; + + newmatrix[2] = orgmatrix[1]*matrix[0] + orgmatrix[3]*matrix[1]; + newmatrix[3] = orgmatrix[1]*matrix[2] + orgmatrix[3]*matrix[3]; + + newmatrix[4] = orgmatrix[0]*matrix[4] + orgmatrix[2]*matrix[5] + orgmatrix[4]; + newmatrix[5] = orgmatrix[1]*matrix[4] + orgmatrix[3]*matrix[5] + orgmatrix[5]; + + if(ISDBG(COMPOSITE)) { + fprintf(stderr, "%*c+-> %2d %s [ %.2f %.2f %.2f %.2f %.2f %.2f ]\n", + level+1, ' ', level, glyph_list[glyphindex].name, + matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5]); + fprintf(stderr, "%*c = [ %.2f %.2f %.2f %.2f %.2f %.2f ]\n", + level+1, ' ', + newmatrix[0], newmatrix[1], newmatrix[2], newmatrix[3], + newmatrix[4], newmatrix[5]); + } + draw_composite_glyf(g, glyph_list, glyphindex, newmatrix, level+1); + + } while (flagbyte & MORE_COMPONENTS); +} + +static void +draw_simple_glyf( + GLYPH *g, + GLYPH *glyph_list, + int glyphno, + double *matrix +) +{ + int i, j, k, k1, len, first, cs, ce; + /* We assume that hsbw always sets to(0, 0) */ + double xlast = 0, ylast = 0; + int finished, nguide, contour_start, contour_end; + short ncontours, n_inst, last_point; + USHORT *contour_end_pt; + BYTE *ptr; +#define GLYFSZ 2000 + short xabs[GLYFSZ], yabs[GLYFSZ], xrel[GLYFSZ], yrel[GLYFSZ]; + double xcoord[GLYFSZ], ycoord[GLYFSZ]; + BYTE flags[GLYFSZ]; + double tx, ty; + int needreverse = 0; /* transformation may require + * that */ + GENTRY *lge; + + lge = g->lastentry; + + get_glyf_table(glyphno, &glyf_table, &len); + + if (len <= 0) { + WARNING_1 fprintf(stderr, + "**** Composite glyph %s refers to non-existent glyph %s, ignored\n", + g->name, + glyph_list[glyphno].name); + return; + } + ncontours = ntohs(glyf_table->numberOfContours); + if (ncontours < 0) { + WARNING_1 fprintf(stderr, + "**** Composite glyph %s refers to composite glyph %s, ignored\n", + g->name, + glyph_list[glyphno].name); + return; + } + contour_end_pt = (USHORT *) ((char *) glyf_table + sizeof(TTF_GLYF)); + + last_point = ntohs(contour_end_pt[ncontours - 1]); + n_inst = ntohs(contour_end_pt[ncontours]); + + ptr = ((BYTE *) contour_end_pt) + (ncontours << 1) + n_inst + 2; + j = k = 0; + while (k <= last_point) { + flags[k] = ptr[j]; + + if (ptr[j] & REPEAT) { + for (k1 = 0; k1 < ptr[j + 1]; k1++) { + k++; + flags[k] = ptr[j]; + } + j++; + } + j++; + k++; + } + + for (k = 0; k <= last_point; k++) { + if (flags[k] & XSHORT) { + if (flags[k] & XSAME) { + xrel[k] = ptr[j]; + } else { + xrel[k] = -ptr[j]; + } + j++; + } else if (flags[k] & XSAME) { + xrel[k] = 0.0; + } else { + xrel[k] = (short)( ptr[j] * 256 + ptr[j + 1] ); + j += 2; + } + if (k == 0) { + xabs[k] = xrel[k]; + } else { + xabs[k] = xrel[k] + xabs[k - 1]; + } + + } + + for (k = 0; k <= last_point; k++) { + if (flags[k] & YSHORT) { + if (flags[k] & YSAME) { + yrel[k] = ptr[j]; + } else { + yrel[k] = -ptr[j]; + } + j++; + } else if (flags[k] & YSAME) { + yrel[k] = 0; + } else { + yrel[k] = ptr[j] * 256 + ptr[j + 1]; + j += 2; + } + if (k == 0) { + yabs[k] = yrel[k]; + } else { + yabs[k] = yrel[k] + yabs[k - 1]; + } + } + + if (matrix) { + for (i = 0; i <= last_point; i++) { + tx = xabs[i]; + ty = yabs[i]; + xcoord[i] = fscale(matrix[0] * tx + matrix[2] * ty + matrix[4]); + ycoord[i] = fscale(matrix[1] * tx + matrix[3] * ty + matrix[5]); + } + } else { + for (i = 0; i <= last_point; i++) { + xcoord[i] = fscale(xabs[i]); + ycoord[i] = fscale(yabs[i]); + } + } + + i = j = 0; + first = 1; + + while (i <= ntohs(contour_end_pt[ncontours - 1])) { + contour_end = ntohs(contour_end_pt[j]); + + if (first) { + fg_rmoveto(g, xcoord[i], ycoord[i]); + xlast = xcoord[i]; + ylast = ycoord[i]; + contour_start = i; + first = 0; + } else if (flags[i] & ONOROFF) { + fg_rlineto(g, xcoord[i], ycoord[i]); + xlast = xcoord[i]; + ylast = ycoord[i]; + } else { + cs = i - 1; + finished = nguide = 0; + while (!finished) { + if (i == contour_end + 1) { + ce = contour_start; + finished = 1; + } else if (flags[i] & ONOROFF) { + ce = i; + finished = 1; + } else { + i++; + nguide++; + } + } + + switch (nguide) { + case 0: + fg_rlineto(g, xcoord[ce], ycoord[ce]); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + break; + + case 1: + fg_rrcurveto(g, + (xcoord[cs] + 2.0 * xcoord[cs + 1]) / 3.0, + (ycoord[cs] + 2.0 * ycoord[cs + 1]) / 3.0, + (2.0 * xcoord[cs + 1] + xcoord[ce]) / 3.0, + (2.0 * ycoord[cs + 1] + ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + + break; + + case 2: + fg_rrcurveto(g, + (-xcoord[cs] + 4.0 * xcoord[cs + 1]) / 3.0, + (-ycoord[cs] + 4.0 * ycoord[cs + 1]) / 3.0, + (4.0 * xcoord[cs + 2] - xcoord[ce]) / 3.0, + (4.0 * ycoord[cs + 2] - ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + break; + + case 3: + fg_rrcurveto(g, + (xcoord[cs] + 2.0 * xcoord[cs + 1]) / 3.0, + (ycoord[cs] + 2.0 * ycoord[cs + 1]) / 3.0, + (5.0 * xcoord[cs + 1] + xcoord[cs + 2]) / 6.0, + (5.0 * ycoord[cs + 1] + ycoord[cs + 2]) / 6.0, + (xcoord[cs + 1] + xcoord[cs + 2]) / 2.0, + (ycoord[cs + 1] + ycoord[cs + 2]) / 2.0 + ); + + fg_rrcurveto(g, + (xcoord[cs + 1] + 5.0 * xcoord[cs + 2]) / 6.0, + (ycoord[cs + 1] + 5.0 * ycoord[cs + 2]) / 6.0, + (5.0 * xcoord[cs + 2] + xcoord[cs + 3]) / 6.0, + (5.0 * ycoord[cs + 2] + ycoord[cs + 3]) / 6.0, + (xcoord[cs + 3] + xcoord[cs + 2]) / 2.0, + (ycoord[cs + 3] + ycoord[cs + 2]) / 2.0 + ); + + fg_rrcurveto(g, + (xcoord[cs + 2] + 5.0 * xcoord[cs + 3]) / 6.0, + (ycoord[cs + 2] + 5.0 * ycoord[cs + 3]) / 6.0, + (2.0 * xcoord[cs + 3] + xcoord[ce]) / 3.0, + (2.0 * ycoord[cs + 3] + ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + ylast = ycoord[ce]; + xlast = xcoord[ce]; + + break; + + default: + k1 = cs + nguide; + fg_rrcurveto(g, + (xcoord[cs] + 2.0 * xcoord[cs + 1]) / 3.0, + (ycoord[cs] + 2.0 * ycoord[cs + 1]) / 3.0, + (5.0 * xcoord[cs + 1] + xcoord[cs + 2]) / 6.0, + (5.0 * ycoord[cs + 1] + ycoord[cs + 2]) / 6.0, + (xcoord[cs + 1] + xcoord[cs + 2]) / 2.0, + (ycoord[cs + 1] + ycoord[cs + 2]) / 2.0 + ); + + for (k = cs + 2; k <= k1 - 1; k++) { + fg_rrcurveto(g, + (xcoord[k - 1] + 5.0 * xcoord[k]) / 6.0, + (ycoord[k - 1] + 5.0 * ycoord[k]) / 6.0, + (5.0 * xcoord[k] + xcoord[k + 1]) / 6.0, + (5.0 * ycoord[k] + ycoord[k + 1]) / 6.0, + (xcoord[k] + xcoord[k + 1]) / 2.0, + (ycoord[k] + ycoord[k + 1]) / 2.0 + ); + + } + + fg_rrcurveto(g, + (xcoord[k1 - 1] + 5.0 * xcoord[k1]) / 6.0, + (ycoord[k1 - 1] + 5.0 * ycoord[k1]) / 6.0, + (2.0 * xcoord[k1] + xcoord[ce]) / 3.0, + (2.0 * ycoord[k1] + ycoord[ce]) / 3.0, + xcoord[ce], + ycoord[ce] + ); + xlast = xcoord[ce]; + ylast = ycoord[ce]; + + break; + } + } + if (i >= contour_end) { + g_closepath(g); + first = 1; + i = contour_end + 1; + j++; + } else { + i++; + } + } + + if (matrix) { + /* guess whether do we need to reverse the results */ + + double x[3], y[3]; + int max = 0, from, to; + + /* transform a triangle going in proper direction */ + /* + * the origin of triangle is in (0,0) so we know it in + * advance + */ + + x[0] = y[0] = 0; + x[1] = matrix[0] * 0 + matrix[2] * 300; + y[1] = matrix[1] * 0 + matrix[3] * 300; + x[2] = matrix[0] * 300 + matrix[2] * 0; + y[2] = matrix[1] * 300 + matrix[3] * 0; + + /* then find the topmost point */ + for (i = 0; i < 3; i++) + if (y[i] > y[max]) + max = i; + from = (max + 3 - 1) % 3; + to = (max + 1) % 3; + + needreverse = 0; + + /* special cases for horizontal lines */ + if (y[max] == y[from]) { + if (x[max] < y[from]) + needreverse = 1; + } else if (y[to] == y[from]) { + if (x[to] < x[max]) + needreverse = 1; + } else { /* generic case */ + if ((x[to] - x[max]) * (y[max] - y[from]) + > (x[max] - x[from]) * (y[to] - y[max])) + needreverse = 1; + } + + if (needreverse) { + if (lge) { + assertpath(lge->next, __FILE__, __LINE__, g->name); + reversepathsfromto(lge->next, NULL); + } else { + assertpath(g->entries, __FILE__, __LINE__, g->name); + reversepaths(g); + } + } + } +} + +static double +f2dot14( + short x +) +{ + short y = ntohs(x); + return (y >> 14) + ((y & 0x3fff) / 16384.0); +} + + +/* check that the pointer points within the file */ +/* returns 0 if pointer is good, 1 if bad */ +static int +badpointer( + void *ptr +) +{ + return (ptr < (void *)filebuffer || ptr >= (void *)filebuffer_end); +} + +/* + * Externally accessible methods + */ + +/* + * Open font and prepare to return information to the main driver. + * May print error and warning messages. + * Exit on error. + */ + +static void +openfont( + char *fname, + char *arg /* unused now */ +) +{ + int i, j; + struct stat statbuf; + static struct { + void **tbpp; /* pointer to pointer to the table */ + char name[5]; /* table name */ + char optional; /* flag: table may be missing */ + } tables[] = { + { (void **)&name_table, "name", 0 }, + { (void **)&head_table, "head", 0 }, + { (void **)&hhea_table, "hhea", 0 }, + { (void **)&post_table, "post", 0 }, + { (void **)&glyf_start, "glyf", 0 }, + { (void **)&cmap_table, "cmap", 0 }, + { (void **)&kern_table, "kern", 1 }, + { (void **)&maxp_table, "maxp", 0 }, + { (void **)&hmtx_table, "hmtx", 0 }, + { (void **)&long_loca_table, "loca", 0 }, + { NULL, "", 0 } /* end of table */ + }; + + if (stat(fname, &statbuf) == -1) { + fprintf(stderr, "**** Cannot access %s ****\n", fname); + exit(1); + } + if ((filebuffer = malloc(statbuf.st_size)) == NULL) { + fprintf(stderr, "**** Cannot malloc space for file ****\n"); + exit(1); + } + + filebuffer_end = filebuffer + statbuf.st_size; + + if ((ttf_file = fopen(fname, "rb")) == NULL) { + fprintf(stderr, "**** Cannot open file '%s'\n", fname); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Processing file %s\n", fname); + } + + if (fread(filebuffer, 1, statbuf.st_size, ttf_file) != statbuf.st_size) { + fprintf(stderr, "**** Could not read whole file \n"); + exit(1); + } + fclose(ttf_file); + + directory = (TTF_DIRECTORY *) filebuffer; + + if (ntohl(directory->sfntVersion) != 0x00010000) { + fprintf(stderr, + "**** Unknown File Version number [%x], or not a TrueType file\n", + directory->sfntVersion); + exit(1); + } + + /* clear the tables */ + for(j=0; tables[j].tbpp != NULL; j++) + *(tables[j].tbpp) = NULL; + + dir_entry = &(directory->list); + + for (i = 0; i < ntohs(directory->numTables); i++) { + + for(j=0; tables[j].tbpp != NULL; j++) + if (memcmp(dir_entry->tag, tables[j].name, 4) == 0) { + *(tables[j].tbpp) = (void *) (filebuffer + ntohl(dir_entry->offset)); + break; + } + + if (memcmp(dir_entry->tag, "EBDT", 4) == 0 || + memcmp(dir_entry->tag, "EBLC", 4) == 0 || + memcmp(dir_entry->tag, "EBSC", 4) == 0) { + WARNING_1 fprintf(stderr, "Font contains bitmaps\n"); + } + dir_entry++; + } + + for(j=0; tables[j].tbpp != NULL; j++) + if(!tables[j].optional && badpointer( *(tables[j].tbpp) )) { + fprintf(stderr, "**** File contains no required table '%s'\n", tables[j].name); + exit(1); + } + + handle_name(); + + handle_head(); + + ttf_nglyphs = ntohs(maxp_table->numGlyphs); + + enc_found_ms = enc_found_mac = 0; +} + +/* + * Close font. + * Exit on error. + */ + +static void +closefont( + void +) +{ + return; /* empty operation */ +} + +/* + * Get the number of glyphs in font. + */ + +static int +getnglyphs ( + void +) +{ + return ttf_nglyphs; +} + +/* + * Get the names of the glyphs. + * Returns 0 if the names were assigned, non-zero if the font + * provides no glyph names. + */ + +static int +glnames( + GLYPH *glyph_list +) +{ + int i, len, n, npost; + unsigned int format; + USHORT *name_index; + char *ptr, *p; + char **ps_name_ptr = (char **) malloc(ttf_nglyphs * sizeof(char *)); + int n_ps_names; + int ps_fmt_3 = 0; + + format = ntohl(post_table->formatType); + + if (format == 0x00010000) { + for (i = 0; i < 258 && i < ttf_nglyphs; i++) { + glyph_list[i].name = mac_glyph_names[i]; + } + } else if (format == 0x00020000) { + npost = ntohs(post_table->numGlyphs); + if (ttf_nglyphs != npost) { + /* This is an error in the font, but we can now cope */ + WARNING_1 fprintf(stderr, "**** Postscript table size mismatch %d/%d ****\n", + npost, ttf_nglyphs); + } + n_ps_names = 0; + name_index = &(post_table->glyphNameIndex); + + /* This checks the integrity of the post table */ + for (i=0; i n_ps_names + 257) { + n_ps_names = n - 257; + } + } + + ptr = (char *) post_table + 34 + (ttf_nglyphs << 1); + i = 0; + while (*ptr > 0 && i < n_ps_names) { + len = *ptr; + /* previously the program wrote nulls into the table. If the table + was corrupt, this could put zeroes anywhere, leading to obscure bugs, + so now I malloc space for the names. Yes it is much less efficient */ + + if ((p = malloc(len+1)) == NULL) { + fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); + exit(255); + } + + ps_name_ptr[i] = p; + strncpy(p, ptr+1, len); + p[len] = '\0'; + i ++; + ptr += len + 1; + } + + if (i != n_ps_names) + { + WARNING_2 fprintf (stderr, "** Postscript Name mismatch %d != %d **\n", + i, n_ps_names); + n_ps_names = i; + } + + /* + * for (i=0; inumGlyphs); + for (i = 0; i < ttf_nglyphs; i++) { + glyph_list[i].name = mac_glyph_names[i + ptr[i]]; + } + } else { + fprintf(stderr, + "**** Postscript table in wrong format %x ****\n", + format); + exit(1); + } + + return ps_fmt_3; +} + +/* + * Get the metrics of the glyphs. + */ + +static void +glmetrics( + GLYPH *glyph_list +) +{ + int i; + int n_hmetrics = ntohs(hhea_table->numberOfHMetrics); + GLYPH *g; + LONGHORMETRIC *hmtx_entry = hmtx_table; + FWORD *lsblist; + + for (i = 0; i < n_hmetrics; i++) { + g = &(glyph_list[i]); + g->width = ntohs(hmtx_entry->advanceWidth); + g->lsb = ntohs(hmtx_entry->lsb); + hmtx_entry++; + } + + lsblist = (FWORD *) hmtx_entry; + hmtx_entry--; + + for (i = n_hmetrics; i < ttf_nglyphs; i++) { + g = &(glyph_list[i]); + g->width = ntohs(hmtx_entry->advanceWidth); + g->lsb = ntohs(lsblist[i - n_hmetrics]); + } + + for (i = 0; i < ttf_nglyphs; i++) { + g = &(glyph_list[i]); + get_glyf_table(i, &glyf_table, &g->ttf_pathlen); + + g->xMin = (short)ntohs(glyf_table->xMin); + g->xMax = (short)ntohs(glyf_table->xMax); + g->yMin = (short)ntohs(glyf_table->yMin); + g->yMax = (short)ntohs(glyf_table->yMax); + } + +} + + +static void +handle_ms_encoding( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + int j, k, kk, set_ok; + USHORT start, end, ro; + short delta, n; + + for (j = 0; j < cmap_n_segs - 1; j++) { + start = ntohs(cmap_seg_start[j]); + end = ntohs(cmap_seg_end[j]); + delta = ntohs(cmap_idDelta[j]); + ro = ntohs(cmap_idRangeOffset[j]); + + for (k = start; k <= end; k++) { + if (ro == 0) { + n = k + delta; + } else { + n = ntohs(*((ro >> 1) + (k - start) + + &(cmap_idRangeOffset[j]))); + if (delta != 0) + { + /* Not exactly sure how to deal with this circumstance, + I suspect it never occurs */ + n += delta; + fprintf (stderr, + "rangeoffset and delta both non-zero - %d/%d", + ro, delta); + } + } + if(n<0 || n>=ttf_nglyphs) { + WARNING_1 fprintf(stderr, "Font contains a broken glyph code mapping, ignored\n"); + continue; + } + if (glyph_list[n].orig_code != -1) { +#if 0 + if (strcmp(glyph_list[n].name, ".notdef") != 0) { + WARNING_2 fprintf(stderr, + "Glyph %s has >= two encodings (A), %4.4x & %4.4x\n", + glyph_list[n].name, + glyph_list[n].orig_code, + k); + } +#endif + set_ok = 0; + } else { + set_ok = 1; + } + if (enc_type==1 || forcemap) { + kk = unicode_rev_lookup(k); + if(ISDBG(UNICODE)) + fprintf(stderr, "Unicode %s - 0x%04x\n",glyph_list[n].name,k); + if (set_ok) { + glyph_list[n].orig_code = k; + /* glyph_list[n].char_no = kk; */ + } + if (kk >= 0 && kk < ENCTABSZ && encoding[kk] == -1) + encoding[kk] = n; + } else { + if ((k & 0xff00) == 0xf000) { + if( encoding[k & 0x00ff] == -1 ) { + encoding[k & 0x00ff] = n; + if (set_ok) { + /* glyph_list[n].char_no = k & 0x00ff; */ + glyph_list[n].orig_code = k; + } + } + } else { + if (set_ok) { + /* glyph_list[n].char_no = k; */ + glyph_list[n].orig_code = k; + } + WARNING_2 fprintf(stderr, + "Glyph %s has non-symbol encoding %4.4x\n", + glyph_list[n].name, + k & 0xffff); + /* + * just use the code + * as it is + */ + if ((k & ~0xff) == 0 && encoding[k] == -1 ) + encoding[k] = n; + } + } + } + } +} + +static void +handle_mac_encoding( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + short n; + int j, size; + + size = ntohs(encoding0->length) - 6; + for (j = 0; j < size; j++) { + n = encoding0->glyphIdArray[j]; + if (glyph_list[n].char_no != -1) { + WARNING_2 fprintf(stderr, + "Glyph %s has >= two encodings (B), %4.4x & %4.4x\n", + glyph_list[n].name, + glyph_list[n].char_no, + j); + } else { + if (j < ENCTABSZ) { + if(encoding[j] == -1) { + glyph_list[n].char_no = j; + encoding[j] = n; + } + } + } + } +} + +/* + * Get the original encoding of the font. + * Returns 1 for if the original encoding is Unicode, 2 if the + * original encoding is other 16-bit, 0 if 8-bit. + */ + +static int +glenc( + GLYPH *glyph_list, + int *encoding, + int *unimap +) +{ + int num_tables = ntohs(cmap_table->numberOfEncodingTables); + BYTE *ptr; + int i, format, offset, seg_c2, found; + int platform, encoding_id; + TTF_CMAP_ENTRY *table_entry; + TTF_CMAP_FMT4 *encoding4; + + if(enc_found_ms) { + handle_ms_encoding(glyph_list, encoding, unimap); + return enc_type; + } else if(enc_found_mac) { + handle_mac_encoding(glyph_list, encoding, unimap); + return 0; + } + + if(force_pid != -1 && force_pid != 3) { + fputs("*** Only platform ID == 3 is supported\n", stderr); + exit(1); + } + + enc_type = 0; + found = 0; + + for (i = 0; i < num_tables && !found; i++) { + table_entry = &(cmap_table->encodingTable[i]); + offset = ntohl(table_entry->offset); + encoding4 = (TTF_CMAP_FMT4 *) ((BYTE *) cmap_table + offset); + format = ntohs(encoding4->format); + platform = ntohs(table_entry->platformID); + encoding_id = ntohs(table_entry->encodingID); + + if (platform == 3 && format == 4) { + if(force_pid == 3) { + if(encoding_id != force_eid) + continue; + WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n", + force_pid, force_eid); + enc_type = 1; + } else { + switch (encoding_id) { + case 0: + WARNING_1 fputs("Found Symbol Encoding\n", stderr); + break; + case 1: + WARNING_1 fputs("Found Unicode Encoding\n", stderr); + enc_type = 1; + break; + default: + WARNING_1 { + fprintf(stderr, + "****MS Encoding ID %d not supported****\n", + encoding_id); + fputs("Treating it like Symbol encoding\n", stderr); + } + break; + } + } + + found = 1; + seg_c2 = ntohs(encoding4->segCountX2); + cmap_n_segs = seg_c2 >> 1; + ptr = (BYTE *) encoding4 + 14; + cmap_seg_end = (USHORT *) ptr; + cmap_seg_start = (USHORT *) (ptr + seg_c2 + 2); + cmap_idDelta = (short *) (ptr + (seg_c2 * 2) + 2); + cmap_idRangeOffset = (short *) (ptr + (seg_c2 * 3) + 2); + enc_found_ms = 1; + + handle_ms_encoding(glyph_list, encoding, unimap); + } + } + + if (!found) { + if(force_pid != -1) { + fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n", + force_pid, force_eid); + exit(1); + } + + WARNING_1 fputs("No Microsoft encoding, looking for MAC encoding\n", stderr); + for (i = 0; i < num_tables && !found; i++) { + table_entry = &(cmap_table->encodingTable[i]); + offset = ntohl(table_entry->offset); + encoding0 = (TTF_CMAP_FMT0 *) ((BYTE *) cmap_table + offset); + format = ntohs(encoding0->format); + platform = ntohs(table_entry->platformID); + encoding_id = ntohs(table_entry->encodingID); + + if (format == 0) { + found = 1; + enc_found_mac = 1; + + handle_mac_encoding(glyph_list, encoding, unimap); + } + } + } + if (!found) { + fprintf(stderr, "**** No Recognised Encoding Table ****\n"); + exit(1); + } + + return enc_type; +} + +/* + * Get the font metrics + */ +static void +fnmetrics( + struct font_metrics *fm +) +{ + char *str; + static int fieldstocheck[]= {2,4,6}; + int i; + + fm->italic_angle = (short) (ntohs(post_table->italicAngle.upper)) + + ((short) ntohs(post_table->italicAngle.lower) / 65536.0); + fm->underline_position = (short) ntohs(post_table->underlinePosition); + fm->underline_thickness = (short) ntohs(post_table->underlineThickness); + fm->is_fixed_pitch = ntohl(post_table->isFixedPitch); + + fm->ascender = (short)ntohs(hhea_table->ascender); + fm->descender = (short)ntohs(hhea_table->descender); + + fm->units_per_em = ntohs(head_table->unitsPerEm); + + fm->bbox[0] = (short) ntohs(head_table->xMin); + fm->bbox[1] = (short) ntohs(head_table->yMin); + fm->bbox[2] = (short) ntohs(head_table->xMax); + fm->bbox[3] = (short) ntohs(head_table->yMax); + + fm->name_copyright = name_fields[0]; + fm->name_family = name_fields[1]; + fm->name_style = name_fields[2]; + fm->name_full = name_fields[4]; + fm->name_version = name_fields[5]; + fm->name_ps = name_fields[6]; + + /* guess the boldness from the font names */ + fm->force_bold=0; + + for(i=0; !fm->force_bold && iforce_bold=1; + break; + } + } + } +} + +/* + * Get the path of contrours for a glyph. + */ + +static void +glpath( + int glyphno, + GLYPH *glyf_list +) +{ + double matrix[6]; + GLYPH *g; + + g = &glyph_list[glyphno]; + + matrix[0] = matrix[3] = 1.0; + matrix[1] = matrix[2] = matrix[4] = matrix[5] = 0.0; + draw_composite_glyf(g, glyf_list, glyphno, matrix, 0 /*level*/); +} + +/* + * Get the kerning data. + */ + +static void +kerning( + GLYPH *glyph_list +) +{ + TTF_KERN_SUB *subtable; + TTF_KERN_ENTRY *kern_entry; + int i, j; + int ntables; + int npairs; + char *ptr; + + if(kern_table == NULL) { + WARNING_1 fputs("No Kerning data\n", stderr); + return; + } + if(badpointer(kern_table)) { + fputs("**** Defective Kerning table, ignored\n", stderr); + return; + } + + ntables = ntohs(kern_table->nTables); + ptr = (char *) kern_table + 4; + + for (i = 0; i < ntables; i++) { + subtable = (TTF_KERN_SUB *) ptr; + if ((ntohs(subtable->coverage) & 0xff00) == 0) { + npairs = (short) ntohs(subtable->nPairs); + kern_entry = (TTF_KERN_ENTRY *) (ptr + sizeof(TTF_KERN_SUB)); + + kern_entry = (TTF_KERN_ENTRY *) (ptr + sizeof(TTF_KERN_SUB)); + for (j = 0; j < npairs; j++) { + if( kern_entry->value != 0) + addkernpair(ntohs(kern_entry->left), + ntohs(kern_entry->right), (short)ntohs(kern_entry->value)); + kern_entry++; + } + } + ptr += subtable->length; + } +} + diff --git a/pdf2swf/ttf2pt1/ttf.h b/pdf2swf/ttf2pt1/ttf.h new file mode 100644 index 0000000..8c38808 --- /dev/null +++ b/pdf2swf/ttf2pt1/ttf.h @@ -0,0 +1,172 @@ +/* + * see COPYRIGHT + */ + +/* these definitions are mostly taken from Microsoft's True Type + documentation. +*/ + +#define BYTE unsigned char +#define CHAR signed char +#define USHORT unsigned short +#define SHORT signed short +#define ULONG unsigned int +#define LONG signed int +#define FWORD SHORT +#define UFWORD USHORT + +#define ONOROFF 0x01 +#define XSHORT 0x02 +#define YSHORT 0x04 +#define REPEAT 0x08 +#define XSAME 0x10 +#define YSAME 0x20 + +#define ARG_1_AND_2_ARE_WORDS 0x0001 +#define ARGS_ARE_XY_VALUES 0x0002 +#define XY_BOUND_TO_GRID 0x0004 +#define WE_HAVE_A_SCALE 0x0008 +#define MORE_COMPONENTS 0x0020 +#define WE_HAVE_AN_X_AND_Y_SCALE 0x0040 +#define WE_HAVE_A_TWO_BY_TWO 0x0080 +#define WE_HAVE_INSTRUCTIONS 0x0100 +#define USE_MY_METRICS 0x0200 + +typedef struct short_2 { + SHORT upper; + USHORT lower; +} FIXED ; + +typedef struct longhormetric { + UFWORD advanceWidth; + FWORD lsb; +} LONGHORMETRIC; + +typedef struct ttf_hhea { + BYTE version[4]; + SHORT ascender, descender, lineGap; + USHORT advnaceWidthMax; + SHORT minLSB, minRSB, xMaxExtent; + SHORT caretSlopeRise, caretSlopeRun; + SHORT reserved[5]; + SHORT metricDataFormat; + USHORT numberOfHMetrics; +} TTF_HHEA; + +typedef struct ttf_dir_entry { + char tag[4]; + ULONG checksum; + ULONG offset; + ULONG length; +} TTF_DIR_ENTRY ; + +typedef struct ttf_directory { + ULONG sfntVersion; + USHORT numTables; + USHORT searchRange; + USHORT entrySelector; + USHORT rangeShift; + TTF_DIR_ENTRY list; +} TTF_DIRECTORY ; + +typedef struct ttf_name_rec { + USHORT platformID; + USHORT encodingID; + USHORT languageID; + USHORT nameID; + USHORT stringLength; + USHORT stringOffset; +} TTF_NAME_REC; + +typedef struct ttf_name { + USHORT format; + USHORT numberOfNameRecords; + USHORT offset; + TTF_NAME_REC nameRecords; +} TTF_NAME ; + +typedef struct ttf_head { + ULONG version; + ULONG fontRevision; + ULONG checksumAdjust; + ULONG magicNo; + USHORT flags; + USHORT unitsPerEm; + BYTE created[8]; + BYTE modified[8]; + FWORD xMin, yMin, xMax, yMax; + USHORT macStyle, lowestRecPPEM; + SHORT fontDirection, indexToLocFormat, glyphDataFormat; +} TTF_HEAD ; + +typedef struct ttf_kern { + USHORT version, nTables; +} TTF_KERN ; + +typedef struct ttf_kern_sub { + USHORT version, length, coverage; + USHORT nPairs, searchRange, entrySelector, rangeShift; +} TTF_KERN_SUB; + +typedef struct ttf_kern_entry { + USHORT left, right; + FWORD value; +} TTF_KERN_ENTRY; + +typedef struct ttf_cmap_fmt0 { + USHORT format; + USHORT length; + USHORT version; + BYTE glyphIdArray[256]; +} TTF_CMAP_FMT0; + +typedef struct ttf_cmap_fmt4 { + USHORT format; + USHORT length; + USHORT version; + USHORT segCountX2; + USHORT searchRange; + USHORT entrySelector; + USHORT rangeShift; +} TTF_CMAP_FMT4; + +typedef struct ttf_cmap_entry { + USHORT platformID; + USHORT encodingID; + ULONG offset; +} TTF_CMAP_ENTRY; + +typedef struct ttf_cmap { + USHORT version; + USHORT numberOfEncodingTables; + TTF_CMAP_ENTRY encodingTable[1]; +} TTF_CMAP ; + +typedef struct ttf_glyf { + SHORT numberOfContours; + FWORD xMin, yMin, xMax, yMax; +} TTF_GLYF ; + +typedef struct ttf_maxp { + ULONG version; + USHORT numGlyphs, maxPoints, maxContours; + USHORT maxCompositePoints, maxCompositeContours; + USHORT maxZones, maxTwilightPoints, maxStorage; + USHORT maxFunctionDefs, maxInstructionsDefs; + USHORT maxSizeOfInstructions, maxComponentElements; + USHORT maxComponentDepth; +} TTF_MAXP ; + +typedef struct ttf_post_head { + ULONG formatType; + FIXED italicAngle; + FWORD underlinePosition; + FWORD underlineThickness; + ULONG isFixedPitch; + ULONG minMemType42; + ULONG maxMemType42; + ULONG minMemType1; + ULONG maxMemType1; + USHORT numGlyphs; + USHORT glyphNameIndex; +} TTF_POST_HEAD ; diff --git a/pdf2swf/ttf2pt1/ttf2pt1.c b/pdf2swf/ttf2pt1/ttf2pt1.c new file mode 100644 index 0000000..2de4b5f --- /dev/null +++ b/pdf2swf/ttf2pt1/ttf2pt1.c @@ -0,0 +1,2317 @@ +/* + * True Type Font to Adobe Type 1 font converter + * By Mark Heath + * Based on ttf2pfa by Andrew Weeks + * With help from Frank M. Siegert + * + * see COPYRIGHT + * +*********************************************************************** + * + * Sergey Babkin , + * + * Added post-processing of resulting outline to correct the errors + * both introduced during conversion and present in the original font, + * autogeneration of hints (has yet to be improved though) and BlueValues, + * scaling to 1000x1000 matrix, option to print the result on STDOUT, + * support of Unicode to CP1251 conversion, optimization of the + * resulting font code by space (that improves the speed too). Excluded + * the glyphs that are unaccessible through the encoding table from + * the output file. Added the built-in Type1 assembler (taken from + * the `t1utils' package). + * +*********************************************************************** + * + * Thomas Henlich + * + * Added generation of .afm file (font metrics) + * Read encoding information from encoding description file + * Fixed bug in error message about unknown language ('-l' option) + * Added `:' after %%!PS-AdobeFont-1.0 + * changed unused entries in ISOLatin1Encoding[] from .notdef to c127,c128... + * +*********************************************************************** + * + * Thomas Henlich + * + * Added generation of .afm file (font metrics) + * +*********************************************************************** + * + * Bug Fixes: +************************************************************************ + * + * Sun, 21 Jun 1998 Thomas Henlich + * 1. "width" should be "short int" because otherwise: + * characters with negative widths (e.g. -4) become *very* wide (65532) + * 2. the number of /CharStrings is numglyphs and not numglyphs+1 + * +*********************************************************************** + * + * + * + * The resultant font file produced by this program still needs to be ran + * through t1asm (from the t1utils archive) to produce a completely valid + * font. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _GNU_SOURCE +#include +#endif + +#ifndef WINDOWS +# include +# include +# define BITBUCKET "/dev/null" +# include +#else +# define WINDOWS_FUNCTIONS /* ask to define functions - in one file only */ +# include "windows.h" +# define BITBUCKET "NUL" +#endif + +#include "pt1.h" +#include "global.h" +#include "version.h" + +/* globals */ + +/* table of front-ends */ + +extern struct frontsw ttf_sw; +#if defined(USE_FREETYPE) + extern struct frontsw freetype_sw; +#endif + +struct frontsw *frontswtab[] = { +#if defined(USE_FREETYPE) && defined(PREFER_FREETYPE) + &freetype_sw, +#endif + &ttf_sw, +#if defined(USE_FREETYPE) && !defined(PREFER_FREETYPE) + &freetype_sw, +#endif + NULL /* end of table */ +}; + +struct frontsw *cursw=0; /* the active front end */ +char *front_arg=""; /* optional argument */ + +/* options */ +int encode = 0; /* encode the resulting file */ +int pfbflag = 0; /* produce compressed file */ +int wantafm=0; /* want to see .afm instead of .t1a on stdout */ +int correctvsize=0; /* try to correct the vertical size of characters */ +int wantuid = 0; /* user wants UniqueID entry in the font */ +int allglyphs = 0; /* convert all glyphs, not only 256 of them */ +int warnlevel = -1; /* the level of permitted warnings */ +int forcemap = 0; /* do mapping even on non-Unicode fonts */ +/* options - maximal limits */ +int max_stemdepth = 128; /* maximal depth of stem stack in interpreter (128 - limit from X11) */ +/* options - debugging */ +int absolute = 0; /* print out in absolute values */ +int reverse = 1; /* reverse font to Type1 path directions */ +/* options - suboptions of Outline Processing, defaults are set in table */ +int optimize; /* enables space optimization */ +int smooth; /* enable smoothing of outlines */ +int transform; /* enables transformation to 1000x1000 matrix */ +int hints; /* enables autogeneration of hints */ +int subhints; /* enables autogeneration of substituted hints */ +int trybold; /* try to guess whether the font is bold */ +int correctwidth; /* try to correct the character width */ + +/* not quite options to select a particular source encoding */ +int force_pid = -1; /* specific platform id */ +int force_eid = -1; /* specific encoding id */ + +/* table of Outline Processing (may think also as Optimization) options */ +static struct { + char disbl; /* character to disable - enforced lowercase */ + char enbl; /* character to enable - auto-set as toupper(disbl) */ + int *valp; /* pointer to the actual variable containing value */ + int dflt; /* default value */ + char *descr; /* description */ +} opotbl[] = { + { 'b', 0/*auto-set*/, &trybold, 1, "guessing of the ForceBold hint" }, + { 'h', 0/*auto-set*/, &hints, 1, "autogeneration of hints" }, + { 'u', 0/*auto-set*/, &subhints, 1, "hint substitution technique" }, + { 'o', 0/*auto-set*/, &optimize, 1, "space optimization of font files" }, + { 's', 0/*auto-set*/, &smooth, 1, "smoothing and repair of outlines" }, + { 't', 0/*auto-set*/, &transform, 1, "auto-scaling to the standard matrix 1000x1000" }, + { 'w', 0/*auto-set*/, &correctwidth, 0, "correct the glyph widths (use only for buggy fonts)" }, +}; + +int debug = DEBUG; /* debugging flag */ + +FILE *pfa_file, *afm_file; +int numglyphs; +struct font_metrics fontm; + +/* non-globals */ +static char *strUID = 0; /* user-supplied UniqueID */ +static unsigned long numUID; /* auto-generated UniqueID */ + +static int ps_fmt_3 = 0; +static double scale_factor, original_scale_factor; + +static char *glyph_rename[ENCTABSZ]; + +/* the names assigned if the original font + * does not specify any + */ + +static char *Fmt3Encoding[256] = { + "c0", "c1", "c2", "c3", + "c4", "c5", "c6", "c7", + "c8", "c9", "c10", "c11", + "c12", "CR", "c14", "c15", + "c16", "c17", "c18", "c19", + "c20", "c21", "c22", "c23", + "c24", "c25", "c26", "c27", + "c28", "c29", "c30", "c31", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "c127", + "c128", "c129", "quotesinglbase", "florin", + "quotedblbase", "ellipsis", "dagger", "daggerdbl", + "circumflex", "perthousand", "Scaron", "guilsinglleft", + "OE", "c141", "c142", "c143", + "c144", "quoteleft", "quoteright", "quotedblleft", + "quotedblright", "bullet", "endash", "emdash", + "tilde", "trademark", "scaron", "guilsinglright", + "oe", "c157", "c158", "Ydieresis", + "nbspace", "exclamdown", "cent", "sterling", + "currency", "yen", "brokenbar", "section", + "dieresis", "copyright", "ordfeminine", "guillemotleft", + "logicalnot", "sfthyphen", "registered", "macron", + "degree", "plusminus", "twosuperior", "threesuperior", + "acute", "mu", "paragraph", "periodcentered", + "cedilla", "onesuperior", "ordmasculine", "guillemotright", + "onequarter", "onehalf", "threequarters", "questiondown", + "Agrave", "Aacute", "Acircumflex", "Atilde", + "Adieresis", "Aring", "AE", "Ccedilla", + "Egrave", "Eacute", "Ecircumflex", "Edieresis", + "Igrave", "Iacute", "Icircumflex", "Idieresis", + "Eth", "Ntilde", "Ograve", "Oacute", + "Ocircumflex", "Otilde", "Odieresis", "multiply", + "Oslash", "Ugrave", "Uacute", "Ucircumflex", + "Udieresis", "Yacute", "Thorn", "germandbls", + "agrave", "aacute", "acircumflex", "atilde", + "adieresis", "aring", "ae", "ccedilla", + "egrave", "eacute", "ecircumflex", "edieresis", + "igrave", "iacute", "icircumflex", "idieresis", + "eth", "ntilde", "ograve", "oacute", + "ocircumflex", "otilde", "odieresis", "divide", + "oslash", "ugrave", "uacute", "ucircumflex", + "udieresis", "yacute", "thorn", "ydieresis" +}; + +#ifdef notdef /* { */ +/* This table is not used anywhere in the code + * so it's ifdef-ed out by default but left in + * the source code for reference purposes (and + * possibly for future use) + */ + +static char *ISOLatin1Encoding[256] = { + ".null", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "CR", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", "c127", + "c128", "c129", "quotesinglbase", "florin", + "quotedblbase", "ellipsis", "dagger", "daggerdbl", + "circumflex", "perthousand", "Scaron", "guilsinglleft", + "OE", "c141", "c142", "c143", + "c144", "quoteleft", "quoteright", "quotedblleft", + "quotedblright", "bullet", "endash", "emdash", + "tilde", "trademark", "scaron", "guilsinglright", + "oe", "c157", "c158", "Ydieresis", + "nbspace", "exclamdown", "cent", "sterling", + "currency", "yen", "brokenbar", "section", + "dieresis", "copyright", "ordfeminine", "guillemotleft", + "logicalnot", "sfthyphen", "registered", "macron", + "degree", "plusminus", "twosuperior", "threesuperior", + "acute", "mu", "paragraph", "periodcentered", + "cedilla", "onesuperior", "ordmasculine", "guillemotright", + "onequarter", "onehalf", "threequarters", "questiondown", + "Agrave", "Aacute", "Acircumflex", "Atilde", + "Adieresis", "Aring", "AE", "Ccedilla", + "Egrave", "Eacute", "Ecircumflex", "Edieresis", + "Igrave", "Iacute", "Icircumflex", "Idieresis", + "Eth", "Ntilde", "Ograve", "Oacute", + "Ocircumflex", "Otilde", "Odieresis", "multiply", + "Oslash", "Ugrave", "Uacute", "Ucircumflex", + "Udieresis", "Yacute", "Thorn", "germandbls", + "agrave", "aacute", "acircumflex", "atilde", + "adieresis", "aring", "ae", "ccedilla", + "egrave", "eacute", "ecircumflex", "edieresis", + "igrave", "iacute", "icircumflex", "idieresis", + "eth", "ntilde", "ograve", "oacute", + "ocircumflex", "otilde", "odieresis", "divide", + "oslash", "ugrave", "uacute", "ucircumflex", + "udieresis", "yacute", "thorn", "ydieresis" +}; + +#endif /* } notdef */ + +static char *adobe_StandardEncoding[256] = { + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + "at", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + "quoteleft", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "exclamdown", "cent", "sterling", + "fraction", "yen", "florin", "section", + "currency", "quotesingle", "quotedblleft", "guillemotleft", + "guilsinglleft", "guilsinglright", "fi", "fl", + ".notdef", "endash", "dagger", "daggerdbl", + "periodcentered", ".notdef", "paragraph", "bullet", + "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", + "ellipsis", "perthousand", ".notdef", "questiondown", + ".notdef", "grave", "acute", "circumflex", + "tilde", "macron", "breve", "dotaccent", + "dieresis", ".notdef", "ring", "cedilla", + ".notdef", "hungarumlaut", "ogonek", "caron", + "emdash", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "AE", ".notdef", "ordfeminine", + ".notdef", ".notdef", ".notdef", ".notdef", + "Lslash", "Oslash", "OE", "ordmasculine", + ".notdef", ".notdef", ".notdef", ".notdef", + ".notdef", "ae", ".notdef", ".notdef", + ".notdef", "dotlessi", ".notdef", ".notdef", + "lslash", "oslash", "oe", "germandbls", + ".notdef", ".notdef", ".notdef", ".notdef" +}; + +/* + * Decription of the supported conversions from Unicode + * + * SB + * Yes, I know that the compiled-in conversion is stupid but + * it is simple to implement and allows not to worry about the + * filesystem context. After all, the source is always available + * and adding another language to it is easy. + * + * The language name is expected to be the same as the subdirectory name + * in the `encodings' directory (for possible future extensions). + * The primary use of the aliases is for guessing based on the current + * locale. + */ + +#define MAXUNIALIAS 10 +#define MAXUNITABLES 3 + +/* the character used as the language argument separator */ +#define LANG_ARG_SEP '+' + + +/* + * Types of language-related routines. Arguments are: + * name is the glyph name + * arg is the user-specified language-dependent argument + * which can for example select the subfont plane for Eastern fonts. + * If none is supplied by user then an empty string ("") is passed. + * If no language is specified by user and auto-guessing happens + * then NULL is passed. + * when shows if the conversion by name was called before conversion by + * map or after (it's called twice) + */ + +/* type of the Unicode map initialization routine */ +typedef void uni_init_t(char *arg); + +/* type of Unicode converter-by-name function + * it's called for each glyph twice: one time for each glyph + * before doing conversion by map and one time after + */ +typedef int uni_conv_t(char *name, char *arg, int when); +#define UNICONV_BYNAME_BEFORE 0 +#define UNICONV_BYNAME_AFTER 1 + +struct uni_language { + uni_init_t *init[MAXUNITABLES]; /* map initialization routines */ + uni_conv_t *convbyname; /* the name-based conversion function */ + char *name; /* the language name */ + char *descr; /* description */ + char *alias[MAXUNIALIAS]; /* aliases of the language name */ + int sample_upper; /* code of some uppercase character for correctvsize() */ +}; + +/* the converter routines have an option of adding this suffix to the font name */ +static char *uni_font_name_suffix = ""; /* empty by default */ +/* this buffer may be used to store the suffix */ +#define UNI_MAX_SUFFIX_LEN 100 +static char uni_suffix_buf[UNI_MAX_SUFFIX_LEN+1]; + +/* + * Prototypes of the conversion routines + */ + +static uni_init_t unicode_latin1; +static uni_init_t unicode_latin2; +static uni_init_t unicode_latin4; +static uni_init_t unicode_latin5; +static uni_init_t unicode_cyrillic; +static uni_init_t unicode_adobestd; +static uni_init_t unicode_plane; +static uni_conv_t unicode_adobestd_byname; + +static uni_init_t unicode_init_user; + +/* + * The order of descriptions is important: if we can't guess the + * language we just call all the conversion routines in order until + * we find one that understands this glyph. + */ +static struct uni_language uni_lang[]= { + /* pseudo-language for all the languages using Latin1 */ + { + { unicode_latin1 }, + 0, /* no name-based mapping */ + "latin1", + "works for most of the Western languages", + { "en_", "de_", "fr_", "nl_", "no_", "da_", "it_" }, + 'A' + }, + { /* by Szalay Tamas */ + { unicode_latin2 }, + 0, /* no name-based mapping */ + "latin2", + "works for Central European languages", + { "hu_","pl_","cz_","si_","sk_" }, + 'A' + }, + { /* by Rièardas Èepas */ + { unicode_latin4 }, + 0, /* no name-based mapping */ + "latin4", + "works for Baltic languages", + { "lt_", "lv_" }, /* doubt about ee_ */ + 'A' + }, + { /* by Turgut Uyar */ + { unicode_latin5 }, + 0, /* no name-based mapping */ + "latin5", + "for Turkish", + { "tr_" }, + 'A' + }, + { /* by Zvezdan Petkovic */ + { unicode_cyrillic, unicode_latin1 }, + 0, /* no name-based mapping */ + "cyrillic", + "in Windows encoding", + { "bg_", "be_", "mk_", "ru_", "sr_", "su_", "uk_" }, + 'A' + }, + { + { unicode_cyrillic, unicode_latin1 }, + 0, /* no name-based mapping */ + "russian", + "obsolete, use cyrillic instead", + { 0 }, + 'A' + }, + { + { unicode_cyrillic, unicode_latin1 }, + 0, /* no name-based mapping */ + "bulgarian", + "obsolete, use cyrillic instead", + { 0 }, + 'A' + }, + { + { unicode_adobestd }, + unicode_adobestd_byname, + "adobestd", + "Adobe Standard, expected by TeX", + { NULL }, + 'A' + }, + { + { unicode_plane }, + 0, /* no name-based mapping */ + "plane", + "one plane of Unicode or other multi-byte encoding as is", + { NULL }, + 0 /* no easy way to predict the capital letters */ + }, +}; + +static struct uni_language uni_lang_user = { + { unicode_init_user }, + 0, /* no name-based mapping */ + 0, /* no name */ + 0, /* no description */ + { 0 }, + 0 /* no sample */ +}; + +static struct uni_language *uni_lang_selected=0; /* 0 means "unknown, try all" */ +static int uni_sample='A'; /* sample of an uppercase character */ +static char *uni_lang_arg=""; /* user-supplied language-dependent argument */ + +extern int runt1asm(int); + +/* + * user-defined loadable maps + */ + + +/* The idea begind buckets is to avoid comparing every code with all ENCTABSZ codes in table. + * All the 16-bit unicode space is divided between a number of equal-sized buckets. + * Initially all the buckets are marked with 0. Then if any code in the bucket is + * used it's marked with 1. Later during translation we check the code's bucket first + * and it it's 0 then return failure right away. This may be useful for + * Chinese fonts with many thousands of glyphs. + */ + +#define BUCKET_ID_BITS 11 +#define MARK_UNI_BUCKET(unicode) SET_BITMAP(uni_user_buckets, (unicode)>>(16-BUCKET_ID_BITS)) +#define IS_UNI_BUCKET(unicode) IS_BITMAP(uni_user_buckets, (unicode)>>(16-BUCKET_ID_BITS)) + +static DEF_BITMAP(uni_user_buckets, 1< UNI_MAX_SUFFIX_LEN-1) + arg = NULL; + else { + sprintf(uni_suffix_buf, "-%s", arg); + uni_font_name_suffix = uni_suffix_buf; + } + } + + /* now read in the encoding description file, if requested */ + if ((unicode_map_file = fopen(path, "r")) == NULL) { + fprintf(stderr, "**** Cannot access map file '%s' ****\n", path); + exit(1); + } + + sawplane = 0; + if(arg==NULL) + enabled = found = 1; + else + enabled = found = 0; + + lineno=0; curpos=0; + while (fgets (buffer, UNIBFSZ, unicode_map_file) != NULL) { + char name[UNIBFSZ]; + + lineno++; + + if(sscanf(buffer, "plane %s", name)==1) { + sawplane = 1; + if(arg == 0) { + fprintf(stderr, "**** map file '%s' requires plane name\n", path); + fprintf(stderr, "for example:\n"); + fprintf(stderr, " ttf2pt1 -L %s%c[pid=N,eid=N,]%s ...\n", + path, LANG_ARG_SEP, name); + fprintf(stderr, "to select plane '%s'\n", name); + exit(1); + } + if( !strcmp(arg, name) ) { + enabled = found = 1; + curpos = 0; + } else { + enabled = 0; + if(found) /* no need to read further */ + break; + } + continue; + } + + if(sscanf(buffer, "id %d %d", pid, eid)==2) { + if( !overid /* only if the user has not overriden */ + && (enabled || !sawplane) ) { + force_pid = pid; force_eid = eid; + forcemap = 1; + } + continue; + } + + if( !enabled ) + continue; /* skip to the next plane */ + + if( sscanf(buffer, "at %i", &curpos) == 1 ) { + if(curpos > 255) { + fprintf(stderr, "**** map file '%s' line %d: code over 255\n", path, lineno); + exit(1); + } + if(ISDBG(EXTMAP)) fprintf(stderr, "=== at 0x%x\n", curpos); + continue; + } + + /* try the format of Roman Czyborra's files */ + if ( sscanf (buffer, " =%x U+%4x", &code, &unicode) == 2 + /* try the format of Linux locale charmap file */ + || sscanf (buffer, " <%*s /x%x ", &code, &unicode) == 2 ) { + if (code < ENCTABSZ) { + if(code >= enctabsz) enctabsz=code+1; + unicode_map[code] = unicode; + glyph_rename[code] = NULL; + } + } + /* try the format with glyph renaming */ + else if (sscanf (buffer, " !%x U+%4x %128s", &code, + &unicode, name) == 3) { + if (code < ENCTABSZ) { + if(code >= enctabsz) enctabsz=code+1; + unicode_map[code] = unicode; + glyph_rename[code] = strdup(name); + } + } + /* try the compact sequence format */ + else if( (n=sscanf(buffer, " %i%n", &unicode, &cnt)) == 1 ) { + p = buffer; + do { + if(curpos > 255) { + fprintf(stderr, "**** map file '%s' line %d: code over 255 for unicode 0x%x\n", + path, lineno, unicode); + exit(1); + } + if(ISDBG(EXTMAP)) fprintf(stderr, "=== 0x%d -> 0x%x\n", curpos, unicode); + unicode_map[curpos++] = unicode; + p += cnt; + if( sscanf(p, " %[,-]%n", &next,&cnt) == 1 ) { + if(ISDBG(EXTMAP)) fprintf(stderr, "=== next: '%c'\n", next); + p += cnt; + if( next == '-' ) { /* range */ + if ( sscanf(p, " %i%n", &unicode2, &cnt) != 1 ) { + fprintf(stderr, "**** map file '%s' line %d: missing end of range\n", path, lineno); + exit(1); + } + p += cnt; + if(ISDBG(EXTMAP)) fprintf(stderr, "=== range 0x%x to 0x%x\n", unicode, unicode2); + for(unicode++; unicode <= unicode2; unicode++) { + if(curpos > 255) { + fprintf(stderr, "**** map file '%s' line %d: code over 255 in unicode range ...-0x%x\n", + path, lineno, unicode2); + exit(1); + } + if(ISDBG(EXTMAP)) fprintf(stderr, "=== 0x%x -> 0x%x\n", curpos, unicode); + unicode_map[curpos++] = unicode; + } + } + } + } while ( sscanf(p, " %i%n", &unicode, &cnt) == 1 ); + } + + } + + fclose (unicode_map_file); + + if( !found ) { + fprintf(stderr, "**** map file '%s' has no plane '%s'\n", path, arg); + exit(1); + } + + if(unicode_map['A'] == 'A') + uni_sample = 'A'; /* seems to be compatible with Latin */ + else + uni_sample = 0; /* don't make any assumptions */ +} + +/* + * by Zvezdan Petkovic + */ +static void +unicode_cyrillic( + char *arg +) +{ + int i; + static unsigned int cyrillic_unicode_map[] = { + 0x0402, 0x0403, 0x201a, 0x0453, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x20ac, 0x2030, 0x0409, 0x2039, 0x040a, 0x040c, 0x040b, 0x040f, /* 88 */ + 0x0452, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, 0x0459, 0x203a, 0x045a, 0x045c, 0x045b, 0x045f, /* 98 */ + 0x00a0, 0x040e, 0x045e, 0x0408, 0x00a4, 0x0490, 0x00a6, 0x00a7, /* A0 */ + 0x0401, 0x00a9, 0x0404, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0407, /* A8 */ + 0x00b0, 0x00b1, 0x0406, 0x0456, 0x0491, 0x00b5, 0x00b6, 0x00b7, /* B0 */ + 0x0451, 0x2116, 0x0454, 0x00bb, 0x0458, 0x0405, 0x0455, 0x0457, /* B8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0xBF; i++) + unicode_map[i] = cyrillic_unicode_map[i-0x80]; + + for(i=0xC0; i<=0xFF; i++) + unicode_map[i] = i+0x350; + +} + +static void +unicode_latin1( + char *arg +) +{ + int i; + static unsigned int latin1_unicode_map[] = { + 0x20ac, -1, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, /* 88 */ + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, /* 98 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0x9F; i++) + unicode_map[i] = latin1_unicode_map[i-0x80]; + + for(i=0xA0; i<=0xFF; i++) + unicode_map[i] = i; +} + +static void +unicode_adobestd( + char *arg +) +{ + int i; + static unsigned int adobestd_unicode_map[] = { + -1, 0x00a1, 0x00a2, 0x00a3, 0x2215, 0x00a5, 0x0192, 0x00a7, /* A0 */ + 0x00a4, 0x0027, 0x201c, 0x00ab, 0x2039, 0x203a, 0xfb01, 0xfb02, /* A8 */ + -1, 0x2013, 0x2020, 0x2021, 0x2219, -1, 0x00b6, 0x2022, /* B0 */ + 0x201a, 0x201e, 0x201d, 0x00bb, 0x2026, 0x2030, -1, 0x00bf, /* B8 */ + -1, 0x0060, 0x00b4, 0x02c6, 0x02dc, 0x02c9, 0x02d8, 0x02d9, /* C0 */ + 0x00a8, -1, 0x02da, 0x00b8, -1, 0x02dd, 0x02db, 0x02c7, /* C8 */ + 0x2014, -1, -1, -1, -1, -1, -1, -1, /* D0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* D8 */ + -1, 0x00c6, -1, 0x00aa, -1, -1, -1, -1, /* E0 */ + 0x0141, 0x00d8, 0x0152, 0x00ba, -1, -1, -1, -1, /* E8 */ + -1, 0x00e6, -1, -1, -1, 0x0131, -1, -1, /* F0 */ + 0x0142, 0x00f8, 0x0153, 0x00df, -1, -1, -1, -1, /* F8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + unicode_map[0x27] = 0x2019; + unicode_map[0x60] = -1; + + /* 0x80 to 0x9F is a hole */ + + for(i=0xA0; i<=0xFF; i++) + unicode_map[i] = adobestd_unicode_map[i-0xA0]; +} + +/* + * Not all of the Adobe glyphs are in the Unicode + * standard maps, so the font creators have + * different ideas about their codes. Because + * of this we try to map based on the glyph + * names instead of Unicode codes. If there are + * no glyph names (ps_fmt_3!=0) we fall back + * to the code-based scheme. + */ + +static int +unicode_adobestd_byname( + char *name, + char *arg, + int where +) +{ + int i; + + /* names always take precedence over codes */ + if(where == UNICONV_BYNAME_AFTER) + return -1; + + for(i=32; i<256; i++) { + if(!strcmp(name, adobe_StandardEncoding[i])) + return i; + } + return -1; + +} + +static void +unicode_latin2( + char *arg +) +{ + int i; + static unsigned int latin2_unicode_map[] = { + 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, /* A0 */ + 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, /* A8 */ + 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, /* B0 */ + 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, /* B8 */ + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, /* C0 */ + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, /* C8 */ + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, /* D0 */ + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, /* D8 */ + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, /* E0 */ + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, /* E8 */ + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, /* F0 */ + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, /* F8 */ + }; + + for(i=0; i<=0x7E; i++) + unicode_map[i] = i; + + /* 7F-9F are unused */ + + for(i=0xA0; i<=0xFF; i++) + unicode_map[i] = latin2_unicode_map[i-0xA0]; +} + +static void +unicode_latin4( + char *arg +) +{ + int i; + static unsigned int latin4_unicode_map[] = { + 0x0080, 0x0081, 0x201a, 0x0192, -1, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x02c6, 0x2030, -1, 0x2039, 0x0152, 0x008d, 0x008e, 0x008f, /* 88 */ + 0x201e, 0x201c, 0x2019, -1, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, -1, 0x203a, 0x0153, 0x009d, 0x009e, 0x0178, /* 98 */ + 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, /* A0 */ + 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, /* A8 */ + 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, /* B0 */ + 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, /* B8 */ + 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, /* C0 */ + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, /* C8 */ + 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, /* D0 */ + 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, /* D8 */ + 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, /* E0 */ + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, /* E8 */ + 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, /* F0 */ + 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, /* F8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0xFF; i++) + unicode_map[i] = latin4_unicode_map[i-0x80]; + +#if 0 /* for documentation purposes only */ + case 0x201e: return 0x90; /* these two quotes are a hack only */ + case 0x201c: return 0x91; /* these two quotes are a hack only */ + case 0x00A0: return 0xA0; /* NO-BREAK SPACE */ + case 0x0104: return 0xA1; /* LATIN CAPITAL LETTER A WITH OGONEK */ + case 0x0138: return 0xA2; /* LATIN SMALL LETTER KRA */ + case 0x0156: return 0xA3; /* LATIN CAPITAL LETTER R WITH CEDILLA */ + case 0x00A4: return 0xA4; /* CURRENCY SIGN */ + case 0x0128: return 0xA5; /* LATIN CAPITAL LETTER I WITH TILDE */ + case 0x013B: return 0xA6; /* LATIN CAPITAL LETTER L WITH CEDILLA */ + case 0x00A7: return 0xA7; /* SECTION SIGN */ + case 0x00A8: return 0xA8; /* DIAERESIS */ + case 0x0160: return 0xA9; /* LATIN CAPITAL LETTER S WITH CARON */ + case 0x0112: return 0xAA; /* LATIN CAPITAL LETTER E WITH MACRON */ + case 0x0122: return 0xAB; /* LATIN CAPITAL LETTER G WITH CEDILLA */ + case 0x0166: return 0xAC; /* LATIN CAPITAL LETTER T WITH STROKE */ + case 0x00AD: return 0xAD; /* SOFT HYPHEN */ + case 0x017D: return 0xAE; /* LATIN CAPITAL LETTER Z WITH CARON */ + case 0x00AF: return 0xAF; /* MACRON */ + case 0x00B0: return 0xB0; /* DEGREE SIGN */ + case 0x0105: return 0xB1; /* LATIN SMALL LETTER A WITH OGONEK */ + case 0x02DB: return 0xB2; /* OGONEK */ + case 0x0157: return 0xB3; /* LATIN SMALL LETTER R WITH CEDILLA */ + case 0x00B4: return 0xB4; /* ACUTE ACCENT */ + case 0x0129: return 0xB5; /* LATIN SMALL LETTER I WITH TILDE */ + case 0x013C: return 0xB6; /* LATIN SMALL LETTER L WITH CEDILLA */ + case 0x02C7: return 0xB7; /* CARON */ + case 0x00B8: return 0xB8; /* CEDILLA */ + case 0x0161: return 0xB9; /* LATIN SMALL LETTER S WITH CARON */ + case 0x0113: return 0xBA; /* LATIN SMALL LETTER E WITH MACRON */ + case 0x0123: return 0xBB; /* LATIN SMALL LETTER G WITH CEDILLA */ + case 0x0167: return 0xBC; /* LATIN SMALL LETTER T WITH STROKE */ + case 0x014A: return 0xBD; /* LATIN CAPITAL LETTER ENG */ + case 0x017E: return 0xBE; /* LATIN SMALL LETTER Z WITH CARON */ + case 0x014B: return 0xBF; /* LATIN SMALL LETTER ENG */ + case 0x0100: return 0xC0; /* LATIN CAPITAL LETTER A WITH MACRON */ + case 0x00C1: return 0xC1; /* LATIN CAPITAL LETTER A WITH ACUTE */ + case 0x00C2: return 0xC2; /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ + case 0x00C3: return 0xC3; /* LATIN CAPITAL LETTER A WITH TILDE */ + case 0x00C4: return 0xC4; /* LATIN CAPITAL LETTER A WITH DIAERESIS */ + case 0x00C5: return 0xC5; /* LATIN CAPITAL LETTER A WITH RING ABOVE */ + case 0x00C6: return 0xC6; /* LATIN CAPITAL LIGATURE AE */ + case 0x012E: return 0xC7; /* LATIN CAPITAL LETTER I WITH OGONEK */ + case 0x010C: return 0xC8; /* LATIN CAPITAL LETTER C WITH CARON */ + case 0x00C9: return 0xC9; /* LATIN CAPITAL LETTER E WITH ACUTE */ + case 0x0118: return 0xCA; /* LATIN CAPITAL LETTER E WITH OGONEK */ + case 0x00CB: return 0xCB; /* LATIN CAPITAL LETTER E WITH DIAERESIS */ + case 0x0116: return 0xCC; /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ + case 0x00CD: return 0xCD; /* LATIN CAPITAL LETTER I WITH ACUTE */ + case 0x00CE: return 0xCE; /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ + case 0x012A: return 0xCF; /* LATIN CAPITAL LETTER I WITH MACRON */ + case 0x0110: return 0xD0; /* LATIN CAPITAL LETTER D WITH STROKE */ + case 0x0145: return 0xD1; /* LATIN CAPITAL LETTER N WITH CEDILLA */ + case 0x014C: return 0xD2; /* LATIN CAPITAL LETTER O WITH MACRON */ + case 0x0136: return 0xD3; /* LATIN CAPITAL LETTER K WITH CEDILLA */ + case 0x00D4: return 0xD4; /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ + case 0x00D5: return 0xD5; /* LATIN CAPITAL LETTER O WITH TILDE */ + case 0x00D6: return 0xD6; /* LATIN CAPITAL LETTER O WITH DIAERESIS */ + case 0x00D7: return 0xD7; /* MULTIPLICATION SIGN */ + case 0x00D8: return 0xD8; /* LATIN CAPITAL LETTER O WITH STROKE */ + case 0x0172: return 0xD9; /* LATIN CAPITAL LETTER U WITH OGONEK */ + case 0x00DA: return 0xDA; /* LATIN CAPITAL LETTER U WITH ACUTE */ + case 0x00DB: return 0xDB; /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ + case 0x00DC: return 0xDC; /* LATIN CAPITAL LETTER U WITH DIAERESIS */ + case 0x0168: return 0xDD; /* LATIN CAPITAL LETTER U WITH TILDE */ + case 0x016A: return 0xDE; /* LATIN CAPITAL LETTER U WITH MACRON */ + case 0x00DF: return 0xDF; /* LATIN SMALL LETTER SHARP S */ + case 0x0101: return 0xE0; /* LATIN SMALL LETTER A WITH MACRON */ + case 0x00E1: return 0xE1; /* LATIN SMALL LETTER A WITH ACUTE */ + case 0x00E2: return 0xE2; /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ + case 0x00E3: return 0xE3; /* LATIN SMALL LETTER A WITH TILDE */ + case 0x00E4: return 0xE4; /* LATIN SMALL LETTER A WITH DIAERESIS */ + case 0x00E5: return 0xE5; /* LATIN SMALL LETTER A WITH RING ABOVE */ + case 0x00E6: return 0xE6; /* LATIN SMALL LIGATURE AE */ + case 0x012F: return 0xE7; /* LATIN SMALL LETTER I WITH OGONEK */ + case 0x010D: return 0xE8; /* LATIN SMALL LETTER C WITH CARON */ + case 0x00E9: return 0xE9; /* LATIN SMALL LETTER E WITH ACUTE */ + case 0x0119: return 0xEA; /* LATIN SMALL LETTER E WITH OGONEK */ + case 0x00EB: return 0xEB; /* LATIN SMALL LETTER E WITH DIAERESIS */ + case 0x0117: return 0xEC; /* LATIN SMALL LETTER E WITH DOT ABOVE */ + case 0x00ED: return 0xED; /* LATIN SMALL LETTER I WITH ACUTE */ + case 0x00EE: return 0xEE; /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ + case 0x012B: return 0xEF; /* LATIN SMALL LETTER I WITH MACRON */ + case 0x0111: return 0xF0; /* LATIN SMALL LETTER D WITH STROKE */ + case 0x0146: return 0xF1; /* LATIN SMALL LETTER N WITH CEDILLA */ + case 0x014D: return 0xF2; /* LATIN SMALL LETTER O WITH MACRON */ + case 0x0137: return 0xF3; /* LATIN SMALL LETTER K WITH CEDILLA */ + case 0x00F4: return 0xF4; /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ + case 0x00F5: return 0xF5; /* LATIN SMALL LETTER O WITH TILDE */ + case 0x00F6: return 0xF6; /* LATIN SMALL LETTER O WITH DIAERESIS */ + case 0x00F7: return 0xF7; /* DIVISION SIGN */ + case 0x00F8: return 0xF8; /* LATIN SMALL LETTER O WITH STROKE */ + case 0x0173: return 0xF9; /* LATIN SMALL LETTER U WITH OGONEK */ + case 0x00FA: return 0xFA; /* LATIN SMALL LETTER U WITH ACUTE */ + case 0x00FB: return 0xFB; /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ + case 0x00FC: return 0xFC; /* LATIN SMALL LETTER U WITH DIAERESIS */ + case 0x0169: return 0xFD; /* LATIN SMALL LETTER U WITH TILDE */ + case 0x016B: return 0xFE; /* LATIN SMALL LETTER U WITH MACRON */ + case 0x02D9: return 0xFF; /* DOT ABOVE */ +#endif +} + +static void +unicode_latin5( + char *arg +) +{ + int i; + static unsigned int latin5_unicode_map1[] = { + 0x0080, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x008e, 0x008f, /* 88 */ + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x009e, 0x0178, /* 98 */ + }; + static unsigned int latin5_unicode_map2[] = { + 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, /* D0 */ + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, /* D8 */ + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, /* E0 direct */ + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, /* E8 direct */ + 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, /* F0 */ + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, /* F8 */ + }; + + for(i=0; i<=0x7F; i++) + unicode_map[i] = i; + + for(i=0x80; i<=0x9F; i++) + unicode_map[i] = latin5_unicode_map1[i-0x80]; + + for(i=0xA0; i<=0xCF; i++) + unicode_map[i] = i; + + for(i=0xD0; i<=0xFF; i++) + unicode_map[i] = latin5_unicode_map2[i-0xD0]; +} + +/* a way to select one 256-character plane from Unicode + * or other multi-byte encoding + */ + +static void +unicode_plane( + char *arg +) +{ + static unsigned plane; + int nchars; + int c1, c2, i; + + if(uni_lang_selected == 0) + return; /* don't participate in auto-guessing */ + + plane = 0; force_pid = force_eid = -1; + + c1 = sscanf(arg, "pid=%d,eid=%d%n", &force_pid, &force_eid, &nchars); + if(c1 == 2) { + arg += nchars; + if(*arg == ',') + arg++; + } + if(arg[0] == '0' && (arg[1]=='x' || arg[1]=='X') ) { + arg += 2; + c2 = sscanf(arg, "%x", &plane); + } else { + c2 = sscanf(arg, "%d", &plane); + } + + if( (c1!=2 && c1!=0) || (c1==0 && c2==0) ) { + fprintf(stderr, "**** option -l plane expects one of the following formats:\n"); + fprintf(stderr, " -l plane+0xNN - select hexadecimal number of plane of Unicode\n"); + fprintf(stderr, " -l plane+NN - select decimal number of plane of Unicode\n"); + fprintf(stderr, " -l plane+pid=N,eid=N - select plane 0 of specified encoding\n"); + fprintf(stderr, " -l plane+pid=N,eid=N,0xNN - select hex plane of TTF encoding with this PID/EID\n"); + fprintf(stderr, " -l plane+pid=N,eid=N,NN - select decimal plane of TTF encoding with this PID/EID\n"); + exit(1); + } + + if(c2!=0) { + if(strlen(arg) > sizeof(uni_suffix_buf)-2) { + fprintf(stderr, "**** plane number is too large\n"); + } + + sprintf(uni_suffix_buf, "-%s", arg); + uni_font_name_suffix = uni_suffix_buf; + } else { + uni_font_name_suffix = ""; + } + + plane <<= 8; + for(i=0; i<=0xFF; i++) + unicode_map[i] = plane | i; +} + +/* look up the 8-bit code by unicode */ + +int +unicode_rev_lookup( + int unival +) +{ + int res; + + if( ! IS_UNI_BUCKET(unival) ) + return -1; + + for (res = 0; res < enctabsz; res++) + if (unicode_map[res] == unival) + return res; + return -1; +} + +/* mark the buckets for quick lookup */ + +static void +unicode_prepare_buckets( + void +) +{ + int i; + + memset(uni_user_buckets, 0, sizeof uni_user_buckets); + for(i=0; i 0 ? scale_factor * val + 0.5 + : scale_factor * val - 0.5); +} + +/* + * Try to force fixed width of characters + */ + +static void +alignwidths(void) +{ + int i; + int n = 0, avg, max = 0, min = 3000, sum = 0, x; + + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + x = glyph_list[i].width; + + if (x != 0) { + if (x < min) + min = x; + if (x > max) + max = x; + + sum += x; + n++; + } + } + } + + if (n == 0) + return; + + avg = sum / n; + + WARNING_3 fprintf(stderr, "widths: max=%d avg=%d min=%d\n", max, avg, min); + + /* if less than 5% variation from average */ + /* force fixed width */ + if (20 * (avg - min) < avg && 20 * (max - avg) < avg) { + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) + glyph_list[i].width = avg; + } + fontm.is_fixed_pitch = 1; + } +} + +static void +convert_glyf( + int glyphno +) +{ + GLYPH *g; + int ncurves; + + g = &glyph_list[glyphno]; + + + g->scaledwidth = iscale(g->width); + + g->entries = 0; + g->lastentry = 0; + g->path = 0; + if (g->ttf_pathlen != 0) { + cursw->glpath(glyphno, glyph_list); + g->lastentry = 0; + + if(ISDBG(BUILDG)) + dumppaths(g, NULL, NULL); + + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fclosepaths(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + /* float processing */ + if(smooth) { + ffixquadrants(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fsplitzigzags(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fforceconcise(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + fstraighten(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + } + + pathtoint(g); + /* all processing past this point expects integer path */ + assertpath(g->entries, __FILE__, __LINE__, g->name); + +#if 0 + fixcontours(g); + testfixcvdir(g); +#endif + + /* int processing */ + if (smooth) { + smoothjoints(g); + assertpath(g->entries, __FILE__, __LINE__, g->name); + + flattencurves(g); + } + + ncurves = 0; + { + GENTRY *ge; + for(ge = g->entries; ge; ge = ge->next) + ncurves++; + } + if (ncurves > 100) { + WARNING_2 fprintf(stderr, + "** Glyph %s is too long, may display incorrectly\n", + g->name); + } + } else { + /* for buildstems */ + g->flags &= ~GF_FLOAT; + } +} + +static void +handle_gnames(void) +{ + int i, n, found, c, type; + + /* get the names from the font file */ + ps_fmt_3 = cursw->glnames(glyph_list); + + /* check for names with wrong characters */ + for (n = 0; n < numglyphs; n++) { + int c; + for (i = 0; (c = glyph_list[n].name[i]) != 0; i++) { + if (!(isalnum(c) || c == '.' || c == '_' ) + || i==0 && isdigit(c)) { /* must not start with a digit */ + WARNING_3 fprintf(stderr, "Glyph %d %s (%s), ", + n, isdigit(c) ? "name starts with a digit" : + "has bad characters in name", + glyph_list[n].name); + glyph_list[n].name = malloc(10); + sprintf(glyph_list[n].name, "_%d", n); + WARNING_3 fprintf(stderr, "changing to %s\n", glyph_list[n].name); + break; + } + } + } + + if( !ps_fmt_3 ) { + /* check for duplicate names */ + for (n = 0; n < numglyphs; n++) { + found = 0; + for (i = 0; i < n && !found; i++) { + if (strcmp(glyph_list[i].name, glyph_list[n].name) == 0) { + glyph_list[n].name = malloc(10); + sprintf(glyph_list[n].name, "_%d", n); + WARNING_3 fprintf(stderr, + "Glyph %d has the same name as %d: (%s), changing to %s\n", + n, i, + glyph_list[i].name, + glyph_list[n].name); + found = 1; + } + } + } + + } + + /* start the encoding stuff */ + for (i = 0; i < ENCTABSZ; i++) { + encoding[i] = -1; + } + + /* do the 1st round of encoding by name */ + if(!ps_fmt_3 && uni_lang_selected && uni_lang_selected->convbyname) { + for (n = 0; n < numglyphs; n++) { + c = uni_lang_selected->convbyname(glyph_list[n].name, + uni_lang_arg, UNICONV_BYNAME_BEFORE); + if(c>=0 && cinit[i]; i++) { + for (n = 0; n < ENCTABSZ; n++) + unicode_map[n] = -1; + uni_lang_selected->init[i](uni_lang_arg); + unicode_prepare_buckets(); + if( cursw->glenc(glyph_list, encoding, unicode_map) == 0 ) + /* if we have an 8-bit encoding we don't need more tries */ + break; + } + } else { + /* language is unknown, try the first table of each */ + for(i=0; i < sizeof uni_lang/(sizeof uni_lang[0]); i++) { + if(uni_lang[i].init[0] == NULL) + continue; + for (n = 0; n < ENCTABSZ; n++) + unicode_map[n] = -1; + uni_lang[i].init[0](uni_lang_arg); + unicode_prepare_buckets(); + if( cursw->glenc(glyph_list, encoding, unicode_map) == 0 ) + /* if we have an 8-bit encoding we don't need more tries */ + break; + } + } + + if (ps_fmt_3) { + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ + if (encoding[i] > 0) { + glyph_list[encoding[i]].name = Fmt3Encoding[i]; + } + } + } + + /* do the 2nd round of encoding by name */ + if(uni_lang_selected && uni_lang_selected->convbyname) { + for (n = 0; n < numglyphs; n++) { + c = uni_lang_selected->convbyname(glyph_list[n].name, + uni_lang_arg, UNICONV_BYNAME_AFTER); + if(c>=0 && c 0) + glyph_list[0].name = ".notdef"; + if(numglyphs > 1) + glyph_list[1].name = ".null"; + + for (i = 0; i < ENCTABSZ; i++) { + if ((encoding[i] != 0) && glyph_rename[i]) { + glyph_list[encoding[i]].name = glyph_rename[i]; + } + } + +} + +static void +usage(void) +{ + +#ifdef _GNU_SOURCE +# define fplop(txt) fputs(txt, stderr); +#else +# define fplop(txt) +#endif + + fputs("Use:\n", stderr); + fputs("ttf2pt1 [-] [-l language | -L file] []\n", stderr); + fputs(" or\n", stderr); + fputs("ttf2pt1 [-] [-l language | -L file] -\n", stderr); + fputs(" or\n", stderr); + fputs("ttf2pt1 [-] [-l language | -L file] - | t1asm > \n", stderr); + + fplop("\n"); + fplop("This build supports both short and long option names,\n"); + fplop("the long options are listed before corresponding short ones\n"); + + fplop(" --afm\n"); + fputs(" -A - write the .afm file to STDOUT instead of the font itself\n", stderr); + fplop(" --all-glyphs\n"); + fputs(" -a - include all glyphs, even those not in the encoding table\n", stderr); + fplop(" --pfb\n"); + fputs(" -b - produce a compressed .pfb file\n", stderr); + fplop(" --debug dbg_suboptions\n"); + fputs(" -d dbg_suboptions - debugging options, run ttf2pt1 -d? for help\n", stderr); + fplop(" --encode\n"); + fputs(" -e - produce a fully encoded .pfa file\n", stderr); + fplop(" --force-unicode\n"); + fputs(" -F - force use of Unicode encoding even if other MS encoding detected\n", stderr); + fplop(" --language language\n"); + fputs(" -l language - convert Unicode to specified language, run ttf2pt1 -l? for list\n", stderr); + fplop(" --language-map file\n"); + fputs(" -L file - convert Unicode according to encoding description file\n", stderr); + fplop(" --limit =\n"); + fputs(" -m = - set maximal limit of given type to value, types:\n", stderr); + fputs(" h - maximal hint stack depth in the PostScript interpreter\n", stderr); + fplop(" --processing suboptions\n"); + fputs(" -O suboptions - control outline processing, run ttf2pt1 -O? for help\n", stderr); + fplop(" --parser name\n"); + fputs(" -p name - use specific front-end parser, run ttf2pt1 -p? for list\n", stderr); + fplop(" --uid id\n"); + fputs(" -u id - use this UniqueID, -u A means autogeneration\n", stderr); + fplop(" --vertical-autoscale size\n"); + fputs(" -v size - scale the font to make uppercase letters >size/1000 high\n", stderr); + fplop(" --version\n"); + fputs(" -V - print ttf2pt1 version number\n", stderr); + fplop(" --warning number\n"); + fputs(" -W number - set the level of permitted warnings (0 - disable)\n", stderr); + fputs("Obsolete options (will be removed in future releases, use -O? instead):\n", stderr); + fputs(" -f - don't try to guess the value of the ForceBold hint\n", stderr); + fputs(" -h - disable autogeneration of hints\n", stderr); + fputs(" -H - disable hint substitution\n", stderr); + fputs(" -o - disable outline optimization\n", stderr); + fputs(" -s - disable outline smoothing\n", stderr); + fputs(" -t - disable auto-scaling to 1000x1000 standard matrix\n", stderr); + fputs(" -w - correct the glyph widths (use only for buggy fonts)\n", stderr); + fputs("With no , write to with suffix replaced.\n", stderr); + fputs("The last '-' means 'use STDOUT'.\n", stderr); + +#undef fplop + +} + +static void +printversion(void) +{ + fprintf(stderr, "ttf2pt1 %s\n", TTF2PT1_VERSION); +} + +int +ttf2pt1_main( + int argc, + char **argv +) +{ + int i, j; + time_t now; + char filename[256]; + int c,nchars,nmetrics; + int ws; + int forcebold= -1; /* -1 means "don't know" */ + char *lang; + int oc; + int subid; +#ifdef _GNU_SOURCE +# define ttf2pt1_getopt(a, b, c, d, e) getopt_long(a, b, c, d, e) + static struct option longopts[] = { + { "afm", 0, NULL, 'A' }, + { "all-glyphs", 0, NULL, 'a' }, + { "pfb", 0, NULL, 'b' }, + { "debug", 1, NULL, 'd' }, + { "encode", 0, NULL, 'e' }, + { "force-unicode", 0, NULL, 'F' }, + { "language", 1, NULL, 'l' }, + { "language-map", 1, NULL, 'L' }, + { "limit", 1, NULL, 'm' }, + { "processing", 1, NULL, 'O' }, + { "parser", 1, NULL, 'p' }, + { "uid", 1, NULL, 'u' }, + { "vertical-autoscale", 1, NULL, 'v' }, + { "version", 0, NULL, 'V' }, + { "warning", 1, NULL, 'W' }, + { NULL, 0, NULL, 0 } + }; +#else +# define ttf2pt1_getopt(a, b, c, d, e) getopt(a, b, c) +#endif + + /* initialize sub-options of -O */ + for(i=0; i< (sizeof opotbl)/(sizeof opotbl[0]); i++) { + opotbl[i].disbl = tolower(opotbl[i].disbl); + opotbl[i].enbl = toupper(opotbl[i].disbl); + *(opotbl[i].valp) = opotbl[i].dflt; + } + + while(( oc=ttf2pt1_getopt(argc, argv, "FaoebAsthHfwVv:p:l:d:u:L:m:W:O:", + longopts, NULL) )!= -1) { + switch(oc) { + case 'F': + forcemap = 1; + break; + case 'o': + fputs("Warning: option -o is obsolete, use -Oo instead\n", stderr); + optimize = 0; + break; + case 'e': + encode = 1; + break; + case 'b': + encode = pfbflag = 1; + break; + case 'A': + wantafm = 1; + break; + case 'a': + allglyphs = 1; + break; + case 's': + fputs("Warning: option -s is obsolete, use -Os instead\n", stderr); + smooth = 0; + break; + case 't': + fputs("Warning: option -t is obsolete, use -Ot instead\n", stderr); + transform = 0; + break; + case 'd': + for(i=0; optarg[i]!=0; i++) + switch(optarg[i]) { + case 'a': + absolute = 1; + break; + case 'r': + reverse = 0; + break; + default: + if (optarg[i] != '?') + fprintf(stderr, "**** Unknown debugging option '%c' ****\n", optarg[i]); + fputs("The recognized debugging options are:\n", stderr); + fputs(" a - enable absolute coordinates\n", stderr); + fputs(" r - do not reverse font outlines directions\n", stderr); + exit(1); + break; + }; + break; + case 'm': + { + char subopt; + int val; + + if(sscanf(optarg, "%c=%d", &subopt, &val) !=2) { + fprintf(stderr, "**** Misformatted maximal limit ****\n"); + fprintf(stderr, "spaces around the equal sign are not allowed\n"); + fprintf(stderr, "good examples: -mh=100 -m h=100\n"); + fprintf(stderr, "bad examples: -mh = 100 -mh= 100\n"); + exit(1); + break; + } + switch(subopt) { + case 'h': + max_stemdepth = val; + break; + default: + if (subopt != '?') + fprintf(stderr, "**** Unknown limit type '%c' ****\n", subopt); + fputs("The recognized limit types are:\n", stderr); + fputs(" h - maximal hint stack depth in the PostScript interpreter\n", stderr); + exit(1); + break; + } + break; + } + case 'O': + { + char subopt; + char *p; + char dflt[20]; /* should be big enough */ + for(p=optarg; (subopt = *p) != 0; p++) { + for(i=0; i< (sizeof opotbl)/(sizeof opotbl[0]); i++) { + if(subopt == opotbl[i].disbl) { + *(opotbl[i].valp) = 0; + break; + } else if(subopt == opotbl[i].enbl) { + *(opotbl[i].valp) = 1; + break; + } + } + if( i == (sizeof opotbl)/(sizeof opotbl[0]) ) { /* found no match */ + if (subopt != '?') + fprintf(stderr, "**** Unknown outline processing suboption '%c' ****\n", subopt); + fprintf(stderr,"The general form of the outline processing option is:\n"); + fprintf(stderr," -O suboptions\n"); + fprintf(stderr,"(To remember easily -O may be also thought of as \"optimization\").\n"); + fprintf(stderr,"The lowercase suboptions disable features, corresponding\n"); + fprintf(stderr,"uppercase suboptions enable them. The supported suboptions,\n"); + fprintf(stderr,"their default states and the features they control are:\n"); + p = dflt; + for(i=0; i< (sizeof opotbl)/(sizeof opotbl[0]); i++) { + fprintf(stderr," %c/%c - [%s] %s\n", opotbl[i].disbl, opotbl[i].enbl, + opotbl[i].dflt ? "enabled" : "disabled", opotbl[i].descr); + if(opotbl[i].dflt) + *p++ = opotbl[i].enbl; + else + *p++ = opotbl[i].disbl; + } + *p = 0; + fprintf(stderr, "The default state corresponds to the option -O %s\n", dflt); + exit(1); + } + } + break; + } + case 'h': + fputs("Warning: option -h is obsolete, use -Oh instead\n", stderr); + hints = 0; + break; + case 'H': + fputs("Warning: meaning of option -H has been changed to its opposite\n", stderr); + fputs("Warning: option -H is obsolete, use -Ou instead\n", stderr); + subhints = 0; + break; + case 'f': + fputs("Warning: option -f is obsolete, use -Ob instead\n", stderr); + trybold = 0; + break; + case 'w': + fputs("Warning: option -w is obsolete, use -OW instead\n", stderr); + correctwidth = 1; + break; + case 'u': + if(wantuid) { + fprintf(stderr, "**** UniqueID may be specified only once ****\n"); + exit(1); + } + wantuid = 1; + if(optarg[0]=='A' && optarg[1]==0) + strUID=0; /* will be generated automatically */ + else { + strUID=optarg; + for(i=0; optarg[i]!=0; i++) + if( !isdigit(optarg[i]) ) { + fprintf(stderr, "**** UniqueID must be numeric or A for automatic ****\n"); + exit(1); + } + } + break; + case 'v': + correctvsize = atoi(optarg); + if(correctvsize <= 0 && correctvsize > 1000) { + fprintf(stderr, "**** wrong vsize '%d', ignored ****\n", correctvsize); + correctvsize=0; + } + break; + case 'p': + if(cursw!=0) { + fprintf(stderr, "**** only one front-end parser be used ****\n"); + exit(1); + } + + { /* separate parser from parser-specific argument */ + char *p = strchr(optarg, LANG_ARG_SEP); + if(p != 0) { + *p = 0; + front_arg = p+1; + } else + front_arg = ""; + } + for(i=0; frontswtab[i] != NULL; i++) + if( !strcmp(frontswtab[i]->name, optarg) ) { + cursw = frontswtab[i]; + break; + } + + if(cursw==0) { + if (strcmp(optarg, "?")) + fprintf(stderr, "**** unknown front-end parser '%s' ****\n", optarg); + fputs("the following front-ends are supported now:\n", stderr); + for(i=0; frontswtab[i] != NULL; i++) { + fprintf(stderr," %s (%s)\n file suffixes: ", + frontswtab[i]->name, + frontswtab[i]->descr ? frontswtab[i]->descr : "no description" + ); + for(j=0; jsuffix[j]) + fprintf(stderr, "%s ", frontswtab[i]->suffix[j]); + fprintf(stderr, "\n"); + } + exit(1); + } + break; + case 'l': + if(uni_lang_selected!=0) { + fprintf(stderr, "**** only one language option may be used ****\n"); + exit(1); + } + + { /* separate language from language-specific argument */ + char *p = strchr(optarg, LANG_ARG_SEP); + if(p != 0) { + *p = 0; + uni_lang_arg = p+1; + } else + uni_lang_arg = ""; + } + for(i=0; i < sizeof uni_lang/(sizeof uni_lang[0]); i++) + if( !strcmp(uni_lang[i].name, optarg) ) { + uni_lang_selected = &uni_lang[i]; + uni_sample = uni_lang[i].sample_upper; + break; + } + + if(uni_lang_selected==0) { + if (strcmp(optarg, "?")) + fprintf(stderr, "**** unknown language '%s' ****\n", optarg); + fputs(" the following languages are supported now:\n", stderr); + for(i=0; i < sizeof uni_lang/(sizeof uni_lang[0]); i++) + fprintf(stderr," %s (%s)\n", + uni_lang[i].name, + uni_lang[i].descr ? uni_lang[i].descr : "no description" + ); + exit(1); + } + break; + case 'L': + if(uni_lang_selected!=0) { + fprintf(stderr, "**** only one language option may be used ****\n"); + exit(1); + } + uni_lang_selected = &uni_lang_user; + uni_lang_arg = optarg; + break; + case 'V': + printversion(); + exit(0); + break; + default: + usage(); + exit(1); + break; + } + } + argc-=optind-1; /* the rest of code counts from argv[0] */ + argv+=optind-1; + + if (absolute && encode) { + fprintf(stderr, "**** options -a and -e are incompatible ****\n"); + exit(1); + } + if ((argc != 2) && (argc != 3)) { + usage(); + exit(1); + } + + /* try to guess the language by the locale used */ + if(uni_lang_selected==0 && (lang=getenv("LANG"))!=0 ) { + for(i=0; i < sizeof uni_lang/sizeof(struct uni_language); i++) { + if( !strncmp(uni_lang[i].name, lang, strlen(uni_lang[i].name)) ) { + uni_lang_selected = &uni_lang[i]; + goto got_a_language; + } + } + /* no full name ? try aliases */ + for(i=0; i < sizeof uni_lang/sizeof(struct uni_language); i++) { + for(c=0; csuffix[j] + && !strcmp(p, frontswtab[i]->suffix[j]) ) { + cursw = frontswtab[i]; + WARNING_1 fprintf(stderr, "Auto-detected front-end parser '%s'\n", + cursw->name); + WARNING_1 fprintf(stderr, " (use ttf2pt1 -p? to get the full list of available front-ends)\n"); + break; + } + } + free(s); + } + + if(cursw==0) { + cursw = frontswtab[0]; + WARNING_1 fprintf(stderr, "Can't detect front-end parser, using '%s' by default\n", + cursw->name); + WARNING_1 fprintf(stderr, " (use ttf2pt1 -p? to get the full list of available front-ends)\n"); + } + } + + /* open the input file */ + cursw->open(argv[1], front_arg); + + /* Get base name of output file (if not specified) + * by removing (known) suffixes + */ + if (argc == 2) { + char *p; + argv[2] = strdup (argv[1]); + p = strrchr(argv[2], '.'); + if (p != NULL) + for (j = 0; (j < MAXSUFFIX) && (cursw->suffix[j]); j++) + if (!strcmp(p+1, cursw->suffix[j])) { + *p = '\0'; + break; + } + } + + if (argv[2][0] == '-' && argv[2][1] == 0) { + pfa_file = stdout; +#ifdef WINDOWS + if(encode) { + fprintf(stderr, "**** can't write encoded file to stdout ***\n"); + exit(1); + } +#endif /* WINDOWS */ + if ((afm_file = fopen(BITBUCKET, "w+")) == NULL) { + fprintf(stderr, "**** Cannot open %s ****\n", + BITBUCKET); + exit(1); + } + if(wantafm) { /* print .afm instead of .pfa */ + FILE *n; + n=pfa_file; + pfa_file=afm_file; + afm_file=n; + } + } else { +#ifndef WINDOWS + sprintf(filename, "%s.%s", argv[2], encode ? (pfbflag ? "pfb" : "pfa") : "t1a" ); +#else /* WINDOWS */ + sprintf(filename, "%s.t1a", argv[2]); +#endif /* WINDOWS */ + if ((pfa_file = fopen(filename, "w+b")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Creating file %s\n", filename); + } + + sprintf(filename, "%s.afm", argv[2]) ; + if ((afm_file = fopen(filename, "w+")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } + } + + /* + * Now check whether we want a fully encoded .pfa file + */ +#ifndef WINDOWS + if (encode) { + int p[2]; + extern FILE *ifp, *ofp; /* from t1asm.c */ + + ifp=stdin; + ofp=stdout; + + if (pipe(p) < 0) { + perror("**** Cannot create pipe ****\n"); + exit(1); + } + ofp = pfa_file; + ifp = fdopen(p[0], "r"); + if (ifp == NULL) { + perror("**** Cannot use pipe for reading ****\n"); + exit(1); + } + pfa_file = fdopen(p[1], "w"); + if (pfa_file == NULL) { + perror("**** Cannot use pipe for writing ****\n"); + exit(1); + } + switch (fork()) { + case -1: + perror("**** Cannot fork the assembler process ****\n"); + exit(1); + case 0: /* child */ + fclose(pfa_file); + exit(runt1asm(pfbflag)); + default: /* parent */ + fclose(ifp); fclose(ofp); + } + } +#endif /* WINDOWS */ + + numglyphs = cursw->nglyphs(); + + WARNING_3 fprintf(stderr, "numglyphs = %d\n", numglyphs); + + glyph_list = (GLYPH *) calloc(numglyphs, sizeof(GLYPH)); + + /* initialize non-0 fields */ + for (i = 0; i < numglyphs; i++) { + GLYPH *g; + + g = &glyph_list[i]; + g->char_no = -1; + g->orig_code = -1; + g->name = "UNKNOWN"; + g->flags = GF_FLOAT; /* we start with float representation */ + } + + handle_gnames(); + + cursw->glmetrics(glyph_list); + cursw->fnmetrics(&fontm); + + original_scale_factor = 1000.0 / (double) fontm.units_per_em; + + if(transform == 0) + scale_factor = 1.0; /* don't transform */ + else + scale_factor = original_scale_factor; + + if(correctvsize && uni_sample!=0) { /* only for known languages */ + /* try to adjust the scale factor to make a typical + * uppercase character of hight at least (correctvsize), this + * may improve the appearance of the font but also + * make it weird, use with caution + */ + int ysz; + + ysz = iscale(glyph_list[encoding[uni_sample]].yMax); + if( ysz 45.0 || italic_angle < -45.0) + italic_angle = 0.0; /* consider buggy */ + + if (hints) { + findblues(); + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + DBG_TO_GLYPH(&glyph_list[i]); + buildstems(&glyph_list[i]); + assertpath(glyph_list[i].entries, __FILE__, __LINE__, glyph_list[i].name); + DBG_FROM_GLYPH(&glyph_list[i]); + } + } + stemstatistics(); + } else { + for(i=0; i<4; i++) + bbox[i] = iscale(fontm.bbox[i]); + } + /* don't touch the width of fixed width fonts */ + if( fontm.is_fixed_pitch ) + correctwidth=0; + docorrectwidth(); /* checks correctwidth inside */ + if (reverse) + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + DBG_TO_GLYPH(&glyph_list[i]); + reversepaths(&glyph_list[i]); + assertpath(glyph_list[i].entries, __FILE__, __LINE__, glyph_list[i].name); + DBG_FROM_GLYPH(&glyph_list[i]); + } + } + + +#if 0 + /* + ** It seems to bring troubles. The problem is that some + ** styles of the font may be recognized as fixed-width + ** while other styles of the same font as proportional. + ** So it's better to be commented out yet. + */ + if (tryfixed) + alignwidths(); +#endif + + if(trybold) { + forcebold = fontm.force_bold; + } + + fprintf(pfa_file, "%%!PS-AdobeFont-1.0: %s %s\n", fontm.name_ps, fontm.name_copyright); + time(&now); + fprintf(pfa_file, "%%%%CreationDate: %s", ctime(&now)); + fprintf(pfa_file, "%% Converted from TrueType font %s by ttf2pt1 %s/%s\n%%\n", argv[1], TTF2PT1_VERSION, cursw->name); + fprintf(pfa_file, "%%%%EndComments\n"); + fprintf(pfa_file, "12 dict begin\n/FontInfo 9 dict dup begin\n"); + + WARNING_3 fprintf(stderr, "FontName %s%s\n", fontm.name_ps, uni_font_name_suffix); + + + fprintf(pfa_file, "/version (%s) readonly def\n", fontm.name_version); + + fprintf(pfa_file, "/Notice (%s) readonly def\n", fontm.name_copyright); + + fprintf(pfa_file, "/FullName (%s) readonly def\n", fontm.name_full); + fprintf(pfa_file, "/FamilyName (%s) readonly def\n", fontm.name_family); + + if(wantuid) { + if(strUID) + fprintf(pfa_file, "/UniqueID %s def\n", strUID); + else { + numUID=0; + for(i=0; fontm.name_full[i]!=0; i++) { + numUID *= 37; /* magic number, good for hash */ + numUID += fontm.name_full[i]-' '; + /* if the name is long the first chars + * may be lost forever, so re-insert + * them thus making kind of CRC + */ + numUID += (numUID>>24) & 0xFF; + } + /* the range for private UIDs is 4 000 000 - 4 999 999 */ + fprintf(pfa_file, "/UniqueID %lu def\n", numUID%1000000+4000000); + } + } + + fprintf(pfa_file, "/Weight (%s) readonly def\n", fontm.name_style); + + fprintf(pfa_file, "/ItalicAngle %f def\n", italic_angle); + fprintf(pfa_file, "/isFixedPitch %s def\n", + fontm.is_fixed_pitch ? "true" : "false"); + + /* we don't print out the unused glyphs */ + nchars = 0; + for (i = 0; i < numglyphs; i++) { + if (glyph_list[i].flags & GF_USED) { + nchars++; + } + } + + fprintf(afm_file, "StartFontMetrics 4.1\n"); + fprintf(afm_file, "FontName %s%s\n", fontm.name_ps, uni_font_name_suffix); + fprintf(afm_file, "FullName %s\n", fontm.name_full); + fprintf(afm_file, "Notice %s\n", fontm.name_copyright); + fprintf(afm_file, "EncodingScheme FontSpecific\n"); + fprintf(afm_file, "FamilyName %s\n", fontm.name_family); + fprintf(afm_file, "Weight %s\n", fontm.name_style); + fprintf(afm_file, "Version %s\n", fontm.name_version); + fprintf(afm_file, "Characters %d\n", nchars); + fprintf(afm_file, "ItalicAngle %.1f\n", italic_angle); + + fprintf(afm_file, "Ascender %d\n", iscale(fontm.ascender)); + fprintf(afm_file, "Descender %d\n", iscale(fontm.descender)); + + fprintf(pfa_file, "/UnderlinePosition %d def\n", + iscale(fontm.underline_position)); + + fprintf(pfa_file, "/UnderlineThickness %hd def\nend readonly def\n", + iscale(fontm.underline_thickness)); + + fprintf(afm_file, "UnderlineThickness %d\n", + iscale(fontm.underline_thickness)); + + fprintf(afm_file, "UnderlinePosition %d\n", + iscale(fontm.underline_position)); + + fprintf(afm_file, "IsFixedPitch %s\n", + fontm.is_fixed_pitch ? "true" : "false"); + fprintf(afm_file, "FontBBox %d %d %d %d\n", + bbox[0], bbox[1], bbox[2], bbox[3]); + + fprintf(pfa_file, "/FontName /%s%s def\n", fontm.name_ps, uni_font_name_suffix); + fprintf(pfa_file, "/PaintType 0 def\n/StrokeWidth 0 def\n"); + /* I'm not sure if these are fixed */ + fprintf(pfa_file, "/FontType 1 def\n"); + + if (transform) { + fprintf(pfa_file, "/FontMatrix [0.001 0 0 0.001 0 0] def\n"); + } else { + fprintf(pfa_file, "/FontMatrix [%9.7f 0 0 %9.7f 0 0] def\n", + original_scale_factor / 1000.0, original_scale_factor / 1000.0); + } + + fprintf(pfa_file, "/FontBBox {%d %d %d %d} readonly def\n", + bbox[0], bbox[1], bbox[2], bbox[3]); + + fprintf(pfa_file, "/Encoding 256 array\n"); + /* determine number of elements for metrics table */ + nmetrics = 256; + for (i = 0; i < numglyphs; i++) { + if( glyph_list[i].flags & GF_USED + && glyph_list[i].char_no == -1 ) { + nmetrics++; + } + } + fprintf(afm_file, "StartCharMetrics %d\n", nmetrics); + + for (i = 0; i < 256; i++) { /* here 256, not ENCTABSZ */ + fprintf(pfa_file, + "dup %d /%s put\n", i, glyph_list[encoding[i]].name); + if( glyph_list[encoding[i]].flags & GF_USED ) { + print_glyph_metrics(i, encoding[i]); + } + } + + /* print the metrics for glyphs not in encoding table */ + for(i=0; ikerning(glyph_list); + print_kerning(afm_file); + + fprintf(afm_file, "EndFontMetrics\n"); + fclose(afm_file); + + WARNING_1 fprintf(stderr, "Finished - font files created\n"); + + cursw->close(); + +#ifndef WINDOWS + while (wait(&ws) > 0) { + } +#else + if (encode) { + extern FILE *ifp, *ofp; /* from t1asm.c */ + + sprintf(filename, "%s.%s", argv[2], pfbflag ? "pfb" : "pfa" ); + + if ((ofp = fopen(filename, "w+b")) == NULL) { + fprintf(stderr, "**** Cannot create %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Creating file %s\n", filename); + } + + sprintf(filename, "%s.t1a", argv[2]); + + if ((ifp = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "**** Cannot read %s ****\n", filename); + exit(1); + } else { + WARNING_2 fprintf(stderr, "Converting file %s\n", filename); + } + + runt1asm(pfbflag); + + WARNING_2 fprintf(stderr, "Removing file %s\n", filename); + if(unlink(filename) < 0) + WARNING_1 fprintf(stderr, "Unable to remove file %s\n", filename); + } +#endif /* WINDOWS */ + + return 0; +} diff --git a/pdf2swf/ttf2pt1/ttf2pt1.h b/pdf2swf/ttf2pt1/ttf2pt1.h new file mode 100644 index 0000000..b0e072f --- /dev/null +++ b/pdf2swf/ttf2pt1/ttf2pt1.h @@ -0,0 +1,8 @@ +#ifndef __ttf2pt1_included__ +int +ttf2pt1_main( + int argc, + char **argv +); +#endif + diff --git a/pdf2swf/ttf2pt1/version.h b/pdf2swf/ttf2pt1/version.h new file mode 100644 index 0000000..70827df --- /dev/null +++ b/pdf2swf/ttf2pt1/version.h @@ -0,0 +1,7 @@ +/* + * see COPYRIGHT + */ + + +/* version number */ +#define TTF2PT1_VERSION "3.3.5" diff --git a/pdf2swf/ttf2pt1/windows.h b/pdf2swf/ttf2pt1/windows.h new file mode 100644 index 0000000..a90ecf7 --- /dev/null +++ b/pdf2swf/ttf2pt1/windows.h @@ -0,0 +1,93 @@ +/* + * Implementation of things missing in Windows + */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#undef ntohs +#undef ntohl +#undef htonl + +#ifdef WINDOWS_FUNCTIONS +/* byte order */ + +static unsigned short StoM(unsigned short inv) { + union iconv { + unsigned short ui; + unsigned char uc[2]; + } *inp, outv; + + inp = (union iconv *)&inv; + + outv.uc[0] = inp->uc[1]; + outv.uc[1] = inp->uc[0]; + + return (outv.ui); +} + +static unsigned int ItoM(unsigned int inv) { + union iconv { + unsigned int ui; + unsigned char uc[4]; + } *inp, outv; + + inp = (union iconv *)&inv; + + outv.uc[0] = inp->uc[3]; + outv.uc[1] = inp->uc[2]; + outv.uc[2] = inp->uc[1]; + outv.uc[3] = inp->uc[0]; + + return (outv.ui); +} + +unsigned short ntohs(unsigned short inv) { return StoM(inv); } +unsigned long ntohl(unsigned long inv) { return ItoM(inv); } +unsigned long htonl(unsigned long inv) { return ItoM(inv); } + +char *optarg; +int optind=1; + +char getopt(int argc, char **argv, char *args) { + int n,nlen=strlen(args),nLen=0; + char nCmd; + + if (argv[optind] && *argv[optind]=='-') { + nCmd=*((argv[optind]+1)); + + for (n=0;n