ref: 3469d0d038b01c7ddce0fb67da68d79ad2f8b8a6
parent: 6930b45f78abc193e84544f804cead9436b86ad5
author: David Turner <[email protected]>
date: Wed Jul 19 16:02:14 EDT 2000
added auto-hinter module. Note that the code has been cleaned up, and it seems a bug was introduced ??? I'll start checking this under Linux, as debugging is a lot easier under this environment..
--- /dev/null
+++ b/src/autohint/CatharonLicense.txt
@@ -1,0 +1,123 @@
+ The Catharon Open Source LICENSE
+ ----------------------------
+
+ 2000-Jul-4
+
+ Copyright (C) 2000 by Catharon Productions, Inc.
+
+
+
+Introduction
+============
+
+ This license applies to source files distributed by Catharon
+ Productions, Inc. in several archive packages. This license
+ applies to all files found in such packages which do not fall
+ under their own explicit license.
+
+ This license was inspired by the BSD, Artistic, and IJG
+ (Independent JPEG Group) licenses, which all encourage inclusion
+ and use of free software in commercial and freeware products
+ alike. As a consequence, its main points are that:
+
+ o We don't promise that this software works. However, we are
+ interested in any kind of bug reports. (`as is' distribution)
+
+ o You can use this software for whatever you want, in parts or
+ full form, without having to pay us. (`royalty-free' usage)
+
+ o You may not pretend that you wrote this software. If you use
+ it, or only parts of it, in a program, you must acknowledge
+ somewhere in your documentation that you have used the
+ Catharon Code. (`credits')
+
+ We specifically permit and encourage the inclusion of this
+ software, with or without modifications, in commercial products.
+ We disclaim all warranties covering the packages distributed by
+ Catharon Productions, Inc. and assume no liability related to
+ their use.
+
+
+Legal Terms
+===========
+
+0. Definitions
+--------------
+
+ Throughout this license, the terms `Catharon Package', `package',
+ and `Catharon Code' refer to the set of files originally
+ distributed by Catharon Productions, Inc.
+
+ `You' refers to the licensee, or person using the project, where
+ `using' is a generic term including compiling the project's source
+ code as well as linking it to form a `program' or `executable'.
+ This program is referred to as `a program using one of the
+ Catharon Packages'.
+
+ This license applies to all files distributed in the original
+ Catharon Package(s), including all source code, binaries and
+ documentation, unless otherwise stated in the file in its
+ original, unmodified form as distributed in the original archive.
+ If you are unsure whether or not a particular file is covered by
+ this license, you must contact us to verify this.
+
+ The Catharon Packages are copyright (C) 2000 by Catharon
+ Productions, Inc. All rights reserved except as specified below.
+
+1. No Warranty
+--------------
+
+ THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO
+ USE THE CATHARON PACKAGE.
+
+2. Redistribution
+-----------------
+
+ This license grants a worldwide, royalty-free, perpetual and
+ irrevocable right and license to use, execute, perform, compile,
+ display, copy, create derivative works of, distribute and
+ sublicense the Catharon Packages (in both source and object code
+ forms) and derivative works thereof for any purpose; and to
+ authorize others to exercise some or all of the rights granted
+ herein, subject to the following conditions:
+
+ o Redistribution of source code must retain this license file
+ (`license.txt') unaltered; any additions, deletions or changes
+ to the original files must be clearly indicated in
+ accompanying documentation. The copyright notices of the
+ unaltered, original files must be preserved in all copies of
+ source files.
+
+ o Redistribution in binary form must provide a disclaimer that
+ states that the software is based in part on the work of
+ Catharon Productions, Inc. in the distribution documentation.
+
+ These conditions apply to any software derived from or based on
+ the Catharon Packages, not just the unmodified files. If you use
+ our work, you must acknowledge us. However, no fee need be paid
+ to us.
+
+3. Advertising
+--------------
+
+ Neither Catharon Productions, Inc. and contributors nor you shall
+ use the name of the other for commercial, advertising, or
+ promotional purposes without specific prior written permission.
+
+ We suggest, but do not require, that you use the following phrase
+ to refer to this software in your documentation: 'this software is
+ based in part on the Catharon Typography Project'.
+
+ As you have not signed this license, you are not required to
+ accept it. However, as the Catharon Packages are copyrighted
+ material, only this license, or another one contracted with the
+ authors, grants you the right to use, distribute, and modify it.
+ Therefore, by using, distributing, or modifying the Catharon
+ Packages, you indicate that you understand and accept all the
+ terms of this license.
+
+--- end of license.txt ---
--- /dev/null
+++ b/src/autohint/ahangles.c
@@ -1,0 +1,125 @@
+/***************************************************************************/
+/* */
+/* ahangles.h */
+/* */
+/* a routine used to compute vector angles with limited accuracy */
+/* and very high speed. */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifdef FT_FLAT_COMPILE
+#include "ahangles.h"
+#else
+#include <autohint/ahangles.h>
+#endif
+
+/* the following two tables are automatically generated with */
+/* the "mather.py" Python script.. */
+
+static const AH_Angle ah_arctan[ 1L << AH_ATAN_BITS ] =
+{
+ 0, 0, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 5,
+ 5, 5, 6, 6, 6, 7, 7, 7,
+ 8, 8, 8, 9, 9, 9, 10, 10,
+ 10, 10, 11, 11, 11, 12, 12, 12,
+ 13, 13, 13, 14, 14, 14, 14, 15,
+ 15, 15, 16, 16, 16, 17, 17, 17,
+ 18, 18, 18, 18, 19, 19, 19, 20,
+ 20, 20, 21, 21, 21, 21, 22, 22,
+ 22, 23, 23, 23, 24, 24, 24, 24,
+ 25, 25, 25, 26, 26, 26, 26, 27,
+ 27, 27, 28, 28, 28, 28, 29, 29,
+ 29, 30, 30, 30, 30, 31, 31, 31,
+ 31, 32, 32, 32, 33, 33, 33, 33,
+ 34, 34, 34, 34, 35, 35, 35, 35,
+ 36, 36, 36, 36, 37, 37, 37, 38,
+ 38, 38, 38, 39, 39, 39, 39, 40,
+ 40, 40, 40, 41, 41, 41, 41, 42,
+ 42, 42, 42, 42, 43, 43, 43, 43,
+ 44, 44, 44, 44, 45, 45, 45, 45,
+ 46, 46, 46, 46, 46, 47, 47, 47,
+ 47, 48, 48, 48, 48, 48, 49, 49,
+ 49, 49, 50, 50, 50, 50, 50, 51,
+ 51, 51, 51, 51, 52, 52, 52, 52,
+ 52, 53, 53, 53, 53, 53, 54, 54,
+ 54, 54, 54, 55, 55, 55, 55, 55,
+ 56, 56, 56, 56, 56, 57, 57, 57,
+ 57, 57, 57, 58, 58, 58, 58, 58,
+ 59, 59, 59, 59, 59, 59, 60, 60,
+ 60, 60, 60, 61, 61, 61, 61, 61,
+ 61, 62, 62, 62, 62, 62, 62, 63,
+ 63, 63, 63, 63, 63, 64, 64, 64
+};
+
+
+ LOCAL_FUNC
+ AH_Angle ah_angle( FT_Vector* v )
+ {
+ FT_Pos dx, dy;
+ AH_Angle angle;
+
+ dx = v->x;
+ dy = v->y;
+
+ /* check trivial cases */
+ if (dy == 0)
+ {
+ angle = 0;
+ if (dx < 0)
+ angle = AH_PI;
+ return angle;
+ }
+ else if (dx == 0)
+ {
+ angle = AH_HALF_PI;
+ if (dy < 0)
+ angle = -AH_HALF_PI;
+ return angle;
+ }
+
+ angle = 0;
+ if ( dx < 0 )
+ {
+ dx = -v->x;
+ dy = -v->y;
+ angle = AH_PI;
+ }
+
+ if ( dy < 0 )
+ {
+ FT_Pos tmp;
+ tmp = dx;
+ dx = -dy;
+ dy = tmp;
+ angle -= AH_HALF_PI;
+ }
+
+ if (dx == 0 && dy == 0)
+ return 0;
+
+ if (dx == dy)
+ angle += AH_PI/4;
+ else if (dx > dy)
+ angle += ah_arctan[ FT_DivFix( dy, dx ) >> (16-AH_ATAN_BITS) ];
+ else
+ angle += AH_HALF_PI - ah_arctan[ FT_DivFix( dx, dy ) >> (16-AH_ATAN_BITS) ];
+
+ if (angle > AH_PI)
+ angle -= AH_2PI;
+
+ return angle;
+ }
+
+
--- /dev/null
+++ b/src/autohint/ahangles.h
@@ -1,0 +1,47 @@
+/***************************************************************************/
+/* */
+/* ahangles.h */
+/* */
+/* a routine used to compute vector angles with limited accuracy */
+/* and very high speed. */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifndef AGANGLES_H
+#define AGANGLES_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+#include <freetype/internal/ftobjs.h>
+
+/* PI expressed in ah_angles - we don't really need an important */
+/* precision, so 256 should be enough.. */
+#define AH_PI 256
+#define AH_2PI (AH_PI*2)
+#define AH_HALF_PI (AH_PI/2)
+#define AH_2PIMASK (AH_2PI-1)
+
+/* the number of bits to use to express an arc tangent */
+/* see the structure of the lookup table.. */
+#define AH_ATAN_BITS 8
+
+LOCAL_DEF const AH_Angle ah_arctan[ 1L << AH_ATAN_BITS ];
+
+LOCAL_DEF AH_Angle ah_angle( FT_Vector* v );
+
+#endif /* AGANGLES_H */
--- /dev/null
+++ b/src/autohint/ahglobal.c
@@ -1,0 +1,365 @@
+/***************************************************************************/
+/* */
+/* ahglobal.c */
+/* */
+/* routines used to compute global metrics automatically */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifdef FT_FLAT_COMPILE
+#include "ahglobal.h"
+#include "ahglyph.h"
+#else
+#include <autohint/ahglobal.h>
+#include <autohint/ahglyph.h>
+#endif
+
+#define MAX_TEST_CHARACTERS 12
+
+ static const char* blue_chars[ ah_blue_max ] =
+ {
+ "THEZOCQS",
+ "HEZLOCUS",
+ "xzroesc",
+ "xzroesc",
+ "pqgjy"
+ };
+
+ /* simple insertion sort */
+ static
+ void sort_values( FT_Int count, FT_Pos* table )
+ {
+ FT_Int i, j, swap;
+
+ for ( i = 1; i < count; i++ )
+ {
+ for ( j = i; j > 1; j-- )
+ {
+ if ( table[j] > table[j-1] )
+ break;
+
+ swap = table[j];
+ table[j] = table[j-1];
+ table[j-1] = swap;
+ }
+ }
+ }
+
+
+ static
+ FT_Error ah_hinter_compute_blues( AH_Hinter* hinter )
+ {
+ AH_Blue blue;
+ AH_Globals* globals = &hinter->globals->design;
+ FT_Pos flats [ MAX_TEST_CHARACTERS ];
+ FT_Pos rounds[ MAX_TEST_CHARACTERS ];
+ FT_Int num_flats;
+ FT_Int num_rounds;
+
+ FT_Face face;
+ FT_GlyphSlot glyph;
+ FT_Error error;
+ FT_CharMap charmap;
+
+ face = hinter->face;
+ glyph = face->glyph;
+
+ /* save current charmap */
+ charmap = face->charmap;
+
+ /* do we have a Unicode charmap in there ?? */
+ error = FT_Select_Charmap( face, ft_encoding_unicode );
+ if (error) goto Exit;
+
+ /* we compute the blues simply by loading each character from the */
+ /* 'blue_chars[blues]' string, then compute its top-most and bottom-most */
+ /* points */
+
+ AH_LOG(( "blue zones computation\n" ));
+ AH_LOG(( "------------------------------------------------\n" ));
+
+ for ( blue = (AH_Blue)0; blue < ah_blue_max; blue++ )
+ {
+ const char* p = blue_chars[blue];
+ const char* limit = p + MAX_TEST_CHARACTERS;
+ FT_Pos *blue_ref, *blue_shoot;
+
+ AH_LOG(( "blue %3d : ", (int)blue ));
+
+ num_flats = 0;
+ num_rounds = 0;
+ for ( ; p < limit; p++ )
+ {
+ FT_UInt glyph_index;
+ FT_Vector* extremum;
+ FT_Vector* points;
+ FT_Vector* point_limit;
+ FT_Vector* point;
+ FT_Bool round;
+
+ /* exit if we reach the end of the string */
+ if (!*p) break;
+
+ AH_LOG(( "'%c'", *p ));
+
+ /* load the character in the face - skip unknown or empty ones */
+ glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
+ if (glyph_index == 0) continue;
+
+ error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+ if (error || glyph->outline.n_points <= 0) continue;
+
+
+ /* now compute min or max point indices and coordinates */
+ points = glyph->outline.points;
+ point_limit = points + glyph->outline.n_points;
+ point = points;
+ extremum = point;
+ point++;
+
+ if ( AH_IS_TOP_BLUE(blue) )
+ {
+ for ( ; point < point_limit; point++ )
+ if ( point->y > extremum->y )
+ extremum = point;
+ }
+ else
+ {
+ for ( ; point < point_limit; point++ )
+ if ( point->y < extremum->y )
+ extremum = point;
+ }
+
+ AH_LOG(( "%5d", (int)extremum->y ));
+
+ /* now, see if the point belongs to a straight or round segment */
+ /* we first need to find in which contour the extremum lies then */
+ /* see its previous and next points.. */
+ {
+ FT_Int index = extremum - points;
+ FT_Int n;
+ FT_Int first = 0;
+ FT_Int last, prev, next, end;
+ FT_Pos dist;
+
+ last = -1;
+ first = 0;
+ for ( n = 0; n < glyph->outline.n_contours; n++ )
+ {
+ end = glyph->outline.contours[n];
+ if ( end >= index )
+ {
+ last = end;
+ break;
+ }
+ first = end+1;
+ }
+
+ /* XXX : should never happen !!! */
+ if ( last < 0 )
+ continue;
+
+ /* now look for the previous and next points that are not on the */
+ /* same Y coordinate. Threshold the "closeness" .. */
+
+ prev = index;
+ next = prev;
+
+ do
+ {
+ if (prev > first) prev--;
+ else prev = last;
+
+ dist = points[prev].y - extremum->y;
+ if ( dist < -5 || dist > 5 )
+ break;
+
+ } while (prev != index);
+
+ do
+ {
+ if (next < last) next++;
+ else next = first;
+
+ dist = points[next].y - extremum->y;
+ if ( dist < -5 || dist > 5 )
+ break;
+
+ } while (next != index);
+
+ /* now, set the "round" flag depending on the segment's kind */
+ round = FT_CURVE_TAG(glyph->outline.tags[prev]) != FT_Curve_Tag_On ||
+ FT_CURVE_TAG(glyph->outline.tags[next]) != FT_Curve_Tag_On ;
+
+ AH_LOG(( "%c ", round ? 'r' : 'f' ));
+ }
+
+ if (round)
+ rounds[ num_rounds++ ] = extremum->y;
+ else
+ flats[ num_flats++ ] = extremum->y;
+ }
+
+ AH_LOG(( "\n" ));
+ /* we have computed the contents of the 'rounds' and 'flats' tables */
+ /* now determine the reference and overshoot position of the blue */
+ /* we simply take the median value after a simple short.. */
+ sort_values( num_rounds, rounds );
+ sort_values( num_flats, flats );
+
+ blue_ref = globals->blue_refs + blue;
+ blue_shoot = globals->blue_shoots + blue;
+ if ( num_flats == 0 && num_rounds == 0 )
+ {
+ *blue_ref = -10000;
+ *blue_shoot = -10000;
+ }
+ else if ( num_flats == 0 )
+ {
+ *blue_ref =
+ *blue_shoot = rounds[ num_rounds/2 ];
+ }
+ else if ( num_rounds == 0 )
+ {
+ *blue_ref =
+ *blue_shoot = flats[ num_flats/2 ];
+ }
+ else
+ {
+ *blue_ref = flats[ num_flats/2 ];
+ *blue_shoot = rounds[ num_rounds/2 ];
+ }
+
+ /* there are sometimes problems, when the overshoot position of top */
+ /* zones is under its reference position, or the opposite for bottom */
+ /* zones. We must thus check everything there.. and correct the errors */
+ if ( *blue_shoot != *blue_ref )
+ {
+ FT_Pos ref = *blue_ref;
+ FT_Pos shoot = *blue_shoot;
+ FT_Bool over_ref = ( shoot > ref );
+
+ if ( AH_IS_TOP_BLUE(blue) ^ over_ref )
+ *blue_shoot = *blue_ref = (shoot+ref)/2;
+ }
+ AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
+ }
+
+ /* reset original face charmap */
+ FT_Set_Charmap( face, charmap );
+ error = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ static
+ FT_Error ah_hinter_compute_widths( AH_Hinter* hinter )
+ {
+ /* scan the array of segments in each direction */
+ AH_Outline* outline = hinter->glyph;
+ AH_Segment* segments;
+ AH_Segment* limit;
+ AH_Globals* globals = &hinter->globals->design;
+ FT_Pos* widths;
+ FT_Int dimension;
+ FT_Int* p_num_widths;
+ FT_Error error = 0;
+ FT_Pos edge_distance_threshold = 32000;
+
+ globals->num_widths = 0;
+ globals->num_heights = 0;
+
+ /* for now, compute the standard width and height from the "o" character */
+ /* I started computing the stem width of the "i" and the stem height of */
+ /* the "-", but it wasn't too good.. Moreover, we now have a single */
+ /* character that gives us standard width and height */
+ {
+ FT_UInt glyph_index;
+
+ glyph_index = FT_Get_Char_Index( hinter->face, 'o' );
+ if (glyph_index == 0) return 0;
+
+ error = FT_Load_Glyph( hinter->face, glyph_index, FT_LOAD_NO_SCALE );
+ if (error) goto Exit;
+
+ error = ah_outline_load( hinter->glyph, hinter->face );
+ if (error) goto Exit;
+
+ ah_outline_compute_segments( hinter->glyph );
+ ah_outline_link_segments( hinter->glyph );
+ }
+
+ segments = outline->horz_segments;
+ limit = segments + outline->num_hsegments;
+ widths = globals->heights;
+ p_num_widths = &globals->num_heights;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Segment* seg = segments;
+ AH_Segment* link;
+ FT_Int num_widths = 0;
+
+ for ( ; seg < limit; seg++ )
+ {
+ link = seg->link;
+ /* we only consider the stem segments there ! */
+ if (link && link->link == seg && link > seg)
+ {
+ FT_Int dist;
+
+ dist = seg->pos - link->pos;
+ if (dist < 0) dist = -dist;
+
+ if ( num_widths < 12 )
+ widths[ num_widths++ ] = dist;
+ }
+ }
+
+ sort_values( num_widths, widths );
+ *p_num_widths = num_widths;
+
+ /* we will now try to find the smallest width */
+ if (num_widths > 0 && widths[0] < edge_distance_threshold )
+ edge_distance_threshold = widths[0];
+
+ segments = outline->vert_segments;
+ limit = segments + outline->num_vsegments;
+ widths = globals->widths;
+ p_num_widths = &globals->num_widths;
+
+ }
+
+ /* now, compute the edge distance threshold as a fraction of the */
+ /* smallest width in the font.. Set it in "hinter.glyph" too !! */
+ if ( edge_distance_threshold == 32000)
+ edge_distance_threshold = 50;
+
+ /* let's try 20% */
+ hinter->glyph->edge_distance_threshold = edge_distance_threshold/5;
+
+ Exit:
+ return error;
+ }
+
+
+ LOCAL_FUNC
+ FT_Error ah_hinter_compute_globals( AH_Hinter* hinter )
+ {
+ return ah_hinter_compute_widths( hinter ) ||
+ ah_hinter_compute_blues ( hinter );
+ }
+
--- /dev/null
+++ b/src/autohint/ahglobal.h
@@ -1,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* ahglobal.h */
+/* */
+/* routines used to compute global metrics automatically */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifndef AGGLOBAL_H
+#define AGGLOBAL_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+#include <freetype/internal/ftobjs.h> /* for LOCAL_DEF/LOCAL_FUNC */
+
+#define AH_IS_TOP_BLUE(b) ( (b) == ah_blue_capital_top || \
+ (b) == ah_blue_small_top )
+
+ /* compute global metrics automatically */
+ LOCAL_DEF
+ FT_Error ah_hinter_compute_globals( AH_Hinter* hinter );
+
+#endif /* AGGLOBAL_H */
--- /dev/null
+++ b/src/autohint/ahglyph.c
@@ -1,0 +1,1192 @@
+/***************************************************************************/
+/* */
+/* ahglyph.c */
+/* */
+/* routines used to load and analyze a given glyph before hinting */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+
+#ifdef FT_FLAT_COMPILE
+#include "ahglyph.h"
+#include "ahangles.h"
+#include "ahglobal.h"
+#else
+#include <autohint/ahglyph.h>
+#include <autohint/ahangles.h>
+#include <autohint/ahglobal.h>
+#endif
+
+#include <stdio.h>
+
+#define xxxAH_DEBUG_GLYPH
+
+ /* compute the direction value of a given vector.. */
+ static
+ AH_Direction ah_compute_direction( FT_Pos dx, FT_Pos dy )
+ {
+ AH_Direction dir;
+ FT_Pos ax = ABS(dx);
+ FT_Pos ay = ABS(dy);
+
+ dir = ah_dir_none;
+
+ /* test for vertical direction */
+ if ( ax*12 < ay )
+ {
+ dir = ( dy > 0 ? ah_dir_up : ah_dir_down );
+ }
+ /* test for horizontal direction */
+ else if ( ay*12 < ax )
+ {
+ dir = ( dx > 0 ? ah_dir_right : ah_dir_left );
+ }
+
+ return dir;
+ }
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_new
+ *
+ * <Description>
+ * Create a new empty AH_Outline
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ FT_Error ah_outline_new( FT_Memory memory,
+ AH_Outline* *aoutline )
+ {
+ FT_Error error;
+ AH_Outline* outline;
+
+ if ( !ALLOC( outline, sizeof(*outline) ) )
+ {
+ outline->memory = memory;
+ *aoutline = outline;
+ }
+ return error;
+ }
+
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_done
+ *
+ * <Description>
+ * Destroys a given AH_Outline
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ void ah_outline_done( AH_Outline* outline )
+ {
+ FT_Memory memory = outline->memory;
+
+ FREE( outline->horz_edges );
+ FREE( outline->horz_segments );
+ FREE( outline->contours );
+ FREE( outline->points );
+ outline->vert_edges = 0;
+ outline->vert_segments = 0;
+
+ outline->num_points = 0;
+ outline->max_points = 0;
+ outline->num_contours = 0;
+ outline->max_contours = 0;
+ FREE( outline );
+ }
+
+
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_save
+ *
+ * <Description>
+ * Save the content of a given AH_Outline into a face's glyph slot
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ void ah_outline_save( AH_Outline* outline,
+ AH_Loader* gloader )
+ {
+ AH_Point* point = outline->points;
+ AH_Point* limit = point + outline->num_points;
+ FT_Vector* vec = gloader->current.outline.points;
+ char* tag = gloader->current.outline.tags;
+
+ /* we assume that the glyph loader has already been checked for storage */
+ for ( ; point < limit; point++, vec++, tag++ )
+ {
+ vec->x = point->x;
+ vec->y = point->y;
+
+ if (point->flags & ah_flah_conic)
+ tag[0] = FT_Curve_Tag_Conic;
+ else if (point->flags & ah_flah_cubic)
+ tag[0] = FT_Curve_Tag_Cubic;
+ else
+ tag[0] = FT_Curve_Tag_On;
+ }
+ }
+
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_load
+ *
+ * <Description>
+ * Loads an unscaled outline from a glyph slot into an AH_Outline
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ FT_Error ah_outline_load( AH_Outline* outline,
+ FT_Face face )
+ {
+ FT_Memory memory = outline->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Outline* source = &face->glyph->outline;
+ FT_Int num_points = source->n_points;
+ FT_Int num_contours = source->n_contours;
+ AH_Point* points;
+
+ /* check arguments */
+ if (!face || !face->size || face->glyph->format != ft_glyph_format_outline)
+ return FT_Err_Invalid_Argument;
+
+
+ /* first of all, realloc the contours array if necessary */
+ if ( num_contours > outline->max_contours )
+ {
+ FT_Int new_contours = (num_contours+3) & -4;
+
+ if ( REALLOC_ARRAY( outline->contours, outline->max_contours,
+ new_contours, AH_Point* ) )
+ goto Exit;
+
+ outline->max_contours = new_contours;
+ }
+
+ /* then, realloc the points, segments & edges arrays if needed */
+ if ( num_points > outline->max_points )
+ {
+ FT_Int news = (num_points+7) & -8;
+ FT_Int max = outline->max_points;
+
+ if ( REALLOC_ARRAY( outline->points, max, news, AH_Point ) ||
+ REALLOC_ARRAY( outline->horz_edges, max, news, AH_Edge ) ||
+ REALLOC_ARRAY( outline->horz_segments, max, news, AH_Segment ) )
+ goto Exit;
+
+ /* readjust some pointers */
+ outline->vert_edges = outline->horz_edges + (news >> 1);
+ outline->vert_segments = outline->horz_segments + (news >> 1);
+ outline->max_points = news;
+ }
+
+ outline->num_points = num_points;
+ outline->num_contours = num_contours;
+
+ outline->num_hedges = 0;
+ outline->num_vedges = 0;
+ outline->num_hsegments = 0;
+ outline->num_vsegments = 0;
+
+ /* compute the vertical and horizontal major directions, this is */
+ /* currently done by inspecting the 'ft_outline_reverse_fill' flag */
+ /* However, some fonts have improper glyphs, and it'd be a good idea */
+ /* to be able to re-compute these values on the fly.. */
+ {
+ outline->vert_major_dir = ah_dir_up;
+ outline->horz_major_dir = ah_dir_left;
+
+ if (source->flags & ft_outline_reverse_fill)
+ {
+ outline->vert_major_dir = ah_dir_down;
+ outline->horz_major_dir = ah_dir_right;
+ }
+ }
+
+ outline->x_scale = face->size->metrics.x_scale;
+ outline->y_scale = face->size->metrics.y_scale;
+
+ points = outline->points;
+
+ {
+ /* do one thing at a time - it is easier to understand, and */
+ /* the code is clearer.. */
+ AH_Point* point = points;
+ AH_Point* limit = point + outline->num_points;
+
+ /* compute coordinates */
+ {
+ FT_Vector* vec = source->points;
+ FT_Fixed x_scale = outline->x_scale;
+ FT_Fixed y_scale = outline->y_scale;
+
+ for (; point < limit; vec++, point++ )
+ {
+ point->fx = vec->x;
+ point->fy = vec->y;
+ point->ox = point->x = FT_MulFix( vec->x, x_scale );
+ point->oy = point->y = FT_MulFix( vec->y, y_scale );
+ point->flags = 0;
+ }
+ }
+
+ /* compute bezier flags */
+ {
+ char* tag = source->tags;
+ for ( point = points; point < limit; point++, tag++ )
+ {
+ switch ( FT_CURVE_TAG( *tag ) )
+ {
+ case FT_Curve_Tag_Conic: point->flags = ah_flah_conic; break;
+ case FT_Curve_Tag_Cubic: point->flags = ah_flah_cubic; break;
+ default:
+ ;
+ }
+ }
+ }
+
+ /* compute "next" and "prev" */
+ {
+ FT_Int contour_index;
+ AH_Point* prev;
+ AH_Point* first;
+ AH_Point* end;
+
+ contour_index = 0;
+
+ first = points;
+ end = points + source->contours[0];
+ prev = end;
+
+ for ( point = points; point < limit; point++ )
+ {
+ point->prev = prev;
+ if (point < end)
+ {
+ point->next = point+1;
+ prev = point;
+ }
+ else
+ {
+ point->next = first;
+ contour_index++;
+ if (point+1 < limit)
+ {
+ end = points + source->contours[contour_index];
+ first = point+1;
+ prev = end;
+ }
+ }
+ }
+ }
+
+ /* set-up the contours array */
+ {
+ AH_Point** contour = outline->contours;
+ AH_Point** limit = contour + outline->num_contours;
+ short* end = source->contours;
+ short index = 0;
+
+ for (; contour < limit; contour++, end++ )
+ {
+ contour[0] = points + index;
+ index = end[0] + 1;
+ }
+ }
+
+ /* compute directions of in & out vectors */
+ {
+ for ( point = points; point < limit; point++ )
+ {
+ AH_Point* prev;
+ AH_Point* next;
+ FT_Vector vec;
+
+ prev = point->prev;
+ vec.x = point->fx - prev->fx;
+ vec.y = point->fy - prev->fy;
+ point->in_dir = ah_compute_direction( vec.x, vec.y );
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+ point->in_angle = ah_angle( &vec );
+#endif
+ next = point->next;
+ vec.x = next->fx - point->fx;
+ vec.y = next->fy - point->fy;
+ point->out_dir = ah_compute_direction( vec.x, vec.y );
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+ point->out_angle = ah_angle( &vec );
+
+ {
+ AH_Angle delta = point->in_angle - point->out_angle;
+ if (delta < 0) delta = -delta;
+
+ if (delta < 2)
+ point->flags |= ah_flah_weak_interpolation;
+ }
+
+ /*
+ if (point->flags & (ah_flah_conic|ah_flah_cubic))
+ point->flags |= ah_flah_weak_interpolation;
+ */
+#endif
+
+#ifdef AH_OPTION_NO_STRONG_INTERPOLATION
+ point->flags |= ah_flah_weak_interpolation;
+#endif
+ }
+ }
+ }
+ Exit:
+ return error;
+ }
+
+
+
+ LOCAL_FUNC
+ void ah_setup_uv( AH_Outline* outline,
+ AH_UV source )
+ {
+ AH_Point* point = outline->points;
+ AH_Point* limit = point + outline->num_points;
+
+ for ( ; point < limit; point++ )
+ {
+ FT_Pos u, v;
+
+ switch (source)
+ {
+ case ah_uv_fxy: u = point->fx; v = point->fy; break;
+ case ah_uv_fyx: u = point->fy; v = point->fx; break;
+ case ah_uv_oxy: u = point->ox; v = point->oy; break;
+ case ah_uv_oyx: u = point->oy; v = point->ox; break;
+ case ah_uv_yx: u = point->y; v = point->x; break;
+ case ah_uv_ox: u = point->x; v = point->ox; break;
+ case ah_uv_oy: u = point->y; v = point->oy; break;
+ default: u = point->x; v = point->y; break;
+ }
+ point->u = u;
+ point->v = v;
+ }
+ }
+
+
+ LOCAL_FUNC
+ void ah_outline_compute_segments( AH_Outline* outline )
+ {
+ int dimension;
+ AH_Segment* segments;
+ FT_Int* p_num_segments;
+ AH_Direction segment_dir;
+ AH_Direction major_dir;
+
+ segments = outline->horz_segments;
+ p_num_segments = &outline->num_hsegments;
+ major_dir = ah_dir_right; /* !!! This value must be positive */
+ segment_dir = major_dir;
+
+ /* set up (u,v) in each point */
+ ah_setup_uv( outline, ah_uv_fyx );
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Point** contour = outline->contours;
+ AH_Point** contour_limit = contour + outline->num_contours;
+ AH_Segment* segment = segments;
+ FT_Int num_segments = 0;
+
+#ifdef AH_HINT_METRICS
+ AH_Point* min_point = 0;
+ AH_Point* max_point = 0;
+ FT_Pos min_coord = 32000;
+ FT_Pos max_coord = -32000;
+#endif
+ /* do each contour separately */
+ for ( ; contour < contour_limit; contour++ )
+ {
+ AH_Point* point = contour[0];
+ AH_Point* last = point->prev;
+ int on_edge = 0;
+ FT_Pos min_pos = +32000; /* minimum segment pos != min_coord */
+ FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
+ FT_Bool passed;
+
+#ifdef AH_HINT_METRICS
+ if (point->u < min_coord)
+ {
+ min_coord = point->u;
+ min_point = point;
+ }
+ if (point->u > max_coord)
+ {
+ max_coord = point->u;
+ max_point = point;
+ }
+#endif
+
+ if (point == last) /* skip singletons - just in case ?? */
+ continue;
+
+ if ( ABS(last->out_dir) == major_dir &&
+ ABS(point->out_dir) == major_dir)
+ {
+ /* we are already on an edge, try to locate its start */
+ last = point;
+ for (;;)
+ {
+ point = point->prev;
+ if ( ABS(point->out_dir) != major_dir )
+ {
+ point = point->next;
+ break;
+ }
+ if ( point == last )
+ break;
+ }
+
+ }
+
+ last = point;
+ passed = 0;
+ for (;;)
+ {
+ FT_Pos u, v;
+
+ if (on_edge)
+ {
+ u = point->u;
+ if ( u < min_pos ) min_pos = u;
+ if ( u > max_pos ) max_pos = u;
+
+ if ( point->out_dir != segment_dir || point == last)
+ {
+ /* we're just leaving an edge, record a new segment !! */
+ segment->last = point;
+ segment->pos = (min_pos + max_pos) >> 1;
+
+ /* a segment is round if either its first or last point */
+ /* is a control point.. */
+ if ( (segment->first->flags | point->flags) & ah_flah_control )
+ segment->flags |= ah_edge_round;
+
+ /* compute segment size */
+ min_pos = max_pos = point->v;
+ v = segment->first->v;
+ if (v < min_pos) min_pos = v;
+ if (v > max_pos) max_pos = v;
+ segment->min_coord = min_pos;
+ segment->max_coord = max_pos;
+
+ on_edge = 0;
+ num_segments++;
+ segment++;
+ /* fallthrough */
+ }
+ }
+
+ /* now exit if we're at the start/end point */
+ if (point == last)
+ {
+ if (passed)
+ break;
+ passed = 1;
+ }
+
+ if ( !on_edge && ABS(point->out_dir) == major_dir )
+ {
+ /* this is the start of a new segment ! */
+ segment_dir = point->out_dir;
+
+ /* clear all segment fields */
+ memset( segment, 0, sizeof(*segment) );
+
+ segment->dir = segment_dir;
+ segment->flags = ah_edge_normal;
+ min_pos = max_pos = point->u;
+ segment->first = point;
+ segment->last = point;
+ segment->contour = contour;
+ on_edge = 1;
+
+ if (point == max_point)
+ max_point = 0;
+
+ if (point == min_point)
+ min_point = 0;
+ }
+
+ point = point->next;
+ }
+
+ } /* contours */
+
+
+#ifdef AH_HINT_METRICS
+ /* we need to ensure that there are edges on the left-most and */
+ /* right-most points of the glyph in order to hint the metrics */
+ /* we do this by inserting fake segments when needed.. */
+ if (dimension == 0)
+ {
+ AH_Point* point = outline->points;
+ AH_Point* limit = point + outline->num_points;
+
+ AH_Point* min_point = 0;
+ AH_Point* max_point = 0;
+ FT_Pos min_pos = 32000;
+ FT_Pos max_pos = -32000;
+
+ /* compute minimum and maximum points */
+ for ( ; point < limit; point++ )
+ {
+ FT_Pos x = point->fx;
+
+ if ( x < min_pos )
+ {
+ min_pos = x;
+ min_point = point;
+ }
+ if ( x > max_pos )
+ {
+ max_pos = x;
+ max_point = point;
+ }
+ }
+
+ /* insert minimum segment */
+ if (min_point)
+ {
+ /* clear all segment fields */
+ memset( segment, 0, sizeof(*segment) );
+
+ segment->dir = segment_dir;
+ segment->flags = ah_edge_normal;
+ segment->first = min_point;
+ segment->last = min_point;
+ segment->pos = min_pos;
+
+ num_segments++;
+ segment++;
+ }
+
+ /* insert maximum segment */
+ if (max_point)
+ {
+ /* clear all segment fields */
+ memset( segment, 0, sizeof(*segment) );
+
+ segment->dir = segment_dir;
+ segment->flags = ah_edge_normal;
+ segment->first = max_point;
+ segment->last = max_point;
+ segment->pos = max_pos;
+
+ num_segments++;
+ segment++;
+ }
+ }
+#endif
+
+
+ *p_num_segments = num_segments;
+
+ segments = outline->vert_segments;
+ major_dir = ah_dir_up;
+ p_num_segments = &outline->num_vsegments;
+ ah_setup_uv( outline, ah_uv_fxy );
+ }
+ }
+
+
+
+ LOCAL_FUNC
+ void ah_outline_link_segments( AH_Outline* outline )
+ {
+ AH_Segment* segments;
+ AH_Segment* limit;
+ int dimension;
+
+ ah_setup_uv( outline, ah_uv_fyx );
+
+ segments = outline->horz_segments;
+ limit = segments + outline->num_hsegments;
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Segment* seg1;
+ AH_Segment* seg2;
+
+ /* now compare each segment to the others */
+ for ( seg1 = segments; seg1 < limit; seg1++ )
+ {
+ FT_Pos best_score = 32000;
+ AH_Segment* best_segment = 0;
+
+ /* the fake segments are introduced to hint the metrics */
+ /* we must never link them to anything.. */
+ if (seg1->first == seg1->last)
+ continue;
+
+ for ( seg2 = segments; seg2 < limit; seg2++ )
+ if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
+ {
+ FT_Pos pos1 = seg1->pos;
+ FT_Pos pos2 = seg2->pos;
+ FT_Bool is_dir;
+ FT_Bool is_pos;
+
+ /* check that the segments are correctly oriented and positioned */
+ /* to form a black distance.. */
+
+ is_dir = ( seg1->dir == outline->horz_major_dir ||
+ seg1->dir == outline->vert_major_dir );
+ is_pos = pos1 > pos2;
+
+ if ( pos1 == pos2 || !(is_dir ^ is_pos) )
+ continue;
+
+ /* check the two segments, we now have a better algorithm */
+ /* that doesn't rely on the segment points themselves but */
+ /* on their relative position. This gets rids of many */
+ /* unpleasant artefacts and incorrect stem/serifs */
+ /* computations.. */
+
+ /* first of all, compute the size of the "common" height */
+ {
+ FT_Pos min = seg1->min_coord;
+ FT_Pos max = seg1->max_coord;
+ FT_Pos len, score;
+ FT_Pos size1, size2;
+
+ size1 = max - min;
+ size2 = seg2->max_coord - seg2->min_coord;
+
+ if ( min < seg2->min_coord )
+ min = seg2->min_coord;
+
+ if ( max < seg2->max_coord )
+ max = seg2->max_coord;
+
+ len = max - min;
+ score = seg2->pos - seg1->pos;
+ if (score < 0)
+ score = -score;
+
+ /* before comparing the scores, take care that the segments */
+ /* are really facing each other (often not for italics..) */
+ if ( 4*len >= size1 && 4*len >= size2 )
+ if (score < best_score)
+ {
+ best_score = score;
+ best_segment = seg2;
+ }
+ }
+ }
+
+ if (best_segment)
+ {
+ seg1->link = best_segment;
+ seg1->score = best_score;
+ best_segment->num_linked++;
+ }
+
+
+ } /* edges 1 */
+
+ /* now, compute the "serif" segments */
+ for ( seg1 = segments; seg1 < limit; seg1++ )
+ {
+ seg2 = seg1->link;
+ if (seg2 && seg2->link != seg1)
+ {
+ seg1->link = 0;
+ seg1->serif = seg2->link;
+ }
+ }
+
+ ah_setup_uv( outline, ah_uv_fxy );
+
+ segments = outline->vert_segments;
+ limit = segments + outline->num_vsegments;
+ }
+ }
+
+
+#ifdef AH_DEBUG_GLYPH
+ /* A function used to dump the array of linked segments */
+ extern
+ void ah_dump_segments( AH_Outline* outline )
+ {
+ AH_Segment* segments;
+ AH_Segment* limit;
+ AH_Point* points;
+ FT_Int dimension;
+
+ points = outline->points;
+ segments = outline->horz_segments;
+ limit = segments + outline->num_hsegments;
+
+ for (dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Segment* seg;
+
+ printf ( "Table of %s segments:\n", !dimension ? "vertical" : "horizontal" );
+ printf ( " [ index | pos | dir | link | serif | numl | first | start ]\n" );
+
+ for ( seg = segments; seg < limit; seg++ )
+ {
+ printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
+ seg - segments,
+ (int)seg->pos,
+ seg->dir == ah_dir_up ? "up" :
+ (seg->dir == ah_dir_down ? "down" :
+ (seg->dir == ah_dir_left ? "left" :
+ (seg->dir == ah_dir_right ? "right" : "none")
+ )
+ ),
+ seg->link ? (seg->link-segments) : -1,
+ seg->serif ? (seg->serif-segments) : -1,
+ (int)seg->num_linked,
+ seg->first - points,
+ seg->last - points );
+ }
+ segments = outline->vert_segments;
+ limit = segments + outline->num_vsegments;
+ }
+ }
+#endif
+
+
+
+ static
+ void ah_outline_compute_edges( AH_Outline* outline )
+ {
+ AH_Edge* edges;
+ AH_Segment* segments;
+ AH_Segment* segment_limit;
+ AH_Direction up_dir;
+ FT_Int* p_num_edges;
+ FT_Int dimension;
+ FT_Fixed scale;
+ FT_Pos edge_distance_threshold;
+
+ edges = outline->horz_edges;
+ segments = outline->horz_segments;
+ segment_limit = segments + outline->num_hsegments;
+ p_num_edges = &outline->num_hedges;
+ up_dir = ah_dir_right;
+ scale = outline->y_scale;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Edge* edge;
+ AH_Edge* edge_limit; /* really == edge + num_edges */
+ AH_Segment* seg;
+
+ /**********************************************************************/
+ /* */
+ /* We will begin by generating a sorted table of edges for the */
+ /* current direction. To do so, we simply scan each segment and */
+ /* try to find an edge in our table that corresponds to its position */
+ /* */
+ /* If no edge is found, we create and insert a new edge in the */
+ /* sorted table. Otherwise, we simply add the segment to the */
+ /* edge's list which will be processed in the second step to */
+ /* compute the edge's properties.. */
+ /* */
+ /* Note that the edges table is sorted along the segment/edge */
+ /* position. */
+ /* */
+
+ edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
+ scale );
+ if (edge_distance_threshold > 64/4)
+ edge_distance_threshold = 64/4;
+
+ edge_limit = edges;
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AH_Edge* found = 0;
+
+ /* look for an edge corresponding to the segment */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Pos dist;
+
+ dist = seg->pos - edge->fpos;
+ if (dist < 0) dist = -dist;
+
+ dist = FT_MulFix( dist, scale );
+ if ( dist < edge_distance_threshold )
+ {
+ found = edge;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ /* insert a new edge in the list. Sort according to the position */
+ while ( edge > edges && edge[-1].fpos > seg->pos )
+ {
+ edge[0] = edge[-1];
+ edge--;
+ }
+ edge_limit++;
+
+ /* clear all edge fields */
+ memset( edge, 0, sizeof(*edge) );
+
+ /* add the segment to the new edge's list */
+ edge->first = seg;
+ edge->last = seg;
+ edge->fpos = seg->pos;
+ edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
+ seg->edge_next = seg;
+ }
+ else
+ {
+ /* if an edge was found, simply add the segment to the edge's */
+ /* list */
+ seg->edge_next = edge->first;
+ edge->last->edge_next = seg;
+ edge->last = seg;
+ }
+ }
+
+ *p_num_edges = edge_limit - edges;
+
+
+ /**********************************************************************/
+ /* */
+ /* Good, we will now compute each edge's properties according to */
+ /* segments found on its position. Basically, these are: */
+ /* */
+ /* - edge's main direction */
+ /* - stem edge, serif edge or both (which defaults to stem then) */
+ /* - rounded edge, straigth or both (which defaults to straight) */
+ /* - link for edge.. */
+ /* */
+
+ /* first of all, set the "edge" field in each segment - this is */
+ /* required in order to compute edge links.. */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ seg = edge->first;
+ if (seg)
+ do
+ {
+ seg->edge = edge;
+ seg = seg->edge_next;
+ }
+ while ( seg != edge->first );
+ }
+
+ /* now, compute each edge properties */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ int is_round = 0; /* does it contain round segments ? */
+ int is_straight = 0; /* does it contain straight segments ? */
+ int ups = 0; /* number of upwards segments */
+ int downs = 0; /* number of downwards segments */
+
+ seg = edge->first;
+ do
+ {
+ FT_Bool is_serif;
+
+ /* check for roundness of segment */
+ if ( seg->flags & ah_edge_round ) is_round++;
+ else is_straight++;
+
+ /* check for segment direction */
+ if ( seg->dir == up_dir ) ups += (seg->max_coord-seg->min_coord);
+ else downs += (seg->max_coord-seg->min_coord);
+
+ /* check for links - if seg->serif is set, then seg->link must be */
+ /* ignored.. */
+ is_serif = seg->serif && seg->serif->edge != edge;
+
+ if ( seg->link || is_serif )
+ {
+ AH_Edge* edge2;
+ AH_Segment* seg2;
+
+ edge2 = edge->link;
+ seg2 = seg->link;
+
+ if (is_serif)
+ {
+ seg2 = seg->serif;
+ edge2 = edge->serif;
+ }
+
+ if (edge2)
+ {
+ FT_Pos edge_delta;
+ FT_Pos seg_delta;
+
+ edge_delta = edge->fpos - edge2->fpos;
+ if (edge_delta < 0) edge_delta = -edge_delta;
+
+ seg_delta = seg->pos - seg2->pos;
+ if (seg_delta < 0) seg_delta = -seg_delta;
+
+ if (seg_delta < edge_delta)
+ edge2 = seg2->edge;
+ }
+ else
+ edge2 = seg2->edge;
+
+ if (is_serif)
+ edge->serif = edge2;
+ else
+ edge->link = edge2;
+ }
+
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+
+ /* set the round/straight flags */
+ edge->flags = ah_edge_normal;
+
+ if ( is_straight == 0 && is_round )
+ edge->flags |= ah_edge_round;
+
+ /* set the edge's main direction */
+ edge->dir = ah_dir_none;
+
+ if ( ups > downs )
+ edge->dir = up_dir;
+
+ else if ( ups < downs )
+ edge->dir = - up_dir;
+
+ else if ( ups == downs )
+ edge->dir = 0; /* both up and down !! */
+
+ /* gets rid of serifs if link is set */
+ /* ZZZZ: this gets rid of many unpleasant artefacts !! */
+ /* example : the "c" in cour.pfa at size 13 */
+
+ if (edge->serif && edge->link)
+ edge->serif = 0;
+ }
+
+ edges = outline->vert_edges;
+ segments = outline->vert_segments;
+ segment_limit = segments + outline->num_vsegments;
+ p_num_edges = &outline->num_vedges;
+ up_dir = ah_dir_up;
+ scale = outline->x_scale;
+ }
+ }
+
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_detect_features
+ *
+ * <Description>
+ * Performs feature detection on a given AH_Outline
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ void ah_outline_detect_features( AH_Outline* outline )
+ {
+ ah_outline_compute_segments( outline );
+ ah_outline_link_segments ( outline );
+ ah_outline_compute_edges ( outline );
+ }
+
+
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_compute_blue_edges
+ *
+ * <Description>
+ * Computes the "blue edges" in a given outline (i.e. those that
+ * must be snapped to a blue zone edge (top or bottom)
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ void ah_outline_compute_blue_edges( AH_Outline* outline,
+ AH_Face_Globals* face_globals )
+ {
+ AH_Edge* edge = outline->horz_edges;
+ AH_Edge* limit = edge + outline->num_hedges;
+ AH_Globals* globals = &face_globals->design;
+ FT_Fixed y_scale = outline->y_scale;
+
+ /* compute for each horizontal edge, which blue zone is closer */
+ for ( ; edge < limit; edge++ )
+ {
+ AH_Blue blue;
+ FT_Pos* best_blue = 0;
+ FT_Pos best_dist; /* initial threshold */
+
+ /* compute the initial threshold as a fraction of the EM size */
+ best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
+ if (best_dist > 64/4)
+ best_dist = 64/4;
+
+ for ( blue = (AH_Blue)0; blue < ah_blue_max; blue++ )
+ {
+ /* if it is a top zone, check for right edges - if it is a bottom */
+ /* zone, check for left edges.. */
+ /* of course, that's for TrueType .. XXXX */
+ FT_Bool is_top_blue = AH_IS_TOP_BLUE(blue);
+ FT_Bool is_major_dir = edge->dir == outline->horz_major_dir;
+
+ /* if it's a top zone, the edge must be against the major direction */
+ /* if it's a bottom zone, it must be in the major direction */
+ if ( is_top_blue ^ is_major_dir )
+ {
+ FT_Pos dist;
+ FT_Pos* blue_pos = globals->blue_refs + blue;
+
+ /* first of all, compare it to the reference position */
+ dist = edge->fpos - *blue_pos;
+ if (dist < 0) dist = -dist;
+
+ dist = FT_MulFix( dist, y_scale );
+ if (dist < best_dist)
+ {
+ best_dist = dist;
+ best_blue = blue_pos;
+ }
+
+ /* now, compare it to the overshoot position if the edge is rounded */
+ /* and when the edge is over the reference position of a top zone, */
+ /* or under the reference position of a bottom zone */
+ if ( edge->flags & ah_edge_round && dist != 0 )
+ {
+ FT_Bool is_under_ref = edge->fpos < *blue_pos;
+
+ if ( is_top_blue ^ is_under_ref )
+ {
+ blue_pos = globals->blue_shoots + blue;
+ dist = edge->fpos - *blue_pos;
+ if (dist < 0) dist = -dist;
+
+ dist = FT_MulFix( dist, y_scale );
+ if (dist < best_dist)
+ {
+ best_dist = dist;
+ best_blue = blue_pos;
+ }
+ }
+ }
+ }
+ }
+
+ if (best_blue)
+ edge->blue_edge = best_blue;
+ }
+ }
+
+
+ /************************************************************************
+ *
+ * <Function>
+ * ah_outline_scale_blue_edges
+ *
+ * <Description>
+ * This functions must be called before hinting in order to re-adjust
+ * the content of the detected edges (basically change the "blue edge"
+ * pointer from 'design units' to 'scaled ones'
+ *
+ ************************************************************************/
+
+ LOCAL_FUNC
+ void ah_outline_scale_blue_edges( AH_Outline* outline,
+ AH_Face_Globals* globals )
+ {
+ AH_Edge* edge = outline->horz_edges;
+ AH_Edge* limit = edge + outline->num_hedges;
+ FT_Int delta;
+
+ delta = globals->scaled.blue_refs - globals->design.blue_refs;
+ for ( ; edge < limit; edge++ )
+ {
+ if (edge->blue_edge)
+ edge->blue_edge += delta;
+ }
+ }
+
+
+
+
+
+#ifdef AH_DEBUG_GLYPH
+ extern
+ void ah_dump_edges( AH_Outline* outline )
+ {
+ AH_Edge* edges;
+ AH_Edge* limit;
+ AH_Segment* segments;
+ FT_Int dimension;
+
+ edges = outline->horz_edges;
+ limit = edges + outline->num_hedges;
+ segments = outline->horz_segments;
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Edge* edge;
+
+ printf ( "Table of %s edges:\n", !dimension ? "vertical" : "horizontal" );
+ printf ( " [ index | pos | dir | link | serif | blue | opos | pos ]\n" );
+
+ for ( edge = edges; edge < limit; edge++ )
+ {
+ printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n",
+ edge - edges,
+ (int)edge->fpos,
+ edge->dir == ah_dir_up ? "up" :
+ (edge->dir == ah_dir_down ? "down" :
+ (edge->dir == ah_dir_left ? "left" :
+ (edge->dir == ah_dir_right ? "right" : "none")
+ )
+ ),
+ edge->link ? (edge->link-edges) : -1,
+ edge->serif ? (edge->serif-edges) : -1,
+ edge->blue_edge ? 'y' : 'n',
+ edge->opos/64.0,
+ edge->pos/64.0 );
+ }
+
+
+ edges = outline->vert_edges;
+ limit = edges + outline->num_vedges;
+ segments = outline->vert_segments;
+ }
+ }
+#endif
--- /dev/null
+++ b/src/autohint/ahglyph.h
@@ -1,0 +1,79 @@
+/***************************************************************************/
+/* */
+/* ahglyph.h */
+/* */
+/* routines used to load and analyze a given glyph before hinting */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifndef AGGLYPH_H
+#define AGGLYPH_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+ typedef enum AH_UV_
+ {
+ ah_uv_fxy,
+ ah_uv_fyx,
+ ah_uv_oxy,
+ ah_uv_oyx,
+ ah_uv_ox,
+ ah_uv_oy,
+ ah_uv_yx,
+ ah_uv_xy /* should always be last !! */
+
+ } AH_UV;
+
+ LOCAL_DEF
+ void ah_setup_uv( AH_Outline* outline,
+ AH_UV source );
+
+
+ /* AH_Outline functions - they should be typically called in this order */
+
+ LOCAL_DEF
+ FT_Error ah_outline_new( FT_Memory memory, AH_Outline* *aoutline );
+
+ LOCAL_DEF
+ FT_Error ah_outline_load( AH_Outline* outline, FT_Face face );
+
+ LOCAL_DEF
+ void ah_outline_compute_segments( AH_Outline* outline );
+
+ LOCAL_DEF
+ void ah_outline_link_segments( AH_Outline* outline );
+
+ LOCAL_DEF
+ void ah_outline_detect_features( AH_Outline* outline );
+
+ LOCAL_DEF
+ void ah_outline_compute_blue_edges( AH_Outline* outline,
+ AH_Face_Globals* globals );
+
+ LOCAL_DEF
+ void ah_outline_scale_blue_edges( AH_Outline* outline,
+ AH_Face_Globals* globals );
+
+ LOCAL_DEF
+ void ah_outline_save( AH_Outline* outline, AH_Loader* loader );
+
+ LOCAL_DEF
+ void ah_outline_done( AH_Outline* outline );
+
+
+#endif /* AGGLYPH_H */
--- /dev/null
+++ b/src/autohint/ahhint.c
@@ -1,0 +1,1293 @@
+/***************************************************************************/
+/* */
+/* ahhint.c */
+/* */
+/* Glyph hinter */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+
+#ifdef FT_FLAT_COMPILE
+#include "ahhint.h"
+#include "ahglyph.h"
+#include "ahangles.h"
+#else
+#include <autohint/ahhint.h>
+#include <autohint/ahglyph.h>
+#include <autohint/ahangles.h>
+#endif
+
+#include <freetype/ftoutln.h>
+
+#define FACE_GLOBALS(face) ((AH_Face_Globals*)(face)->autohint.data)
+
+#define AH_USE_IUP
+
+
+ /*******************************************************************/
+ /*******************************************************************/
+ /**** ****/
+ /**** Hinting routines ****/
+ /**** ****/
+ /*******************************************************************/
+ /*******************************************************************/
+
+ static int disable_horz_edges = 0;
+ static int disable_vert_edges = 0;
+
+ /* snap a given width in scaled coordinates to one of the */
+ /* current standard widths.. */
+ static
+ FT_Pos ah_snap_width( FT_Pos* widths,
+ FT_Int count,
+ FT_Pos width )
+ {
+ int n;
+ FT_Pos best = 64+32+2;
+ FT_Pos reference = width;
+
+ for ( n = 0; n < count; n++ )
+ {
+ FT_Pos w;
+ FT_Pos dist;
+
+ w = widths[n];
+ dist = width - w;
+ if (dist < 0) dist = -dist;
+ if (dist < best)
+ {
+ best = dist;
+ reference = w;
+ }
+ }
+
+ if ( width >= reference )
+ {
+ width -= 0x21;
+ if ( width < reference )
+ width = reference;
+ }
+ else
+ {
+ width += 0x21;
+ if ( width > reference )
+ width = reference;
+ }
+
+ return width;
+ }
+
+
+
+ /* Align one stem edge relative to the previous stem edge */
+ static
+ void ah_align_linked_edge( AH_Hinter* hinter,
+ AH_Edge* base_edge,
+ AH_Edge* stem_edge,
+ int vertical )
+ {
+ FT_Pos dist = stem_edge->opos - base_edge->opos;
+ AH_Globals* globals = &hinter->globals->scaled;
+ FT_Pos sign = 1;
+
+ if (dist < 0)
+ {
+ dist = -dist;
+ sign = -1;
+ }
+
+ if (vertical)
+ {
+ dist = ah_snap_width( globals->heights, globals->num_heights, dist );
+
+ /* in the case of vertical hinting, always round */
+ /* the stem heights to integer pixels.. */
+ if (dist >= 64)
+ dist = (dist+16) & -64;
+ else
+ dist = 64;
+ }
+ else
+ {
+ dist = ah_snap_width( globals->widths, globals->num_widths, dist );
+
+ if (hinter->flags & ah_hinter_monochrome)
+ {
+ /* monochrome horizontal hinting: snap widths to integer pixels */
+ /* with a different threshold.. */
+ if (dist < 64)
+ dist = 64;
+ else
+ dist = (dist+32) & -64;
+ }
+ else
+ {
+ /* for horizontal anti-aliased hinting, we adopt a more subtle */
+ /* approach, we strengthen small stems, round stems whose size */
+ /* is between 1 and 2 pixels to an integer, otherwise nothing */
+ if (dist < 48)
+ dist = (dist+64) >> 1;
+
+ else if (dist < 128)
+ dist = (dist+42) & -64;
+
+ }
+ }
+ stem_edge->pos = base_edge->pos + sign*dist;
+ }
+
+
+
+ static
+ void ah_align_serif_edge( AH_Hinter* hinter,
+ AH_Edge* base,
+ AH_Edge* serif )
+ {
+ FT_Pos dist;
+ FT_Pos sign = 1;
+
+ UNUSED(hinter);
+ dist = serif->opos - base->opos;
+ if (dist < 0)
+ {
+ dist = -dist;
+ sign = -1;
+ }
+
+ if (base->flags & ah_edge_done)
+ /* do not strengthen serifs */
+ {
+ if (dist > 64)
+ dist = (dist+16) & -64;
+
+ else if (dist <= 32)
+ dist = (dist+33) >> 1;
+ }
+ serif->pos = base->pos + sign*dist;
+ }
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**** ****/
+ /**** E D G E H I N T I N G ****/
+ /**** ****/
+ /**** ****/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+
+ /* Another alternative edge hinting algorithm */
+ static
+ void ah_hint_edges_3( AH_Hinter* hinter )
+ {
+ AH_Edge* edges;
+ AH_Edge* edge_limit;
+ AH_Outline* outline = hinter->glyph;
+ FT_Int dimension;
+
+ edges = outline->horz_edges;
+ edge_limit = edges + outline->num_hedges;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Edge* edge;
+ AH_Edge* before = 0;
+ AH_Edge* after = 0;
+ AH_Edge* anchor = 0;
+ int has_serifs = 0;
+
+ if (disable_vert_edges && !dimension)
+ goto Next_Dimension;
+
+ if (disable_horz_edges && dimension)
+ goto Next_Dimension;
+
+ /* we begin by aligning all stems relative to the blue zone */
+ /* if needed.. that's only for horizontal edges.. */
+ if (dimension)
+ {
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Pos* blue;
+ AH_Edge *edge1, *edge2;
+
+ if (edge->flags & ah_edge_done)
+ continue;
+
+ blue = edge->blue_edge;
+ edge1 = 0;
+ edge2 = edge->link;
+
+ if (blue)
+ {
+ edge1 = edge;
+ }
+ else if (edge2 && edge2->blue_edge)
+ {
+ blue = edge2->blue_edge;
+ edge1 = edge2;
+ edge2 = edge;
+ }
+
+ if (!edge1)
+ continue;
+
+ edge1->pos = blue[0];
+ edge1->flags |= ah_edge_done;
+
+ if (edge2 && !edge2->blue_edge)
+ {
+ ah_align_linked_edge( hinter, edge1, edge2, dimension );
+ edge2->flags |= ah_edge_done;
+ }
+
+ if (!anchor)
+ anchor = edge;
+ }
+ }
+
+ /* now, we will align all stem edges, trying to maintain the */
+ /* relative order of stems in the glyph.. */
+ before = 0;
+ after = 0;
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AH_Edge *edge2;
+
+ if (edge->flags & ah_edge_done)
+ continue;
+
+ /* skip all non-stem edges */
+ edge2 = edge->link;
+ if (!edge2)
+ {
+ has_serifs++;
+ continue;
+ }
+
+ /* now, align the stem */
+
+ /* this should not happen, but it's better to be safe.. */
+ if (edge2->blue_edge || edge2 < edge)
+ {
+#if 0
+ printf( "strange blue alignement, edge %d to %d\n",
+ edge - edges, edge2 - edges );
+#endif
+ ah_align_linked_edge( hinter, edge2, edge, dimension );
+ edge->flags |= ah_edge_done;
+ continue;
+ }
+
+ {
+ FT_Bool min = 0;
+ FT_Pos delta;
+
+ if (!anchor)
+ {
+ edge->pos = (edge->opos+32) & -64;
+ anchor = edge;
+ }
+ else
+ edge->pos = anchor->pos + ((edge->opos - anchor->opos + 32) & -64);
+
+ edge->flags |= ah_edge_done;
+
+ if (edge > edges && edge->pos < edge[-1].pos)
+ {
+ edge->pos = edge[-1].pos;
+ min = 1;
+ }
+
+ ah_align_linked_edge( hinter, edge, edge2, dimension );
+ delta = 0;
+ if ( edge2+1 < edge_limit &&
+ edge2[1].flags & ah_edge_done )
+ delta = edge2[1].pos - edge2->pos;
+
+ if (delta < 0)
+ {
+ edge2->pos += delta;
+ if (!min)
+ edge->pos += delta;
+ }
+ edge2->flags |= ah_edge_done;
+ }
+ }
+
+ if (!has_serifs)
+ goto Next_Dimension;
+
+ /* now, hint the remaining edges (serifs and single) in order */
+ /* to complete our processing.. */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ if (edge->flags & ah_edge_done)
+ continue;
+
+ if (edge->serif)
+ {
+ ah_align_serif_edge( hinter, edge->serif, edge );
+ }
+ else if (!anchor)
+ {
+ edge->pos = (edge->opos+32) & -64;
+ anchor = edge;
+ }
+ else
+ edge->pos = anchor->pos + ((edge->opos-anchor->opos+32) & -64);
+
+ edge->flags |= ah_edge_done;
+
+ if (edge > edges && edge->pos < edge[-1].pos)
+ edge->pos = edge[-1].pos;
+
+ if ( edge+1 < edge_limit && edge[1].flags & ah_edge_done &&
+ edge->pos > edge[1].pos)
+ edge->pos = edge[1].pos;
+ }
+
+ Next_Dimension:
+ edges = outline->vert_edges;
+ edge_limit = edges + outline->num_vedges;
+ }
+
+ }
+
+
+
+
+
+
+
+ LOCAL_FUNC
+ void ah_hinter_hint_edges( AH_Hinter* hinter,
+ int no_horz_edges,
+ int no_vert_edges )
+ {
+ disable_horz_edges = no_horz_edges;
+ disable_vert_edges = no_vert_edges;
+
+ /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help */
+ /* reduce the problem of the disappearing eye in the "e" of Times */
+ /* also, creates some artifacts near the blue zones ?? */
+ {
+ ah_hint_edges_3( hinter );
+
+ /* outline optimiser removed temporarily */
+#if 0
+ if (hinter->flags & ah_hinter_optimize)
+ {
+ AH_Optimizer opt;
+
+ if (!AH_Optimizer_Init( &opt, hinter->glyph, hinter->memory ))
+ {
+ AH_Optimizer_Compute( &opt );
+ AH_Optimizer_Done( &opt );
+ }
+ }
+#endif
+ }
+ }
+
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**** ****/
+ /**** P O I N T H I N T I N G ****/
+ /**** ****/
+ /**** ****/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+
+ static
+ void ah_hinter_align_edge_points( AH_Hinter* hinter )
+ {
+ AH_Outline* outline = hinter->glyph;
+ AH_Edge* edges;
+ AH_Edge* edge_limit;
+ FT_Int dimension;
+
+ edges = outline->horz_edges;
+ edge_limit = edges + outline->num_hedges;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Edge* edge;
+ AH_Edge* before;
+ AH_Edge* after;
+
+ before = 0;
+ after = 0;
+
+ edge = edges;
+ for ( ; edge < edge_limit; edge++ )
+ {
+ /* move the points of each segment in each edge to the edge's position */
+ AH_Segment* seg = edge->first;
+ do
+ {
+ AH_Point* point = seg->first;
+ for (;;)
+ {
+ if (dimension)
+ {
+ point->y = edge->pos;
+ point->flags |= ah_flah_touch_y;
+ }
+ else
+ {
+ point->x = edge->pos;
+ point->flags |= ah_flah_touch_x;
+ }
+ if (point == seg->last)
+ break;
+
+ point = point->next;
+ }
+
+ seg = seg->edge_next;
+ }
+ while (seg != edge->first);
+ }
+ edges = outline->vert_edges;
+ edge_limit = edges + outline->num_vedges;
+ }
+ }
+
+
+ /* hint the strong points - this is equivalent to the TrueType "IP" */
+ static
+ void ah_hinter_align_strong_points( AH_Hinter* hinter )
+ {
+ AH_Outline* outline = hinter->glyph;
+ FT_Int dimension;
+ AH_Edge* edges;
+ AH_Edge* edge_limit;
+ AH_Point* points;
+ AH_Point* point_limit;
+ AH_Flags touch_flag;
+
+ points = outline->points;
+ point_limit = points + outline->num_points;
+
+ edges = outline->horz_edges;
+ edge_limit = edges + outline->num_hedges;
+ touch_flag = ah_flah_touch_y;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Point* point;
+ AH_Edge* edge;
+ AH_Edge* before;
+ AH_Edge* after;
+
+ before = 0;
+ after = 0;
+
+ if ( edges < edge_limit )
+ for ( point = points; point < point_limit; point++ )
+ {
+ FT_Pos u, ou, fu; /* point position */
+ FT_Pos delta;
+
+ if ( point->flags & touch_flag )
+ continue;
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+ /* if this point is candidate to weak interpolation, we'll */
+ /* interpolate it after all strong points have been processed */
+ if ( point->flags & ah_flah_weak_interpolation )
+ continue;
+#endif
+
+ if (dimension) { u = point->fy; ou = point->oy; }
+ else { u = point->fx; ou = point->ox; }
+
+ fu = u;
+
+ /* is the point before the first edge ? */
+ edge = edges;
+ delta = edge->fpos - u;
+ if (delta >= 0)
+ {
+ u = edge->pos - (edge->opos - ou);
+ goto Store_Point;
+ }
+
+ /* is the point after the last edge ? */
+ edge = edge_limit-1;
+ delta = u - edge->fpos;
+ if ( delta >= 0 )
+ {
+ u = edge->pos + (ou - edge->opos);
+ goto Store_Point;
+ }
+
+ /* otherwise, interpolate the point in between */
+ {
+ AH_Edge* before = 0;
+ AH_Edge* after = 0;
+
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ if ( u == edge->fpos )
+ {
+ u = edge->pos;
+ goto Store_Point;
+ }
+ if ( u < edge->fpos )
+ break;
+ before = edge;
+ }
+
+ for ( edge = edge_limit-1; edge >= edges; edge-- )
+ {
+ if ( u == edge->fpos )
+ {
+ u = edge->pos;
+ goto Store_Point;
+ }
+ if ( u > edge->fpos )
+ break;
+ after = edge;
+ }
+
+ /* assert( before && after && before != after ) */
+ u = before->pos + FT_MulDiv( fu - before->fpos,
+ after->pos - before->pos,
+ after->fpos - before->fpos );
+ }
+ Store_Point:
+
+ /* save the point position */
+ if (dimension) point->y = u;
+ else point->x = u;
+
+ point->flags |= touch_flag;
+ }
+
+ edges = outline->vert_edges;
+ edge_limit = edges + outline->num_vedges;
+ touch_flag = ah_flah_touch_x;
+ }
+ }
+
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+ static
+ void ah_iup_shift( AH_Point* p1,
+ AH_Point* p2,
+ AH_Point* ref )
+ {
+ AH_Point* p;
+ FT_Pos delta = ref->u - ref->v;
+
+ for ( p = p1; p < ref; p++ )
+ p->u = p->v + delta;
+
+ for ( p = ref+1; p <= p2; p++ )
+ p->u = p->v + delta;
+ }
+
+
+ static
+ void ah_iup_interp( AH_Point* p1,
+ AH_Point* p2,
+ AH_Point* ref1,
+ AH_Point* ref2 )
+ {
+ AH_Point* p;
+ FT_Pos u;
+ FT_Pos v1 = ref1->v;
+ FT_Pos v2 = ref2->v;
+ FT_Pos d1 = ref1->u - v1;
+ FT_Pos d2 = ref2->u - v2;
+
+ if (p1 > p2) return;
+
+ if (v1 == v2)
+ {
+ for ( p = p1; p <= p2; p++ )
+ {
+ FT_Pos u = p->v;
+
+ if (u <= v1) u += d1;
+ else u += d2;
+ p->u = u;
+ }
+ return;
+ }
+
+ if ( v1 < v2 )
+ {
+ for ( p = p1; p <= p2; p++ )
+ {
+ u = p->v;
+
+ if (u <= v1) u += d1;
+ else if (u >= v2) u += d2;
+ else u = ref1->u+FT_MulDiv( u-v1, ref2->u-ref1->u, v2-v1 );
+
+ p->u = u;
+ }
+ }
+ else
+ {
+ for ( p = p1; p <= p2; p++ )
+ {
+ u = p->v;
+ if (u <= v2) u += d2;
+ else if (u >= v1) u += d1;
+ else u = ref1->u+FT_MulDiv( u-v1, ref2->u-ref1->u, v2-v1 );
+
+ p->u = u;
+ }
+ }
+ }
+
+ /* interpolate weak points - this is equivalent to the TrueType "IUP" */
+ static
+ void ah_hinter_align_weak_points( AH_Hinter* hinter )
+ {
+ AH_Outline* outline = hinter->glyph;
+ FT_Int dimension;
+ AH_Edge* edges;
+ AH_Edge* edge_limit;
+ AH_Point* points;
+ AH_Point* point_limit;
+ AH_Point** contour_limit;
+ AH_Flags touch_flag;
+
+ points = outline->points;
+ point_limit = points + outline->num_points;
+
+ /* PASS 1 : Move segment points to edge positions */
+
+ edges = outline->horz_edges;
+ edge_limit = edges + outline->num_hedges;
+ touch_flag = ah_flah_touch_y;
+
+ contour_limit = outline->contours + outline->num_contours;
+
+ ah_setup_uv( outline, ah_uv_oy );
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Point* point;
+ AH_Point* end_point;
+ AH_Point* first_point;
+ AH_Point** contour;
+
+ point = points;
+ contour = outline->contours;
+
+ for ( ; contour < contour_limit; contour++ )
+ {
+ point = *contour;
+ end_point = point->prev;
+ first_point = point;
+
+ while (point <= end_point && !(point->flags & touch_flag))
+ point++;
+
+ if (point <= end_point)
+ {
+ AH_Point* first_touched = point;
+ AH_Point* cur_touched = point;
+
+ point++;
+ while ( point <= end_point )
+ {
+ if (point->flags & touch_flag)
+ {
+ /* we found two succesive touched points, we interpolate */
+ /* all contour points between them.. */
+ ah_iup_interp( cur_touched+1, point-1,
+ cur_touched, point );
+ cur_touched = point;
+ }
+ point++;
+ }
+
+ if (cur_touched == first_touched)
+ {
+ /* this is a special case: only one point was touched in the */
+ /* contour.. we thus simply shift the whole contour.. */
+ ah_iup_shift( first_point, end_point, cur_touched );
+ }
+ else
+ {
+ /* now interpolate after the last touched point to the end */
+ /* of the contour.. */
+ ah_iup_interp( cur_touched+1, end_point,
+ cur_touched, first_touched );
+
+ /* if the first contour point isn't touched, interpolate */
+ /* from the contour start to the first touched point */
+ if (first_touched > points)
+ ah_iup_interp( first_point, first_touched-1,
+ cur_touched, first_touched );
+ }
+ }
+ }
+
+ /* now save the interpolated values back to x/y */
+ if (dimension)
+ {
+ for ( point = points; point < point_limit; point++ )
+ point->y = point->u;
+
+ touch_flag = ah_flah_touch_x;
+ ah_setup_uv( outline, ah_uv_ox );
+ }
+ else
+ {
+ for ( point = points; point < point_limit; point++ )
+ point->x = point->u;
+
+ break; /* exit loop */
+ }
+ }
+ }
+#endif
+
+ LOCAL_FUNC
+ void ah_hinter_align_points( AH_Hinter* hinter )
+ {
+ ah_hinter_align_edge_points( hinter );
+
+#ifndef AH_OPTION_NO_STRONG_INTERPOLATION
+ ah_hinter_align_strong_points( hinter );
+#endif
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+ ah_hinter_align_weak_points( hinter );
+#endif
+ }
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**** ****/
+ /**** H I N T E R O B J E C T M E T H O D S ****/
+ /**** ****/
+ /**** ****/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+
+ /* scale and fit the global metrics */
+ static
+ void ah_hinter_scale_globals( AH_Hinter* hinter,
+ FT_Fixed x_scale,
+ FT_Fixed y_scale )
+ {
+ FT_Int n;
+ AH_Face_Globals* globals = hinter->globals;
+ AH_Globals* design = &globals->design;
+ AH_Globals* scaled = &globals->scaled;
+
+ /* copy content */
+ *scaled = *design;
+
+ /* scale the standard widths & heights */
+ for ( n = 0; n < design->num_widths; n++ )
+ scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
+
+ for ( n = 0; n < design->num_heights; n++ )
+ scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
+
+ /* scale the blue zones */
+ for ( n = 0; n < ah_blue_max; n++ )
+ {
+ FT_Pos delta, delta2;
+
+ delta = design->blue_shoots[n] - design->blue_refs[n];
+ delta2 = delta; if (delta < 0) delta2 = -delta2;
+ delta2 = FT_MulFix( delta2, y_scale );
+
+ if (delta2 < 32)
+ delta2 = 0;
+ else if (delta2 < 64)
+ delta2 = 32 + (((delta2-32)+16) & -32);
+ else
+ delta2 = (delta2+32) & -64;
+
+ if (delta < 0) delta2 = -delta2;
+
+ scaled->blue_refs [n] = (FT_MulFix(design->blue_refs[n],y_scale)+32) & -64;
+ scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
+ }
+ }
+
+
+ static
+ void ah_hinter_align( AH_Hinter* hinter )
+ {
+ ah_hinter_align_edge_points( hinter );
+ ah_hinter_align_points( hinter );
+ }
+
+
+ /* finalise a hinter object */
+ void ah_hinter_done( AH_Hinter* hinter )
+ {
+ if (hinter)
+ {
+ FT_Memory memory = hinter->memory;
+
+ ah_loader_done( hinter->loader );
+ ah_outline_done( hinter->glyph );
+
+ /* note: the globals pointer is _not_ owned by the hinter */
+ /* but by the current face object, we don't need to */
+ /* release it.. */
+ hinter->globals = 0;
+ hinter->face = 0;
+
+ FREE(hinter);
+ }
+ }
+
+
+ /* create a new empty hinter object */
+ FT_Error ah_hinter_new( FT_Library library, AH_Hinter* *ahinter )
+ {
+ AH_Hinter* hinter = 0;
+ FT_Memory memory = library->memory;
+ FT_Error error;
+
+ *ahinter = 0;
+
+ /* allocate object */
+ if (ALLOC( hinter, sizeof(*hinter) )) goto Exit;
+
+ hinter->memory = memory;
+ hinter->flags = 0;
+
+ /* allocate outline and loader */
+ error = ah_outline_new( memory, &hinter->glyph ) ||
+ ah_loader_new ( memory, &hinter->loader ) ||
+ ah_loader_create_extra( hinter->loader );
+ if (error) goto Exit;
+
+ *ahinter = hinter;
+
+ Exit:
+ if (error)
+ ah_hinter_done( hinter );
+
+ return error;
+ }
+
+
+ /* create a face's autohint globals */
+ FT_Error ah_hinter_new_face_globals( AH_Hinter* hinter,
+ FT_Face face,
+ AH_Globals* globals )
+ {
+ FT_Error error;
+ FT_Memory memory = hinter->memory;
+ AH_Face_Globals* face_globals;
+
+ if ( ALLOC( face_globals, sizeof(*face_globals) ) )
+ goto Exit;
+
+ hinter->face = face;
+ hinter->globals = face_globals;
+ if (globals)
+ face_globals->design = *globals;
+ else
+ ah_hinter_compute_globals( hinter );
+
+ face->autohint.data = face_globals;
+ face->autohint.finalizer = (FT_Generic_Finalizer)ah_hinter_done_face_globals;
+ face_globals->face = face;
+
+ Exit:
+ return error;
+ }
+
+
+
+ /* discard a face's autohint globals */
+ void ah_hinter_done_face_globals( AH_Face_Globals* globals )
+ {
+ FT_Face face = globals->face;
+ FT_Memory memory = face->memory;
+
+ FREE( globals );
+ }
+
+
+
+ static
+ FT_Error ah_hinter_load( AH_Hinter* hinter,
+ FT_UInt glyph_index,
+ FT_UInt load_flags,
+ FT_UInt depth )
+ {
+ FT_Face face = hinter->face;
+ FT_GlyphSlot slot = face->glyph;
+ FT_Fixed x_scale = face->size->metrics.x_scale;
+ FT_Fixed y_scale = face->size->metrics.y_scale;
+ FT_Glyph_Metrics metrics; /* temporary metrics */
+ FT_Error error;
+ AH_Outline* outline = hinter->glyph;
+ AH_Loader* gloader = hinter->loader;
+ FT_Bool no_horz_hints = (load_flags & AH_HINT_NO_HORZ_EDGES) != 0;
+ FT_Bool no_vert_hints = (load_flags & AH_HINT_NO_VERT_EDGES) != 0;
+
+ /* load the glyph */
+ error = FT_Load_Glyph( face, glyph_index, load_flags );
+ if (error) goto Exit;
+
+ /* save current glyph metrics */
+ metrics = slot->metrics;
+
+ switch (slot->format)
+ {
+ case ft_glyph_format_outline:
+ {
+ /* first of all, copy the outline points in the loader's current */
+ /* extra points, which is used to keep original glyph coordinates */
+ error = ah_loader_check_points( gloader, slot->outline.n_points+2,
+ slot->outline.n_contours );
+ if (error) goto Exit;
+
+ MEM_Copy( gloader->current.extra_points, slot->outline.points,
+ slot->outline.n_points * sizeof(FT_Vector) );
+
+ MEM_Copy( gloader->current.outline.contours, slot->outline.contours,
+ slot->outline.n_contours*sizeof(short) );
+
+ MEM_Copy( gloader->current.outline.tags, slot->outline.tags,
+ slot->outline.n_points * sizeof(char) );
+
+ gloader->current.outline.n_points = slot->outline.n_points;
+ gloader->current.outline.n_contours = slot->outline.n_contours;
+
+ /* compute original phantom points */
+ hinter->pp1.x = 0;
+ hinter->pp1.y = 0;
+ hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
+ hinter->pp2.y = 0;
+
+ /* be sure to check for spacing glyphs */
+ if (slot->outline.n_points == 0)
+ goto Hint_Metrics;
+
+ /* now, load the slot image into the auto-outline, and run the */
+ /* automatic hinting process.. */
+ error = ah_outline_load( outline, face ); /* XXXX: change to slot */
+ if (error) goto Exit;
+
+ /* perform feature detection */
+ ah_outline_detect_features( outline );
+
+ if ( !no_horz_hints )
+ {
+ ah_outline_compute_blue_edges( outline, hinter->globals );
+ ah_outline_scale_blue_edges( outline, hinter->globals );
+ }
+
+ /* perform alignment control */
+ ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints );
+ ah_hinter_align( hinter );
+
+ /* now save the current outline into the loader's current table */
+ ah_outline_save( outline, gloader );
+
+ /* we now need to hint the metrics according to the change in */
+ /* width/positioning that occured during the hinting process */
+ {
+ FT_Pos old_width, new_width;
+ FT_Pos old_advance, new_advance;
+ FT_Pos old_lsb, new_lsb;
+ AH_Edge* edge1 = hinter->glyph->horz_edges; /* left-most edge */
+ AH_Edge* edge2 = edge1 + hinter->glyph->num_hedges-1; /* right-mode edge */
+
+ old_width = edge2->opos - edge1->opos;
+ new_width = edge2->pos - edge1->pos;
+
+ old_advance = hinter->pp2.x;
+ old_lsb = edge1->opos;
+ new_lsb = edge1->pos;
+
+ new_advance = old_advance + (new_width+new_lsb-old_width-old_lsb);
+
+ hinter->pp1.x = ((new_lsb - old_lsb)+32) & -64;
+ hinter->pp2.x = ((edge2->pos + (old_advance - edge2->opos))+32) & -64;
+ }
+
+ /* good, we simply add the glyph to our loader's base */
+ ah_loader_add( gloader );
+ }
+ break;
+
+ case ft_glyph_format_composite:
+ {
+ FT_UInt nn, num_subglyphs = slot->num_subglyphs;
+ FT_UInt num_base_subgs, start_point, start_contour;
+ FT_SubGlyph* subglyph;
+
+ start_point = gloader->base.outline.n_points;
+ start_contour = gloader->base.outline.n_contours;
+
+ /* first of all, copy the subglyph descriptors in the glyph loader */
+ error = ah_loader_check_subglyphs( gloader, num_subglyphs );
+ if (error) goto Exit;
+
+ MEM_Copy( gloader->current.subglyphs, slot->subglyphs,
+ num_subglyphs*sizeof(FT_SubGlyph) );
+
+ gloader->current.num_subglyphs = num_subglyphs;
+ num_base_subgs = gloader->base.num_subglyphs;
+
+ /* now, read each subglyph independently */
+ for ( nn = 0; nn < num_subglyphs; nn++ )
+ {
+ FT_Vector pp1, pp2;
+ FT_Pos x, y;
+ FT_UInt num_points, num_new_points, num_base_points;
+
+ /* gloader.current.subglyphs can change during glyph loading due */
+ /* to re-allocation. We must recompute the current subglyph on */
+ /* each iteration.. */
+ subglyph = gloader->base.subglyphs + num_base_subgs + nn;
+
+ pp1 = hinter->pp1;
+ pp2 = hinter->pp2;
+
+ num_base_points = gloader->base.outline.n_points;
+
+ error = ah_hinter_load( hinter, subglyph->index, load_flags, depth+1 );
+ if ( error ) goto Exit;
+
+ /* recompute subglyph pointer */
+ subglyph = gloader->base.subglyphs + num_base_subgs + nn;
+
+ if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
+ {
+ pp1 = hinter->pp1;
+ pp2 = hinter->pp2;
+ }
+ else
+ {
+ hinter->pp1 = pp1;
+ hinter->pp2 = pp2;
+ }
+
+ num_points = gloader->base.outline.n_points;
+ num_new_points = num_points - num_base_points;
+
+ /* now perform the transform required for this subglyph */
+
+ if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
+ FT_SUBGLYPH_FLAG_XY_SCALE |
+ FT_SUBGLYPH_FLAG_2X2 ) )
+ {
+ FT_Vector* cur = gloader->base.outline.points + num_base_points;
+ FT_Vector* org = gloader->base.extra_points + num_base_points;
+ FT_Vector* limit = cur + num_new_points;
+
+ for ( ; cur < limit; cur++, org++ )
+ {
+ FT_Vector_Transform( cur, &subglyph->transform );
+ FT_Vector_Transform( org, &subglyph->transform );
+ }
+ }
+
+ /* apply offset */
+
+ if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
+ {
+ FT_Int k = subglyph->arg1;
+ FT_UInt l = subglyph->arg2;
+ FT_Vector* p1;
+ FT_Vector* p2;
+
+ if ( start_point + k >= num_base_points ||
+ l >= (FT_UInt)num_new_points )
+ {
+ error = FT_Err_Invalid_Composite;
+ goto Exit;
+ }
+
+ l += num_base_points;
+
+ /* for now, only use the current point coordinates */
+ /* we may consider another approach in the near future */
+ p1 = gloader->base.outline.points + start_point + k;
+ p2 = gloader->base.outline.points + start_point + l;
+
+ x = p1->x - p2->x;
+ y = p1->y - p2->y;
+ }
+ else
+ {
+ x = FT_MulFix( subglyph->arg1, x_scale );
+ y = FT_MulFix( subglyph->arg2, y_scale );
+
+ x = ( x + 32 ) & -64;
+ y = ( y + 32 ) & -64;
+ }
+
+ {
+ FT_Outline dummy = gloader->base.outline;
+ dummy.points += num_base_points;
+ dummy.n_points = num_new_points;
+
+ FT_Outline_Translate( &dummy, x, y );
+ }
+ }
+ }
+ break;
+
+ default:
+ /* we don't support other formats (yet ?) */
+ error = FT_Err_Unimplemented_Feature;
+ }
+
+ Hint_Metrics:
+ if (depth == 0)
+ {
+ FT_BBox bbox;
+
+ /* we must translate our final outline by -pp1.x, and compute */
+ /* the new metrics.. */
+ if (hinter->pp1.x)
+ FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
+
+ FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
+ bbox.xMin &= -64;
+ bbox.yMin &= -64;
+ bbox.xMax = (bbox.xMax+63) & -64;
+ bbox.yMax = (bbox.yMax+63) & -64;
+
+ slot->metrics.width = (bbox.xMax - bbox.xMin);
+ slot->metrics.height = (bbox.yMax - bbox.yMin);
+ slot->metrics.horiBearingX = bbox.xMin;
+ slot->metrics.horiBearingY = bbox.yMax;
+ slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
+ /* XXXX: TO DO - slot->linearHoriAdvance */
+
+ /* now copy outline into glyph slot */
+ ah_loader_rewind( slot->loader );
+ error = ah_loader_copy_points( slot->loader, gloader );
+ if (error) goto Exit;
+
+ slot->outline = slot->loader->base.outline;
+ slot->format = ft_glyph_format_outline;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* load and hint a given glyph */
+ FT_Error ah_hinter_load_glyph( AH_Hinter* hinter,
+ FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int load_flags )
+ {
+ FT_Face face = slot->face;
+ FT_Error error;
+ FT_Fixed x_scale = size->metrics.x_scale;
+ FT_Fixed y_scale = size->metrics.y_scale;
+ AH_Face_Globals* face_globals = FACE_GLOBALS(face);
+
+ /* first of all, we need to check that we're using the correct face and */
+ /* global hints to load the glyph */
+ if ( hinter->face != face || hinter->globals != face_globals )
+ {
+ hinter->face = face;
+ if (!face_globals)
+ {
+ error = ah_hinter_new_face_globals( hinter, face, 0 );
+ if (error) goto Exit;
+ }
+ hinter->globals = FACE_GLOBALS(face);
+ face_globals = FACE_GLOBALS(face);
+ }
+
+ /* now, we must check the current character pixel size to see if we need */
+ /* to rescale the global metrics.. */
+ if ( face_globals->x_scale != x_scale ||
+ face_globals->y_scale != y_scale )
+ {
+ ah_hinter_scale_globals( hinter, x_scale, y_scale );
+ }
+
+ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
+
+ ah_loader_rewind( hinter->loader );
+
+ error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
+
+ Exit:
+ return error;
+ }
+
+
+ /* retrieve a face's autohint globals for client applications */
+ void ah_hinter_get_global_hints( AH_Hinter* hinter,
+ FT_Face face,
+ void* *global_hints,
+ long *global_len )
+ {
+ AH_Globals* globals = 0;
+ FT_Memory memory = hinter->memory;
+ FT_Error error;
+
+ /* allocate new master globals */
+ if (ALLOC( globals, sizeof(*globals)))
+ goto Fail;
+
+ /* compute face globals if needed */
+ if (!FACE_GLOBALS(face))
+ {
+ error = ah_hinter_new_face_globals( hinter, face, 0 );
+ if (error) goto Fail;
+ }
+
+ *globals = FACE_GLOBALS(face)->design;
+ *global_hints = globals;
+ *global_len = sizeof(*globals);
+ return;
+
+ Fail:
+ FREE( globals );
+ *global_hints = 0;
+ *global_len = 0;
+ }
+
+
+ void ah_hinter_done_global_hints( AH_Hinter* hinter,
+ void* global_hints )
+ {
+ FT_Memory memory = hinter->memory;
+ FREE( global_hints );
+ }
+
+
--- /dev/null
+++ b/src/autohint/ahhint.h
@@ -1,0 +1,65 @@
+/***************************************************************************/
+/* */
+/* ahhint.h */
+/* */
+/* Glyph hinter declarations */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifndef AGHINT_H
+#define AGHINT_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahglobal.h"
+#else
+#include <autohint/ahglobal.h>
+#endif
+
+#define AH_HINT_DEFAULT 0
+#define AH_HINT_NO_ALIGNMENT 1
+#define AH_HINT_NO_HORZ_EDGES 0x20000
+#define AH_HINT_NO_VERT_EDGES 0x40000
+
+
+
+ /* create a new empty hinter object */
+ extern
+ FT_Error ah_hinter_new( FT_Library library, AH_Hinter* *ahinter );
+
+ /* Load a hinted glyph in the hinter */
+ extern
+ FT_Error ah_hinter_load_glyph( AH_Hinter* hinter,
+ FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int load_flags );
+
+ /* finalise a hinter object */
+ extern
+ void ah_hinter_done( AH_Hinter* hinter );
+
+ LOCAL_DEF
+ void ah_hinter_done_face_globals( AH_Face_Globals* globals );
+
+ extern
+ void ah_hinter_get_global_hints( AH_Hinter* hinter,
+ FT_Face face,
+ void* *global_hints,
+ long *global_len );
+
+ extern
+ void ah_hinter_done_global_hints( AH_Hinter* hinter,
+ void* global_hints );
+
+#endif /* AGHINT_H */
--- /dev/null
+++ b/src/autohint/ahloader.h
@@ -1,0 +1,103 @@
+/***************************************************************************/
+/* */
+/* ahloader.h */
+/* */
+/* Glyph loader implementation for the auto-hinting module */
+/* This defines the AG_GlyphLoader type in two different ways: */
+/* */
+/* - when the module is compiled within FreeType 2, the type */
+/* is simply a typedef to FT_GlyphLoader */
+/* */
+/* - when the module is compiled as a standalone object, */
+/* AG_GlyphLoader has its own implementation.. */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifndef AGLOADER_H
+#define AGLOADER_H
+
+#ifdef _STANDALONE_
+
+ typedef struct AH_GlyphLoad_
+ {
+ FT_Outline outline; /* outline */
+ FT_UInt num_subglyphs; /* number of subglyphs */
+ FT_SubGlyph* subglyphs; /* subglyphs */
+ FT_Vector* extra_points; /* extra points table.. */
+
+ } AH_GlyphLoad;
+
+
+ struct AH_GlyphLoader_
+ {
+ FT_Memory memory;
+ FT_UInt max_points;
+ FT_UInt max_contours;
+ FT_UInt max_subglyphs;
+ FT_Bool use_extra;
+
+ AH_GlyphLoad base;
+ AH_GlyphLoad current;
+
+ void* other; /* for possible future extension ? */
+
+ };
+
+
+ LOCAL_DEF FT_Error AH_GlyphLoader_New( FT_Memory memory,
+ AH_GlyphLoader* *aloader );
+
+ LOCAL_DEF FT_Error AH_GlyphLoader_Create_Extra( AH_GlyphLoader* loader );
+
+ LOCAL_DEF void AH_GlyphLoader_Done( AH_GlyphLoader* loader );
+
+ LOCAL_DEF void AH_GlyphLoader_Reset( AH_GlyphLoader* loader );
+
+ LOCAL_DEF void AH_GlyphLoader_Rewind( AH_GlyphLoader* loader );
+
+ LOCAL_DEF FT_Error AH_GlyphLoader_Check_Points( AH_GlyphLoader* loader,
+ FT_UInt n_points,
+ FT_UInt n_contours );
+
+ LOCAL_DEF FT_Error AH_GlyphLoader_Check_Subglyphs( AH_GlyphLoader* loader,
+ FT_UInt n_subs );
+
+ LOCAL_DEF void AH_GlyphLoader_Prepare( AH_GlyphLoader* loader );
+
+
+ LOCAL_DEF void AH_GlyphLoader_Add( AH_GlyphLoader* loader );
+
+ LOCAL_DEF FT_Error AH_GlyphLoader_Copy_Points( AH_GlyphLoader* target,
+ FT_GlyphLoader* source );
+
+#else
+#include <freetype/internal/ftobjs.h>
+
+ #define AH_Load FT_GlyphLoad
+ #define AH_Loader FT_GlyphLoader
+
+ #define ah_loader_new FT_GlyphLoader_New
+ #define ah_loader_done FT_GlyphLoader_Done
+ #define ah_loader_reset FT_GlyphLoader_Reset
+ #define ah_loader_rewind FT_GlyphLoader_Rewind
+ #define ah_loader_create_extra FT_GlyphLoader_Create_Extra
+ #define ah_loader_check_points FT_GlyphLoader_Check_Points
+ #define ah_loader_check_subglyphs FT_GlyphLoader_Check_Subglyphs
+ #define ah_loader_prepare FT_GlyphLoader_Prepare
+ #define ah_loader_add FT_GlyphLoader_Add
+ #define ah_loader_copy_points FT_GlyphLoader_Copy_Points
+
+#endif
+
+#endif /* AGLOADER_H */
--- /dev/null
+++ b/src/autohint/ahmodule.c
@@ -1,0 +1,111 @@
+/***************************************************************************/
+/* */
+/* ahmodule.c */
+/* */
+/* Auto-hinting module implementation */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#include <freetype/ftmodule.h>
+
+#ifdef FT_FLAT_COMPILE
+#include "ahhint.h"
+#else
+#include <autohint/ahhint.h>
+#endif
+
+
+ typedef struct FT_AutoHinterRec_
+ {
+ FT_ModuleRec root;
+ AH_Hinter* hinter;
+
+ } FT_AutoHinterRec;
+
+
+ static
+ FT_Error ft_autohinter_init( FT_AutoHinter module )
+ {
+ return ah_hinter_new( module->root.library, &module->hinter );
+ }
+
+ static
+ void ft_autohinter_done( FT_AutoHinter module )
+ {
+ ah_hinter_done( module->hinter );
+ }
+
+ static
+ FT_Error ft_autohinter_load( FT_AutoHinter module,
+ FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_ULong load_flags )
+ {
+ return ah_hinter_load_glyph( module->hinter,
+ slot, size, glyph_index, load_flags );
+ }
+
+ static
+ void ft_autohinter_reset( FT_AutoHinter module,
+ FT_Face face )
+ {
+ UNUSED(module);
+ if (face->autohint.data)
+ ah_hinter_done_face_globals( face->autohint.data );
+ }
+
+ static
+ void ft_autohinter_get_globals( FT_AutoHinter module,
+ FT_Face face,
+ void* *global_hints,
+ long *global_len )
+ {
+ ah_hinter_get_global_hints( module->hinter, face,
+ global_hints, global_len );
+ }
+
+
+ static
+ void ft_autohinter_done_globals( FT_AutoHinter module,
+ void* global_hints )
+ {
+ ah_hinter_done_global_hints( module->hinter, global_hints );
+ }
+
+
+ static
+ const FT_AutoHinter_Interface autohinter_interface =
+ {
+ ft_autohinter_reset,
+ ft_autohinter_load,
+ ft_autohinter_get_globals,
+ ft_autohinter_done_globals
+ };
+
+ const FT_Module_Class autohint_module_class =
+ {
+ ft_module_hinter,
+ sizeof( FT_AutoHinterRec ),
+
+ "autohinter",
+ 0x10000, /* version 1.0 of the autohinter */
+ 0x20000, /* requires FreeType 2.0 or above */
+
+ (const void*)&autohinter_interface,
+
+ (FT_Module_Constructor) ft_autohinter_init,
+ (FT_Module_Destructor) ft_autohinter_done,
+ (FT_Module_Requester) 0
+ };
--- /dev/null
+++ b/src/autohint/ahmodule.h
@@ -1,0 +1,27 @@
+/***************************************************************************/
+/* */
+/* ahmodule.h */
+/* */
+/* Auto-hinting module declaration */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+#ifndef AHMODULE_H
+#define AHMODULE_H
+
+#include <freetype/ftmodule.h>
+
+ FT_EXPORT_VAR(const FT_Module_Class) autohint_module_class;
+
+#endif /* AHMODULE_H */
--- /dev/null
+++ b/src/autohint/ahoptim.c
@@ -1,0 +1,808 @@
+/***************************************************************************/
+/* */
+/* FreeType Auto-Gridder Outline Optimisation */
+/* */
+/* This module is in charge of optimising the outlines produced by the */
+/* auto-hinter in direct mode. This is required at small pixel sizes in */
+/* order to ensure coherent spacing, among other things.. */
+/* */
+/* The technique used in this module is a simplified simulated annealing. */
+/* */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+
+#include <freetype/internal/ftobjs.h> /* for ALLOC_ARRAY and FREE */
+
+#ifdef FT_FLAT_COMPILE
+#include "ahoptim.h"
+#else
+#include <autohint/ahoptim.h>
+#endif
+
+/* define this macro to use brute force optimisation, this is slow, but */
+/* a good way to perfect the distortion function "by hand" through */
+/* tweaking.. */
+#define BRUTE_FORCE
+
+#define xxxDEBUG_OPTIM
+
+#undef LOG
+#ifdef DEBUG_OPTIM
+#define LOG(x) optim_log##x
+#else
+#define LOG(x)
+#endif
+
+#ifdef DEBUG_OPTIM
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define FLOAT(x) ((float)((x)/64.0))
+
+static
+void optim_log( const char* fmt, ... )
+{
+ va_list ap;
+
+
+ va_start( ap, fmt );
+ vprintf( fmt, ap );
+ va_end( ap );
+}
+#endif
+
+
+#ifdef DEBUG_OPTIM
+ static
+ void AH_Dump_Stems( AH_Optimizer* optimizer )
+ {
+ int n;
+ AH_Stem* stem;
+
+ stem = optimizer->stems;
+ for ( n = 0; n < optimizer->num_stems; n++, stem++ )
+ {
+ LOG(( " %c%2d [%.1f:%.1f]={%.1f:%.1f}=<%1.f..%1.f> force=%.1f speed=%.1f\n",
+ optimizer->vertical ? 'V' : 'H', n,
+ FLOAT(stem->edge1->opos), FLOAT(stem->edge2->opos),
+ FLOAT(stem->edge1->pos), FLOAT(stem->edge2->pos),
+ FLOAT(stem->min_pos), FLOAT(stem->max_pos),
+ FLOAT(stem->force), FLOAT(stem->velocity) ));
+ }
+ }
+
+ static
+ void AH_Dump_Stems2( AH_Optimizer* optimizer )
+ {
+ int n;
+ AH_Stem* stem;
+
+ stem = optimizer->stems;
+ for ( n = 0; n < optimizer->num_stems; n++, stem++ )
+ {
+ LOG(( " %c%2d [%.1f]=<%1.f..%1.f> force=%.1f speed=%.1f\n",
+ optimizer->vertical ? 'V' : 'H', n,
+ FLOAT(stem->pos),
+ FLOAT(stem->min_pos), FLOAT(stem->max_pos),
+ FLOAT(stem->force), FLOAT(stem->velocity) ));
+ }
+ }
+
+ static
+ void AH_Dump_Springs( AH_Optimizer* optimizer )
+ {
+ int n;
+ AH_Spring* spring;
+ AH_Stem* stems;
+
+ spring = optimizer->springs;
+ stems = optimizer->stems;
+ LOG(( "%cSprings ", optimizer->vertical ? 'V' : 'H' ));
+ for ( n = 0; n < optimizer->num_springs; n++, spring++ )
+ {
+ LOG(( " [%d-%d:%.1f:%1.f:%.1f]", spring->stem1 - stems, spring->stem2 - stems,
+ FLOAT(spring->owidth),
+ FLOAT(spring->stem2->pos-(spring->stem1->pos+spring->stem1->width)),
+ FLOAT(spring->tension) ));
+ }
+
+ LOG(( "\n" ));
+ }
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** COMPUTE STEMS AND SPRINGS IN AN OUTLINE ****/
+ /**** ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static
+ int valid_stem_segments( AH_Segment* seg1, AH_Segment* seg2 )
+ {
+ return seg1->serif == 0 && seg2 && seg2->link == seg1 && seg1->pos < seg2->pos &&
+ seg1->min_coord <= seg2->max_coord &&
+ seg2->min_coord <= seg1->max_coord;
+ }
+
+ /* compute all stems in an outline */
+ static
+ int optim_compute_stems( AH_Optimizer* optimizer )
+ {
+ AH_Outline* outline = optimizer->outline;
+ FT_Fixed scale;
+ FT_Memory memory = optimizer->memory;
+ FT_Error error = 0;
+ FT_Int dimension;
+ AH_Edge* edges;
+ AH_Edge* edge_limit;
+ AH_Stem** p_stems;
+ FT_Int* p_num_stems;
+
+ edges = outline->horz_edges;
+ edge_limit = edges + outline->num_hedges;
+ scale = outline->y_scale;
+
+ p_stems = &optimizer->horz_stems;
+ p_num_stems = &optimizer->num_hstems;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AH_Stem* stems = 0;
+ FT_Int num_stems = 0;
+ AH_Edge* edge;
+
+ /* first of all, count the number of stems in this direction */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AH_Segment* seg = edge->first;
+ do
+ {
+ if (valid_stem_segments( seg, seg->link ))
+ num_stems++;
+
+ seg = seg->edge_next;
+
+ } while (seg != edge->first);
+ }
+
+ /* now allocate the stems and build their table */
+ if (num_stems > 0)
+ {
+ AH_Stem* stem;
+
+ if ( ALLOC_ARRAY( stems, num_stems, AH_Stem ) )
+ goto Exit;
+
+ stem = stems;
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AH_Segment* seg = edge->first;
+ AH_Segment* seg2;
+ do
+ {
+ seg2 = seg->link;
+ if (valid_stem_segments(seg,seg2))
+ {
+ AH_Edge* edge1 = seg->edge;
+ AH_Edge* edge2 = seg2->edge;
+
+ stem->edge1 = edge1;
+ stem->edge2 = edge2;
+ stem->opos = edge1->opos;
+ stem->pos = edge1->pos;
+ stem->owidth = edge2->opos - edge1->opos;
+ stem->width = edge2->pos - edge1->pos;
+
+ /* compute min_coord and max_coord */
+ {
+ FT_Pos min_coord = seg->min_coord;
+ FT_Pos max_coord = seg->max_coord;
+
+ if (seg2->min_coord > min_coord)
+ min_coord = seg2->min_coord;
+
+ if (seg2->max_coord < max_coord)
+ max_coord = seg2->max_coord;
+
+ stem->min_coord = min_coord;
+ stem->max_coord = max_coord;
+ }
+
+ /* compute minimum and maximum positions for stem */
+ /* note that the left-most/bottom-most stem has always */
+ /* a fixed position.. */
+ if (stem == stems || edge1->blue_edge || edge2->blue_edge)
+ {
+ /* this stem cannot move, it is snapped to a blue edge */
+ stem->min_pos = stem->pos;
+ stem->max_pos = stem->pos;
+ }
+ else
+ {
+ /* this edge can move, compute its min and max positions */
+ FT_Pos pos1 = stem->opos;
+ FT_Pos pos2 = pos1 + stem->owidth - stem->width;
+ FT_Pos min1 = (pos1 & -64);
+ FT_Pos min2 = (pos2 & -64);
+
+ stem->min_pos = min1;
+ stem->max_pos = min1+64;
+ if (min2 < min1)
+ stem->min_pos = min2;
+ else
+ stem->max_pos = min2+64;
+
+ /* XXX : just to see what it does */
+ stem->max_pos += 64;
+
+ /* just for the case where direct hinting did some incredible */
+ /* things (e.g. blue edge shifts..) */
+ if (stem->min_pos > stem->pos)
+ stem->min_pos = stem->pos;
+
+ if (stem->max_pos < stem->pos)
+ stem->max_pos = stem->pos;
+ }
+
+ stem->velocity = 0;
+ stem->force = 0;
+
+ stem++;
+ }
+ seg = seg->edge_next;
+ }
+ while (seg != edge->first);
+ }
+ }
+
+ *p_stems = stems;
+ *p_num_stems = num_stems;
+
+ edges = outline->vert_edges;
+ edge_limit = edges + outline->num_vedges;
+ scale = outline->x_scale;
+
+ p_stems = &optimizer->vert_stems;
+ p_num_stems = &optimizer->num_vstems;
+ }
+ Exit:
+ #ifdef DEBUG_OPTIM
+ AH_Dump_Stems(optimizer);
+ #endif
+ return error;
+ }
+
+
+ /* returns the spring area between two stems, 0 if none */
+ static
+ FT_Pos stem_spring_area( AH_Stem* stem1, AH_Stem* stem2 )
+ {
+ FT_Pos area1 = stem1->max_coord - stem1->min_coord;
+ FT_Pos area2 = stem2->max_coord - stem2->min_coord;
+ FT_Pos min = stem1->min_coord;
+ FT_Pos max = stem1->max_coord;
+ FT_Pos area;
+
+ /* order stems */
+ if (stem2->opos <= stem1->opos + stem1->owidth)
+ return 0;
+
+ if (min < stem2->min_coord)
+ min = stem2->min_coord;
+
+ if (max < stem2->max_coord)
+ max = stem2->max_coord;
+
+ area = (max-min);
+ if ( 2*area < area1 && 2*area < area2 )
+ area = 0;
+
+ return area;
+ }
+
+
+ /* compute all springs in an outline */
+ static
+ int optim_compute_springs( AH_Optimizer* optimizer )
+ {
+ /* basically, a spring exists between two stems if most of their */
+ /* surface is aligned.. */
+ FT_Memory memory = optimizer->memory;
+
+ AH_Stem* stems;
+ AH_Stem* stem_limit;
+ AH_Stem* stem;
+ int dimension;
+ int error = 0;
+
+ FT_Int* p_num_springs;
+ AH_Spring** p_springs;
+
+ stems = optimizer->horz_stems;
+ stem_limit = stems + optimizer->num_hstems;
+
+ p_springs = &optimizer->horz_springs;
+ p_num_springs = &optimizer->num_hsprings;
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ FT_Int num_springs = 0;
+ AH_Spring* springs = 0;
+
+ /* first of all, count stem springs */
+ for ( stem = stems; stem+1 < stem_limit; stem++ )
+ {
+ AH_Stem* stem2;
+ for ( stem2 = stem+1; stem2 < stem_limit; stem2++ )
+ if (stem_spring_area(stem,stem2))
+ num_springs++;
+ }
+
+ /* then allocate and build the springs table */
+ if (num_springs > 0)
+ {
+ AH_Spring* spring;
+
+ /* allocate table of springs */
+ if ( ALLOC_ARRAY( springs, num_springs, AH_Spring ) )
+ goto Exit;
+
+ /* fill the springs table */
+ spring = springs;
+ for ( stem = stems; stem+1 < stem_limit; stem++ )
+ {
+ AH_Stem* stem2;
+ FT_Pos area;
+
+ for ( stem2 = stem+1; stem2 < stem_limit; stem2++ )
+ {
+ area = stem_spring_area(stem,stem2);
+ if (area)
+ {
+ /* add a new spring here */
+ spring->stem1 = stem;
+ spring->stem2 = stem2;
+ spring->owidth = stem2->opos - (stem->opos + stem->owidth);
+ spring->tension = 0;
+
+ spring++;
+ }
+ }
+ }
+ }
+ *p_num_springs = num_springs;
+ *p_springs = springs;
+
+ stems = optimizer->vert_stems;
+ stem_limit = stems + optimizer->num_vstems;
+
+ p_springs = &optimizer->vert_springs;
+ p_num_springs = &optimizer->num_vsprings;
+ }
+
+ Exit:
+ #ifdef DEBUG_OPTIM
+ AH_Dump_Springs(optimizer);
+ #endif
+ return error;
+ }
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** OPTIMISE THROUGH MY STRANGE SIMULATED ANNEALING ALGO ;-) ****/
+ /**** ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#ifndef BRUTE_FORCE
+ /* compute all spring tensions */
+ static
+ void optim_compute_tensions( AH_Optimizer* optimizer )
+ {
+ AH_Spring* spring = optimizer->springs;
+ AH_Spring* limit = spring + optimizer->num_springs;
+ for ( ; spring < limit; spring++ )
+ {
+ AH_Stem* stem1 = spring->stem1;
+ AH_Stem* stem2 = spring->stem2;
+ FT_Int status;
+
+ FT_Pos width;
+ FT_Pos tension;
+ FT_Pos sign;
+
+ /* compute the tension, it simply is -K*(new_width-old_width) */
+ width = stem2->pos - (stem1->pos + stem1->width);
+ tension = width - spring->owidth;
+
+ sign = 1;
+ if (tension < 0)
+ {
+ sign = -1;
+ tension = -tension;
+ }
+
+ if (width <= 0)
+ tension = 32000;
+ else
+ tension = (tension << 10)/width;
+
+ tension = -sign*FT_MulFix( tension, optimizer->tension_scale );
+ spring->tension = tension;
+
+ /* now, distribute tension among the englobing stems, if they */
+ /* are able to move.. */
+ status = 0;
+ if (stem1->pos <= stem1->min_pos)
+ status |= 1;
+ if (stem2->pos >= stem2->max_pos)
+ status |= 2;
+
+ if (!status)
+ tension /= 2;
+
+ if ((status & 1)== 0)
+ stem1->force -= tension;
+
+ if ((status & 2)== 0)
+ stem2->force += tension;
+ }
+ }
+
+
+
+ /* compute all stem movements - returns 0 if nothing moved */
+ static
+ int optim_compute_stem_movements( AH_Optimizer* optimizer )
+ {
+ AH_Stem* stems = optimizer->stems;
+ AH_Stem* limit = stems + optimizer->num_stems;
+ AH_Stem* stem = stems;
+ int moved = 0;
+
+ /* set initial forces to velocity */
+ for ( stem = stems; stem < limit; stem++ )
+ {
+ stem->force = stem->velocity;
+ stem->velocity /= 2; /* XXXX: Heuristics */
+ }
+
+ /* compute the sum of forces applied on each stem */
+ optim_compute_tensions( optimizer );
+ #ifdef DEBUG_OPTIM
+ AH_Dump_Springs( optimizer );
+ AH_Dump_Stems2( optimizer );
+ #endif
+
+ /* now, see if something can move ? */
+ for ( stem = stems; stem < limit; stem++ )
+ {
+ if (stem->force > optimizer->tension_threshold)
+ {
+ /* there is enough tension to move the stem to the right */
+ if (stem->pos < stem->max_pos)
+ {
+ stem->pos += 64;
+ stem->velocity = stem->force/2;
+ moved = 1;
+ }
+ else
+ stem->velocity = 0;
+ }
+ else if (stem->force < optimizer->tension_threshold)
+ {
+ /* there is enough tension to move the stem to the left */
+ if (stem->pos > stem->min_pos)
+ {
+ stem->pos -= 64;
+ stem->velocity = stem->force/2;
+ moved = 1;
+ }
+ else
+ stem->velocity = 0;
+ }
+ }
+ /* return 0 if nothing moved */
+ return moved;
+ }
+
+#endif /* BRUTE_FORCE */
+
+
+ /* compute current global distortion from springs */
+ static
+ FT_Pos optim_compute_distorsion( AH_Optimizer* optimizer )
+ {
+ AH_Spring* spring = optimizer->springs;
+ AH_Spring* limit = spring + optimizer->num_springs;
+ FT_Pos distorsion = 0;
+
+ for ( ; spring < limit; spring++ )
+ {
+ AH_Stem* stem1 = spring->stem1;
+ AH_Stem* stem2 = spring->stem2;
+ FT_Pos width;
+
+ width = stem2->pos - (stem1->pos + stem1->width);
+ width -= spring->owidth;
+ if (width < 0)
+ width = -width;
+
+ distorsion += width;
+ }
+ return distorsion;
+ }
+
+
+ /* record stems configuration in "best of" history */
+ static
+ void optim_record_configuration( AH_Optimizer* optimizer )
+ {
+ FT_Pos distorsion;
+ AH_Configuration* configs = optimizer->configs;
+ AH_Configuration* limit = configs + optimizer->num_configs;
+ AH_Configuration* config;
+
+ distorsion = optim_compute_distorsion( optimizer );
+ LOG(( "config distorsion = %.1f ", FLOAT(distorsion*64) ));
+
+ /* check that we really need to add this configuration to our */
+ /* sorted history.. */
+ if ( limit > configs && limit[-1].distorsion < distorsion )
+ {
+ LOG(( "ejected\n" ));
+ return;
+ }
+
+ /* add new configuration at the end of the table */
+ {
+ int n;
+
+ config = limit;
+ if (optimizer->num_configs < AH_MAX_CONFIGS)
+ optimizer->num_configs++;
+ else
+ config--;
+
+ config->distorsion = distorsion;
+
+ for ( n = 0; n < optimizer->num_stems; n++ )
+ config->positions[n] = optimizer->stems[n].pos;
+ }
+
+ /* move the current configuration towards the front of the list */
+ /* when necessary, yes this is slow bubble sort ;-) */
+ while ( config > configs && config[0].distorsion < config[-1].distorsion )
+ {
+ AH_Configuration temp;
+ config--;
+ temp = config[0];
+ config[0] = config[1];
+ config[1] = temp;
+ }
+ LOG(( "recorded !!\n" ));
+ }
+
+
+#ifdef BRUTE_FORCE
+ /* optimize outline in a single direction */
+ static
+ void optim_compute( AH_Optimizer* optimizer )
+ {
+ int n;
+ FT_Bool moved;
+
+
+ AH_Stem* stem = optimizer->stems;
+ AH_Stem* limit = stem + optimizer->num_stems;
+
+ /* empty, exit */
+ if (stem >= limit)
+ return;
+
+ optimizer->num_configs = 0;
+
+ stem = optimizer->stems;
+ for ( ; stem < limit; stem++ )
+ stem->pos = stem->min_pos;
+
+ do
+ {
+ /* record current configuration */
+ optim_record_configuration(optimizer);
+
+ /* now change configuration */
+ moved = 0;
+ for ( stem = optimizer->stems; stem < limit; stem++ )
+ {
+ if (stem->pos < stem->max_pos)
+ {
+ stem->pos += 64;
+ moved = 1;
+ break;
+ }
+
+ stem->pos = stem->min_pos;
+ }
+ }
+ while (moved);
+
+ /* now, set the best stem positions */
+ for ( n = 0; n < optimizer->num_stems; n++ )
+ {
+ AH_Stem* stem = optimizer->stems + n;
+ FT_Pos pos = optimizer->configs[0].positions[n];
+
+ stem->edge1->pos = pos;
+ stem->edge2->pos = pos + stem->width;
+
+ stem->edge1->flags |= ah_edge_done;
+ stem->edge2->flags |= ah_edge_done;
+ }
+ }
+#else
+ /* optimize outline in a single direction */
+ static
+ void optim_compute( AH_Optimizer* optimizer )
+ {
+ int n, counter, counter2;
+
+ optimizer->num_configs = 0;
+ optimizer->tension_scale = 0x80000L;
+ optimizer->tension_threshold = 64;
+
+ /* record initial configuration threshold */
+ optim_record_configuration(optimizer);
+ counter = 0;
+ for ( counter2 = optimizer->num_stems*8; counter2 >= 0; counter2-- )
+ {
+ if (counter == 0)
+ counter = 2*optimizer->num_stems;
+
+ if (!optim_compute_stem_movements( optimizer ))
+ break;
+
+ optim_record_configuration(optimizer);
+ counter--;
+ if (counter == 0)
+ optimizer->tension_scale /= 2;
+ }
+
+ /* now, set the best stem positions */
+ for ( n = 0; n < optimizer->num_stems; n++ )
+ {
+ AH_Stem* stem = optimizer->stems + n;
+ FT_Pos pos = optimizer->configs[0].positions[n];
+
+ stem->edge1->pos = pos;
+ stem->edge2->pos = pos + stem->width;
+
+ stem->edge1->flags |= ah_edge_done;
+ stem->edge2->flags |= ah_edge_done;
+ }
+ }
+#endif
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** HIGH-LEVEL OPTIMIZER API ****/
+ /**** ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* releases the optimisation data */
+ void AH_Optimizer_Done( AH_Optimizer* optimizer )
+ {
+ if (optimizer)
+ {
+ FT_Memory memory = optimizer->memory;
+ FREE( optimizer->horz_stems );
+ FREE( optimizer->vert_stems );
+ FREE( optimizer->horz_springs );
+ FREE( optimizer->vert_springs );
+ FREE( optimizer->positions );
+ }
+ }
+
+ /* loads the outline into the optimizer */
+ int AH_Optimizer_Init( AH_Optimizer* optimizer,
+ AH_Outline* outline,
+ FT_Memory memory )
+ {
+ FT_Error error;
+
+ MEM_Set( optimizer, 0, sizeof(*optimizer));
+ optimizer->outline = outline;
+ optimizer->memory = memory;
+
+ LOG(( "initializing new optimizer\n" ));
+ /* compute stems and springs */
+ error = optim_compute_stems ( optimizer ) ||
+ optim_compute_springs( optimizer );
+ if (error) goto Fail;
+
+ /* allocate stem positions history and configurations */
+ {
+ int n, max_stems;
+
+ max_stems = optimizer->num_hstems;
+ if (max_stems < optimizer->num_vstems)
+ max_stems = optimizer->num_vstems;
+
+ if ( ALLOC_ARRAY( optimizer->positions, max_stems*AH_MAX_CONFIGS, FT_Pos ) )
+ goto Fail;
+
+ optimizer->num_configs = 0;
+ for ( n = 0; n < AH_MAX_CONFIGS; n++ )
+ optimizer->configs[n].positions = optimizer->positions + n*max_stems;
+ }
+
+ return error;
+
+ Fail:
+ AH_Optimizer_Done( optimizer );
+ return error;
+ }
+
+
+ /* compute optimal outline */
+ void AH_Optimizer_Compute( AH_Optimizer* optimizer )
+ {
+ optimizer->num_stems = optimizer->num_hstems;
+ optimizer->stems = optimizer->horz_stems;
+ optimizer->num_springs = optimizer->num_hsprings;
+ optimizer->springs = optimizer->horz_springs;
+
+ if (optimizer->num_springs > 0)
+ {
+ LOG(( "horizontal optimisation ------------------------\n" ));
+ optim_compute( optimizer );
+ }
+
+ optimizer->num_stems = optimizer->num_vstems;
+ optimizer->stems = optimizer->vert_stems;
+ optimizer->num_springs = optimizer->num_vsprings;
+ optimizer->springs = optimizer->vert_springs;
+
+ if (optimizer->num_springs)
+ {
+ LOG(( "vertical optimisation --------------------------\n" ));
+ optim_compute( optimizer );
+ }
+ }
+
+
+
+
+
--- /dev/null
+++ b/src/autohint/ahoptim.h
@@ -1,0 +1,137 @@
+/***************************************************************************/
+/* */
+/* FreeType Auto-Gridder Outline Optimisation */
+/* */
+/* This module is in charge of optimising the outlines produced by the */
+/* auto-hinter in direct mode. This is required at small pixel sizes in */
+/* order to ensure coherent spacing, among other things.. */
+/* */
+/* The technique used in this module is a simplified simulated annealing. */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+
+#ifndef AGOPTIM_H
+#define AGOPTIM_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+/* the maximal number of stem configurations to record during optimisation */
+#define AH_MAX_CONFIGS 8
+
+
+ typedef struct AH_Stem_
+ {
+ FT_Pos pos; /* current position */
+ FT_Pos velocity; /* current velocity */
+ FT_Pos force; /* sum of current forces */
+ FT_Pos width; /* normalized width */
+
+ FT_Pos min_pos; /* minimum grid position */
+ FT_Pos max_pos; /* maximum grid position */
+
+ AH_Edge* edge1; /* left/bottom edge */
+ AH_Edge* edge2; /* right/top edge */
+
+ FT_Pos opos; /* original position */
+ FT_Pos owidth; /* original width */
+
+ FT_Pos min_coord; /* minimum coordinate */
+ FT_Pos max_coord; /* maximum coordinate */
+
+ } AH_Stem;
+
+
+ /* A spring between two stems */
+ typedef struct AH_Spring_
+ {
+ AH_Stem* stem1;
+ AH_Stem* stem2;
+ FT_Pos owidth; /* original width */
+ FT_Pos tension; /* current tension */
+
+ } AH_Spring;
+
+
+ /* A configuration records the position of each stem at a given time */
+ /* as well as the associated distortion.. */
+ typedef struct AH_Configuration_
+ {
+ FT_Pos* positions;
+ FT_Long distorsion;
+
+ } AH_Configuration;
+
+
+
+
+ typedef struct AH_Optimizer_
+ {
+ FT_Memory memory;
+ AH_Outline* outline;
+
+ FT_Int num_hstems;
+ AH_Stem* horz_stems;
+
+ FT_Int num_vstems;
+ AH_Stem* vert_stems;
+
+ FT_Int num_hsprings;
+ FT_Int num_vsprings;
+ AH_Spring* horz_springs;
+ AH_Spring* vert_springs;
+
+ FT_Int num_configs;
+ AH_Configuration configs[ AH_MAX_CONFIGS ];
+ FT_Pos* positions;
+
+ /* during each pass, use these instead */
+ FT_Int num_stems;
+ AH_Stem* stems;
+
+ FT_Int num_springs;
+ AH_Spring* springs;
+ FT_Bool vertical;
+
+ FT_Fixed tension_scale;
+ FT_Pos tension_threshold;
+
+ } AH_Optimizer;
+
+
+ /* loads the outline into the optimizer */
+ extern
+ int AH_Optimizer_Init( AH_Optimizer* optimizer,
+ AH_Outline* outline,
+ FT_Memory memory );
+
+
+
+ /* compute optimal outline */
+ extern
+ void AH_Optimizer_Compute( AH_Optimizer* optimizer );
+
+
+
+
+ /* releases the optimisation data */
+ extern
+ void AH_Optimizer_Done( AH_Optimizer* optimizer );
+
+
+#endif /* AGOPTIM_H */
--- /dev/null
+++ b/src/autohint/ahtypes.h
@@ -1,0 +1,440 @@
+/***************************************************************************/
+/* */
+/* ahtypes.h */
+/* */
+/* General types and definitions for the auto-hint module */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+
+#ifndef AGTYPES_H
+#define AGTYPES_H
+
+#include <freetype/internal/ftobjs.h> /* for freetype.h + LOCAL_DEF etc.. */
+
+#ifdef FT_FLAT_COMPILE
+#include "ahloader.h" /* glyph loader types & declarations */
+#else
+#include <autohint/ahloader.h> /* glyph loader types & declarations */
+#endif
+
+#define xxDEBUG_AG
+
+#ifdef DEBUG_AG
+#include <stdio.h>
+#define AH_LOG(x) printf##x
+#else
+#define AH_LOG(x) /* nothing */
+#endif
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/**** ****/
+/**** COMPILE-TIME BUILD OPTIONS ****/
+/**** ****/
+/**** Toggle these configuration macros to experiment with ****/
+/**** "features" of the auto-hinter.. ****/
+/**** ****/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/* if this option is defined, only strong interpolation will be used to */
+/* place the points between edges. Otherwise, "smooth" points are detected */
+/* and later hinted through weak interpolation to correct some unpleasant */
+/* artefacts.. */
+/* */
+#undef AH_OPTION_NO_WEAK_INTERPOLATION
+#undef AH_OPTION_NO_STRONG_INTERPOLATION
+
+/* undefine this macro if you don't want to hint the metrics */
+/* there is no reason to do this, except for experimentation */
+#define AH_HINT_METRICS
+
+/* define this macro if you do not want to insert extra edges at a glyph's */
+/* x and y extrema (when there isn't one already available). This help */
+/* reduce a number of artefacts and allow hinting of metrics.. */
+/* */
+#undef AH_OPTION_NO_EXTREMUM_EDGES
+
+/* don't touch for now.. */
+#define AH_MAX_WIDTHS 12
+#define AH_MAX_HEIGHTS 12
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/**** ****/
+/**** TYPES DEFINITIONS ****/
+/**** ****/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+ /* see agangles.h */
+ typedef FT_Int AH_Angle;
+
+
+ /* hint flags */
+ typedef enum AH_Flags_
+ {
+ ah_flah_none = 0,
+
+ /* bezier control points flags */
+ ah_flah_conic = 1,
+ ah_flah_cubic = 2,
+ ah_flah_control = ah_flah_conic | ah_flah_cubic,
+
+ /* extrema flags */
+ ah_flah_extrema_x = 4,
+ ah_flah_extrema_y = 8,
+
+ /* roundness */
+ ah_flah_round_x = 16,
+ ah_flah_round_y = 32,
+
+ /* touched */
+ ah_flah_touch_x = 64,
+ ah_flah_touch_y = 128,
+
+ /* weak interpolation */
+ ah_flah_weak_interpolation = 256,
+
+ /* never remove this one !! */
+ ah_flah_max
+
+ } AH_Flags;
+
+
+ /* edge hint flags */
+ typedef enum AH_Edge_Flags_
+ {
+ ah_edge_normal = 0,
+ ah_edge_round = 1,
+ ah_edge_serif = 2,
+ ah_edge_done = 4
+
+ } AH_Edge_Flags;
+
+
+ /* hint directions - the values are computed so that two vectors are */
+ /* in opposite directions iff "dir1+dir2 == 0" */
+ typedef enum AH_Direction_
+ {
+ ah_dir_none = 4,
+ ah_dir_right = 1,
+ ah_dir_left = -1,
+ ah_dir_up = 2,
+ ah_dir_down = -2
+
+ } AH_Direction;
+
+
+ typedef struct AH_Point AH_Point;
+ typedef struct AH_Segment AH_Segment;
+ typedef struct AH_Edge AH_Edge;
+
+ /***************************************************************************
+ *
+ * <Struct>
+ * AH_Point
+ *
+ * <Description>
+ * A structure used to model an outline point to the AH_Outline type
+ *
+ * <Fields>
+ * flags :: current point hint flags
+ * ox, oy :: current original scaled coordinates
+ * fx, fy :: current coordinates in font units
+ * x, y :: current hinter coordinates
+ * u, v :: point coordinates - meaning varies with context
+ *
+ * in_dir :: direction of inwards vector (prev->point)
+ * out_dir :: direction of outwards vector (point->next)
+ *
+ * in_angle :: angle of inwards vector
+ * out_angle :: angle of outwards vector
+ *
+ * next :: next point in same contour
+ * prev :: previous point in same contour
+ *
+ */
+ struct AH_Point
+ {
+ AH_Flags flags; /* point flags used by hinter */
+ FT_Pos ox, oy;
+ FT_Pos fx, fy;
+ FT_Pos x, y;
+ FT_Pos u, v;
+
+ AH_Direction in_dir; /* direction of inwards vector */
+ AH_Direction out_dir; /* direction of outwards vector */
+
+ AH_Angle in_angle;
+ AH_Angle out_angle;
+
+ AH_Point* next; /* next point in contour */
+ AH_Point* prev; /* previous point in contour */
+ };
+
+
+ /***************************************************************************
+ *
+ * <Struct>
+ * AH_Segment
+ *
+ * <Description>
+ * a structure used to describe an edge segment to the auto-hinter. A
+ * segment is simply a sequence of successive points located on the same
+ * horizontal or vertical "position", in a given direction.
+ *
+ * <Fields>
+ * flags :: segment edge flags ( straight, rounded.. )
+ * dir :: segment direction
+ *
+ * first :: first point in segment
+ * last :: last point in segment
+ * contour :: ptr to first point of segment's contour
+ *
+ * pos :: segment position in font units
+ * size :: segment size
+ *
+ * edge :: edge of current segment
+ * edge_next :: next segment on same edge
+ *
+ * link :: the pairing segment for this edge
+ * serif :: the primary segment for serifs
+ * num_linked :: the number of other segments that link to this one
+ *
+ * score :: used to score the segment when selecting them..
+ *
+ */
+ struct AH_Segment
+ {
+ AH_Edge_Flags flags;
+ AH_Direction dir;
+
+ AH_Point* first; /* first point in edge segment */
+ AH_Point* last; /* last point in edge segment */
+ AH_Point** contour; /* ptr to first point of segment's contour */
+
+ FT_Pos pos; /* position of segment */
+ FT_Pos min_coord; /* minimum coordinate of segment */
+ FT_Pos max_coord; /* maximum coordinate of segment */
+
+ AH_Edge* edge;
+ AH_Segment* edge_next;
+
+ AH_Segment* link; /* link segment */
+ AH_Segment* serif; /* primary segment for serifs */
+ FT_Pos num_linked; /* number of linked segments */
+ FT_Int score;
+ };
+
+
+ /***************************************************************************
+ *
+ * <Struct>
+ * AH_Edge
+ *
+ * <Description>
+ * a structure used to describe an edge, which really is a horizontal
+ * or vertical coordinate which will be hinted depending on the segments
+ * located on it..
+ *
+ * <Fields>
+ * flags :: segment edge flags ( straight, rounded.. )
+ * dir :: main segment direction on this edge
+ *
+ * first :: first edge segment
+ * last :: last edge segment
+ *
+ * fpos :: original edge position in font units
+ * opos :: original scaled edge position
+ * pos :: hinted edge position
+ *
+ * link :: the linked edge
+ * serif :: the serif edge
+ * num_paired :: the number of other edges that pair to this one
+ *
+ * score :: used to score the edge when selecting them..
+ *
+ * blue_edge :: indicate the blue zone edge this edge is related to
+ * only set for some of the horizontal edges in a Latin
+ * font..
+ *
+ ***************************************************************************/
+ struct AH_Edge
+ {
+ AH_Edge_Flags flags;
+ AH_Direction dir;
+
+ AH_Segment* first;
+ AH_Segment* last;
+
+ FT_Pos fpos;
+ FT_Pos opos;
+ FT_Pos pos;
+
+ AH_Edge* link;
+ AH_Edge* serif;
+ FT_Int num_linked;
+
+ FT_Int score;
+ FT_Pos* blue_edge;
+ };
+
+
+ /* an outline as seen by the hinter */
+ typedef struct AH_Outline_
+ {
+ FT_Memory memory;
+
+ AH_Direction vert_major_dir; /* vertical major direction */
+ AH_Direction horz_major_dir; /* horizontal major direction */
+
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+ FT_Pos edge_distance_threshold;
+
+ FT_Int max_points;
+ FT_Int num_points;
+ AH_Point* points;
+
+ FT_Int max_contours;
+ FT_Int num_contours;
+ AH_Point** contours;
+
+ FT_Int num_hedges;
+ AH_Edge* horz_edges;
+
+ FT_Int num_vedges;
+ AH_Edge* vert_edges;
+
+ FT_Int num_hsegments;
+ AH_Segment* horz_segments;
+
+ FT_Int num_vsegments;
+ AH_Segment* vert_segments;
+
+ } AH_Outline;
+
+
+
+ typedef enum AH_Blue_
+ {
+ ah_blue_capital_top, /* THEZOCQS */
+ ah_blue_capital_bottom, /* HEZLOCUS */
+ ah_blue_small_top, /* xzroesc */
+ ah_blue_small_bottom, /* xzroesc */
+ ah_blue_small_minor, /* pqgjy */
+
+ ah_blue_max
+
+ } AH_Blue;
+
+ typedef enum
+ {
+ ah_hinter_monochrome = 1,
+ ah_hinter_optimize = 2
+
+ } AH_Hinter_Flags;
+
+
+ /************************************************************************
+ *
+ * <Struct>
+ * AH_Globals
+ *
+ * <Description>
+ * Holds the global metrics for a given font face (be it in design
+ * units, or scaled pixel values)..
+ *
+ * <Fields>
+ * num_widths :: number of widths
+ * num_heights :: number of heights
+ * widths :: snap widths, including standard one
+ * heights :: snap height, including standard one
+ * blue_refs :: reference position of blue zones
+ * blue_shoots :: overshoot position of blue zones
+ *
+ ************************************************************************/
+
+ typedef struct AH_Globals_
+ {
+ FT_Int num_widths;
+ FT_Int num_heights;
+
+ FT_Pos widths [ AH_MAX_WIDTHS ];
+ FT_Pos heights[ AH_MAX_HEIGHTS ];
+
+ FT_Pos blue_refs [ ah_blue_max ];
+ FT_Pos blue_shoots[ ah_blue_max ];
+
+ } AH_Globals;
+
+
+ /************************************************************************
+ *
+ * <Struct>
+ * AH_Face_Globals
+ *
+ * <Description>
+ * Holds the complete global metrics for a given font face (i.e. the
+ * design units version + a scaled version + the current scales used)
+ *
+ * <Fields>
+ * face :: handle to source face object
+ * design :: globals in font design units
+ * scaled :: scaled globals in sub-pixel values
+ * x_scale :: current horizontal scale
+ * y_scale :: current vertical scale
+ *
+ ************************************************************************/
+
+ typedef struct AH_Face_Globals_
+ {
+ FT_Face face;
+ AH_Globals design;
+ AH_Globals scaled;
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+ FT_Bool control_overshoot;
+
+ } AH_Face_Globals;
+
+
+
+
+ typedef struct AH_Hinter
+ {
+ FT_Memory memory;
+ FT_Long flags;
+
+ FT_Int algorithm;
+ FT_Face face;
+
+ AH_Face_Globals* globals;
+
+ AH_Outline* glyph;
+
+ AH_Loader* loader;
+ FT_Vector pp1;
+ FT_Vector pp2;
+
+ } AH_Hinter;
+
+#endif /* AGTYPES_H */
--- /dev/null
+++ b/src/autohint/autohint.c
@@ -1,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* autohint.c */
+/* */
+/* Automatic Hinting wrapper. */
+/* */
+/* Copyright 2000: Catharon Productions Inc. */
+/* Author: David Turner */
+/* */
+/* This file is part of the Catharon Typography Project and shall only */
+/* be used, modified, and distributed under the terms of the Catharon */
+/* Open Source License that should come with this file under the name */
+/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/* Note that this license is compatible with the FreeType license */
+/* */
+/***************************************************************************/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#ifdef FT_FLAT_COMPILE
+
+#include "ahangles.c"
+#include "ahglyph.c"
+#include "ahglobal.c"
+#include "ahhint.c"
+#include "ahmodule.c"
+
+#else
+
+#include <autohint/ahangles.c>
+#include <autohint/ahglyph.c>
+#include <autohint/ahglobal.c>
+#include <autohint/ahhint.c>
+#include <autohint/ahmodule.c>
+
+#endif
+
--- /dev/null
+++ b/src/autohint/mather.py
@@ -1,0 +1,54 @@
+import math
+
+ag_pi = 256
+
+def print_arctan( atan_bits ):
+ atan_base = 1 << atan_bits
+
+ print "static AH_Angle ag_arctan[ 1L << AG_ATAN_BITS ] ="
+ print "{"
+
+ count = 0
+ line = ""
+
+ for n in range(atan_base):
+ comma = ","
+ if (n == atan_base-1):
+ comma = ""
+
+ angle = math.atan(n*1.0/atan_base)/math.pi*ag_pi
+ line = line + " " + repr(int(angle+0.5)) + comma
+ count = count+1;
+ if (count == 8):
+ count = 0
+ print line
+ line = ""
+
+ if (count >0):
+ print line
+ print "};"
+
+
+def print_sines():
+ print "static FT_Fixed ah_sines[ AG_HALF_PI+1 ] ="
+ print "{"
+ count = 0
+ line = ""
+
+ for n in range(ag_pi/2):
+ sinus = math.sin(n*math.pi/ag_pi)
+ line = line + " " + repr(int(65536.0*sinus)) + ","
+ count = count+1
+ if (count == 8):
+ count = 0
+ print line
+ line = ""
+
+ if (count > 0):
+ print line
+ print " 65536"
+ print "};"
+
+print_arctan(8)
+
--- /dev/null
+++ b/src/autohint/module.mk
@@ -1,0 +1,7 @@
+make_module_list: add_autohint_module
+
+add_autohint_module:
+ $(OPEN_DRIVER)autohint_module_class$(CLOSE_DRIVER)
+ $(ECHO_DRIVER)autohint $(ECHO_DRIVER_DESC)automatic hinting module$(ECHO_DRIVER_DONE)
+
+# EOF
--- /dev/null
+++ b/src/autohint/rules.mk
@@ -1,0 +1,86 @@
+#
+# FreeType 2 auto-hinter module configuration rules
+#
+
+
+#
+# Copyright 2000: Catharon Productions Inc.
+# Author: David Turner
+#
+# This file is part of the Catharon Typography Project and shall only
+# be used, modified, and distributed under the terms of the Catharon
+# Open Source License that should come with this file under the name
+# "CatharonLicense.txt". By continuing to use, modify, or distribute
+# this file you indicate that you have read the license and
+# understand and accept it fully.
+#
+# Note that this license is compatible with the FreeType license
+#
+#
+# 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.
+
+
+# AUTO driver directory
+#
+AUTO_DIR := $(SRC_)autohint
+AUTO_DIR_ := $(AUTO_DIR)$(SEP)
+
+
+# compilation flags for the driver
+#
+AUTO_COMPILE := $(FT_COMPILE)
+
+
+# AUTO driver sources (i.e., C files)
+#
+AUTO_DRV_SRC := $(AUTO_DIR_)ahangles.c \
+ $(AUTO_DIR_)ahglobal.c \
+ $(AUTO_DIR_)ahglyph.c \
+ $(AUTO_DIR_)ahhint.c \
+ $(AUTO_DIR_)ahmodule.c
+
+# AUTO driver headers
+#
+AUTO_DRV_H := $(AUTO_DRV_SRC:%c=%h)
+
+
+# AUTO driver object(s)
+#
+# AUTO_DRV_OBJ_M is used during `multi' builds.
+# AUTO_DRV_OBJ_S is used during `single' builds.
+#
+AUTO_DRV_OBJ_M := $(AUTO_DRV_SRC:$(AUTO_DIR_)%.c=$(OBJ_)%.$O)
+AUTO_DRV_OBJ_S := $(OBJ_)autohint.$O
+
+# AUTO driver source file for single build
+#
+AUTO_DRV_SRC_S := $(AUTO_DIR_)autohint.c
+
+
+# AUTO driver - single object
+#
+$(AUTO_DRV_OBJ_S): $(AUTO_DRV_SRC_S) $(AUTO_DRV_SRC) \
+ $(FREETYPE_H) $(AUTO_DRV_H)
+ $(AUTO_COMPILE) $T$@ $(AUTO_DRV_SRC_S)
+
+
+# AUTO driver - multiple objects
+#
+$(OBJ_)%.$O: $(AUTO_DIR_)%.c $(FREETYPE_H) $(AUTO_DRV_H)
+ $(AUTO_COMPILE) $T$@ $<
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(AUTO_DRV_OBJ_S)
+DRV_OBJS_M += $(AUTO_DRV_OBJ_M)
+
+
+# EOF