From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- .../classpath/gnu/java/awt/font/FontDelegate.java | 329 ++++ .../classpath/gnu/java/awt/font/FontFactory.java | 90 + .../gnu/java/awt/font/GNUGlyphVector.java | 663 +++++++ .../gnu/java/awt/font/OpenTypeFontPeer.java | 565 ++++++ .../gnu/java/awt/font/autofit/AutoHinter.java | 83 + .../gnu/java/awt/font/autofit/AxisHints.java | 112 ++ .../gnu/java/awt/font/autofit/Constants.java | 86 + .../classpath/gnu/java/awt/font/autofit/Edge.java | 82 + .../gnu/java/awt/font/autofit/GlyphHints.java | 640 +++++++ .../gnu/java/awt/font/autofit/HintScaler.java | 53 + .../classpath/gnu/java/awt/font/autofit/Latin.java | 1363 +++++++++++++++ .../gnu/java/awt/font/autofit/LatinAxis.java | 62 + .../gnu/java/awt/font/autofit/LatinBlue.java | 61 + .../gnu/java/awt/font/autofit/LatinMetrics.java | 66 + .../gnu/java/awt/font/autofit/Script.java | 62 + .../gnu/java/awt/font/autofit/ScriptMetrics.java | 53 + .../gnu/java/awt/font/autofit/Segment.java | 97 ++ .../classpath/gnu/java/awt/font/autofit/Utils.java | 255 +++ .../classpath/gnu/java/awt/font/autofit/Width.java | 64 + .../gnu/java/awt/font/opentype/CharGlyphMap.java | 1027 +++++++++++ .../gnu/java/awt/font/opentype/GlyphNamer.java | 1135 ++++++++++++ .../gnu/java/awt/font/opentype/Hinter.java | 63 + .../java/awt/font/opentype/MacResourceFork.java | 235 +++ .../gnu/java/awt/font/opentype/NameDecoder.java | 702 ++++++++ .../gnu/java/awt/font/opentype/OpenTypeFont.java | 882 ++++++++++ .../awt/font/opentype/OpenTypeFontFactory.java | 140 ++ .../gnu/java/awt/font/opentype/Scaler.java | 205 +++ .../gnu/java/awt/font/opentype/truetype/Fixed.java | 176 ++ .../awt/font/opentype/truetype/GlyphLoader.java | 447 +++++ .../awt/font/opentype/truetype/GlyphLocator.java | 187 ++ .../awt/font/opentype/truetype/GlyphMeasurer.java | 228 +++ .../gnu/java/awt/font/opentype/truetype/Point.java | 287 ++++ .../awt/font/opentype/truetype/TrueTypeScaler.java | 380 ++++ .../awt/font/opentype/truetype/VirtualMachine.java | 1815 ++++++++++++++++++++ .../gnu/java/awt/font/opentype/truetype/Zone.java | 291 ++++ .../font/opentype/truetype/ZonePathIterator.java | 393 +++++ .../truetype/doc-files/ZonePathIterator-1.dia | Bin 0 -> 1572 bytes .../truetype/doc-files/ZonePathIterator-1.png | Bin 0 -> 11278 bytes 38 files changed, 13379 insertions(+) create mode 100644 libjava/classpath/gnu/java/awt/font/FontDelegate.java create mode 100644 libjava/classpath/gnu/java/awt/font/FontFactory.java create mode 100644 libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java create mode 100644 libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Constants.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Edge.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Latin.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Script.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Segment.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Utils.java create mode 100644 libjava/classpath/gnu/java/awt/font/autofit/Width.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/Hinter.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/Scaler.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia create mode 100644 libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png (limited to 'libjava/classpath/gnu/java/awt/font') diff --git a/libjava/classpath/gnu/java/awt/font/FontDelegate.java b/libjava/classpath/gnu/java/awt/font/FontDelegate.java new file mode 100644 index 000000000..e5c03a10f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/FontDelegate.java @@ -0,0 +1,329 @@ +/* FontDelegate.java -- Interface implemented by all font delegates. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.text.CharacterIterator; +import java.util.Locale; + + +/** + * The interface that all font delegate objects implement, + * irrespective of where they get their information from. + * + *

Thread Safety: All classes that implement the + * FontDelegate interface must allow calling these + * methods from multiple concurrent threads. The delegates are + * responsible for performing the necessary synchronization. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public interface FontDelegate +{ + public static final int FLAG_FITTED = 1 << 0; + public static final int FLAG_NO_HINT_HORIZONTAL = 1 << 1; + public static final int FLAG_NO_HINT_VERTICAL = 1 << 2; + public static final int FLAG_NO_HINT_EDGE_POINTS = 1 << 3; + public static final int FLAG_NO_HINT_STRONG_POINTS = 1 << 4; + public static final int FLAG_NO_HINT_WEAK_POINTS = 1 << 5; + + /** + * Returns the full name of this font face in the specified + * locale, for example “Univers Light”. + * + * @param locale the locale for which to localize the name. + * + * @return the face name. + */ + public String getFullName(Locale locale); + + + /** + * Returns the name of the family to which this font face belongs, + * for example “Univers”. + * + * @param locale the locale for which to localize the name. + * + * @return the family name. + */ + public String getFamilyName(Locale locale); + + + /** + * Returns the name of this font face inside the family, for example + * “Light”. + * + * @param locale the locale for which to localize the name. + * + * @return the name of the face inside its family. + */ + public String getSubFamilyName(Locale locale); + + + /** + * Returns the PostScript name of this font face, for example + * “Helvetica-Bold”. + * + * @return the PostScript name, or null if the font + * does not provide a PostScript name. + */ + public String getPostScriptName(); + + + /** + * Returns the number of glyphs in this font face. + */ + public int getNumGlyphs(); + + /** + * Returns the glyph code for the specified character. + * + * @param c the character to map + * + * @return the glyph code + */ + public int getGlyphIndex(int c); + + /** + * Returns the index of the glyph which gets displayed if the font + * cannot map a Unicode code point to a glyph. Many fonts show this + * glyph as an empty box. + */ + public int getMissingGlyphCode(); + + + /** + * Creates a GlyphVector by mapping each character in a + * CharacterIterator to the corresponding glyph. + * + *

The mapping takes only the font’s cmap + * tables into consideration. No other operations (such as glyph + * re-ordering, composition, or ligature substitution) are + * performed. This means that the resulting GlyphVector will not be + * correct for text in languages that have complex + * character-to-glyph mappings, such as Arabic, Hebrew, Hindi, or + * Thai. + * + * @param font the font object that the created GlyphVector + * will return when it gets asked for its font. This argument is + * needed because the public API works with java.awt.Font, + * not with some private delegate like OpenTypeFont. + * + * @param frc the font rendering parameters that are used for + * measuring glyphs. The exact placement of text slightly depends on + * device-specific characteristics, for instance the device + * resolution or anti-aliasing. For this reason, any measurements + * will only be accurate if the passed + * FontRenderContext correctly reflects the relevant + * parameters. Hence, frc should be obtained from the + * same Graphics2D that will be used for drawing, and + * any rendering hints should be set to the desired values before + * obtaining frc. + * + * @param ci a CharacterIterator for iterating over the + * characters to be displayed. + */ + public GlyphVector createGlyphVector(Font font, + FontRenderContext frc, + CharacterIterator ci); + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @param advance a point whose x and y + * fields will hold the advance in each direction. It is well + * possible that both values are non-zero, for example for rotated + * text or for Urdu fonts. + */ + public void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance); + + + /** + * Returns the shape of a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, this + * parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional + * metrics, false for rounding the result to a pixel + * boundary. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or null for bitmap fonts. + */ + public GeneralPath getGlyphOutline(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + int type); + + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. + * + *

Names are not unique: Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.

+ * + *

This situation would occur for an OpenType or TrueType font + * that has a post table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * Zapf table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + */ + public String getGlyphName(int glyphIndex); + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public float getAscent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); +} diff --git a/libjava/classpath/gnu/java/awt/font/FontFactory.java b/libjava/classpath/gnu/java/awt/font/FontFactory.java new file mode 100644 index 000000000..53eb5df5f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/FontFactory.java @@ -0,0 +1,90 @@ +/* FontFactory.java -- Factory for font delegates. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font; + +import java.nio.ByteBuffer; + +import java.awt.FontFormatException; +import gnu.java.awt.font.opentype.OpenTypeFontFactory; + + +/** + * A factory for creating font delegate objects. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class FontFactory +{ + /** + * The constructor is private so nobody can construct an instance + */ + private FontFactory() + { + } + + + /** + * Creates FontDelegate objects for the fonts in the specified buffer. + * The following font formats are currently recognized: + * recognized font formats are: + * + *

+ * + *

Some formats may contain more than a single font, for example + * *.ttc and *.dfont files. This is the reason why this function + * returns an array. + * + *

The implementation reads data from the buffer only when + * needed. Therefore, it greatly increases efficiency if + * buf has been obtained through mapping a file into + * the virtual address space. + * + * @throws FontFormatException if the font data is not in one of the + * known formats. + */ + public static FontDelegate[] createFonts(ByteBuffer buf) + throws FontFormatException + { + return OpenTypeFontFactory.createFonts(buf); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java new file mode 100644 index 000000000..9fd80e79e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/GNUGlyphVector.java @@ -0,0 +1,663 @@ +/* GNUGlyphVector.java -- The GNU implementation of GlyphVector. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font; + +import gnu.java.awt.java2d.ShapeWrapper; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphVector; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + + +/** + * The GNU implementation of the abstract GlyphVector class, which + * uses the services provided by a FontDelegate for its functionality. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class GNUGlyphVector + extends GlyphVector +{ + private FontDelegate fontDelegate; + private Font font; + private FontRenderContext renderContext; + private int[] glyphs; + private float fontSize; + private AffineTransform transform; + private boolean valid; + + + /** + * The position of each glyph. The horizontal position of the + * i-th glyph is at pos[i * 2], its + * vertical position at pos[i * 2 + 1]. The total + * advance width of the entire vector is stored at + * pos[numGlyphs], the total advance height at + * pos[numGlyphs + 1]. + */ + private float[] pos; + + + private AffineTransform[] transforms; + private int layoutFlags; + + /** + * The cached non-transformed outline of this glyph vector. + */ + private Shape cleanOutline; + + /** + * Constructs a new GNUGlyphVector. + * + * @param fontDelegate the FontDelegate that creates this vector. + * + * @param font the Font that this GlyphVector will return for {@link + * #getFont()}. That object is also used to determine the point + * size, which affects the affine transformation used by the font + * scaler. + * + * @param renderContext an object with parameters for font + * rendering, such as whether anti-aliasing is enabled. + * + * @param glyphs the glyphs in this vector. + */ + public GNUGlyphVector(FontDelegate fontDelegate, + Font font, + FontRenderContext renderContext, + int[] glyphs) + { + this.fontDelegate = fontDelegate; + this.font = font; + this.renderContext = renderContext; + this.glyphs = glyphs; + + fontSize = font.getSize2D(); + transform = font.getTransform(); // returns a modifiable copy + //transform.concatenate(renderContext.getTransform()); + } + + + + /** + * Returns the font of the glyphs in this GlyphVector. + */ + public Font getFont() + { + return font; + } + + + /** + * Returns the FontRenderContext that is used to calculate the + * extent and position of the glyphs. + */ + public FontRenderContext getFontRenderContext() + { + return renderContext; + } + + + /** + * Moves each glyph in the vector to its default position. + */ + public void performDefaultLayout() + { + float x, y, advanceWidth, advanceHeight; + int i, p; + AffineTransform tx; + Point2D.Float advance = new Point2D.Float(); + + pos = new float[(glyphs.length + 1) * 2]; + x = y = 0.0f; + p = 0; + for (i = p = 0; i < glyphs.length; i++) + { + p += 2; + + if ((transforms == null) || (tx = transforms[i]) == null) + tx = this.transform; + else + { + tx = new AffineTransform(tx); + tx.concatenate(this.transform); + } + + fontDelegate.getAdvance(glyphs[i], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true, + advance); + // FIXME: We shouldn't round here, but instead hint the metrics + // correctly. + pos[p] = x += Math.round(advance.x); + pos[p + 1] = y += advance.y; + } + valid = true; + } + + + /** + * Determines the number of glyphs in this GlyphVector. + */ + public int getNumGlyphs() + { + return glyphs.length; + } + + + /** + * Determines the glyph number by index in this vector. + * Glyph numbers are specific to each font, so two fonts + * will likely assign different numbers to the same glyph. + * + * @param glyphIndex the index of the glyph whose glyph number is to + * be retrieved. + * + * @throws IndexOutOfBoundsException if glyphIndex + * is not in the range . + */ + public int getGlyphCode(int glyphIndex) + { + /* The exception is thrown automatically if the index is out + * of the valid bounds. + */ + return glyphs[glyphIndex]; + } + + + /** + * Returns a slice of this GlyphVector. + * + * @param firstGlyphIndex the index of the first glyph in the + * returned slice. + * + * @param numEntries the size of the returned slice. + * + * @param outCodes a pre-allocated array for storing the slice, + * or null to cause allocation of a new array. + * + * @return a slice of this GlyphVector. If outCodes + * is null, the slice will be stored into a freshly + * allocated array; otherwise, the result will be stored into + * outCodes. + */ + public int[] getGlyphCodes(int firstGlyphIndex, + int numEntries, + int[] outCodes) + { + if (numEntries < 0) + throw new IllegalArgumentException(); + if (outCodes == null) + outCodes = new int[numEntries]; + System.arraycopy(glyphs, firstGlyphIndex, outCodes, 0, numEntries); + return outCodes; + } + + + public Rectangle2D getLogicalBounds() + { + float ascent, descent; + + validate(); + + return new Rectangle2D.Float(0, 0, + pos[pos.length - 2], + getAscent() - getDescent()); + } + + + public Rectangle2D getVisualBounds() + { + validate(); + + // FIXME: Not yet implemented. + return getLogicalBounds(); + } + + + /** + * Returns the shape of this GlyphVector. + */ + public Shape getOutline() + { + return getOutline(0.0f, 0.0f); + } + + + /** + * Returns the shape of this GlyphVector, translated to the + * specified position. + * + * @param x the horizontal position for rendering this vector. + * @param y the vertical position for rendering this vector. + */ + public Shape getOutline(float x, float y) + { + validate(); + + Shape outline; + if (cleanOutline == null) + { + GeneralPath path = new GeneralPath(); + int len = glyphs.length; + for (int i = 0; i < len; i++) + { + GeneralPath p = new GeneralPath(getGlyphOutline(i)); + path.append(p, false); + } + // Protect the cached instance from beeing modified by application + // code. + cleanOutline = new ShapeWrapper(path); + outline = cleanOutline; + } + else + { + outline = cleanOutline; + } + if (x != 0 || y != 0) + { + GeneralPath path = new GeneralPath(outline); + AffineTransform t = new AffineTransform(); + t.translate(x, y); + path.transform(t); + outline = path; + } + return outline; + } + + public Shape getOutline(float x, float y, int type) + { + validate(); + + GeneralPath outline = new GeneralPath(); + int len = glyphs.length; + for (int i = 0; i < len; i++) + { + GeneralPath p = new GeneralPath(getGlyphOutline(i, type)); + outline.append(p, false); + } + AffineTransform t = new AffineTransform(); + t.translate(x, y); + outline.transform(t); + return outline; + } + + /** + * Determines the shape of the specified glyph. + * + * @throws IndexOutOfBoundsException if glyphIndex is + * not in the range . + */ + public Shape getGlyphOutline(int glyphIndex) + { + AffineTransform tx, glyphTx; + GeneralPath path; + + validate(); + + if ((transforms != null) + && ((glyphTx = transforms[glyphIndex]) != null)) + { + tx = new AffineTransform(transform); + tx.concatenate(glyphTx); + } + else + tx = transform; + + path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + FontDelegate.FLAG_FITTED); + + tx = new AffineTransform(); + tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]); + path.transform(tx); + return path; + } + + public Shape getGlyphOutline(int glyphIndex, int type) + { + AffineTransform tx, glyphTx; + GeneralPath path; + + validate(); + + if ((transforms != null) + && ((glyphTx = transforms[glyphIndex]) != null)) + { + tx = new AffineTransform(transform); + tx.concatenate(glyphTx); + } + else + tx = transform; + + path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + type); + + tx = new AffineTransform(); + tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]); + path.transform(tx); + return path; + } + + /** + * Determines the position of the specified glyph, or the + * total advance width and height of the vector. + * + * @param glyphIndex the index of the glyph in question. + * If this value equals getNumGlyphs(), the + * position after the last glyph will be returned, + * which is the total advance width and height of the vector. + * + * @throws IndexOutOfBoundsException if glyphIndex is + * not in the range . + */ + public Point2D getGlyphPosition(int glyphIndex) + { + validate(); + return new Point2D.Float(pos[glyphIndex * 2], + pos[glyphIndex * 2 + 1]); + } + + + /** + * Moves the specified glyph to a new position, or changes the + * advance width and height of the entire glyph vector. + * + *

Note that the position of an individual glyph may also + * affected by its affine transformation. + * + * @param glyphIndex the index of the moved glyph. If + * glyphIndex equals the total number of glyphs in this + * vector, the advance width and height of the vector is changed. + * + * @param position the new position of the glyph. + * + * @throws IndexOutOfBoundsException if glyphIndex is + * not in the range . + */ + public void setGlyphPosition(int glyphIndex, Point2D position) + { + validate(); + pos[glyphIndex * 2] = (float) position.getX(); + pos[glyphIndex * 2 + 1] = (float) position.getY(); + } + + + /** + * Returns the affine transformation that is applied to the + * glyph at the specified index. + * + * @param glyphIndex the index of the glyph whose transformation + * is to be retrieved. + * + * @return an affine transformation, or null + * for the identity transformation. + * + * @throws IndexOutOfBoundsException if glyphIndex is + * not in the range . + */ + public AffineTransform getGlyphTransform(int glyphIndex) + { + if (transforms == null) + return null; + else + return transforms[glyphIndex]; + } + + + /** + * Applies an affine transformation to the glyph at the specified + * index. + * + * @param glyphIndex the index of the glyph to which the + * transformation is applied. + * + * @param transform the affine transformation for the glyph, or + * null for an identity transformation. + */ + public void setGlyphTransform(int glyphIndex, + AffineTransform transform) + { + if (transforms == null) + transforms = new AffineTransform[glyphs.length]; + transforms[glyphIndex] = transform; + + /* If the GlyphVector has only a transform for a single glyph, and + * the caller clears its transform, the FLAG_HAS_TRANSFORMS bit + * should be cleared in layoutFlags. However, this would require + * that we keep track of the number of transformed glyphs, or that + * we count them when a transform is cleared. This would + * complicate the code quite a bit. Note that the only drawback of + * wrongly setting FLAG_HAS_TRANSFORMS is that a slower code path + * might be taken for rendering the vector. Right now, we never + * really look at the flag, so it does not make any difference. + */ + if (transform != null) + layoutFlags |= FLAG_HAS_TRANSFORMS; + valid = false; + } + + + /** + * Returns flags that can be used for optimizing the rendering + * of this GlyphVector. + * + * @return a bit mask with the applicable flags set. + * + * @since 1.4 + * + * @see GlyphVector#FLAG_HAS_POSITION_ADJUSTMENTS + * @see GlyphVector#FLAG_HAS_TRANSFORMS + * @see GlyphVector#FLAG_RUN_RTL + * @see GlyphVector#FLAG_COMPLEX_GLYPHS + * @see GlyphVector#FLAG_MASK + */ + public int getLayoutFlags() + { + return layoutFlags; + } + + + /** + * Returns the positions of a range of glyphs in this vector. + * + * @param firstGlyphIndex the index of the first glyph whose + * position is retrieved. + * + * @param numGlyphs the number of glyphs whose positions + * are retrieved. + * + * @param outPositions an array for storing the results + * (the length must be at least twice numGlyphs), + * or null for freshly allocating an array. + * + * @return an array with the glyph positions. The horizontal + * position of the i-th glyph is at index 2 * + * i, the vertical position at index 2 * i + 1. + * + * @throws IllegalArgumentException if numGlyphs + * is less than zero. + * + * @throws IndexOutOfBoundsException if either + * firstGlyphIndex or (firstGlyphIndex + + * numGlyphs) is not in the range [0 .. getNumGlyphs() - + * 1]. + */ + public float[] getGlyphPositions(int firstGlyphIndex, + int numGlyphs, + float[] outPositions) + { + if (numGlyphs < 0) + throw new IllegalArgumentException(); + + validate(); + if (outPositions == null) + outPositions = new float[numGlyphs * 2]; + + System.arraycopy(/*src */ pos, /* srcStart */ firstGlyphIndex * 2, + /* dest */ outPositions, /* destStart */ 0, + /* length */ numGlyphs * 2); + return outPositions; + } + + + private float getAscent() + { + return fontDelegate.getAscent(fontSize, transform, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true); + } + + + private float getDescent() + { + return fontDelegate.getDescent(fontSize, transform, + renderContext.isAntiAliased(), + renderContext.usesFractionalMetrics(), + /* horizontal */ true); + } + + + public Shape getGlyphLogicalBounds(int glyphIndex) + { + float x, y, ascent; + + validate(); + ascent = getAscent(); + x = pos[glyphIndex * 2]; + y = pos[glyphIndex * 2 + 1]; + + return new Rectangle2D.Float(x, y - ascent, + pos[(glyphIndex + 1) * 2] - x, + ascent - getDescent()); + } + + + public Shape getGlyphVisualBounds(int glyphIndex) + { + return getGlyphOutline(glyphIndex).getBounds2D(); + } + + + /** + * Determines the metrics of the glyph at the specified index. + * + * @param glyphIndex the index of the glyph whose metrics is to be + * retrieved. + * + * @throws IndexOutOfBoundsException if glyphIndex is + * not in the range . + */ + public GlyphMetrics getGlyphMetrics(int glyphIndex) + { + // FIXME: Not yet implemented. + throw new UnsupportedOperationException(); + } + + + /** + * Determines the justification information for the glyph at the + * specified index. + * + * @param glyphIndex the index of the glyph whose justification + * information is to be retrieved. + * + * @throws IndexOutOfBoundsException if glyphIndex is + * not in the range . + */ + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) + { + // FIXME: Not yet implemented. + throw new UnsupportedOperationException(); + } + + + /** + * Determines whether another GlyphVector is for the same font and + * rendering context, uses the same glyphs and positions them to the + * same location. + * + * @param other the GlyphVector to compare with. + * + * @return true if the two vectors are equal, + * false otherwise. + */ + public boolean equals(GlyphVector other) + { + GNUGlyphVector o; + if (!(other instanceof GNUGlyphVector)) + return false; + + o = (GNUGlyphVector) other; + if ((this.font != o.font) + || (this.fontDelegate != o.fontDelegate) + || (this.renderContext != o.renderContext) + || (this.glyphs.length != o.glyphs.length)) + return false; + + for (int i = 0; i < glyphs.length; i++) + if (this.glyphs[i] != o.glyphs[i]) + return false; + + validate(); + o.validate(); + for (int i = 0; i < pos.length; i++) + if (this.pos[i] != o.pos[i]) + return false; + + return true; + } + + private void validate() + { + if (!valid) + performDefaultLayout(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java b/libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java new file mode 100644 index 000000000..d8bff1199 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/OpenTypeFontPeer.java @@ -0,0 +1,565 @@ +/* XFontPeer2.java -- A Java based TTF font peer for X + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font; + + +import gnu.java.awt.peer.ClasspathFontPeer; +import gnu.java.lang.CPStringBuilder; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +public class OpenTypeFontPeer + extends ClasspathFontPeer +{ + + /** + * The font mapping as specified in the file fonts.properties. + */ + private static Properties fontProperties; + + /** + * The available font family names. + */ + private static Set availableFontNames; + + /** + * Font spec to file mapping. + */ + private static Map> fontToFileMap; + + static + { + fontProperties = new Properties(); + InputStream in = OpenTypeFontPeer.class.getResourceAsStream("fonts.properties"); + try + { + fontProperties.load(in); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + private class XLineMetrics + extends LineMetrics + { + + private Font font; + private GlyphVector glyphVector; +// private CharacterIterator characterIterator; +// private int begin; +// private int limit; + private FontRenderContext fontRenderContext; + XLineMetrics(Font f, CharacterIterator ci, int b, int l, + FontRenderContext rc) + { + font = f; +// characterIterator = ci; +// begin = b; +// limit = l; + fontRenderContext = rc; + glyphVector = fontDelegate.createGlyphVector(font, fontRenderContext, + ci); + } + + public float getAscent() + { + return fontDelegate.getAscent(font.getSize(), fontRenderContext.getTransform(), + fontRenderContext.isAntiAliased(), + fontRenderContext.usesFractionalMetrics(), true); + } + + public int getBaselineIndex() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public float[] getBaselineOffsets() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public float getDescent() + { + return (int) fontDelegate.getDescent(font.getSize(), IDENDITY, false, + false, false); + } + + public float getHeight() + { + return (float) glyphVector.getLogicalBounds().getHeight(); + } + + public float getLeading() + { + return getHeight() - getAscent() - getDescent(); + } + + public int getNumChars() + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public float getStrikethroughOffset() + { + return 0.F; + } + + public float getStrikethroughThickness() + { + return 0.F; + } + + public float getUnderlineOffset() + { + return 0.F; + } + + public float getUnderlineThickness() + { + return 0.F; + } + + } + + private class XFontMetrics + extends FontMetrics + { + /** + * A cached point instance, to be used in #charWidth(). + */ + private Point2D cachedPoint = new Point2D.Double(); + + XFontMetrics(Font f) + { + super(f); + } + + public int getAscent() + { + return (int) fontDelegate.getAscent(getFont().getSize(), IDENDITY, + false, false, false); + } + + public int getDescent() + { + return (int) fontDelegate.getDescent(getFont().getSize(), IDENDITY, + false, false, false); + } + + public int getHeight() + { + GlyphVector gv = fontDelegate.createGlyphVector(getFont(), + new FontRenderContext(IDENDITY, false, false), + new StringCharacterIterator("m")); + Rectangle2D b = gv.getVisualBounds(); + return (int) b.getHeight(); + } + + public int charWidth(char c) + { + int code = fontDelegate.getGlyphIndex(c); + Point2D advance = cachedPoint; + fontDelegate.getAdvance(code, font.getSize2D(), IDENDITY, + false, false, true, advance); + return (int) advance.getX(); + } + + public int charsWidth(char[] chars, int offs, int len) + { + return stringWidth(new String(chars, offs, len)); + } + + public int stringWidth(String s) + { + GlyphVector gv = fontDelegate.createGlyphVector(getFont(), + new FontRenderContext(IDENDITY, false, false), + new StringCharacterIterator(s)); + Rectangle2D b = gv.getVisualBounds(); + return (int) b.getWidth(); + } + } + + /** + * The indendity transform, to be used in several methods. + */ + private static final AffineTransform IDENDITY = new AffineTransform(); + + private FontDelegate fontDelegate; + + public OpenTypeFontPeer(String name, int style, int size) + { + super(name, style, size); + try + { + String fontSpec = encodeFont(name, style); + String filename = mapFontToFilename(fontSpec); + File fontfile = new File(filename); + FileInputStream in = new FileInputStream(fontfile); + FileChannel ch = in.getChannel(); + ByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, + fontfile.length()); + fontDelegate = FontFactory.createFonts(buffer)[0]; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + public OpenTypeFontPeer(String name, Map atts) + { + super(name, atts); + try + { + String fontSpec = encodeFont(name, atts); + String filename = mapFontToFilename(fontSpec); + File fontfile = new File(filename); + FileInputStream in = new FileInputStream(fontfile); + FileChannel ch = in.getChannel(); + ByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, + fontfile.length()); + fontDelegate = FontFactory.createFonts(buffer)[0]; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + public boolean canDisplay(Font font, int c) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public int canDisplayUpTo(Font font, CharacterIterator i, int start, int limit) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getSubFamilyName(Font font, Locale locale) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getPostScriptName(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public int getNumGlyphs(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public int getMissingGlyphCode(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public byte getBaselineFor(Font font, char c) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getGlyphName(Font font, int glyphIndex) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public GlyphVector createGlyphVector(Font font, FontRenderContext frc, CharacterIterator ci) + { + return fontDelegate.createGlyphVector(font, frc, ci); + } + + public GlyphVector createGlyphVector(Font font, FontRenderContext ctx, int[] glyphCodes) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public GlyphVector layoutGlyphVector(Font font, FontRenderContext frc, char[] chars, int start, int limit, int flags) + { + StringCharacterIterator i = new StringCharacterIterator(new String(chars), start, limit, 0); + return fontDelegate.createGlyphVector(font, frc, i); + } + + public FontMetrics getFontMetrics(Font font) + { + return new XFontMetrics(font); + } + + public boolean hasUniformLineMetrics(Font font) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public LineMetrics getLineMetrics(Font font, CharacterIterator ci, int begin, int limit, FontRenderContext rc) + { + return new XLineMetrics(font, ci, begin, limit, rc); + } + + public Rectangle2D getMaxCharBounds(Font font, FontRenderContext rc) + { + // FIXME: Implement this. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Encodes a font name + style + size specification into a X logical font + * description (XLFD) as described here: + * + * http://www.meretrx.com/e93/docs/xlfd.html + * + * This is implemented to look up the font description in the + * fonts.properties of this package. + * + * @param name the font name + * @param atts the text attributes + * + * @return the encoded font description + */ + public static String encodeFont(String name, Map atts) + { + String family = name; + if (family == null || family.equals("")) + family = (String) atts.get(TextAttribute.FAMILY); + if (family == null) + family = "SansSerif"; + + int style = 0; + // Detect italic attribute. + Float posture = (Float) atts.get(TextAttribute.POSTURE); + if (posture != null && !posture.equals(TextAttribute.POSTURE_REGULAR)) + style |= Font.ITALIC; + + // Detect bold attribute. + Float weight = (Float) atts.get(TextAttribute.WEIGHT); + if (weight != null && weight.compareTo(TextAttribute.WEIGHT_REGULAR) > 0) + style |= Font.BOLD; + + return encodeFont(name, style); + } + + /** + * Encodes a font name + style into a combined string. + * + * This is implemented to look up the font description in the + * fonts.properties of this package. + * + * @param name the font name + * @param style the font style + * + * @return the encoded font description + */ + static String encodeFont(String name, int style) + { + CPStringBuilder key = new CPStringBuilder(); + key.append(validName(name)); + key.append('/'); + switch (style) + { + case Font.BOLD: + key.append("b"); + break; + case Font.ITALIC: + key.append("i"); + break; + case (Font.BOLD | Font.ITALIC): + key.append("bi"); + break; + case Font.PLAIN: + default: + key.append("p"); + + } + + return key.toString(); + } + + /** + * Checks the specified font name for a valid font name. If the font name + * is not known, then this returns 'sansserif' as fallback. + * + * @param name the font name to check + * + * @return a valid font name + */ + static String validName(String name) + { + String retVal; + Set fontNames = getFontNames(); + if (fontNames.contains(name)) + { + retVal = name; + } + else + { + retVal = "SansSerif"; + } + return retVal; + } + + public static String[] getAvailableFontFamilyNames(Locale l) + { + Set fontNames = getFontNames(); + int numNames = fontNames.size(); + String[] ret = fontNames.toArray(new String[numNames]); + return ret; + } + + private static synchronized Set getFontNames() + { + if (availableFontNames == null) + { + HashSet familyNames = new HashSet(); + for (Object o : fontProperties.keySet()) + { + if (o instanceof String) + { + String key = (String) o; + int slashIndex = key.indexOf('/'); + String name = key.substring(0, slashIndex); + familyNames.add(name); + } + } + availableFontNames = familyNames; + } + return availableFontNames; + } + + /** + * Takes a font spec as returned by {@link #encodeFont(String, int)}, + * and returns the corresponding font file, or null if no such + * font mapping exists. + * + * @param fontSpec font name and style as returned by + * {@link #encodeFont(String, int)} + * + * @return filename of the corresponding font file + */ + private synchronized String mapFontToFilename(String fontSpec) + { + if (fontToFileMap == null) + { + fontToFileMap = new HashMap>(); + + // Initialize font spec to file mapping according to the + // font.properties. + for (Object o : fontProperties.keySet()) + { + if (o instanceof String) + { + String key = (String) o; + int slashIndex = key.indexOf('/'); + String name = key.substring(0, slashIndex); + String spec = key.substring(slashIndex + 1); + // Handle aliases in the 2nd pass below. + if (! spec.equals("a")) + { + Map specToFileMap = fontToFileMap.get(name); + if (specToFileMap == null) + { + specToFileMap = new HashMap(); + fontToFileMap.put(name, specToFileMap); + } + specToFileMap.put(spec, fontProperties.getProperty(key)); + } + } + } + // 2nd pass for handling aliases. + for (Object o : fontProperties.keySet()) + { + if (o instanceof String) + { + String key = (String) o; + int slashIndex = key.indexOf('/'); + String name = key.substring(0, slashIndex); + String spec = key.substring(slashIndex + 1); + // Handle aliases in the 2nd pass below. + if (spec.equals("a")) + { + String alias = fontProperties.getProperty(key); + Map specToFileMap = fontToFileMap.get(alias); + fontToFileMap.put(name, specToFileMap); + } + } + } + } + // Look up font file. + int slashIndex = fontSpec.indexOf('/'); + String name = fontSpec.substring(0, slashIndex); + String spec = fontSpec.substring(slashIndex + 1); + return fontToFileMap.get(name).get(spec); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java b/libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java new file mode 100644 index 000000000..b0420ab7a --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/AutoHinter.java @@ -0,0 +1,83 @@ +/* AutoHinter.java -- The entry point into the hinter implementation. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.opentype.Hinter; +import gnu.java.awt.font.opentype.OpenTypeFont; +import gnu.java.awt.font.opentype.truetype.Fixed; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The public interface to the automatic gridfitter. + */ +public class AutoHinter + implements Hinter +{ + Latin latinScript; + LatinMetrics metrics; + GlyphHints hints; + + HintScaler scaler = new HintScaler(); + public void init(OpenTypeFont font) + { + // TODO: Should support other scripts too. + latinScript = new Latin(); + metrics = new LatinMetrics(font); + latinScript.initMetrics(metrics, font); + scaler.face = font; + } + + public void applyHints(Zone outline) + { + if (hints == null) + hints = new GlyphHints(); + scaler.xScale = Fixed.valueOf16(outline.scaleX * 64); + scaler.yScale = Fixed.valueOf16(outline.scaleY * 64); + latinScript.scaleMetrics(metrics, scaler); + latinScript.applyHints(hints, outline, metrics); + } + + public void setFlags(int flags) + { + if (hints == null) + hints = new GlyphHints(); + hints.flags = flags; + } + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java b/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java new file mode 100644 index 000000000..87f2abcc3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/AxisHints.java @@ -0,0 +1,112 @@ +/* AxisHints.java -- Hints specific to an axis + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +class AxisHints +{ + + Segment[] segments; + int majorDir; + int numSegments; + int numEdges; + Edge[] edges; + + AxisHints() + { + segments = new Segment[4]; + edges = new Edge[4]; + } + + Segment newSegment() + { + if (numSegments >= segments.length) + { + // Grow array. + int newMax = segments.length; + newMax += (newMax >> 2) + 4; // From FreeType. + Segment[] newSegs = new Segment[newMax]; + System.arraycopy(segments, 0, newSegs, 0, numSegments); + segments = newSegs; + } + Segment seg = new Segment(); + segments[numSegments] = seg; + numSegments++; + return seg; + } + + public Edge newEdge(int pos) + { + if (numEdges >= edges.length) + { + // Grow array. + int newMax = edges.length; + newMax += (newMax >> 2) + 4; // From FreeType. + Edge[] newEdges = new Edge[newMax]; + System.arraycopy(edges, 0, newEdges, 0, numEdges); + edges = newEdges; + } + int edgeIndex = numEdges; + Edge edge = edges[edgeIndex] = new Edge(); + while (edgeIndex > 0 && edges[edgeIndex - 1].fpos > pos) + { + edges[edgeIndex] = edges[edgeIndex - 1]; + edgeIndex--; + } + edges[edgeIndex] = edge; + numEdges++; + edge.fpos = pos; + + return edge; + + } + + int getEdgeIndex(Edge edge2) + { + int idx = -1; + for (int i = 0; i < numEdges; i++) + { + if (edges[i] == edge2) + { + idx = i; + break; + } + } + return idx; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Constants.java b/libjava/classpath/gnu/java/awt/font/autofit/Constants.java new file mode 100644 index 000000000..c5b90fa54 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Constants.java @@ -0,0 +1,86 @@ +/* Constants.java -- Some constants used in the autofitter + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +/** + * Some constants used in the autofitter. + */ +interface Constants +{ + + /** + * The horizontal dimension. + */ + static final int DIMENSION_HORZ = 0; + + /** + * The vertical dimension. + */ + static final int DIMENSION_VERT = 1; + + /** + * The number of dimensions. + */ + static final int DIMENSION_MAX = 2; + + /** + * Indicates a vector with no specific direction. + */ + static final int DIR_NONE = 0; + + /** + * Right direction. + */ + static final int DIR_RIGHT = 1; + + /** + * Left direction. + */ + static final int DIR_LEFT = -1; + + /** + * Up direction. + */ + static final int DIR_UP = 2; + + /** + * Down direction. + */ + static final int DIR_DOWN = -2; +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Edge.java b/libjava/classpath/gnu/java/awt/font/autofit/Edge.java new file mode 100644 index 000000000..6420fa1cb --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Edge.java @@ -0,0 +1,82 @@ +/* Edge.java -- An edge of segments + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.lang.CPStringBuilder; + +class Edge +{ + int fpos; + Segment first; + Segment last; + int opos; + Edge link; + Edge serif; + int flags; + int dir; + Width blueEdge; + int pos; + int scale; + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Edge] id"); + s.append(hashCode()); + s.append(", fpos: "); + s.append(fpos); + s.append(", opos: "); + s.append(opos); + s.append(", pos: "); + s.append(pos); + s.append(", dir: "); + s.append(dir); + s.append(", serif: "); + s.append(serif != null ? serif.hashCode() : "null"); + s.append(", link: "); + s.append(link != null ? link.hashCode() : "null"); + s.append(", flags: " + flags); + s.append(", blue: " + blueEdge); + s.append(", first: "); + s.append(first == null ? "null" : first.hashCode()); + s.append(", last: "); + s.append(last == null ? "null" : last.hashCode()); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java b/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java new file mode 100644 index 000000000..033d63fa4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/GlyphHints.java @@ -0,0 +1,640 @@ +/* GlyphHints.java -- Data and methods for actual hinting + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.FontDelegate; +import gnu.java.awt.font.opentype.truetype.Fixed; +import gnu.java.awt.font.opentype.truetype.Point; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The data and methods used for the actual hinting process. + */ +class GlyphHints + implements Constants +{ + + int xScale; + int xDelta; + int yScale; + int yDelta; + + AxisHints[] axis; + + Point[] points; + int numPoints; + int maxPoints; + + Point[] contours; + int numContours; + int maxContours; + + ScriptMetrics metrics; + + int flags; + + GlyphHints() + { + axis = new AxisHints[Constants.DIMENSION_MAX]; + axis[Constants.DIMENSION_VERT] = new AxisHints(); + axis[Constants.DIMENSION_HORZ] = new AxisHints(); + + xScale = Fixed.ONE; + yScale = Fixed.ONE; + } + + void rescale(ScriptMetrics m) + { + metrics = m; + // TODO: Copy scalerFlags. + } + + void reload(Zone outline) + { + numPoints = 0; + numContours = 0; + axis[0].numSegments = 0; + axis[0].numEdges = 0; + axis[1].numSegments = 0; + axis[1].numEdges = 0; + + // Create/reallocate the contours array. + int newMax = outline.getNumContours(); + if (newMax > maxContours || contours == null) + { + newMax = (newMax + 3) & ~3; // Taken from afhints.c . + Point[] newContours = new Point[newMax]; + if (contours != null) + { + System.arraycopy(contours, 0, newContours, 0, maxContours); + } + contours = newContours; + maxContours = newMax; + } + + // Create/reallocate the points array. + newMax = outline.getSize() + 2; + if (newMax > maxPoints || points == null) + { + newMax = (newMax + 2 + 7) & ~7; // Taken from afhints.c . + Point[] newPoints = new Point[newMax]; + if (points != null) + { + System.arraycopy(points, 0, newPoints, 0, maxPoints); + } + points = newPoints; + maxPoints = newMax; + } + + numPoints = outline.getSize() - 4; // 4 phantom points. + numContours = outline.getNumContours(); + + // Set major direction. We don't handle Type 1 fonts yet. + axis[DIMENSION_HORZ].majorDir = DIR_UP; + axis[DIMENSION_VERT].majorDir = DIR_LEFT; + + // TODO: Freetype seems to scale and translate the glyph at that point. + // I suppose that this is not really needed. + // The scales are scaling from font units to 1/64 device pixels. + xScale = Fixed.valueOf16(outline.scaleX * 64); + yScale = Fixed.valueOf16(outline.scaleY * 64); + + // FIXME: What is that xDelta and yDelta used for? + System.arraycopy(outline.getPoints(), 0, points, 0, numPoints); + + // Setup prev and next and contours array. + // TODO: Probably cache this. + contours = new Point[numContours]; + Point currentContour = points[0]; + for (int i = 0, cIndex = 0; i < numPoints; i++) + { + // Start new contour when the last point has been a contour end. + if (outline.isContourEnd(i)) + { + // Connect the contour end point to the start point. + points[i].setNext(currentContour); + currentContour.setPrev(points[i]); + contours[cIndex] = currentContour; + cIndex++; + currentContour = i < numPoints - 1 ? points[i + 1] : null; + } + else + { + // Connect the current and the previous point. + points[i].setNext(points[i + 1]); + points[i + 1].setPrev(points[i]); + } + } + // Compute directions of in and out vectors of all points as well + // as the weak point flag. + for (int i = 0; i < numPoints; i++) + { + // Compute in and out dir. + Point p = points[i]; + Point prev = p.getPrev(); + int inX = p.getOrigX() - prev.getOrigX(); + int inY = p.getOrigY() - prev.getOrigY(); + p.setInDir(Utils.computeDirection(inX, inY)); + Point next = p.getNext(); + int outX = next.getOrigX() - p.getOrigX(); + int outY = next.getOrigY() - p.getOrigY(); + p.setOutDir(Utils.computeDirection(outX, outY)); + + if (p.isControlPoint()) + { + setWeakPoint(p); + } + else if (p.getOutDir() == p.getInDir()) + { + if (p.getOutDir() != DIR_NONE) + setWeakPoint(p); + else + { + int angleIn = Utils.atan(inY, inX); + int angleOut = Utils.atan(outY, outX); + int delta = Utils.angleDiff(angleIn, angleOut); + if (delta < 2 && delta > -2) + setWeakPoint(p); + } + } + else if (p.getInDir() == - p.getOutDir()) + { + setWeakPoint(p); + } + } + computeInflectionPoints(); + } + + private void setWeakPoint(Point p) + { + p.setFlags((byte) (p.getFlags() | Point.FLAG_WEAK_INTERPOLATION)); + } + + /** + * Computes the inflection points for a glyph. + */ + private void computeInflectionPoints() + { + // Do each contour separately. + contours : for (int c = 0; c < contours.length; c++) + { + Point point = contours[c]; + Point first = point; + Point start = point; + Point end = point; + do + { + end = end.getNext(); + if (end == first) + continue contours; + } while (end.getOrigX() == first.getOrigX() + && end.getOrigY() == first.getOrigY()); + + // Extend segment start whenever possible. + Point before = start; + int angleIn; + int angleSeg = Utils.atan(end.getOrigX() - start.getOrigX(), + end.getOrigY() - start.getOrigY()); + do + { + do + { + start = before; + before = before.getPrev(); + if (before == first) + continue contours; + } while (before.getOrigX() == start.getOrigX() + && before.getOrigY() == start.getOrigY()); + angleIn = Utils.atan(start.getOrigX() - before.getOrigX(), + start.getOrigY() - before.getOrigY()); + } while (angleIn == angleSeg); + + first = start; + int diffIn = Utils.angleDiff(angleIn, angleSeg); + // Now, process all segments in the contour. + Point after; + boolean finished = false; + int angleOut, diffOut; + do + { + // First, extend the current segment's end whenever possible. + after = end; + do + { + do + { + end = after; + after = after.getNext(); + if (after == first) + finished = true; + } while (end.getOrigX() == after.getOrigX() + && end.getOrigY() == after.getOrigY()); + angleOut = Utils.atan(after.getOrigX() - end.getOrigX(), + after.getOrigY() - end.getOrigY()); + } while (angleOut == angleSeg); + diffOut = Utils.angleDiff(angleSeg, angleOut); + if ((diffIn ^ diffOut) < 0) + { + // diffIn and diffOut have different signs, we have + // inflection points here. + do + { + start.addFlags(Point.FLAG_INFLECTION); + start = start.getNext(); + } while (start != end); + start.addFlags(Point.FLAG_INFLECTION); + } + start = end; + end = after; + angleSeg = angleOut; + diffIn = diffOut; + } while (! finished); + } + } + + boolean doHorizontal() + { + return (flags & FontDelegate.FLAG_NO_HINT_HORIZONTAL) == 0; + } + + boolean doVertical() + { + return (flags & FontDelegate.FLAG_NO_HINT_VERTICAL) == 0; + } + + void alignWeakPoints(int dim) + { + short touchFlag; + Point point; + // PASS 1 : Move segments to edge positions. + if (dim == DIMENSION_HORZ) + { + touchFlag = Point.FLAG_DONE_X; + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setU(point.getX()); + point.setV(point.getScaledX()); + } + } + else + { + touchFlag = Point.FLAG_DONE_Y; + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setU(point.getY()); + point.setV(point.getScaledY()); + } + } + point = points[0]; + for (int c = 0; c < numContours; c++) + { + point = contours[c]; + int idx = getPointIndex(point); + Point endPoint = point.getPrev(); + int endIdx = getPointIndex(endPoint); + int firstIdx = idx; + while (idx <= endIdx + && (point.getFlags() & touchFlag) == 0) + { + idx++; + point = points[idx]; + } + if (idx <= endIdx) + { + int firstTouched = idx; + int curTouched = idx; + idx++; + point = points[idx]; + while (idx <= endIdx) + { + if ((point.getFlags() & touchFlag) != 0) + { + // We found two successive touch points. We interpolate + // all contour points between them. + iupInterp(curTouched + 1, idx - 1, curTouched, idx); + curTouched = idx; + } + idx++; + point = points[idx]; + } + if (curTouched == firstTouched) + { + // This is a special case: Only one point was touched in the + // contour. We thus simply shift the whole contour. + iupShift(firstIdx, endIdx, curTouched); + } + else + { + // Now interpolate after the last touched point to the end + // of the contour. + iupInterp(curTouched + 1, endIdx, curTouched, firstTouched); + // If the first contour point isn't touched, interpolate + // from the contour start to the first touched point. + if (firstTouched > 0) + { + iupInterp(firstIdx, firstTouched - 1, curTouched, + firstTouched); + } + } + } + } + // Now store the values back. + if (dim == DIMENSION_HORZ) + { + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setX(point.getU()); + } + } + else + { + for (int p = 0; p < numPoints; p++) + { + point = points[p]; + point.setY(point.getU()); + } + } + } + + private void iupShift(int p1, int p2, int ref) + { + int delta = points[ref].getU() - points[ref].getV(); + for (int p = p1; p < ref; p++) + { + points[p].setU(points[p].getV() + delta); + } + for (int p = ref + 1; p <= p2; p++) + { + points[p].setU(points[p].getV() + delta); + } + } + + private void iupInterp(int p1, int p2, int ref1, int ref2) + { + int v1 = points[ref1].getV(); + int v2 = points[ref2].getV(); + int d1 = points[ref1].getU() - v1; + int d2 = points[ref2].getU() - v2; + if (p1 > p2) + return; + if (v1 == v2) + { + for (int p = p1; p <= p2; p++) + { + int u = points[p].getV(); + if (u <= v1) + u += d1; + else + u += d2; + points[p].setU(u); + } + } + else if (v1 < v2) + { + for (int p = p1; p <= p2; p++) + { + int u = points[p].getV(); + if (u <= v1) + u += d1; + else if (u >= v2) + u += d2; + else + { + u = points[ref1].getU() + Utils.mulDiv(u - v1, + points[ref2].getU() + - points[ref1].getU(), + v2 - v1); + } + points[p].setU(u); + } + } + else + { + for (int p = p1; p <= p2; p++) + { + int u = points[p].getV(); + if (u <= v2) + u += d2; + else if (u >= v1) + u += d1; + else + { + u = points[ref1].getU() + Utils.mulDiv(u - v1, + points[ref2].getU() + - points[ref1].getU(), + v2 - v1); + } + points[p].setU(u); + } + } + } + + void alignStrongPoints(int dim) + { + AxisHints ax = axis[dim]; + Edge[] edges = ax.edges; + int numEdges = ax.numEdges; + short touchFlag; + if (dim == DIMENSION_HORZ) + touchFlag = Point.FLAG_DONE_X; + else + touchFlag = Point.FLAG_DONE_Y; + + if (numEdges > 0) + { + for (int p = 0; p < numPoints; p++) + { + Point point = points[p]; + if ((point.getFlags() & touchFlag) != 0) + continue; + // If this point is a candidate for weak interpolation, we + // interpolate it after all strong points have been processed. + if ((point.getFlags() & Point.FLAG_WEAK_INTERPOLATION) != 0 + && (point.getFlags() & Point.FLAG_INFLECTION) == 0) + continue; + + int u, ou, fu, delta; + if (dim == DIMENSION_VERT) + { + u = point.getOrigY(); + ou = point.getScaledY(); + } + else + { + u = point.getOrigX(); + ou = point.getScaledX(); + } + fu = u; + // Is the point before the first edge? + Edge edge = edges[0]; + // Inversed vertical dimension. + delta = edge.fpos - u; + if (delta >= 0) + { + u = edge.pos - (edge.opos - ou); + storePoint(point, u, dim, touchFlag); + } + else + { + // Is the point after the last edge? + edge = edges[numEdges - 1]; + delta = u - edge.fpos; + if (delta >= 0) + { + u = edge.pos + (ou - edge.opos); + storePoint(point, u, dim, touchFlag); + } + else + { + // Find enclosing edges. + int min = 0; + int max = numEdges; + int mid, fpos; + boolean found = false; + while (min < max) + { + mid = (max + min) / 2; + edge = edges[mid]; + fpos = edge.fpos; + if (u < fpos) + max = mid; + else if (u > fpos) + min = mid + 1; + else + { + // Directly on the edge. + u = edge.pos; + storePoint(point, u, dim, touchFlag); + found = true; + break; + } + } + if (! found) + { + Edge before = edges[min - 1]; + Edge after = edges[min]; + if (before.scale == 0) + { + before.scale = Fixed.div16(after.pos - before.pos, + after.fpos - before.fpos); + } + u = before.pos + Fixed.mul16(fu - before.fpos, + before.scale); + } + storePoint(point, u, dim, touchFlag); + } + } + } + } + } + + private void storePoint(Point p, int u, int dim, short touchFlag) + { + if (dim == DIMENSION_HORZ) + p.setX(u); + else + p.setY(u); + p.addFlags(touchFlag); + } + + void alignEdgePoints(int dim) + { + AxisHints ax = axis[dim]; + Edge[] edges = ax.edges; + int numEdges = ax.numEdges; + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + Segment seg = edge.first; + do + { + Point point = seg.first; + while (true) + { + if (dim == DIMENSION_HORZ) + { + point.setX(edge.pos); + point.addFlags(Point.FLAG_DONE_X); + } + else + { + point.setY(edge.pos); + point.addFlags(Point.FLAG_DONE_Y); + } + if (point == seg.last) + break; + point = point.getNext(); + } + seg = seg.edgeNext; + } while (seg != edge.first); + } + } + + private int getPointIndex(Point p) + { + int idx = -1; + for (int i = 0; i < numPoints; i++) + { + if (p == points[i]) + { + idx = i; + break; + } + } + return idx; + } + + public boolean doAlignEdgePoints() + { + return (flags & FontDelegate.FLAG_NO_HINT_EDGE_POINTS) == 0; + } + + public boolean doAlignStrongPoints() + { + return (flags & FontDelegate.FLAG_NO_HINT_STRONG_POINTS) == 0; + } + + public boolean doAlignWeakPoints() + { + return (flags & FontDelegate.FLAG_NO_HINT_WEAK_POINTS) == 0; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java b/libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java new file mode 100644 index 000000000..01276b4db --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/HintScaler.java @@ -0,0 +1,53 @@ +/* Scaler.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; + +class HintScaler +{ + + int xScale; + int xDelta; + int yScale; + int yDelta; + OpenTypeFont face; + int renderMode; + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Latin.java b/libjava/classpath/gnu/java/awt/font/autofit/Latin.java new file mode 100644 index 000000000..c132c2cdc --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Latin.java @@ -0,0 +1,1363 @@ +/* Latin.java -- Latin specific glyph handling + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +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.Fixed; +import gnu.java.awt.font.opentype.truetype.Point; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * Implements Latin specific glyph handling. + */ +class Latin + implements Script, Constants +{ + + static final int MAX_WIDTHS = 16; + + private final static int MAX_TEST_CHARS = 12; + + /** + * The types of the 6 blue zones. + */ + private static final int CAPITAL_TOP = 0; + private static final int CAPITAL_BOTTOM = 1; + private static final int SMALL_F_TOP = 2; + private static final int SMALL_TOP = 3; + private static final int SMALL_BOTTOM = 4; + private static final int SMALL_MINOR = 5; + static final int BLUE_MAX = 6; + + /** + * The test chars for the blue zones. + * + * @see #initBlues(LatinMetrics, OpenTypeFont) + */ + private static final String[] TEST_CHARS = + new String[]{"THEZOCQS", "HEZLOCUS", "fijkdbh", + "xzroesc", "xzroesc", "pqgjy"}; + + public void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics) + { + hints.reload(outline); + hints.rescale(metrics); + if (hints.doHorizontal()) + { + detectFeatures(hints, DIMENSION_HORZ); + } + if (hints.doVertical()) + { + detectFeatures(hints, DIMENSION_VERT); + computeBlueEdges(hints, (LatinMetrics) metrics); + } + // Grid-fit the outline. + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + if (dim == DIMENSION_HORZ && hints.doHorizontal() + || dim == DIMENSION_VERT && hints.doVertical()) + { + hintEdges(hints, dim); + if (hints.doAlignEdgePoints()) + hints.alignEdgePoints(dim); + if (hints.doAlignStrongPoints()) + hints.alignStrongPoints(dim); + if (hints.doAlignWeakPoints()) + hints.alignWeakPoints(dim); + + } + } + // FreeType does a save call here. I guess that's not needed as we operate + // on the live glyph data anyway. + } + + private void hintEdges(GlyphHints hints, int dim) + { + AxisHints axis = hints.axis[dim]; + Edge[] edges = axis.edges; + int numEdges = axis.numEdges; + Edge anchor = null; + int hasSerifs = 0; + + // We begin by aligning all stems relative to the blue zone if + // needed -- that's only for horizontal edges. + if (dim == DIMENSION_VERT) + { + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0) + continue; + + Width blue = edge.blueEdge; + Edge edge1 = null; + Edge edge2 = edge.link; + if (blue != null) + { + edge1 = edge; + } + else if (edge2 != null && edge2.blueEdge != null) + { + blue = edge2.blueEdge; + edge1 = edge2; + edge2 = edge; + } + if (edge1 == null) + continue; + + edge1.pos = blue.fit; + edge1.flags |= Segment.FLAG_EDGE_DONE; + + if (edge2 != null && edge2.blueEdge == null) + { + alignLinkedEdge(hints, dim, edge1, edge2); + edge2.flags |= Segment.FLAG_EDGE_DONE; + } + if (anchor == null) + anchor = edge; + } + } + + // Now we will align all stem edges, trying to maintain the + // relative order of stems in the glyph. + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0) + continue; + Edge edge2 = edge.link; + if (edge2 == null) + { + hasSerifs++; + continue; + } + // Now align the stem. + // This should not happen, but it's better to be safe. + if (edge2.blueEdge != null || axis.getEdgeIndex(edge2) < e) + { + alignLinkedEdge(hints, dim, edge2, edge); + edge.flags |= Segment.FLAG_EDGE_DONE; + continue; + } + + if (anchor == null) + { + int orgLen = edge2.opos - edge.opos; + int curLen = computeStemWidth(hints, dim, orgLen, edge.flags, + edge2.flags); + int uOff, dOff, orgCenter, curPos1, error1, error2; + if (curLen <= 64) // < 1 Pixel. + { + uOff = 32; + dOff = 32; + } + else + { + uOff = 38; + dOff = 26; + } + if (curLen < 96) + { + orgCenter = edge.opos + (orgLen >> 1); + curPos1 = Utils.pixRound(orgCenter); + error1 = orgCenter - (curPos1 - uOff); + if (error1 < 0) + error1 = -error1; + error2 = orgCenter - (curPos1 + dOff); + if (error2 < 0) + error2 = -error2; + if (error1 < error2) + { + curPos1 -= uOff; + } + else + { + curPos1 += dOff; + } + edge.pos = curPos1 - curLen / 2; + edge2.pos = curPos1 + curLen / 2; + } + else + { + edge.pos = Utils.pixRound(edge.opos); + } + anchor = edge; + edge.flags |= Segment.FLAG_EDGE_DONE; + alignLinkedEdge(hints, dim, edge, edge2); + } + else + { + int aDiff = edge.opos - anchor.opos; + int orgPos = anchor.pos + aDiff; + int orgLen = edge2.opos - edge.opos; + int orgCenter = orgPos + (orgLen >> 1); + int curLen = computeStemWidth(hints, dim, orgLen, edge.flags, + edge2.flags); + //System.err.println("stem width: " + curLen); + if (curLen < 96) + { + int uOff, dOff; + int curPos1 = Utils.pixRound(orgCenter); + if (curLen <= 64) + { + uOff = 32; + dOff = 32; + } + else + { + uOff = 38; + dOff = 26; + } + int delta1 = orgCenter - (curPos1 - uOff); + if (delta1 < 0) + delta1 = -delta1; + int delta2 = orgCenter - (curPos1 + dOff); + if (delta2 < 0) + delta2 = -delta2; + if (delta1 < delta2) + { + curPos1 -= uOff; + } + else + { + curPos1 += dOff; + } + edge.pos = curPos1 - curLen / 2; + edge2.pos = curPos1 + curLen / 2; + } + else + { + orgPos = anchor.pos + (edge.opos - anchor.opos); + orgLen = edge2.opos - edge.opos; + orgCenter = orgPos + (orgLen >> 1); + curLen = computeStemWidth(hints, dim, orgLen, edge.flags, + edge2.flags); + int curPos1 = Utils.pixRound(orgPos); + int delta1 = curPos1 + (curLen >> 1) - orgCenter; + if (delta1 < 0) + delta1 = -delta1; + int curPos2 = Utils.pixRound(orgPos + orgLen) - curLen; + int delta2 = curPos2 + (curLen >> 1) - orgCenter; + if (delta2 < 0) + delta2 = -delta2; + edge.pos = (delta1 < delta2) ? curPos1 : curPos2; + edge2.pos = edge.pos + curLen; + } + edge.flags |= Segment.FLAG_EDGE_DONE; + edge2.flags |= Segment.FLAG_EDGE_DONE; + + if (e > 0 && edge.pos < edges[e - 1].pos) + { + edge.pos = edges[e - 1].pos; + } + } + } + // TODO: Implement the lowercase m symmetry thing. + + // Now we hint the remaining edges (serifs and singles) in order + // to complete our processing. + if (hasSerifs > 0 || anchor == null) + { + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0) + continue; + if (edge.serif != null) + { + alignSerifEdge(hints, edge.serif, edge); + } + else if (anchor == null) + { + edge.pos = Utils.pixRound(edge.opos); + anchor = edge; + } + else + { + edge.pos = anchor.pos + + Utils.pixRound(edge.opos - anchor.opos); + } + edge.flags |= Segment.FLAG_EDGE_DONE; + + if (e > 0 && edge.pos < edges[e - 1].pos) + { + edge.pos = edges[e - 1].pos; + } + if (e + 1 < numEdges + && (edges[e + 1].flags & Segment.FLAG_EDGE_DONE) != 0 + && edge.pos > edges[e + 1].pos) + { + edge.pos = edges[e + 1].pos; + } + } + } + + // Debug: print all hinted edges. + // System.err.println("hinted edges: " ); + // for (int i = 0; i < numEdges; i++) + // { + // System.err.println("edge#" + i + ": " + edges[i]); + // } + } + + private void alignSerifEdge(GlyphHints hints, Edge base, Edge serif) + { + serif.pos = base.pos + (serif.opos - base.opos); + } + + private int computeStemWidth(GlyphHints hints, int dim, int width, + int baseFlags, int stemFlags) + { + LatinMetrics metrics = (LatinMetrics) hints.metrics; + LatinAxis axis = metrics.axis[dim]; + int dist = width; + int sign = 0; + boolean vertical = dim == DIMENSION_VERT; + if (! doStemAdjust(hints)) + return width; + if (dist < 0) + { + dist = -width; + sign = 1; + } + if ((vertical && ! doVertSnap(hints)) || ! vertical && ! doHorzSnap(hints)) + { + // Smooth hinting process. Very lightly quantize the stem width. + // Leave the widths of serifs alone. + if ((stemFlags & Segment.FLAG_EDGE_SERIF) != 0 && vertical + && dist < 3 * 64) + { + return doneWidth(dist, sign); + } + else if ((baseFlags & Segment.FLAG_EDGE_ROUND) != 0) + { + if (dist < 80) + dist = 64; + } + else if (dist < 56) + { + dist = 56; + } + if (axis.widthCount > 0) + { + int delta; + if (axis.widthCount > 0) + { + delta = dist - axis.widths[0].cur; + if (delta < 0) + { + delta = -delta; + } + if (delta < 40) + { + dist = axis.widths[0].cur; + if (dist < 48) + dist = 48; + return doneWidth(dist, sign); + } + } + if (dist < 3 * 64) // < 3 pixels. + { + delta = dist & 63; + dist &= -64; + if (delta < 10) + dist += delta; + else if (delta < 32) + dist += 10; + else if (delta < 54) + dist += 54; + else + dist += delta; + + } + else + { + dist = (dist + 32) & ~63; + } + } + } + else + { + // Strong hinting process: Snap the stem width to integer pixels. + dist = snapWidth(axis.widths, axis.widthCount, dist); + if (vertical) + { + // In the case of vertical hinting, always round + // the stem heights to integer pixels. + if (dist >= 64) + dist = (dist + 16) & ~63; + else + dist = 64; + } + else + { + if (doMono(hints)) + { + // Monochrome horizontal hinting: Snap widths to integer pixels + // with a different threshold. + if (dist < 64) + dist = 64; + else + dist = (dist + 32) & ~63; + } + else + { + // For anti-aliased hinting, we adopt a more subtle + // approach: We strengthen small stems, round those 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 + 22) & ~63; + else + // Round otherwise to prevent color fringes in LCD mode. + dist = (dist + 32) & ~63; + } + } + } + return doneWidth(dist, sign); + } + + private boolean doMono(GlyphHints hints) + { + return true; + } + + private int snapWidth(Width[] widths, int count, int width) + { + int best = 64 + 32 + 2; + int reference = width; + for (int n = 0; n < count; n++) + { + int w = widths[n].cur; + int dist = width - w; + if (dist < 0) + dist = -dist; + if (dist < best) + { + best = dist; + reference = w; + } + } + int scaled = Utils.pixRound(reference); + if (width >= reference) + { + if (width < scaled + 48) + width = reference; + } + else + { + if (width > scaled + 48) + width = reference; + } + return width; + } + + private int doneWidth(int w, int s) + { + if (s == 1) + w = -w; + return w; + } + + private boolean doVertSnap(GlyphHints hints) + { + // TODO Auto-generated method stub + return true; + } + + private boolean doHorzSnap(GlyphHints hints) + { + // TODO Auto-generated method stub + return true; + } + + private boolean doStemAdjust(GlyphHints hints) + { + // TODO Auto-generated method stub + return true; + } + + private void alignLinkedEdge(GlyphHints hints, int dim, Edge base, Edge stem) + { + int dist = stem.opos - base.opos; + int fitted = computeStemWidth(hints, dim, dist, base.flags, stem.flags); + stem.pos = base.pos + fitted; + } + + public void doneMetrics(ScriptMetrics metrics) + { + // TODO Auto-generated method stub + + } + + /** + * Initializes the hints object. + * + * @param hints the hints to initialize + * @param metrics the metrics to use + */ + public void initHints(GlyphHints hints, ScriptMetrics metrics) + { + hints.rescale(metrics); + LatinMetrics lm = (LatinMetrics) metrics; + hints.xScale = lm.axis[DIMENSION_HORZ].scale; + hints.xDelta = lm.axis[DIMENSION_HORZ].delta; + hints.yScale = lm.axis[DIMENSION_VERT].scale; + hints.yDelta = lm.axis[DIMENSION_VERT].delta; + // TODO: Set the scaler and other flags. + } + + /** + * Initializes the script metrics. + * + * @param metrics the script metrics to initialize + * @param face the font + */ + public void initMetrics(ScriptMetrics metrics, OpenTypeFont face) + { + assert metrics instanceof LatinMetrics; + LatinMetrics lm = (LatinMetrics) metrics; + lm.unitsPerEm = face.unitsPerEm; + + // TODO: Check for latin charmap. + + initWidths(lm, face, 'o'); + initBlues(lm, face); + } + + public void scaleMetrics(ScriptMetrics metrics, HintScaler scaler) + { + LatinMetrics lm = (LatinMetrics) metrics; + lm.scaler.renderMode = scaler.renderMode; + lm.scaler.face = scaler.face; + scaleMetricsDim(lm, scaler, DIMENSION_HORZ); + scaleMetricsDim(lm, scaler, DIMENSION_VERT); + } + + private void scaleMetricsDim(LatinMetrics lm, HintScaler scaler, int dim) + { + int scale; + int delta; + if (dim == DIMENSION_HORZ) + { + scale = scaler.xScale; + delta = scaler.xDelta; + } + else + { + scale = scaler.yScale; + delta = scaler.yDelta; + } + LatinAxis axis = lm.axis[dim]; + if (axis.orgScale == scale && axis.orgDelta == delta) + // No change, no need to adjust. + return; + axis.orgScale = scale; + axis.orgDelta = delta; + + // Correct X and Y scale to optimize the alignment of the top small + // letters to the pixel grid. + LatinAxis axis2 = lm.axis[DIMENSION_VERT]; + LatinBlue blue = null; +// for (int nn = 0; nn < axis2.blueCount; nn++) +// { +// if ((axis2.blues[nn].flags & LatinBlue.FLAG_ADJUSTMENT) != 0) +// { +// blue = axis2.blues[nn]; +// break; +// } +// } +// if (blue != null) +// { +// int scaled = Fixed.mul16(blue.shoot.org, scaler.yScale); +// int fitted = Utils.pixRound(scaled); +// if (scaled != fitted) +// { +// if (dim == DIMENSION_HORZ) +// { +// if (fitted < scaled) +// { +// scale -= scale / 50; +// } +// } +// else +// { +// scale = Utils.mulDiv(scale, fitted, scaled); +// } +// } +// } + axis.scale = scale; + axis.delta = delta; + if (dim == DIMENSION_HORZ) + { + lm.scaler.xScale = scale; + lm.scaler.xDelta = delta; + } + else + { + lm.scaler.yScale = scale; + lm.scaler.yDelta = delta; + } + // Scale the standard widths. + for (int nn = 0; nn < axis.widthCount; nn++) + { + Width w = axis.widths[nn]; + w.cur = Fixed.mul16(w.org, scale); + w.fit = w.cur; + } + // Scale blue zones. + if (dim == DIMENSION_VERT) + { + for (int nn = 0; nn < axis.blueCount; nn++) + { + blue = axis.blues[nn]; + blue.ref.cur = Fixed.mul16(blue.ref.org, scale) + delta; + blue.ref.fit = blue.ref.cur; + blue.shoot.cur = Fixed.mul16(blue.ref.org, scale) + delta; + blue.flags &= ~LatinBlue.FLAG_BLUE_ACTIVE; + // A blue zone is only active if it is less than 3/4 pixels tall. + int dist = Fixed.mul16(blue.ref.org - blue.shoot.org, scale); + if (dist <= 48 && dist >= -48) + { + int delta1 = blue.shoot.org - blue.ref.org; + int delta2 = delta1; + if (delta1 < 0) + delta2 = -delta2; + delta2 = Fixed.mul16(delta2, scale); + if (delta2 < 32) + delta2 = 0; + else if (delta2 < 64) + delta2 = 32 + (((delta2 - 32) + 16) & ~31); + else + delta2 = Utils.pixRound(delta2); + if (delta1 < 0) + delta2 = -delta2; + blue.ref.fit = Utils.pixRound(blue.ref.cur); + blue.shoot.fit = blue.ref.fit + delta2; + blue.flags |= LatinBlue.FLAG_BLUE_ACTIVE; + } + } + } + } + + /** + * Determines the standard stem widths. + * + * @param metrics the metrics to use + * @param face the font face + * @param ch the character that is used for getting the widths + */ + private void initWidths(LatinMetrics metrics, OpenTypeFont face, char ch) + { + GlyphHints hints = new GlyphHints(); + metrics.axis[DIMENSION_HORZ].widthCount = 0; + metrics.axis[DIMENSION_VERT].widthCount = 0; + int glyphIndex = face.getGlyph(ch); + Zone outline = face.getRawGlyphOutline(glyphIndex, IDENTITY); + LatinMetrics dummy = new LatinMetrics(); + HintScaler scaler = dummy.scaler; + dummy.unitsPerEm = metrics.unitsPerEm; + scaler.xScale = scaler.yScale = 10000; + scaler.xDelta = scaler.yDelta = 0; + scaler.face = face; + hints.rescale(dummy); + hints.reload(outline); + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + LatinAxis axis = metrics.axis[dim]; + AxisHints axHints = hints.axis[dim]; + int numWidths = 0; + computeSegments(hints, dim); + linkSegments(hints, dim); + Segment[] segs = axHints.segments; + HashSet touched = new HashSet(); + for (int i = 0; i < segs.length; i++) + { + Segment seg = segs[i]; + Segment link = seg.link; + if (link != null && link.link == seg && ! touched.contains(link)) + { + int dist = Math.abs(seg.pos - link.pos); + if (numWidths < MAX_WIDTHS) + axis.widths[numWidths++] = new Width(dist); + } + touched.add(seg); + } + Utils.sort(numWidths, axis.widths); + axis.widthCount = numWidths; + } + for (int dim = 0; dim < DIMENSION_MAX; dim++) + { + LatinAxis axis = metrics.axis[dim]; + int stdw = axis.widthCount > 0 ? axis.widths[0].org + : constant(metrics, 50); + axis.edgeDistanceTreshold= stdw / 5; + } + } + + 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. + * + * @param metrics the metrics to use + * @param face the font face to analyze + */ + private void initBlues(LatinMetrics metrics, OpenTypeFont face) + { + int[] flats = new int[MAX_TEST_CHARS]; + int[] rounds = new int[MAX_TEST_CHARS]; + int numFlats; + int numRounds; + LatinBlue blue; + LatinAxis axis = metrics.axis[DIMENSION_VERT]; + // We compute the blues simply by loading each character in the test + // strings, then compute its topmost or bottommost points. + for (int bb = 0; bb < BLUE_MAX; bb++) + { + String p = TEST_CHARS[bb]; + int blueRef; + int blueShoot; + numFlats = 0; + numRounds = 0; + for (int i = 0; i < p.length(); i++) + { + // Load the character. + int glyphIndex = face.getGlyph(p.charAt(i)); + Zone glyph = + face.getRawGlyphOutline(glyphIndex, IDENTITY); + + // Now compute the min and max points. + int numPoints = glyph.getSize() - 4; // 4 phantom points. + Point[] points = glyph.getPoints(); + Point point = points[0]; + int extremum = 0; + int index = 1; + if (isTopBlue(bb)) + { + for (; index < numPoints; index++) + { + point = points[index]; + // We have the vertical direction swapped. The higher + // points have smaller (negative) Y. + if (point.getOrigY() < points[extremum].getOrigY()) + extremum = index; + } + } + else + { + for (; index < numPoints; index++) + { + point = points[index]; + // We have the vertical direction swapped. The higher + // points have smaller (negative) Y. + if (point.getOrigY() > points[extremum].getOrigY()) + extremum = index; + } + } + // Debug, prints out the maxima. + // System.err.println("extremum for " + bb + " / "+ p.charAt(i) + // + ": " + points[extremum]); + + // Now determine if the point is part of a straight or round + // segment. + boolean round; + int idx = extremum; + int first, last, prev, next, end; + int dist; + last = -1; + first = 0; + for (int n = 0; n < glyph.getNumContours(); n++) + { + end = glyph.getContourEnd(n); + // System.err.println("contour end for " + n + ": " + end); + if (end >= idx) + { + last = end; + break; + } + first = end + 1; + } + // Should never happen. + assert last >= 0; + + // Now look for the previous and next points that are not on the + // same Y coordinate. Threshold the 'closeness'. + prev = idx; + next = prev; + do + { + if (prev > first) + prev--; + else + prev = last; + dist = points[prev].getOrigY() - points[extremum].getOrigY(); + if (dist < -5 || dist > 5) + break; + } while (prev != idx); + do + { + if (next < last) + next++; + else + next = first; + dist = points[next].getOrigY() - points[extremum].getOrigY(); + if (dist < -5 || dist > 5) + break; + } while (next != idx); + round = points[prev].isControlPoint() + || points[next].isControlPoint(); + + if (round) + { + rounds[numRounds++] = points[extremum].getOrigY(); + // System.err.println("new round extremum: " + bb + ": " + // + points[extremum].getOrigY()); + } + else + { + flats[numFlats++] = points[extremum].getOrigY(); + // System.err.println("new flat extremum: " + bb + ": " + // + points[extremum].getOrigY()); + } + } + // We have computed the contents of the rounds and flats tables. + // Now determine the reference and overshoot position of the blues -- + // we simply take the median after a simple sort. + Utils.sort(numRounds, rounds); + Utils.sort(numFlats, flats); + blue = axis.blues[axis.blueCount] = new LatinBlue(); + axis.blueCount++; + if (numFlats == 0) + { + blue.ref = blue.shoot = new Width(rounds[numRounds / 2]); + } + else if (numRounds == 0) + { + blue.ref = blue.shoot = new Width(flats[numFlats / 2]); + } + else + { + blue.ref = new Width(flats[numFlats / 2]); + blue.shoot = new Width(rounds[numRounds / 2]); + } + // There are sometimes problems: if the overshoot position of top + // zones is under its reference position, or the opposite for bottom + // zones. We must check everything there and correct problems. + if (blue.shoot != blue.ref) + { + int ref = blue.ref.org; + int shoot = blue.shoot.org; + // Inversed vertical coordinates! + boolean overRef = shoot < ref; + if (isTopBlue(bb) ^ overRef) + { + blue.shoot = blue.ref = new Width((shoot + ref) / 2); + } + } + blue.flags = 0; + if (isTopBlue(bb)) + blue.flags |= LatinBlue.FLAG_TOP; + // The following flag is used later to adjust y and x scales in + // order to optimize the pixel grid alignment of the top small + // letters. + if (bb == SMALL_TOP) + { + blue.flags |= LatinBlue.FLAG_ADJUSTMENT; + } + // Debug: print out the blue zones. + // System.err.println("blue zone #" + bb + ": " + blue); + } + } + + private static final AffineTransform IDENTITY = new AffineTransform(); + + private int constant(LatinMetrics metrics, int c) + { + 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(); + } + } + + } + + private boolean isTopBlue(int b) + { + return b == CAPITAL_TOP || b == SMALL_F_TOP || b == SMALL_TOP; + } + + private void detectFeatures(GlyphHints hints, int dim) + { + computeSegments(hints, dim); + linkSegments(hints, dim); + computeEdges(hints, dim); + } + + private void computeEdges(GlyphHints hints, int dim) + { + AxisHints axis = hints.axis[dim]; + LatinAxis laxis = ((LatinMetrics) hints.metrics).axis[dim]; + Segment[] segments = axis.segments; + int numSegments = axis.numSegments; + Segment seg; + int upDir; + int scale; + int edgeDistanceThreshold; + axis.numEdges = 0; + scale = dim == DIMENSION_HORZ ? hints.xScale : hints.yScale; + upDir = dim == DIMENSION_HORZ ? DIR_UP : DIR_RIGHT; + + // 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 one and insert a new edge in the + // sorted table. Otherwise, we simply add the segment to the egde's + // list which will be processed in the second step to compute the + // edge's properties. + // + // Note that the edge table is sorted along the segment/edge + // position. + + edgeDistanceThreshold = Fixed.mul16(laxis.edgeDistanceTreshold, scale); + if (edgeDistanceThreshold > 64 / 4) + edgeDistanceThreshold = 64 / 4; + edgeDistanceThreshold = Fixed.div16(edgeDistanceThreshold, scale); + for (int i = 0; i < numSegments; i++) + { + seg = segments[i]; + Edge found = null; + for (int ee = 0; ee < axis.numEdges; ee++) + { + Edge edge = axis.edges[ee]; + int dist = seg.pos - edge.fpos; + if (dist < 0) + dist = -dist; + if (dist < edgeDistanceThreshold) + { + found = edge; + break; + } + } + if (found == null) + { + // Insert new edge in the list and sort according to + // the position. + Edge edge = axis.newEdge(seg.pos); + edge.first = seg; + edge.last = seg; + edge.fpos = seg.pos; + edge.opos = edge.pos = Fixed.mul16(seg.pos, scale); + seg.edgeNext = seg; + seg.edge = edge; + } + else + { + seg.edgeNext = found.first; + found.last.edgeNext = seg; + found.last = seg; + seg.edge = found; + } + } + // 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 edge). + // - Rounded edge, straight or both (which defaults to straight). + // - Link for edge. + + // Now, compute each edge properties. + for (int e = 0; e < axis.numEdges; e++) + { + Edge edge = axis.edges[e]; + // Does it contain round segments? + int isRound = 0; + // Does it contain straight segments? + int isStraight = 0; + // Number of upward segments. + int ups = 0; + // Number of downward segments. + int downs = 0; + + seg = edge.first; + do + { + // Check for roundness of segment. + if ((seg.flags & Segment.FLAG_EDGE_ROUND) != 0) + isRound++; + else + isStraight++; + + // Check for segment direction. + if (seg.dir == upDir) + ups += seg.maxPos - seg.minPos; + else + downs += seg.maxPos - seg.minPos; + + // Check for links. If seg.serif is set, then seg.link must + // be ignored. + boolean isSerif = seg.serif != null && seg.serif.edge != edge; + if (seg.link != null || isSerif) + { + Edge edge2 = edge.link; + Segment seg2 = seg.link; + if (isSerif) + { + seg2 = seg.serif; + edge2 = edge.serif; + } + if (edge2 != null) + { + int edgeDelta = edge.fpos - edge2.fpos; + if (edgeDelta < 0) + edgeDelta = -edgeDelta; + int segDelta = seg.pos - seg2.pos; + if (segDelta < 0) + segDelta = -segDelta; + if (segDelta < edgeDelta) + edge2 = seg2.edge; + } + else + { + edge2 = seg2.edge; + } + if (isSerif) + { + edge.serif = edge2; + edge2.flags |= Segment.FLAG_EDGE_SERIF; + } + else + { + edge.link = edge2; + } + } + seg = seg.edgeNext; + } while (seg != edge.first); + edge.flags = Segment.FLAG_EDGE_NORMAL; + if (isRound > 0 && isRound > isStraight) + edge.flags |= Segment.FLAG_EDGE_ROUND; + + // Set the edge's main direction. + edge.dir = DIR_NONE; + if (ups > downs) + edge.dir = upDir; + else if (ups < downs) + edge.dir = -upDir; + else if (ups == downs) + edge.dir = 0; + + // Gets rid of serif if link is set. This gets rid of many + // unpleasant artifacts. + if (edge.serif != null && edge.link != null) + { + edge.serif = null; + } + + // Debug: Print out all edges. + // System.err.println("edge# " + e + ": " + edge); + } + } + + private void computeBlueEdges(GlyphHints hints, LatinMetrics metrics) + { + AxisHints axis = hints.axis[DIMENSION_VERT]; + Edge[] edges = axis.edges; + int numEdges = axis.numEdges; + LatinAxis latin = metrics.axis[DIMENSION_VERT]; + int scale = latin.scale; + + // Compute which blue zones are active. I.e. have their scaled + // size < 3/4 pixels. + + // For each horizontal edge search the blue zone that is closest. + for (int e = 0; e < numEdges; e++) + { + Edge edge = edges[e]; + // System.err.println("checking edge: " + edge); + Width bestBlue = null; + int bestDist = Fixed.mul16(metrics.unitsPerEm / 40, scale); + + if (bestDist > 64 / 2) + bestDist = 64 / 2; + for (int bb = 0; bb < BLUE_MAX; bb++) + { + LatinBlue blue = latin.blues[bb]; + // System.err.println("checking blue: " + blue); + // Skip inactive blue zones, i.e. those that are too small. + if ((blue.flags & LatinBlue.FLAG_BLUE_ACTIVE) == 0) + continue; + // If it is a top zone, check for right edges. If it is a bottom + // zone, check for left edges. + boolean isTopBlue = (blue.flags & LatinBlue.FLAG_TOP) != 0; + boolean isMajorDir = edge.dir == axis.majorDir; + + // If it is a top zone, the edge must be against the major + // direction. If it is a bottom zone it must be in the major + // direction. + if (isTopBlue ^ isMajorDir) + { + int dist = edge.fpos - blue.ref.org; + if (dist < 0) + dist = -dist; + dist = Fixed.mul16(dist, scale); + if (dist < bestDist) + { + bestDist = dist; + bestBlue = blue.ref; + } + + // Now, compare it to the overshoot position if the edge is + // rounded, and if the edge is over the reference position of + // a top zone, or under the reference position of a bottom + // zone. + if ((edge.flags & Segment.FLAG_EDGE_ROUND) != 0 && dist != 0) + { + // Inversed vertical coordinates! + boolean isUnderRef = edge.fpos > blue.ref.org; + if (isTopBlue ^ isUnderRef) + { + blue = latin.blues[bb]; // Needed? + dist = edge.fpos - blue.shoot.org; + if (dist < 0) + dist = -dist; + dist = Fixed.mul16(dist, scale); + if (dist < bestDist) + { + bestDist = dist; + bestBlue = blue.shoot; + } + } + } + + } + } + if (bestBlue != null) + { + edge.blueEdge = bestBlue; + // Debug: Print out the blue edges. + // System.err.println("blue edge for: " + edge + ": " + bestBlue); + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java new file mode 100644 index 000000000..9237d0ee5 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinAxis.java @@ -0,0 +1,62 @@ +/* LatinAxis.java -- Axis specific data + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +/** + * Some axis specific data. + */ +class LatinAxis +{ + + int scale; + int delta; + + int widthCount; + Width[] widths; + int edgeDistanceTreshold; + LatinBlue[] blues; + int blueCount; + int orgDelta; + int orgScale; + LatinAxis() + { + widths = new Width[Latin.MAX_WIDTHS]; + blues = new LatinBlue[Latin.BLUE_MAX]; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java new file mode 100644 index 000000000..2cf68b75c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinBlue.java @@ -0,0 +1,61 @@ +/* LatinBlue.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.lang.CPStringBuilder; + +public class LatinBlue +{ + static final int FLAG_BLUE_ACTIVE = 1 << 0; + static final int FLAG_TOP = 1 << 1; + static final int FLAG_ADJUSTMENT = 1 << 2; + Width ref; + Width shoot; + int flags; + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[BlueZone]"); + s.append(" ref: "); + s.append(ref.org); + s.append(", shoot: "); + s.append(shoot.org); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java b/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java new file mode 100644 index 000000000..33fc63ad4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/LatinMetrics.java @@ -0,0 +1,66 @@ +/* LatinMetrics.java -- Latin specific metrics data + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; + +/** + * Latin specific metrics data. + */ +class LatinMetrics + extends ScriptMetrics +{ + + LatinAxis[] axis; + + int unitsPerEm; + + LatinMetrics() + { + super(); + axis = new LatinAxis[Constants.DIMENSION_MAX]; + axis[Constants.DIMENSION_HORZ] = new LatinAxis(); + axis[Constants.DIMENSION_VERT] = new LatinAxis(); + } + LatinMetrics(OpenTypeFont face) + { + this(); + unitsPerEm = face.unitsPerEm; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Script.java b/libjava/classpath/gnu/java/awt/font/autofit/Script.java new file mode 100644 index 000000000..c223f0a26 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Script.java @@ -0,0 +1,62 @@ +/* Script.java -- Defines script specific interface to the autofitter + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.opentype.OpenTypeFont; +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * Defines script specific methods for the auto fitter. + */ +interface Script +{ + + /** + * Initializes the metrics. + */ + void initMetrics(ScriptMetrics metrics, OpenTypeFont face); + + void scaleMetrics(ScriptMetrics metrics , HintScaler scaler); + + void doneMetrics(ScriptMetrics metrics); + + void initHints(GlyphHints hints, ScriptMetrics metrics); + + void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics); +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java b/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java new file mode 100644 index 000000000..984a06dae --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/ScriptMetrics.java @@ -0,0 +1,53 @@ +/* ScriptMetrics.java -- Script specific metrics data + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +/** + * Script specific metrics data. + */ +class ScriptMetrics +{ + + Script script; + HintScaler scaler; + ScriptMetrics() + { + scaler = new HintScaler(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Segment.java b/libjava/classpath/gnu/java/awt/font/autofit/Segment.java new file mode 100644 index 000000000..9f9da6792 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Segment.java @@ -0,0 +1,97 @@ +/* Segment.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.opentype.truetype.Point; + +import gnu.java.lang.CPStringBuilder; + +class Segment +{ + + static final int FLAG_EDGE_NORMAL = 0; + static final int FLAG_EDGE_ROUND = 1; + static final int FLAG_EDGE_SERIF = 2; + static final int FLAG_EDGE_DONE = 4; + int dir; + int flags; + Segment link; + Segment serif; + int numLinked; + int pos; + Point first; + Point last; + Point contour; + int minPos; + int maxPos; + int score; + int len; + Segment edgeNext; + Edge edge; + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Segment] id: "); + s.append(hashCode()); + s.append(", len:"); + s.append(len); + s.append(", round: "); + s.append(((flags & FLAG_EDGE_ROUND) != 0)); + s.append(", dir: "); + s.append(dir); + s.append(", pos: "); + s.append(pos); + s.append(", minPos: "); + s.append(minPos); + s.append(", maxPos: "); + s.append(maxPos); + s.append(", first: "); + s.append(first); + s.append(", last: "); + s.append(last); + s.append(", contour: "); + s.append(contour); + s.append(", link: "); + s.append(link == null ? "null" : link.hashCode()); + s.append(", serif: "); + s.append(serif == null ? "null" : serif.hashCode()); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Utils.java b/libjava/classpath/gnu/java/awt/font/autofit/Utils.java new file mode 100644 index 000000000..ca45bb2e4 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Utils.java @@ -0,0 +1,255 @@ +/* Utils.java -- A collection of utility functions for the autofitter + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.awt.font.opentype.truetype.Fixed; + +/** + * A collection of utility methods used all around the auto fitter. + */ +class Utils + implements Constants +{ + + private static final int ATAN_BITS = 8; + private static final byte[] ATAN = new byte[] + { + 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 + }; + + private static final int ANGLE_PI = 256; + private static final int ANGLE_PI2 = ANGLE_PI / 2; + private static final int ANGLE_PI4 = ANGLE_PI / 4; + private static final int ANGLE_2PI = ANGLE_PI * 2; + + /** + * Computes the direction constant for the specified vector. The vector is + * given as differential value already. + * + * @param dx the x vector + * @param dy the y vector + * + * @return the direction of that vector, or DIR_NONE, if that vector is not + * approximating against one of the major axises + */ + static int computeDirection(int dx, int dy) + { + int dir = DIR_NONE; + if (dx < 0) + { + if (dy < 0) + { + if (-dx * 12 < -dy) + dir = DIR_UP; + else if (-dy * 12 < -dx) + dir = DIR_LEFT; + } + else // dy >= 0 . + { + if (-dx * 12 < dy) + dir = DIR_DOWN; + else if (dy * 12 < -dx) + dir = DIR_LEFT; + } + } + else // dx >= 0 . + { + if (dy < 0) + { + if (dx * 12 < -dy) + dir = DIR_UP; + else if (-dy * 12 < dx) + dir = DIR_RIGHT; + } + else // dy >= 0 . + { + if (dx * 12 < dy) + dir = DIR_DOWN; + else if (dy * 12 < dx) + dir = DIR_RIGHT; + } + } + return dir; + } + + public static int atan(int dx, int dy) + { + int angle; + // Trivial cases. + if (dy == 0) + { + angle = 0; + if (dx < 0) + angle = ANGLE_PI; + return angle; + } + else if (dx == 0) + { + angle = ANGLE_PI2; + if (dy < 0) + angle = - ANGLE_PI2; + return angle; + } + + + angle = 0; + if (dx < 0) + { + dx = -dx; + dy = -dy; + angle = ANGLE_PI; + } + if (dy < 0) + { + int tmp = dx; + dx = -dy; + dy = tmp; + angle -= ANGLE_PI2; + } + if (dx == 0 && dy == 0) + return 0; + + if (dx == dy) + angle += ANGLE_PI4; + else if (dx > dy) + { + angle += ATAN[Fixed.div(dy, dx) << (ATAN_BITS - 6)]; + } + else + { + angle += ANGLE_PI2 - ATAN[Fixed.div(dx, dy) << (ATAN_BITS - 6)]; + } + + if (angle > ANGLE_PI) + angle -= ANGLE_2PI; + return angle; + } + + public static int angleDiff(int ang1, int ang2) + { + int delta = ang2 - ang1; + delta %= ANGLE_2PI; + if (delta < 0) + delta += ANGLE_2PI; + if (delta > ANGLE_PI) + delta -= ANGLE_2PI; + return delta; + } + + static void sort(int num, int[] array) + { + int swap; + for (int i = 1; i < num; i++) + { + for (int j = i; j > 0; j--) + { + if (array[j] > array[j - 1]) + break; + swap = array[j]; + array[j] = array[j - 1]; + array[j - 1] = swap; + } + } + } + + static void sort(int num, Width[] array) + { + Width swap; + for (int i = 1; i < num; i++) + { + for (int j = 1; j > 0; j--) + { + if (array[j].org > array[j - 1].org) + break; + swap = array[j]; + array[j] = array[j - 1]; + array[j - 1] = swap; + } + } + } + + static int pixRound(int val) + { + return pixFloor(val + 32); + } + + static int pixFloor(int val) + { + return val & ~63; + } + + public static int mulDiv(int a, int b, int c) + { + long prod = a * b; + long div = (prod / c); + return (int) div; + } + +} diff --git a/libjava/classpath/gnu/java/awt/font/autofit/Width.java b/libjava/classpath/gnu/java/awt/font/autofit/Width.java new file mode 100644 index 000000000..079f7b396 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/autofit/Width.java @@ -0,0 +1,64 @@ +/* Width.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.autofit; + +import gnu.java.lang.CPStringBuilder; + +public class Width +{ + int org; + int cur; + int fit; + Width(int dist) + { + org = dist; + } + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Width] org: "); + s.append(org); + s.append(", cur: "); + s.append(cur); + s.append(", fit: "); + s.append(fit); + return s.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java new file mode 100644 index 000000000..8529f7e47 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/CharGlyphMap.java @@ -0,0 +1,1027 @@ +/* CharGlyphMap.java -- Manages the 'cmap' table of TrueType fonts + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.ShortBuffer; +import java.nio.IntBuffer; + + +/** + * A mapping from Unicode codepoints to glyphs. This mapping + * does not perform any re-ordering or decomposition, so it + * is not everything that is needed to support Unicode. + * + *

This class manages the cmap table of + * OpenType and TrueType fonts. + * + * @see + * the cmap part of Adobe’ OpenType Specification + * + * @see + * the cmap section of Apple’s TrueType Reference + * Manual + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class CharGlyphMap +{ + private static final int PLATFORM_UNICODE = 0; + private static final int PLATFORM_MACINTOSH = 1; + private static final int PLATFORM_MICROSOFT = 3; + + + /** + * Determines the glyph index for a given Unicode codepoint. Users + * should be aware that the character-to-glyph mapping not not + * everything that is needed for full Unicode support. For example, + * the cmap table is not able to synthesize accented + * glyphs from the canonical decomposition sequence, even if the + * font would contain a glyph for the composed form. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. Surrogates + * (U+D800 to U+DFFF) cannot be passed, they must be mapped to + * UCS-4 first. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public abstract int getGlyph(int ucs4); + + + /** + * Reads a CharGlyphMap from an OpenType or TrueType cmap + * table. The current implementation works as follows: + * + *

  1. If the font has a type 4 cmap for the Unicode platform + * (encoding 0, 1, 2, 3 or 4), or a type 4 cmap for the Microsoft + * platform (encodings 1 or 10), that table is used to map Unicode + * codepoints to glyphs. Most recent fonts, both for Macintosh and + * Windows, should provide such a table.
  2. + * + *
  3. Otherwise, if the font has any type 0 cmap for the Macintosh + * platform, a Unicode-to-glyph mapping is synthesized from certain + * type 0 cmaps. The current implementation collects mappings from + * Roman, Icelandic, Turkish, Croatian, Romanian, Eastern European, + * Cyrillic, Greek, Hebrew, Arabic and Farsi cmaps.
  4. .
+ * + * @param buf a buffer whose position is right at the start + * of the entire cmap table, and whose limit + * is at its end. + * + * @return a concrete subclass of CharGlyphMap + * that performs the mapping. + * + * @see the cmap part of Adobe’ OpenType Specification + * + * @see the cmap section of Apple’s TrueType Reference + * Manual + */ + public static CharGlyphMap forTable(ByteBuffer buf) + { + boolean hasType0 = false; + int start4 = -1, platform4 = 0, encoding4 = 0; + int start12 = -1, platform12 = 0, encoding12 = 0; + int version; + int numTables; + int tableStart = buf.position(); + int limit = buf.limit(); + int format, platform, language, encoding, length, offset; + + version = buf.getChar(); + if (version != 0) + return null; + + numTables = buf.getChar(); + for (int i = 0; i < numTables; i++) + { + buf.limit(limit).position(tableStart + 4 + i * 8); + platform = buf.getChar(); + encoding = buf.getChar(); + offset = tableStart + buf.getInt(); + + buf.position(offset); + format = buf.getChar(); + + switch (format) + { + case 0: + hasType0 = true; + break; + + case 4: + length = buf.getChar(); + language = buf.getChar(); + if ((start4 == -1) + && Type4.isSupported(platform, language, encoding)) + { + start4 = offset; + platform4 = platform; + encoding4 = encoding; + } + break; + + case 12: + if ((start12 == -1) && Type12.isSupported(platform, encoding)) + { + start12 = offset; + platform12 = platform; + encoding12 = encoding; + } + break; + } + } + + + if (start12 >= 0) + { + try + { + buf.limit(limit).position(start12); + return new Type12(buf, platform12, encoding12); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + if (start4 >= 0) + { + try + { + buf.limit(limit).position(start4); + return Type4.readTable(buf, platform4, encoding4); + } + catch (Exception ex) + { + } + } + + if (hasType0) + { + try + { + buf.limit(limit).position(tableStart); + return new Type0(buf); + } + catch (Exception ex) + { + } + } + + return new Dummy(); + } + + + /** + * A dummy mapping that maps anything to the undefined glyph. + * Used if no other cmap is understood in a font. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Dummy + extends CharGlyphMap + { + public int getGlyph(int ucs4) + { + return 0; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 0 tables. These tables have serious limitations: Only the first + * 256 glyphs can be addressed, and the source of the mapping is not + * Unicode, but an encoding used on the Macintosh. + * + *

However, some fonts have only a Type 0 cmap. In this case, we + * process all the Type 0 tables we understand, and establish + * a reversed glyph-to-Unicode mapping. When a glyph is requested + * for a given Unicode character, we perform a linear search on the + * reversed table to find the glyph which maps to the requested + * character. While not blazingly fast, this gives a reasonable + * fallback for old fonts. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type0 + extends CharGlyphMap + { + /** + * An array whose i-th element indicates the + * Unicode code point of glyph i in the font. + */ + private char[] glyphToUCS2 = new char[256]; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Arabic encoding. + * + * @see the Unicode mapping table for the MacOS Arabic encoding + */ + private static final String UPPER_ARABIC + = "\u007e\u0000\u00c4\u00a0\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u06ba\u00ab\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u2026\u00ee\u00ef\u00f1\u00f3\u00bb\u00f4\u00f6\u00f7" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u066a" + + "\u0026\u0027\u0028\u0029\u002a\u002b\u060c\u002d\u002e\u002f" + + "\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669" + + "\u003a\u061b\u003c\u003d\u003e\u061f\u274a\u0621\u0622\u0623" + + "\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d" + + "\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637" + + "\u0638\u0639\u063a\u005b\\\u005d\u005e\u005f\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u067e\u0679\u0686" + + "\u06d5\u06a4\u06af\u0688\u0691\u007b\u007c\u007d\u0698\u06d2"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS East European Roman encoding. + * + * @see the Unicode mapping table for the MacOS Central European + * encoding + */ + private static final String UPPER_EAST_EUROPEAN_ROMAN + = "\u007e\u0000\u00c4\u0100\u0101\u00c9\u0104\u00d6\u00dc\u00e1" + + "\u0105\u010c\u00e4\u010d\u0106\u0107\u00e9\u0179\u017a\u010e" + + "\u00ed\u010f\u0112\u0113\u0116\u00f3\u0117\u00f4\u00f6\u00f5" + + "\u00fa\u011a\u011b\u00fc\u2020\u00b0\u0118\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u0119\u00a8\u2260\u0123\u012e" + + "\u012f\u012a\u2264\u2265\u012b\u0136\u2202\u2211\u0142\u013b" + + "\u013c\u013d\u013e\u0139\u013a\u0145\u0146\u0143\u00ac\u221a" + + "\u0144\u0147\u2206\u00ab\u00bb\u2026\u00a0\u0148\u0150\u00d5" + + "\u0151\u014c\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u014d\u0154\u0155\u0158\u2039\u203a\u0159\u0156\u0157\u0160" + + "\u201a\u201e\u0161\u015a\u015b\u00c1\u0164\u0165\u00cd\u017d" + + "\u017e\u016a\u00d3\u00d4\u016b\u016e\u00da\u016f\u0170\u0171" + + "\u0172\u0173\u00dd\u00fd\u0137\u017b\u0141\u017c\u0122\u02c7"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Roman encoding for the Croatian language. + * + * @see the Unicode mapping table for the MacOS Croatian encoding + */ + private static final String UPPER_CROATIAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u0160\u2122\u00b4\u00a8\u2260\u017d\u00d8" + + "\u221e\u00b1\u2264\u2265\u2206\u00b5\u2202\u2211\u220f\u0161" + + "\u222b\u00aa\u00ba\u03a9\u017e\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u0106\u00ab\u010c\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u0110\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\uf8ff\u00a9\u2044\u20ac\u2039\u203a\u00c6\u00bb\u2013\u00b7" + + "\u201a\u201e\u2030\u00c2\u0107\u00c1\u010d\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\u0111\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u03c0\u00cb\u02da\u00b8\u00ca\u00e6\u02c7"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Cyrillic encoding. + * + * @see the Unicode mapping table for the MacOS Cyrillic encoding + */ + private static final String UPPER_CYRILLIC + = "\u007e\u0000\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417" + + "\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421" + + "\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b" + + "\u042c\u042d\u042e\u042f\u2020\u00b0\u0490\u00a3\u00a7\u2022" + + "\u00b6\u0406\u00ae\u00a9\u2122\u0402\u0452\u2260\u0403\u0453" + + "\u221e\u00b1\u2264\u2265\u0456\u00b5\u0491\u0408\u0404\u0454" + + "\u0407\u0457\u0409\u0459\u040a\u045a\u0458\u0405\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u040b\u045b\u040c" + + "\u045c\u0455\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u201e" + + "\u040e\u045e\u040f\u045f\u2116\u0401\u0451\u044f\u0430\u0431" + + "\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b" + + "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445" + + "\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u20ac"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Arabic encoding with the Farsi language. + * + * @see the Unicode mapping table for the MacOS Farsi encoding + */ + private static final String UPPER_FARSI + = "\u007e\u0000\u00c4\u00a0\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u06ba\u00ab\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u2026\u00ee\u00ef\u00f1\u00f3\u00bb\u00f4\u00f6\u00f7" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u066a" + + "\u0026\u0027\u0028\u0029\u002a\u002b\u060c\u002d\u002e\u002f" + + "\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9" + + "\u003a\u061b\u003c\u003d\u003e\u061f\u274a\u0621\u0622\u0623" + + "\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d" + + "\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637" + + "\u0638\u0639\u063a\u005b\\\u005d\u005e\u005f\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u067e\u0679\u0686" + + "\u06d5\u06a4\u06af\u0688\u0691\u007b\u007c\u007d\u0698\u06d2"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Greek encoding. + * + * @see the Unicode mapping table for the MacOS Greek encoding + */ + private static final String UPPER_GREEK + = "\u007e\u0000\u00c4\u00b9\u00b2\u00c9\u00b3\u00d6\u00dc\u0385" + + "\u00e0\u00e2\u00e4\u0384\u00a8\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00a3\u2122\u00ee\u00ef\u2022\u00bd\u2030\u00f4\u00f6\u00a6" + + "\u20ac\u00f9\u00fb\u00fc\u2020\u0393\u0394\u0398\u039b\u039e" + + "\u03a0\u00df\u00ae\u00a9\u03a3\u03aa\u00a7\u2260\u00b0\u00b7" + + "\u0391\u00b1\u2264\u2265\u00a5\u0392\u0395\u0396\u0397\u0399" + + "\u039a\u039c\u03a6\u03ab\u03a8\u03a9\u03ac\u039d\u00ac\u039f" + + "\u03a1\u2248\u03a4\u00ab\u00bb\u2026\u00a0\u03a5\u03a7\u0386" + + "\u0388\u0153\u2013\u2015\u201c\u201d\u2018\u2019\u00f7\u0389" + + "\u038a\u038c\u038e\u03ad\u03ae\u03af\u03cc\u038f\u03cd\u03b1" + + "\u03b2\u03c8\u03b4\u03b5\u03c6\u03b3\u03b7\u03b9\u03be\u03ba" + + "\u03bb\u03bc\u03bd\u03bf\u03c0\u03ce\u03c1\u03c3\u03c4\u03b8" + + "\u03c9\u03c2\u03c7\u03c5\u03b6\u03ca\u03cb\u0390\u03b0\u00ad"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Hebrew encoding. + * + *

The codepoint 0x81 (HEBREW LIGATURE YIDDISH YOD YOD PATAH) + * has no composed Unicode equivalent, but is expressed as the + * sequence U+05F2 U+05B7 in Unicode. A similar situation exists + * with the codepoint 0xC0 (HEBREW LIGATURE LAMED HOLAM), which + * MacOS converts to U+F86A U+05DC U+05B9. To correctly deal + * with these sequences, we probably should synthesize a ligature + * table if a Hebrew font only provides a Type 0 CMAP. + * + * @see the Unicode mapping table for the MacOS Hebrew encoding + */ + private static final String UPPER_HEBREW + = "\u007e\u0000\u00c4\u0000\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u0020\u0021\"\u0023\u0024\u0025" + + "\u20aa\u0027\u0029\u0028\u002a\u002b\u002c\u002d\u002e\u002f" + + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039" + + "\u003a\u003b\u003c\u003d\u003e\u003f\u0000\u201e\uf89b\uf89c" + + "\uf89d\uf89e\u05bc\ufb4b\ufb35\u2026\u00a0\u05b8\u05b7\u05b5" + + "\u05b6\u05b4\u2013\u2014\u201c\u201d\u2018\u2019\ufb2a\ufb2b" + + "\u05bf\u05b0\u05b2\u05b1\u05bb\u05b9\u0000\u05b3\u05d0\u05d1" + + "\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db" + + "\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5" + + "\u05e6\u05e7\u05e8\u05e9\u05ea\u007d\u005d\u007b\u005b\u007c"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Roman encoding with the Icelandic language. + * + * @see the Unicode mapping table for the MacOS Icelandic encoding + */ + private static final String UPPER_ICELANDIC + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u00dd\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u00d0\u00f0\u00de\u00fe\u00fd\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Roman encoding for most languages. Exceptions include + * Croatian, Icelandic, Romanian, and Turkish. + * + * @see the Unicode mapping table for the MacOS Roman encoding + */ + private static final String UPPER_ROMAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u2039\u203a\ufb01\ufb02\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Roman encoding with the Romanian language. + * + * @see the Unicode mapping table for the MacOS Romanian encoding + */ + private static final String UPPER_ROMANIAN + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u0102\u0218" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u0103\u0219\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u2044\u20ac\u2039\u203a\u021a\u021b\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * A String whose charAt(i) is the Unicode character + * that corresponds to the codepoint i + 127 in the + * MacOS Roman encoding with the Turkish language. + * + * @see the Unicode mapping table for the MacOS Turkish encoding + */ + private static final String UPPER_TURKISH + = "\u007e\u0000\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1" + + "\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb" + + "\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5" + + "\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022" + + "\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8" + + "\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0" + + "\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a" + + "\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5" + + "\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca" + + "\u00ff\u0178\u011e\u011f\u0130\u0131\u015e\u015f\u2021\u00b7" + + "\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce" + + "\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\uf8a0" + + "\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7"; + + + /** + * Constructs a CharGlyphMap.Type0 from all type 0 cmaps provided + * by the font. The implementation is able to fuse multiple type + * 0 cmaps, such as the MacRoman, Turkish, Icelandic and Croatian + * encoding, into a single map from Unicode characters to glyph + * indices. + * + * @param buf a ByteBuffer whose position is right at the + * beginning of the entire cmap table of the font (not + * at some subtable). + */ + public Type0(ByteBuffer buf) + { + int numTables; + int tableStart = buf.position(); + int limit = buf.limit(); + + /* The CMAP version must be 0. */ + if (buf.getChar() != 0) + throw new IllegalStateException(); + + numTables = buf.getChar(); + for (int i = 0; i < numTables; i++) + { + buf.limit(limit).position(tableStart + 4 + i * 8); + int platform = buf.getChar(); + int encoding = buf.getChar(); + int offset = tableStart + buf.getInt(); + + buf.position(offset); + int format = buf.getChar(); + int length = buf.getChar(); + buf.limit(offset + length); + int language = buf.getChar(); + + if (format == 0) + readSingleTable(buf, platform, language, encoding); + } + } + + + /** + * Processes a CMAP Type 0 table whose platform, encoding and + * language are already known. + * + * @param buf the buffer to read the table from, positioned + * right after the language tag. + */ + private void readSingleTable(ByteBuffer buf, + int platform, int language, + int encoding) + { + String upper = getUpper129(platform, encoding, language); + if (upper == null) + return; + + /* Skip the MacOS codepoints [0 .. 31] because they do not + * correspond to any Unicode codepoint. + */ + buf.position(buf.position() + 32); + + /* Irrespective of script and language, the MacOS codepoints + * [32 .. 126] correspond to the same Unicode codepoint. + */ + for (int i = 32; i < 126; i++) + glyphToUCS2[buf.get() & 0xff] = (char) i; + + for (int i = 127; i < 256; i++) + glyphToUCS2[buf.get() & 0xff] = upper.charAt(i - 127); + + /* Glyph 0 is always the undefined character, which has + * no codepoint in Unicode. + */ + glyphToUCS2[0] = 0; + } + + + /** + * Determines the glyph index for a given Unicode codepoint. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + /* This linear search is not exactly super fast. However, + * only really ancient fonts have only a type 0 cmap, + * so it should not hurt in very many cases. If it shows + * to be a performance problem, one could do a binary search + * on a 256-entry table sorted by Unicode codepoint. The + * matching index of that table could then be used to look + * up the glyph ID at that position. + */ + for (int i = 0; i < 256; i++) + if (glyphToUCS2[i] == ucs4) + return i; + return 0; + } + + + /** + * Returns a String whose charAt(i) is the Unicode + * character that corresponds to the codepoint i + + * 127 in the encoding specified by the platform, script + * and language tag of a Type 0 CMAP. + * + * @param language the language tag in the cmap subtable. For the + * Macintosh platform, this is 0 to indicate language-neutral + * encoding, or the MacOS language code plus one. The + * Apple documentation does not mention that one needs to be + * added, but the Adobe OpenType specification does. + * + * @return a String for mapping the top 129 characters to + * UCS-2. If platform is not 1 + * (indicating Macintosh), or if the combination of + * script and language is not + * recognized, null will be returned. + */ + private static String getUpper129(int platform, int script, int language) + { + if (platform != PLATFORM_MACINTOSH) + return null; + + switch (script) + { + case 0: /* smRoman */ + if (language == /* langIcelandic+1 */ 16) + return UPPER_ICELANDIC; + else if (language == /* langTurkish+1 */ 18) + return UPPER_TURKISH; + else if (language == /* langCroatian+1 */ 19) + return UPPER_CROATIAN; + else if (language == /* langRomanian+1 */ 38) + return UPPER_ROMANIAN; + else if (language == /* language-neutral */ 0) + return UPPER_ROMAN; + else + return null; + + case 4: /* smArabic */ + if (language == /* langFarsi+1 */ 32) + return UPPER_FARSI; + else + return UPPER_ARABIC; + + case 5: /* smHebrew */ + return UPPER_HEBREW; + + case 6: /* smGreek */ + return UPPER_GREEK; + + case 7: /* smCyrillic */ + return UPPER_CYRILLIC; + + case 29: /* smSlavic == smEastEurRoman */ + return UPPER_EAST_EUROPEAN_ROMAN; + } + + return null; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 4 tables. These tables are able to map two-byte encoded text + * to glyph IDs, such as Unicode Basic Multilingual Plane which + * contains U+0000 .. U+FFFE without surrogates. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type4 + extends CharGlyphMap + { + /** + * Determines whether this implementation supports a combination + * of platform, language and encoding is supported for a type 4 + * cmap table. + * + *

Currently, we support the following combinations: + * + *

+ * + *

Most recent Macintosh fonts provide a type 4 + * cmap for Unicode. Microsoft recommends providing a + * type 4 cmap for encoding 1 of the Microsoft + * platform. The implementation of GNU Classpath supports both + * variants. + * + *

Not supported are ShiftJIS, Big5, Wansung, Johab, and other + * non-Unicode encodings. Text can easily be converted to Unicode + * using the java.nio.charset package. + */ + static boolean isSupported(int platform, int language, int encoding) + { + switch (platform) + { + case PLATFORM_UNICODE: + return (encoding >= 0) && (encoding <= 4); + + case PLATFORM_MICROSOFT: + return (encoding == /* Basic Multilingual Plane */ 1) + || (encoding == /* Full Unicode */ 10); + } + + return false; + } + + + /** + * Processes a CMAP Type 4 table whose platform, encoding and + * language are already known. We understand the Unicode platform + * with encodings 0, 1, 2, 3 and 4, and the Microsoft platform + * with encodings 1 (Unicode BMP) and 10 (UCS-4). + * + * @param buf the buffer to read the table from, positioned at + * its beginning. + * + * @return a Type4 table, or null if the combination + * of platform and encoding is not understood. + */ + static Type4 readTable(ByteBuffer buf, + int platform, int encoding) + { + int tableStart = buf.position(); + char format = buf.getChar(); + int length = buf.getChar(); + int language = buf.getChar(); + + if ((format != 4) || !isSupported(platform, language, encoding)) + throw new IllegalArgumentException(); + + buf.limit(tableStart + length); + + int segCountX2 = buf.getChar(); + int segCount = segCountX2 / 2; + int searchRange = buf.getChar(); + int entrySelector = buf.getChar(); + int rangeShift = buf.getChar(); + + CharBuffer endCode, startCode, idRangeOffset_glyphID; + ShortBuffer idDelta; + + int pos = buf.position(); + endCode = buf.asCharBuffer(); + pos += segCountX2 + /* reservedPad */ 2; + + buf.position(pos); + startCode = buf.asCharBuffer(); + pos += segCountX2; + + buf.position(pos); + idDelta = buf.asShortBuffer(); + pos += segCountX2; + + buf.position(pos); + idRangeOffset_glyphID = buf.asCharBuffer(); + + endCode.limit(segCount); + startCode.limit(segCount); + idDelta.limit(segCount); + idRangeOffset_glyphID.limit((buf.limit() - pos) / 2); + + return new Type4(segCount, + endCode, startCode, idDelta, + idRangeOffset_glyphID); + } + + + private CharBuffer lastChar; + private CharBuffer firstChar; + private ShortBuffer idDelta; + private CharBuffer rangeID; + private int numSegments; + + private Type4(int numSegments, + CharBuffer lastChar, CharBuffer firstChar, + ShortBuffer idDelta, CharBuffer rangeID) + { + this.numSegments = numSegments; + this.lastChar = lastChar; + this.firstChar = firstChar; + this.idDelta = idDelta; + this.rangeID = rangeID; + } + + + /** + * Determines the glyph index for a given Unicode codepoint. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + char c, segStart; + int segment, idRangeOffset; + + if (ucs4 > 0xffff) + return 0; + + c = (char) ucs4; + segment = find(c); + segStart = firstChar.get(segment); + if ((c < segStart) || (c > lastChar.get(segment))) + return 0; + + /* + * System.out.println("seg " + segment + * + ", range=" + (int) rangeID[segment] + * + ", delta=" + delta[segment]); + */ + + idRangeOffset = rangeID.get(segment); + if (idRangeOffset == 0) + return (int) (char) (((int) c) + idDelta.get(segment)); + int result = rangeID.get((idRangeOffset >> 1) + + (c - segStart) + segment); + if (result == 0) + return 0; + return (int) (char) (result + idDelta.get(segment)); + } + + + private int find(char c) + { + int min, max, mid; + + min = 0; + max = numSegments - 1; + mid = max >> 1; + + while (min < max) + { + // System.out.println("(" + min + "," + max + ") " + mid); + char val = lastChar.get(mid); + if (val == c) + break; + else if (val < c) + min = mid + 1; + else if (val > c) + max = mid; + mid = (min + max) >> 1; + } + + return mid; + } + } + + + /** + * A mapping from Unicode code points to glyph IDs through CMAP Type + * 12 tables. These tables are able to map four-byte encoded text + * to glyph IDs, such as Unicode UCS-4. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static final class Type12 + extends CharGlyphMap + { + int numGroups; + IntBuffer data; + + + /** + * Determines whether this implementation supports a combination + * of platform and encoding for a type 12 cmap table. + * + *

Currently, we support the following combinations: + * + *

+ */ + static boolean isSupported(int platform, int encoding) + { + switch (platform) + { + case PLATFORM_UNICODE: + return (encoding >= 0) && (encoding <= 4); + + case PLATFORM_MICROSOFT: + return (encoding == /* Basic Multilingual Plane */ 1) + || (encoding == /* Full Unicode */ 10); + } + + return false; + } + + + /** + * Constructs a cmap type 12 table whose platform and + * encoding are already known. We understand the Unicode platform + * with encodings 0, 1, 2, 3 and 4, and the Microsoft platform + * with encodings 1 (Unicode BMP) and 10 (UCS-4). + * + * @param buf the buffer to read the table from, positioned at + * its beginning. + */ + Type12(ByteBuffer buf, int platform, int encoding) + { + int tableStart = buf.position(); + int format = buf.getChar(); + if ((format != 12) || !isSupported(platform, encoding)) + throw new IllegalStateException(); + + buf.getChar(); // skip reserved field + buf.limit(tableStart + buf.getInt()); + int language = buf.getInt(); + numGroups = buf.getInt(); + data = buf.asIntBuffer(); + } + + + /** + * Determines the glyph index for a given Unicode codepoint. Users + * should be aware that the character-to-glyph mapping not not + * everything that is needed for full Unicode support. For example, + * the cmap table is not able to synthesize accented + * glyphs from the canonical decomposition sequence, even if the + * font would contain a glyph for the composed form. + * + * @param ucs4 the Unicode codepoint in UCS-4 encoding. Surrogates + * (U+D800 to U+DFFF) cannot be passed, they must be mapped to + * UCS-4 first. + * + * @return the glyph index, or 0 if the font does not contain + * a glyph for this codepoint. + */ + public int getGlyph(int ucs4) + { + int min, max, mid, startCharCode, endCharCode; + + min = 0; + max = numGroups - 1; + mid = max >> 1; + do + { + startCharCode = data.get(3 * mid); + endCharCode = data.get(3 * mid + 1); + + + /* + System.out.println("group " + mid + " (U+" + + Integer.toHexString(startCharCode) + + " .. U+" + Integer.toHexString(endCharCode) + + "): glyph " + (int) data.get(mid*3+2)); + */ + + if ((startCharCode <= ucs4) && (ucs4 <= endCharCode)) + return ucs4 + - startCharCode + + /* startGlyphID */ data.get(mid * 3 + 2); + + if (endCharCode < ucs4) + min = mid + 1; + else + max = mid; + mid = (min + max) >> 1; + } + while (min < max); + + startCharCode = data.get(3 * mid); + endCharCode = data.get(3 * mid + 1); + if ((startCharCode <= ucs4) && (ucs4 <= endCharCode)) + return ucs4 + - startCharCode + + /* startGlyphID */ data.get(mid * 3 + 2); + + return 0; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java b/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java new file mode 100644 index 000000000..72cecb542 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/GlyphNamer.java @@ -0,0 +1,1135 @@ +/* GlyphNamer.java -- Provides glyph names. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype; + +import gnu.java.lang.CPStringBuilder; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.CharBuffer; + + +/** + * Provides names for glyphs, which is useful when embedding fonts + * in PostScript or PDF documents. + * + *

If the font has a Zapf table, it is used to map + * glyph IDs back to a sequence of Unicode codepoints, which then + * makes it possible to look up or synthesize a PostScript glyph name + * according to Adobe’s conventions. This allows to extract the + * original text from the generated PDF or PostScript file, which is + * important for indexing, searching and extracting. + * + *

Otherwise, glyph names are taken from the post table. All known formats (1, 2, 2.5, 3 and + * 4) are supported. + * + *

Open Tasks: The code could be cleaner structured by + * having separate sub-classes for each variant of the POST table. + * Also, the implementation should not read in all glyph names if a + * font provides them in a POST table of type 2. It would be + * sufficient to just read in the offsets and delay the String + * fetching and conversion to the time when the glyph name is actually + * requested. + * + *

Lack of Thread Safety: The GlyphNamer class is + * intentionally not safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the GlyphNamer. + * It would thus be wasteful to acquire additional locks for the + * GlyphNamer. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphNamer +{ + /** + * The 'post' table of the font. + */ + private ByteBuffer postTable; + + + /** + * The 'Zapf' table of the font, or null if the font has no + * such table. + */ + private ByteBuffer zapfTable; + + + /** + * The offset of each glyph relative to the Zapf table, + * or null if the font does not have a Zapf table. + */ + private IntBuffer zapfOffsets; + + + /** + * The offset from the start of the Zapf table to the start + * of the extra info area. + */ + private int zapfExtraInfo; + + + /** + * The format of the post table, a Fixed 16.16 number. + */ + private int postFormat; + + + /** + * An array of glyph names. Used for table formats 1, 2, 2.5. + */ + private String[] glyphNames; + + + /** + * An array from glyph to character codes. Similar to the + * workings of a Zapf table, but maps to CID instead of + * Unicode. Used for table format 4. + */ + private CharBuffer glyphCharacterCodes; + + + /** + * The PostScript names of the 258 standard Macintosh glyphs. Note + * that some of these glyphs are not in the Adobe Standard Glyph + * List for New Fonts, namely .notdef, .null, nonmarkingreturn, + * nonbreakingspace, apple, onesuperior, twosuperior, and + * threesuperior. + */ + private static final String[] STANDARD_POSTSCRIPT_GLYPH_NAMES = + { + ".notdef", // glyph #0 + ".null", // glyph #1 + "nonmarkingreturn", // glyph #2 + "space", // glyph #3 + "exclam", // glyph #4 + "quotedbl", // glyph #5 + "numbersign", // glyph #6 + "dollar", // glyph #7 + "percent", // glyph #8 + "ampersand", // glyph #9 + "quotesingle", // glyph #10 + "parenleft", // glyph #11 + "parenright", // glyph #12 + "asterisk", // glyph #13 + "plus", // glyph #14 + "comma", // glyph #15 + "hyphen", // glyph #16 + "period", // glyph #17 + "slash", // glyph #18 + "zero", // glyph #19 + "one", // glyph #20 + "two", // glyph #21 + "three", // glyph #22 + "four", // glyph #23 + "five", // glyph #24 + "six", // glyph #25 + "seven", // glyph #26 + "eight", // glyph #27 + "nine", // glyph #28 + "colon", // glyph #29 + "semicolon", // glyph #30 + "less", // glyph #31 + "equal", // glyph #32 + "greater", // glyph #33 + "question", // glyph #34 + "at", // glyph #35 + "A", // glyph #36 + "B", // glyph #37 + "C", // glyph #38 + "D", // glyph #39 + "E", // glyph #40 + "F", // glyph #41 + "G", // glyph #42 + "H", // glyph #43 + "I", // glyph #44 + "J", // glyph #45 + "K", // glyph #46 + "L", // glyph #47 + "M", // glyph #48 + "N", // glyph #49 + "O", // glyph #50 + "P", // glyph #51 + "Q", // glyph #52 + "R", // glyph #53 + "S", // glyph #54 + "T", // glyph #55 + "U", // glyph #56 + "V", // glyph #57 + "W", // glyph #58 + "X", // glyph #59 + "Y", // glyph #60 + "Z", // glyph #61 + "bracketleft", // glyph #62 + "backslash", // glyph #63 + "bracketright", // glyph #64 + "asciicircum", // glyph #65 + "underscore", // glyph #66 + "grave", // glyph #67 + "a", // glyph #68 + "b", // glyph #69 + "c", // glyph #70 + "d", // glyph #71 + "e", // glyph #72 + "f", // glyph #73 + "g", // glyph #74 + "h", // glyph #75 + "i", // glyph #76 + "j", // glyph #77 + "k", // glyph #78 + "l", // glyph #79 + "m", // glyph #80 + "n", // glyph #81 + "o", // glyph #82 + "p", // glyph #83 + "q", // glyph #84 + "r", // glyph #85 + "s", // glyph #86 + "t", // glyph #87 + "u", // glyph #88 + "v", // glyph #89 + "w", // glyph #90 + "x", // glyph #91 + "y", // glyph #92 + "z", // glyph #93 + "braceleft", // glyph #94 + "bar", // glyph #95 + "braceright", // glyph #96 + "asciitilde", // glyph #97 + "Adieresis", // glyph #98 + "Aring", // glyph #99 + "Ccedilla", // glyph #100 + "Eacute", // glyph #101 + "Ntilde", // glyph #102 + "Odieresis", // glyph #103 + "Udieresis", // glyph #104 + "aacute", // glyph #105 + "agrave", // glyph #106 + "acircumflex", // glyph #107 + "adieresis", // glyph #108 + "atilde", // glyph #109 + "aring", // glyph #110 + "ccedilla", // glyph #111 + "eacute", // glyph #112 + "egrave", // glyph #113 + "ecircumflex", // glyph #114 + "edieresis", // glyph #115 + "iacute", // glyph #116 + "igrave", // glyph #117 + "icircumflex", // glyph #118 + "idieresis", // glyph #119 + "ntilde", // glyph #120 + "oacute", // glyph #121 + "ograve", // glyph #122 + "ocircumflex", // glyph #123 + "odieresis", // glyph #124 + "otilde", // glyph #125 + "uacute", // glyph #126 + "ugrave", // glyph #127 + "ucircumflex", // glyph #128 + "udieresis", // glyph #129 + "dagger", // glyph #130 + "degree", // glyph #131 + "cent", // glyph #132 + "sterling", // glyph #133 + "section", // glyph #134 + "bullet", // glyph #135 + "paragraph", // glyph #136 + "germandbls", // glyph #137 + "registered", // glyph #138 + "copyright", // glyph #139 + "trademark", // glyph #140 + "acute", // glyph #141 + "dieresis", // glyph #142 + "notequal", // glyph #143 + "AE", // glyph #144 + "Oslash", // glyph #145 + "infinity", // glyph #146 + "plusminus", // glyph #147 + "lessequal", // glyph #148 + "greaterequal", // glyph #149 + "yen", // glyph #150 + "mu", // glyph #151 + "partialdiff", // glyph #152 + "summation", // glyph #153 + "product", // glyph #154 + "pi", // glyph #155 + "integral", // glyph #156 + "ordfeminine", // glyph #157 + "ordmasculine", // glyph #158 + "Omega", // glyph #159 + "ae", // glyph #160 + "oslash", // glyph #161 + "questiondown", // glyph #162 + "exclamdown", // glyph #163 + "logicalnot", // glyph #164 + "radical", // glyph #165 + "florin", // glyph #166 + "approxequal", // glyph #167 + "Delta", // glyph #168 + "guillemotleft", // glyph #169 + "guillemotright", // glyph #170 + "ellipsis", // glyph #171 + "nonbreakingspace", // glyph #172 + "Agrave", // glyph #173 + "Atilde", // glyph #174 + "Otilde", // glyph #175 + "OE", // glyph #176 + "oe", // glyph #177 + "endash", // glyph #178 + "emdash", // glyph #179 + "quotedblleft", // glyph #180 + "quotedblright", // glyph #181 + "quoteleft", // glyph #182 + "quoteright", // glyph #183 + "divide", // glyph #184 + "lozenge", // glyph #185 + "ydieresis", // glyph #186 + "Ydieresis", // glyph #187 + "fraction", // glyph #188 + "currency", // glyph #189 + "guilsinglleft", // glyph #190 + "guilsinglright", // glyph #191 + "fi", // glyph #192 + "fl", // glyph #193 + "daggerdbl", // glyph #194 + "periodcentered", // glyph #195 + "quotesinglbase", // glyph #196 + "quotedblbase", // glyph #197 + "perthousand", // glyph #198 + "Acircumflex", // glyph #199 + "Ecircumflex", // glyph #200 + "Aacute", // glyph #201 + "Edieresis", // glyph #202 + "Egrave", // glyph #203 + "Iacute", // glyph #204 + "Icircumflex", // glyph #205 + "Idieresis", // glyph #206 + "Igrave", // glyph #207 + "Oacute", // glyph #208 + "Ocircumflex", // glyph #209 + "apple", // glyph #210 + "Ograve", // glyph #211 + "Uacute", // glyph #212 + "Ucircumflex", // glyph #213 + "Ugrave", // glyph #214 + "dotlessi", // glyph #215 + "circumflex", // glyph #216 + "tilde", // glyph #217 + "macron", // glyph #218 + "breve", // glyph #219 + "dotaccent", // glyph #220 + "ring", // glyph #221 + "cedilla", // glyph #222 + "hungarumlaut", // glyph #223 + "ogonek", // glyph #224 + "caron", // glyph #225 + "Lslash", // glyph #226 + "lslash", // glyph #227 + "Scaron", // glyph #228 + "scaron", // glyph #229 + "Zcaron", // glyph #230 + "zcaron", // glyph #231 + "brokenbar", // glyph #232 + "Eth", // glyph #233 + "eth", // glyph #234 + "Yacute", // glyph #235 + "yacute", // glyph #236 + "Thorn", // glyph #237 + "thorn", // glyph #238 + "minus", // glyph #239 + "multiply", // glyph #240 + "onesuperior", // glyph #241 + "twosuperior", // glyph #242 + "threesuperior", // glyph #243 + "onehalf", // glyph #244 + "onequarter", // glyph #245 + "threequarters", // glyph #246 + "franc", // glyph #247 + "Gbreve", // glyph #248 + "gbreve", // glyph #249 + "Idotaccent", // glyph #250 + "Scedilla", // glyph #251 + "scedilla", // glyph #252 + "Cacute", // glyph #253 + "cacute", // glyph #254 + "Ccaron", // glyph #255 + "ccaron", // glyph #256 + "dcroat" // glyph #257 + }; + + + private GlyphNamer(int numGlyphs, + ByteBuffer postTable, + ByteBuffer zapfTable) + { + this.postTable = postTable; + this.zapfTable = zapfTable; + + if ((zapfTable != null) && (zapfTable.getInt(0) == 0x00010000)) + { + readZapf(numGlyphs); + return; + } + + readPost(); + } + + + /** + * Sets up the information which allows to retrieve the information + * on demand. + * + * @param numGlyphs the number of glyphs in the font. This value + * comes from the maxp table. + */ + public static GlyphNamer forTables(int numGlyphs, + ByteBuffer postTable, + ByteBuffer zapfTable) + { + return new GlyphNamer(numGlyphs, postTable, zapfTable); + } + + + /** + * Retrieves or synthesizes a PostScript name for the glyph. + * Although the code is reasonably fast, it is recommended + * to cache the results in the printer driver. + * + *

If a font provides a 'Zapf' table, the reverse mapping + * from glyph to UTF-16 sequence is performed, and a glyph + * name is synthesized following the recommendations by Adobe. + * This allows to extract the original text from the generated + * PostScript or PDF, which is a requirement for indexing + * and searching. + * + *

If a font does not provide a 'Zapf' table, the glyph name + * is taken from the 'post' table. Note that some fonts have + * wrong data in their post data, in which case the resulting + * name will be garbage. Usually, this does not hurt, unless + * the user wants to extract text from the generated PostScript + * or PDF file. The GNU implementation understands all known + * formats of the post table (1, 2, 2.5, 3 and 4). + * + * @param glyph the index of the glyph whose name is to be + * retrieved. + * + * @return the glyph name, such as A, + * gcircumflex, z_uni0302, or + * u11C42. + */ + String getGlyphName(int glyph) + { + if (zapfOffsets != null) + { + zapfTable.position(zapfOffsets.get(glyph) + 8); + int numChars = zapfTable.getChar(); + char[] chars = new char[numChars]; + for (int i = 0; i < numChars; i++) + chars[i] = zapfTable.getChar(); + return getGlyphName(chars); + } + + + /* Type 1, Type 2, Type 2.5 */ + if (glyphNames != null) + return glyphNames[glyph]; + + /* Type 4: Synthesized glyph name. */ + if (glyphCharacterCodes != null) + return "a" + glyphCharacterCodes.get(glyph); + + /* Type 3: Arbitrary, but unique name for the glyph. + * + * To find out what a good naming scheme would be, we have printed + * a document containing the character U+201C in the font + * "Hiragino Kaku Gothic Pro W3" (by Dainippon Screen Mfg. Co., + * Ltd.) on Apple MacOS X 10.1.5. This font has a type 3 'post' + * table, and its 'cmap' maps U+201C to glyph #108. The generated + * PostScript file defined a character whose name was "g108". + * + * Therefore, we use 'g' as name prefix. According to the + * TrueType/OpenType specification, it should not matter what + * prefix we use. On the other hand, it does not hurt either to be + * compatible with a good printer driver. + * + * Actually, that specific font also contains a 'Zapf' table, + * which allows to generate glyph names according to Adobe's + * conventions, so that extracting text from and searching in the + * generated PostScript or PDF becomes possible. While the Apple + * PostScript printer driver does not seem to use the 'Zapf' table + * for this purpose, we do. + */ + return "g" + glyph; + } + + + /** + * Sets up some buffers which allow to quickly read information from + * the Zapf table. + * + * @see + * Apple’s documentation of the Zapf table + */ + private void readZapf(int numGlyphs) + { + zapfExtraInfo = zapfTable.getInt(4); + zapfTable.position(8); + zapfOffsets = zapfTable.asIntBuffer(); + zapfOffsets.limit(numGlyphs); + } + + + /** + * Reads in the PostScript data from a post table of a + * TrueType or OpenType font. The implementation currently + * understands the table formats 1, 2, 2.5, 3, and 4. + */ + private void readPost() + { + int numGlyphs, nameIndex, maxNameIndex; + char[] nameIndices; + String[] names; + byte[] pascalName; + + postTable.position(0); + postFormat = postTable.getInt(); + switch (postFormat) + { + case 0x00010000: + glyphNames = STANDARD_POSTSCRIPT_GLYPH_NAMES; + return; + + case 0x00020000: + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphNames = new String[numGlyphs]; + pascalName = new byte[255]; + nameIndices = new char[numGlyphs]; + maxNameIndex = 0; + for (int i = 0; i < numGlyphs; i++) + maxNameIndex = Math.max(maxNameIndex, + nameIndices[i] = postTable.getChar()); + + names = new String[Math.max(maxNameIndex - 258 + 1, 0)]; + for (int i = 258; i <= maxNameIndex; i++) + { + int nameLen = (postTable.get() & 0xff); + postTable.get(pascalName, 0, nameLen); + names[i - 258] = new String(pascalName, 0, nameLen); + } + for (int i = 0; i < numGlyphs; i++) + { + nameIndex = nameIndices[i]; + if (nameIndex < 258) + glyphNames[i] = STANDARD_POSTSCRIPT_GLYPH_NAMES[nameIndex]; + else + glyphNames[i] = names[nameIndex - 258]; + } + return; + + case 0x00025000: // in case some font has a wrong representation of 2.5 + case 0x00028000: + /* Format 2.5 is a re-ordering of the standard names. It has + * been deprecated in February 2000, but might still occasionally + * float around. Since it can be supported with so little code, + * we do so. + */ + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphNames = new String[numGlyphs]; + for (int i = 0; i < numGlyphs; i++) + glyphNames[i] = STANDARD_POSTSCRIPT_GLYPH_NAMES[i + postTable.get()]; + return; + + case 0x00030000: + /* Format 3 leaves it to the printer driver to choose whatever + * name it wants to. + */ + return; + + case 0x00040000: + /* Format 4 is used by Apple for composite fonts that have + * synthetic glyph names. The name of a glyph is "a" plus + * the integer (in decimal notation) that follows the table + * after numGlyphs. + */ + postTable.position(32); + numGlyphs = postTable.getChar(); + glyphCharacterCodes = postTable.asCharBuffer(); + glyphCharacterCodes.limit(numGlyphs); + return; + } + } + + + + /* For generating the following tables, a quick-and-dirty Python + * script was used. It is unlikely that we ever need to run it + * again, but for information and convenient access, it is included + * below. Initial '#' characters need to be removed from the generated + * strings, they are present so that the lines not break in the middle + * of Java escape sequences (no, this is not very clean). + * + * import string + * + * javaEscapes = {0x22:'\\"', 0x5c:'\\\\'} + * def escape(c): + * if javaEscapes.has_key(c): + * return javaEscapes[c] + * elif 0x20 <= c <= 0x7e: + * return chr(c) + * else: + * return '\\u%04x' % c + * + * def dump(name, s, stride): + * s = ('#' * stride) + s + * print " private static final String %s" % name + * for i in range(0, len(s), 60): + * print ' + "%s"' % s[i:i+60] + * + * glyphs = {} + * for line in open('aglfn13.txt', 'r').readlines(): + * if line[0] == '#': continue + * [ucs, glyphName, desc] = line.split(';') + * glyph = int('0x' + ucs, 0) + * assert (not glyphs.has_key(glyph)) or (glyphs[glyph] == glyphName) + * glyphs[glyph] = glyphName + * del glyphs[0] # arrowvertex + * k = glyphs.keys() + * k.sort() + * numGlyphs = len(k) + * names = '' + * pos = [] + * for glyph in k: + * pos.append(len(names) + 1) + * names = names + '/' + glyphs[glyph] + * dump('AGLFN_GLYPHS', string.join(map(escape, k), ''), 5) + * dump('AGLFN_NAME_OFFSET', string.join(map(escape, pos), ''), 4) + * dump('AGLFN_NAMES', names + '/', 0) + */ + + + /** + * A String that contains the Unicode codepoint for each glyph + * in the Adobe Glyph List. The characters are in sorted order. + * + * Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see Adobe + * Glyph List for New Fonts + */ + private static final String AGLFN_GLYPHS + = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU" + + "VWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u00a1\u00a2\u00a3" + + "\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ae" + + "\u00af\u00b0\u00b1\u00b4\u00b5\u00b6\u00b7\u00b8\u00ba\u00bb" + + "\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5" + + "\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf" + + "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9" + + "\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3" + + "\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed" + + "\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7" + + "\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff\u0100\u0101" + + "\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b" + + "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115" + + "\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u011e\u011f" + + "\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129" + + "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0132\u0133" + + "\u0134\u0135\u0136\u0137\u0138\u0139\u013a\u013b\u013c\u013d" + + "\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147" + + "\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f\u0150\u0151" + + "\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b" + + "\u015c\u015d\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165" + + "\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d\u016e\u016f" + + "\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179" + + "\u017a\u017b\u017c\u017d\u017e\u017f\u0192\u01a0\u01a1\u01af" + + "\u01b0\u01e6\u01e7\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff\u0218" + + "\u0219\u02bc\u02bd\u02c6\u02c7\u02d8\u02d9\u02da\u02db\u02dc" + + "\u02dd\u0300\u0301\u0303\u0309\u0323\u0384\u0385\u0386\u0387" + + "\u0388\u0389\u038a\u038c\u038e\u038f\u0390\u0391\u0392\u0393" + + "\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e" + + "\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03aa" + + "\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4" + + "\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bd\u03be\u03bf" + + "\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9" + + "\u03ca\u03cb\u03cc\u03cd\u03ce\u03d1\u03d2\u03d5\u03d6\u0401" + + "\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b" + + "\u040c\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416" + + "\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420" + + "\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a" + + "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434" + + "\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e" + + "\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448" + + "\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0451\u0452\u0453" + + "\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045e" + + "\u045f\u0462\u0463\u0472\u0473\u0474\u0475\u0490\u0491\u04d9" + + "\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9" + + "\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05d0" + + "\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da" + + "\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4" + + "\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u060c" + + "\u061b\u061f\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628" + + "\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632" + + "\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640\u0641" + + "\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b" + + "\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0660\u0661\u0662" + + "\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u066a\u066d\u0679" + + "\u067e\u0686\u0688\u0691\u0698\u06a4\u06af\u06ba\u06d2\u06d5" + + "\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u200c\u200d" + + "\u200e\u200f\u2012\u2013\u2014\u2015\u2017\u2018\u2019\u201a" + + "\u201b\u201c\u201d\u201e\u2020\u2021\u2022\u2024\u2025\u2026" + + "\u202c\u202d\u202e\u2030\u2032\u2033\u2039\u203a\u203c\u2044" + + "\u20a1\u20a3\u20a4\u20a7\u20aa\u20ab\u20ac\u2105\u2111\u2113" + + "\u2116\u2118\u211c\u211e\u2122\u2126\u212e\u2135\u2153\u2154" + + "\u215b\u215c\u215d\u215e\u2190\u2191\u2192\u2193\u2194\u2195" + + "\u21a8\u21b5\u21d0\u21d1\u21d2\u21d3\u21d4\u2200\u2202\u2203" + + "\u2205\u2206\u2207\u2208\u2209\u220b\u220f\u2211\u2212\u2217" + + "\u221a\u221d\u221e\u221f\u2220\u2227\u2228\u2229\u222a\u222b" + + "\u2234\u223c\u2245\u2248\u2260\u2261\u2264\u2265\u2282\u2283" + + "\u2284\u2286\u2287\u2295\u2297\u22a5\u22c5\u2302\u2310\u2320" + + "\u2321\u2329\u232a\u2500\u2502\u250c\u2510\u2514\u2518\u251c" + + "\u2524\u252c\u2534\u253c\u2550\u2551\u2552\u2553\u2554\u2555" + + "\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f" + + "\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569" + + "\u256a\u256b\u256c\u2580\u2584\u2588\u258c\u2590\u2591\u2592" + + "\u2593\u25a0\u25a1\u25aa\u25ab\u25ac\u25b2\u25ba\u25bc\u25c4" + + "\u25ca\u25cb\u25cf\u25d8\u25d9\u25e6\u263a\u263b\u263c\u2640" + + "\u2642\u2660\u2663\u2665\u2666\u266a\u266b"; + + + /** + * The offset of each glyph name in AGLFN_NAMES. + * + * Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see Adobe + * Glyph List for New Fonts + */ + private static final String AGLFN_NAME_OFFSET + = "\u0001\u0007\u000e\u0017\")1;GQ\\ejpw~\u0084\u0089\u008d" + + "\u0091\u0097\u009c\u00a1\u00a5\u00ab\u00b1\u00b6\u00bc\u00c6" + + "\u00cb\u00d1\u00d9\u00e2\u00e5\u00e7\u00e9\u00eb\u00ed\u00ef" + + "\u00f1\u00f3\u00f5\u00f7\u00f9\u00fb\u00fd\u00ff\u0101\u0103" + + "\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117" + + "\u0119\u0125\u012f\u013c\u0148\u0153\u0159\u015b\u015d\u015f" + + "\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173" + + "\u0175\u0177\u0179\u017b\u017d\u017f\u0181\u0183\u0185\u0187" + + "\u0189\u018b\u018d\u0197\u019b\u01a6\u01b1\u01bc\u01c1\u01ca" + + "\u01d3\u01d7\u01e1\u01e9\u01f2\u01fc\u0208\u0216\u0221\u022c" + + "\u0233\u023a\u0244\u024a\u024d\u0257\u0266\u026e\u027b\u028a" + + "\u0295\u029d\u02ab\u02b8\u02bf\u02c6\u02d2\u02d9\u02e3\u02e9" + + "\u02ec\u02f5\u02fc\u0303\u030f\u0319\u0320\u0327\u0333\u033d" + + "\u0341\u0348\u034f\u0356\u0362\u0369\u0373\u037c\u0383\u038a" + + "\u0391\u039d\u03a7\u03ae\u03b4\u03bf\u03c6\u03cd\u03d9\u03e0" + + "\u03ea\u03f0\u03f3\u03fc\u0403\u040a\u0416\u0420\u0427\u042e" + + "\u043a\u0444\u0448\u044f\u0456\u045d\u0469\u0470\u047a\u0481" + + "\u0488\u048f\u0496\u04a2\u04ac\u04b3\u04b9\u04c3\u04cb\u04d3" + + "\u04da\u04e1\u04e9\u04f1\u04f8\u04ff\u050b\u0517\u0522\u052d" + + "\u0534\u053b\u0542\u0549\u0550\u0557\u055f\u0567\u056e\u0575" + + "\u0580\u058b\u0593\u059b\u05a2\u05a9\u05b5\u05c1\u05c8\u05cf" + + "\u05da\u05e5\u05f2\u05ff\u060b\u0617\u061c\u0621\u0628\u062f" + + "\u0637\u063f\u0646\u064d\u0655\u065d\u0668\u0671\u0674\u0677" + + "\u0683\u068f\u069c\u06a9\u06b6\u06bd\u06c4\u06d1\u06de\u06e5" + + "\u06ec\u06f1\u06f6\u06fd\u0704\u070b\u0712\u071f\u072c\u0733" + + "\u073a\u0746\u074a\u074e\u0756\u075e\u0765\u076c\u077a\u0788" + + "\u078b\u078e\u0795\u079c\u07a9\u07b6\u07bd\u07c4\u07cb\u07d2" + + "\u07de\u07ea\u07f3\u07fc\u0803\u080a\u0817\u0824\u082b\u0832" + + "\u0837\u083c\u0843\u084a\u0852\u085a\u0861\u0868\u086e\u0874" + + "\u0882\u0890\u0898\u08a0\u08ac\u08b8\u08c4\u08d0\u08da\u08e1" + + "\u08e8\u08f3\u08fe\u0905\u090c\u0912\u0919\u091f\u0925\u092b" + + "\u0931\u0938\u093f\u094a\u0955\u095d\u0965\u0971\u097d\u098a" + + "\u0997\u09a1\u09ab\u09b6\u09bc\u09c2\u09cc\u09d1\u09d8\u09de" + + "\u09eb\u09f5\u09ff\u0a09\u0a17\u0a24\u0a2a\u0a38\u0a43\u0a4d" + + "\u0a5a\u0a63\u0a6d\u0a7a\u0a87\u0a92\u0aa4\u0aaa\u0aaf\u0ab5" + + "\u0abd\u0ac2\u0ac6\u0acc\u0ad1\u0ad7\u0ade\u0ae1\u0ae4\u0ae7" + + "\u0aef\u0af2\u0af6\u0afc\u0b00\u0b08\u0b0c\u0b10\u0b14\u0b21" + + "\u0b31\u0b3c\u0b49\u0b52\u0b5c\u0b71\u0b77\u0b7c\u0b82\u0b88" + + "\u0b90\u0b95\u0b99\u0b9f\u0ba4\u0baa\u0bb1\u0bb4\u0bb7\u0bbf" + + "\u0bc2\u0bc6\u0bcd\u0bd3\u0bd7\u0bdf\u0be3\u0be7\u0beb\u0bf1" + + "\u0bfe\u0c0e\u0c1b\u0c28\u0c33\u0c3a\u0c43\u0c48\u0c4f\u0c59" + + "\u0c63\u0c6d\u0c77\u0c81\u0c8b\u0c95\u0c9f\u0ca9\u0cb3\u0cbd" + + "\u0cc7\u0cd1\u0cdb\u0ce5\u0cef\u0cf9\u0d03\u0d0d\u0d17\u0d21" + + "\u0d2b\u0d35\u0d3f\u0d49\u0d53\u0d5d\u0d67\u0d71\u0d7b\u0d85" + + "\u0d8f\u0d99\u0da3\u0dad\u0db7\u0dc1\u0dcb\u0dd5\u0ddf\u0de9" + + "\u0df3\u0dfd\u0e07\u0e11\u0e1b\u0e25\u0e2f\u0e39\u0e43\u0e4d" + + "\u0e57\u0e61\u0e6b\u0e75\u0e7f\u0e89\u0e93\u0e9d\u0ea7\u0eb1" + + "\u0ebb\u0ec5\u0ecf\u0ed9\u0ee3\u0eed\u0ef7\u0f01\u0f0b\u0f15" + + "\u0f1f\u0f29\u0f33\u0f3d\u0f47\u0f51\u0f5b\u0f65\u0f6f\u0f79" + + "\u0f83\u0f8d\u0f97\u0fa1\u0fab\u0fb5\u0fbf\u0fc9\u0fd3\u0fdd" + + "\u0fe7\u0ff1\u0ffb\u1005\u100f\u1019\u1023\u102d\u1037\u1041" + + "\u104b\u1055\u105f\u1069\u1073\u107d\u1087\u1091\u109b\u10a5" + + "\u10af\u10b9\u10c3\u10cd\u10d7\u10e1\u10eb\u10f5\u10ff\u1109" + + "\u1113\u111d\u1127\u1131\u113b\u1145\u114f\u1159\u1163\u116d" + + "\u1177\u1181\u118b\u1195\u119f\u11a9\u11b3\u11bd\u11c7\u11d1" + + "\u11db\u11e5\u11ef\u11f9\u1203\u120d\u1217\u1221\u122b\u1235" + + "\u123f\u1249\u1253\u125d\u1267\u1271\u127b\u1285\u128f\u1299" + + "\u12a3\u12ad\u12b7\u12c1\u12cb\u12d5\u12df\u12e9\u12f3\u12fd" + + "\u1307\u1311\u131b\u1325\u132f\u1339\u1343\u134d\u1357\u1361" + + "\u136b\u1375\u137f\u1389\u1393\u139d\u13a7\u13b1\u13bb\u13c5" + + "\u13cf\u13d9\u13e3\u13ed\u13f7\u1401\u140b\u1415\u141f\u1429" + + "\u1433\u143d\u1447\u1451\u145b\u1465\u146f\u1479\u1483\u148d" + + "\u1497\u14a1\u14ab\u14b5\u14bf\u14c9\u14d3\u14dd\u14e7\u14f1" + + "\u14f8\u14ff\u1506\u150d\u1517\u1521\u1528\u152f\u1539\u1541" + + "\u1549\u1551\u155c\u1563\u156a\u1574\u1582\u158c\u1597\u15a6" + + "\u15b4\u15c1\u15cf\u15dc\u15e3\u15ed\u15f4\u1603\u1612\u161b" + + "\u1625\u162f\u1639\u1645\u164c\u1653\u1661\u1670\u167a\u1683" + + "\u1691\u1697\u169c\u16a3\u16ad\u16b2\u16b7\u16c1\u16ca\u16d4" + + "\u16de\u16ea\u16f3\u1700\u170a\u1710\u171a\u1720\u1729\u1733" + + "\u173d\u174a\u1756\u1763\u176d\u1775\u1780\u178a\u1794\u179e" + + "\u17ab\u17ba\u17c7\u17d2\u17e0\u17ed\u17fa\u1804\u1810\u181c" + + "\u1825\u182b\u1834\u183c\u1847\u1850\u1858\u1862\u1868\u1875" + + "\u187d\u188a\u1893\u189e\u18a4\u18af\u18b9\u18c6\u18cc\u18d5" + + "\u18df\u18e7\u18f1\u18fd\u1906\u1912\u191c\u1929\u1936\u1945" + + "\u194f\u195c\u196b\u1976\u1985\u1993\u199b\u19a1\u19af\u19ba" + + "\u19c5\u19cf\u19da\u19e3\u19ec\u19f5\u19fe\u1a07\u1a10\u1a19" + + "\u1a22\u1a2b\u1a34\u1a3d\u1a46\u1a4f\u1a58\u1a61\u1a6a\u1a73" + + "\u1a7c\u1a85\u1a8e\u1a97\u1aa0\u1aa9\u1ab2\u1abb\u1ac4\u1acd" + + "\u1ad6\u1adf\u1ae8\u1af1\u1afa\u1b03\u1b0c\u1b15\u1b1e\u1b27" + + "\u1b30\u1b39\u1b42\u1b4a\u1b52\u1b58\u1b60\u1b68\u1b70\u1b76" + + "\u1b7e\u1b88\u1b8f\u1b96\u1b9d\u1ba8\u1bb0\u1bb8\u1bc0\u1bc8" + + "\u1bd0\u1bd7\u1bde\u1be8\u1bf2\u1bfd\u1c07\u1c14\u1c18\u1c1f" + + "\u1c24\u1c2a\u1c2f\u1c35\u1c3d\u1c49"; + + + /** + * The name of each glyph in the Adobe Glyph List for New Fonts + * (AGLFN). The name of the n-th glyph starts at position + * AGLFN_NAME_OFFSET.charAt(n). It ends before the following + * slash (slashes cannot be part of a PostScript name, which + * is why we use it for separation). + * + *

Generated from the Adobe Glyph List for New Fonts, version 1.1 + * of 17 April 2003. + * + * @see Adobe + * Glyph List for New Fonts + */ + private static final String AGLFN_NAMES + = "/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/q" + + "uotesingle/parenleft/parenright/asterisk/plus/comma/hyphen/p" + + "eriod/slash/zero/one/two/three/four/five/six/seven/eight/nin" + + "e/colon/semicolon/less/equal/greater/question/at/A/B/C/D/E/F" + + "/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backsla" + + "sh/bracketright/asciicircum/underscore/grave/a/b/c/d/e/f/g/h" + + "/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/bracerigh" + + "t/asciitilde/exclamdown/cent/sterling/currency/yen/brokenbar" + + "/section/dieresis/copyright/ordfeminine/guillemotleft/logica" + + "lnot/registered/macron/degree/plusminus/acute/mu/paragraph/p" + + "eriodcentered/cedilla/ordmasculine/guillemotright/onequarter" + + "/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumfle" + + "x/Atilde/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumfl" + + "ex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis/Eth/Ntilde/" + + "Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply/Oslash/U" + + "grave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls/a" + + "grave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/" + + "egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumfle" + + "x/idieresis/eth/ntilde/ograve/oacute/ocircumflex/otilde/odie" + + "resis/divide/oslash/ugrave/uacute/ucircumflex/udieresis/yacu" + + "te/thorn/ydieresis/Amacron/amacron/Abreve/abreve/Aogonek/aog" + + "onek/Cacute/cacute/Ccircumflex/ccircumflex/Cdotaccent/cdotac" + + "cent/Ccaron/ccaron/Dcaron/dcaron/Dcroat/dcroat/Emacron/emacr" + + "on/Ebreve/ebreve/Edotaccent/edotaccent/Eogonek/eogonek/Ecaro" + + "n/ecaron/Gcircumflex/gcircumflex/Gbreve/gbreve/Gdotaccent/gd" + + "otaccent/Gcommaaccent/gcommaaccent/Hcircumflex/hcircumflex/H" + + "bar/hbar/Itilde/itilde/Imacron/imacron/Ibreve/ibreve/Iogonek" + + "/iogonek/Idotaccent/dotlessi/IJ/ij/Jcircumflex/jcircumflex/K" + + "commaaccent/kcommaaccent/kgreenlandic/Lacute/lacute/Lcommaac" + + "cent/lcommaaccent/Lcaron/lcaron/Ldot/ldot/Lslash/lslash/Nacu" + + "te/nacute/Ncommaaccent/ncommaaccent/Ncaron/ncaron/napostroph" + + "e/Eng/eng/Omacron/omacron/Obreve/obreve/Ohungarumlaut/ohunga" + + "rumlaut/OE/oe/Racute/racute/Rcommaaccent/rcommaaccent/Rcaron" + + "/rcaron/Sacute/sacute/Scircumflex/scircumflex/Scedilla/scedi" + + "lla/Scaron/scaron/Tcommaaccent/tcommaaccent/Tcaron/tcaron/Tb" + + "ar/tbar/Utilde/utilde/Umacron/umacron/Ubreve/ubreve/Uring/ur" + + "ing/Uhungarumlaut/uhungarumlaut/Uogonek/uogonek/Wcircumflex/" + + "wcircumflex/Ycircumflex/ycircumflex/Ydieresis/Zacute/zacute/" + + "Zdotaccent/zdotaccent/Zcaron/zcaron/longs/florin/Ohorn/ohorn" + + "/Uhorn/uhorn/Gcaron/gcaron/Aringacute/aringacute/AEacute/aea" + + "cute/Oslashacute/oslashacute/Scommaaccent/scommaaccent/afii5" + + "7929/afii64937/circumflex/caron/breve/dotaccent/ring/ogonek/" + + "tilde/hungarumlaut/gravecomb/acutecomb/tildecomb/hookaboveco" + + "mb/dotbelowcomb/tonos/dieresistonos/Alphatonos/anoteleia/Eps" + + "ilontonos/Etatonos/Iotatonos/Omicrontonos/Upsilontonos/Omega" + + "tonos/iotadieresistonos/Alpha/Beta/Gamma/Epsilon/Zeta/Eta/Th" + + "eta/Iota/Kappa/Lambda/Mu/Nu/Xi/Omicron/Pi/Rho/Sigma/Tau/Upsi" + + "lon/Phi/Chi/Psi/Iotadieresis/Upsilondieresis/alphatonos/epsi" + + "lontonos/etatonos/iotatonos/upsilondieresistonos/alpha/beta/" + + "gamma/delta/epsilon/zeta/eta/theta/iota/kappa/lambda/nu/xi/o" + + "micron/pi/rho/sigma1/sigma/tau/upsilon/phi/chi/psi/omega/iot" + + "adieresis/upsilondieresis/omicrontonos/upsilontonos/omegaton" + + "os/theta1/Upsilon1/phi1/omega1/afii10023/afii10051/afii10052" + + "/afii10053/afii10054/afii10055/afii10056/afii10057/afii10058" + + "/afii10059/afii10060/afii10061/afii10062/afii10145/afii10017" + + "/afii10018/afii10019/afii10020/afii10021/afii10022/afii10024" + + "/afii10025/afii10026/afii10027/afii10028/afii10029/afii10030" + + "/afii10031/afii10032/afii10033/afii10034/afii10035/afii10036" + + "/afii10037/afii10038/afii10039/afii10040/afii10041/afii10042" + + "/afii10043/afii10044/afii10045/afii10046/afii10047/afii10048" + + "/afii10049/afii10065/afii10066/afii10067/afii10068/afii10069" + + "/afii10070/afii10072/afii10073/afii10074/afii10075/afii10076" + + "/afii10077/afii10078/afii10079/afii10080/afii10081/afii10082" + + "/afii10083/afii10084/afii10085/afii10086/afii10087/afii10088" + + "/afii10089/afii10090/afii10091/afii10092/afii10093/afii10094" + + "/afii10095/afii10096/afii10097/afii10071/afii10099/afii10100" + + "/afii10101/afii10102/afii10103/afii10104/afii10105/afii10106" + + "/afii10107/afii10108/afii10109/afii10110/afii10193/afii10146" + + "/afii10194/afii10147/afii10195/afii10148/afii10196/afii10050" + + "/afii10098/afii10846/afii57799/afii57801/afii57800/afii57802" + + "/afii57793/afii57794/afii57795/afii57798/afii57797/afii57806" + + "/afii57796/afii57807/afii57839/afii57645/afii57841/afii57842" + + "/afii57804/afii57803/afii57658/afii57664/afii57665/afii57666" + + "/afii57667/afii57668/afii57669/afii57670/afii57671/afii57672" + + "/afii57673/afii57674/afii57675/afii57676/afii57677/afii57678" + + "/afii57679/afii57680/afii57681/afii57682/afii57683/afii57684" + + "/afii57685/afii57686/afii57687/afii57688/afii57689/afii57690" + + "/afii57716/afii57717/afii57718/afii57388/afii57403/afii57407" + + "/afii57409/afii57410/afii57411/afii57412/afii57413/afii57414" + + "/afii57415/afii57416/afii57417/afii57418/afii57419/afii57420" + + "/afii57421/afii57422/afii57423/afii57424/afii57425/afii57426" + + "/afii57427/afii57428/afii57429/afii57430/afii57431/afii57432" + + "/afii57433/afii57434/afii57440/afii57441/afii57442/afii57443" + + "/afii57444/afii57445/afii57446/afii57470/afii57448/afii57449" + + "/afii57450/afii57451/afii57452/afii57453/afii57454/afii57455" + + "/afii57456/afii57457/afii57458/afii57392/afii57393/afii57394" + + "/afii57395/afii57396/afii57397/afii57398/afii57399/afii57400" + + "/afii57401/afii57381/afii63167/afii57511/afii57506/afii57507" + + "/afii57512/afii57513/afii57508/afii57505/afii57509/afii57514" + + "/afii57519/afii57534/Wgrave/wgrave/Wacute/wacute/Wdieresis/w" + + "dieresis/Ygrave/ygrave/afii61664/afii301/afii299/afii300/fig" + + "uredash/endash/emdash/afii00208/underscoredbl/quoteleft/quot" + + "eright/quotesinglbase/quotereversed/quotedblleft/quotedblrig" + + "ht/quotedblbase/dagger/daggerdbl/bullet/onedotenleader/twodo" + + "tenleader/ellipsis/afii61573/afii61574/afii61575/perthousand" + + "/minute/second/guilsinglleft/guilsinglright/exclamdbl/fracti" + + "on/colonmonetary/franc/lira/peseta/afii57636/dong/Euro/afii6" + + "1248/Ifraktur/afii61289/afii61352/weierstrass/Rfraktur/presc" + + "ription/trademark/Omega/estimated/aleph/onethird/twothirds/o" + + "neeighth/threeeighths/fiveeighths/seveneighths/arrowleft/arr" + + "owup/arrowright/arrowdown/arrowboth/arrowupdn/arrowupdnbse/c" + + "arriagereturn/arrowdblleft/arrowdblup/arrowdblright/arrowdbl" + + "down/arrowdblboth/universal/partialdiff/existential/emptyset" + + "/Delta/gradient/element/notelement/suchthat/product/summatio" + + "n/minus/asteriskmath/radical/proportional/infinity/orthogona" + + "l/angle/logicaland/logicalor/intersection/union/integral/the" + + "refore/similar/congruent/approxequal/notequal/equivalence/le" + + "ssequal/greaterequal/propersubset/propersuperset/notsubset/r" + + "eflexsubset/reflexsuperset/circleplus/circlemultiply/perpend" + + "icular/dotmath/house/revlogicalnot/integraltp/integralbt/ang" + + "leleft/angleright/SF100000/SF110000/SF010000/SF030000/SF0200" + + "00/SF040000/SF080000/SF090000/SF060000/SF070000/SF050000/SF4" + + "30000/SF240000/SF510000/SF520000/SF390000/SF220000/SF210000/" + + "SF250000/SF500000/SF490000/SF380000/SF280000/SF270000/SF2600" + + "00/SF360000/SF370000/SF420000/SF190000/SF200000/SF230000/SF4" + + "70000/SF480000/SF410000/SF450000/SF460000/SF400000/SF540000/" + + "SF530000/SF440000/upblock/dnblock/block/lfblock/rtblock/ltsh" + + "ade/shade/dkshade/filledbox/H22073/H18543/H18551/filledrect/" + + "triagup/triagrt/triagdn/triaglf/lozenge/circle/H18533/invbul" + + "let/invcircle/openbullet/smileface/invsmileface/sun/female/m" + + "ale/spade/club/heart/diamond/musicalnote/musicalnotedbl/"; + + + /** + * Determines the name of a glyph according to the Adobe Glyph List + * for New Fonts (AGLFN). Because all glyphs in AGLFN correspond to + * a precomposed Unicode codepoint, the mismatch between characters + * and glyphs is not an issue here. + * + * @param c the Unicode codepoint that corresponds to the glyph, for + * example 0x010a for LATIN CAPITAL LETTER C WITH + * DOT ABOVE. + * + * @return the glyph name, for example Cdotaccent. If + * the glyph is not in the Adobe Glyph List for New Fonts, + * null is returned. + * + * @see Adobe + * Glyph List for New Fonts (AGLFN), version 1.1 of April 17, + * 2003 + * + * @see Adobe’s guidelines related to Unicode + */ + private static String getAGLFNName(char c) + { + int min, max, mid; + char midChar; + + /* Performs a binary search in the sorted array (actually, a + * String) of glyphs in the Adobe Glyph List for New Fonts. + * + * A good compiler might be able to optimize a call to charAt for + * a static final String, but this routine is probably not that + * critical to performance. + */ + min = 0; + max = AGLFN_GLYPHS.length() - 1; + mid = max >> 1; + midChar = AGLFN_GLYPHS.charAt(mid); + do + { + if (midChar == c) + break; + else if (midChar < c) + min = mid + 1; + else + max = mid; + mid = (min + max) >> 1; + midChar = AGLFN_GLYPHS.charAt(mid); + } + while (min < max); + + if (midChar != c) + return null; + + int pos = AGLFN_NAME_OFFSET.charAt(mid); + return AGLFN_NAMES.substring(pos, AGLFN_NAMES.indexOf('/', pos)); + } + + + /** + * Returns the PostScript name of a glyph, given the sequence of + * Unicode characters that is required to produce the glyph. The + * returned name follows Adobe’s glyph naming recommendations + * in order to allow searching and indexing of the produced + * PostScript and PDF. + * + *

Some examples: + *

. + * + *

The routine does not bring sequences in any canonical + * form. Therefore, the result for U+0067 U+0302 (the + * decomposition of U+011D) will be + * g_uni0302, not gcircumflex. + * + * @see Unicode + * and Glyph Names and Glyph Names and Current Implementations + */ + private static String getGlyphName(char[] chars) + { + char c; + String name; + int numChars; + boolean hasSurrogates = false; + + if ((chars == null) || ((numChars = chars.length) == 0)) + return ".notdef"; + + /* The vast majority of cases will be just a single character. + * Therefore, we have a special code path for this case. + */ + if (numChars == 1) + { + c = chars[0]; + name = getAGLFNName(c); + if (name != null) + return name; + } + + CPStringBuilder buf = new CPStringBuilder(numChars * 8); + for (int i = 0; i < numChars; i++) + { + if (i > 0) + buf.append('_'); + c = chars[i]; + + /* handle surrogate pairs */ + if (c >> 10 == 0x36) // U+D800 .. U+DBFF: High surrogate + { + /* Adobe recommends using the 'u' prefix only for + * characters outside the Unicode Basic Multilingual Plane, + * because Acrobat 4 and 5 understand only the "uni" prefix. + * The 'u' prefix will be supported by Acrobat 6 and later. + * + * For further information, please refer to this page: + * http://partners.adobe.com/asn/tech/type/glyphnamelimits.jsp#3 + */ + int ucs4 = (((c & 0x3ff) << 10) | (chars[++i] & 0x3ff)) + 0x10000; + buf.append('u'); + buf.append(Integer.toHexString(ucs4).toUpperCase()); + } + else + { + /* Try the Adobe Glyph List. */ + name = getAGLFNName(c); + if (name != null) + buf.append(name); + else + { + char nibble; + buf.append("uni"); + nibble = (char) (((c >> 12) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 8) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 4) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + nibble = (char) (((c >> 0) & 0xf) + 0x30); + if (nibble > 0x39) + nibble += 7; + buf.append(nibble); + } + } + } + return buf.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/Hinter.java b/libjava/classpath/gnu/java/awt/font/opentype/Hinter.java new file mode 100644 index 000000000..9758a2896 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/Hinter.java @@ -0,0 +1,63 @@ +/* Hinter.java -- The interface to a hinting implementation + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype; + +import gnu.java.awt.font.opentype.truetype.Zone; + +/** + * The interface to a hinting implementation. + */ +public interface Hinter +{ + /** + * Initializes the hinter. + * + * @param face the font for which the hinter should be used + */ + void init(OpenTypeFont face); + + /** + * Hints the specified outline. + * + * @param outline the outline to hint + */ + void applyHints(Zone outline); + + void setFlags(int flags); +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java b/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java new file mode 100644 index 000000000..c0f3de80e --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/MacResourceFork.java @@ -0,0 +1,235 @@ +/* MacResourceFork.java -- Parses MacOS resource forks. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype; + +import java.nio.ByteBuffer; + + +/** + * A class for accessing data that is stored in the resource fork of + * Macintosh files. Writing resource forks is currently not supported. + * + *

The gnu.java.awt.font package uses this class for accessing + * fonts in the MacOS X ".dfont" format, which is is a file in the + * format of a Macintosh resource fork, but stored in the normal data + * fork of the file. + * + *

The implementation has been designed to work efficiently with + * the virtual memory subsystem. It is recommended to pass an + * instance of {@link java.nio.MappedByteBuffer} to the constructor. + * + *

Thread Safety: All access is synchronized on the ByteBuffer + * that is passed to the constructor. + * + * @see Apple’ developer documentation about the Resource File + * Format + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class MacResourceFork +{ + int[] types; + Resource[][] resources; + ByteBuffer buf; + + public MacResourceFork(ByteBuffer buf) + { + int typeListOffset; + int refListOffset; + int nameListOffset; + int mapOffset, mapLen; + int dataOffset, dataLen; + int numTypes; + + synchronized (buf) + { + buf = buf.duplicate(); + this.buf = buf; + buf.position(0); + dataOffset = buf.getInt(); + mapOffset = buf.getInt(); + dataLen = buf.getInt(); + mapLen = buf.getInt(); + buf.position(mapOffset + 24); + refListOffset = mapOffset + buf.getChar(); + nameListOffset = mapOffset + buf.getChar(); + numTypes = buf.getChar() + 1; + types = new int[numTypes]; + resources = new Resource[numTypes][]; + + /* Parse resource type list. */ + typeListOffset = buf.position(); + for (int i = 0; i < numTypes; i++) + { + buf.position(typeListOffset + 8 * i); + int resType = buf.getInt(); + int numRes = buf.getChar() + 1; + + types[i] = resType; + resources[i] = new Resource[numRes]; + + buf.position(refListOffset + buf.getChar()); + for (int j = 0; j < numRes; j++) + { + short resID = buf.getShort(); + int resNameOffset = nameListOffset + buf.getChar(); + int resDataOffset = buf.getInt(); + byte resAttr = (byte) (resDataOffset >> 24); + resDataOffset = dataOffset + (resDataOffset & 0x00ffffff); + buf.getInt(); /* skip four reserved bytes */ + + Resource rsrc = new Resource(buf, resType, resID, resDataOffset, + resNameOffset); + resources[i][j] = rsrc; + } + } + } + } + + + public Resource[] getResources(int type) + { + synchronized (buf) + { + for (int i = 0; i < types.length; i++) + { + if (types[i] == type) + return resources[i]; + } + } + return null; + } + + + public Resource getResource(int type, short id) + { + Resource[] res; + + synchronized (buf) + { + for (int i = 0; i < types.length; i++) + { + if (types[i] != type) + continue; + + res = resources[i]; + for (int j = 0; j < res.length; j++) + if (res[j].getID() == id) + return res[j]; + } + } + + return null; + } + + + /** + * A single resource that is contained in a resource fork. + */ + public static final class Resource + { + int type; + short id; + byte attribute; + int nameOffset; + int dataOffset; + ByteBuffer buf; + + private Resource(ByteBuffer buf, + int type, short id, int dataOffset, int nameOffset) + { + this.buf = buf; + this.type = type; + this.id = id; + this.dataOffset = dataOffset; + this.nameOffset = nameOffset; + } + + + /** + * Returns the type of this resource. + * + * @return an int encoding a four-byte type tag, + * such as 0x464f4e54 for 'FONT'. + */ + public int getType() + { + return type; + } + + + /** + * Returns the ID of this resource. + */ + public short getID() + { + return id; + } + + + /** + * Retrieves the content of the resource. Only one page of memory + * is touched, irrespective of the actual size of the resource. + */ + public ByteBuffer getContent() + { + synchronized (buf) + { + buf.limit(buf.capacity()); + int len = buf.getInt(dataOffset); + buf.position(dataOffset + 4).limit(dataOffset + 4 + len); + return buf.slice(); + } + } + + + /** + * Determines the length of the resource in bytes. + */ + public int getLength() + { + synchronized (buf) + { + return buf.getInt(dataOffset); + } + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java new file mode 100644 index 000000000..1f1d50ac1 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/NameDecoder.java @@ -0,0 +1,702 @@ +/* NameDecoder.java -- Decodes names of OpenType and TrueType fonts. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Locale; + + +/** + * A utility class that helps with decoding the names of OpenType + * and TrueType fonts. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class NameDecoder +{ + public static final int NAME_COPYRIGHT = 0; + + + /** + * Specifies the name of the family to which a font belongs, for + * example “Univers”. + */ + public static final int NAME_FAMILY = 1; + + + /** + * Specified the name of the font inside its family, for + * example “Light”. + */ + public static final int NAME_SUBFAMILY = 2; + + + public static final int NAME_UNIQUE = 3; + + + /** + * Specifies the full human-readable name of a font, for example + * “Univers Light” + */ + public static final int NAME_FULL = 4; + + + public static final int NAME_VERSION = 5; + + + /** + * Specifies the PostScript name of a font, for example + * “Univers-Light”. + */ + public static final int NAME_POSTSCRIPT = 6; + + + public static final int NAME_TRADEMARK = 7; + public static final int NAME_MANUFACTURER = 8; + public static final int NAME_DESIGNER = 9; + public static final int NAME_DESCRIPTION = 10; + public static final int NAME_VENDOR_URL = 11; + public static final int NAME_DESIGNER_URL = 12; + public static final int NAME_LICENSE = 13; + public static final int NAME_LICENSE_URL = 14; + public static final int NAME_PREFERRED_FAMILY = 16; + public static final int NAME_PREFERRED_SUBFAMILY = 17; + public static final int NAME_FULL_MACCOMPATIBLE = 18; + public static final int NAME_SAMPLE_TEXT = 19; + public static final int NAME_POSTSCRIPT_CID = 20; + + + private static final int PLATFORM_MACINTOSH = 1; + private static final int PLATFORM_MICROSOFT = 3; + + + public static String getName(ByteBuffer nameTable, + int name, Locale locale) + { + int numRecords; + int macLanguage, msLanguage; + int offset; + int namePlatform, nameEncoding, nameLanguage, nameID, nameLen; + int nameStart; + String result; + boolean match; + + if (nameTable == null) + return null; + + nameTable.position(0); + /* We understand only format 0 of the name table. */ + if (nameTable.getShort() != 0) + return null; + + macLanguage = getMacLanguageCode(locale); + msLanguage = getMicrosoftLanguageCode(locale); + numRecords = nameTable.getShort(); + offset = nameTable.getShort(); + + for (int i = 0; i < numRecords; i++) + { + namePlatform = nameTable.getShort(); + nameEncoding = nameTable.getShort(); + nameLanguage = nameTable.getShort(); + nameID = nameTable.getShort(); + nameLen = nameTable.getShort(); + nameStart = offset + nameTable.getShort(); + + + if (nameID != name) + continue; + + // Handle PS seperately as it can be only ASCII, although + // possibly encoded as UTF-16BE + if ( name == NAME_POSTSCRIPT ) + { + if( nameTable.get(nameStart) == 0 ) // Peek at top byte + result = decodeName("UTF-16BE", nameTable, nameStart, nameLen); + else + result = decodeName("ASCII", nameTable, nameStart, nameLen); + return result; + } + + match = false; + switch (namePlatform) + { + case PLATFORM_MACINTOSH: + if ((nameLanguage == macLanguage) || (locale == null)) + match = true; + else + { + switch (macLanguage) + { + case 49: /* Azerbaijani/Cyrillic */ + match = (nameLanguage == /* Azerbaijani/Arabic */ 50) + || (nameLanguage == /* Azerbaijani/Roman */ 150); + break; + + case 57: /* Mongolian/Mongolian */ + match = (nameLanguage == /* Mongolian/Cyrillic */ 58); + break; + + case 83: /* Malay/Roman */ + match = (nameLanguage == /* Malay/Arabic */ 84); + break; + } + } + break; + + case PLATFORM_MICROSOFT: + if (((nameLanguage & 0xff) == msLanguage) || (locale == null)) + match = true; + break; + } + + + if (match) + { + result = decodeName(namePlatform, nameEncoding, nameLanguage, + nameTable, nameStart, nameLen); + if (result != null) + return result; + } + } + + return null; + } + + + /** + * The language codes used by the Macintosh operating system. MacOS + * defines numeric language identifiers in the range [0 .. 95] and + * [128 .. 150]. To map this numeric identifier into an ISO 639 + * language code, multiply it by two and take the substring at that + * position. + * + *

ISO 639 has revised the code for some languages, namely + * he for Hebrew (formerly iw), + * yi (formerly ji), and id + * for Indonesian (formerly in). In those cases, this + * table intentionally contains the older, obsolete code. The + * reason is that this is the code which + * java.util.Locale.getLanguage() is specified to return. The + * implementation of {@link #getMacLanguageCode} depends on this. + * + * @see Language Codes: ISO 639, Microsoft and Macintosh + */ + private static final String macLanguageCodes + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + = "enfrdeitnlsvesdaptnoiwjaarfielismttrhrzhurhithkoltplhuetlv " + + // 3 4 5 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "fofaruzhnlgdsqrocssksljisrmkbgukbeuzkkazazhykamokytgtkmnmnps" + + // 6 7 8 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "kukssdbonesamrbnasgupaormlkntatesimykmloviintlmsmsamti sosw" + + // 9 10 11 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "rwrn mgeo " + + // 12 13 14 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + " cyeucalaqugnayttugtsjwsuglafbriugdgvgatoelkl" + + // 15 + // 0 + + "az"; + + + /** + * The primary language IDs used by the Microsoft operating systems. + * + *

ISO 639 has revised the code for some languages, namely + * he for Hebrew (formerly iw), + * yi (formerly ji), and id + * for Indonesian (formerly in). In those cases, this + * table intentionally contains the older, obsolete code. The + * reason is that this is the code which + * java.util.Locale.getLanguage() is specified to return. The + * implementation of {@link #getMicrosoftLanguageCode} depends on + * this. + * + * @see Language Codes: ISO 639, Microsoft and Macintosh + */ + private static final String microsoftLanguageCodes + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + = " arbgcazhcsdadeelenesfifriwhuisitjakonlnoplptrmrorushsksqsv" + + // 3 4 5 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "thtrurinukbesletlvlttgfavihyazeu mk ts xhzuafkafohimt " + + // 6 7 8 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + "gajimskkkyswtkuzttbnpaguortateknmlasmrsamnbocykmlomygl sd" + + // 9 10 11 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + + " si iuam ksnefypstl ha yo omtign laso"; + + + /** + * Maps a Java Locale into a MacOS language code. + * + *

For languages that are written in several script systems, + * MacOS defines multiple language codes. Java Locales have a + * variant which could be used for that purpose, but a small + * test program revealed that with Sun's JDK 1.4.1_01, only two + * of 134 available Locales have a variant tag (namely no_NO_NY + * and th_TH_TH).

+ * + *

The following cases are problematic: + * + *

+ * + * @return a MacOS language code, or -1 if there is no such code for + * loc’s language. + */ + private static int getMacLanguageCode(Locale loc) + { + int code; + + if (loc == null) + return -1; + + code = findLanguageCode(loc.getLanguage(), macLanguageCodes); + switch (code) + { + case 19: + /* Traditional Chinese (MacOS language #19) and and Simplified + * Chinese (MacOS language #33) both have "zh" as their ISO 639 + * code. + */ + if (loc.equals(Locale.SIMPLIFIED_CHINESE)) + code = 33; + break; + + // Other special cases would be 49, 57 and 83, but we do not + // know what do do about them. See the method documentation for + // details. + } + + return code; + } + + + /** + * Maps a Java Locale into a Microsoft language code. + */ + private static int getMicrosoftLanguageCode(Locale locale) + { + String isoCode; + int code; + + if (locale == null) + return -1; + + isoCode = locale.getLanguage(); + code = findLanguageCode(isoCode, microsoftLanguageCodes); + if (code == -1) + { + if (isoCode.equals("hr") || isoCode.equals("sr")) + { + /* Microsoft uses code 26 for "sh" (Serbo-Croatian), + * "hr" (Croatian) and "sr" (Serbian). Our table contains + * "sh". + */ + code = 26; + } + else if (isoCode.equals("gd")) + { + /* Microsoft uses code 60 for "gd" (Scottish Gaelic) and + * "ga" (Irish Gaelic). Out table contains "ga". + */ + code = 60; + } + } + return code; + } + + + private static int findLanguageCode(String lang, String langCodes) + { + int index; + if (lang == null) + return -1; + + if (lang.length() != 2) + return -1; + + index = 0; + do + { + index = langCodes.indexOf(lang, index); + + /* The index must be even to be considered a match. Otherwise, we + * could match with the second letter of one language and the + * first of antoher one. + */ + } + while (!((index < 0) || ((index & 1) == 0))); + if (index < 0) + return -1; + + index = index / 2; + return index; + } + + + private static String decodeName(int platform, int encoding, int language, + ByteBuffer buffer, int offset, int len) + { + String charsetName = getCharsetName(platform, language, encoding); + if (charsetName == null) + return null; + + return decodeName(charsetName, buffer, offset, len); + } + + private static String decodeName(String charsetName, + ByteBuffer buffer, int offset, int len) + { + byte[] byteBuf; + int oldPosition; + + byteBuf = new byte[len]; + oldPosition = buffer.position(); + try + { + buffer.position(offset); + buffer.get(byteBuf); + try + { + return new String(byteBuf, charsetName); + } + catch (UnsupportedEncodingException uex) + { + } + } + finally + { + buffer.position(oldPosition); + } + + return null; + } + + + /** + * Maps a MacOS language code into a Java Locale. + * + * @param macLanguageCode the MacOS language code for + * the language whose Java locale is to be retrieved. + * + * @return an suitable Locale, or null if + * the mapping cannot be performed. + */ + private static Locale getMacLocale(int macLanguageCode) + { + String isoCode; + + switch (macLanguageCode) + { + case 0: return Locale.ENGLISH; + case 1: return Locale.FRENCH; + case 2: return Locale.GERMAN; + case 3: return Locale.ITALIAN; + case 11: return Locale.JAPANESE; + case 23: return Locale.KOREAN; + case 19: return Locale.TRADITIONAL_CHINESE; + case 33: return Locale.SIMPLIFIED_CHINESE; + } + + if ((macLanguageCode < 0) || (macLanguageCode > 150)) + return null; + + isoCode = macLanguageCodes.substring(macLanguageCode << 1, + (macLanguageCode + 1) << 1); + if (isoCode.charAt(0) == ' ') + return null; + + return new Locale(isoCode); + } + + + + /** + * Maps a Windows LCID into a Java Locale. + * + * @param lcid the Windows language ID whose Java locale + * is to be retrieved. + * + * @return an suitable Locale, or null if + * the mapping cannot be performed. + */ + private static Locale getWindowsLocale(int lcid) + { + /* FIXME: This is grossly incomplete. */ + switch (lcid) + { + case 0x0407: return Locale.GERMAN; + case 0x0408: return new Locale("el", "GR"); + case 0x0409: return Locale.ENGLISH; + case 0x040b: return new Locale("fi"); + case 0x040c: return Locale.FRENCH; + case 0x0416: return new Locale("pt"); + case 0x0807: return new Locale("de", "CH"); + case 0x0809: return new Locale("en", "UK"); + case 0x080c: return new Locale("fr", "BE"); + case 0x0816: return new Locale("pt", "BR"); + case 0x0c07: return new Locale("de", "AT"); + case 0x0c09: return new Locale("en", "AU"); + case 0x0c0c: return new Locale("fr", "CA"); + case 0x1007: return new Locale("de", "LU"); + case 0x1009: return new Locale("en", "CA"); + case 0x100c: return new Locale("fr", "CH"); + case 0x1407: return new Locale("de", "LI"); + case 0x1409: return new Locale("en", "NZ"); + case 0x140c: return new Locale("fr", "LU"); + case 0x1809: return new Locale("en", "IE"); + + default: + return null; + } + } + + + /** + * Maps a Macintosh Script Manager code to the name of the + * corresponding Java Charset. + * + * @param macScript a MacOS ScriptCode, for example + * 6 for smGreek. + * + * @return a String that can be used to retrieve a Java + * CharsetDecorder, for example MacGreek, or + * null if macScript has an + * unsupported value. + */ + private static String getMacCharsetName(int macScript) + { + switch (macScript) + { + case 0: return "MacRoman"; + case 1: return "MacJapanese"; + case 2: return "MacKorean"; + case 3: return "MacTradChinese"; + case 4: return "MacArabic"; + case 5: return "MacHebrew"; + case 6: return "MacGreek"; + case 7: return "MacCyrillic"; + case 8: return "MacRSymbol"; + case 9: return "MacDevanagari"; + case 10: return "MacGurmukhi"; + case 11: return "MacGujarati"; + case 12: return "MacOriya"; + case 13: return "MacBengali"; + case 14: return "MacTamil"; + case 15: return "MacTelugu"; + case 16: return "MacKannada"; + case 17: return "MacMalayalam"; + case 18: return "MacSinhalese"; + case 19: return "MacBurmese"; + case 20: return "MacKhmer"; + case 21: return "MacThai"; + case 22: return "MacLao"; + case 23: return "MacGeorgian"; + case 24: return "MacArmenian"; + case 25: return "MacSimpChinese"; + case 26: return "MacTibetan"; + case 27: return "MacMongolian"; + case 28: return "MacEthiopic"; + case 29: return "MacCentralEurope"; + case 30: return "MacVietnamese"; + case 31: return "MacExtArabic"; + + default: return null; + } + } + + + /** + * Maps a Microsoft locale ID (LCID) to the name of the + * corresponding Java Charset. + * + * @param lcid the Microsoft locale ID. + * + * @return a String that can be used to retrieve a Java + * CharsetDecorder, for example windows-1252, or + * null if lcid has an unsupported value. + */ + private static String getMicrosoftCharsetName(int lcid) + { + int lang; + char codePage = '?'; + + /* Extract the language code from the LCID. */ + lang = lcid & 0x3ff; + + /* In the majority of cases, the language alone determines the + * codepage. + */ + if (lang < 100) + codePage = (" 612D022322225022EC2202201?002A462110777 68 ?2 1 " + + " 2 2 2112 ?1 1 2 2 ") + .charAt(lang); + + /* There are a few exceptions, however, where multiple code pages + * are used for the same language. */ + if (codePage == '?') + { + switch (lcid) + { + case 0x041a: // Croatian --> Windows-1250 (Central Europe) + case 0x081a: // Serbian (Latin) --> Windows-1250 (Central Europe) + codePage = '0'; + break; + + case 0x42c: // Azeri (Latin) --> Windows-1254 (Turkish) + case 0x443: // Uzbek (Latin) --> Windows-1254 (Turkish) + codePage = '4'; + break; + + case 0x82c: // Azeri (Cyrillic) --> Windows-1251 (Cyrillic) + case 0x843: // Uzbek (Cyrillic) --> Windows-1251 (Cyrillic) + case 0xc1a: // Serbian (Cyrillic) --> Windows-1251 (Cyrillic) + codePage = '1'; + break; + } + } + + switch (codePage) + { + case '0': return "windows-1250"; // Central Europe + case '1': return "windows-1251"; // Cyrillic + case '2': return "windows-1252"; // Latin 1 + case '3': return "windows-1253"; // Greek + case '4': return "windows-1254"; // Turkish + case '5': return "windows-1255"; // Hebrew + case '6': return "windows-1256"; // Arabic + case '7': return "windows-1257"; // Baltic + case '8': return "windows-1258"; // Vietnam + case 'A': return "windows-874"; // Thai + case 'B': return "windows-936"; // Simplified Chinese, GBK + case 'C': return "windows-949"; // Korean + case 'D': return "windows-950"; // Traditional Chinese, Big5 + case 'E': return "windows-932"; // Japanese Shift-JIS + default: return null; + } + } + + + /** + * Returns the Locale of an OpenType name. + * + * @param platform the OpenType platform ID. + * + * @param language the language tag of the OpenType name. If + * platform is 1, this is the MacOS language code. + * + * @param encoding the encoding tag of the OpenType name. If + * platform is 1, this is the MacOS script code. + */ + public static Locale getLocale(int platform, int language, int encoding) + { + switch (platform) + { + case 1: /* Apple Macintosh */ + return getMacLocale(language); + + case 3: /* Microsoft Windows */ + return getWindowsLocale(language); + + default: + return null; + } + } + + + /** + * Determines the name of the charset for an OpenType font name. + * + * @param platform the OpenType platform ID. + * + * @param language the language tag of the OpenType name. If + * platform is 1, this is the MacOS language code. + * + * @param encoding the encoding tag of the OpenType name. If + * platform is 1, this is the MacOS script code. + * + * @return a charset name such as "MacRoman", + * or null if the combination is not known. + */ + public static String getCharsetName(int platform, int language, int encoding) + { + switch (platform) + { + case 1: /* Apple Macintosh */ + return getMacCharsetName(encoding); + + case 3: /* Microsoft Windows */ + return getMicrosoftCharsetName(language); + + default: + return null; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java new file mode 100644 index 000000000..f46addc3c --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java @@ -0,0 +1,882 @@ +/* OpenTypeFont.java -- Manages OpenType and TrueType fonts. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.OpenType; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.nio.ByteBuffer; +import java.text.CharacterIterator; +import java.util.Locale; + +import gnu.java.awt.font.FontDelegate; +import gnu.java.awt.font.GNUGlyphVector; +import gnu.java.awt.font.autofit.AutoHinter; +import gnu.java.awt.font.opentype.truetype.TrueTypeScaler; +import gnu.java.awt.font.opentype.truetype.Zone; + + +/** + * A font that takes its data from OpenType or TrueType font tables. + * + *

OpenType is an extension of the TrueType font format. In addition + * to tables for names, kerning or layout, it also stores the shapes + * of individual glyphs. Three formats are recognized for glyphs: + * Quadratic splines (classic TrueType), cubic splines (PostScript), + * and bitmaps. + * + * @see Adobe’s + * OpenType specification + * + * @see Apple’s + * TrueType specification + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class OpenTypeFont + implements FontDelegate +{ + static final int TAG_OTTO = 0x4f54544f; // 'OTTO' + static final int TAG_SFNT = 0x73666e74; // 'sfnt' + static final int TAG_TRUE = 0x74727565; // 'true' + static final int TAG_TTCF = 0x74746366; // 'ttcf' + static final int TAG_ZAPF = 0x5a617066; // 'Zapf' + + + /** + * A buffer containing the font data. Note that this may well be an + * instance of the subclass MappedByteBuffer, in which case the + * virtual memory subsystem can more efficiently handle requests for + * font data. This is especially recommended for large font files + * that contain many glyphs that are rarely accessed. + */ + ByteBuffer buf; + + + /** + * The number of glyphs in this font. + */ + final int numGlyphs; + + int[] tableTag, tableStart, tableLength; + + + /** + * The version of the font in 16.16 fixed-point encoding, for + * example 0x00010000 for version 1.0. There are also two special + * version IDs used by fonts for Apple Macintosh, namely 'true' + * (0x74727565) and 'typ1'. OpenType fonts sometimes have 'OTTO' as + * their version. + */ + private int version; + + + /** + * The number of font units per em. For fonts with TrueType + * outlines, this is usually a power of two (such as 2048). For + * OpenType fonts with PostScript outlines, other values are + * acceptable (such as 1000). + */ + public int unitsPerEm; + + + /** + * A factor to convert font units into ems. This value is 1 / + * unitsPerEm. + */ + private float emsPerUnit; + + + /** + * The scaler to which the actual scaling work is delegated. + */ + private Scaler scaler; + + + /** + * A delegate object for mapping Unicode UCS-4 codepoints to glyph + * IDs. + */ + private CharGlyphMap cmap; + + + /** + * A delegate object for providing a name for each glyph. + */ + private GlyphNamer glyphNamer; + + private Hinter hinter; + + /** + * Constructs an OpenType or TrueType font. + * + * @param buf a buffer with the contents of the font file. It is + * recommended to use a MappedByteBuffer for very + * large font files. + * + * @param offsetTablePosition the position of the OpenType offset + * table in the font file. The offset table of most OpenType and + * TrueType fonts starts at position 0. However, so-called TrueType + * Collections support multiple OpenType fonts in a single file, + * which allows sharing some glyphs between fonts. If many glyphs + * are shared (for example all the Kanji glyphs between multiple + * Japanese fonts), the space savings can be considerable. In that + * case, the offset table of each individual font would start at its + * own position. + * + * @throws java.awt.FontFormatException if the font data is + * not in OpenType or TrueType format. + */ + OpenTypeFont(ByteBuffer buf, int offsetTablePosition) + throws FontFormatException + { + int numTables, searchRange, entrySelector, rangeShift; + + //buf = buf.duplicate(); + this.buf = buf; + buf.limit(buf.capacity()); + buf.position(offsetTablePosition); + + /* Check that the font data is in a supported format. */ + version = buf.getInt(); + switch (version) + { + case 0x00010000: // Microsoft TrueType + case OpenType.TAG_TYP1: // Adobe PostScript embeded in Apple SFNT ('typ1') + case TAG_SFNT: // Apple TrueType + case TAG_TRUE: // Apple TrueType + case TAG_OTTO: // OpenType + break; + + default: + throw new FontFormatException("not in OpenType or TrueType format"); + } + + numTables = buf.getShort(); + searchRange = buf.getShort(); + entrySelector = buf.getShort(); + rangeShift = buf.getShort(); + + tableTag = new int[numTables]; + tableStart = new int[numTables]; + tableLength = new int[numTables]; + int lastTag = 0; + for (int i = 0; i < numTables; i++) + { + tableTag[i] = buf.getInt(); + if (lastTag >= tableTag[i]) + throw new FontFormatException("unordered OpenType table"); + + buf.getInt(); // ignore checksum + tableStart[i] = buf.getInt(); + tableLength[i] = buf.getInt(); + + //System.out.println(tagToString(tableTag[i]) + ", " + tableLength[i]); + } + + ByteBuffer head = getFontTable(OpenType.TAG_HEAD); + if ((head.getInt(0) != 0x00010000) + || (head.getInt(12) != 0x5f0f3cf5)) + throw new FontFormatException("unsupported head version"); + + unitsPerEm = head.getChar(18); + emsPerUnit = 1.0f / (float) unitsPerEm; + + + ByteBuffer maxp = getFontTable(OpenType.TAG_MAXP); + int maxpVersion = maxp.getInt(0); + switch (maxpVersion) + { + case 0x00005000: /* version 0.5, with wrong fractional part */ + numGlyphs = maxp.getChar(4); + break; + + case 0x00010000: /* version 1.0 */ + numGlyphs = maxp.getChar(4); + scaler = new TrueTypeScaler(unitsPerEm, + getFontTable(OpenType.TAG_HHEA), + getFontTable(OpenType.TAG_HMTX), + getFontTable(OpenType.TAG_VHEA), + getFontTable(OpenType.TAG_VMTX), + maxp, + getFontTable(OpenType.TAG_CVT), + getFontTable(OpenType.TAG_FPGM), + /* loca format */ head.getShort(50), + getFontTable(OpenType.TAG_LOCA), + getFontTable(OpenType.TAG_GLYF), + getFontTable(OpenType.TAG_PREP)); + break; + + default: + throw new FontFormatException("unsupported maxp version"); + } + } + + + /** + * Determines the index of a table into the offset table. The + * result can be used to find the offset and length of a table, as + * in tableStart[getTableIndex(TAG_NAME)]. + * + * @param tag the table identifier, for instance + * OpenType.TAG_NAME. + * + * @return the index of that table into the offset table, or + * -1 if the font does not contain the table specified by + * tag. + */ + private int getTableIndex(int tag) + { + /* FIXME: Since the font specification requires tableTag[] to be + * ordered, one should do binary search here. + */ + for (int i = 0; i < tableTag.length; i++) + if (tableTag[i] == tag) + return i; + return -1; + } + + + + /** + * Returns the name of the family to which this font face belongs, + * for example “Univers”. + * + * @param locale the locale for which to localize the name. + * + * @return the family name. + */ + public synchronized String getFamilyName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_FAMILY, locale); + if (name == null) + name = getName(NameDecoder.NAME_FAMILY, Locale.ENGLISH); + if (name == null) + name = getName(NameDecoder.NAME_FAMILY, /* any language */ null); + if (name == null) + name = getName(NameDecoder.NAME_FULL, locale); + if (name == null) + name = getName(NameDecoder.NAME_FULL, /* any language */ null); + return name; + } + + + /** + * Returns the name of this font face inside the family, for example + * “Light”. + * + * @param locale the locale for which to localize the name. + * + * @return the name of the face inside its family. + */ + public synchronized String getSubFamilyName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_SUBFAMILY, locale); + if (name == null) + { + name = getName(NameDecoder.NAME_SUBFAMILY, Locale.ENGLISH); + if ("Regular".equals(name)) + name = null; + } + + if (name == null) + { + String lang = locale.getLanguage(); + if ("de".equals(lang)) + name = "Standard"; + else if ("fr".equals(lang)) + name = "Standard"; + else if ("it".equals(lang)) + name = "Normale"; + else if ("nl".equals(lang)) + name = "Normaal"; + else if ("fi".equals(lang)) + name = "Normaali"; + else if ("sv".equals(lang)) + name = "Normal"; + else + name = "Regular"; + } + + return name; + } + + + + /** + * Returns the full name of this font face, for example + * “Univers Light”. + * + * @param locale the locale for which to localize the name. + * + * @return the face name. + */ + public synchronized String getFullName(Locale locale) + { + String name; + + if (locale == null) + locale = Locale.getDefault(); + + name = getName(NameDecoder.NAME_FULL, locale); + if (name == null) + name = getName(NameDecoder.NAME_FULL, Locale.ENGLISH); + if (name == null) + name = getName(NameDecoder.NAME_FULL, /* any language */ null); + + return name; + } + + + /** + * Returns the PostScript name of this font face, for example + * “Univers-Light”. + * + * @return the PostScript name, or null if the font + * does not provide a PostScript name. + */ + public synchronized String getPostScriptName() + { + return getName(NameDecoder.NAME_POSTSCRIPT, /* any language */ null); + } + + + /** + * Returns the number of glyphs in this font face. + */ + public int getNumGlyphs() + { + /* No synchronization is needed because the number of glyphs is + * set in the constructor, and it cannot change during the + * lifetime of the object. + */ + return numGlyphs; + } + + + /** + * Returns the index of the glyph which gets displayed if the font + * cannot map a Unicode code point to a glyph. Many fonts show this + * glyph as an empty box. + */ + public int getMissingGlyphCode() + { + /* No synchronization is needed because the result is constant. */ + return 0; + } + + + /** + * The font’s name table, or null if this + * table has not yet been accessed. + */ + private ByteBuffer nameTable; + + + /** + * Extracts a String from the font’s name table. + * + * @param name the numeric TrueType or OpenType name ID. + * + * @param locale the locale for which names shall be localized, or + * null if the locale does mot matter because the name + * is known to be language-independent (for example, because it is + * the PostScript name). + */ + private String getName(int name, Locale locale) + { + if (nameTable == null) + nameTable = getFontTable(OpenType.TAG_NAME); + return NameDecoder.getName(nameTable, name, locale); + } + + + /** + * Returns the version of the font. + * + * @see java.awt.font.OpenType#getVersion + * + * @return the version in 16.16 fixed-point encoding, for example + * 0x00010000 for version 1.0. + */ + public int getVersion() + { + /* No synchronization is needed because the version is set in the + * constructor, and it cannot change during the lifetime of the + * object. + */ + return version; + } + + + /** + * Creates a view buffer for an OpenType table. The caller can + * access the returned buffer without needing to synchronize access + * from multiple threads. + * + * @param tag the table identifier, for example + * OpenType.GLYF. + * + * @return a slice of the underlying buffer containing the table, or + * null if the font does not contain the requested + * table. + */ + public synchronized ByteBuffer getFontTable(int tag) + { + int index, start, len; + ByteBuffer result; + + index = getTableIndex(tag); + if (index < 0) + return null; + + start = tableStart[index]; + len = tableLength[index]; + buf.limit(start + len).position(start); + result = buf.slice(); + result.limit(len); + return result; + } + + + /** + * Returns the size of one of the tables in the font, + * or -1 if the table does not exist. + */ + public int getFontTableSize(int tag) + { + int index = getTableIndex(tag); + if (index == -1) + return index; + return tableLength[index]; + } + + + private CharGlyphMap getCharGlyphMap() + { + if (cmap != null) + return cmap; + + synchronized (this) + { + if (cmap == null) + { + int index = getTableIndex(OpenType.TAG_CMAP); + int start = tableStart[index]; + buf.limit(start + tableLength[index]).position(start); + cmap = CharGlyphMap.forTable(buf); + } + return cmap; + } + } + + + + /** + * Looks up a glyph in the font’s cmap tables, + * without performing any glyph substitution or reordering. Because + * of this limitation, this method cannot be used for script systems + * that need advanced glyph mapping, such as Arabic, Korean, or even + * Latin with exotic accents. + * + *

It is safe to call this method from any thread. + * + * @param ucs4 the Unicode codepoint in the 32-bit Unicode character + * set UCS-4. Because UTF-16 surrogates do not correspond to a single + * glyph, it does not make sense to pass them here. + * + * @return the glyph index, or zero if the font does not contain + * a glyph for the specified codepoint. + */ + public int getGlyph(int ucs4) + { + return getCharGlyphMap().getGlyph(ucs4); + } + + + /** + * Creates a GlyphVector by mapping each character in a + * CharacterIterator to the corresponding glyph. + * + *

The mapping takes only the font’s cmap + * tables into consideration. No other operations (such as glyph + * re-ordering, composition, or ligature substitution) are + * performed. This means that the resulting GlyphVector will not be + * correct for text in languages that have complex + * character-to-glyph mappings, such as Arabic, Hebrew, Hindi, or + * Thai. + * + * @param font the font object that the created GlyphVector + * will return when it gets asked for its font. This argument is + * needed because the public API works with java.awt.Font, + * not with some private delegate like OpenTypeFont. + * + * @param frc the font rendering parameters that are used for + * measuring glyphs. The exact placement of text slightly depends on + * device-specific characteristics, for instance the device + * resolution or anti-aliasing. For this reason, any measurements + * will only be accurate if the passed + * FontRenderContext correctly reflects the relevant + * parameters. Hence, frc should be obtained from the + * same Graphics2D that will be used for drawing, and + * any rendering hints should be set to the desired values before + * obtaining frc. + * + * @param ci a CharacterIterator for iterating over the + * characters to be displayed. + */ + public synchronized GlyphVector createGlyphVector(Font font, + FontRenderContext frc, + CharacterIterator ci) + { + // Initialize hinter if necessary. + checkHinter(FontDelegate.FLAG_FITTED); + + CharGlyphMap cmap; + int numGlyphs; + int[] glyphs; + int glyph; + int c; + + cmap = getCharGlyphMap(); + numGlyphs = ci.getEndIndex() - ci.getBeginIndex(); + glyphs = new int[numGlyphs]; + glyph = 0; + for (c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) + { + /* handle surrogate pairs */ + if (c >> 10 == 0x36) // U+D800 .. U+DBFF: High surrogate + c = (((c & 0x3ff) << 10) | (ci.next() & 0x3ff)) + 0x10000; + glyphs[glyph] = cmap.getGlyph(c); + glyph += 1; + } + + /* If we had surrogates, the allocated array is too large. + * Because this will occur very rarely, it seems acceptable to + * re-allocate a shorter array and copy the contents around. + */ + if (glyph != numGlyphs) + { + int[] newGlyphs = new int[glyph]; + System.arraycopy(glyphs, 0, newGlyphs, 0, glyph); + glyphs = newGlyphs; + } + + return new GNUGlyphVector(this, font, frc, glyphs); + } + + /** + * Returns the glyph code for the specified character. + * + * @param c the character to map + * + * @return the glyph code + */ + public int getGlyphIndex(int c) + { + return getCharGlyphMap().getGlyph(c); + } + + /** + * Determines the advance width for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @param advance a point whose x and y + * fields will hold the advance in each direction. It is possible + * that both values are non-zero, for example if + * transform is a rotation, or in the case of Urdu + * fonts. + */ + public synchronized void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance) + { + /* Delegate the measurement to the scaler. The synchronization is + * needed because the scaler is not synchronized. + */ + scaler.getAdvance(glyphIndex, pointSize, transform, + antialias, fractionalMetrics, horizontal, + advance); + } + + + /** + * Returns the shape of a glyph. + * + * @param glyph the glyph whose advance width is to be determined + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, this + * parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional + * metrics, false for rounding the result to a pixel + * boundary. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or null for bitmap fonts. + */ + public synchronized GeneralPath getGlyphOutline(int glyph, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + int flags) + { + /* The synchronization is needed because the scaler is not + * synchronized. + */ + checkHinter(flags); + return scaler.getOutline(glyph, pointSize, transform, + antialias, fractionalMetrics, hinter, flags); + } + + /** + * Fetches the raw glyph outline for the specified glyph index. This is used + * for the autofitter only ATM and is otherwise not usable for outside code. + * + * @param glyph the glyph index to fetch + * @param transform the transform to apply + * + * @return the raw outline of that glyph + */ + public synchronized Zone getRawGlyphOutline(int glyph, + AffineTransform transform) + { + return scaler.getRawOutline(glyph, transform); + } + + /** + * Returns a name for the specified glyph. This is useful for + * generating PostScript or PDF files that embed some glyphs of a + * font. + * + *

Names are not unique: Under some rare circumstances, + * the same name can be returned for different glyphs. It is + * therefore recommended that printer drivers check whether the same + * name has already been returned for antoher glyph, and make the + * name unique by adding the string ".alt" followed by the glyph + * index.

+ * + *

This situation would occur for an OpenType or TrueType font + * that has a post table of format 3 and provides a + * mapping from glyph IDs to Unicode sequences through a + * Zapf table. If the same sequence of Unicode + * codepoints leads to different glyphs (depending on contextual + * position, for example, or on typographic sophistication level), + * the same name would get synthesized for those glyphs. + * + * @param glyphIndex the glyph whose name the caller wants to + * retrieve. + */ + public synchronized String getGlyphName(int glyphIndex) + { + if (glyphNamer == null) + glyphNamer = GlyphNamer.forTables(numGlyphs, + getFontTable(OpenType.TAG_POST), + getFontTable(TAG_ZAPF)); + + return glyphNamer.getGlyphName(glyphIndex); + } + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public synchronized float getAscent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + return scaler.getAscent(pointSize, transform, + antialiased, fractionalMetrics, + horizontal); + } + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public synchronized float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + return scaler.getDescent(pointSize, transform, + antialiased, fractionalMetrics, + horizontal); + } + + + /** + * Converts a four-byte tag identifier into a String that can be + * displayed when debugging this class. + * + * @param tag the tag as an int. + * + * @return the tag in human-readable form, for example + * name or glyf. + */ + static String tagToString(int tag) + { + char[] c = new char[4]; + c[0] = (char) ((tag >> 24) & 0xff); + c[1] = (char) ((tag >> 16) & 0xff); + c[2] = (char) ((tag >> 8) & 0xff); + c[3] = (char) (tag & 0xff); + return new String(c); + } + + /** + * Checks if a hinter is installed and installs one when not. + */ + private void checkHinter(int flags) + { + // When another hinting impl gets added (maybe a true TrueType hinter) + // then add some options here. The Hinter interface might need to be + // tweaked. + if (hinter == null) + { + try + { + hinter = new AutoHinter(); + hinter.init(this); + } + catch (Exception ex) + { + // Protect from problems inside hinter. + hinter = null; + ex.printStackTrace(); + } + } + hinter.setFlags(flags); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java new file mode 100644 index 000000000..32c4828c9 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/OpenTypeFontFactory.java @@ -0,0 +1,140 @@ +/* OpenTypeFontFactory.java -- Creates OpenType and TrueType fonts. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype; + +import gnu.java.awt.font.FontDelegate; +import java.awt.FontFormatException; +import java.awt.font.OpenType; +import java.nio.ByteBuffer; + + +/** + * A factory for creating fonts that are stored in an + * sfnt-housed format, for example OpenType or TrueType. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class OpenTypeFontFactory +{ + /** + * The constructor is private so nobody can construct an instance + * of this class. + */ + private OpenTypeFontFactory() + { + } + + + /** + * Creates FontDelegate objects for the fonts in the specified + * buffer. The following font formats are currently recognized: + * + *

+ * + *

Some formats may contain more than a single font, for example + * *.ttc and *.dfont files. This is the reason why this function + * returns an array. + * + *

The implementation reads data from the buffer only when + * needed. Therefore, it greatly increases efficiency if + * buf has been obtained through mapping a file into + * the virtual address space. + * + * @throws FontFormatException if the font data is not in one of the + * known formats. + */ + public static FontDelegate[] createFonts(ByteBuffer buf) + throws FontFormatException + { + OpenTypeFont[] fonts; + int version; + + version = buf.getInt(0); + switch (version) + { + case 0x00010000: // Microsoft Windows TrueType + case OpenType.TAG_TYP1: // Apple MacOS PostScript ('typ1') + case OpenTypeFont.TAG_SFNT: // Apple MacOS TrueType ('sfnt') + case OpenTypeFont.TAG_TRUE: // Apple MacOS TrueType ('true') + case OpenTypeFont.TAG_OTTO: // OpenType + return new OpenTypeFont[] { new OpenTypeFont(buf, 0) }; + } + + + /* TrueType Collection, see "TrueType Collections" in + * http://partners.adobe.com/asn/tech/type/opentype/otff.html + */ + if (version == OpenTypeFont.TAG_TTCF) + { + // This code has never been tested. + fonts = new OpenTypeFont[buf.getInt(8)]; + for (int i = 0; i < fonts.length; i++) + fonts[i] = new OpenTypeFont(buf, buf.getInt(16 + 4 * i)); + return fonts; + } + + + /* The MacOS X .dfont format is a Macintosh resource fork in + * a normal file, contaning one or several 'sfnt' resources. + * Unfortunately, MacOS resource forks have no magic code + * that could be used for identification. Instead, we just try + * to extract at least one 'sfnt'. + */ + try + { + MacResourceFork fork = new MacResourceFork(buf); + MacResourceFork.Resource[] rsrc; + + rsrc = fork.getResources(OpenTypeFont.TAG_SFNT); + fonts = new OpenTypeFont[rsrc.length]; + for (int i = 0; i < fonts.length; i++) + fonts[i] = new OpenTypeFont(rsrc[i].getContent(), 0); + + return fonts; + } + catch (Exception ex) + { + } + + throw new FontFormatException("not in OpenType or TrueType format"); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java new file mode 100644 index 000000000..c7582b666 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/Scaler.java @@ -0,0 +1,205 @@ +/* Scaler.java -- Common superclass for font scalers. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype; + +import gnu.java.awt.font.opentype.truetype.Zone; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; + + +/** + * An common superclass for all font scalers. The main task of font + * scaler is to retrieve a scaled and hinted outline for a glyph. + * + *

To make text more legible, high-quality fonts contain + * instructions (sometimes also called “hints”) for + * moving the scaled control points towards the coordinate grid of the + * display device. + * + *

Lack of Thread Safety: Font scalers are intentionally + * not safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font that uses this scaler already has obtained a lock before + * calling the scaler. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class Scaler +{ + /** + * Retrieves the scaled outline of a glyph, adjusting control points + * to the raster grid if necessary. + * + * @param glyph the glyph number whose outline is retrieved. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias whether or not the rasterizer will perform + * anti-aliasing on the returned path. + * + * @param fractionalMetrics false for adjusting glyph + * positions to the raster grid of device space. + * + * @return the scaled and grid-fitted outline of the specified + * glyph, or null for bitmap fonts. + */ + public abstract GeneralPath getOutline(int glyph, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + Hinter hinter, int type); + + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width is to be + * determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @param advance a point whose x and y + * fields will hold the advance in each direction. It is well + * possible that both values are non-zero, for example for rotated + * text or for Urdu fonts. + */ + public abstract void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance); + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public abstract float getAscent(float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal); + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public abstract float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal); + + /** + * Returns the raw outline data. This is used for the autofitter atm. + * + * @param glyph the glyph index + * @param transform the transform to apply + * + * @return the raw glyph outline + */ + public abstract Zone getRawOutline(int glyph, AffineTransform transform); +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java new file mode 100644 index 000000000..87dfebd41 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Fixed.java @@ -0,0 +1,176 @@ +/* Fixed.java -- Fixed-point arithmetics for TrueType coordinates. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +/** + * A utility class for fixed-point arithmetics, where numbers are + * represented with 26 dot 6 digits. This representation is used by + * TrueType coordinates. + * + *

A good compiler will inline calls of methods in this class. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class Fixed +{ + public static final int ONE = 1<<6; + + + /** + * The constructor is private so nobody can use it. + */ + private Fixed() + { + } + + + /** + * Multiplies two fixed-point numbers. + */ + public static int mul(int a, int b) + { + return (int) ((((long) a) * b) >> 6); + } + + public static int mul16(int a, int b) + { + return (int) ((((long) a) * b) >> 16); + } + + public static int div(int a, int b) + { + return (int) ((((long) a) << 6) / b); + } + + public static int div16(int a, int b) + { + return (int) ((((long) a) << 16) / b); + } + + public static int ceil(int a) + { + return (a + 63) & -64; + } + + + public static int floor(int a) + { + return a & -64; + } + + + /** + * Calculates the length of a fixed-point vector. + */ + public static int vectorLength(int x, int y) + { + int shift; + float fx, fy; + + if (x == 0) + return Math.abs(y); + else if (y == 0) + return Math.abs(x); + + /* Use the FPU. */ + fx = ((float) x) / 64.0f; + fy = ((float) y) / 64.0f; + return (int) (Math.sqrt(fx * fx + fy * fy) * 64.0); + } + + + public static int intValue(int f) + { + return f >> 6; + } + + + public static float floatValue(int f) + { + return ((float) f) / 64; + } + public static float floatValue16(int f) + { + return ((float) f) / 65536; + } + + public static double doubleValue(int f) + { + return ((double) f) / 64; + } + + + public static int valueOf(float f) + { + return (int) (f * 64); + } + + + public static int valueOf(double d) + { + return (int) (d * 64); + } + + public static int valueOf16(double d) + { + return (int) (d * (1 << 16)); + } + + /** + * Makes a string representation of a fixed-point number. + */ + public static String toString(int f) + { + return String.valueOf(floatValue(f)); + } + + + public static String toString(int x, int y) + { + CPStringBuilder sbuf = new CPStringBuilder(40); + sbuf.append('('); + sbuf.append(((float) x) / 64); + sbuf.append(", "); + sbuf.append(((float) y) / 64); + sbuf.append(')'); + return sbuf.toString(); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java new file mode 100644 index 000000000..8a3c56665 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java @@ -0,0 +1,447 @@ +/* GlyphLoader.java -- Helper for loading TrueType glyph outlines. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.awt.font.opentype.Hinter; + +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; + + +/** + * A class for loading scaled and hinted glyph outlines. + * + *

Lack of Thread Safety: Glyph loaders are intentionally + * not safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the GlyphLoader. It would thus be wasteful to + * acquire additional locks for the GlyphLoader. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphLoader +{ + /** + * A helper object for locating glyph data. GlyphLocator is an + * abstract superclass, and there is a concretization for each glyph + * location table ('loca') format. + */ + private final GlyphLocator glyphLocator; + + + /** + * A helper object for measuring the advance width and height of a + * glyph. + */ + private final GlyphMeasurer glyphMeasurer; + + + /** + * The virtual machine for executing TrueType bytecodes. + */ + private final VirtualMachine vm; + + + /** + * The number of font units in one em. A typical value is 2048, + * but this depends on the font. + */ + private final int unitsPerEm; + + private final int[] contourEndPoints; + private final byte[] pointFlags; + + + /** + * Constructs a GlyphLoader. + */ + GlyphLoader(GlyphLocator glyphLocator, VirtualMachine vm, + int unitsPerEm, int maxContours, int maxPoints, + GlyphMeasurer glyphMeasurer) + { + this.glyphLocator = glyphLocator; + this.glyphMeasurer = glyphMeasurer; + this.unitsPerEm = unitsPerEm; + this.vm = vm; + + contourEndPoints = new int[maxContours]; + pointFlags = new byte[maxPoints]; + } + + + /** + * @param glyphIndex the number of the glyph whose outlines are to be + * retrieved. + */ + public void loadGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + Zone glyphZone, Hinter hinter) + { + glyphZone.setNumPoints(4); + loadSubGlyph(glyphIndex, pointSize, transform, antialias, glyphZone, + 0, 0, hinter); + } + + public void loadGlyph(int glyphIndex, AffineTransform transform, + Zone glyphZone, Hinter hinter) + { + loadGlyph(glyphIndex, unitsPerEm, transform, false, glyphZone, hinter); + } + + private void loadSubGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + Zone glyphZone, + int preTranslateX, + int preTranslateY, + Hinter hinter) + { + ByteBuffer glyph; + int numContours; + int xMin, yMin, xMax, yMax; + byte flag; + + glyph = glyphLocator.getGlyphData(glyphIndex); + + if (glyph == null) + { + glyphZone.setNumPoints(4); + setPhantomPoints(glyphIndex, 0, glyphZone); + glyphZone.transform(pointSize, transform, unitsPerEm, + preTranslateX, preTranslateY); + return; + } + + numContours = glyph.getShort(); + xMin = glyph.getChar(); + yMin = glyph.getChar(); + xMax = glyph.getChar(); + yMax = glyph.getChar(); + + + if (numContours >= 0) + loadSimpleGlyph(glyphIndex, pointSize, transform, antialias, + numContours, glyph, glyphZone, + preTranslateX, preTranslateY, hinter); + else + loadCompoundGlyph(glyphIndex, pointSize, transform, antialias, + glyph, glyphZone, + preTranslateX, preTranslateY, hinter); + } + + + private void loadSimpleGlyph(int glyphIndex, + double pointSize, AffineTransform transform, + boolean antialias, + int numContours, ByteBuffer glyph, + Zone glyphZone, + int preTranslateX, int preTranslateY, + Hinter hinter) + { + int numPoints; + int posInstructions, numInstructions; + boolean execInstructions; + + execInstructions = vm.setup(pointSize, transform, antialias); + + /* Load the contour end points and determine the number of + * points. + */ + for (int i = 0; i < numContours; i++) + contourEndPoints[i] = glyph.getChar(); + if (numContours > 0) + numPoints = 1 + contourEndPoints[numContours - 1]; + else + numPoints = 0; + glyphZone.setNumPoints(numPoints + 4); + + numInstructions = glyph.getChar(); + posInstructions = glyph.position(); + glyph.position(posInstructions + numInstructions); + loadFlags(numPoints, glyph); + loadCoordinates(numPoints, glyph, glyphZone); + for (int i = 0; i < numContours; i++) + glyphZone.setContourEnd(contourEndPoints[i], true); + + setPhantomPoints(glyphIndex, numPoints, glyphZone); + glyphZone.transform(pointSize, transform, unitsPerEm, + preTranslateX, preTranslateY); + + if (execInstructions && hinter != null) + { + hinter.applyHints(glyphZone); + } + } + + + private static final short ARGS_ARE_WORDS = 1; + private static final short ARGS_ARE_XY_VALUES = 2; + private static final short ROUND_XY_TO_GRID = 4; + private static final short WE_HAVE_A_SCALE = 8; + private static final short MORE_COMPONENTS = 32; + private static final short WE_HAVE_AN_X_AND_Y_SCALE = 64; + private static final short WE_HAVE_A_TWO_BY_TWO = 128; + private static final short WE_HAVE_INSTRUCTIONS = 256; + private static final short USE_MY_METRICS = 512; + private static final short OVERLAP_COMPOUND = 1024; + private static final short SCALED_COMPONENT_OFFSET = 2048; + private static final short UNSCALED_COMPONENT_OFFSET = 4096; + + private void loadCompoundGlyph(int glyphIndex, + double pointSize, + AffineTransform transform, + boolean antialias, + ByteBuffer glyph, + Zone glyphZone, + int preTranslateX, int preTranslateY, + Hinter hinter) + { + short flags; + int subGlyphIndex; + int metricsGlyphIndex; + Zone subGlyphZone = new Zone(glyphZone.getCapacity()); + int arg1, arg2; + double a, b, c, d, e, f; + AffineTransform componentTransform = new AffineTransform(); + + /* By default, use the metrics of the compound glyph. The default + * is overridden if some component glyph has the USE_MY_METRICS + * flag set. + */ + metricsGlyphIndex = glyphIndex; + + do + { + flags = glyph.getShort(); + subGlyphIndex = glyph.getChar(); + + if ((flags & USE_MY_METRICS) != 0) + metricsGlyphIndex = subGlyphIndex; + + if ((flags & ARGS_ARE_WORDS) != 0) + { + arg1 = glyph.getShort(); + arg2 = glyph.getShort(); + } + else + { + arg1 = glyph.get(); + arg2 = glyph.get(); + } + + if ((flags & WE_HAVE_A_SCALE) != 0) + { + a = d = getDouble214(glyph); + b = c = 0.0; + } + else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) + { + a = getDouble214(glyph); + d = getDouble214(glyph); + b = c = 0.0; + } + else if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0) + { + a = getDouble214(glyph); + b = getDouble214(glyph); + c = getDouble214(glyph); + d = getDouble214(glyph); + } + else + { + a = d = 1.0; + b = c = 0.0; + } + + double m = Math.max(Math.abs(a), Math.abs(b)); + double n = Math.max(Math.abs(c), Math.abs(d)); + + /* The Apple TrueType specification actually says that m is + * multiplied by two if + * + * abs(abs(a) - abs(c)) <= 33/65536, + * + * but this is probably a typo. On 2003-07-23, Sascha Brawer + * wrote an e-mail message to applefonts@apple.com, asking + * whether this might possibly be an error in the specification. + */ + if (Math.abs(Math.abs(a) - Math.abs(b)) <= 33.0/65536.0) + m = m * 2; + + if (Math.abs(Math.abs(c) - Math.abs(d)) <= 33.0/65536.0) + n = n * 2; + + if ((flags & ARGS_ARE_XY_VALUES) != 0) + { + e = m * arg1; + f = n * arg2; + } + else + e = f = 0.0; + + componentTransform.setTransform(a, b, c, d, 0.0, 0.0); + + // System.out.println("componentTransform = " + componentTransform + // + ", e=" + e + ", f=" + f); + componentTransform.concatenate(transform); + + int pos = glyph.position(); + int lim = glyph.limit(); + + loadSubGlyph(subGlyphIndex, pointSize, componentTransform, + antialias, subGlyphZone, + Math.round((float) e + preTranslateX), + Math.round(-((float) f + preTranslateY)), hinter); + glyphZone.combineWithSubGlyph(subGlyphZone, 4); + glyph.limit(lim).position(pos); + } + while ((flags & MORE_COMPONENTS) != 0); + + setPhantomPoints(metricsGlyphIndex, glyphZone.getSize() - 4, glyphZone); + } + + + private double getDouble214(ByteBuffer buf) + { + return ((double) buf.getShort()) / (1 << 14); + } + + + /** + * Loads the per-point flags of a glyph into the + * pointFlags field. + */ + private void loadFlags(int numPoints, ByteBuffer glyph) + { + byte flag; + int numRepetitions; + + for (int i = 0; i < numPoints; i++) + { + pointFlags[i] = flag = glyph.get(); + if ((flag & 8) != 0) + { + numRepetitions = ((int) glyph.get()) & 0xff; + while (numRepetitions > 0) + { + pointFlags[++i] = flag; + --numRepetitions; + } + } + } + } + + + private void loadCoordinates(int numPoints, ByteBuffer glyph, + Zone glyphZone) + { + int x, y; + byte flag; + + x = 0; + for (int i = 0; i < numPoints; i++) + { + flag = pointFlags[i]; + if ((flag & 2) == 0) + { + if ((flag & 16) == 0) + x += glyph.getShort(); + } + else + { + if ((flag & 16) != 0) + x += (glyph.get() & 0xff); + else + x -= (glyph.get() & 0xff); + } + glyphZone.setOriginalX(i, x); + glyphZone.setOnCurve(i, (flag & 1) == 1); + } + + y = 0; + for (int i = 0; i < numPoints; i++) + { + flag = pointFlags[i]; + if ((flag & 4) == 0) + { + if ((flag & 32) == 0) + y += glyph.getShort(); + } + else + { + if ((flag & 32) != 0) + y += (glyph.get() & 0xff); + else + y -= (glyph.get() & 0xff); + } + glyphZone.setOriginalY(i, -y); + } + } + + + private void setPhantomPoints(int glyphIndex, int numPoints, + Zone glyphZone) + { + /* Phantom point 0: Character origin. */ + glyphZone.setOriginalX(numPoints, 0); + glyphZone.setOriginalY(numPoints, 0); + + /* Phantom point 1: Horizontal advance point. */ + glyphZone.setOriginalX(numPoints + 1, + glyphMeasurer.getAdvanceWidth(glyphIndex, true)); + glyphZone.setOriginalY(numPoints + 1, + glyphMeasurer.getAdvanceHeight(glyphIndex, true)); + + /* Phantom point 2: Vertical origin. */ + int vertX = glyphMeasurer.getAscent(/* vertical */ false); + int vertY = glyphMeasurer.getAscent(/* horizontal */ true); + glyphZone.setOriginalX(numPoints + 2, vertX); + glyphZone.setOriginalY(numPoints + 2, vertY); + + /* Phantom point 3: Vertical advance point. */ + glyphZone.setOriginalX(numPoints + 3, + vertX + glyphMeasurer.getAdvanceWidth(glyphIndex, false)); + glyphZone.setOriginalY(numPoints + 3, + vertY + glyphMeasurer.getAdvanceHeight(glyphIndex, false)); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java new file mode 100644 index 000000000..fc2256b15 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphLocator.java @@ -0,0 +1,187 @@ +/* GlyphLocator.java -- Locates outlines of TrueType glyphs. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.IntBuffer; + + +/** + * Locates glyph outlines in a TrueType or OpenType glyf + * table. + * + * @see Adobe’s specification of the OpenType ‘loca’ + * table + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +abstract class GlyphLocator +{ + /** + * The actual glyph data of the font, which is contained in the + * 'glyf' table. + */ + protected ByteBuffer glyfTable; + + + /** + * Creates a new GlyphLocator for a loca table. + * + * @param format the format of the loca table. The + * value must be 0 for two-byte offsets, or 1 for four-byte + * offsets. TrueType and OpenType fonts indicate the format in the + * indexToLoc field of the font header. + * + * @param loca the loca table of the font, which + * contains the position of each glyph in the glyf + * table. + * + * @param glyf the glyf table of the font, which + * contains the outline data of each glyph. + * + * @throws FontFormatException if format is neither 0 + * nor 1. + */ + public static GlyphLocator forTable(int format, ByteBuffer loca, + ByteBuffer glyf) + throws FontFormatException + { + switch (format) + { + case 0: + return new GlyphLocator.TwoByte(loca, glyf); + + case 1: + return new GlyphLocator.FourByte(loca, glyf); + + default: + throw new FontFormatException("unsupported loca format"); + } + } + + + /** + * Locates the outline data for a glyph. + * + *

For efficiency, the glyph locator does not create a new buffer + * for each invocation. Instead, this method always returns the same + * buffer object. Therefore, the data of a glyph must have been read + * completely before another glyph of the same font gets requested + * through this method. + * + * @param glyph the number of the glyph whose outlines are to be + * retrieved. + * + * @return a buffer whose position is set to the first byte of glyph + * data, and whose limit is set to disallow accessing any data that + * does not belong to the glyph. If there is no outline data for the + * requested glyph, as would be the case for the space glyph, the + * result will be null. + */ + public abstract ByteBuffer getGlyphData(int glyph); + + + /** + * A GlyphLocator that locates glyphs using two-byte offsets, + * interpreting loca tables of format 0. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private final static class TwoByte + extends GlyphLocator + { + final CharBuffer indexToLoc; + + TwoByte(ByteBuffer loca, ByteBuffer glyf) + { + this.glyfTable = glyf; + indexToLoc = loca.asCharBuffer(); + } + + + public ByteBuffer getGlyphData(int glyph) + { + int offset, limit; + offset = ((int) indexToLoc.get(glyph)) << 1; + limit = ((int) indexToLoc.get(glyph + 1)) << 1; + if (offset >= limit) + return null; + + glyfTable.limit(limit).position(offset); + return glyfTable; + } + } + + + /** + * A GlyphLocator that locates glyphs using four-byte offsets, + * interpreting loca tables of format 1. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private final static class FourByte + extends GlyphLocator + { + final IntBuffer indexToLoc; + + FourByte(ByteBuffer loca, ByteBuffer glyf) + { + this.glyfTable = glyf; + indexToLoc = loca.asIntBuffer(); + } + + + public ByteBuffer getGlyphData(int glyph) + { + int offset, limit; + offset = indexToLoc.get(glyph); + limit = indexToLoc.get(glyph + 1); + if (offset >= limit) + return null; + + glyfTable.limit(limit).position(offset); + return glyfTable; + } + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java new file mode 100644 index 000000000..452456d13 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/GlyphMeasurer.java @@ -0,0 +1,228 @@ +/* GlyphMeasurer.java -- Helper for measuring TrueType glyphs. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype.truetype; + +import java.awt.FontFormatException; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A class for measuring TrueType and OpenType glyphs. + * + *

Lack of Thread Safety: Glyph measurers are intentionally + * not safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the GlyphMeasurer. It would thus be wasteful to + * acquire additional locks for the GlyphMeasurer. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class GlyphMeasurer +{ + /** + * A view buffer that allows accessing the contents of the + * font’s hmtx table as shorts. + */ + private final ShortBuffer horizontalGlyphMetrics; + + + /** + * A view buffer that allows accessing the contents of the + * font’s vmtx table as shorts. + */ + private final ShortBuffer verticalGlyphMetrics; + + + private final int numLongHorizontalMetricsEntries; + private final int numLongVerticalMetricsEntries; + + private final int horizontalAscent; + private final int verticalAscent; + + private final int horizontalDescent; + private final int verticalDescent; + + private final int horizontalLineGap; + + + /** + * Constructs a GlyphMeasurer from TrueType/OpenType font tables. + * + * @param hhea the hhea table, which contains + * information about horizontal metrics that is common to all + * glyphs. + * + * @param hmtx the hmtx table, which contains + * glyph-specific information about horizontal metrics. + * + * @param vhea the vhea table, which contains + * information about vertical metrics that is common to all + * glyphs. If a font does not provide such a table, pass + * null. + * + * @param vmtx the vmtx table, which contains + * glyph-specific information about vertical metrics. If a font + * does not provide such a table, pass null. + */ + GlyphMeasurer(ByteBuffer hhea, ByteBuffer hmtx, + ByteBuffer vhea, ByteBuffer vmtx) + throws FontFormatException + { + if ((hhea.getInt(0) != 0x00010000) || (hhea.getInt(30) != 0)) + throw new FontFormatException("unsupported hhea format"); + + horizontalAscent = hhea.getShort(4); + horizontalDescent = hhea.getShort(6); + horizontalLineGap = hhea.getShort(8); + + numLongHorizontalMetricsEntries = hhea.getChar(34); + horizontalGlyphMetrics = hmtx.asShortBuffer(); + + if (vhea != null) + { + verticalAscent = vhea.getShort(4); + verticalDescent = vhea.getShort(6); + numLongVerticalMetricsEntries = vhea.getChar(34); + verticalGlyphMetrics = vmtx.asShortBuffer(); + } + else + { + verticalAscent = /* advanceWidthMax */ hhea.getChar(10) / 2; + verticalDescent = -verticalAscent; + numLongVerticalMetricsEntries = 0; + verticalGlyphMetrics = null; + } + } + + + /** + * Returns the distance from the baseline to the highest ascender. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the maximal ascent, in font units. + */ + public int getAscent(boolean horizontal) + { + return horizontal ? horizontalAscent : verticalAscent; + } + + + /** + * Returns the distance from the baseline to the lowest descender. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the maximal descent, in font units. + */ + public int getDescent(boolean horizontal) + { + return horizontal ? horizontalDescent : verticalDescent; + } + + + /** + * Returns the typographic line gap. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the line gap, in font units. + */ + public int getLineGap(boolean horizontal) + { + return horizontalLineGap; + } + + + /** + * Determines the advance width of a glyph, without considering + * hinting. + * + * @param glyphIndex the index of the glyph whose advance width is + * to be determined. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the advance width, in font units. + */ + public int getAdvanceWidth(int glyphIndex, boolean horizontal) + { + if (!horizontal) + return 0; + + glyphIndex = Math.min(glyphIndex, + numLongHorizontalMetricsEntries - 1); + return horizontalGlyphMetrics.get(glyphIndex << 1); + } + + + /** + * Determines the advance width of a glyph, without considering + * hinting. + * + * @param glyphIndex the index of the glyph whose advance width is + * to be determined. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the advance width, in font units. + */ + public int getAdvanceHeight(int glyphIndex, boolean horizontal) + { + if (horizontal) + return 0; + + /* If a font does not provide vertical glyph metrics, advance + * by the height of one horizontal line. + */ + if (verticalGlyphMetrics == null) + return horizontalAscent - horizontalDescent + horizontalLineGap; + + glyphIndex = Math.min(glyphIndex, + numLongVerticalMetricsEntries - 1); + return verticalGlyphMetrics.get(glyphIndex << 1); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java new file mode 100644 index 000000000..438eb6558 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Point.java @@ -0,0 +1,287 @@ +/* Point.java -- Holds information for one point on a glyph outline + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +/** + * Encapsulates information regarding one point on a glyph outline. + */ +public class Point +{ + public static final short FLAG_TOUCHED_X = 1; + public static final short FLAG_TOUCHED_Y = 2; + public static final short FLAG_ON_CURVE = 4; + public static final short FLAG_CONTOUR_END = 8; + public static final short FLAG_WEAK_INTERPOLATION = 16; + public static final short FLAG_INFLECTION = 32; + public static final short FLAG_DONE_X = 64; + public static final short FLAG_DONE_Y = 128; + + /** + * Right direction. + */ + public static final int DIR_RIGHT = 1; + + /** + * Left direction. + */ + public static final int DIR_LEFT = -1; + + /** + * Up direction. + */ + public static final int DIR_UP = 2; + + /** + * Down direction. + */ + public static final int DIR_DOWN = -2; + + /** + * The original x coordinate in font units. + */ + int origX; + + /** + * The original y coordinate in font units. + */ + int origY; + + /** + * The x coordinate scaled to the target. + */ + int scaledX; + + /** + * The y coordinate scaled to the target. + */ + int scaledY; + + /** + * The final hinted and scaled x coordinate. + */ + int x; + + /** + * The final hinted and scaled y coordinate. + */ + int y; + + int u; + int v; + + /** + * The glyph flags. + */ + short flags; + + /** + * The previous point in the contour. + */ + private Point prev; + + /** + * The next point in the contour. + */ + private Point next; + + /** + * The in-direction of the point, according to the DIR_* constants of this + * class. + */ + int inDir; + + /** + * The out-direction of the point, according to the DIR_* constants of this + * class. + */ + int outDir; + + public Point getNext() + { + return next; + } + + public void setNext(Point next) + { + this.next = next; + } + + public Point getPrev() + { + return prev; + } + + public void setPrev(Point prev) + { + this.prev = prev; + } + + public int getOrigX() + { + return origX; + } + + public void setOrigX(int origX) + { + this.origX = origX; + } + + public int getOrigY() + { + return origY; + } + + public void setOrigY(int origY) + { + this.origY = origY; + } + + public int getInDir() + { + return inDir; + } + + public void setInDir(int inDir) + { + this.inDir = inDir; + } + + public int getOutDir() + { + return outDir; + } + + public void setOutDir(int outDir) + { + this.outDir = outDir; + } + + public short getFlags() + { + return flags; + } + + public void setFlags(short flags) + { + this.flags = flags; + } + + public void addFlags(short flags) + { + this.flags |= flags; + } + + public boolean isControlPoint() + { + return (flags & FLAG_ON_CURVE) == 0; + } + + public int getU() + { + return u; + } + + public void setU(int u) + { + this.u = u; + } + + public int getV() + { + return v; + } + + public void setV(int v) + { + this.v = v; + } + + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append("[Point] origX: "); + s.append(origX); + s.append(", origY: "); + s.append(origY); + // TODO: Add more info when needed. + return s.toString(); + } + + public int getX() + { + return x; + } + + public void setX(int x) + { + this.x = x; + } + + public int getY() + { + return y; + } + + public void setY(int y) + { + this.y = y; + } + + public int getScaledX() + { + return scaledX; + } + + public void setScaledX(int scaledX) + { + this.scaledX = scaledX; + } + + public int getScaledY() + { + return scaledY; + } + + public void setScaledY(int scaledY) + { + this.scaledY = scaledY; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java new file mode 100644 index 000000000..1d5c53f9d --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java @@ -0,0 +1,380 @@ +/* TrueTypeScaler.java -- Font scaler for TrueType outlines. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.awt.font.opentype.Hinter; +import gnu.java.awt.font.opentype.Scaler; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.nio.ByteBuffer; + + +/** + * A scaler for fonts whose outlines are described in the TrueType + * format. + * + *

Lack of Thread Safety: Font scalers are intentionally + * not safe to access from multiple concurrent threads. + * Synchronization needs to be performed externally. Usually, the font + * that uses this scaler already has obtained a lock before calling + * the scaler. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class TrueTypeScaler + extends Scaler +{ + /** + * The TrueType or OpenType table that contains the glyph outlines. + */ + private ByteBuffer glyfTable; + + + /** + * A helper object for loading glyph outlines. + */ + private GlyphLoader glyphLoader; + + + /** + * A helper object for measuring the advance width and height of a + * glyph. + */ + private final GlyphMeasurer glyphMeasurer; + + private final Zone glyphZone; + + + /** + * The number of units per em. A typical value is 2048, but some + * font use other numbers as well. + */ + private int unitsPerEm; + + + /** + * Constructs a new TrueTypeScaler. + * + * @param unitsPerEm the number of font units per em. This value can + * be retrieved from the font’s head table. + * + * @param maxp the maxp table of the font, which + * contains various constants needed for setting up the virtual + * machine that interprets TrueType bytecodes. + * + * @param controlValueTable the cvt table of the font, + * which contains the initial values of the control value table. + * + * @param fpgm the fpgm table of the font, which + * contains a font program that is executed exactly once. The + * purpose of the font program is to define functions and to patch + * the interpreter. + * + * @param locaFormat the format of the loca table. The + * value must be 0 for two-byte offsets, or 1 for four-byte + * offsets. TrueType and OpenType fonts indicate the format in the + * indexToLoc field of the font header. + * + * @param loca the loca table of the font, which + * contains for each glyph the offset of its outline data + * in glyf. + * + * @param glyf the glyf table of the font, which + * contains the outline data for all glyphs in the font. + * + * @param preProgram the prep table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass null. + * + * @throws FontFormatException if format is neither 0 + * nor 1. + */ + public TrueTypeScaler(int unitsPerEm, + ByteBuffer hhea, + ByteBuffer htmx, + ByteBuffer vhea, + ByteBuffer vtmx, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fpgm, + int locaFormat, ByteBuffer loca, + ByteBuffer glyf, + ByteBuffer preProgram) + throws FontFormatException + { + int maxContours, maxPoints; + VirtualMachine vm; + + maxContours = Math.max(/* maxContours */ (int) maxp.getChar(8), + /* maxCompositeContours */ (int) maxp.getChar(12)) + + /* fix for some broken fonts */ 8; + maxPoints = Math.max(/* maxPoints */ (int) maxp.getChar(6), + /* maxCompositePoints */ (int) maxp.getChar(10)) + + /* fix for some broken fonts */ 12; + + + glyphZone = new Zone(maxPoints + /* four phantom points */ 4); + this.glyfTable = glyf; + vm = new VirtualMachine(unitsPerEm, maxp, + controlValueTable, fpgm, + preProgram); + + GlyphLocator locator = GlyphLocator.forTable(locaFormat, loca, glyf); + glyphMeasurer = new GlyphMeasurer(hhea, htmx, vhea, vtmx); + glyphLoader = new GlyphLoader(locator, vm, unitsPerEm, + maxContours, maxPoints, + glyphMeasurer); + + this.unitsPerEm = unitsPerEm; + } + + + /** + * Retrieves the scaled outline of a glyph, adjusting control points + * to the raster grid if necessary. + * + * @param glyphIndex the glyph number whose outline is retrieved. + * + * @param pointSize the point size for the glyph. + * + * @param deviceTransform an affine transformation for the device. + * + * @param antialias whether or not the rasterizer will perform + * anti-aliasing on the returned path. + * + * @param fractionalMetrics false for adjusting glyph + * positions to the raster grid of device space. + */ + public GeneralPath getOutline(int glyphIndex, + float pointSize, + AffineTransform deviceTransform, + boolean antialias, + boolean fractionalMetrics, Hinter hinter, + int type) + { + glyphLoader.loadGlyph(glyphIndex, pointSize, deviceTransform, + antialias, glyphZone, hinter); + return glyphZone.getPath(type); + } + + public Zone getRawOutline(int glyphIndex, AffineTransform transform) + { + Zone zone = new Zone(glyphZone.getCapacity()); + glyphLoader.loadGlyph(glyphIndex, transform, zone, null); + return zone; + } + + /** + * Determines the advance width and height for a glyph. + * + * @param glyphIndex the glyph whose advance width and height is to + * be determined. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @param advance a point whose x and y + * fields will hold the advance in each direction. It is possible + * that both values are non-zero, for example if + * transform is a rotation, or in the case of Urdu + * fonts. + */ + public void getAdvance(int glyphIndex, + float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal, + Point2D advance) + { + double x, y; + double scaleFactor = (double) pointSize / unitsPerEm; + + /* FIXME: Should grid-fit if needed. Also, use cache if present + * in the font. + */ + advance.setLocation( + scaleFactor * glyphMeasurer.getAdvanceWidth(glyphIndex, horizontal), + scaleFactor * glyphMeasurer.getAdvanceHeight(glyphIndex, horizontal)); + + transform.transform(advance, advance); + } + + + /** + * Scales a value from font units to pixels, given the point size + * and the transform. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. + * + * @param fractionalMetrics true for fractional + * metrics, false for rounding the result to a pixel + * boundary. + * + * @param horizontal true if the funits + * value is along the x axis, false if it is along the + * y axis. + */ + private float scaleFromFUnits(int funits, + float pointSize, + AffineTransform transform, + boolean fractionalMetrics, + boolean horizontal) + { + double s; + + s = (double) pointSize / unitsPerEm; + if (transform != null) + s *= horizontal ? transform.getScaleY() : transform.getScaleX(); + s *= funits; + if (!fractionalMetrics) + s = Math.round(s); + return (float) s; + } + + + /** + * Determines the distance between the base line and the highest + * ascender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialias true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the ascent, which usually is a positive number. + */ + public float getAscent(float pointSize, + AffineTransform transform, + boolean antialias, + boolean fractionalMetrics, + boolean horizontal) + { + /* Note that the ascent is orthogonal to the direction of line + * layout: If the line direction is horizontal, the measurement of + * ascent is along the vertical axis, and vice versa. + */ + return scaleFromFUnits(glyphMeasurer.getAscent(horizontal), + pointSize, + transform, + fractionalMetrics, + /* reverse */ !horizontal); + } + + + /** + * Determines the distance between the base line and the lowest + * descender. + * + * @param pointSize the point size of the font. + * + * @param transform a transform that is applied in addition to + * scaling to the specified point size. This is often used for + * scaling according to the device resolution. Those who lack any + * aesthetic sense may also use the transform to slant or stretch + * glyphs. + * + * @param antialiased true for anti-aliased rendering, + * false for normal rendering. For hinted fonts, + * this parameter may indeed affect the result. + * + * @param fractionalMetrics true for fractional metrics, + * false for rounding the result to a pixel boundary. + * + * @param horizontal true for horizontal line layout, + * false for vertical line layout. + * + * @return the descent, which usually is a nagative number. + */ + public float getDescent(float pointSize, + AffineTransform transform, + boolean antialiased, + boolean fractionalMetrics, + boolean horizontal) + { + /* Note that the descent is orthogonal to the direction of line + * layout: If the line direction is horizontal, the measurement of + * descent is along the vertical axis, and vice versa. + */ + return scaleFromFUnits(glyphMeasurer.getDescent(horizontal), + pointSize, + transform, + fractionalMetrics, + /* reverse */ !horizontal); + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java new file mode 100644 index 000000000..512c39ffd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java @@ -0,0 +1,1815 @@ +/* VirtualMachine.java -- Virtual machine for TrueType bytecodes. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A virtual machine for interpreting TrueType bytecodes. + * + *

Lack of Thread Safety: The virtual machine is + * intentionally not safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the VM. It would be wasteful to acquire + * additional locks for the VM. + * + *

Implementation Status: The current implementation can + * execute pre-programs of fonts, but it does not yet actually move + * any points. Control flow and arithmeti instructions are + * implemented, but most geometric instructions are not working + * yet. So, the VirtualMachine class is currently a no-op. However, + * not very much is missing. You are more than welcome to complete the + * implementation. + * + *

Patents: Apple Computer holds three United States Patents + * for the mathematical algorithms that are used by TrueType + * instructions. The monopoly granted by these patents will expire in + * October 2009. Before the expiration date, a license must be + * obtained from Apple Computer to use the patented technology inside + * the United States. For other countries, different dates might + * apply, or no license might be needed. + * + *

The default build of this class does not use the patented + * algorithms. If you have obtained a license from Apple, or if the + * patent protection has expired, or if no license is required for + * your contry, you can set a flag in the source file which will + * enable the use of the patented mathematical algorithms.

+ * + *

The relevant patents are listed subsequently.

+ * + *

  1. United States Patent 5155805, Method and Apparatus + * for Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices, invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 13, + * 1992.
  2. + * + *
  3. United States Patent 5159668, Method and Apparatus for + * Manipulating Outlines in Improving Digital Typeface on Raster + * Output Devices, invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 27, + * 1992.
  4. + * + *
  5. United States Patent 5325479, Method and Apparatus for + * Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices, invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 28, 1989. Date of patent: June 28, 1994 + * (with a statement that “[t]he portion of the term of this + * patent subsequent to Oct. 13, 2009 has been + * disclaimed”).
+ * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +class VirtualMachine +{ + /** + * Indicates whether or not to perform hinting operations that are + * protected by a number of US patents, two of which will expire on + * October 13, 2009, and one of which will expire on October 27, + * 2009. + */ + private final static boolean PATENTED_HINTING = false; + + + /** + * Indicates whether the execution of the Virtual Machine is traced + * to System.out. + */ + private final static boolean TRACE_EXECUTION = false; + + + /** + * The value 1 in 2-dot-14 fixed notation. + */ + private static final short ONE_214 = 0x4000; // 1 << 14 + + + /** + * The storage area of the virtual machine. + */ + private final int[] storage; + + + /** + * The stack. The stack grows from bottom to top, so + * sp[0] gets used before sp[1]. + */ + private int[] stack; + + + /** + * The maximum number of stack elements. + */ + private final int maxStackElements; + + + /** + * The current stack pointer of the virtual machine. + */ + private int sp; + + + /** + * fdefBuffer[i] is the buffer that contains the TrueType + * instructions of function #i. Most of the time, functions are + * defined in the font program, but a font may also re-define + * functions in its CVT program. + */ + private ByteBuffer[] fdefBuffer; + + + /** + * fdefEntryPoint[i] is the position in fdefBuffer[i] where the + * first TrueType instruction after the FDEF is located. + */ + private int[] fdefEntryPoint; + + + /** + * The original Control Value Table, sometimes abbreviated as CVT. + * The table contains signed 16-bit FUnits. Some fonts have no CVT, + * in which case the field will be null. + */ + private ShortBuffer controlValueTable; + + + /** + * The scaled values inside the control value table. + */ + private int[] cvt; + + + /** + * A value that is used by rounding operations to compensate for dot + * gain. + */ + private int engineCompensation = 0; + + + /** + * The contents of the font’s fpgm table, or + * null after the font program has been executed once. + */ + private ByteBuffer fontProgram; + + + /** + * The prep table of the font, which contains a program + * that is executed whenever the point size or the device transform + * have changed. This program is called pre-program because it gets + * executed before the instructions of the individual glyphs. If + * the font does not contain a pre-program, the value of this field + * is null. + */ + private ByteBuffer preProgram; + + + /** + * The number of points in the Twilight Zone. + */ + private int numTwilightPoints; + + + /** + * The current point size of the scaled font. The value is in Fixed + * 26.6 notation. + */ + private int pointSize; // 26.6 + + private AffineTransform deviceTransform; + + private int scaleX, scaleY, shearX, shearY; // 26.6 + + + /** + * Indicates whether or not scan-line conversion will use + * anti-aliasing (with gray levels). Font programs can ask for this + * value with the GETINFO instruction, and some + * programs may behave differently according to this setting. + */ + private boolean antialiased; + + + /* Graphics State. FIXME: Move this to its own class? Some + * documentation would not hurt, either. + */ + private int cvtCutIn; // 26.6 + private int deltaBase; // uint32 + private int deltaShift; // uint32 + private short freeX; // 2.14 + private short freeY; // 2.14 + private int loop; // int + private int minimumDistance; // 26.6 + private short projX; // 2.14 + private short projY; // 2.14 + private short dualX; // 2.14 + private short dualY; // 2.14 + private int rp0, rp1, rp2; // point numbers + private boolean scanControl; + private int scanType; + private int singleWidthValue; // 26.6 + private Zone zp0, zp1, zp2; + + private Zone twilightZone; + private Zone glyphZone; + + + /** + * Indicates whether or not the instructions that are associated + * with individual glyphs shall be executed. Set as a side effect + * of executing the pre-program when the point size, device + * transform or some other relevant parameter have changed. + */ + private boolean executeGlyphInstructions; + + + /** + * Indicates whether to ignore any modifications to the control + * value table that the font’s pre-program might have + * performed. Set as a side effect of executing the pre-program + * when the point size, device transform or some other relevant + * parameter have changed. + */ + private boolean ignoreCVTProgram; + + + /** + * The length of the space between rounded values. A value + * of zero means that rounding has been switched off. + */ + private int roundPeriod; // 26.6 + + + /** + * The offset of the rounded values from multiples of + * roundPeriod. + */ + private int roundPhase; // 26.6 + + + private int roundThreshold; // 26.6 + + + /** + * A cache for the number of pixels per EM. The value is a normal + * integer, not a fixed point notation. + * + * @see #getPixelsPerEM() + */ + private int cachedPixelsPerEM; + + + /** + * The number of font units per EM. + */ + private int unitsPerEm; + + + /** + * Constructs a new Virtual Machine for executing TrueType + * instructions. + * + * @param unitsPerEm the number of font units in one typographic + * em. + * + * @param preProgram the prep table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass null. + */ + VirtualMachine(int unitsPerEm, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fontProgram, + ByteBuffer preProgram) + throws FontFormatException + { + int maxStorage, numFunctionDefs, maxInstructionDefs; + + if (maxp.getInt(0) != 0x00010000) + throw new FontFormatException("unsupported maxp version"); + + this.unitsPerEm = unitsPerEm; + maxStorage = maxp.getChar(18); + + /* FreeType says that there exist some broken fonts (like + * "Keystrokes MT") that contain function defs, but have a zero + * value in their maxp table. + */ + numFunctionDefs = maxp.getChar(20); + if (numFunctionDefs == 0) + numFunctionDefs = 64; + fdefBuffer = new ByteBuffer[numFunctionDefs]; + fdefEntryPoint = new int[numFunctionDefs]; + + /* Read the contents of the Control Value Table. */ + if (controlValueTable != null) + this.controlValueTable = controlValueTable.asShortBuffer(); + + maxInstructionDefs = maxp.getChar(22); + maxStackElements = maxp.getChar(24); + storage = new int[maxStorage]; + this.fontProgram = fontProgram; + this.preProgram = preProgram; + numTwilightPoints = maxp.getChar(16); + } + + + /** + * Sets the graphics state to default values. + */ + private void resetGraphicsState() + { + /* The freedom, projection and dual vector default to the x axis. */ + freeX = projX = dualX = ONE_214; + freeY = projY = dualX = 0; + cachedPixelsPerEM = 0; + + cvtCutIn = 68; // 17/16 in 26.6 notation + deltaBase = 9; + deltaShift = 3; + loop = 1; + minimumDistance = Fixed.ONE; + singleWidthValue = 0; + rp0 = rp1 = rp2 = 0; + scanControl = false; + scanType = 2; + zp0 = zp1 = zp2 = getZone(1); + + setRoundingMode(Fixed.ONE, 0x48); // round to grid + } + + + /** + * Reloads the control value table and scales each entry from font + * units to pixel values. + */ + private void reloadControlValueTable() + { + /* Some TrueType fonts have no control value table. */ + if (controlValueTable == null) + return; + + /* Read in the Control Value Table. */ + if (cvt == null) + cvt = new int[controlValueTable.capacity()]; + + /* Scale the entries. */ + for (int i = 0; i < cvt.length; i++) + cvt[i] = funitsToPixels(controlValueTable.get(i)); + } + + + /** + * Scales a value from font unites to pixels. + * + * @return the scaled value. + */ + private int funitsToPixels(int funits) + { + return (int) (((long) funits * scaleY + (unitsPerEm>>1)) + / unitsPerEm); + } + + + /** + * Sets up the virtual machine for the specified parameters. If + * there is no change to the last set-up, the method will quickly + * return. Otherwise, the font’s pre-program will be + * executed. + * + * @param pointSize the point size of the scaled font. + * + * @param deviceTransform an affine transformation which gets + * applied in addition to scaling by pointSize. Font + * programs can separately inquire about the point size. For this + * reason, it is not recommended to pre-multiply the point size to + * the device transformation. + * + * @param antialiased true if the scan-line conversion + * algorithm will use gray levels to give a smoother appearance, + * false otherwise. Font programs can ask for this + * value with the GETINFO instruction, and some + * programs may behave differently according to this setting. + */ + public boolean setup(double pointSize, + AffineTransform deviceTransform, + boolean antialiased) + { + boolean changeCTM; + int pointSize_Fixed; + + if (stack == null) + stack = new int[maxStackElements]; + + if (twilightZone == null) + twilightZone = new Zone(numTwilightPoints); + + /* If the font program has not yet been executed, do so. */ + if (fontProgram != null) + { + resetGraphicsState(); + sp = -1; + execute(fontProgram, 0); + fontProgram = null; // prevent further execution + } + + /* Determine whether the transformation matrix has changed. */ + pointSize_Fixed = Fixed.valueOf(pointSize); + changeCTM = ((pointSize_Fixed != this.pointSize) + || !deviceTransform.equals(this.deviceTransform) + || (antialiased != this.antialiased)); + + if (changeCTM) + { + this.pointSize = pointSize_Fixed; + this.deviceTransform = deviceTransform; + this.antialiased = antialiased; + scaleX = (int) (deviceTransform.getScaleX() * pointSize * 64); + scaleY = (int) (deviceTransform.getScaleY() * pointSize * 64); + shearX = (int) (deviceTransform.getShearX() * pointSize * 64); + shearY = (int) (deviceTransform.getShearY() * pointSize * 64); + + resetGraphicsState(); + reloadControlValueTable(); + executeGlyphInstructions = true; + ignoreCVTProgram = false; + + if (preProgram != null) + { + sp = -1; + execute(preProgram, 0); + if (ignoreCVTProgram) + reloadControlValueTable(); + } + } + + return executeGlyphInstructions; + } + + + /** + * Executes a stream of TrueType instructions. + */ + private void execute(ByteBuffer instructions, int pos) + { + instructions.position(pos); + + // FIXME: SECURITY: Possible denial-of-service attack + // via instructions that have an endless loop. + while (instructions.hasRemaining() + && executeInstruction(instructions)) + ; + } + + + /** + * Writes a textual description of the current TrueType instruction, + * including the top stack elements, to System.out. + * This is useful for debugging. + * + * @param inst the instruction stream, positioned at the current + * instruction. + */ + private void dumpInstruction(ByteBuffer inst) + { + CPStringBuilder sbuf = new CPStringBuilder(40); + int pc = inst.position(); + int bcode = inst.get(pc) & 0xff; + int count; + int delta; + + char pcPrefix = 'c'; + for (int i = 0; i < fdefBuffer.length; i++) + { + if (fdefBuffer[i] == inst) + { + pcPrefix = 'f'; + break; + } + } + sbuf.append(pcPrefix); + + + sbuf.append(getHex((short) inst.position())); + sbuf.append(": "); + sbuf.append(getHex((byte) bcode)); + sbuf.append(" "); + sbuf.append(INST_NAME[bcode]); + + if (bcode == 0x40) // NPUSHB + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(" "); + sbuf.append('$'); + sbuf.append(getHex(inst.get(pc + 2 + i))); + } + } + if (bcode == 0x41) // NPUSHW + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(' '); + sbuf.append('$'); + sbuf.append(getHex(inst.getShort(pc + 2 + 2*i))); + } + } + else + { + count = getInstructionLength(bcode) - 1; + for (int i = 0; i < count; i++) + { + sbuf.append(" $"); + sbuf.append(getHex(inst.get(pc + 1 + i))); + } + } + + while (sbuf.length() < 30) + sbuf.append(' '); + sbuf.append('|'); + sbuf.append(sp + 1); + sbuf.append("| "); + for (int i = sp; i >= Math.max(0, sp - 5); i = i - 1) + { + if (i < sp) + sbuf.append(" "); + if ((stack[i] >> 16) != 0) + sbuf.append(getHex((short) (stack[i] >> 16))); + sbuf.append(getHex((short) stack[i])); + } + System.out.println(sbuf); + } + + + private static char getNibble(int i, int rightShift) + { + i = (i >> rightShift) & 15; + if (i < 10) + return (char) (i + '0'); + else + return (char) (i + 'a' - 10); + } + + + private static String getHex(byte b) + { + char[] a = new char[2]; + a[0] = getNibble(b, 4); + a[1] = getNibble(b, 0); + return new String(a); + } + + + private static String getHex(short b) + { + char[] a = new char[4]; + a[0] = getNibble(b, 12); + a[1] = getNibble(b, 8); + a[2] = getNibble(b, 4); + a[3] = getNibble(b, 0); + return new String(a); + } + + + /** + * Skips any instructions until the specified opcode has been + * encoutered. + * + * @param inst the current instruction stream. After the call, + * the position of inst is right after the first + * occurence of opcode. + * + * @param opcode1 the opcode for which to look. + * + * @param opcode2 another opcode for which to look. Pass -1 + * if only opcode1 would terminate skipping. + * + * @param illegalCode1 an opcode that must not be encountered + * while skipping. Pass -1 if any opcode is acceptable. + * + * @param illegalCode2 another opcode that must not be encountered + * while skipping. Pass -1 to perform no check. + * + * @param handleNestedIfClauses true to handle + * nested IF [ELSE] EIF clauses, false + * to ignore them. From the TrueType specification document, + * one would think that nested if clauses would not be valid, + * but they do appear in some fonts. + * + * @throws IllegalStateException if illegalCode1 or + * illegalCode2 has been encountered while skipping. + */ + private static void skipAfter(ByteBuffer inst, + int opcode1, int opcode2, + int illegalCode1, int illegalCode2, + boolean handleNestedIfClauses) + { + int pos = inst.position(); + int curOpcode; + int instLen; + int nestingLevel = 0; // increased inside IF [ELSE] EIF sequences + + while (true) + { + curOpcode = inst.get(pos) & 0xff; + instLen = getInstructionLength(curOpcode); + + if (false && TRACE_EXECUTION) + { + for (int i = 0; i < nestingLevel; i++) + System.out.print("--"); + System.out.print("--" + pos + "-" + INST_NAME[curOpcode]); + if (nestingLevel > 0) + System.out.print(", ifNestingLevel=" + nestingLevel); + System.out.println(); + } + + if (curOpcode == 0x40) // NPUSHB + pos += 1 + (inst.get(pos + 1) & 0xff); + else if (curOpcode == 0x41) // NPUSHW + pos += 1 + 2 * (inst.get(pos + 1) & 0xff); + else + pos += instLen; + + if ((nestingLevel == 0) + && ((curOpcode == opcode1) || (curOpcode == opcode2))) + break; + + if (handleNestedIfClauses) + { + if (curOpcode == /* IF */ 0x58) + ++nestingLevel; + else if (curOpcode == /* EIF */ 0x59) + --nestingLevel; + } + + if ((nestingLevel < 0) + || (curOpcode == illegalCode1) + || (curOpcode == illegalCode2)) + throw new IllegalStateException(); + } + + inst.position(pos); + } + + + /** + * Returns the number of bytes that a TrueType instruction occupies. + * + * @param opcode the instruction. + * + * @return the number of bytes occupied by the instructions and its + * operands. For NPUSHB and NPUSHW, where + * the instruction length depends on the first operand byte, the + * result is -1. + */ + private static int getInstructionLength(int opcode) + { + /* NPUSHB, NPUSHW --> see following byte */ + if ((opcode == 0x40) || (opcode == 0x41)) + return -1; + + /* PUSHB[0] .. PUSHB[7] --> 2, 3, 4, 5, 6, 7, 8, 9 */ + if ((opcode >= 0xb0) && (opcode <= 0xb7)) + return opcode - 0xae; + + /* PUSHW[0] .. PUSHW[7] --> 3, 5, 6, 7, 11, 13, 15, 17*/ + if ((opcode >= 0xb8) && (opcode <= 0xbf)) + return 1 + ((opcode - 0xb7) << 1); + + return 1; + } + + + /** + * Executes a single TrueType instruction. This is the core + * routine of the Virtual Machine. + * + * @return true if another instruction shall be + * executed in the same call frame; false if the + * current call frame shall be popped. + */ + private boolean executeInstruction(ByteBuffer inst) + { + if (TRACE_EXECUTION) + dumpInstruction(inst); + + int i, count, e1, e2, e3, e4, x, y; + int bcode = inst.get() & 0xff; + + switch (bcode) + { + case 0x00: // SVTCA[0], Set freedom and proj. Vectors To Coord. Axis [y] + setFreedomVector((short) 0, ONE_214); + setProjectionVector((short) 0, ONE_214); + break; + + case 0x01: // SVTCA[1], Set freedom and proj. Vectors To Coord. Axis [x] + setFreedomVector(ONE_214, (short) 0); + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x02: // SPVTCA[0], Set Projection Vector To Coordinate Axis [y] + setProjectionVector((short) 0, ONE_214); + break; + + case 0x03: // SPVTCA[1], Set Projection Vector To Coordinate Axis [x] + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x0c: // GPV, Get Projection Vector + stack[++sp] = projX; + stack[++sp] = projY; + break; + + case 0x0d: // GPV, Get Freedom Vector + stack[++sp] = freeX; + stack[++sp] = freeY; + break; + + case 0x0F: // ISECT, move point p to the InterSECTION of two lines + sp -= 4; + handleISECT(stack[sp], stack[sp+1], stack[sp+2], + stack[sp+3], stack[sp+4]); + break; + + case 0x10: // SRP0, Set Reference Point 0 + rp0 = stack[sp--]; + break; + + case 0x11: // SRP1, Set Reference Point 1 + rp1 = stack[sp--]; + break; + + case 0x12: // SRP2, Set Reference Point 2 + rp2 = stack[sp--]; + break; + + case 0x13: // SZP0, Set Zone Pointer 0 + zp0 = getZone(stack[sp--]); + break; + + case 0x14: // SZP1, Set Zone Pointer 1 + zp1 = getZone(stack[sp--]); + break; + + case 0x15: // SZP2, Set Zone Pointer 2 + zp2 = getZone(stack[sp--]); + break; + + case 0x16: // SZPS, Set Zone PointerS + zp0 = zp1 = zp2 = getZone(stack[sp--]); + break; + + case 0x17: // SLOOP, Set LOOP variable + loop = stack[sp--]; + break; + + case 0x18: // RTG, Round To Grid + setRoundingMode(Fixed.ONE, 0x48); + break; + + case 0x19: // RTHG, Round To Half Grid + setRoundingMode(Fixed.ONE, 0x68); + break; + + case 0x1a: // SMD, Set Minimum Distance + minimumDistance = stack[sp--]; + break; + + case 0x1B: // ELSE, ELSE clause + skipAfter(inst, + /* look for: EIF, -- */ 0x59, -1, + /* illegal: --, -- */ -1, -1, + /* handle nested if clauses */ true); + break; + + case 0x1C: // JMPR, JuMP Relative + inst.position(inst.position() - 1 + stack[sp--]); + break; + + case 0x1D: // SCVTCI, Set Control Value Table Cut-In + cvtCutIn = stack[sp--]; + break; + + case 0x1F: // SSW, Set Single Width + singleWidthValue = stack[sp--]; + break; + + case 0x20: // DUP, DUPlicate top stack element + e1 = stack[sp]; + stack[++sp] = e1; + break; + + case 0x21: // POP, POP top stack element + sp--; + break; + + case 0x22: // CLEAR, CLEAR the stack + sp = -1; + break; + + case 0x23: // SWAP, SWAP the top two elements on the stack + e1 = stack[sp--]; + e2 = stack[sp]; + stack[sp] = e1; + stack[++sp] = e2; + break; + + case 0x24: // DEPTH, DEPTH of the stack + stack[++sp] = sp + 1; + break; + + case 0x25: // CINDEX, Copy the INDEXed element to the top of the stack + stack[sp] = stack[sp - stack[sp]]; + break; + + case 0x26: // MINDEX, Move the INDEXed element to the top of the stack + i = stack[sp]; + e1 = stack[sp - i]; + System.arraycopy(/* src */ stack, /* srcPos */ sp - i + 1, + /* dest */ stack, /* destPos*/ sp - i, + /* length */ i - 1); + --sp; + stack[sp] = e1; + break; + + case 0x2a: // LOOPCALL, LOOP and CALL function + i = stack[sp--]; + count = stack[sp--]; + e1 = inst.position(); + e2 = sp; + for (int j = 0; j < count; j++) + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2B: // CALL, CALL function + i = stack[sp--]; + e1 = inst.position(); + e2 = sp; + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2C: // FDEF, Function DEFinition + i = stack[sp--]; + fdefBuffer[i] = inst; + fdefEntryPoint[i] = inst.position(); + skipAfter(inst, + /* look for: ENDF */ 0x2d, + /* look for: --- */ -1, + /* illegal: IDEF */ 0x89, + /* illegal: FDEF */ 0x2c, + /* do not handle nested if clauses */ false); + break; + + case 0x2D: // ENDF, END Function definition + /* Pop the current stack frame. */ + return false; + + case 0x2e: // MDAP[0], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ false); + break; + + case 0x2f: // MDAP[1], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ true); + break; + + case 0x39: // IP, Interpolate Point by the last relative stretch + handleIP(); + break; + + case 0x3d: // RTDG, Round To Double Grid + setRoundingMode(Fixed.ONE, 0x08); + roundThreshold = roundThreshold / 64; // period/128 + break; + + case 0x3e: // MIAP[0], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ false); + break; + + case 0x3f: // MIAP[1], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ true); + break; + + case 0x40: // NPUSHB + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0x41: // NPUSHW + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + case 0x42: // WS, Write Store + e1 = stack[sp--]; i = stack[sp--]; + storage[i] = e1; + break; + + case 0x43: // RS, Read Store + stack[sp] = storage[stack[sp]]; + break; + + case 0x44: // WCVTP, Write Control Value Table in Pixel units + e1 = stack[sp--]; + i = stack[sp--]; + if (i < cvt.length) + cvt[i] = e1; + break; + + case 0x45: // RCVT, Read Control Value Table entry + if (stack[sp] < cvt.length) + stack[sp] = cvt[stack[sp]]; + else + stack[sp] = 0; + break; + + case 0x46: // GC[0], Get Coordinate projected onto the projection vector + stack[sp] = getProjection(zp2, stack[sp]); + break; + + case 0x47: // GC[1], Get Coordinate projected onto the projection vector + stack[sp] = getOriginalProjection(zp2, stack[sp]); + break; + + case 0x4B: // MPPEM, Measure Pixels Per EM + stack[++sp] = getPixelsPerEM(); + break; + + case 0x4c: // MPS, Measure Point Size + /* FreeType2 returns pixels per em here, because they think that + * the point size would be irrelevant in a given font program. + * This is extremely surprising, because the appearance of good + * fonts _should_ change with point size. For example, a good + * font should be wider at small point sizes, and the holes + * inside glyphs ("Punzen" in German, I do not know the correct + * English expression) should be larger. Note that this change + * of appearance is dependent on point size, _not_ the + * resolution of the display device. + */ + stack[++sp] = pointSize; + break; + + case 0x4f: // DEBUG, DEBUG call + sp--; + break; + + case 0x50: // LT, Less Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] < e1) ? 1 : 0; + break; + + case 0x51: // LTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] <= e1) ? 1 : 0; + break; + + case 0x52: // GT, Greater Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] > e1) ? 1 : 0; + break; + + case 0x53: // GTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] >= e1) ? 1 : 0; + break; + + case 0x54: // EQ, EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] == e1) ? 1 : 0; + break; + + case 0x55: // NEQ, Not EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] != e1) ? 1 : 0; + break; + + case 0x58: // IF, IF test + if (stack[sp--] == 0) + skipAfter(inst, + /* look for: ELSE */ 0x1B, + /* look for: EIF */ 0x59, + /* illegal: -- */ -1, + /* illegal: -- */ -1, + /* handle nested if clauses */ true); + break; + + case 0x59: // EIF, End IF + // Do nothing. + break; + + case 0x5A: // AND + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) && (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5B: // OR + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) || (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5C: // NOT + stack[sp] = (stack[sp] != 0) ? 0 : 1; + break; + + case 0x5e: // SDB, Set Delta Base in the graphics state + deltaBase = stack[sp--]; + break; + + case 0x5f: // SDS, Set Delta Shift in the graphics state + deltaShift = stack[sp--]; + break; + + case 0x60: // ADD + e1 = stack[sp--]; + stack[sp] += e1; + break; + + case 0x61: // SUB, SUBtract + e1 = stack[sp--]; + stack[sp] -= e1; + break; + + case 0x62: // DIV, DIVide + e1 = stack[sp--]; + stack[sp] = Fixed.div(e1, stack[sp]); + break; + + case 0x63: // MUL, MULtiply + e1 = stack[sp--]; + stack[sp] = Fixed.mul(e1, stack[sp]); + break; + + case 0x64: // ABS, ABSolute value + stack[sp] = Math.abs(stack[sp]); + break; + + case 0x65: // NEG, NEGate + stack[sp] = -stack[sp]; + break; + + case 0x66: // FLOOR + stack[sp] = Fixed.floor(stack[sp]); + break; + + case 0x67: // CEILING + stack[sp] = Fixed.ceil(stack[sp]); + break; + + case 0x68: // ROUND[0] -- round grey distance + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x69: // ROUND[1] -- round black distance + stack[sp] = round(stack[sp], -engineCompensation); + break; + + case 0x6a: // ROUND[2] -- round white distance + stack[sp] = round(stack[sp], engineCompensation); + break; + + case 0x6b: // ROUND[3] -- round distance (not yet defined) + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x6c: // NROUND[0] -- compensate grey distance + stack[sp] = nround(stack[sp], 0); + break; + + case 0x6d: // NROUND[1] -- compensate black distance + stack[sp] = nround(stack[sp], -engineCompensation); + break; + + case 0x6e: // NROUND[2] -- compensate white distance + stack[sp] = nround(stack[sp], engineCompensation); + break; + + case 0x6f: // NROUND[3] -- compensate distance (not yet defined) + stack[sp] = nround(stack[sp], 0); + break; + + case 0x70: // WCVTF, Write Control Value Table in Funits + e1 = stack[sp--]; + cvt[stack[sp--]] = e1 * getPixelsPerEM(); + break; + + case 0x73: // DELTAC1, DELTA exception C1 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 0); + break; + + case 0x74: // DELTAC2, DELTA exception C2 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 16); + break; + + case 0x75: // DELTAC3, DELTA exception C3 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 32); + break; + + case 0x76: // SROUND, Super ROUND + setRoundingMode(Fixed.ONE, stack[sp--]); + break; + + case 0x77: // S45ROUND, Super ROUND 45 degrees + setRoundingMode(/* sqrt(2)/2 */ 0x2d, stack[sp--]); + break; + + case 0x78: // JROT, Jump Relative On True + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 != 0) + inst.position(i); + break; + + case 0x79: // JROF, Jump Relative On False + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 == 0) + inst.position(i); + break; + + case 0x7a: // ROFF, Round OFF + roundPeriod = 0; + break; + + case 0x7c: // RUTG, Round Up To Grid + setRoundingMode(Fixed.ONE, 0x40); + break; + + case 0x7d: // RDTG, Round Down To Grid + setRoundingMode(Fixed.ONE, 0x40); + roundThreshold = 0; + break; + + case 0x7e: // SANGW, Set ANGle Weight (no-op according to TrueType spec) + case 0x7f: // AA, Adjust Angle (no-op according to TrueType spec) + sp--; + break; + + case 0x85: // SCANCTRL, SCAN conversion ConTRoL + e1 = stack[sp--]; + int ppemThreshold = e1 & 255; + scanControl = false; + boolean ppemCondition = (ppemThreshold == 255) + || ((ppemThreshold != 0) && (getPixelsPerEM() > ppemThreshold)); + if (((e1 & (1<<8)) != 0) && ppemCondition) + scanControl = true; + if (((e1 & (1<<9)) != 0) && isRotated()) + scanControl = true; + if (((e1 & (1<<10)) != 0) && isStretched()) + scanControl = true; + if (((e1 & (1<<11)) != 0) && !ppemCondition) + scanControl = false; + if (((e1 & (1<<12)) != 0) && !isRotated()) + scanControl = false; + if (((e1 & (1<<13)) != 0) && !isStretched()) + scanControl = false; + break; + + case 0x88: // GETINFO, GET INFOrmation + e1 = 0; + if ((stack[sp] & 1) != 0) // ask for rasterizer version + e1 |= 35; // "Microsoft Rasterizer version 1.7" (grayscale-capable) + if (((stack[sp] & 2) != 0) && isRotated()) + e1 |= 1 << 8; // bit 8: glyph has been rotated + if (((stack[sp] & 4) != 0) && isStretched()) + e1 |= 1 << 9; // bit 9: glyph has been stretched + if (((stack[sp] & 32) != 0) && antialiased) + e1 |= 1 << 12; // bit 12: antialiasing is active + stack[sp] = e1; + break; + + case 0x8a: // ROLL, ROLL the top three stack elements + e1 = stack[sp - 2]; + stack[sp - 2] = stack[sp - 1]; + stack[sp - 1] = stack[sp]; + stack[sp] = e1; + break; + + case 0x8b: // MAX, MAXimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.max(e1, stack[sp]); + break; + + case 0x8c: // MIN, MINimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.min(e1, stack[sp]); + break; + + case 0x8d: // SCANTYPE + scanType = stack[sp--]; + break; + + case 0x8e: // INSTCTRL, INSTRuction execution ConTRoL + e1 = stack[sp--]; // selector + e2 = stack[sp--]; // value + switch (e1) + { + case 1: + executeGlyphInstructions = (e2 == 0); + break; + + case 2: + ignoreCVTProgram = (e2 != 0); + break; + } + break; + + case 0xb0: // PUSHB[0] + case 0xb1: // PUSHB[1] + case 0xb2: // PUSHB[2] + case 0xb3: // PUSHB[3] + case 0xb4: // PUSHB[4] + case 0xb5: // PUSHB[5] + case 0xb6: // PUSHB[6] + case 0xb7: // PUSHB[7] + count = bcode - 0xb0 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0xb8: // PUSHW[0] + case 0xb9: // PUSHW[1] + case 0xba: // PUSHW[2] + case 0xbb: // PUSHW[3] + case 0xbc: // PUSHW[4] + case 0xbd: // PUSHW[5] + case 0xbe: // PUSHW[6] + case 0xbf: // PUSHW[7] + count = bcode - 0xb8 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + // MIRPxxxx, Move Indirect Relative Point + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + e1 = stack[sp--]; + handleMIRP(bcode, /* point */ e1, /* cvtIndex */ stack[sp--]); + break; + + default: + throw new IllegalStateException(); + } + + return true; + } + + + /** + * Sets the rounding mode. + * + * @param period the grid period in fixed-point notation, such as + * {@link Fixed#ONE} for the SROUND instruction or + * sqrt(2)/2 for the S45ROUND instruction. + * + * @param mode a byte whose bits are set according to the TrueType + * specification for SROUND and S45ROUND parameters. + */ + private void setRoundingMode(int period, int mode) + { + /* Set the period. */ + switch ((mode & 0xc0) >> 6) + { + case 0: + roundPeriod = period / 2; + break; + + case 2: + roundPeriod = period * 2; + break; + + default: + roundPeriod = period; + break; + } + + /* Set the phase. */ + switch ((mode & 0x30) >> 4) + { + case 0: + roundPhase = 0; + break; + + case 1: + roundPhase = roundPeriod >> 2; // period/4 + break; + + case 2: + roundPhase = roundPeriod >> 1; // period/2 + break; + + case 3: + roundPhase = (roundPeriod >> 1) + (roundPeriod >> 2); // period * 3/4 + break; + } + + /* Set the threshold. */ + int threshold = mode & 0x0f; + if (threshold == 0) + roundThreshold = roundPeriod - Fixed.ONE; + else + roundThreshold = ((threshold - 4) * roundPeriod) / 8; + } + + + + /** + * Implements the DELTAC instructions. These instructions check + * whether the current number of pixels per em is contained in an + * exception table. If it is, a delta value is determined, and the + * specified entry in the Control Value Table is modified according + * to the delta. + * + * @param pairs the delta table. Because the delta table is on + * the stack, callers usually just want to pass the stack array. + * + * @param offset the offset of the first pair in pairs. + * + * @param numPairs the number of pairs. + * + * @param base 0 for DELTAC1, 16 for DELTAC2, + * or 32 for DELTAC2. + * + * @see Apple’s documentation for DELTAC1, DELTAC2, and DELTAC3 + */ + private void deltaC(int[] pairs, int offset, int numPairs, int base) + { + int arg, relativePpem; + int ppemTrigger = getPixelsPerEM() - (deltaBase + base); + int delta, cvtIndex, rightShift; + for (int i = 0; i < numPairs; i++) + { + arg = pairs[offset + 2 * i]; + relativePpem = (arg >> 4) & 15; + if (relativePpem == ppemTrigger) + { + delta = (arg & 15) - 8; + if (delta >= 0) + ++delta; + + rightShift = deltaShift - 6; + if (rightShift > 0) + delta = delta >> rightShift; + else if (rightShift < 0) + delta = delta << (-rightShift); + cvt[pairs[offset + 2 * i + 1]] += delta; + + break; + } + } + } + + + private Zone getZone(int zoneNumber) + { + return (zoneNumber == 0) ? twilightZone : glyphZone; + } + + + /** + * Projects the specified vector along the current projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getProjection(int x, int y) + { + return (int) (((((long) x) * projX + ((long) y) * projY)) >> 14); + } + + + /** + * Projects the specified vector along the current dual projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getDualProjection(int x, int y) + { + return (int) (((((long) x) * dualX + ((long) y) * dualY)) >> 14); + } + + + private int getProjection(Zone zone, int point) + { + return getProjection(zone.getX(point), zone.getY(point)); + } + + + private int getOriginalProjection(Zone zone, int point) + { + return getDualProjection(zone.getOriginalX(point), + zone.getOriginalY(point)); + } + + + private void handleISECT(int a0, int a1, int b0, int b1, int p) + { + System.out.println("FIXME: Unimplemented ISECT " + p); + } + + + private static int muldiv(int a, int b, int c) + { + int s; + s = a; a = Math.abs(a); + s ^= b; b = Math.abs(b); + s ^= c; c = Math.abs(c); + a = (int) ((((long) a) * b + (c>>1)) / c); + return (s < 0) ? -a : a; + } + + + private int getFreeDotProj() + { + int result; + + result = ((((int) projX) * freeX) << 2) + + ((((int) projY) * freeY) << 2); + + /* FIXME: This seems somewhat bogus. Need to contact the + * developers of FreeType. + */ + if (Math.abs(result) < 0x4000000) + result = 0x40000000; + return result; + } + + + private void movePoint(Zone zone, int point, int distance) + { + int freeDotProj = getFreeDotProj(); + int c; + + if (freeX != 0) + { + c = zone.getX(point); + c += muldiv(distance, freeX << 16, freeDotProj); + zone.setX(point, c, /* touch */ true); + } + + if (freeY != 0) + { + c = zone.getY(point); + c += muldiv(distance, freeY << 16, freeDotProj); + zone.setY(point, c, /* touch */ true); + } + + if (TRACE_EXECUTION) + { + System.out.println("point[" + point + "] moved to " + + Fixed.toString(zone.getX(point), + zone.getY(point))); + dumpVectors(); + } + } + + private void dumpVectors() + { + System.out.println(" proj=" + Fixed.toString(projX>>8, projY>>8) + + ", free=" + Fixed.toString(freeX>>8, freeY>>8)); + } + + + private void handleIP() + { + // Implementation taken from FreeType. + int p, org_a, org_b, org_x, cur_a, cur_b, cur_x, distance; + int freeDotProj; + + org_a = getOriginalProjection(zp0, rp1); + cur_a = getProjection(zp0, rp1); + + org_b = getOriginalProjection(zp1, rp2); + cur_b = getProjection(zp1, rp2); + + while (--loop >= 0) + { + p = stack[sp--]; + org_x = getOriginalProjection(zp2, p); + cur_x = getProjection(zp2, p); + + if (((org_a <= org_b) && (org_x <= org_a)) + || ((org_a > org_b) && (org_x >= org_a))) + distance = (cur_a - org_a) + (org_x - cur_x); + else if (((org_a <= org_b) && (org_x >= org_b)) + || ((org_a > org_b) && (org_x < org_b))) + distance = (cur_b - org_b) + (org_x - cur_x); + else + distance = muldiv(cur_b - cur_a, org_x - org_a, org_b - org_a) + + (cur_a - cur_x); + movePoint(zp2, p, distance); + } + loop = 1; + } + + + private void handleMDAP(int point, boolean round) + { + System.out.println("FIXME: Unimplemented MDAP: point " + + point + "/" + zp0); + } + + + private void handleMIAP(int cvtIndex, int point, boolean round) + { + int previousPos, pos; + + previousPos = getProjection(zp0, point); + pos = cvt[cvtIndex]; + + if (round) + { + if (Math.abs(pos - previousPos) > cvtCutIn) + pos = previousPos; + pos = round(pos, /* no engine compensation */ 0); + } + movePoint(zp0, point, pos - previousPos); + rp0 = rp1 = point; + } + + + private void handleMIRP(int bcode, int point, int cvtIndex) + { + System.out.println("FIXME: Unimplemented mirp " + point + ", " + cvtIndex); + } + + + + private int round(int distance, int compensation) + { + int result; + + if (roundPeriod == 0) + return nround(distance, compensation); + + if (distance >= 0) + { + result = distance + compensation - roundPhase + roundThreshold; + result &= -roundPeriod; // truncate to the next lowest periodic value + return Math.max(result, 0) + roundPhase; + } + else + { + result = compensation - roundPhase + roundThreshold - distance; + result &= -roundPeriod; + return Math.max(-result, 0) - roundPhase; + } + } + + + private static int nround(int distance, int compensation) + { + if (distance >= 0) + return Math.max(distance + compensation, 0); + else + return Math.min(distance - compensation, 0); + } + + + /** + * Determines whether the current glyph is rotated. + * + * @return false if the shearing factors for the + * x and y axes are zero; true if they + * are non-zero. + */ + private boolean isRotated() + { + return (shearX != 0) || (shearY != 0); + } + + + /** + * Determines whether the current glyph is stretched. + * + * @return false if the scaling factors for the + * x and y axes are are equal; true if + * they differ. + */ + private boolean isStretched() + { + return scaleX != scaleY; + } + + + /** + * Returns how many pixels there are per EM, in direction of the + * current projection vector. The result is a normal integer, + * not a Fixed. + */ + private int getPixelsPerEM() + { + if (cachedPixelsPerEM == 0) + { + cachedPixelsPerEM = Fixed.intValue(Fixed.vectorLength( + applyCTM_x(projX >> 8, projY >> 8), + applyCTM_y(projX >> 8, projY >> 8))); + } + + return cachedPixelsPerEM; + } + + + private void setProjectionVector(short x, short y) + { + if (PATENTED_HINTING) + { + if ((x != projX) || (y != projY)) + cachedPixelsPerEM = 0; + + projX = x; + projY = y; + } + } + + + private void setFreedomVector(short x, short y) + { + if (PATENTED_HINTING) + { + freeX = x; + freeY = y; + } + } + + + private void setDualVector(short x, short y) + { + if (PATENTED_HINTING) + { + dualX = x; + dualY = y; + } + } + + + private int applyCTM_x(int x, int y) + { + return (int) (((long) scaleX * x + (long) shearX * y) >> 6); + } + + private int applyCTM_y(int x, int y) + { + return (int) (((long) shearY * x + (long) scaleY * y) >> 6); + } + + + private static final String[] INST_NAME = + { + /* 00 */ "SVTCA[0]", "SVTCA[1]", "SPVTCA[0]", "SPVTCA[1]", + /* 04 */ "INST_04", "INST_05", "INST_06", "INST_07", + /* 08 */ "INST_08", "INST_09", "INST_0A", "INST_0B", + /* 0c */ "GPV", "GFV", "INST_0E", "ISECT", + /* 10 */ "SRP0", "SRP1", "SRP2", "SZP0", + /* 14 */ "SZP1", "SZP2", "SZPS", "SLOOP", + /* 18 */ "RTG", "RTHG", "SMD", "ELSE", + /* 1c */ "JMPR", "SCVTCI", "INST_1E", "SSW", + /* 20 */ "DUP", "POP", "CLEAR", "SWAP", + /* 24 */ "DEPTH", "CINDEX", "MINDEX", "INST_27", + /* 28 */ "INST_28", "INST_29", "LOOPCALL", "CALL", + /* 2c */ "FDEF", "ENDF", "MDAP[0]", "MDAP[1]", + /* 30 */ "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]", + /* 34 */ "INST_34", "INST_35", "INST_36", "INST_37", + /* 38 */ "INST_38", "IP", "INST_3A", "INST_3B", + /* 3c */ "INST_3C", "RTDG", "MIAP[0]", "MIAP[1]", + /* 40 */ "NPUSHB", "NPUSHW", "WS", "RS", + /* 44 */ "WCVTP", "RCVT", "GC[0]", "GC[1]", + /* 48 */ "INST_48", "INST_49", "INST_4A", "MPPEM", + /* 4c */ "MPS", "FLIPON", "FLIPOFF", "DEBUG", + /* 50 */ "LT", "LTEQ", "GT", "GTEQ", + /* 54 */ "EQ", "NEQ", "INST_56", "INST_57", + /* 58 */ "IF", "EIF", "AND", "OR", + /* 5c */ "NOT", "INST_5D", "SDB", "SDS", + /* 60 */ "ADD", "SUB", "DIV", "MUL", + /* 64 */ "ABS", "NEG", "FLOOR", "CEILING", + /* 68 */ "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]", + /* 6c */ "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]", + /* 70 */ "WCVTF", "INST_71", "INST_72", "DELTAC1", + /* 74 */ "DELTAC2", "DELTAC3", "SROUND", "S45ROUND", + /* 78 */ "JROT", "JROF", "ROFF", "INST_7B", + /* 7c */ "RUTG", "RDTG", "SANGW", "AA", + /* 80 */ "FLIPPT", "FLIPRGON", "FLIPRGOFF", "INST_83", + /* 84 */ "INST_84", "SCANCTRL", "INST_86", "INST_87", + /* 88 */ "GETINFO", "INST_89", "ROLL", "MAX", + /* 8c */ "MIN", "SCANTYPE", "INSTCTRL", "INST_8F", + /* 90 */ "INST_90", "INST_91", "INST_92", "INST_93", + /* 94 */ "INST_94", "INST_95", "INST_96", "INST_97", + /* 98 */ "INST_98", "INST_99", "INST_9A", "INST_9B", + /* 9c */ "INST_9C", "INST_9D", "INST_9E", "INST_9F", + /* a0 */ "INST_A0", "INST_A1", "INST_A2", "INST_A3", + /* a4 */ "INST_A4", "INST_A5", "INST_A6", "INST_A7", + /* a8 */ "INST_A8", "INST_A9", "INST_AA", "INST_AB", + /* ac */ "INST_AC", "INST_AD", "INST_AE", "INST_AF", + /* b0 */ "PUSHB[0]", "PUSHB[1]", "PUSHB[2]", "PUSHB[3]", + /* b4 */ "PUSHB[4]", "PUSHB[5]", "PUSHB[6]", "PUSHB[7]", + /* b8 */ "PUSHW[0]", "PUSHW[1]", "PUSHW[2]", "PUSHW[3]", + /* bc */ "PUSHW[4]", "PUSHW[5]", "PUSHW[6]", "PUSHW[7]", + /* c0 */ "INST_C0", "INST_C1", "INST_C2", "INST_C3", + /* c4 */ "INST_C4", "INST_C5", "INST_C6", "INST_C7", + /* c8 */ "INST_C8", "INST_C9", "INST_CA", "INST_CB", + /* cc */ "INST_CC", "INST_CD", "INST_CE", "INST_CF", + /* d0 */ "INST_D0", "INST_D1", "INST_D2", "INST_D3", + /* d4 */ "INST_D4", "INST_D5", "INST_D6", "INST_D7", + /* d8 */ "INST_D8", "INST_D9", "INST_DA", "INST_DB", + /* dc */ "INST_DC", "INST_DD", "INST_DE", "INST_DF", + /* e0 */ "MIRP00000", "MIRP00001", "MIRP00010", "MIRP00011", + /* e4 */ "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111", + /* e8 */ "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011", + /* ec */ "MIRP01100", "MIRP01101", "MIRP01110", "MIRP01111", + /* f0 */ "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011", + /* f4 */ "MIRP10100", "MIRP10101", "MIRP10110", "MIRP10111", + /* f8 */ "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011", + /* fc */ "MIRP11100", "MIRP11101", "MIRP11110", "MIRP11111" + }; +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java new file mode 100644 index 000000000..b0559e0e3 --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/Zone.java @@ -0,0 +1,291 @@ +/* Zone.java -- A collection of points with some additional information. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.awt.font.FontDelegate; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; + + +/** + * A collection of points with some additional information. + */ +public final class Zone +{ + private Point[] points; + private int numPoints; + + public double scaleX, scaleY, shearX, shearY; + + public Zone(int maxNumPoints) + { + points = new Point[maxNumPoints]; + } + + public int getCapacity() + { + return points.length; + } + + + public int getSize() + { + return numPoints; + } + + + public int getX(int point) + { + return getX(point, FontDelegate.FLAG_FITTED); + } + + public int getX(int point, int flags) + { + int x; + if ((flags & FontDelegate.FLAG_FITTED) != 0) + x = points[point].x; + else + x = points[point].scaledX; + return x; + } + + + public void setX(int point, int value, boolean touch) + { + points[point].scaledX = value; + points[point].x = value; + if (touch) + points[point].flags |= Point.FLAG_TOUCHED_X; + } + + + public void setY(int point, int value, boolean touch) + { + points[point].scaledY = value; + points[point].y = value; + if (touch) + points[point].flags |= Point.FLAG_TOUCHED_Y; + } + + public int getY(int point) + { + return getY(point, FontDelegate.FLAG_FITTED); + } + + public int getY(int point, int flags) + { + int y; + if ((flags & FontDelegate.FLAG_FITTED) != 0) + y = points[point].y; + else + y = points[point].scaledY; + return y; + } + + + public int getOriginalX(int point) + { + return points[point].origX; + } + + + public int getOriginalY(int point) + { + return points[point].origY; + } + + + public void setOriginalX(int point, int x) + { + points[point].origX = x; + } + + public void setOriginalY(int point, int y) + { + points[point].origY = y; + } + + public void setNumPoints(int numPoints) + { + for (int i = 0; i < numPoints; i++) + points[i] = new Point(); + this.numPoints = numPoints; + } + + + public boolean isOnCurve(int point) + { + return (points[point].flags & Point.FLAG_ON_CURVE) != 0; + } + + + public void setOnCurve(int point, boolean onCurve) + { + if (onCurve) + points[point].flags |= Point.FLAG_ON_CURVE; + else + points[point].flags &= ~Point.FLAG_ON_CURVE; + } + + + public boolean isContourEnd(int point) + { + return (points[point].flags & Point.FLAG_CONTOUR_END) != 0; + } + + + public void setContourEnd(int point, boolean segEnd) + { + if (segEnd) + points[point].flags |= Point.FLAG_CONTOUR_END; + else + points[point].flags &= ~Point.FLAG_CONTOUR_END; + } + + + + + void transform(double pointSize, AffineTransform deviceTransform, + int unitsPerEm, int preTranslateX, int preTranslateY) + { + double factor; + + factor = pointSize / (double) unitsPerEm; + scaleX = deviceTransform.getScaleX() * factor; + scaleY = deviceTransform.getScaleY() * factor; + shearX = deviceTransform.getShearX() * factor; + shearY = deviceTransform.getShearY() * factor; + + for (int i = 0; i < numPoints; i++) + { + int x = points[i].origX + preTranslateX; + int y = points[i].origY + preTranslateY; + + points[i].scaledX = points[i].x = Fixed.valueOf(scaleX * x + + shearX * y); + points[i].scaledY = points[i].y = Fixed.valueOf(shearY * x + + scaleY * y); + } + } + + + + void combineWithSubGlyph(Zone zone, int numPhantomPoints) + { + int offset = this.numPoints - numPhantomPoints; + int count = zone.numPoints; + System.arraycopy(zone.points, 0, this.points, offset, count); + this.numPoints += count - numPhantomPoints; + } + + + private void dump() + { + for (int i = 0; i < numPoints; i++) + { + System.out.print(" " + i + ": "); + System.out.print(Fixed.toString(points[i].scaledX, points[i].scaledY)); + System.out.print(' '); + System.out.print(Fixed.toString(points[i].origX, points[i].origY)); + System.out.print(' '); + if (isOnCurve(i)) + System.out.print('.'); + else + System.out.print('c'); + if (isContourEnd(i)) + System.out.print('E'); + System.out.println(); + if (isContourEnd(i)) + System.out.println(); + } + } + + + public PathIterator getPathIterator(int type) + { + return new ZonePathIterator(this, type); + } + + + public GeneralPath getPath(int type) + { + GeneralPath p = new GeneralPath(GeneralPath.WIND_NON_ZERO, numPoints); + p.append(getPathIterator(type), /* connect */ false); + return p; + } + + /** + * Returns the number of contours in this outline. + * + * @return the number of contours in this outline + */ + public int getNumContours() + { + int num = 0; + for (int i = 0; i < numPoints; i++) + { + if (isContourEnd(i)) + num++; + } + return num; + } + + public int getContourEnd(int n) + { + int idx = -1; + int num = 0; + for (int i = 0; i < numPoints; i++) + { + if (isContourEnd(i)) + { + idx = i; + if (num == n) + break; + num++; + } + } + return idx; + } + + public Point[] getPoints() + { + return points; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java new file mode 100644 index 000000000..f4534f36f --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java @@ -0,0 +1,393 @@ +/* ZonePathIterator.java -- A PathIterator over glyph zones. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype.truetype; + +import java.awt.geom.PathIterator; + + +/** + * A PathIterator that enumerates the non-phantom points in a zone. + * + *

Lack of thread safety: Instances of this class are + * not safe to access from multiple concurrent threads. + * + * @see Zone + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +final class ZonePathIterator + implements PathIterator +{ + /** + * If state has this value, currentSegment + * will emit a SEG_LINETO or SEG_QUADTO segment + * to the current point. For a discussion of subtleties of on-curve + * and off-curve points, please refer to the documentation for + * {@link #getSegment}. + */ + private static final int EMIT_SEGMENT = 0; + + + /** + * If state has this value, currentSegment + * will emit a SEG_CLOSE in order to close the sub-path + * for the current contour. + */ + private static final int EMIT_CLOSE = 1; + + + /** + * If state has this value, currentSegment + * will emit a SEG_MOVETO segment to the first point in + * the current contour. If the first point is off-curve, a suitable + * on-curve point is calculated. + * + * @see #getStartSegment + */ + private static final int EMIT_MOVETO = 2; + + + /** + * The state of the iterator, which is one of + * EMIT_SEGMENT, EMIT_CLOSE, or + * EMIT_MOVETO. + */ + private int state; + + + + /** + * The zone whose segments are enumerated by this iterator. + */ + private Zone zone; + + + /** + * The total number of points in the zone, not including the four + * phantom points at its end. + */ + private int numPoints; + + + /** + * The number of the current point. + */ + private int curPoint; + + + /** + * The number of the first point in the current contour. + */ + private int contourStart; + + + private int type; + + /** + * Constructs a ZonePathIterator for the specified zone. + * + * @param zone the zone whose segments will be enumerated + * by this iterator. + */ + ZonePathIterator(Zone zone, int t) + { + this.zone = zone; + type = t; + numPoints = zone.getSize() - /* four phantom points */ 4; + + // The first segment that needs to be emitted is a SEG_MOVETO. + state = EMIT_MOVETO; + } + + + /** + * Returns the winding rule. TrueType glyphs always use the non-zero + * winding rule, so this method will always return {@link + * PathIterator#WIND_NON_ZERO}. + */ + public int getWindingRule() + { + return PathIterator.WIND_NON_ZERO; + } + + + + public boolean isDone() + { + return (state != EMIT_CLOSE) && (curPoint >= numPoints); + } + + + public void next() + { + boolean onCurve; + + /* If the current point is the end of a segment, and no SEG_CLOSE + * has been emitted yet, this will be the next segment. + */ + if (zone.isContourEnd(curPoint) && (state != EMIT_CLOSE)) + { + state = EMIT_CLOSE; + return; + } + + /* If the previously emitted segment was a SEG_CLOSE, we are now + * at the beginning of a new contour. + */ + if (state == EMIT_CLOSE) + { + contourStart = ++curPoint; + state = EMIT_MOVETO; + return; + } + + onCurve = zone.isOnCurve(curPoint); + + /* If the last segment was a moveto, and the current point + * (which is the first point in the contour) is off-curve, + * we need to emit a quadto segment for the first point. + */ + if ((state == EMIT_MOVETO) && !onCurve) + { + state = EMIT_SEGMENT; + return; + } + + + curPoint++; + + /* If the last point has been off-curve, and the now current + * point is on-curve, the last segment was a quadto that + * had the now current point at its end. In this case, we can + * skip a segment. + */ + if (!onCurve && zone.isOnCurve(curPoint)) + { + /* But if the skipped point is the end of a contour, we must not + * skip the SEG_CLOSE. An example where this matters is the 'o' + * glyph in the Helvetica font face that comes with MacOS X + * 10.1.5. + */ + if (zone.isContourEnd(curPoint)) + { + state = EMIT_CLOSE; + return; + } + + curPoint++; + } + + state = EMIT_SEGMENT; + } + + + /** + * Determines the successor of the current point in the current + * contour. The successor of the last point in a contour is the + * start of that contour. + * + * @return the number of the point that follows the current point in + * the same contour. + */ + private int getSuccessor(int p) + { + if (zone.isContourEnd(p)) + return contourStart; + else + return p + 1; + } + + + + /** + * Retrieves the current path segment using single-precision + * coordinate values. + */ + public int currentSegment(float[] coords) + { + switch (state) + { + case EMIT_CLOSE: + return PathIterator.SEG_CLOSE; + + case EMIT_MOVETO: + return getStartSegment(curPoint, coords); + + default: + return getSegment(curPoint, coords); + } + } + + + /** + * A helper array that is used by {@link + * #currentSegment(double[])}. + */ + float[] floats; + + + /** + * Retrieves the current path segment using double-precision + * coordinate values. + */ + public int currentSegment(double[] coords) + { + if (floats == null) + floats = new float[6]; + int result; + + result = currentSegment(floats); + for (int i = 0; i < 6; i++) + coords[i] = floats[i]; + return result; + } + + + /** + * Returns the segment for the specified point. + * + *

An example curve

+ * + *

If cur is an on-curve point, the returned segment + * is a straight line to cur. In the illustration, this + * would be the case for cur = 4.

+ * + *

If cur is an off-curve point, and + * cur’s successor succ is also + * off-curve, the returned segment is a quadratic Bézier + * spline whose control point is cur, and whose end + * point is located at the middle of the line connecting + * cur and succ. In the illustration, + * this would be the case for cur = 5.

+ * + *

If cur is an off-curve point, and + * cur’s successor succ is + * on-curve, the returned segment is a quadratic Bézier + * spline whose control point is cur, and whose end + * point is succ. In the illustration, this would + * be the case for cur = 6.

+ * + * @return either PathIterator.SEG_LINETO or + * PathIterator.SEG_QUADTO. + */ + private int getSegment(int cur, float[] coords) + { + int curX, curY; + int succ, succX, succY; + + curX = zone.getX(cur, type); + curY = zone.getY(cur, type); + coords[0] = Fixed.floatValue(curX); + coords[1] = Fixed.floatValue(curY); + + if (zone.isOnCurve(cur)) + return PathIterator.SEG_LINETO; + + succ = getSuccessor(cur); + succX = zone.getX(succ, type); + succY = zone.getY(succ, type); + + if (zone.isOnCurve(succ)) + { + coords[2] = Fixed.floatValue(succX); + coords[3] = Fixed.floatValue(succY); + } + else + { + coords[2] = Fixed.floatValue((curX + succX) / 2); + coords[3] = Fixed.floatValue((curY + succY) / 2); + } + return PathIterator.SEG_QUADTO; + } + + + /** + * Returns the start segment for the contour which starts + * at the specified point. + * + *

If the contour starts with an on-curve point, the returned + * segment is a SEG_MOVETO to that point.

+ * + *

If the contour starts with an off-curve point, and the contour + * ends with an on-curve point, the returned segment is a + * SEG_MOVETO to the end point.

+ * + *

If the contour starts with an off-curve point, and the contour + * also ends with an off-curve point, the returned segment is a + * SEG_MOVETO to the location at the middle between the + * start and end points of the contour.

+ * + * @return PathIterator.SEG_MOVETO. + */ + private int getStartSegment(int contourStart, float[] coords) + { + int x, y; + + if (zone.isOnCurve(contourStart)) + { + x = zone.getX(contourStart, type); + y = zone.getY(contourStart, type); + } + else + { + /* Find the last point of the current contour. */ + int contourEnd = contourStart; + while (!zone.isContourEnd(contourEnd)) + ++contourEnd; + + if (zone.isOnCurve(contourEnd)) + { + /* An example is the 'o' glyph of the Helvetica which comes + * with Apple MacOS X 10.1.5. + */ + x = zone.getX(contourEnd, type); + y = zone.getY(contourEnd, type); + } + else + { + x = (zone.getX(contourStart, type) + zone.getX(contourEnd, type)) / 2; + y = (zone.getY(contourStart, type) + zone.getY(contourEnd, type)) / 2; + } + } + + coords[0] = Fixed.floatValue(x); + coords[1] = Fixed.floatValue(y); + return PathIterator.SEG_MOVETO; + } +} diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia new file mode 100644 index 000000000..b715ea02c Binary files /dev/null and b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.dia differ diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png new file mode 100644 index 000000000..81d09d839 Binary files /dev/null and b/libjava/classpath/gnu/java/awt/font/opentype/truetype/doc-files/ZonePathIterator-1.png differ -- cgit v1.2.3