summaryrefslogtreecommitdiff
path: root/gnu/java/awt/font/autofit/Latin.java
diff options
context:
space:
mode:
authorRoman Kennke <roman@kennke.org>2006-12-13 22:59:43 +0000
committerRoman Kennke <roman@kennke.org>2006-12-13 22:59:43 +0000
commita049c95fb5678f8ae1ba46c69f4984506e46f47c (patch)
tree791cfbf28cd75b33f20e596f3f89d98af0986f7f /gnu/java/awt/font/autofit/Latin.java
parentf765c5a58eb9d1a38ccccacc10c7f49aab4a706e (diff)
downloadclasspath-a049c95fb5678f8ae1ba46c69f4984506e46f47c.tar.gz
2006-12-13 Roman Kennke <kennke@aicas.com>
* gnu/java/awt/font/autofit/AutoHinter.java: New class. The entry point into the autohinter. * gnu/java/awt/font/autofit/AxisHints.java (majorDir): New field. (numEdges): New field. (numSegments): New field. (AxisHints): New constructor. (newSegment): New method. Records a new segment. * gnu/java/awt/font/autofit/Constants.java (DIR_DOWN): New constant. (DIR_LEFT): New constant. (DIR_RIGHT): New constant. (DIR_TOP): New constant. (DIR_NONE): New constant. * gnu/java/awt/font/autofit/GlyphHints.java (contours): New field. (numContours): New field. (maxPoints): New field. (metrics): New field. (numPoints): New field. (points): New field. (GlyphHints): New constructor. (computeInflectionPoints): New helper method. (computeSegments): Moved to Latin. (linkSegments): Moved to Lating. (reload): Implemented. (rescale): Copy the metrics. (setWeakPoint): New helper method. * gnu/java/awt/font/autofit/Latin.java (MAX_WIDTH): Make package private. (computeSegments): New method. Computes the segments to a glyph. (initWidths): Implemented. Determines the standard widths of stems for the font. (linkSegments): New method. Links stem segments and determines serif segments. * gnu/java/awt/font/autofit/LatinMetrics.java (LatinMetrics()): New constructor. (LatinMetrics(OpenTypeFont)): New constructor. * gnu/java/awt/font/autofit/ScriptMetrics.java (ScriptMetrics): New constructor. * gnu/java/awt/font/autofit/Segment.java (FLAG_EDGE_NORMAL): New constant. (FLAG_EDGE_ROUND): New constant. (contour): New field. (dir): New field. (first): New field. (flags): New field. (index): Removed. (last): New field. (len): New field. (maxPos): New field. (minPos): New field. (numLinked): New field. (score): New field. (serif): New field. (toString): New method. For debug output. * gnu/java/awt/font/autofit/Utils.java: New utility class. * gnu/java/awt/font/opentype/Hinter.java: New interface for hinter implementations. * gnu/java/awt/font/opentype/OpenTypeFont.java (hinter): New field. Stores the hinter for that font. (checkHinter): Checks if a hinter is installed and installs one if necessary. (createGlyphVector): Check installed hinter. * gnu/java/awt/font/opentype/truetype/Fixed.java: Make class public for access in the autohinting package. * gnu/java/awt/font/opentype/truetype/Point.java: New class. Stores coordinates and additional info that describe the outline of a glyph. * gnu/java/awt/font/opentype/truetype/Zone.java: Use Point class for storing the points.
Diffstat (limited to 'gnu/java/awt/font/autofit/Latin.java')
-rw-r--r--gnu/java/awt/font/autofit/Latin.java200
1 files changed, 195 insertions, 5 deletions
diff --git a/gnu/java/awt/font/autofit/Latin.java b/gnu/java/awt/font/autofit/Latin.java
index 0352b41a4..a26529270 100644
--- a/gnu/java/awt/font/autofit/Latin.java
+++ b/gnu/java/awt/font/autofit/Latin.java
@@ -39,8 +39,10 @@ exception statement from your version. */
package gnu.java.awt.font.autofit;
import java.awt.geom.AffineTransform;
+import java.util.HashSet;
import gnu.java.awt.font.opentype.OpenTypeFont;
+import gnu.java.awt.font.opentype.truetype.Point;
import gnu.java.awt.font.opentype.truetype.Zone;
/**
@@ -50,7 +52,7 @@ class Latin
implements Script, Constants
{
- private static final int MAX_WIDTHS = 16;
+ static final int MAX_WIDTHS = 16;
public void applyHints(GlyphHints hints, ScriptMetrics metrics)
{
@@ -135,19 +137,21 @@ class Latin
LatinAxis axis = metrics.axis[dim];
AxisHints axHints = hints.axis[dim];
int numWidths = 0;
- hints.computeSegments(dim);
- hints.linkSegments(dim);
+ computeSegments(hints, dim);
+ linkSegments(hints, dim);
Segment[] segs = axHints.segments;
+ HashSet<Segment> touched = new HashSet<Segment>();
for (int i = 0; i < segs.length; i++)
{
Segment seg = segs[i];
Segment link = seg.link;
- if (link != null && link.link == seg && link.index > i)
+ if (link != null && link.link == seg && ! touched.contains(link))
{
int dist = Math.abs(seg.pos - link.pos);
if (numWidths < MAX_WIDTHS)
- axis.widths[numWidths++].org = dist;
+ axis.widths[numWidths++] = new Width(dist);
}
+ touched.add(seg);
}
}
for (int dim = 0; dim < DIMENSION_MAX; dim++)
@@ -159,6 +163,78 @@ class Latin
}
}
+ void linkSegments(GlyphHints hints, int dim)
+ {
+ AxisHints axis = hints.axis[dim];
+ Segment[] segments = axis.segments;
+ int numSegs = axis.numSegments;
+ int majorDir = axis.majorDir;
+ int lenThreshold = constant((LatinMetrics) hints.metrics, 8);
+ lenThreshold = Math.min(1, lenThreshold);
+ int lenScore = constant((LatinMetrics) hints.metrics, 3000);
+ for (int i1 = 0; i1 < numSegs; i1++)
+ {
+ Segment seg1 = segments[i1];
+ // The fake segments are introduced to hint the metrics.
+ // Never link them to anything.
+ if (seg1.first == seg1.last || seg1.dir != majorDir)
+ continue;
+ for (int i2 = 0; i2 < numSegs; i2++)
+ {
+ Segment seg2 = segments[i2];
+ if (seg2 != seg1 && seg1.dir + seg2.dir == 0)
+ {
+ int pos1 = seg1.pos;
+ int pos2 = seg2.pos;
+ // The vertical coords are swapped compared to how FT handles
+ // this.
+ int dist = dim == DIMENSION_VERT ? pos1 - pos2 : pos2 - pos1;
+ if (dist >= 0)
+ {
+ int min = seg1.minPos;
+ int max = seg1.maxPos;
+ int len, score;
+ if (min < seg2.minPos)
+ min = seg2.minPos;
+ if (max > seg2.maxPos)
+ max = seg2.maxPos;
+ len = max - min;
+ if (len > lenThreshold)
+ {
+ score = dist + lenScore / len;
+ if (score < seg1.score)
+ {
+ seg1.score = score;
+ seg1.link = seg2;
+ }
+ if (score < seg2.score)
+ {
+ seg2.score = score;
+ seg2.link = seg1;
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int i1 = 0; i1 < numSegs; i1++)
+ {
+ Segment seg1 = segments[i1];
+ Segment seg2 = seg1.link;
+ if (seg2 != null)
+ {
+ seg2.numLinked++;
+ if (seg2.link != seg1)
+ {
+ seg1.link = null;
+ seg1.serif = seg2.link;
+ }
+ }
+ // Uncomment to show all segments.
+ // System.err.println("segment#" + i1 + ": " + seg1);
+ }
+ }
+
/**
* Initializes the blue zones of the font.
*
@@ -174,4 +250,118 @@ class Latin
{
return c * (metrics.unitsPerEm / 2048);
}
+
+ private void computeSegments(GlyphHints hints, int dim)
+ {
+ Point[] points = hints.points;
+ if (dim == DIMENSION_HORZ)
+ {
+ for (int i = 0; i < hints.numPoints; i++)
+ {
+ points[i].setU(points[i].getOrigX());
+ points[i].setV(points[i].getOrigY());
+ }
+ }
+ else
+ {
+ for (int i = 0; i < hints.numPoints; i++)
+ {
+ points[i].setU(points[i].getOrigY());
+ points[i].setV(points[i].getOrigX());
+ }
+ }
+ // Now look at each contour.
+ AxisHints axis = hints.axis[dim];
+ int majorDir = Math.abs(axis.majorDir);
+ int segmentDir = majorDir;
+ Point[] contours = hints.contours;
+ int numContours = hints.numContours;
+ Segment segment = null;
+ for (int i = 0; i < numContours; i++)
+ {
+ int minPos = 32000;
+ int maxPos = -32000;
+
+ Point point = contours[i];
+ Point last = point.getPrev();
+ if (point == last) // Skip singletons.
+ continue;
+ if (Math.abs(last.getOutDir()) == majorDir
+ && Math.abs(point.getOutDir()) == majorDir)
+ {
+ // We are already on an edge. Locate its start.
+ last = point;
+ while (true)
+ {
+ point = point.getPrev();
+ if (Math.abs(point.getOutDir()) != majorDir)
+ {
+ point = point.getNext();
+ break;
+ }
+ if (point == last)
+ break;
+ }
+ }
+ last = point;
+ boolean passed = false;
+ boolean onEdge = false;
+ while (true)
+ {
+ int u, v;
+ if (onEdge)
+ {
+ u = point.getU();
+ if (u < minPos)
+ minPos = u;
+ if (u > maxPos)
+ maxPos = u;
+ if (point.getOutDir() != segmentDir || point == last)
+ {
+ // Leaving an edge. Record new segment.
+ segment.last = point;
+ // (minPos + maxPos) / 2.
+ segment.pos = (minPos + maxPos) >> 1;
+ if (segment.first.isControlPoint()
+ || point.isControlPoint())
+ segment.flags |= Segment.FLAG_EDGE_ROUND;
+ minPos = maxPos = point.getV();
+ v = segment.first.getV();
+ if (v < minPos)
+ minPos = v;
+ if (v > maxPos)
+ maxPos = v;
+ segment.minPos = minPos;
+ segment.maxPos = maxPos;
+ onEdge = false;
+ segment = null;
+ }
+ }
+ if (point == last)
+ {
+ if (passed)
+ break;
+ passed = true;
+ }
+ if (! onEdge && Math.abs(point.getOutDir()) == majorDir)
+ {
+ // This is the start of a new segment.
+ segmentDir = point.getOutDir();
+ segment = axis.newSegment();
+ segment.dir = segmentDir;
+ segment.flags = Segment.FLAG_EDGE_NORMAL;
+ minPos = maxPos = point.getU();
+ segment.first = point;
+ segment.last = point;
+ segment.contour = contours[i];
+ segment.score = 32000;
+ segment.len = 0;
+ segment.link = null;
+ onEdge = true;
+ }
+ point = point.getNext();
+ }
+ }
+
+ }
}