diff --git a/src/type1z/module.mk b/src/type1z/module.mk new file mode 100644 index 000000000..659cd3f00 --- /dev/null +++ b/src/type1z/module.mk @@ -0,0 +1,6 @@ +make_module_list: add_type1z_driver + +add_type1z_driver: + $(OPEN_DRIVER)t1z_driver_interface$(CLOSE_DRIVER) + $(ECHO_DRIVER)type1z $(ECHO_DRIVER_DESC) Postscript font files with extension *.pfa or *.pfb $(ECHO_DRIVER_DONE) + diff --git a/src/type1z/rules.mk b/src/type1z/rules.mk new file mode 100644 index 000000000..fb69d2105 --- /dev/null +++ b/src/type1z/rules.mk @@ -0,0 +1,168 @@ +#**************************************************************************** +#* * +#* Type1z driver Makefile * +#* * +#* Copyright 1996-1999 by * +#* David Turner, Robert Wilhelm, and Werner Lemberg. * +#* * +#* This file is part of the FreeType project, and may only be used * +#* modified and distributed under the terms of the FreeType project * +#* license, LICENSE.TXT. By continuing to use, modify, or distribute * +#* this file you indicate that you have read the license and * +#* understand and accept it fully. * +#* * +#* The "Type1z" driver is an experimental replacement for the current * +#* Type 1 driver. It features a very different loading mechanism that * +#* is much faster than the one used by the "normal" driver, and also * +#* deals nicely with nearly broken Type 1 font files.. It is also * +#* much smaller.. * +#* * +#* Note that it may become a permanent replacement of the current * +#* "src/type1" driver in the future.. * +#* * +#**************************************************************************** + + +#**************************************************************************** +#* * +#* IMPORTANT NOTE: This Makefile is intended for GNU Make! * +#* If you provide Makefiles for other make utilities, * +#* please place them in `freetype/lib/arch/'. * +#* * +#* * +#* This file is to be included by the FreeType Makefile.lib, located in * +#* the `freetype/lib' directory. Here is the list of the variables that * +#* must be defined to use it: * +#* * +#* * +#* BASE_DIR: The location of the base layer's directory. This is * +#* usually `freetype/lib/base'. * +#* * +#* ARCH_DIR: The location of the architecture-dependent directory. * +#* This is usually `freetype/lib/arch/'. * +#* * +#* DRIVERS_DIR: The location of the font driver sub-dirs, usually * +#* `freetype/lib/drivers'. * +#* * +#* OBJ_DIR: The location where the compiled object(s) file will be * +#* placed. * +#* * +#* BASE_H: A list of pathnames to the base layer's header files on * +#* which the driver depends. * +#* * +#* FT_CFLAGS: A set of flags used for compilation of object files. * +#* This contains at least the include paths of the arch * +#* and base directories + optimization + warnings + ANSI * +#* compliance. * +#* * +#* FT_IFLAG: The flag used to specify an include path on the * +#* compiler command line. For example, with GCC, this is * +#* `-I', while some other compilers use `/i=' or `-J', * +#* etc. * +#* * +#* FT_OBJ: The suffix of an object file for the platform; can be * +#* `o', `obj', `coff', `tco', etc. depending on the * +#* platform. * +#* * +#* * +#* It also updates the following variables defined and used in the main * +#* Makefile: * +#* * +#* DRV_OBJ_S: The list of driver object files in * +#* single-object mode. * +#* * +#* DRV_OBJ_M: The list of driver object files in * +#* multiple-objects mode. * +#* * +#* FTINIT_DRIVER_PATHS: The list of include paths used to compile the * +#* `ftinit' component which registers all font * +#* drivers in the FT_Init_FreeType() function. * +#* * +#* FTINIT_DRIVER_H: The list of header dependencies used to * +#* compile the `ftinit' component. * +#* * +#* FTINIT_DRIVER_MACROS: The list of macros to be defined when * +#* compiling the `ftinit' component. * +#* * +#* `Single-object compilation' means that each font driver is compiled * +#* into a single object file. This is useful to get rid of all * +#* driver-specific entries. * +#* * +#**************************************************************************** + + +# Type1z driver directory +# +T1Z_DIR := $(SRC_)type1z +T1Z_DIR_ := $(T1Z_DIR)$(SEP) + + +# additional include flags used when compiling the driver +# +T1Z_INCLUDE := $(SHARED) $(T1Z_DIR) +T1Z_COMPILE := $(FT_COMPILE) $(T1Z_INCLUDE:%=$I%) + + +# Type1 driver sources (i.e., C files) +# +T1Z_DRV_SRC := $(T1Z_DIR_)t1parse.c \ + $(T1Z_DIR_)t1load.c +# $(T1Z_DIR_)t1parse.c \ +# $(T1Z_DIR_)t1tokens.c \ +# $(T1Z_DIR_)t1driver.c \ +# $(T1Z_DIR_)t1encode.c \ +# $(T1Z_DIR_)t1hinter.c \ +# $(T1Z_DIR_)t1gload.c + + +# Type1 driver headers +# +T1Z_DRV_H := $(T1Z_DIR_)t1errors.h \ + $(T1Z_DIR_)t1config.h \ + $(T1SHARED_H) \ + $(T1Z_DRV_SRC:%.c=%.h) + + +# driver object(s) +# +# T1Z_DRV_OBJ_M is used during `debug' builds +# T1Z_DRV_OBJ_S is used during `release' builds +# +T1Z_DRV_OBJ_M := $(T1Z_DRV_SRC:$(T1Z_DIR_)%.c=$(OBJ_)%.$O) \ + $(T1SHARED:$(T1SHARED_DIR_)%.c=$(OBJ_)%.$O) +T1Z_DRV_OBJ_S := $(OBJ_)type1z.$O + + + +# driver root source file(s) +# +T1Z_DRV_SRC_M := $(T1Z_DRV_SRC) $(T1SHARED_SRC) +T1Z_DRV_SRC_S := $(T1Z_DIR_)type1z.c + + +# driver - single object +# +# the driver is recompiled if any of the header or source files is changed +# +$(T1Z_DRV_OBJ_S): $(BASE_H) $(T1Z_DRV_H) $(T1Z_DRV_SRC) $(T1Z_DRV_SRC_S) + $(T1Z_COMPILE) $T$@ $(T1Z_DRV_SRC_S) + + + +# driver - multiple objects +# +# All objects are recompiled if any of the header files is changed +# +$(OBJ_)t1%.$O: $(T1Z_DIR_)t1%.c $(BASE_H) $(T1Z_DRV_H) + $(T1Z_COMPILE) $T$@ $< + +$(OBJ_)t1%.$O: $(T1SHARED_DIR_)t1%.c $(BASE_H) $(T1SHARED_H) + $(T1Z_COMPILE) $T$@ $< + + +# update main driver object lists +# +DRV_OBJS_S += $(T1Z_DRV_OBJ_S) +DRV_OBJS_M += $(T1Z_DRV_OBJ_M) + +# END diff --git a/src/type1z/t1config.h b/src/type1z/t1config.h new file mode 100644 index 000000000..a72b177b2 --- /dev/null +++ b/src/type1z/t1config.h @@ -0,0 +1,45 @@ +/******************************************************************* + * + * t1config.h + * + * Type 1 driver configuration file + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#ifndef T1CONFIG_H +#define T1CONFIG_H + +/* T1_MAX_STACK_DEPTH is the maximal depth of the token stack used */ +/* by the Type 1 parser (see t1load.c). A minimum of 16 is required */ +/* */ +#define T1_MAX_STACK_DEPTH 16 + +/* T1_MAX_DICT_DEPTH is the maximal depth of nest dictionaries and */ +/* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ +/* required */ +#define T1_MAX_DICT_DEPTH 5 + +/* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ +/* calls during glyph loading */ +#define T1_MAX_SUBRS_CALLS 8 + + +/* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity */ +#define T1_MAX_CHARSTRINGS_OPERANDS 32 + + +/* Define T1_CONFIG_OPTION_DISABLE_HINTER if you want to generate */ +/* a driver with no hinter. This can be useful to debug the parser */ +/* */ +#undef T1_CONFIG_OPTION_DISABLE_HINTER + +#endif /* T1CONFIG_H */ diff --git a/src/type1z/t1driver.c b/src/type1z/t1driver.c new file mode 100644 index 000000000..039ecd118 --- /dev/null +++ b/src/type1z/t1driver.c @@ -0,0 +1,271 @@ +/******************************************************************* + * + * t1driver.c + * + * High-level Type1 driver interface for FreeType 2.0 + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#include +#include + +#include +#include + +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1driver + + /*************************************************************************/ + /* */ + /* */ + /* Get_Interface */ + /* */ + /* */ + /* Each driver can provide one or more extensions to the base */ + /* FreeType API. These can be used to access format specific */ + /* features (e.g., all TrueType/OpenType resources share a common */ + /* file structure and common tables which can be accessed through the */ + /* `sfnt' interface), or more simply generic ones (e.g., the */ + /* `postscript names' interface which can be used to retrieve the */ + /* PostScript name of a given glyph index). */ + /* */ + /* */ + /* driver :: A handle to a driver object. */ + /* */ + /* */ + /* interface :: A string designing the interface. Examples are */ + /* `sfnt', `post_names', `charmaps', etc. */ + /* */ + /* */ + /* A typeless pointer to the extension's interface (normally a table */ + /* of function pointers). Returns NULL if the requested extension */ + /* isn't available (i.e., wasn't compiled in the driver at build */ + /* time). */ + /* */ + static + void* Get_Interface( FT_Driver* driver, + const FT_String* interface ) + { + UNUSED(driver); + UNUSED(interface); + return 0; + } + + + /******************************************************************/ + /* */ + /* Set_Char_Sizes */ + /* */ + /* */ + /* A driver method used to reset a size's character sizes */ + /* (horizontal and vertical) expressed in fractional points. */ + /* */ + /* */ + /* size :: handle to target size object */ + /* char_width :: character width expressed in 26.6 points */ + /* char_height :: character height expressed in 26.6 points */ + /* */ + /* */ + /* FreeType error code. 0 means success */ + /* */ + static + T1_Error Set_Char_Sizes( T1_Size size, + T1_F26Dot6 char_width, + T1_F26Dot6 char_height, + T1_UInt horz_resolution, + T1_UInt vert_resolution ) + { + UNUSED(char_width); + UNUSED(horz_resolution); + UNUSED(vert_resolution); + + size->valid = FALSE; + return T1_Reset_Size( size ); + } + + + /******************************************************************/ + /* */ + /* Set_Pixel_Sizes */ + /* */ + /* */ + /* A driver method used to reset a size's character sizes */ + /* (horizontal and vertical) expressed in integer pixels. */ + /* */ + /* */ + /* size :: handle to target size object */ + /* */ + /* pixel_width :: character width expressed in 26.6 points */ + /* */ + /* pixel_height :: character height expressed in 26.6 points */ + /* */ + /* char_size :: the corresponding character size in points */ + /* This value is only sent to the TrueType */ + /* bytecode interpreter, even though 99% of */ + /* glyph programs will simply ignore it. A */ + /* safe value there is the maximum of the */ + /* pixel width and height (multiplied by */ + /* 64 to make it a 26.6 fixed float !) */ + /* */ + /* FreeType error code. 0 means success */ + /* */ + static + T1_Error Set_Pixel_Sizes( T1_Size size, + T1_Int pixel_width, + T1_Int pixel_height ) + { + UNUSED(pixel_width); + UNUSED(pixel_height); + + size->valid = FALSE; + return T1_Reset_Size(size); + } + + + /******************************************************************/ + /* */ + /* FT_DriverInterface */ + /* */ + /* */ + /* A structure used to hold a font driver's basic interface */ + /* used by the high-level parts of FreeType (or other apps) */ + /* */ + /* Most scalable drivers provide a specialized interface to */ + /* access format-specific features. It can be retrieved with */ + /* a call to the "get_format_interface", and should be defined */ + /* in each font driver header (e.g. ttdriver.h, t1driver.h,..) */ + /* */ + /* All fields are function pointers .. */ + /* */ + /* */ + /* */ + /* */ + /* new_engine :: */ + /* used to create and initialise a new driver object */ + /* */ + /* done_engine :: */ + /* used to finalise and destroy a given driver object */ + /* */ + /* get_format_interface :: */ + /* return a typeless pointer to the format-specific */ + /* driver interface. */ + /* */ + /* new_face :: */ + /* create a new face object from a resource */ + /* */ + /* done_face :: */ + /* discards a face object, as well as all child objects */ + /* ( sizes, charmaps, glyph slots ) */ + /* */ + /* get_face_properties :: */ + /* return generic face properties */ + /* */ + /* get_kerning :: */ + /* return the kerning vector corresponding to a pair */ + /* of glyphs, expressed in unscaled font units. */ + /* */ + /* new_size :: */ + /* create and initialise a new scalable size object. */ + /* */ + /* new_fixed_size :: */ + /* create and initialise a new fixed-size object. */ + /* */ + /* done_size :: */ + /* finalize a given face size object. */ + /* */ + /* set_size_resolutions :: */ + /* reset a scalable size object's output resolutions */ + /* */ + /* set_size_char_sizes :: */ + /* reset a scalable size object's character size */ + /* */ + /* set_pixel_sizes :: */ + /* reset a face size object's pixel dimensions. Applies */ + /* to both scalable and fixed faces. */ + /* */ + /* new_glyph_slot :: */ + /* create and initialise a new glyph slot */ + /* */ + /* done_glyph_slot :: */ + /* discard a given glyph slot */ + /* */ + /* load_glyph :: */ + /* load a given glyph into a given slot */ + /* */ + /* get_glyph_metrics :: */ + /* return a loaded glyph's metrics. */ + /* */ + + EXPORT_FUNC + const FT_DriverInterface t1_driver_interface = + { + sizeof( FT_DriverRec ), + sizeof( T1_FaceRec ), + sizeof( T1_SizeRec ), + sizeof( T1_GlyphSlotRec ), + + "type1", + 100, /* driver version == 1.0 */ + 200, /* requires FreeType 2.0 or above */ + + 0, /* format interface */ + + (FTDriver_initDriver) T1_Init_Driver, + (FTDriver_doneDriver) T1_Done_Driver, + (FTDriver_getInterface) Get_Interface, + + (FTDriver_initFace) T1_Init_Face, + (FTDriver_doneFace) T1_Done_Face, + (FTDriver_getKerning) 0, + + (FTDriver_initSize) T1_Init_Size, + (FTDriver_doneSize) T1_Done_Size, + (FTDriver_setCharSizes) Set_Char_Sizes, + (FTDriver_setPixelSizes) Set_Pixel_Sizes, + + (FTDriver_initGlyphSlot) T1_Init_GlyphSlot, + (FTDriver_doneGlyphSlot) T1_Done_GlyphSlot, + (FTDriver_loadGlyph) T1_Load_Glyph, + + (FTDriver_getCharIndex) 0, + }; + + /*************************************************************************/ + /* */ + /* */ + /* getDriverInterface */ + /* */ + /* */ + /* This function is used when compiling the font driver as a */ + /* shared library (`.DLL' or `.so'). It will be used by the */ + /* high-level library of FreeType to retrieve the address of the */ + /* driver's generic interface. */ + /* */ + /* It shouldn't be implemented in a static build, as each driver must */ + /* have the same function as an exported entry point. */ + /* */ + /* */ + /* The address of the TrueType's driver generic interface. The */ + /* format-specific interface can then be retrieved through the method */ + /* interface->get_format_interface. */ + /* */ +#ifdef FT_CONFIG_OPTION_DYNAMIC_DRIVERS + + EXPORT_FUNC + FT_DriverInterface* getDriverInterface( void ) + { + return &t1_driver_interface; + } + +#endif /* CONFIG_OPTION_DYNAMIC_DRIVERS */ + + diff --git a/src/type1z/t1driver.h b/src/type1z/t1driver.h new file mode 100644 index 000000000..a442035c3 --- /dev/null +++ b/src/type1z/t1driver.h @@ -0,0 +1,28 @@ +/******************************************************************* + * + * t1driver.h + * + * High-level Type1 driver interface for FreeType 2.0 + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#ifndef T1DRIVER_H +#define T1DRIVER_H + +#include +#include + + EXPORT_DEF + const FT_DriverInterface t1_driver_interface; + +#endif /* T1DRIVER_H */ + diff --git a/src/type1z/t1encode.c b/src/type1z/t1encode.c new file mode 100644 index 000000000..9c91027a7 --- /dev/null +++ b/src/type1z/t1encode.c @@ -0,0 +1,332 @@ +/***************************************************************************/ +/* */ +/* t1encode.c */ +/* */ +/* Type 1 standard encoding tables definitions (body). */ +/* */ +/* Copyright 1996-1999 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/* */ +/* This file is included by both the Type1 and Type2 driver. */ +/* It should never be compiled directly. */ +/* */ +/***************************************************************************/ + +#include +#include + + /*************************************************************************/ + /* */ + /* t1_standard_strings: */ + /* */ + /* This array contains the Adobe Standard Glyph Names ordered by */ + /* SID. It was taken from the CFF specification. */ + /* */ + LOCAL_FUNC + const T1_String* t1_standard_strings[] = + { + /* 0 */ + ".notdef", "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", "parenleft", + /* 10 */ + "parenright", "asterisk", "plus", "comma", "hyphen", + "period", "slash", "zero", "one", "two", + /* 20 */ + "three", "four", "five", "six", "seven", + "height", "nine", "colon", "semicolon", "less", + /* 30 */ + "equal", "greater", "question", "at", "A", + "B", "C", "D", "E", "F", + /* 40 */ + "G", "H", "I", "J", "K", + "L", "M", "N", "O", "P", + /* 50 */ + "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", + /* 60 */ + "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", + "quoteleft", "a", "b", "c", "d", + /* 70 */ + "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", + /* 80 */ + "o", "p", "q", "r", "s", + "t", "u", "v", "w", "x", + /* 90 */ + "y", "z", "braceleft", "bar", "braceright", + "asciitilde", "exclamdown", "cent", "sterling", "fraction", + /* 100 */ + "yen", "florin", "section", "currency", "quotesingle", + "quotedblleft", "quillemotleft", "guilsinglleft", "guilsinglright", "fi", + /* 110 */ + "fl", "endash", "dagger", "daggerdbl", "periodcenter", + "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", + /* 120 */ + "quillemotright", "ellipsis", "perthousand", "questiondown", "grave", + "acute", "circumflex", "tilde", "macron", "breve", + /* 130 */ + "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", + "ogonek", "caron", "emdash", "AE", "ordfeminine", + /* 140 */ + "Lslash", "Oslash", "OE", "ordmasculine", "ae", + "dotlessi", "Islash", "oslash", "oe", "germandbls", + /* 150 */ + "onesuperior", "logicalnot", "mu", "trademark", "Eth", + "onehalf", "plusminus", "Thorn", "onequarter", "divide", + /* 160 */ + "brokenbar", "degree", "thorn", "threequarters", "twosuperior", + "regitered", "minus", "eth", "multiply", "threesuperior", + /* 170 */ + "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", + "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", + /* 180 */ + "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", + "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", + /* 190 */ + "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", + "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", + /* 200 */ + "aacute", "acircumflex", "adieresis", "agrave", "aring", + "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", + /* 210 */ + "egrave", "iacute", "icircumflex", "idieresis", "igrave", + "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", + /* 220 */ + "otilde", "scaron", "uacute", "ucircumflex", "udieresis", + "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", + /* 230 */ + "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", + "Acutesmall", + "parenleftsuperior", "parenrightsuperior", "twodotenleader", + "onedotenleader", "zerooldstyle", + /* 240 */ + "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", + "fiveoldstyle", + "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", + "commasuperior", + /* 250 */ + "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", + "bsuperior", + "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", + /* 260 */ + "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", + "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", + /* 270 */ + "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", + "Asmall", + "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", + /* 280 */ + "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", + "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", + /* 290 */ + "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", + "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", + /* 300 */ + "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", + "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", + "Dieresissmall", + /* 310 */ + "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", + "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", + "questiondownsmall", + /* 320 */ + "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", + "twothirds", "zerosuperior", "foursuperior", "fivesuperior", + "sixsuperior", + /* 330 */ + "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", + "oneinferior", + "twoinferior", "threeinferior", "fourinferior", "fiveinferior", + "sixinferior", + /* 340 */ + "seveninferior", "eightinferior", "nineinferior", "centinferior", + "dollarinferior", + "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", + "Acircumflexsmall", + /* 350 */ + "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", + "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", + "Igravesmall", + /* 360 */ + "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", + "Ntildesmall", + "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", + "Odieresissmall", + /* 370 */ + "OEsmall", "Oslashsmall", "Ugravesmall", "Uacautesmall", + "Ucircumflexsmall", + "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", + "001.000", + /* 380 */ + "001.001", "001.002", "001.003", "Black", "Bold", + "Book", "Light", "Medium", "Regular", "Roman", + /* 390 */ + "Semibold" + }; + + + /*************************************************************************/ + /* */ + /* t1_standard_encoding: */ + /* */ + /* A simple table used to encode the Adobe StandardEncoding. The */ + /* table values are the SID of the standard glyphs; the table index */ + /* is the character code for the encoding. */ + /* */ + /* Example: */ + /* */ + /* t1_standard_encoding[33] == 2 */ + /* */ + /* which means that the glyph name for character code 32 is */ + /* */ + /* t1_standard_strings[2] == "exclam" */ + /* */ + /* (this correspond to the exclamation mark `!'). */ + /* */ + LOCAL_FUNC + T1_Short t1_standard_encoding[256] = + { + /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + /* 50 */ + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + /* 100 */ + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 150 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 0, 111, 112, 113, + 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, + 0, 123, 0, 124, 125, 126, 127, 128, 129, 130, + /* 200 */ + 131, 0, 132, 133, 0, 134, 135, 136, 137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 138, 0, 139, 0, 0, + 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, + /* 250 */ + 148, 149, 0, 0, 0, 0 + }; + + + /*************************************************************************/ + /* */ + /* t1_expert_encoding: */ + /* */ + /* A simple table used to encode the Adobe ExpertEncoding. The */ + /* table values are the SID of the standard glyphs; the table index */ + /* is the character code for the encoding. */ + /* */ + /* Example: */ + /* */ + /* t1_expert_encoding[33] == 229 */ + /* */ + /* which means that the glyph name for character code 32 is */ + /* */ + /* t1_standard_strings[229] == "exclamsmall" */ + /* */ + LOCAL_FUNC + T1_Short t1_expert_encoding[256] = + { + /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 229, 230, 0, 231, 232, 233, 234, + 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, + /* 50 */ + 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, + 249, 250, 251, 252, 0, 253, 254, 255, 256, 257, + 0, 0, 0, 258, 0, 0, 259, 260, 261, 262, + 0, 0, 263, 264, 265, 0, 266, 109, 110, 267, + 268, 269, 0, 270, 271, 272, 273, 274, 275, 276, + /* 100 */ + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 150 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 304, 305, 306, 0, 0, 307, 308, 309, 310, + 311, 0, 312, 0, 0, 312, 0, 0, 314, 315, + 0, 0, 316, 317, 318, 0, 0, 0, 158, 155, + 163, 319, 320, 321, 322, 323, 324, 325, 0, 0, + /* 200 */ + 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + /* 250 */ + 373, 374, 375, 376, 377, 378 + }; + + + /*************************************************************************/ + /* */ + /* t1_expert_subset_encoding: */ + /* */ + /* A simple table used to encode the Adobe ExpertEncoding subset */ + /* defined in the CFF specification. It will probably evolve into */ + /* another form sooner or later, as we deal with charsets */ + /* differently than with encodings. */ + /* */ + LOCAL_FUNC + FT_Short t1_expert_subset_encoding[256] = + { + /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 231, 232, 0, 0, + 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, + /* 50 */ + 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, + 249, 250, 251, 252, 0, 253, 254, 255, 256, 257, + 0, 0, 0, 258, 0, 0, 259, 260, 261, 262, + 0, 0, 263, 264, 265, 0, 266, 109, 110, 267, + 268, 269, 0, 270, 0, 272, 0, 0, 0, 0, + /* 100 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 300, 301, 302, 303, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 150 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 304, 305, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 314, 315, + 0, 0, 0, 0, 0, 0, 0, 0, 158, 155, + 163, 0, 320, 321, 322, 323, 324, 325, 0, 0, + /* 200 */ + 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 345, 346, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 250 */ + 0, 0, 0, 0, 0, 0 + }; + + +/* END */ diff --git a/src/type1z/t1encode.h b/src/type1z/t1encode.h new file mode 100644 index 000000000..0f5c0d7c0 --- /dev/null +++ b/src/type1z/t1encode.h @@ -0,0 +1,98 @@ +/***************************************************************************/ +/* */ +/* t1encode.h */ +/* */ +/* Type 1 standard encoding tables definitions (specification). */ +/* */ +/* Copyright 1996-1999 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/* */ +/* This file is included by both the Type1 and Type2 driver. */ +/* It should never be compiled directly. */ +/* */ +/***************************************************************************/ + + +#ifndef T1ENCODE_H +#define T1ENCODE_H + +#include + + + /*************************************************************************/ + /* */ + /* t1_standard_strings: */ + /* */ + /* This array contains the Adobe Standard Glyph Names ordered by */ + /* SID. It was taken from the CFF specification. */ + /* */ + LOCAL_DEF + const T1_String* t1_standard_strings[]; + + + /*************************************************************************/ + /* */ + /* t1_standard_encoding: */ + /* */ + /* A simple table used to encode the Adobe StandardEncoding. The */ + /* table values are the SID of the standard glyphs; the table index */ + /* is the character code for the encoding. */ + /* */ + /* Example: */ + /* */ + /* t1_standard_encoding[33] == 2 */ + /* */ + /* which means that the glyph name for character code 33 is */ + /* */ + /* t1_standard_strings[2] == "exclam" */ + /* */ + /* (this correspond to the exclamation mark `!'). */ + /* */ + LOCAL_DEF + T1_Short t1_standard_encoding[256]; + + + /*************************************************************************/ + /* */ + /* t1_expert_encoding: */ + /* */ + /* A simple table used to encode the Adobe ExpertEncoding. The */ + /* table values are the SID of the standard glyphs; the table index */ + /* is the character code for the encoding. */ + /* */ + /* Example: */ + /* */ + /* t1_expert_encoding[33] == 229 */ + /* */ + /* which means that the glyph name for character code 33 is */ + /* */ + /* t1_standard_strings[229] == "exclamsmall" */ + /* */ + LOCAL_DEF + T1_Short t1_expert_encoding[256]; + + + /*************************************************************************/ + /* */ + /* t1_expert_subset_encoding: */ + /* */ + /* A simple table used to encode the Adobe ExpertEncoding subset */ + /* defined in the CFF specification. It will probably evolve into */ + /* another form sooner or later, as we deal with charsets */ + /* differently than with encodings. */ + /* */ + LOCAL_DEF + T1_Short t1_expert_subset_encoding[256]; + + +#endif /* T1ENCODE_H */ + + +/* END */ diff --git a/src/type1z/t1errors.h b/src/type1z/t1errors.h new file mode 100644 index 000000000..f268d8770 --- /dev/null +++ b/src/type1z/t1errors.h @@ -0,0 +1,75 @@ +/******************************************************************* + * + * t1errors.h + * + * Type1 Error ID definitions + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#ifndef T1ERRORS_H +#define T1ERRORS_H + +#include + + /************************ error codes declaration **************/ + + /* The error codes are grouped in 'classes' used to indicate the */ + /* 'level' at which the error happened. */ + /* The class is given by an error code's high byte. */ + + +/* ------------- Success is always 0 -------- */ + +#define T1_Err_Ok FT_Err_Ok + +/* ----------- high level API errors -------- */ + +#define T1_Err_Invalid_File_Format FT_Err_Invalid_File_Format +#define T1_Err_Invalid_Argument FT_Err_Invalid_Argument +#define T1_Err_Invalid_Driver_Handle FT_Err_Invalid_Driver_Handle +#define T1_Err_Invalid_Face_Handle FT_Err_Invalid_Face_Handle +#define T1_Err_Invalid_Size_Handle FT_Err_Invalid_Size_Handle +#define T1_Err_Invalid_Glyph_Handle FT_Err_Invalid_Slot_Handle +#define T1_Err_Invalid_CharMap_Handle FT_Err_Invalid_CharMap_Handle +#define T1_Err_Invalid_Glyph_Index FT_Err_Invalid_Glyph_Index + +#define T1_Err_Unimplemented_Feature FT_Err_Unimplemented_Feature +#define T1_Err_Unavailable_Outline FT_Err_Unavailable_Outline +#define T1_Err_Unavailable_Bitmap FT_Err_Unavailable_Bitmap +#define T1_Err_Unavailable_Pixmap FT_Err_Unavailable_Pixmap +#define T1_Err_File_Is_Not_Collection FT_Err_File_Is_Not_Collection + +#define T1_Err_Invalid_Engine FT_Err_Invalid_Driver_Handle + +/* ------------- internal errors ------------ */ + +#define T1_Err_Out_Of_Memory FT_Err_Out_Of_Memory +#define T1_Err_Unlisted_Object FT_Err_Unlisted_Object + +/* ------------ general glyph outline errors ------ */ + +#define T1_Err_Too_Many_Points FT_Err_Too_Many_Points +#define T1_Err_Too_Many_Contours FT_Err_Too_Many_Contours +#define T1_Err_Too_Many_Hints FT_Err_Too_Many_Hints +#define T1_Err_Invalid_Composite FT_Err_Invalid_Composite +#define T1_Err_Too_Many_Edges FT_Err_Too_Many_Edges +#define T1_Err_Too_Many_Strokes FT_Err_Too_Many_Strokes + + +#define T1_Err_Syntax_Error FT_Err_Invalid_File_Format +#define T1_Err_Stack_Underflow FT_Err_Invalid_File_Format +#define T1_Err_Stack_Overflow FT_Err_Invalid_File_Format + +#endif /* TDERRORS_H */ + + +/* END */ diff --git a/src/type1z/t1gload.c b/src/type1z/t1gload.c new file mode 100644 index 000000000..05cd0a26b --- /dev/null +++ b/src/type1z/t1gload.c @@ -0,0 +1,1582 @@ +/******************************************************************* + * + * t1gload.c 1.0 + * + * Type1 Glyph Loader. + * + * Copyright 1996-1999 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#include +#include +#include +#include + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER +#include +#endif + + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + /********** *********/ + /********** *********/ + /********** GENERIC CHARSTRINGS PARSING *********/ + /********** *********/ + /********** *********/ + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + +/********************************************************************* + * + * + * T1_Init_Builder + * + * + * Initialise a given glyph builder. + * + * + * builder :: glyph builder to initialise + * face :: current face object + * size :: current size object + * glyph :: current glyph object + * funcs :: glyph builder functions (or "methods"). + * + *********************************************************************/ + + EXPORT_FUNC + void T1_Init_Builder( T1_Builder* builder, + T1_Face face, + T1_Size size, + T1_GlyphSlot glyph, + const T1_Builder_Funcs* funcs ) + { + builder->funcs = *funcs; + builder->path_begun = 0; + builder->load_points = 1; + + builder->face = face; + builder->size = size; + builder->glyph = glyph; + builder->memory = face->root.memory; + + if (glyph) + { + builder->base = glyph->root.outline; + builder->max_points = glyph->max_points; + builder->max_contours = glyph->max_contours; + } + + if (size) + { + builder->scale_x = size->root.metrics.x_scale; + builder->scale_y = size->root.metrics.y_scale; + } + + builder->pos_x = 0; + builder->pos_y = 0; + + builder->left_bearing.x = 0; + builder->left_bearing.y = 0; + builder->advance.x = 0; + builder->advance.y = 0; + + builder->base.n_points = 0; + builder->base.n_contours = 0; + builder->current = builder->base; + + builder->pass = 0; + builder->hint_point = 0; + } + + +/********************************************************************* + * + * + * T1_Done_Builder + * + * + * Finalise a given glyph builder. Its content can still be + * used after the call, but the function saves important information + * within the corresponding glyph slot. + * + * + * builder :: glyph builder to initialise + * + *********************************************************************/ + + EXPORT_FUNC + void T1_Done_Builder( T1_Builder* builder ) + { + T1_GlyphSlot glyph = builder->glyph; + + if (glyph) + { + glyph->root.outline = builder->base; + glyph->max_points = builder->max_points; + glyph->max_contours = builder->max_contours; + } + } + + + +/********************************************************************* + * + * + * T1_Init_Decoder + * + * + * Initialise a given Type 1 decoder for parsing + * + * + * decoder :: Type 1 decoder to initialise + * funcs :: hinter functions interface + * + *********************************************************************/ + + EXPORT_FUNC + void T1_Init_Decoder( T1_Decoder* decoder, + const T1_Hinter_Funcs* funcs ) + { + decoder->hinter = *funcs; /* copy hinter interface */ + decoder->top = 0; + decoder->zone = 0; + + decoder->flex_state = 0; + decoder->num_flex_vectors = 0; + + /* Clear loader */ + MEM_Set( &decoder->builder, 0, sizeof(decoder->builder) ); + } + + +/********************************************************************* + * + * + * lookup_glyph_by_stdcharcode + * + * + * Lookup a given glyph by its StandardEncoding charcode. Used + * to implement the SEAC Type 1 operator. + * + * + * face :: current face object + * charcode :: charcode to look for + * + * + * glyph index in font face. Returns -1 if the corresponding + * glyph wasn't found. + * + *********************************************************************/ + + static + T1_Int lookup_glyph_by_stdcharcode( T1_Face face, + T1_Int charcode ) + { + T1_Int n; + const T1_String* glyph_name; + + /* check range of standard char code */ + if (charcode < 0 || charcode > 255) + return -1; + + glyph_name = t1_standard_strings[t1_standard_encoding[charcode]]; + + for ( n = 0; n < face->type1.num_glyphs; n++ ) + { + T1_String* name = (T1_String*)face->type1.glyph_names[n]; + + if ( name && strcmp(name,glyph_name) == 0 ) + return n; + } + + return -1; + } + + +/********************************************************************* + * + * + * t1operator_seac + * + * + * Implements the "seac" Type 1 operator for a Type 1 decoder + * + * + * decoder :: current Type 1 decoder + * asb :: accent's side bearing + * adx :: horizontal position of accent + * ady :: vertical position of accent + * bchar :: base character's StandardEncoding charcode + * achar :: accent character's StandardEncoding charcode + * + * + * Error code. 0 means success. + * + *********************************************************************/ + + static + T1_Error t1operator_seac( T1_Decoder* decoder, + T1_Pos asb, + T1_Pos adx, + T1_Pos ady, + T1_Int bchar, + T1_Int achar ) + { + T1_Error error; + T1_Face face = decoder->builder.face; + T1_Int bchar_index, achar_index, n_base_points; + FT_Outline* cur = &decoder->builder.current; + FT_Outline* base = &decoder->builder.base; + T1_Vector left_bearing, advance; + T1_Font* type1 = &face->type1; + + bchar_index = lookup_glyph_by_stdcharcode( face, bchar ); + achar_index = lookup_glyph_by_stdcharcode( face, achar ); + + if (bchar_index < 0 || achar_index < 0) + { + FT_ERROR(( "T1.Parse_Seac : invalid seac character code arguments\n" )); + return T1_Err_Syntax_Error; + } + + /* First load "bchar" in builder */ + /* now load the unscaled outline */ + cur->n_points = 0; + cur->n_contours = 0; + cur->points = base->points + base->n_points; + cur->flags = base->flags + base->n_points; + cur->contours = base->contours + base->n_contours; + + error = T1_Parse_CharStrings( decoder, + type1->charstrings [bchar_index], + type1->charstrings_len[bchar_index], + type1->num_subrs, + type1->subrs, + type1->subrs_len ); + if (error) return error; + + n_base_points = cur->n_points; + + /* save the left bearing and width of the base character */ + /* as they will be erase by the next load.. */ + left_bearing = decoder->builder.left_bearing; + advance = decoder->builder.advance; + + decoder->builder.left_bearing.x = 0; + decoder->builder.left_bearing.y = 0; + + /* Now load "achar" on top of */ + /* the base outline */ + /* */ + cur->n_points = 0; + cur->n_contours = 0; + cur->points = base->points + base->n_points; + cur->flags = base->flags + base->n_points; + cur->contours = base->contours + base->n_contours; + + error = T1_Parse_CharStrings( decoder, + type1->charstrings [achar_index], + type1->charstrings_len[achar_index], + type1->num_subrs, + type1->subrs, + type1->subrs_len ); + if (error) return error; + + /* adjust contours in accented character outline */ + { + T1_Int n; + + for ( n = 0; n < cur->n_contours; n++ ) + cur->contours[n] += n_base_points; + } + + /* restore the left side bearing and */ + /* advance width of the base character */ + decoder->builder.left_bearing = left_bearing; + decoder->builder.advance = advance; + + /* Finally, move the accent */ + FT_Translate_Outline( cur, adx - asb, ady ); + + (void)asb; /* ignore this parameter */ + return T1_Err_Ok; + } + +/********************************************************************* + * + * + * t1operator_flex + * + * + * Implements the "flex" Type 1 operator for a Type 1 decoder + * + * + * decoder :: current Type 1 decoder + * threshold :: threshold + * end_x :: position of final flex point + * end_y :: position of final flex point + * + * + * Error code. 0 means success. + * + *********************************************************************/ + + static + T1_Error t1operator_flex( T1_Decoder* decoder, + T1_Pos threshold, + T1_Pos end_x, + T1_Pos end_y ) + { + T1_Vector vec; + T1_Vector* flex = decoder->flex_vectors; + T1_Int n; + + /* we don't even try to test the threshold in the non-hinting */ + /* builder, even if the flex operator is said to be a path */ + /* construction statement in the specification. This is better */ + /* left to the hinter.. */ + + flex = decoder->flex_vectors; + vec = *flex++; + + for ( n = 0; n < 6; n++ ) + { + flex->x += vec.x; + flex->y += vec.y; + + vec = *flex++; + } + + + (void)threshold; + (void)end_x; + (void)end_y; + + flex = decoder->flex_vectors; + + return decoder->builder.funcs.rcurve_to( &decoder->builder, + flex[0].x, flex[0].y, + flex[1].x, flex[1].y, + flex[2].x, flex[2].y ) || + + decoder->builder.funcs.rcurve_to( &decoder->builder, + flex[3].x, flex[3].y, + flex[4].x, flex[4].y, + flex[5].x, flex[5].y ); + } + + +/********************************************************************* + * + * + * T1_Parse_CharStrings + * + * + * Parses a given Type 1 charstrings program + * + * + * decoder :: current Type 1 decoder + * charstring_base :: base of the charstring stream + * charstring_len :: length in bytes of the charstring stream + * num_subrs :: number of sub-routines + * subrs_base :: array of sub-routines addresses + * subrs_len :: array of sub-routines lengths + * + * + * Error code. 0 means success. + * + *********************************************************************/ + + EXPORT_FUNC + T1_Error T1_Parse_CharStrings( T1_Decoder* decoder, + T1_Byte* charstring_base, + T1_Int charstring_len, + T1_Int num_subrs, + T1_Byte** subrs_base, + T1_Int* subrs_len ) + { + T1_Error error; + T1_Decoder_Zone* zone; + T1_Byte* ip; + T1_Byte* limit; + T1_Builder* builder = &decoder->builder; + T1_Builder_Funcs* builds = &builder->funcs; + T1_Hinter_Funcs* hints = &decoder->hinter; + + static const T1_Int args_count[ op_max ] = + { + 0, /* none */ + 0, /* endchar */ + 2, /* hsbw */ + 5, /* seac */ + 4, /* sbw */ + 0, /* closepath */ + 1, /* hlineto */ + 1, /* hmoveto */ + 4, /* hvcurveto */ + 2, /* rlineto */ + 2, /* rmoveto */ + 6, /* rrcurveto */ + 4, /* vhcurveto */ + 1, /* vlineto */ + 1, /* vmoveto */ + 0, /* dotsection */ + 2, /* hstem */ + 6, /* hstem3 */ + 2, /* vstem */ + 6, /* vstem3 */ + 2, /* div */ + -1, /* callothersubr */ + 1, /* callsubr */ + 0, /* pop */ + 0, /* return */ + 2 /* setcurrentpoint */ + }; + + /* First of all, initialise the decoder */ + decoder->top = decoder->stack; + decoder->zone = decoder->zones; + zone = decoder->zones; + + builder->path_begun = 0; + + zone->base = charstring_base; + limit = zone->limit = charstring_base + charstring_len; + ip = zone->cursor = zone->base; + + error = T1_Err_Ok; + + /* now, execute loop */ + while ( ip < limit ) + { + T1_Int* top = decoder->top; + T1_Operator op = op_none; + T1_Long value = 0; + + /* First of all, decompress operator or value */ + switch (*ip++) + { + case 1: op = op_hstem; break; + + case 3: op = op_vstem; break; + case 4: op = op_vmoveto; break; + case 5: op = op_rlineto; break; + case 6: op = op_hlineto; break; + case 7: op = op_vlineto; break; + case 8: op = op_rrcurveto; break; + case 9: op = op_closepath; break; + case 10: op = op_callsubr; break; + case 11: op = op_return; break; + + case 13: op = op_hsbw; break; + case 14: op = op_endchar; break; + + case 21: op = op_rmoveto; break; + case 22: op = op_hmoveto; break; + + case 30: op = op_vhcurveto; break; + case 31: op = op_hvcurveto; break; + + case 12: + { + if (ip > limit) + { + FT_ERROR(( "T1.Parse_CharStrings : invalid escape (12+EOF)\n" )); + goto Syntax_Error; + } + + switch (*ip++) + { + case 0: op = op_dotsection; break; + case 1: op = op_vstem3; break; + case 2: op = op_hstem3; break; + case 6: op = op_seac; break; + case 7: op = op_sbw; break; + case 12: op = op_div; break; + case 16: op = op_callothersubr; break; + case 17: op = op_pop; break; + case 33: op = op_setcurrentpoint; break; + + default: + FT_ERROR(( "T1.Parse_CharStrings : invalid escape (12+%d)\n", + ip[-1] )); + goto Syntax_Error; + } + } + break; + + case 255: /* four bytes integer */ + { + if (ip+4 > limit) + { + FT_ERROR(( "T1.Parse_CharStrings : unexpected EOF in integer\n" )); + goto Syntax_Error; + } + + value = ((long)ip[0] << 24) | + ((long)ip[1] << 16) | + ((long)ip[2] << 8) | + ip[3]; + ip += 4; + } + break; + + default: + if (ip[-1] >= 32) + { + if (ip[-1] < 247) + value = (long)ip[-1] - 139; + else + { + if (++ip > limit) + { + FT_ERROR(( "T1.Parse_CharStrings : unexpected EOF in integer\n" )); + goto Syntax_Error; + } + + if (ip[-2] < 251) + value = ((long)(ip[-2]-247) << 8) + ip[-1] + 108; + else + value = -((((long)ip[-2]-251) << 8) + ip[-1] + 108 ); + } + } + else + { + FT_ERROR(( "T1.Parse_CharStrings : invalid byte (%d)\n", + ip[-1] )); + goto Syntax_Error; + } + } + + /* push value if needed */ + if ( op == op_none ) + { + if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS ) + { + FT_ERROR(( "T1.Parse_CharStrings : Stack overflow !!\n" )); + goto Syntax_Error; + } + + *top++ = value; + decoder->top = top; + } + + else if ( op == op_callothersubr ) /* check arguments differently */ + { + if ( top - decoder->stack < 2) + goto Stack_Underflow; + + top -= 2; + + switch (top[1]) + { + case 1: /* start flex feature ----------------------------- */ + { + if (top[0] != 0) goto Unexpected_OtherSubr; + + decoder->flex_state = 1; + decoder->num_flex_vectors = 0; + decoder->flex_vectors[0].x = 0; + decoder->flex_vectors[0].y = 0; + } + break; + + + case 2: /* add flex vector ------------------------------- */ + { + T1_Int index; + T1_Vector* flex; + + if (top[0] != 0) goto Unexpected_OtherSubr; + + top -= 2; + if (top < decoder->stack) goto Stack_Underflow; + + index = decoder->num_flex_vectors++; + if (index >= 7) + { + FT_ERROR(( "T1.Parse_CharStrings: too many flex vectors !\n" )); + goto Syntax_Error; + } + + flex = decoder->flex_vectors + index; + flex->x += top[0]; + flex->y += top[1]; + + } + break; + + + case 0: /* end flex feature ------------------------------ */ + { + if ( decoder->flex_state == 0 || + decoder->num_flex_vectors != 7 ) + { + FT_ERROR(( "T1.Parse_CharStrings: unexpected flex end\n" )); + goto Syntax_Error; + } + + if (top[0] != 3) goto Unexpected_OtherSubr; + + top -= 3; + if (top < decoder->stack) goto Stack_Underflow; + + /* now consume the remaining "pop pop setcurrentpoint" */ + if ( ip+6 > limit || + ip[0] != 12 || ip[1] != 17 || /* pop */ + ip[2] != 12 || ip[3] != 17 || /* pop */ + ip[4] != 12 || ip[5] != 33 ) /* setcurrentpoint */ + { + FT_ERROR(( "T1.Parse_CharStrings: invalid flex charstring\n" )); + goto Syntax_Error; + } + + decoder->flex_state = 0; + decoder->top = top; + + error = t1operator_flex( decoder, top[0], top[1], top[2] ); + } + break; + + + case 3: /* change hints ------------------------------------ */ + { + if (top[0] != 1) goto Unexpected_OtherSubr; + + /* eat the following "pop" */ + if (ip+2 > limit) + { + FT_ERROR(( "T1.Parse_CharStrings: invalid escape (12+%d)\n", + ip[-1] )); + goto Syntax_Error; + } + + if (ip[0] != 12 || ip[1] != 17) + { + FT_ERROR(( "T1.Parse_CharStrings: 'pop' expected, found (%d %d)\n", + ip[0], ip[1] )); + goto Syntax_Error; + } + + ip += 2; + error = hints->change_hints(builder); + } + break; + + + default: + /* invalid OtherSubrs call */ + Unexpected_OtherSubr: + FT_ERROR(( "T1.Parse_CharStrings: unexpected OtherSubrs [%d %d]\n", + top[0], top[1] )); + goto Syntax_Error; + } + decoder->top = top; + } + else + { + T1_Int num_args = args_count[op]; + + if ( top - decoder->stack < num_args ) + goto Stack_Underflow; + + top -= num_args; + + switch (op) + { + case op_endchar: + error = builds->end_char( builder ); + break; + + case op_hsbw: + error = builds->set_bearing_point( builder, top[0], 0, + top[1], 0 ); + break; + + case op_seac: + /* return immediately after the processing */ + return t1operator_seac( decoder, top[0], top[1], + top[2], top[3], top[4] ); + + case op_sbw: + error = builds->set_bearing_point( builder, top[0], top[1], + top[2], top[3] ); + break; + + case op_closepath: + error = builds->close_path( builder ); + break; + + case op_hlineto: + error = builds->rline_to( builder, top[0], 0 ); + break; + + case op_hmoveto: + error = builds->rmove_to( builder, top[0], 0 ); + break; + + case op_hvcurveto: + error = builds->rcurve_to( builder, top[0], 0, + top[1], top[2], + 0, top[3] ); + break; + + case op_rlineto: + error = builds->rline_to( builder, top[0], top[1] ); + break; + + case op_rmoveto: + /* ignore operator when in flex mode */ + if (decoder->flex_state == 0) + error = builds->rmove_to( builder, top[0], top[1] ); + else + top += 2; + break; + + case op_rrcurveto: + { + error = builds->rcurve_to( builder, top[0], top[1], + top[2], top[3], + top[4], top[5] ); + } + break; + + case op_vhcurveto: + error = builds->rcurve_to( builder, 0, top[0], + top[1], top[2], + top[3], 0 ); + break; + + case op_vlineto: + error = builds->rline_to( builder, 0, top[0] ); + break; + + case op_vmoveto: + error = builds->rmove_to( builder, 0, top[0] ); + break; + + case op_dotsection: + error = hints->dot_section( builder ); + break; + + case op_hstem: + error = hints->stem( builder, top[0], top[1], 0 ); + break; + + case op_hstem3: + error = hints->stem3( builder, top[0], top[1], top[2], + top[3], top[4], top[5], 0 ); + break; + + case op_vstem: + error = hints->stem( builder, top[0], top[1], 1 ); + break; + + case op_vstem3: + error = hints->stem3( builder, top[0], top[1], top[2], + top[3], top[4], top[5], 1 ); + break; + + case op_div: + if (top[1]) + *top++ = top[0] / top[1]; + else + { + FT_ERROR(( "T1.Parse_CHarStrings : division by 0\n" )); + goto Syntax_Error; + } + break; + + case op_callsubr: + { + T1_Int index = top[0]; + + if ( index < 0 || index >= num_subrs ) + { + FT_ERROR(( "T1.Parse_CharStrings : invalid subrs index\n" )); + goto Syntax_Error; + } + + if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS ) + { + FT_ERROR(( "T1.Parse_CharStrings : too many nested subrs\n" )); + goto Syntax_Error; + } + + zone->cursor = ip; /* save current instruction pointer */ + + zone++; + zone->base = subrs_base[index]; + zone->limit = zone->base + subrs_len[index]; + zone->cursor = zone->base; + + if (!zone->base) + { + FT_ERROR(( "T1.Parse_CharStrings : invoking empty subrs !!\n" )); + goto Syntax_Error; + } + + decoder->zone = zone; + ip = zone->base; + limit = zone->limit; + } + break; + + case op_pop: + FT_ERROR(( "T1.Parse_CharStrings : unexpected POP\n" )); + goto Syntax_Error; + + + case op_return: + if ( zone <= decoder->zones ) + { + FT_ERROR(( "T1.Parse_CharStrings : unexpected return\n" )); + goto Syntax_Error; + } + + zone--; + ip = zone->cursor; + limit = zone->limit; + decoder->zone = zone; + break; + + case op_setcurrentpoint: + FT_ERROR(( "T1.Parse_CharStrings : unexpected SETCURRENTPOINT\n" )); + goto Syntax_Error; + break; + + default: + FT_ERROR(( "T1.Parse_CharStrings : unhandled opcode %d\n", op )); + goto Syntax_Error; + } + + decoder->top = top; + } + } + + return error; + + Syntax_Error: + return T1_Err_Syntax_Error; + + Stack_Underflow: + return T1_Err_Stack_Underflow; + } + + + +/*************************************************************************/ +/* */ +/* T1_Add_Points */ +/* */ +/* */ +/* Checks that there is enough room in the current load glyph outline */ +/* to accept "num_points" additional outline points. If not, this */ +/* function grows the load outline's arrays accordingly.. */ +/* */ +/* */ +/* builder :: pointer to glyph builder object */ +/* num_points :: number of points that will be added later */ +/* */ +/* */ +/* Type1 error code. 0 means success */ +/* */ +/* */ +/* This function does NOT update the points count in the glyph builder*/ +/* This must be done by the caller itself, after this function is */ +/* invoked.. */ +/* */ + LOCAL_FUNC + T1_Error T1_Add_Points( T1_Builder* builder, + T1_Int num_points ) + { + T1_Int new_points; + + new_points = builder->base.n_points + + builder->current.n_points + + num_points; + + if ( new_points > builder->max_points ) + { + FT_Memory memory = builder->memory; + T1_Error error; + T1_Int increment = builder->current.points - builder->base.points; + T1_Int current = builder->max_points; + + while ( builder->max_points < new_points ) + builder->max_points += 16; + + if ( REALLOC_ARRAY( builder->base.points, + current, builder->max_points, T1_Vector ) || + + REALLOC_ARRAY( builder->base.flags, + current, builder->max_points, T1_Byte ) ) + return error; + + builder->current.points = builder->base.points + increment; + builder->current.flags = builder->base.flags + increment; + } + + return T1_Err_Ok; + } + +/*************************************************************************/ +/* */ +/* T1_Add_Contours */ +/* */ +/* */ +/* Checks that there is enough room in the current load glyph outline */ +/* to accept "num_contours" additional contours. If not, this func */ +/* the load outline's arrays accordingly.. */ +/* */ +/* */ +/* builder :: pointer to glyph builder object */ +/* num_contours :: number of contours that will be added later */ +/* */ +/* */ +/* Type1 error code. 0 means success */ +/* */ +/* */ +/* This function does NOT update the contours count in the load glyph */ +/* This must be done by the caller itself, after this function is */ +/* invoked.. */ +/* */ + LOCAL_FUNC + T1_Error T1_Add_Contours( T1_Builder* builder, + T1_Int num_contours ) + { + T1_Int new_contours; + + new_contours = builder->base.n_contours + + builder->current.n_contours + + num_contours; + + if ( new_contours > builder->max_contours && builder->load_points ) + { + T1_Error error; + FT_Memory memory = builder->memory; + T1_Int increment = builder->current.contours - builder->base.contours; + T1_Int current = builder->max_contours; + + while ( builder->max_contours < new_contours ) + builder->max_contours += 4; + + if ( REALLOC_ARRAY( builder->base.contours, + current, builder->max_contours, T1_Short ) ) + return error; + + builder->current.contours = builder->base.contours + increment; + } + + return T1_Err_Ok; + } + + + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + /********** *********/ + /********** *********/ + /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/ + /********** *********/ + /********** The following code is in charge of computing *********/ + /********** the maximum advance width of the font. It *********/ + /********** quickly process each glyph charstring to *********/ + /********** extract the value from either a "sbw" or "seac" *********/ + /********** operator. *********/ + /********** *********/ + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + + + + static + T1_Error maxadv_sbw( T1_Decoder* decoder, + T1_Pos sbx, + T1_Pos sby, + T1_Pos wx, + T1_Pos wy ) + { + if (wx > decoder->builder.advance.x) + decoder->builder.advance.x = wx; + + (void)sbx; + (void)sby; + (void)wy; + return -1; /* return an error code to exit the Type 1 parser */ + /* immediately. */ + } + + + static + T1_Int maxadv_error( void ) + { + /* we should never reach this code, unless with a buggy font */ + return -2; + } + + /* the maxadv_gbuilder_interface is used when computing the maximum */ + /* advance width of all glyphs in a given font. We only process the */ + /* 'sbw' operator here, and return an error for all others.. */ + + /* Note that "seac" is processed by the T1_Decoder */ + static + const T1_Builder_Funcs maxadv_builder_interface = + { + (T1_Builder_EndChar) maxadv_error, + (T1_Builder_Sbw) maxadv_sbw, + (T1_Builder_ClosePath) maxadv_error, + (T1_Builder_RLineTo) maxadv_error, + (T1_Builder_RMoveTo) maxadv_error, + (T1_Builder_RCurveTo) maxadv_error + }; + + + /* the maxadv_interface is used when computing the maximum advance */ + /* with of the set of glyphs in a given font file. We only process */ + /* the "seac" operator and return immediately.. */ + static + const T1_Hinter_Funcs maxadv_hinter_interface = + { + (T1_Hinter_DotSection) maxadv_error, + (T1_Hinter_ChangeHints) maxadv_error, + (T1_Hinter_Stem) maxadv_error, + (T1_Hinter_Stem3) maxadv_error, + }; + + + + LOCAL_FUNC + T1_Error T1_Compute_Max_Advance( T1_Face face, + T1_Int *max_advance ) + { + T1_Error error; + T1_Decoder decoder; + T1_Int glyph_index; + T1_Font* type1 = &face->type1; + + *max_advance = 0; + + /* Initialise load decoder */ + T1_Init_Decoder( &decoder, &maxadv_hinter_interface ); + + T1_Init_Builder( &decoder.builder, face, 0, 0, + &maxadv_builder_interface ); + + /* For each glyph, parse the glyph charstring and extract */ + /* the advance width.. */ + for ( glyph_index = 0; glyph_index < type1->num_glyphs; glyph_index++ ) + { + /* now get load the unscaled outline */ + error = T1_Parse_CharStrings( &decoder, + type1->charstrings [glyph_index], + type1->charstrings_len[glyph_index], + type1->num_subrs, + type1->subrs, + type1->subrs_len ); + /* ignore the error if one occured - skip to next glyph */ + (void)error; + } + + *max_advance = decoder.builder.advance.x; + return T1_Err_Ok; + } + + + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + /********** *********/ + /********** *********/ + /********** UNHINTED GLYPH LOADER *********/ + /********** *********/ + /********** The following code is in charge of loading a *********/ + /********** single outline. It completely ignores hinting *********/ + /********** and is used when FT_LOAD_NO_HINTING is set. *********/ + /********** *********/ + /********** The Type 1 hinter is located in "t1hint.c" *********/ + /********** *********/ + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + + + + static + T1_Error close_open_path( T1_Builder* builder ) + { + T1_Error error; + FT_Outline* cur = &builder->current; + T1_Int num_points; + T1_Int first_point; + + /* Some fonts, like Hershey, are made of "open paths" which are */ + /* now managed directly by FreeType. In this case, it is necessary */ + /* to close the path by duplicating its points in reverse order, */ + /* which is precisely the purpose of this function */ + + /* first compute the number of points to duplicate.. */ + if (cur->n_contours > 1) + first_point = cur->contours[ cur->n_contours-2 ]+1; + else + first_point = 0; + + num_points = cur->n_points - first_point - 2; + if ( num_points > 0 ) + { + T1_Vector* source_point; + char* source_flags; + T1_Vector* point; + char* flags; + + error = T1_Add_Points( builder, num_points ); + if (error) return error; + + point = cur->points + cur->n_points; + flags = cur->flags + cur->n_points; + + source_point = point - 2; + source_flags = flags - 2; + + cur->n_points += num_points; + + if ( builder->load_points ) + do + { + *point++ = *source_point--; + *flags++ = *source_flags--; + num_points--; + } + while (num_points > 0); + } + + builder->path_begun = 0; + return T1_Err_Ok; + } + + + static + T1_Error gload_closepath( T1_Builder* builder ) + { + FT_Outline* cur = &builder->current; + + /* save current contour, if any */ + if ( cur->n_contours > 0 ) + cur->contours[cur->n_contours-1] = cur->n_points-1; + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + /* hint latest points if needed - this is not strictly required */ + /* there, but it helps for debugging, and doesn't affect performance */ + if ( builder->pass == 1 ) + T1_Hint_Points( builder ); +#endif + + builder->path_begun = 0; + return T1_Err_Ok; + } + + + + static + T1_Error gload_endchar( T1_Builder* builder ) + { + FT_Outline* cur = &builder->current; + T1_Error error; + + /* close path if needed */ + if (builder->path_begun) + { + error = close_open_path( builder ); + if (error) return error; + } + + error = gload_closepath( builder ); + + builder->base.n_points += cur->n_points; + builder->base.n_contours += cur->n_contours; + + return error; + } + + + + static + T1_Error gload_sbw( T1_Builder* builder, + T1_Pos sbx, + T1_Pos sby, + T1_Pos wx, + T1_Pos wy ) + { + builder->left_bearing.x += sbx; + builder->left_bearing.y += sby; + builder->advance.x = wx; + builder->advance.y = wy; + + builder->last.x = sbx; + builder->last.y = sby; + return 0; + } + + + + + static + T1_Error gload_rlineto( T1_Builder* builder, + T1_Pos dx, + T1_Pos dy ) + { + T1_Error error; + FT_Outline* cur = &builder->current; + T1_Vector vec; + + /* grow buffer if necessary */ + error = T1_Add_Points ( builder, 1 ); + if (error) return error; + + if ( builder->load_points ) + { + /* save point */ + vec.x = builder->last.x + dx; + vec.y = builder->last.y + dy; + + cur->points[cur->n_points] = vec; + cur->flags [cur->n_points] = FT_Curve_Tag_On; + + builder->last = vec; + } + cur->n_points++; + + builder->path_begun = 1; + return T1_Err_Ok; + } + + + static + T1_Error gload_rmoveto( T1_Builder* builder, + T1_Pos dx, + T1_Pos dy ) + { + T1_Error error; + FT_Outline* cur = &builder->current; + T1_Vector vec; + + /* in the case where "path_begun" is set, we have a rmoveto */ + /* after some normal path definition. When the face's paint */ + /* type is set to 1, this means that we have an "open path", */ + /* also called a 'stroke'. The FreeType raster doesn't support */ + /* opened path, so we'll close it explicitely there.. */ + if ( builder->path_begun && builder->face->type1.paint_type == 1 ) + { + if ( builder->face->type1.paint_type == 1 ) + { + error = close_open_path( builder ); + if (error) return error; + } + } + + /* grow buffer if necessary */ + error = T1_Add_Contours( builder, 1 ) || + T1_Add_Points ( builder, 1 ); + if (error) return error; + + /* save current contour, if any */ + if ( cur->n_contours > 0 ) + cur->contours[cur->n_contours-1] = cur->n_points-1; + + if ( builder->load_points ) + { + /* save point */ + vec.x = builder->last.x + dx; + vec.y = builder->last.y + dy; + cur->points[cur->n_points] = vec; + cur->flags [cur->n_points] = FT_Curve_Tag_On; + + builder->last = vec; + } + + cur->n_contours++; + cur->n_points++; + + return T1_Err_Ok; + } + + + static + T1_Error gload_rrcurveto( T1_Builder* builder, + T1_Pos dx1, + T1_Pos dy1, + T1_Pos dx2, + T1_Pos dy2, + T1_Pos dx3, + T1_Pos dy3 ) + { + T1_Error error; + FT_Outline* cur = &builder->current; + T1_Vector vec; + T1_Vector* points; + char* flags; + + /* grow buffer if necessary */ + error = T1_Add_Points ( builder, 3 ); + if (error) return error; + + if ( builder->load_points ) + { + /* save point */ + points = cur->points + cur->n_points; + flags = cur->flags + cur->n_points; + + vec.x = builder->last.x + dx1; + vec.y = builder->last.y + dy1; + points[0] = vec; flags[0] = FT_Curve_Tag_Cubic; + + vec.x += dx2; + vec.y += dy2; + points[1] = vec; flags[1] = FT_Curve_Tag_Cubic; + + vec.x += dx3; + vec.y += dy3; + points[2] = vec; flags[2] = FT_Curve_Tag_On; + + builder->last = vec; + } + + cur->n_points += 3; + builder->path_begun = 1; + return T1_Err_Ok; + } + + + + + static + T1_Error gload_ignore( void ) + { + return 0; + } + + + static + const T1_Builder_Funcs gload_builder_interface = + { + gload_endchar, + gload_sbw, + gload_closepath, + gload_rlineto, + gload_rmoveto, + gload_rrcurveto + }; + + + static + const T1_Builder_Funcs gload_builder_interface_null = + { + (T1_Builder_EndChar) gload_ignore, + (T1_Builder_Sbw) gload_sbw, /* record left bearing */ + (T1_Builder_ClosePath) gload_ignore, + (T1_Builder_RLineTo) gload_ignore, + (T1_Builder_RMoveTo) gload_ignore, + (T1_Builder_RCurveTo) gload_ignore + }; + + + static + const T1_Hinter_Funcs gload_hinter_interface = + { + (T1_Hinter_DotSection) gload_ignore, /* dotsection */ + (T1_Hinter_ChangeHints) gload_ignore, /* changehints */ + (T1_Hinter_Stem) gload_ignore, /* hstem & vstem */ + (T1_Hinter_Stem3) gload_ignore, /* hstem3 & vestem3 */ + }; + + + + + LOCAL_FUNC + T1_Error T1_Load_Glyph( T1_GlyphSlot glyph, + T1_Size size, + T1_Int glyph_index, + T1_Int load_flags ) + { + T1_Error error; + T1_Decoder decoder; + T1_Face face = (T1_Face)glyph->root.face; + T1_Bool hinting; + T1_Font* type1 = &face->type1; + + glyph->x_scale = size->root.metrics.x_scale; + glyph->y_scale = size->root.metrics.y_scale; + + glyph->root.outline.n_points = 0; + glyph->root.outline.n_contours = 0; + + hinting = ( load_flags & FT_LOAD_NO_SCALE ) == 0 && + ( load_flags & FT_LOAD_NO_HINTING ) == 0; + + glyph->root.format = ft_glyph_format_none; + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + /*****************************************************************/ + /* */ + /* Hinter overview : */ + /* */ + /* This is a two-pass hinter. On the first pass, the hints */ + /* are all recorded by the hinter, and no point is loaded */ + /* in the outline. */ + /* */ + /* When the first pass is finished, all stems hints are */ + /* grid-fitted at once. */ + /* */ + /* Then, a second pass is performed to load the outline */ + /* points as well as hint/scale them correctly. */ + /* */ + + if ( hinting ) + { + /* Pass 1 - don't record points, simply stem hints */ + T1_Init_Decoder( &decoder, &t1_hinter_funcs ); + T1_Init_Builder( &decoder.builder, face, size, glyph, + &gload_builder_interface_null ); + + glyph->hints->hori_stems.num_stems = 0; + glyph->hints->vert_stems.num_stems = 0; + + error = T1_Parse_CharStrings( &decoder, + type1->charstrings [glyph_index], + type1->charstrings_len[glyph_index], + type1->num_subrs, + type1->subrs, + type1->subrs_len ); + + /* All right, pass 1 is finished, now grid-fit all stem hints */ + T1_Hint_Stems( &decoder.builder ); + + /* Pass 2 - record and scale/hint the points */ + T1_Init_Decoder( &decoder, &t1_hinter_funcs ); + T1_Init_Builder( &decoder.builder, face, size, glyph, + &gload_builder_interface ); + + decoder.builder.pass = 1; + + error = T1_Parse_CharStrings( &decoder, + type1->charstrings [glyph_index], + type1->charstrings_len[glyph_index], + type1->num_subrs, + type1->subrs, + type1->subrs_len ); + + /* save new glyph tables */ + T1_Done_Builder( &decoder.builder ); + } + else +#endif + { + T1_Init_Decoder( &decoder, &gload_hinter_interface ); + + T1_Init_Builder( &decoder.builder, face, size, glyph, + &gload_builder_interface ); + + /* now load the unscaled outline */ + error = T1_Parse_CharStrings( &decoder, + type1->charstrings [glyph_index], + type1->charstrings_len[glyph_index], + type1->num_subrs, + type1->subrs, + type1->subrs_len ); + + /* save new glyph tables */ + T1_Done_Builder( &decoder.builder ); + } + + + /* Now, set the metrics.. - this is rather simple, as : */ + /* the left side bearing is the xMin, and the top side */ + /* bearing the yMax.. */ + if (!error) + { + FT_BBox cbox; + FT_Glyph_Metrics* metrics = &glyph->root.metrics; + + FT_Get_Outline_CBox( &glyph->root.outline, &cbox ); + + /* grid fit the bounding box if necessary */ + if (hinting) + { + cbox.xMin &= -64; + cbox.yMin &= -64; + cbox.xMax = ( cbox.xMax+63 ) & -64; + cbox.yMax = ( cbox.yMax+63 ) & -64; + } + + metrics->width = cbox.xMax - cbox.xMin; + metrics->height = cbox.yMax - cbox.yMin; + + metrics->horiBearingX = cbox.xMin; + metrics->horiBearingY = cbox.yMax; + + /* copy the _unscaled_ advance width */ + metrics->horiAdvance = decoder.builder.advance.x; + + /* make up vertical metrics */ + metrics->vertBearingX = 0; + metrics->vertBearingY = 0; + metrics->vertAdvance = 0; + + glyph->root.format = ft_glyph_format_outline; + + glyph->root.outline.second_pass = TRUE; + glyph->root.outline.high_precision = ( size->root.metrics.y_ppem < 24 ); + glyph->root.outline.dropout_mode = 2; + + if ( hinting ) + { + /* adjust the advance width */ + /* XXX : TODO : consider stem hints grid-fit */ + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, + glyph->x_scale ); + } + else if ( (load_flags & FT_LOAD_NO_SCALE) == 0 ) + { + /* scale the outline and the metrics */ + T1_Int n; + FT_Outline* cur = &decoder.builder.base; + T1_Vector* vec = cur->points; + T1_Fixed x_scale = glyph->x_scale; + T1_Fixed y_scale = glyph->y_scale; + + /* First of all, scale the points */ + for ( n = cur->n_points; n > 0; n--, vec++ ) + { + vec->x = FT_MulFix( vec->x, x_scale ); + vec->y = FT_MulFix( vec->y, y_scale ); + } + + /* Then scale the metrics */ + metrics->width = FT_MulFix( metrics->width, x_scale ); + metrics->height = FT_MulFix( metrics->height, y_scale ); + + metrics->horiBearingX = FT_MulFix( metrics->horiBearingX, x_scale ); + metrics->horiBearingY = FT_MulFix( metrics->horiBearingY, y_scale ); + metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); + + metrics->vertBearingX = FT_MulFix( metrics->vertBearingX, x_scale ); + metrics->vertBearingY = FT_MulFix( metrics->vertBearingY, y_scale ); + metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, x_scale ); + + } + } + + return error; + } + diff --git a/src/type1z/t1gload.h b/src/type1z/t1gload.h new file mode 100644 index 000000000..f635b8197 --- /dev/null +++ b/src/type1z/t1gload.h @@ -0,0 +1,436 @@ +/******************************************************************* + * + * t1gload.h 1.0 + * + * Type1 Glyph Loader. + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + * The Type 1 glyph loader uses three distinct objects to build + * scaled and hinted outlines from a charstrings program. These are : + * + * - a glyph builder, T1_Builder, used to store the built outline + * + * - a glyph hinter, T1_Hinter, used to record and apply the stem + * hints + * + * - a charstrings interpreter, T1_Decoder, used to parse the + * Type 1 charstrings stream, manage a stack and call the builder + * and/or hinter depending on the opcodes. + * + * Ideally, a Type 2 glyph loader would only need to have its own + * T2_Decoder object (assuming the hinter is able to manage all + * kinds of hints). + * + ******************************************************************/ + +#ifndef T1GLOAD_H +#define T1GLOAD_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + + +/*************************************************************************/ +/* */ +/* T1_Builder_Funcs */ +/* */ +/* */ +/* a structure used to store the address of various functions */ +/* used by a glyph builder to implement the outline's "path */ +/* construction". */ +/* */ +/* */ + typedef struct T1_Builder_ T1_Builder; + + typedef T1_Error (*T1_Builder_EndChar)( T1_Builder* loader ); + + typedef T1_Error (*T1_Builder_Sbw) ( T1_Builder* loader, + T1_Pos sbx, + T1_Pos sby, + T1_Pos wx, + T1_Pos wy ); + + typedef T1_Error (*T1_Builder_ClosePath)( T1_Builder* loader ); + + typedef T1_Error (*T1_Builder_RLineTo)( T1_Builder* loader, + T1_Pos dx, + T1_Pos dy ); + + typedef T1_Error (*T1_Builder_RMoveTo)( T1_Builder* loader, + T1_Pos dx, + T1_Pos dy ); + + typedef T1_Error (*T1_Builder_RCurveTo)( T1_Builder* loader, + T1_Pos dx1, + T1_Pos dy1, + T1_Pos dx2, + T1_Pos dy2, + T1_Pos dx3, + T1_Pos dy3 ); + + typedef struct T1_Builder_Funcs_ + { + T1_Builder_EndChar end_char; + T1_Builder_Sbw set_bearing_point; + T1_Builder_ClosePath close_path; + T1_Builder_RLineTo rline_to; + T1_Builder_RMoveTo rmove_to; + T1_Builder_RCurveTo rcurve_to; + + } T1_Builder_Funcs; + + + +/*************************************************************************/ +/* */ +/* T1_Builder */ +/* */ +/* */ +/* a structure used during glyph loading to store its outline. */ +/* */ +/* */ +/* system :: current system object */ +/* face :: current face object */ +/* size :: current size object */ +/* glyph :: current glyph slot */ +/* */ +/* current :: current glyph outline */ +/* base :: base glyph outline */ +/* */ +/* max_points :: maximum points in builder outline */ +/* max_contours :: maximum contours in builder outline */ +/* */ +/* last :: last point position */ +/* */ +/* scale_x :: horizontal scale ( FUnits to sub-pixels ) */ +/* scale_y :: vertical scale ( FUnits to sub-pixels ) */ +/* pos_x :: horizontal translation (composite glyphs) */ +/* pos_y :: vertical translation (composite glyph) */ +/* */ +/* left_bearing :: left side bearing point */ +/* advance :: horizontal advance vector */ +/* */ +/* path_begun :: flag, indicates that a new path has begun */ +/* load_points :: flag, if not set, no points are loaded */ +/* */ +/* pass :: pass number for multi-pass hinters */ +/* */ +/* funcs :: table of builder functions used to perform */ +/* the outline's path construction */ +/* */ +/* hint_point :: index of next point to hint.. */ +/* */ +/* */ +/* */ +/* */ + + struct T1_Builder_ + { + FT_Memory memory; + T1_Face face; + T1_Size size; + T1_GlyphSlot glyph; + + FT_Outline current; /* the current glyph outline */ + FT_Outline base; /* the composite glyph outline */ + + T1_Int max_points; /* capacity of base outline in points */ + T1_Int max_contours; /* capacity of base outline in contours */ + + T1_Vector last; + + T1_Fixed scale_x; + T1_Fixed scale_y; + + T1_Pos pos_x; + T1_Pos pos_y; + + T1_Vector left_bearing; + T1_Vector advance; + + T1_BBox bbox; /* bounding box */ + T1_Bool path_begun; + T1_Bool load_points; + + T1_Int pass; + T1_Int hint_point; + + /* path construction function interface */ + T1_Builder_Funcs funcs; + }; + + +/*************************************************************************/ +/* */ +/* T1_Hinter_Funcs */ +/* */ +/* */ +/* a structure used to store the address of various functions */ +/* used by a Type 1 hinter to perform outline hinting. */ +/* */ + + typedef T1_Error (*T1_Hinter_ChangeHints)( T1_Builder* builder ); + + typedef T1_Error (*T1_Hinter_DotSection)( T1_Builder* builder ); + + typedef T1_Error (*T1_Hinter_Stem)( T1_Builder* builder, + T1_Pos pos, + T1_Pos width, + T1_Bool vertical ); + + + typedef T1_Error (*T1_Hinter_Stem3)( T1_Builder* builder, + T1_Pos pos0, + T1_Pos width0, + T1_Pos pos1, + T1_Pos width1, + T1_Pos pos2, + T1_Pos width2, + T1_Bool vertical ); + + typedef struct T1_Hinter_Func_ + { + T1_Hinter_ChangeHints change_hints; + T1_Hinter_DotSection dot_section; + T1_Hinter_Stem stem; + T1_Hinter_Stem3 stem3; + + } T1_Hinter_Funcs; + + + + typedef enum T1_Operator_ + { + op_none = 0, + op_endchar, + op_hsbw, + op_seac, + op_sbw, + op_closepath, + op_hlineto, + op_hmoveto, + op_hvcurveto, + op_rlineto, + op_rmoveto, + op_rrcurveto, + op_vhcurveto, + op_vlineto, + op_vmoveto, + op_dotsection, + op_hstem, + op_hstem3, + op_vstem, + op_vstem3, + op_div, + op_callothersubr, + op_callsubr, + op_pop, + op_return, + op_setcurrentpoint, + + op_max /* never remove this one */ + + } T1_Operator; + + + + + /* execution context charstring zone */ + typedef struct T1_Decoder_Zone_ + { + T1_Byte* base; + T1_Byte* limit; + T1_Byte* cursor; + + } T1_Decoder_Zone; + + + typedef struct T1_Decoder_ + { + T1_Builder builder; + T1_Hinter_Funcs hinter; + + T1_Int stack[ T1_MAX_CHARSTRINGS_OPERANDS ]; + T1_Int* top; + + T1_Decoder_Zone zones[ T1_MAX_SUBRS_CALLS+1 ]; + T1_Decoder_Zone* zone; + + T1_Int flex_state; + T1_Int num_flex_vectors; + T1_Vector flex_vectors[7]; + + } T1_Decoder; + + + +/********************************************************************* + * + * + * T1_Init_Builder + * + * + * Initialise a given glyph builder. + * + * + * builder :: glyph builder to initialise + * face :: current face object + * size :: current size object + * glyph :: current glyph object + * funcs :: glyph builder functions (or "methods"). + * + * + * This function is exported for now because it is used by the + * "t1dump" utility. Later, it will be accessed through a + * format-specific extension + * + *********************************************************************/ + + EXPORT_DEF + void T1_Init_Builder( T1_Builder* builder, + T1_Face face, + T1_Size size, + T1_GlyphSlot glyph, + const T1_Builder_Funcs* funcs ); + +/********************************************************************* + * + * + * T1_Done_Builder + * + * + * Finalise a given glyph builder. Its content can still be + * used after the call, but the function saves important information + * within the corresponding glyph slot. + * + * + * builder :: glyph builder to initialise + * + * + * This function is exported for now because it is used by the + * "t1dump" utility. Later, it will be accessed through a + * format-specific extension + * + *********************************************************************/ + + EXPORT_DEF + void T1_Done_Builder( T1_Builder* builder ); + + +/********************************************************************* + * + * + * T1_Init_Decoder + * + * + * Initialise a given Type 1 decoder for parsing + * + * + * decoder :: Type 1 decoder to initialise + * funcs :: hinter functions interface + * + * + * This function is exported for now because it is used by the + * "t1dump" utility. Later, it will be accessed through a + * format-specific extension + * + *********************************************************************/ + + EXPORT_DEF + void T1_Init_Decoder( T1_Decoder* decoder, + const T1_Hinter_Funcs* funcs ); + + + + /* Compute the maximum advance width of a font through quick parsing */ + LOCAL_DEF + T1_Error T1_Compute_Max_Advance( T1_Face face, + T1_Int *max_advance ); + + + /* This function is exported, because it is used by the T1Dump utility */ + EXPORT_DEF + T1_Error T1_Parse_CharStrings( T1_Decoder* decoder, + T1_Byte* charstring_base, + T1_Int charstring_len, + T1_Int num_subrs, + T1_Byte** subrs_base, + T1_Int* subrs_len ); + + + +/*************************************************************************/ +/* */ +/* T1_Add_Points */ +/* */ +/* */ +/* Checks that there is enough room in the current load glyph outline */ +/* to accept "num_points" additional outline points. If not, this */ +/* function grows the load outline's arrays accordingly.. */ +/* */ +/* */ +/* builder :: pointer to glyph builder object */ +/* num_points :: number of points that will be added later */ +/* */ +/* */ +/* Type1 error code. 0 means success */ +/* */ +/* */ +/* This function does NOT update the points count in the glyph loader */ +/* This must be done by the caller itself, after this function is */ +/* invoked.. */ +/* */ + LOCAL_DEF + T1_Error T1_Add_Points( T1_Builder* builder, + T1_Int num_points ); + +/*************************************************************************/ +/* */ +/* T1_Add_Contours */ +/* */ +/* */ +/* Checks that there is enough room in the current load glyph outline */ +/* to accept "num_contours" additional contours. If not, this func */ +/* the load outline's arrays accordingly.. */ +/* */ +/* */ +/* builder :: pointer to glyph builder object */ +/* num_contours :: number of contours that will be added later */ +/* */ +/* */ +/* Type1 error code. 0 means success */ +/* */ +/* */ +/* This function does NOT update the contours count in the load glyph */ +/* This must be done by the caller itself, after this function is */ +/* invoked.. */ +/* */ + LOCAL_DEF + T1_Error T1_Add_Contours( T1_Builder* builder, + T1_Int num_contours ); + + + LOCAL_DEF + T1_Error T1_Load_Glyph( T1_GlyphSlot glyph, + T1_Size size, + T1_Int glyph_index, + T1_Int load_flags ); + + +#ifdef __cplusplus + } +#endif + +#endif /* T1GLOAD_H */ diff --git a/src/type1z/t1hinter.c b/src/type1z/t1hinter.c new file mode 100644 index 000000000..dc59441bd --- /dev/null +++ b/src/type1z/t1hinter.c @@ -0,0 +1,1269 @@ +/******************************************************************* + * + * t1hinter.c 1.2 + * + * Type1 hinter. + * + * Copyright 1996-1999 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + * The Hinter is in charge of fitting th scaled outline to the + * pixel grid in order to considerably improve the quality of + * the Type 1 font driver's output.. + * + ******************************************************************/ + +#include +#include +#include + +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1hint /* for debugging/tracing */ + + +#undef ONE_PIXEL +#define ONE_PIXEL 64 + +#undef ROUND +#define ROUND(x) (( x + ONE_PIXEL/2 ) & -ONE_PIXEL) + +#undef SCALE +#define SCALE(val) FT_MulFix( val, scale ) + +/* various constants used to describe the alignment of a horizontal */ +/* stem with regards to the blue zones */ +#define T1_ALIGN_NONE 0 +#define T1_ALIGN_BOTTOM 1 +#define T1_ALIGN_TOP 2 +#define T1_ALIGN_BOTH 3 + + +/************************************************************************ + * + * + * t1_set_blue_zones + * + * + * Set a size object's blue zones during reset. This will compute + * the "snap" zone corresponding to each blue zone. + * + * + * size :: handle to target size object + * + * + * Error code. 0 means success + * + * + * This functions does the following : + * + * 1. It extracts the bottom and top blue zones from the + * face object. + * + * 2. Each zone is then grown by BlueFuzz, overlapping + * is eliminated by adjusting the zone edges appropriately + * + * 3. For each zone, we keep its original font units position, its + * original scaled position, as well as its grown/adjusted + * edges. + * + ************************************************************************/ + + /* ultra simple bubble sort (not a lot of elements, mostly */ + /* pre-sorted, no need for quicksort) */ + static + void t1_sort_blues( T1_Int* blues, + T1_Int count ) + { + T1_Int i, swap; + T1_Int* cur; + + for ( i = 2; i < count; i += 2 ) + { + cur = blues + i; + do + { + if ( cur[-1] < cur[0] ) + break; + + swap = cur[-2]; cur[-2] = cur[0]; cur[0] = swap; + swap = cur[-1]; cur[-1] = cur[1]; cur[1] = swap; + cur -= 2; + } + while ( cur > blues ); + } + } + + + static + T1_Error t1_set_blue_zones( T1_Size size ) + { + T1_Face face = (T1_Face)size->root.face; + T1_Font* priv = &face->type1; + T1_Int n; + T1_Int blues[24]; + T1_Int num_bottom; + T1_Int num_top; + T1_Int num_blues; + T1_Size_Hints* hints = size->hints; + T1_Snap_Zone* zone; + T1_Pos pix, orus; + T1_Pos min, max, threshold; + T1_Fixed scale; + T1_Bool is_bottom; + + /**********************************************************************/ + /* */ + /* COPY BOTTOM AND TOP BLUE ZONES IN LOCAL ARRAYS */ + /* */ + /* */ + + /* First of all, check the sizes of the /BlueValues and /OtherBlues */ + /* tables. They all must contain an even number of arguments */ + if ( priv->num_other_blues & 1 || + priv->num_blues & 1 ) + { + FT_ERROR(( "T1.Copy_Blues : odd number of blue values\n" )); + return T1_Err_Syntax_Error; + } + + /* copy the bottom blue zones from /OtherBlues */ + num_top = 0; + num_bottom = priv->num_other_blues; + + for ( n = 0; n < num_bottom; n ++ ) + blues[n] = priv->other_blues[n]; + + /* Add the first blue zone in /BlueValues to the table */ + num_top = priv->num_blues - 2; + if ( num_top >= 0 ) + { + blues[ num_bottom ] = priv->blue_values[0]; + blues[num_bottom+1] = priv->blue_values[1]; + + num_bottom += 2; + } + + /* sort the bottom blue zones */ + t1_sort_blues( blues, num_bottom ); + + hints->num_bottom_zones = num_bottom >> 1; + + /* now copy the /BlueValues to the top of the blues array */ + if ( num_top > 0 ) + { + for ( n = 0; n < num_top; n++ ) + blues[ num_bottom+n ] = priv->blue_values[n+2]; + + /* sort the top blue zones */ + t1_sort_blues( blues + num_bottom, num_top ); + } + else + num_top = 0; + + num_blues = num_top + num_bottom; + hints->num_blue_zones = ( num_blues ) >> 1; + + /**********************************************************************/ + /* */ + /* BUILD BLUE SNAP ZONES FROM THE LOCAL BLUES ARRAYS */ + /* */ + /* */ + + scale = size->root.metrics.y_scale; + zone = hints->blue_zones; + threshold = ONE_PIXEL/4; /* 0.25 pixels */ + + for ( n = 0; n < num_blues; n += 2, zone ++ ) + { + is_bottom = ( n < num_bottom ? 1 : 0 ); + + orus = blues[n+is_bottom]; /* get alignement coordinate */ + pix = SCALE( orus ); /* scale it */ + + min = SCALE( blues[ n ] - priv->blue_fuzz ); + max = SCALE( blues[n+1] + priv->blue_fuzz ); + + if ( min > pix - threshold ) min = pix - threshold; + if ( max < pix + threshold ) max = pix + threshold; + + zone->orus = orus; + zone->pix = pix; + zone->min = min; + zone->max = max; + } + + /* adjust edges in case of overlap */ + zone = hints->blue_zones; + for ( n = 0; n < num_blues-2; n += 2, zone ++ ) + { + if ( n != num_bottom-2 && + zone[0].max > zone[1].min ) + { + zone[0].max = zone[1].min = (zone[0].pix+zone[1].pix)/2; + } + } + + + /* Compare the current pixel size with the BlueScale value */ + /* to know wether to supress overshoots.. */ + + hints->supress_overshoots = + ( size->root.metrics.y_ppem < FT_MulFix(1000,priv->blue_scale) ); + + /* Now print the new blue values in tracing mode */ +#ifdef FT_DEBUG_LEVEL_TRACE + + FT_TRACE2(( "Blue Zones for size object at $%08lx :\n", (long)size )); + FT_TRACE2(( " orus pix min max\n" )); + FT_TRACE2(( "-------------------------------\n" )); + + zone = hints->blue_zones; + for ( n = 0; n < hints->num_blue_zones; n++ ) + { + FT_TRACE2(( " %3d %.2f %.2f %.2f\n", + zone->orus, + zone->pix/64.0, + zone->min/64.0, + zone->max/64.0 )); + zone++; + } + FT_TRACE2(( "\nOver shoots are %s\n\n", + hints->supress_overshoots ? "supressed" : "active" )); + +#endif /* DEBUG_LEVEL_TRACE */ + + return T1_Err_Ok; + } + + + +/************************************************************************ + * + * + * t1_set_snap_zones + * + * + * This function set a size object's stem snap zones. + * + * + * size :: handle to target size object + * + * + * Error code. 0 means success + * + * + * This function performs the following : + * + * 1. It reads and scales the stem snap widths from the parent face + * + * 2. A "snap zone" is computed for each snap width, by "growing" + * it with a threshold of a 1/2 pixel. Overlapping is avoided + * through proper edge adjustment. + * + * 3. Each width whose zone contain the scaled standard set width + * is removed from the table + * + * 4. Finally, the standard set width is scaled, and its correponding + * "snap zone" is inserted into the sorted snap zones table + * + ************************************************************************/ + + static + T1_Error t1_set_snap_zones( T1_Size size ) + { + T1_Int n, direction, n_zones, num_zones; + T1_Snap_Zone* zone; + T1_Snap_Zone* base_zone; + T1_Short* orgs; + T1_Pos standard_width; + T1_Fixed scale; + + T1_Face face = (T1_Face)size->root.face; + T1_Font* priv = &face->type1; + T1_Size_Hints* hints = size->hints; + + /* start with horizontal snap zones */ + direction = 0; + standard_width = priv->standard_width; + n_zones = priv->num_snap_widths; + base_zone = hints->snap_widths; + orgs = priv->stem_snap_widths; + scale = size->root.metrics.x_scale; + + while (direction < 2) + { + /*****************************************************************/ + /* */ + /* Read and scale stem snap widths table from the physical */ + /* font record. */ + /* */ + T1_Pos prev, orus, pix, min, max, threshold; + + threshold = ONE_PIXEL/4; + zone = base_zone; + + if ( n_zones > 0 ) + { + orus = *orgs++; + pix = SCALE( orus ); + min = pix-threshold; + max = pix+threshold; + + zone->orus = orus; + zone->pix = pix; + zone->min = min; + prev = pix; + + for ( n = 1; n < n_zones; n++ ) + { + orus = *orgs++; + pix = SCALE( orus ); + + if ( pix-prev < 2*threshold ) + { + min = max = (pix+prev)/2; + } + else + min = pix-threshold; + + zone->max = max; + zone++; + zone->orus = orus; + zone->pix = pix; + zone->min = min; + + max = pix+threshold; + prev = pix; + } + zone->max = max; + } + + /* print the scaled stem snap values in tracing modes */ +#ifdef FT_DEBUG_LEVEL_TRACE + + FT_TRACE2(( "Set_Snap_Zones : first %s pass\n", + direction ? "vertical" : "horizontal" )); + + FT_TRACE2(( "Scaled original stem snap zones :\n" )); + FT_TRACE2(( " orus pix min max\n" )); + FT_TRACE2(( "-----------------------------\n" )); + + zone = base_zone; + for ( n = 0; n < n_zones; n++, zone++ ) + FT_TRACE2(( " %3d %.2f %.2f %.2f\n", + zone->orus, + zone->pix/64.0, + zone->min/64.0, + zone->max/64.0 )); + FT_TRACE2(( "\n" )); + + FT_TRACE2(( "Standard width = %d\n", standard_width )); +#endif + + /*****************************************************************/ + /* */ + /* Now, each snap width which is in the range of the standard */ + /* set width will be removed from the list.. */ + /* */ + + if ( standard_width > 0 ) + { + T1_Snap_Zone* parent; + T1_Pos std_pix, std_min, std_max; + + std_pix = SCALE( standard_width ); + + std_min = std_pix-threshold; + std_max = std_pix+threshold; + + num_zones = 0; + zone = base_zone; + parent = base_zone; + + for ( n = 0; n < n_zones; n++ ) + { + if ( zone->pix >= std_min && zone->pix <= std_max ) + { + /* this zone must be removed from the list */ + if ( std_min > zone->min ) std_min = zone->min; + if ( std_max < zone->max ) std_max = zone->max; + } + else + { + *parent++ = *zone; + num_zones++; + } + zone++; + } + + /**********************************************/ + /* Now, insert the standard width zone */ + + zone = base_zone+num_zones; + while ( zone > base_zone && zone[-1].pix > std_max ) + { + zone[0] = zone[-1]; + zone --; + } + + /* check border zones */ + if ( zone > base_zone && zone[-1].max > std_min ) + zone[-1].max = std_min; + + if ( zone < base_zone+num_zones && zone[1].min < std_max ) + zone[1].min = std_max; + + zone->orus = standard_width; + zone->pix = std_pix; + zone->min = std_min; + zone->max = std_max; + + num_zones++; + } + else + num_zones = n_zones; + + /* save total number of stem snaps now */ + if (direction) hints->num_snap_heights = num_zones; + else hints->num_snap_widths = num_zones; + + /* print the scaled stem snap values in tracing modes */ +#ifdef FT_DEBUG_LEVEL_TRACE + + FT_TRACE2(( "Set_Snap_Zones : second %s pass\n", + direction ? "vertical" : "horizontal" )); + + FT_TRACE2(( "Scaled clipped stem snap zones :\n" )); + FT_TRACE2(( " orus pix min max\n" )); + FT_TRACE2(( "-----------------------------\n" )); + + zone = base_zone; + for ( n = 0; n < num_zones; n++, zone++ ) + FT_TRACE2(( " %3d %.2f %.2f %.2f\n", + zone->orus, + zone->pix/64.0, + zone->min/64.0, + zone->max/64.0 )); + FT_TRACE2(( "\n" )); + + FT_TRACE2(( "Standard width = %d\n", standard_width )); +#endif + + /* continue with vertical snap zone */ + direction++; + standard_width = priv->standard_height; + n_zones = priv->num_snap_heights; + base_zone = hints->snap_heights; + orgs = priv->stem_snap_heights; + scale = size->root.metrics.y_scale; + } + + return T1_Err_Ok; + } + + +/************************************************************************ + * + * + * T1_New_Size_Hinter + * + * + * Allocates a new hinter structure for a given size object + * + * + * size :: handle to target size object + * + * + * Error code. 0 means success + * + ************************************************************************/ + + LOCAL_FUNC + T1_Error T1_New_Size_Hinter( T1_Size size ) + { + FT_Memory memory = size->root.face->memory; + + return MEM_Alloc( size->hints, sizeof(*size->hints) ); + } + + +/************************************************************************ + * + * + * T1_Done_Size_Hinter + * + * + * Releases a given size object's hinter structure + * + * + * size :: handle to target size object + * + ************************************************************************/ + + LOCAL_FUNC + void T1_Done_Size_Hinter( T1_Size size ) + { + FT_Memory memory = size->root.face->memory; + + FREE( size->hints ); + } + + + +/************************************************************************ + * + * + * T1_Reset_Size_Hinter + * + * + * Recomputes hinting information when a given size object has + * changed its resolutions/char sizes/pixel sizes + * + * + * size :: handle to size object + * + * + * Error code. 0 means success + * + ************************************************************************/ + + LOCAL_FUNC + T1_Error T1_Reset_Size_Hinter( T1_Size size ) + { + return t1_set_blue_zones(size) || t1_set_snap_zones(size); + } + + + +/************************************************************************ + * + * + * T1_New_Glyph_Hinter + * + * + * Allocates a new hinter structure for a given glyph slot + * + * + * glyph :: handle to target glyph slot + * + * + * Error code. 0 means success + * + ************************************************************************/ + + LOCAL_FUNC + T1_Error T1_New_Glyph_Hinter( T1_GlyphSlot glyph ) + { + FT_Memory memory = glyph->root.face->memory; + + return MEM_Alloc( glyph->hints, sizeof(*glyph->hints) ); + } + + +/************************************************************************ + * + * + * T1_Done_Glyph_Hinter + * + * + * Releases a given glyph slot's hinter structure + * + * + * glyph :: handle to glyph slot + * + ************************************************************************/ + + LOCAL_FUNC + void T1_Done_Glyph_Hinter( T1_GlyphSlot glyph ) + { + FT_Memory memory = glyph->root.face->memory; + + FREE( glyph->hints ); + } + + + + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + /********** *********/ + /********** *********/ + /********** HINTED GLYPH LOADER *********/ + /********** *********/ + /********** The following code is in charge of the first *********/ + /********** and second pass when loading a single outline *********/ + /********** *********/ + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + + static + T1_Error t1_hinter_ignore( void ) + { + /* do nothing, used for "dotsection" which is unsupported for now */ + return 0; + } + + static + T1_Error t1_hinter_stem( T1_Builder* builder, + T1_Pos pos, + T1_Int width, + T1_Bool vertical ) + { + T1_Stem_Table* stem_table; + T1_Stem_Hint* stems; + T1_Stem_Hint* cur_stem; + T1_Int min, max, n, num_stems; + T1_Bool new_stem; + T1_Glyph_Hints* hinter = builder->glyph->hints; + + /* select the appropriate stem array */ + stem_table = vertical ? &hinter->vert_stems : &hinter->hori_stems; + stems = stem_table->stems; + num_stems = stem_table->num_stems; + + /* Compute minimum and maximum orus for the stem */ + min = pos + ( vertical + ? builder->left_bearing.x + : builder->left_bearing.y ); + + if ( width >= 0 ) + max = min + width; + else + { + /* a negative width indicates a ghost stem */ + if ( width == -21 ) + min += width; + + max = min; + } + + /* now scan the array. If we find a stem with the same borders */ + /* simply activate it.. */ + cur_stem = stems; + new_stem = 1; + + for ( n = 0; n < num_stems; n++, cur_stem++ ) + { + if ( cur_stem->min_edge.orus == min && + cur_stem->max_edge.orus == max ) + { + /* This stem is already in the table, simply activate it */ + if ( (cur_stem->hint_flags & T1_HINT_FLAG_ACTIVE) == 0) + { + cur_stem->hint_flags |= T1_HINT_FLAG_ACTIVE; + stem_table->num_active ++; + } + new_stem = 0; + break; + } + } + + /* add a new stem to the array when necessary */ + if (new_stem) + { + if (cur_stem >= stems + T1_HINTER_MAX_EDGES) + { + FT_ERROR(( "T1.Hinter : too many stems in glyph charstring\n" )); + return T1_Err_Syntax_Error; + } + + /* on the first pass, we record the stem, otherwise, this is */ + /* a bug in the glyph loader !! */ + if ( builder->pass == 0 ) + { + cur_stem->min_edge.orus = min; + cur_stem->max_edge.orus = max; + cur_stem->hint_flags = T1_HINT_FLAG_ACTIVE; + + stem_table->num_stems++; + stem_table->num_active++; + } + else + { + FT_ERROR(( "T1.Hinter : fatal glyph loader bug - pass2-stem\n" )); + return T1_Err_Syntax_Error; + } + } + + return T1_Err_Ok; + } + + + static + T1_Error t1_hinter_stem3( T1_Builder* builder, + T1_Pos pos0, + T1_Int width0, + T1_Pos pos1, + T1_Int width1, + T1_Pos pos2, + T1_Int width2, + T1_Bool vertical ) + { + /* For now, don't be elitist and simply call "stem" 3 times */ + return t1_hinter_stem( builder, pos0, width0, vertical ) || + t1_hinter_stem( builder, pos1, width1, vertical ) || + t1_hinter_stem( builder, pos2, width2, vertical ); + } + + + static + T1_Error t1_hinter_changehints( T1_Builder* builder ) + { + T1_Int dimension; + T1_Stem_Table* stem_table; + T1_Glyph_Hints* hinter = builder->glyph->hints; + + /* if we're in the second pass of glyph hinting, we must */ + /* call the function T1_Hint_Points on the builder in order */ + /* to force the fit the latest points to the pixel grid */ + if ( builder->pass == 1 ) + T1_Hint_Points( builder ); + + /* Simply de-activate all hints in all arrays */ + stem_table = &hinter->hori_stems; + + for ( dimension = 2; dimension > 0; dimension-- ) + { + T1_Stem_Hint* cur = stem_table->stems; + T1_Stem_Hint* limit = cur + stem_table->num_stems; + + for ( ; cur < limit; cur++ ) + cur->hint_flags &= ~T1_HINT_FLAG_ACTIVE; + + stem_table->num_active = 0; + stem_table = &hinter->vert_stems; + } + + return T1_Err_Ok; + } + + + LOCAL_FUNC + const T1_Hinter_Funcs t1_hinter_funcs = + { + (T1_Hinter_ChangeHints) t1_hinter_changehints, + (T1_Hinter_DotSection) t1_hinter_ignore, + (T1_Hinter_Stem) t1_hinter_stem, + (T1_Hinter_Stem3) t1_hinter_stem3 + }; + + + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + /********** *********/ + /********** *********/ + /********** STEM HINTS MANAGEMENT *********/ + /********** *********/ + /********** The following code is in charge of computing *********/ + /********** the placement of each scaled stem hint.. *********/ + /********** *********/ + /**********************************************************************/ + /**********************************************************************/ + /**********************************************************************/ + +/************************************************************************ + * + * + * t1_sort_hints + * + * + * Sort the list of active stems in increasing order, through + * the "sort" indexing table + * + * + * table :: a stem hints table + * + ************************************************************************/ + + static + void t1_sort_hints( T1_Stem_Table* table ) + { + T1_Int num_stems = table->num_stems; + T1_Int num_active = 0; + T1_Int* sort = table->sort; + T1_Stem_Hint* stems = table->stems; + T1_Int n; + + /* record active stems in sort table */ + for ( n = 0; n < num_stems; n++ ) + { + if ( stems[n].hint_flags & T1_HINT_FLAG_ACTIVE ) + sort[num_active++] = n; + } + + /* now sort the indices. There are usually very few stems, */ + /* and they are pre-sorted in 90% cases, so we choose a */ + /* simple bubble sort (quicksort would be slower).. */ + for ( n = 1; n < num_active; n++ ) + { + T1_Int p = n-1; + T1_Stem_Hint* cur = stems + sort[n]; + + do + { + T1_Int swap; + T1_Stem_Hint* prev = stems + sort[p]; + + /* note that by definition, the active stems cannot overlap */ + /* so we simply compare their "min" to sort them.. */ + /* (we could compare their max, this wouldn't change anything) */ + if ( prev->min_edge.orus <= cur->min_edge.orus ) + break; + + /* swap elements */ + swap = sort[ p ]; + sort[ p ] = sort[p+1]; + sort[p+1] = swap; + p--; + } + while ( p >= 0 ); + } + + table->num_active = num_active; + } + + +/************************************************************************ + * + * + * t1_hint_horizontal_stems + * + * + * Compute the location of each scaled horizontal stem hint. + * This takes care of the blue zones and the horizontal stem + * snap table + * + * + * table :: the horizontal stem hints table + * hints :: the current size's hint structure + * blueShift :: the value of the /BlueShift as taken from the + * face object. + * scale :: the 16.16 scale used to convert outline + * units to 26.6 pixels + * + * + * For now, all stems are hinted independently from each other. + * It might be necessary, for better performance, to introduce + * the notion of "controlled" hints describing things like + * counter-stems, stem3 as well as overlapping stems control. + * + ************************************************************************/ + + static + void t1_hint_horizontal_stems( T1_Stem_Table* table, + T1_Size_Hints* hints, + T1_Pos blueShift, + T1_Fixed scale ) + { + T1_Stem_Hint* stem = table->stems; + T1_Stem_Hint* limit = stem + table->num_stems; + + /* first of all, scale the blueShift */ + blueShift = SCALE(blueShift); + + /* then scan the horizontal stem table */ + for ( ; stem < limit; stem++ ) + { + T1_Pos bottom_orus = stem->min_edge.orus; + T1_Pos top_orus = stem->max_edge.orus; + + T1_Pos top_pix = SCALE( top_orus ); + T1_Pos bottom_pix = SCALE( bottom_orus ); + T1_Pos width_pix = top_pix - bottom_pix; + + T1_Pos bottom = bottom_pix; + T1_Pos top = top_pix; + T1_Int align = T1_ALIGN_NONE; + + /******************************************************************/ + /* Snap pixel width if in stem snap range */ + { + T1_Snap_Zone* zone = hints->snap_heights; + T1_Snap_Zone* zone_limit = zone + hints->num_snap_heights; + + for ( ; zone < zone_limit; zone++ ) + { + if ( width_pix < zone->min ) + break; + + if ( width_pix <= zone->max ) + { + width_pix = zone->pix; + break; + } + } + } + + /******************************************************************/ + /* round width - minimum 1 pixel if this isn't a ghost stem */ + if ( width_pix > 0 ) + width_pix = ( width_pix < ONE_PIXEL ? ONE_PIXEL : ROUND(width_pix) ); + + + /******************************************************************/ + /* Now check for bottom blue zones alignement */ + { + T1_Int num_blues = hints->num_bottom_zones; + T1_Snap_Zone* blue = hints->blue_zones; + T1_Snap_Zone* blue_limit = blue + num_blues; + + for ( ; blue < blue_limit; blue++ ) + { + if ( bottom_pix < blue->min ) + break; + + if ( bottom_pix <= blue->max ) + { + align = T1_ALIGN_BOTTOM; + bottom = ROUND( blue->pix ); + + /* implements blue shift */ + if (!hints->supress_overshoots) + { + T1_Pos delta = blue->pix - bottom_pix; + + delta = ( delta < blueShift ? 0 : ROUND( delta ) ); + bottom -= delta; + } + } + } + } + + + /******************************************************************/ + /* Check for top blue zones alignement */ + { + T1_Int num_blues = hints->num_blue_zones - + hints->num_bottom_zones; + + T1_Snap_Zone* blue = hints->blue_zones + + hints->num_bottom_zones; + + T1_Snap_Zone* blue_limit = blue + num_blues; + + for ( ; blue < blue_limit; blue++ ) + { + if ( top_pix < blue->min ) + break; + + if ( top_pix <= blue->max ) + { + align |= T1_ALIGN_TOP; + top = ROUND( blue->pix ); + + /* implements blue shift */ + if (!hints->supress_overshoots) + { + T1_Pos delta = top - blue->pix; + + delta = ( delta < blueShift ? 0 : ROUND( delta ) ); + top += delta; + } + } + } + } + + + /******************************************************************/ + /* compute the hinted stem position, according to its alignment */ + switch (align) + { + case T1_ALIGN_BOTTOM: /* bottom zone alignement */ + bottom_pix = bottom; + top_pix = bottom + width_pix; + break; + + case T1_ALIGN_TOP: /* top zone alignement */ + top_pix = top; + bottom_pix = top - width_pix; + + break; + + case T1_ALIGN_BOTH: /* bottom+top zone alignement */ + bottom_pix = bottom; + top_pix = top; + break; + + default: /* no alignement */ + + /* XXXX : TODO : Add management of controlled stems */ + bottom = ( SCALE(bottom_orus+top_orus) - width_pix )/2; + + bottom_pix = ROUND(bottom); + top_pix = bottom_pix + width_pix; + } + + stem->min_edge.pix = bottom_pix; + stem->max_edge.pix = top_pix; + } + } + + + + +/************************************************************************ + * + * + * t1_hint_vertical_stems + * + * + * Compute the location of each scaled vertical stem hint. + * This takes care of the vertical stem snap table + * + * + * table :: the vertical stem hints table + * hints :: the current size's hint structure + * scale :: the 16.16 scale used to convert outline + * units to 26.6 pixels + * + * + * For now, all stems are hinted independently from each other. + * It might be necessary, for better performance, to introduce + * the notion of "controlled" hints describing things like + * counter-stems, stem3 as well as overlapping stems control. + * + ************************************************************************/ + + /* compute the location of each scaled vertical stem hint. */ + /* Take care of blue zones and stem snap table */ + static + void t1_hint_vertical_stems( T1_Stem_Table* table, + T1_Size_Hints* hints, + T1_Fixed scale ) + { + T1_Stem_Hint* stem = table->stems; + T1_Stem_Hint* limit = stem + table->num_stems; + + for ( ; stem < limit; stem++ ) + { + T1_Pos stem_left = stem->min_edge.orus; + T1_Pos stem_right = stem->max_edge.orus; + T1_Pos width_pix, left; + + width_pix = SCALE( stem_right - stem_left ); + + /* Snap pixel width if in stem snap range */ + { + T1_Snap_Zone* zone = hints->snap_widths; + T1_Snap_Zone* zone_limit = zone + hints->num_snap_widths; + + for ( ; zone < zone_limit; zone++ ) + { + if ( width_pix < zone->min ) + break; + + if ( width_pix <= zone->max ) + { + width_pix = zone->pix; + break; + } + } + } + + /* round width - minimum 1 pixel if this isn't a ghost stem */ + if ( width_pix > 0 ) + width_pix = ( width_pix < ONE_PIXEL ? ONE_PIXEL : + ROUND( width_pix ) ); + + /* now place the snapped and rounded stem */ + + /* XXXX : TODO : implement controlled stems for the overlapping */ + /* cases.. */ + + left = ( SCALE(stem_left+stem_right) - width_pix )/2; + + stem->min_edge.pix = ROUND(left); + stem->max_edge.pix = stem->min_edge.pix + width_pix; + } + } + + + + +/************************************************************************ + * + * + * t1_hint_point + * + * + * Grid-fit a coordinate with regards to a given stem hints table + * + * + * table :: the source stem hints table + * coord :: original coordinate, expressed in font units + * scale :: the 16.16 scale used to convert font units into + * 26.6 pixels + * + * + * the hinted/scaled value in 26.6 pixels + * + * + * For now, all stems are hinted independently from each other. + * It might be necessary, for better performance, to introduce + * the notion of "controlled" hints describing things like + * counter-stems, stem3 as well as overlapping stems control. + * + ************************************************************************/ + + static + T1_Pos t1_hint_point( T1_Stem_Table* table, + T1_Pos coord, + T1_Fixed scale ) + { + T1_Int num_active = table->num_active; + T1_Int n; + T1_Stem_Hint* prev = 0; + T1_Stem_Hint* cur = 0; + T1_Edge* min; + T1_Edge* max; + T1_Pos delta; + + /* only hint when there is at least one stem defined */ + if (num_active <= 0) + return SCALE(coord); + + /* scan the stem table to determine placement of coordinate */ + /* relative to the list of sorted and stems */ + for ( n = 0; n < num_active; n++, prev = cur ) + { + cur = table->stems + table->sort[n]; + + /* is it on the left of the current edge ? */ + delta = cur->min_edge.orus - coord; + if ( delta == 0 ) return cur->min_edge.pix; + + if (delta > 0) + { + /* if this is the left of the first edge, simply shift */ + if (!prev) return cur->min_edge.pix - SCALE(delta); + + /* otherwise, interpolate between the maximum of the */ + /* previous stem, and the minimum of the current one */ + min = &prev->max_edge; + max = &cur->min_edge; + goto Interpolate; + } + + /* is it within the current edge ? */ + delta = cur->max_edge.orus - coord; + if ( delta == 0 ) return cur->max_edge.pix; + + if (delta > 0) + { + /* interpolate within the stem */ + min = &cur->min_edge; + max = &cur->max_edge; + goto Interpolate; + } + } + + /* apparently, this coordinate is on the right of the last stem */ + delta = coord - cur->max_edge.orus; + return cur->max_edge.pix + SCALE(delta); + + Interpolate: + return min->pix + + FT_MulDiv( coord - min->orus, + max->pix - min->pix, + max->orus - min->orus ); + } + + + + + + +#if 1 + +/************************************************************************ + * + * + * T1_Hint_Points + * + * + * this function grid-fits several points in a given Type 1 builder + * at once. + * + * + * builder :: handle to target Type 1 builder + * first :: first point to hint in builder's current outline + * last :: last point to hint in builder's current outline + * + ************************************************************************/ + + LOCAL_FUNC + void T1_Hint_Points( T1_Builder* builder ) + { + T1_Int first = builder->hint_point; + T1_Int last = builder->current.n_points-1; + + T1_Size size = builder->size; + T1_Fixed scale_x = size->root.metrics.x_scale; + T1_Fixed scale_y = size->root.metrics.y_scale; + + T1_Glyph_Hints* hints = builder->glyph->hints; + T1_Stem_Table* hori_stems = &hints->hori_stems; + T1_Stem_Table* vert_stems = &hints->vert_stems; + + T1_Vector* cur = builder->current.points + first; + T1_Vector* limit = cur + last - first + 1; + + /* first of all, sort the active stem hints */ + t1_sort_hints( hori_stems ); + t1_sort_hints( vert_stems ); + + for ( ; cur < limit; cur++ ) + { + cur->x = t1_hint_point( vert_stems, cur->x, scale_x ); + cur->y = t1_hint_point( hori_stems, cur->y, scale_y ); + } + + builder->hint_point = builder->current.n_points; + } + + +/************************************************************************ + * + * + * T1_Hint_Stems + * + * + * This function is used to compute the location of each stem hint + * between the first and second passes of the glyph loader on the + * charstring. + * + * + * builder :: handle to target builder + * + ************************************************************************/ + + LOCAL_FUNC + void T1_Hint_Stems( T1_Builder* builder ) + { + T1_Glyph_Hints* hints = builder->glyph->hints; + T1_Font* priv = &builder->face->type1; + + T1_Size size = builder->size; + T1_Fixed scale_x = size->root.metrics.x_scale; + T1_Fixed scale_y = size->root.metrics.y_scale; + + t1_hint_horizontal_stems( &hints->hori_stems, + builder->size->hints, + priv->blue_shift, + scale_y ); + + t1_hint_vertical_stems( &hints->vert_stems, + builder->size->hints, + scale_x ); + } + +#endif diff --git a/src/type1z/t1hinter.h b/src/type1z/t1hinter.h new file mode 100644 index 000000000..4bf753af9 --- /dev/null +++ b/src/type1z/t1hinter.h @@ -0,0 +1,380 @@ +/******************************************************************* + * + * t1hinter.h 1.2 + * + * Type1 hinter. + * + * Copyright 1996-1999 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + * The Hinter is in charge of fitting th scaled outline to the + * pixel grid in order to considerably improve the quality of + * the Type 1 font driver's output.. + * + ******************************************************************/ + +#ifndef T1HINTER_H +#define T1HINTER_H + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + + +/************************************************************************ + * + * + * T1_Snap_Zone + * + * + * A "snap zone" is used to model either a blue zone or a stem width + * at a given character size. It is made of a minimum and maximum + * edge, defined in 26.6 pixels, as well as one "original" and + * "scaled" position. + * + * the position corresponds to the stem width (for stem snap zones) + * or to the blue position (for blue zones) + * + * + * orus :: original position in font units + * pix :: current position in sub-pixel units + * min :: minimum boundary in sub-pixel units + * max :: maximim boundary in sub-pixel units + * + ************************************************************************/ + + typedef struct T1_Snap_Zone_ + { + T1_Pos orus; + T1_Pos pix; + T1_Pos min; + T1_Pos max; + + } T1_Snap_Zone; + + +/************************************************************************ + * + * + * T1_Edge + * + * + * A very simply structure used to model an stem edge + * + * + * orus :: original edge position in font units + * pix :: scaled edge position in sub-pixel units + * + ************************************************************************/ + + typedef struct T1_Edge_ + { + T1_Pos orus; + T1_Pos pix; + + } T1_Edge; + + +/************************************************************************ + * + * + * T1_Stem_Hint + * + * + * A simple structure used to model a stem hint + * + * + * min_edge :: the hint's minimum edge + * max_edge :: the hint's maximum edge + * hint_flags :: some flags describing the stem properties + * + * + * the min and max edges of a ghost stem have the same position, + * even if they're coded in a weird way in the charstrings + * + ************************************************************************/ + + typedef struct T1_Stem_Hint_ + { + T1_Edge min_edge; + T1_Edge max_edge; + T1_Int hint_flags; + + } T1_Stem_Hint; + + +#define T1_HINT_FLAG_ACTIVE 1 /* indicates an active stem */ +#define T1_HINT_FLAG_MIN_BORDER 2 /* unused for now.. */ +#define T1_HINT_FLAG_MAX_BORDER 4 /* unused for now.. */ + + +/* hinter's configuration constants */ +#define T1_HINTER_MAX_BLUES 24 /* maximum number of blue zones */ +#define T1_HINTER_MAX_SNAPS 16 /* maximum number of stem snap zones */ +#define T1_HINTER_MAX_EDGES 64 /* maximum number of stem hints */ + + +/************************************************************************ + * + * + * T1_Size_Hints + * + * + * A structure used to model the hinting information related to + * a size object + * + * + * supress_overshoots :: a boolean flag to tell when overshoot + * supression should occur. + * + * num_blue_zones :: the total number of blue zones (top+bottom) + * num_bottom_zones :: the number of bottom zones + * + * blue_zones :: the blue zones table. bottom zones are + * stored first in the table, followed by + * all top zones + * + * num_stem_snapH :: number of horizontal stem snap zones + * stem_snapH :: horizontal stem snap zones + * + * num_stem_snapV :: number of vertical stem snap zones + * stem_snapV :: vertical stem snap zones + * + ************************************************************************/ + + struct T1_Size_Hints_ + { + T1_Bool supress_overshoots; + + T1_Int num_blue_zones; + T1_Int num_bottom_zones; + T1_Snap_Zone blue_zones[ T1_HINTER_MAX_BLUES ]; + + T1_Int num_snap_widths; + T1_Snap_Zone snap_widths[ T1_HINTER_MAX_SNAPS ]; + + T1_Int num_snap_heights; + T1_Snap_Zone snap_heights[ T1_HINTER_MAX_SNAPS ]; + }; + + + +/************************************************************************ + * + * + * T1_Stem_Table + * + * + * A simple structure used to model a set of stem hints in a + * single direction during the loading of a given glyph outline. + * Not all stem hints are active at a time. Moreover, stems must + * be sorted regularly + * + * + * num_stems :: total number of stems in table + * num_active :: number of active stems in table + * + * stems :: the table of all stems + * sort :: a table of indices into the stems table, used + * to keep a sorted list of the active stems + * + ************************************************************************/ + + typedef struct T1_Stem_Table_ + { + T1_Int num_stems; + T1_Int num_active; + + T1_Stem_Hint stems[ T1_HINTER_MAX_EDGES ]; + T1_Int sort [ T1_HINTER_MAX_EDGES ]; + + } T1_Stem_Table; + + + +/************************************************************************ + * + * + * T1_Glyph_Hints + * + * + * A structure used to model the stem hints of a given glyph outline + * during glyph loading. + * + * + * hori_stems :: horizontal stem hints table + * vert_stems :: vertical stem hints table + * + ************************************************************************/ + + struct T1_Glyph_Hints_ + { + T1_Stem_Table hori_stems; + T1_Stem_Table vert_stems; + }; + + + +/************************************************************************ + * + * + * t1_hinter_funcs + * + * + * A table containing the address of various functions used during + * the loading of an hinted scaled outline + * + ************************************************************************/ + + LOCAL_DEF + const T1_Hinter_Funcs t1_hinter_funcs; + + +/************************************************************************ + * + * + * T1_New_Size_Hinter + * + * + * Allocates a new hinter structure for a given size object + * + * + * size :: handle to target size object + * + * + * Error code. 0 means success + * + ************************************************************************/ + + LOCAL_DEF + T1_Error T1_New_Size_Hinter( T1_Size size ); + + +/************************************************************************ + * + * + * T1_Done_Size_Hinter + * + * + * Releases a given size object's hinter structure + * + * + * size :: handle to target size object + * + ************************************************************************/ + + LOCAL_DEF + void T1_Done_Size_Hinter( T1_Size size ); + + +/************************************************************************ + * + * + * T1_Reset_Size_Hinter + * + * + * Recomputes hinting information when a given size object has + * changed its resolutions/char sizes/pixel sizes + * + * + * size :: handle to size object + * + * + * Error code. 0 means success + * + ************************************************************************/ + + LOCAL_DEF + T1_Error T1_Reset_Size_Hinter( T1_Size size ); + + +/************************************************************************ + * + * + * T1_New_Glyph_Hinter + * + * + * Allocates a new hinter structure for a given glyph slot + * + * + * glyph :: handle to target glyph slot + * + * + * Error code. 0 means success + * + ************************************************************************/ + + LOCAL_DEF + T1_Error T1_New_Glyph_Hinter( T1_GlyphSlot glyph ); + + +/************************************************************************ + * + * + * T1_Done_Glyph_Hinter + * + * + * Releases a given glyph slot's hinter structure + * + * + * glyph :: handle to glyph slot + * + ************************************************************************/ + + LOCAL_DEF + void T1_Done_Glyph_Hinter( T1_GlyphSlot glyph ); + + + + +/************************************************************************ + * + * + * T1_Hint_Points + * + * + * this function grid-fits several points in a given Type 1 builder + * at once. + * + * + * builder :: handle to target Type 1 builder + * + ************************************************************************/ + + LOCAL_DEF + void T1_Hint_Points( T1_Builder* builder ); + + +/************************************************************************ + * + * + * T1_Hint_Stems + * + * + * This function is used to compute the location of each stem hint + * between the first and second passes of the glyph loader on the + * charstring. + * + * + * builder :: handle to target builder + * + ************************************************************************/ + + LOCAL_DEF + void T1_Hint_Stems( T1_Builder* builder ); + +#ifdef __cplusplus + } +#endif + +#endif /* T1HINTER_H */ diff --git a/src/type1z/t1load.c b/src/type1z/t1load.c new file mode 100644 index 000000000..4acec0720 --- /dev/null +++ b/src/type1z/t1load.c @@ -0,0 +1,741 @@ +/******************************************************************* + * + * t1load.h 2.0 + * + * Type1 Loader. + * + * Copyright 1996-2000 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + * This is the new and improved Type 1 data loader for FreeType 2. + * The old loader has several problems: it is slow, complex, difficult + * to maintain, and contains incredible hacks to make it accept some + * ill-formed Type 1 fonts without hiccup-ing. Moreover, about 5% + * of the Type 1 fonts on my machine still aren't loaded correctly + * by it. + * + * This version is much simpler, much faster and also easier to + * read and maintain by a great order of magnitude. The idea behind + * it is to _not_ try to read the Type 1 token stream with a state + * machine (i.e. a Postscript-like interpreter) but rather to perform + * simple pattern-matching. + * + * Indeed, nearly all data definitions follow a simple pattern + * like : + * + * ..... /Field .... + * + * where can be a number, a boolean, a string, or an + * array of numbers. There are a few exceptions, namely the + * encoding, font name, charstrings and subrs and they are + * handled with a special pattern-matching routine. + * + * All other common cases are handled very simply. The matching + * rules are defined in the file "t1tokens.h" through the use + * of several macros calls PARSE_XXXX + * + * This file is included twice here, the first time to generate + * parsing callback functions, the second to generate a table + * of keywords (with pointers to the associated callback). + * + * The function "parse_dict" simply scans *linearly* a given + * dictionary (either the top-level or private one) and calls + * the appropriate callback when it encounters an immediate + * keyword. + * + * This is by far the fastest way one can find to parse and read + * all data :-) + * + * This led to tremendous code size reduction. Note that later, + * the glyph loader will also be _greatly_ simplified, and the + * automatic hinter will replace the clumsy "t1hinter".. + * + ******************************************************************/ + + #include + +#include +#include +#include +#include +#include + +#include + +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1load + + typedef void (*T1_Parse_Func)( T1_Face face, T1_Loader* loader ); + + typedef struct T1_KeyWord_ + { + const char* name; + T1_Parse_Func parsing; + + } T1_KeyWord; + + /* some handy macros used to easily define parsing callback functions */ + /* each callback is in charge of loading a value and storing it in a */ + /* given field of the Type 1 face.. */ + +#define PARSE_(x) static void parse_##x##( T1_Face face, T1_Loader* loader ) + +#define PARSE_STRING(s,x) PARSE_(x) \ + { \ + FACE.##x = T1_ToString(&loader->parser); \ + FT_TRACE2(( "type1.parse_##x##: \"%s\"\n", FACE.##x )); \ + } + +#define PARSE_INT(s,x) PARSE_(x) \ + { \ + FACE.##x = T1_ToInt(&loader->parser); \ + FT_TRACE2(( "type1.parse_##x##: \"%d\"\n", FACE.##x )); \ + } + +#define PARSE_BOOL(s,x) PARSE_(x) \ + { \ + FACE.##x = T1_ToBool(&loader->parser); \ + FT_TRACE2(( "type1.parse_##x##: \"%s\"\n", \ + FACE.##x ? "true" : "false" )); \ + } + +#define PARSE_FIXED(s,x) PARSE_(x) \ + { \ + FACE.##x = T1_ToFixed(&loader->parser,3); \ + FT_TRACE2(( "type1.parse_##x##: \"%f\"\n", FACE.##x/65536.0 )); \ + } + +#define PARSE_COORDS(s,c,m,x) PARSE_(x) \ + { \ + FACE.##c = T1_ToCoordArray(&loader->parser, m, (T1_Short*)FACE.##x ); \ + FT_TRACE2(( "type1.parse_##x##\n" )); \ + } + +#define PARSE_FIXEDS(s,c,m,x) PARSE_(x) \ + { \ + FACE.##c = T1_ToFixedArray(&loader->parser, m, (T1_Fixed*)FACE.##x, 3 ); \ + FT_TRACE2(( "type1.parse_##x##\n" )); \ + } + + +#define PARSE_COORDS2(s,m,x) PARSE_(x) \ + { \ + (void)T1_ToCoordArray( &loader->parser, m, (T1_Short*)&FACE.##x ); \ + FT_TRACE2(( "type1.parse_##x##\n" )); \ + } + +#define PARSE_FIXEDS2(s,m,x) PARSE_(x) \ + { \ + (void)T1_ToFixedArray(&loader->parser, m, (T1_Fixed*)&FACE.##x, 3 ); \ + FT_TRACE2(( "type1.parse_##x##\n" )); \ + } + + +/* define all parsing callbacks */ +#include + + + static + int is_space( char c ) + { + return ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ); + } + + static + int is_alpha( char c ) + { + return ( (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '.') || + (c == '_') ); + } + + static + void skip_whitespace( T1_Parser* parser ) + { + T1_Byte* cur = parser->cursor; + + while ( cur < parser->limit && is_space(*cur) ) + cur++; + + parser->cursor = cur; + } + + static + void skip_blackspace( T1_Parser* parser ) + { + T1_Byte* cur = parser->cursor; + + while ( cur < parser->limit && !is_space(*cur) ) + cur++; + + parser->cursor = cur; + } + + static + int read_binary_data( T1_Parser* parser, T1_Int *size, T1_Byte* *base ) + { + T1_Byte* cur; + T1_Byte* limit = parser->limit; + + /* the binary data has the following format */ + /* */ + /* "size" [white*] RD white ....... ND */ + /* */ + + skip_whitespace(parser); + cur = parser->cursor; + + if ( cur < limit && (T1_Byte)(*cur-'0') < 10 ) + { + *size = T1_ToInt(parser); + + skip_whitespace(parser); + skip_blackspace(parser); /* "RD" or "-|" or something else */ + + /* there is only one whitespace char after the */ + /* "RD" or "-|" token */ + *base = parser->cursor + 1; + + parser->cursor += *size+1; + return 1; + } + + FT_ERROR(( "type1.read_binary_data: invalid size field\n" )); + parser->error = FT_Err_Invalid_File_Format; + return 0; + } + + + /* we will now define the routines used to handle */ + /* the /Encoding, /Subrs and /CharStrings */ + /* dictionaries.. */ + + static + void parse_font_name( T1_Face face, T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + FT_Error error; + FT_Memory memory = parser->memory; + T1_Int len; + T1_Byte* cur; + T1_Byte* cur2; + T1_Byte* limit; + + skip_whitespace(parser); + cur = parser->cursor; + limit = parser->limit; + if ( cur >= limit-1 || *cur != '/' ) return; + + cur++; + cur2 = cur; + while (cur2 < limit && is_alpha(*cur2)) cur2++; + len = cur2-cur; + if (len > 0) + { + if ( ALLOC( face->type1.font_name, len+1 ) ) + { + parser->error = error; + return; + } + + MEM_Copy( face->type1.font_name, cur, len ); + face->type1.font_name[len] = '\0'; + } + parser->cursor = cur2; + } + + static + void parse_font_bbox( T1_Face face, T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + T1_Short temp[4]; + T1_BBox* bbox = &face->type1.font_bbox; + + (void)T1_ToCoordArray( parser, 4, temp ); + bbox->xMin = temp[0]; + bbox->yMin = temp[1]; + bbox->xMax = temp[2]; + bbox->yMax = temp[3]; + } + + static + void parse_encoding( T1_Face face, T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + T1_Byte* cur = parser->cursor; + T1_Byte* limit = parser->limit; + + /* skip whitespace */ + while (is_space(*cur)) + { + cur++; + if (cur >= limit) + { + FT_ERROR(( "type1.parse_encoding: out of bounds !!\n" )); + parser->error = FT_Err_Invalid_File_Format; + return; + } + } + + /* if we have a number, then the encoding is an array, */ + /* and we must load it now */ + if ((T1_Byte)(*cur - '0') < 10) + { + T1_Encoding* encode = &face->type1.encoding; + T1_Int count, n; + T1_Table* char_table = &loader->encoding_table; + FT_Memory memory = parser->memory; + FT_Error error; + + /* read the number of entries in the encoding, should be 256 */ + count = T1_ToInt( parser ); + if (parser->error) return; + + /* we use a T1_Table to store our charnames */ + encode->num_chars = count; + if ( ALLOC_ARRAY( encode->char_index, count, T1_Short ) || + ALLOC_ARRAY( encode->char_name, count, T1_String* ) || + (error = T1_New_Table( char_table, count, memory )) != 0 ) + { + parser->error = error; + return; + } + + /* now, we will need to read a record of the form */ + /* ... charcode /charname ... for each entry in our table */ + /* */ + /* we simply look for a number followed by an immediate */ + /* name. Note that this ignores correctly the sequence */ + /* that is often seen in type1 fonts : */ + /* */ + /* 0 1 255 { 1 index exch /.notdef put } for dup */ + /* */ + /* used to clean the encoding array before anything else */ + /* */ + /* we stop when we encounter a "def" */ + /* */ + + cur = parser->cursor; + limit = parser->limit; + n = 0; + + for ( ; cur < limit; ) + { + T1_Byte c; + + c = *cur; + + /* we stop when we encounter a 'def' */ + if ( c == 'd' && cur+3 < limit ) + { + if ( cur[1] == 'e' && + cur[2] == 'f' && + is_space(cur[3]) ) + break; + } + + /* otherwise, we must find a number before anything else */ + if ( (T1_Byte)(c-'0') < 10 ) + { + T1_Int charcode; + + parser->cursor = cur; + charcode = T1_ToInt(parser); + cur = parser->cursor; + + /* skip whitespace */ + while (cur < limit && is_space(*cur)) cur++; + + if (cur < limit && *cur == '/') + { + /* bingo, we have an immediate name - it must be a */ + /* character name */ + FT_Byte* cur2 = cur+1; + T1_Int len; + + while (cur2 < limit && is_alpha(*cur2)) cur2++; + len = cur2-cur-1; + parser->error = T1_Add_Table( char_table, charcode, cur+1, len+1 ); + char_table->elements[charcode][len] = '\0'; + if (parser->error) return; + + cur = cur2; + } + } + else + cur++; + } + + face->type1.encoding_type = t1_encoding_array; + } + /* Otherwise, we should have either "StandardEncoding" or */ + /* "ExpertEncoding" */ + else + { + if ( cur+17 < limit && strncmp( cur, "StandardEncoding", 16 ) == 0 ) + face->type1.encoding_type = t1_encoding_standard; + + else if (cur+15 < limit && strncmp( cur, "ExpertEncoding", 14 ) == 0 ) + face->type1.encoding_type = t1_encoding_expert; + + else + { + FT_ERROR(( "type1.parse_encoding: invalid token !!\n" )); + parser->error = FT_Err_Invalid_File_Format; + } + } + } + + + static + void parse_subrs( T1_Face face, T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + T1_Table* table = &loader->subrs; + FT_Memory memory = parser->memory; + FT_Error error; + T1_Int n; + + loader->num_subrs = T1_ToInt( parser ); + if (parser->error) return; + + /* initialise subrs array */ + error = T1_New_Table( table, loader->num_subrs, memory ); + if (error) goto Fail; + + /* the format is simple : */ + /* */ + /* "index" + binary data */ + /* */ + + for ( n = 0; n < loader->num_subrs; n++ ) + { + T1_Int index, size; + T1_Byte* base; + + index = T1_ToInt(parser); + if (!read_binary_data(parser,&size,&base)) return; + + error = T1_Add_Table( table, index, base, size ); + if (error) goto Fail; + } + return; + + Fail: + parser->error = error; + } + + + + + static + void parse_charstrings( T1_Face face, T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + T1_Table* code_table = &loader->charstrings; + T1_Table* name_table = &loader->glyph_names; + FT_Memory memory = parser->memory; + FT_Error error; + + T1_Byte* cur; + T1_Byte* limit = parser->limit; + T1_Int n; + + loader->num_glyphs = T1_ToInt( parser ); + if (parser->error) return; + + /* initialise tables */ + error = T1_New_Table( code_table, loader->num_glyphs, memory ) || + T1_New_Table( name_table, loader->num_glyphs, memory ); + if (error) goto Fail; + + n = 0; + for ( ;; ) + { + T1_Int size; + T1_Byte* base; + + /* the format is simple : */ + /* "/glyphname" + binary data */ + /* */ + /* note that we stop when we find a "def" */ + /* */ + skip_whitespace(parser); + cur = parser->cursor; + if (cur >= limit) break; + + /* we stop when we find a "def" */ + if (*cur == 'd' && + cur+3 < limit && + cur[1] == 'e' && + cur[2] == 'f' ) + break; + + if (*cur != '/') + skip_blackspace(parser); + else + { + T1_Byte* cur2 = cur+1; + T1_Int len; + + while (cur2 < limit && is_alpha(*cur2)) cur2++; + len = cur2-cur-1; + error = T1_Add_Table( name_table, n, cur+1, len+1 ); + if (error) goto Fail; + + /* add a trailing zero to the name table */ + name_table->elements[n][len] = '\0'; + + parser->cursor = cur2; + if (!read_binary_data(parser,&size,&base)) return; + + error = T1_Add_Table( code_table, n, base, size ); + if (error) goto Fail; + + n++; + if (n >= loader->num_glyphs) + break; + } + } + return; + + Fail: + parser->error = error; + } + + +#undef PARSE_STRING +#undef PARSE_INT +#undef PARSE_BOOL +#undef PARSE_FIXED +#undef PARSE_COORDS +#undef PARSE_FIXEDS +#undef PARSE_COORDS2 +#undef PARSE_FIXEDS2 + +#undef PARSE_ +#define PARSE_(s,x) { s, parse_##x }, + +#define PARSE_STRING(s,x) PARSE_(s,x) +#define PARSE_INT(s,x) PARSE_(s,x) +#define PARSE_BOOL(s,x) PARSE_(s,x) +#define PARSE_FIXED(s,x) PARSE_(s,x) +#define PARSE_COORDS(s,c,m,x) PARSE_(s,x) +#define PARSE_FIXEDS(s,c,m,x) PARSE_(s,x) +#define PARSE_COORDS2(s,m,x) PARSE_(s,x) +#define PARSE_FIXEDS2(s,m,x) PARSE_(s,x) + + static + const T1_KeyWord t1_keywords[] = + { +#include + /* now add the special functions... */ + { "FontName", parse_font_name }, + { "FontBBox", parse_font_bbox }, + { "Encoding", parse_encoding }, + { "Subrs", parse_subrs }, + { "CharStrings", parse_charstrings }, + { 0, 0 } + }; + + + static + T1_Error parse_dict( T1_Face face, + T1_Loader* loader, + T1_Byte* base, + T1_Int size ) + { + T1_Parser* parser = &loader->parser; + + parser->cursor = base; + parser->limit = base + size; + parser->error = 0; + + { + T1_Byte* cur = base; + T1_Byte* limit = cur + size; + + for ( ;cur < limit; cur++ ) + { + /* look for immediates */ + if (*cur == '/' && cur+2 < limit) + { + T1_Byte* cur2; + T1_Int len; + + cur ++; + cur2 = cur; + while (cur2 < limit && is_alpha(*cur2)) cur2++; + len = cur2-cur; + + if (len > 0) + { + /* now, compare the immediate name to the keyword table */ + T1_KeyWord* keyword = (T1_KeyWord*)t1_keywords; + + for (;;) + { + T1_Byte* name; + + name = (T1_Byte*)keyword->name; + if (!name) break; + + if (cur[0] == name[0] && len == strlen(name) ) + { + T1_Int n; + for ( n = 1; n < len; n++ ) + if (cur[n] != name[n]) + break; + + if (n >= len) + { + /* we found it - run the parsing callback !! */ + parser->cursor = cur2; + skip_whitespace( parser ); + keyword->parsing( face, loader ); + if (parser->error) + return parser->error; + + cur = parser->cursor; + } + } + keyword++; + } + } + } + } + } + return parser->error; + } + + static + void t1_init_loader( T1_Loader* loader, T1_Face face ) + { + loader->num_glyphs = 0; + loader->num_chars = 0; + + loader->encoding_table.init = 0; + loader->charstrings.init = 0; + loader->glyph_names.init = 0; + loader->subrs.init = 0; + } + + static + void t1_done_loader( T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + + /* finalize tables */ + T1_Release_Table( &loader->encoding_table ); + T1_Release_Table( &loader->charstrings ); + T1_Release_Table( &loader->glyph_names ); + T1_Release_Table( &loader->subrs ); + + /* finalize parser */ + T1_Done_Parser( parser ); + } + + LOCAL_FUNC + T1_Error T1_Open_Face( T1_Face face ) + { + T1_Loader loader; + T1_Parser* parser; + T1_Font* type1 = &face->type1; + FT_Error error; + + t1_init_loader( &loader, face ); + + /* default lenIV */ + type1->lenIV = 4; + + parser = &loader.parser; + error = T1_New_Parser( parser, face->root.stream, face->root.memory ); + if (error) goto Exit; + + error = parse_dict( face, &loader, parser->base_dict, parser->base_len ); + if (error) goto Exit; + + error = T1_Get_Private_Dict( parser ); + if (error) goto Exit; + + error = parse_dict( face, &loader, parser->private_dict, parser->private_len ); + if (error) goto Exit; + + /* now, propagate the subrs, charstrings and glyphnames tables */ + /* to the Type1 data */ + type1->num_glyphs = loader.num_glyphs; + + loader.subrs.init = 0; + type1->num_subrs = loader.num_subrs; + type1->subrs_block = loader.subrs.block; + type1->subrs = loader.subrs.elements; + type1->subrs_len = loader.subrs.lengths; + + loader.charstrings.init = 0; + type1->charstrings_block = loader.charstrings.block; + type1->charstrings = loader.charstrings.elements; + type1->charstrings_len = loader.charstrings.lengths; + + /* we copy the glyph names "block" and "elements" fields */ + /* but the "lengths" field must be released later.. */ + type1->glyph_names_block = loader.glyph_names.block; + type1->glyph_names = (T1_String**)loader.glyph_names.elements; + loader.glyph_names.block = 0; + loader.glyph_names.elements = 0; + + /* we must now build type1.encoding when we have a custom */ + /* array.. */ + if ( type1->encoding_type == t1_encoding_array ) + { + T1_Int charcode, index, min_char, max_char; + T1_Byte* char_name; + T1_Byte* glyph_name; + + /* OK, we do the following : for each element in the encoding */ + /* table, lookup the index of the glyph having the same name */ + /* the index is then stored in type1.encoding.char_index, and */ + /* a the name to type1.encoding.char_name */ + + min_char = +32000; + max_char = -32000; + + charcode = 0; + for ( ; charcode < loader.encoding_table.num_elems; charcode++ ) + { + type1->encoding.char_index[charcode] = 0; + type1->encoding.char_name [charcode] = ".notdef"; + + char_name = loader.encoding_table.elements[charcode]; + if (char_name) + for ( index = 0; index < type1->num_glyphs; index++ ) + { + glyph_name = type1->glyph_names[index]; + if ( strcmp( char_name, glyph_name ) == 0 ) + { + type1->encoding.char_index[charcode] = index; + type1->encoding.char_name [charcode] = glyph_name; + + if (charcode < min_char) min_char = charcode; + if (charcode > max_char) max_char = charcode; + break; + } + } + } + type1->encoding.code_first = min_char; + type1->encoding.code_last = max_char; + type1->encoding.num_chars = loader.num_chars; + } + + Exit: + t1_done_loader( &loader ); + return error; + } diff --git a/src/type1z/t1load.h b/src/type1z/t1load.h new file mode 100644 index 000000000..6a1a45c03 --- /dev/null +++ b/src/type1z/t1load.h @@ -0,0 +1,56 @@ +/******************************************************************* + * + * t1load.h 2.0 + * + * Type1 Loader. + * + * Copyright 1996-2000 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#ifndef T1LOAD_H +#define T1LOAD_H + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + + typedef struct T1_Loader_ + { + T1_Parser parser; /* parser used to read the stream */ + + T1_Int num_chars; /* number of characters in encoding */ + T1_Table encoding_table; /* T1_Table used to store the */ + /* encoding character names */ + + T1_Int num_glyphs; + T1_Table glyph_names; + T1_Table charstrings; + + T1_Int num_subrs; + T1_Table subrs; + + } T1_Loader; + + LOCAL_DEF + T1_Error T1_Open_Face( T1_Face face ); + + +#ifdef __cplusplus + } +#endif + +#endif /* T1LOAD_H */ + + +/* END */ diff --git a/src/type1z/t1objs.c b/src/type1z/t1objs.c new file mode 100644 index 000000000..0d59b0f7c --- /dev/null +++ b/src/type1z/t1objs.c @@ -0,0 +1,404 @@ +/******************************************************************* + * + * t1objs.c 1.0 + * + * Type1 Objects manager. + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#include +#include + +#include +#include + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER +#include +#endif + +/* Required by tracing mode */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1objs + +/******************************************************************* + * * + * SIZE FUNCTIONS * + * * + * * + *******************************************************************/ + +/******************************************************************* + * + * T1_Done_Size + * + * + * The TrueDoc instance object destructor. Used to discard + * a given instance object.. + * + * + * instance :: handle to the target instance object + * + * + * TrueDoc error code. 0 means success + * + ******************************************************************/ + + LOCAL_FUNC + void T1_Done_Size( T1_Size size ) + { + if (size) + { +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + T1_Done_Size_Hinter( size ); +#endif + size->valid = 0; + } + } + + +/******************************************************************* + * + * T1_Init_Size + * + * + * The instance object constructor + * + * + * instance : handle to new instance object + * face : pointer to parent face object + * + * + * TrueDoc error code. 0 means success. + * + ******************************************************************/ + + LOCAL_DEF + T1_Error T1_Init_Size( T1_Size size ) + { + T1_Error error; + + size->valid = 0; + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + error = T1_New_Size_Hinter( size ); + return error; +#else + (void)error; + return T1_Err_Ok; +#endif + } + + +/******************************************************************* + * + * T1_Reset_Size + * + * + * Resets an instance to a new pointsize/transform. + * This function is in charge of resetting the blue zones, + * As well as the stem snap tables for a given size.. + * + * + * instance the instance object to destroy + * + * + * Error code. + * + ******************************************************************/ + + LOCAL_FUNC + T1_Error T1_Reset_Size( T1_Size size ) + { +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + return T1_Reset_Size_Hinter( size ); +#else + (void)size; + return 0; +#endif + } + + +/******************************************************************* + * * + * FACE FUNCTIONS * + * * + * * + *******************************************************************/ + +/******************************************************************* + * + * T1_Done_Face + * + * + * The face object destructor. + * + * + * face :: typeless pointer to the face object to destroy + * + * + * Error code. + * + ******************************************************************/ + + LOCAL_FUNC + void T1_Done_Face( T1_Face face ) + { + FT_Memory memory; + + if (face) + { + memory = face->root.memory; + /* XXXX : TO DO */ + } + } + +/******************************************************************* + * + * T1_Init_Face + * + * + * The face object constructor. + * + * + * face :: face record to build + * Input :: input stream where to load font data + * + * + * Error code. + * + ******************************************************************/ + + LOCAL_FUNC + T1_Error T1_Init_Face( FT_Stream stream, + FT_Int face_index, + T1_Face face ) + { + T1_Error error; + + (void)face_index; + (void)face; + + face->root.num_faces = 1; + + /* open the tokenizer, this will also check the font format */ + error = T1_Open_Face( face ); + if (error) goto Exit; + + /* if we just wanted to check the format, leave successfully now */ + if (face_index < 0) + goto Exit; + + /* check the face index */ + if ( face_index != 0 ) + { + FT_ERROR(( "T1.Init_Face : invalid face index\n" )); + error = T1_Err_Invalid_Argument; + goto Exit; + } + + /* Now, load the font program into the face object */ + { + /* Init the face object fields */ + /* Now set up root face fields */ + { + FT_Face root = (FT_Face)&face->root; + + root->num_glyphs = face->type1.num_glyphs; + root->num_charmaps = 1; + + root->face_index = face_index; + root->face_flags = FT_FACE_FLAG_SCALABLE; + + root->face_flags |= FT_FACE_FLAG_HORIZONTAL; + + if ( face->type1.is_fixed_pitch ) + root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; + + /* XXX : TO DO - add kerning with .afm support */ + + /* get style name - be careful, some broken fonts only */ + /* have a /FontName dictionary entry .. !! */ + root->family_name = face->type1.family_name; + if (root->family_name) + { + char* full = face->type1.full_name; + char* family = root->family_name; + + while ( *family && *full == *family ) + { + family++; + full++; + } + + root->style_name = ( *full == ' ' ? full+1 : "Regular" ); + } + else + { + /* do we have a /FontName ?? */ + if (face->type1.font_name) + { + root->family_name = face->type1.font_name; + root->style_name = "Regular"; + } + } + + /* no embedded bitmap support */ + root->num_fixed_sizes = 0; + root->available_sizes = 0; + + root->bbox = face->type1.font_bbox; + root->units_per_EM = 1000; + root->ascender = (T1_Short)face->type1.font_bbox.yMax; + root->descender = -(T1_Short)face->type1.font_bbox.yMin; + root->height = ((root->ascender + root->descender)*12)/10; + + /* now compute the maximum advance width */ + + root->max_advance_width = face->type1.standard_width; + + /* compute max advance width for proportional fonts */ + if (!face->type1.is_fixed_pitch) + { + T1_Int max_advance; + + error = T1_Compute_Max_Advance( face, &max_advance ); + + /* in case of error, keep the standard width */ + if (!error) + root->max_advance_width = max_advance; + else + error = 0; /* clear error */ + } + + root->max_advance_height = root->height; + + root->underline_position = face->type1.underline_position; + root->underline_thickness = face->type1.underline_thickness; + + root->max_points = 0; + root->max_contours = 0; + } + } + + Exit: + return error; + } + + +/******************************************************************* + * + * Function : Glyph_Destroy + * + * Description : The glyph object destructor. + * + * Input : _glyph typeless pointer to the glyph record to destroy + * + * Output : Error code. + * + ******************************************************************/ + + LOCAL_FUNC + void T1_Done_GlyphSlot( T1_GlyphSlot glyph ) + { + FT_Memory memory = glyph->root.face->memory; + FT_Library library = glyph->root.face->driver->library; + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + T1_Done_Glyph_Hinter( glyph ); +#endif + /* the bitmaps are created on demand */ + FREE( glyph->root.bitmap.buffer ); + FT_Done_Outline( library, &glyph->root.outline ); + return; + } + + +/******************************************************************* + * + * Function : Glyph_Create + * + * Description : The glyph object constructor. + * + * Input : glyph glyph record to build. + * face the glyph's parent face. + * + * Output : Error code. + * + ******************************************************************/ + + LOCAL_FUNC + T1_Error T1_Init_GlyphSlot( T1_GlyphSlot glyph ) + { + FT_Library library = glyph->root.face->driver->library; + T1_Error error; + + glyph->max_points = 0; + glyph->max_contours = 0; + glyph->root.bitmap.buffer = 0; + + error = FT_New_Outline( library, 0, 0, &glyph->root.outline ); + if (error) return error; + +#ifndef T1_CONFIG_OPTION_DISABLE_HINTER + error = T1_New_Glyph_Hinter( glyph ); + if (error) + FT_Done_Outline( library, &glyph->root.outline ); +#endif + + return error; + } + + +/******************************************************************* + * + * T1_Init_Driver + * + * + * Initialise a given Type 1 driver object + * + * + * driver :: handle to target driver object + * + * + * Error code. + * + ******************************************************************/ + + LOCAL_FUNC + T1_Error T1_Init_Driver( T1_Driver driver ) + { + (void)driver; + return T1_Err_Ok; + } + + + +/******************************************************************* + * + * T1_Done_Driver + * + * + * finalise a given Type 1 driver + * + * + * driver :: handle to target Type 1 driver + * + ******************************************************************/ + + LOCAL_DEF + void T1_Done_Driver( T1_Driver driver ) + { + (void)driver; + } + + +/* END */ diff --git a/src/type1z/t1objs.h b/src/type1z/t1objs.h new file mode 100644 index 000000000..417ae5380 --- /dev/null +++ b/src/type1z/t1objs.h @@ -0,0 +1,302 @@ +/******************************************************************* + * + * t1objs.h 1.0 + * + * Type1 objects definition. + * + * Copyright 1996-1999 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + ******************************************************************/ + +#ifndef T1OBJS_H +#define T1OBJS_H + +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + + /* The following structures must be defined by the hinter */ + typedef struct T1_Size_Hints_ T1_Size_Hints; + typedef struct T1_Glyph_Hints_ T1_Glyph_Hints; + + /***********************************************************************/ + /* */ + /* T1_Driver */ + /* */ + /* */ + /* A handle to a Type 1 driver object. */ + /* */ + typedef struct T1_DriverRec_ *T1_Driver; + + + /***********************************************************************/ + /* */ + /* T1_Size */ + /* */ + /* */ + /* A handle to a Type 1 size object. */ + /* */ + typedef struct T1_SizeRec_* T1_Size; + + + /***********************************************************************/ + /* */ + /* T1_GlyphSlot */ + /* */ + /* */ + /* A handle to a Type 1 glyph slot object. */ + /* */ + typedef struct T1_GlyphSlotRec_* T1_GlyphSlot; + + + /***********************************************************************/ + /* */ + /* T1_CharMap */ + /* */ + /* */ + /* A handle to a Type 1 character mapping object. */ + /* */ + /* */ + /* The Type 1 format doesn't use a charmap but an encoding table. */ + /* The driver is responsible for making up charmap objects */ + /* corresponding to these tables.. */ + /* */ + typedef struct T1_CharMapRec_* T1_CharMap; + + + + /**************************************************************************/ + /* */ + /* NOW BEGINS THE TYPE1 SPECIFIC STUFF .............................. */ + /* */ + /**************************************************************************/ + + + /***************************************************/ + /* */ + /* T1_Size : */ + /* */ + /* Type 1 size record.. */ + /* */ + + typedef struct T1_SizeRec_ + { + FT_SizeRec root; + T1_Bool valid; + T1_Size_Hints* hints; /* defined in the hinter. This allows */ + /* us to experiment with different */ + /* hinting schemes without having to */ + /* change 't1objs' each time.. */ + } T1_SizeRec; + + + + /***************************************************/ + /* */ + /* T1_GlyphSlot : */ + /* */ + /* TrueDoc glyph record.. */ + /* */ + + typedef struct T1_GlyphSlotRec_ + { + FT_GlyphSlotRec root; + + T1_Bool hint; + T1_Bool scaled; + + T1_Int max_points; + T1_Int max_contours; + + FT_Fixed x_scale; + FT_Fixed y_scale; + + T1_Glyph_Hints* hints; /* defined in the hinter */ + + } T1_GlyphSlotRec; + + +/******************************************************************* + * + * T1_Init_Face + * + * + * Initialise a given Type 1 face object + * + * + * face_index :: index of font face in resource + * resource :: source font resource + * face :: face record to build + * + * + * Error code. + * + ******************************************************************/ + + LOCAL_DEF + T1_Error T1_Init_Face( FT_Stream stream, + FT_Int face_index, + T1_Face face ); + + + +/******************************************************************* + * + * T1_Done_Face + * + * + * Finalise a given face object + * + * + * face :: handle to the face object to destroy + * + ******************************************************************/ + + LOCAL_DEF + void T1_Done_Face( T1_Face face ); + + + +/******************************************************************* + * + * T1_Init_Size + * + * + * Initialise a new Type 1 size object + * + * + * size :: handle to size object + * + * + * Type 1 error code. 0 means success. + * + ******************************************************************/ + + LOCAL_DEF + T1_Error T1_Init_Size( T1_Size size ); + + + +/******************************************************************* + * + * T1_Done_Size + * + * + * The Type 1 size object finaliser. + * + * + * size :: handle to the target size object. + * + ******************************************************************/ + + LOCAL_DEF + void T1_Done_Size( T1_Size size ); + + +/******************************************************************* + * + * T1_Reset_Size + * + * + * Reset a Type 1 size when resolutions and character dimensions + * have been changed.. + * + * + * size :: handle to the target size object. + * + ******************************************************************/ + + LOCAL_DEF + T1_Error T1_Reset_Size( T1_Size size ); + + + +/******************************************************************* + * + * T1_Init_GlyphSlot + * + * The TrueType glyph slot initialiser + * + * glyph :: glyph record to build. + * + * Error code. + * + ******************************************************************/ + + LOCAL_DEF + T1_Error T1_Init_GlyphSlot( T1_GlyphSlot slot ); + + + +/******************************************************************* + * + * T1_Done_GlyphSlot + * + * The Type 1 glyph slot finaliser + * + * glyph :: handle to glyph slot object + * + * Error code. + * + ******************************************************************/ + + LOCAL_DEF + void T1_Done_GlyphSlot( T1_GlyphSlot slot ); + + + +/******************************************************************* + * + * T1_Init_Driver + * + * + * Initialise a given Type 1 driver object + * + * + * driver :: handle to target driver object + * + * + * Error code. + * + ******************************************************************/ + + LOCAL_DEF + T1_Error T1_Init_Driver( T1_Driver driver ); + + + +/******************************************************************* + * + * T1_Done_Driver + * + * + * finalise a given Type 1 driver + * + * + * driver :: handle to target Type 1 driver + * + ******************************************************************/ + + LOCAL_DEF + void T1_Done_Driver( T1_Driver driver ); + +#ifdef __cplusplus + } +#endif + +#endif /* T1OBJS_H */ + + +/* END */ diff --git a/src/type1z/t1parse.c b/src/type1z/t1parse.c new file mode 100644 index 000000000..a326d3cf7 --- /dev/null +++ b/src/type1z/t1parse.c @@ -0,0 +1,901 @@ +/******************************************************************* + * + * t1parse.c 2.0 + * + * Type1 parser. + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * The Type 1 parser is in charge of the following: + * + * - provide an implementation of a growing sequence of + * objects called a T1_Table (used to build various tables + * needed by the loader). + * + * - opening .pfb and .pfa files to extract their top-level + * and private dictionaries + * + * - read numbers, arrays & strings from any dictionary + * + * See "t1load.c" to see how data is loaded from the font file + * + ******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#undef FT_COMPONENT +#define FT_COMPONENT trace_t1load + +/*************************************************************************/ +/* */ +/* T1_New_Table */ +/* */ +/* */ +/* Initialise a T1_Table. */ +/* */ +/* */ +/* table :: address of target table */ +/* count :: table size = maximum number of elements */ +/* memory :: memory object to use for all subsequent reallocations */ +/* */ +/* */ +/* Error code. 0 means success */ +/* */ + + LOCAL_FUNC + T1_Error T1_New_Table( T1_Table* table, + T1_Int count, + FT_Memory memory ) + { + T1_Error error; + + table->memory = memory; + if ( ALLOC_ARRAY( table->elements, count, T1_Byte* ) || + ALLOC_ARRAY( table->lengths, count, T1_Byte* ) ) + goto Exit; + + table->max_elems = count; + table->init = 0xdeadbeef; + table->num_elems = 0; + table->block = 0; + table->capacity = 0; + table->cursor = 0; + + Exit: + if (error) FREE(table->elements); + + return error; + } + + + +/*************************************************************************/ +/* */ +/* T1_Add_Table */ +/* */ +/* */ +/* Adds an object to a T1_Table, possibly growing its memory block */ +/* */ +/* */ +/* table :: target table */ +/* index :: index of object in table */ +/* object :: address of object to copy in memory */ +/* length :: length in bytes of source object */ +/* */ +/* */ +/* Error code. 0 means success. An error is returned when a */ +/* realloc failed.. */ +/* */ + + + static void shift_elements( T1_Table* table, T1_Byte* old_base ) + { + T1_Long delta = table->block - old_base; + T1_Byte** offset = table->elements; + T1_Byte** limit = offset + table->max_elems; + + if (delta) + for ( ; offset < limit; offset++ ) + { + if (offset[0]) + offset[0] += delta; + } + } + + static + T1_Error reallocate_t1_table( T1_Table* table, + T1_Int new_size ) + { + FT_Memory memory = table->memory; + T1_Byte* old_base = table->block; + T1_Error error; + + /* realloc the base block */ + if ( REALLOC( table->block, table->capacity, new_size ) ) + return error; + + table->capacity = new_size; + + /* shift all offsets when needed */ + if (old_base) + shift_elements( table, old_base ); + + return T1_Err_Ok; + } + + + + LOCAL_FUNC + T1_Error T1_Add_Table( T1_Table* table, + T1_Int index, + void* object, + T1_Int length ) + { + if (index < 0 || index > table->max_elems) + { + FT_ERROR(( "T1.Add_Table: invalid index\n" )); + return T1_Err_Syntax_Error; + } + + /* grow the base block if needed */ + if ( table->cursor + length > table->capacity ) + { + T1_Error error; + T1_Int new_size = table->capacity; + + while ( new_size < table->cursor+length ) + new_size += 1024; + + error = reallocate_t1_table( table, new_size ); + if (error) return error; + } + + /* add the object to the base block and adjust offset */ + table->elements[ index ] = table->block + table->cursor; + table->lengths [ index ] = length; + MEM_Copy( table->block + table->cursor, object, length ); + + table->cursor += length; + return T1_Err_Ok; + } + + +/*************************************************************************/ +/* */ +/* T1_Done_Table */ +/* */ +/* */ +/* Finalise a T1_Table. (realloc it to its current cursor). */ +/* */ +/* */ +/* table :: target table */ +/* */ +/* */ +/* This function does NOT release the heap's memory block. It is up */ +/* to the caller to clean it, or reference it in its own structures. */ +/* */ + LOCAL_FUNC + void T1_Done_Table( T1_Table* table ) + { + FT_Memory memory = table->memory; + T1_Error error; + T1_Byte* old_base; + + /* should never fail, as rec.cursor <= rec.size */ + old_base = table->block; + if (!old_base) + return; + + (void)REALLOC( table->block, table->capacity, table->cursor ); + table->capacity = table->cursor; + + if (old_base != table->block) + shift_elements( table, old_base ); + } + + + LOCAL_FUNC + void T1_Release_Table( T1_Table* table ) + { + FT_Memory memory = table->memory; + + if (table->init == 0xdeadbeef) + { + FREE( table->block ); + FREE( table->elements ); + FREE( table->lengths ); + table->init = 0; + } + } + + static + T1_Long t1_toint( T1_Byte* *cursor, + T1_Byte* limit ) + { + T1_Long result = 0; + T1_Byte* cur = *cursor; + T1_Byte c, d; + + for (; cur < limit; cur++) + { + c = *cur; + d = (T1_Byte)(c - '0'); + if (d < 10) break; + + if ( c=='-' ) + { + cur++; + break; + } + } + + if (cur < limit) + { + do + { + d = (T1_Byte)(cur[0] - '0'); + if (d >= 10) + break; + + result = result*10 + d; + cur++; + + } while (cur < limit); + + if (c == '-') + result = -result; + } + + *cursor = cur; + return result; + } + + + static + T1_Long t1_tofixed( T1_Byte* *cursor, + T1_Byte* limit, + T1_Long power_ten ) + { + T1_Byte* cur = *cursor; + T1_Long num, divider, result; + T1_Int sign = 0; + T1_Byte d; + + if (cur >= limit) return 0; + + /* first of all, read the integer part */ + result = t1_toint( &cur, limit ) << 16; + num = 0; + divider = 1; + + if (result < 0) + { + sign = 1; + result = -result; + } + if (cur >= limit) goto Exit; + + /* read decimal part, if any */ + if (*cur == '.' && cur+1 < limit) + { + cur++; + + for (;;) + { + d = (T1_Byte)(*cur - '0'); + if (d >= 10) break; + + if (divider < 10000000L) + { + num = num*10 + d; + divider *= 10; + } + cur++; + if (cur >= limit) break; + } + } + + /* read exponent, if any */ + if ( cur+1 < limit && (*cur == 'e' || *cur == 'E')) + { + cur++; + power_ten += t1_toint( &cur, limit ); + } + + Exit: + /* raise to power of ten if needed */ + while (power_ten > 0) + { + result = result*10; + num = num*10; + power_ten--; + } + + while (power_ten < 0) + { + result = result/10; + divider = divider*10; + power_ten++; + } + + if (num) + result += FT_DivFix( num, divider ); + + if (sign) + result = -result; + + *cursor = cur; + return result; + } + + + static + T1_Int t1_tocoordarray( T1_Byte* *cursor, + T1_Byte* limit, + T1_Int max_coords, + T1_Short* coords ) + { + T1_Byte* cur = *cursor; + T1_Int count = 0; + T1_Byte c, ender; + + if (cur >= limit) goto Exit; + + /* check for the beginning of an array. If not, only one number will be read */ + c = *cur; + ender = 0; + + if (c == '[') + ender = ']'; + + if (c == '{') + ender = '}'; + + if (ender) + cur++; + + /* now, read the coordinates */ + for ( ; cur < limit; cur++ ) + { + c = *cur; + if (count >= max_coords || c == ender) + break; + + coords[count] = (T1_Short)(t1_tofixed(&cur,limit,0) >> 16); + count++; + + if (!ender) + break; + } + + Exit: + *cursor = cur; + return count; + } + + + + static + T1_Int t1_tofixedarray( T1_Byte* *cursor, + T1_Byte* limit, + T1_Int max_values, + T1_Fixed* values, + T1_Int power_ten ) + { + T1_Byte* cur = *cursor; + T1_Int count = 0; + T1_Byte c, ender; + + if (cur >= limit) goto Exit; + + /* check for the beginning of an array. If not, only one number will be read */ + c = *cur; + ender = 0; + + if (c == '[') + ender = ']'; + + if (c == '{') + ender = '}'; + + if (ender) + cur++; + + /* now, read the values */ + for ( ; cur < limit; cur++ ) + { + c = *cur; + if (count >= max_values || c == ender) + break; + + values[count] = t1_tofixed(&cur,limit,power_ten); + count++; + + if (!ender) + break; + } + + Exit: + *cursor = cur; + return count; + } + + + static + T1_String* t1_tostring( T1_Byte* *cursor, T1_Byte* limit, FT_Memory memory ) + { + T1_Byte* cur = *cursor; + T1_Int len = 0; + T1_Int count; + T1_String* result; + FT_Error error; + + /* first of all, skip everything until we encounter a string */ + while ( cur < limit && *cur != '(' ) cur++; + cur++; + if (cur >= limit) return 0; + + *cursor = cur; + count = 0; + + /* then, count its length */ + for ( ; cur < limit; cur++ ) + { + if (*cur == '(') + count++; + + else if (*cur == ')') + { + count--; + if (count < 0) + break; + } + } + + len = cur - *cursor; + if (cur >= limit || ALLOC(result,len+1)) return 0; + + /* now copy the string */ + MEM_Copy( result, *cursor, len ); + result[len] = '\0'; + + return result; + } + + static + int t1_tobool( T1_Byte* *cursor, T1_Byte* limit ) + { + T1_Byte* cur = *cursor; + T1_Bool result = 0; + + /* return 1 if we find a "true", 0 otherwise */ + if ( cur+3 < limit && + cur[0] == 't' && + cur[1] == 'r' && + cur[2] == 'u' && + cur[3] == 'e' ) + { + result = 1; + cur += 5; + } + else if ( cur+4 < limit && + cur[0] == 'f' && + cur[1] == 'a' && + cur[2] == 'l' && + cur[3] == 's' && + cur[4] == 'e' ) + { + result = 0; + cur += 6; + } + *cursor = cur; + return result; + } + + + LOCAL_FUNC + T1_Long T1_ToInt ( T1_Parser* parser ) + { + return t1_toint( &parser->cursor, parser->limit ); + } + + + LOCAL_FUNC + T1_Long T1_ToFixed( T1_Parser* parser, T1_Int power_ten ) + { + return t1_tofixed( &parser->cursor, parser->limit, power_ten ); + } + + + LOCAL_FUNC + T1_Int T1_ToCoordArray( T1_Parser* parser, + T1_Int max_coords, + T1_Short* coords ) + { + return t1_tocoordarray( &parser->cursor, parser->limit, max_coords, coords ); + } + + + LOCAL_FUNC + T1_Int T1_ToFixedArray( T1_Parser* parser, + T1_Int max_values, + T1_Fixed* values, + T1_Int power_ten ) + { + return t1_tofixedarray( &parser->cursor, parser->limit, max_values, values, power_ten ); + } + + + LOCAL_FUNC + T1_String* T1_ToString( T1_Parser* parser ) + { + return t1_tostring( &parser->cursor, parser->limit, parser->memory ); + } + + + LOCAL_FUNC + T1_Bool T1_ToBool( T1_Parser* parser ) + { + return t1_tobool( &parser->cursor, parser->limit ); + } + + static + FT_Error read_pfb_tag( FT_Stream stream, T1_UShort *tag, T1_Long* size ) + { + FT_Error error; + + if (READ_UShort(*tag)) goto Exit; + if (*tag == 0x8001 || *tag == 0x8002) + { + FT_Long asize; + + if (READ_ULong(asize)) goto Exit; + + /* swap between big and little endianness */ + *size = ((asize & 0xFF000000) >> 24) | + ((asize & 0x00FF0000) >> 8 ) | + ((asize & 0x0000FF00) << 8 ) | + ((asize & 0x000000FF) << 24); + } + + Exit: + return error; + } + + + + LOCAL_FUNC + T1_Error T1_New_Parser( T1_Parser* parser, + FT_Stream stream, + FT_Memory memory ) + { + FT_Error error; + T1_UShort tag; + T1_Long size; + + parser->stream = stream; + parser->memory = memory; + parser->base_len = 0; + parser->base_dict = 0; + parser->private_len = 0; + parser->private_dict = 0; + parser->in_pfb = 0; + parser->in_memory = 0; + parser->single_block = 0; + + parser->cursor = 0; + parser->limit = 0; + + /******************************************************************/ + /* */ + /* Here's a short summary of what is going on : */ + /* */ + /* When creating a new Type 1 parser, we try to locate and */ + /* load the base dictionary when this is possible (i.e. for */ + /* .pfb files). Otherwise, we load the whole font in memory. */ + /* */ + /* When "loading" the base dictionary, we only setup pointers */ + /* in the case of a memory-based stream. Otherwise, we allocate */ + /* and load the base dict in it. */ + /* */ + /* parser->in_pfb is set when we are in a binary (".pfb") font */ + /* parser->in_memory is set when we have a memory stream. */ + /* */ + + /* try to compute the size of the base dictionary */ + /* look for a Postscript binary file tag, i.e 0x8001 */ + if ( FILE_Seek(0L) ) + goto Exit; + + error = read_pfb_tag( stream, &tag, &size ); + if (error) goto Exit; + + if (tag != 0x8001) + { + /* assume that this is a PFA file for now, an error will */ + /* be produced later when more things are checked */ + (void)FILE_Seek(0L); + size = stream->size; + } + else + parser->in_pfb = 1; + + /* now, try to load the "size" bytes of the "base" dictionary we */ + /* found previously */ + + /* if it's a memory-based resource, set up pointers */ + if ( !stream->read ) + { + parser->base_dict = (T1_Byte*)stream->base + stream->pos; + parser->base_len = size; + parser->in_memory = 1; + + /* check that the "size" field is valid */ + if ( FILE_Skip(size) ) goto Exit; + } + else + { + /* read segment in memory */ + if ( ALLOC( parser->base_dict, size ) || + FILE_Read( parser->base_dict, size ) ) + goto Exit; + } + + /* Now check font format, we must see a '%!PS-AdobeFont-1' */ + /* or a '%!FontType' */ + { + if ( size <= 16 || + ( strncmp( (const char*)parser->base_dict, "%!PS-AdobeFont-1", 16 ) && + strncmp( (const char*)parser->base_dict, "%!FontType", 10 ) ) ) + { + FT_TRACE2(( "Not a Type1 font\n" )); + error = T1_Err_Invalid_File_Format; + } + else + { + parser->cursor = parser->base_dict; + parser->limit = parser->cursor + parser->base_len; + } + } + + Exit: + if (error && !parser->in_memory) + FREE( parser->base_dict ); + + return error; + } + + + LOCAL_FUNC + void T1_Done_Parser( T1_Parser* parser ) + { + FT_Memory memory = parser->memory; + + /* always free the private dictionary */ + FREE( parser->private_dict ); + + /* free the base dictionary only when we have a disk stream */ + if (!parser->in_memory) + FREE( parser->base_dict ); + } + + + /* return the value of an hexadecimal digit */ + static + int hexa_value( char c ) + { + unsigned int d; + + d = (unsigned int)(c-'0'); + if ( d <= 9 ) return (int)d; + + d = (unsigned int)(c-'a'); + if ( d <= 5 ) return (int)(d+10); + + d = (unsigned int)(c-'A'); + if ( d <= 5 ) return (int)(d+10); + + return -1; + } + + + LOCAL_FUNC + void T1_Decrypt( T1_Byte* buffer, + T1_Int length, + T1_UShort seed ) + { + while ( length > 0 ) + { + T1_Byte plain; + + plain = (*buffer ^ (seed >> 8)); + seed = (*buffer+seed)*52845+22719; + *buffer++ = plain; + length--; + } + } + + + LOCAL_FUNC + T1_Error T1_Get_Private_Dict( T1_Parser* parser ) + { + FT_Stream stream = parser->stream; + FT_Memory memory = parser->memory; + FT_Error error = 0; + T1_Long size; + + if (parser->in_pfb) + { + /* in the case of the PFB format, the private dictionary can be */ + /* made of several segments. We thus first read the number of */ + /* segments to compute the total size of the private dictionary */ + /* then re-read them into memory.. */ + T1_Long start_pos = FILE_Pos(); + T1_UShort tag; + T1_Long size; + + parser->private_len = 0; + for (;;) + { + error = read_pfb_tag(stream, &tag, &size); + if (error) goto Fail; + + if (tag != 0x8002) + break; + + parser->private_len += size; + + if ( FILE_Skip(size) ) + goto Fail; + } + + /* Check that we have a private dictionary there */ + /* and allocate private dictionary buffer */ + if ( parser->private_len == 0 ) + { + FT_ERROR(( "T1.Open_Private: invalid private dictionary section\n" )); + error = T1_Err_Invalid_File_Format; + goto Fail; + } + + if ( FILE_Seek( start_pos ) || + ALLOC( parser->private_dict, parser->private_len ) ) + goto Fail; + + parser->private_len = 0; + for (;;) + { + error = read_pfb_tag( stream, &tag, &size ); + if (error || tag != 0x8002) { error = 0; break; } + + if ( FILE_Read( parser->private_dict + parser->private_len, size ) ) + goto Fail; + + parser->private_len += size; + } + } + else + { + /* we have already "loaded" the whole PFA font file in memory */ + /* if this is a memory resource, allocate a new block to hold */ + /* the private dict. Otherwise, simply overwrite into the */ + /* base dict block in the heap.. */ + + /* first of all, look at the "eexec" keyword */ + FT_Byte* cur = parser->base_dict; + FT_Byte* limit = cur + parser->base_len; + FT_Byte c; + + for (;;) + { + c = cur[0]; + if (c == 'e' && cur+9 < limit) /* 9 = 5 letters for 'eexec' + newline + 4 chars */ + { + if ( cur[1] == 'e' && cur[2] == 'x' && + cur[3] == 'e' && cur[4] == 'c' ) + { + cur += 6; /* we skip the newling after the "eexec" */ + break; + } + } + cur++; + if (cur >= limit) + { + FT_ERROR(("T1.Open_Private: could not find 'eexec' keyword\n")); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + } + + /* now determine wether where to write the _encrypted_ binary private */ + /* dictionary. We overwrite the base dictionary for disk-based resources */ + /* and allocate a new block otherwise */ + + size = parser->base_len - (cur-parser->base_dict); + + if ( parser->in_memory ) + { + /* note that we allocate one more byte to put a terminating '0' */ + if (ALLOC( parser->private_dict, size+1 )) goto Fail; + parser->private_len = size; + } + else + { + parser->single_block = 1; + parser->private_dict = parser->base_dict; + parser->private_len = size; + parser->base_dict = 0; + parser->base_len = 0; + } + + /* now determine wether the private dictionary is encoded in binary */ + /* or hexadecimal ASCII format.. */ + /* and decode it accordingly */ + + /* we need to access the next 4 bytes (after the final \r following */ + /* the 'eexec' keyword..) if they all are hexadecimal digits, then */ + /*we have a case of ASCII storage.. */ + + if ( ( hexa_value( cur[0] ) | hexa_value( cur[1] ) | + hexa_value( cur[2] ) | hexa_value( cur[3] ) ) < 0 ) + { + /* binary encoding - "simply" copy the private dict */ + MEM_Copy( parser->private_dict, cur, size ); + } + else + { + /* ASCII hexadecimal encoding.. This blows goats !!.. */ + + T1_Byte* write; + T1_Int count; + + write = parser->private_dict; + count = 0; + + for ( ;cur < limit; cur++) + { + int hex1; + + /* check for newline */ + if (cur[0] == '\r' || cur[0] == '\n') + continue; + + /* exit if we have a non-hexadecimal digit that isn't a newline */ + hex1 = hexa_value(cur[0]); + if (hex1 < 0 || cur+1 >= limit) + break; + + /* otherwise, store byte */ + *write++ = (hex1 << 4) | hexa_value(cur[1]); + count++; + cur++; + } + + /* put a safeguard */ + parser->private_len = write - parser->private_dict; + *write++ = 0; + } + } + + /* we now decrypt the encoded binary private dictionary */ + T1_Decrypt( parser->private_dict, parser->private_len, 55665 ); + parser->cursor = parser->private_dict; + parser->limit = parser->cursor + parser->private_len; + + Fail: + Exit: + return error; + } + diff --git a/src/type1z/t1parse.h b/src/type1z/t1parse.h new file mode 100644 index 000000000..a34b079e3 --- /dev/null +++ b/src/type1z/t1parse.h @@ -0,0 +1,206 @@ +/******************************************************************* + * + * t1parse.h 2.0 + * + * Type1 parser. + * + * Copyright 1996-1998 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * The Type 1 parser is in charge of the following: + * + * - provide an implementation of a growing sequence of + * objects called a T1_Table (used to build various tables + * needed by the loader). + * + * - opening .pfb and .pfa files to extract their top-level + * and private dictionaries + * + * - read numbers, arrays & strings from any dictionary + * + * See "t1load.c" to see how data is loaded from the font file + * + ******************************************************************/ + +#ifndef T1PARSE_H +#define T1PARSE_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/************************************************************************* + * + * T1_Table + * + * + * A T1_Table is a simple object used to store an array of objects + * in a single memory block. + * + * + * block :: address in memory of the growheap's block. This + * can change between two object adds, due to the use + * of 'realloc'. + * + * cursor :: current top of the grow heap within its block + * + * capacity :: current size of the heap block. Increments by 1 Kb + * + * init :: boolean. set when the table has been initialized + * (the table user should set this field) + * + * max_elems :: maximum number of elements in table + * num_elems :: current number of elements in table + * + * elements :: table of element addresses within the block + * lengths :: table of element sizes within the block + * + * memory :: memory object used for memory operations (alloc/realloc) + */ + + typedef struct T1_Table_ + { + T1_Byte* block; /* current memory block */ + T1_Int cursor; /* current cursor in memory block */ + T1_Int capacity; /* current size of memory block */ + T1_Long init; + + T1_Int max_elems; + T1_Int num_elems; + T1_Byte** elements; /* addresses of table elements */ + T1_Int* lengths; /* lengths of table elements */ + + FT_Memory memory; + + } T1_Table; + + +/************************************************************************* + * + * T1_Parser + * + * + * A T1_Parser is an object used to parse a Type 1 fonts very + * quickly. + * + * + * stream :: current input stream + * memory :: current memory object + * + * base_dict :: pointer to top-level dictionary + * base_len :: length in bytes of top dict + * + * private_dict :: pointer to private dictionary + * private_len :: length in bytes of private dict + * + * in_pfb :: boolean. Indicates that we're in a .pfb file + * in_memory :: boolean. Indicates a memory-based stream + * single_block :: boolean. Indicates that the private dict + * is stored in lieu of the base dict + * + * cursor :: current parser cursor + * limit :: current parser limit (first byte after current + * dictionary). + * + * error :: current parsing error + */ + typedef struct T1_Parser_ + { + FT_Stream stream; + FT_Memory memory; + + T1_Byte* base_dict; + T1_Int base_len; + + T1_Byte* private_dict; + T1_Int private_len; + + T1_Byte in_pfb; + T1_Byte in_memory; + T1_Byte single_block; + + T1_Byte* cursor; + T1_Byte* limit; + T1_Error error; + + } T1_Parser; + + + LOCAL_DEF + T1_Error T1_New_Table( T1_Table* table, + T1_Int count, + FT_Memory memory ); + + + LOCAL_DEF + T1_Error T1_Add_Table( T1_Table* table, + T1_Int index, + void* object, + T1_Int length ); + + + LOCAL_DEF + void T1_Done_Table( T1_Table* table ); + + LOCAL_DEF + void T1_Release_Table( T1_Table* table ); + + LOCAL_DEF + T1_Long T1_ToInt ( T1_Parser* parser ); + + LOCAL_DEF + T1_Long T1_ToFixed( T1_Parser* parser, T1_Int power_ten ); + + LOCAL_DEF + T1_Int T1_ToCoordArray( T1_Parser* parser, + T1_Int max_coords, + T1_Short* coords ); + + LOCAL_DEF + T1_Int T1_ToFixedArray( T1_Parser* parser, + T1_Int max_values, + T1_Fixed* values, + T1_Int power_ten ); + + LOCAL_DEF + T1_String* T1_ToString( T1_Parser* parser ); + + + LOCAL_DEF + T1_Bool T1_ToBool( T1_Parser* parser ); + + + LOCAL_DEF + T1_Int T1_ToImmediate( T1_Parser* parser ); + + + LOCAL_DEF + T1_Error T1_New_Parser( T1_Parser* parser, + FT_Stream stream, + FT_Memory memory ); + + LOCAL_DEF + T1_Error T1_Get_Private_Dict( T1_Parser* parser ); + + LOCAL_DEF + void T1_Decrypt( T1_Byte* buffer, + T1_Int length, + T1_UShort seed ); + +#ifdef __cplusplus + } +#endif + +#endif /* T1PARSE_H */ + + +/* END */ + diff --git a/src/type1z/t1tokens.h b/src/type1z/t1tokens.h new file mode 100644 index 000000000..4f251ed5f --- /dev/null +++ b/src/type1z/t1tokens.h @@ -0,0 +1,73 @@ +/******************************************************************* + * + * t1tokens.h + * + * Type 1 tokens definition + * + * Copyright 2000 David Turner, Robert Wilhelm and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used + * modified and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * This file only contains macros that are expanded when compiling + * the "t1load.c" source file. + * + ******************************************************************/ + +#undef FACE +#define FACE (face->type1) + + /* define the font info dictionary parsing callbacks */ + + PARSE_STRING("version",version) + PARSE_STRING("Notice",notice) + PARSE_STRING("FullName",full_name) + PARSE_STRING("FamilyName",family_name) + PARSE_STRING("Weight",weight) + + PARSE_INT("ItalicAngle",italic_angle) + PARSE_BOOL("isFixedPitch",is_fixed_pitch) + PARSE_INT("UnderlinePosition",underline_position) + PARSE_INT("UnderlineThickness",underline_thickness) + + /* define the private dict parsing callbacks */ + + PARSE_INT("UniqueID",unique_id) + PARSE_INT("lenIV",lenIV) + + PARSE_COORDS( "BlueValues", num_blues, 14, blue_values) + PARSE_COORDS( "OtherBlues", num_other_blues, 10, other_blues) + + PARSE_COORDS( "FamilyBlues", num_family_blues, 14, family_blues) + PARSE_COORDS( "FamilyOtherBlues", num_family_other_blues, 10, family_other_blues) + + PARSE_FIXED( "BlueScale", blue_scale) + PARSE_INT( "BlueShift", blue_shift) + + PARSE_INT( "BlueFuzz", blue_fuzz) + + PARSE_COORDS2( "StdHW", 1, standard_width ) + PARSE_COORDS2( "StdVW", 1, standard_height ) + + PARSE_COORDS( "StemSnapH", num_snap_widths, 12, stem_snap_widths ) + PARSE_COORDS( "StemSnapV", num_snap_heights, 12, stem_snap_heights ) + + PARSE_INT( "LanguageGroup", language_group ) + PARSE_INT( "password", password ) + PARSE_COORDS2( "MinFeature", 2, min_feature ) + + /* define the top-level dictionary parsing callbacks */ + +/* PARSE_STRING( "FontName", font_name ) -- handled by special routine */ + PARSE_INT( "PaintType", paint_type ) + PARSE_INT( "FontType", font_type ) + PARSE_FIXEDS2( "FontMatrix", 4, font_matrix ) +/* PARSE_COORDS2( "FontBBox", 4, font_bbox ) -- handled by special func */ + PARSE_INT( "StrokeWidth", stroke_width ) + +#undef FACE + + diff --git a/src/type1z/type1z.c b/src/type1z/type1z.c new file mode 100644 index 000000000..f2d28009b --- /dev/null +++ b/src/type1z/type1z.c @@ -0,0 +1,41 @@ +/***************************************************************************/ +/* */ +/* type1.c */ +/* */ +/* FreeType Type 1 driver component */ +/* */ +/* Copyright 1996-1998 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/* */ +/* This file is used to compile the FreeType Type 1 font driver. */ +/* It relies on all components included in the "base" layer (see */ +/* the file "ftbase.c"). Source code is located in "freetype/ttlib" */ +/* and contains : */ +/* */ +/* - a driver interface */ +/* - an object manager */ +/* - a table loader */ +/* - a glyph loader */ +/* - a glyph hinter */ +/* */ +/***************************************************************************/ + + + +#include +#include +#include +#include +/* +#include +#include +#include +*/ +