All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.fontengine.font.FontData Maven / Gradle / Ivy

The newest version!
/*
 *
 *	File: FontData.java
 *
 *
 *	ADOBE CONFIDENTIAL
 *	___________________
 *
 *	Copyright 2004-2006 Adobe Systems Incorporated
 *	All Rights Reserved.
 *
 *	NOTICE: All information contained herein is, and remains the property of
 *	Adobe Systems Incorporated and its suppliers, if any. The intellectual
 *	and technical concepts contained herein are proprietary to Adobe Systems
 *	Incorporated and its suppliers and may be covered by U.S. and Foreign
 *	Patents, patents in process, and are protected by trade secret or
 *	copyright law. Dissemination of this information or reproduction of this
 *	material is strictly forbidden unless prior written permission is obtained
 *	from Adobe Systems Incorporated.
 *
 */

/*
 * Adobe Patent and/or Adobe Patent Pending invention included within this file:
 *
 * Adobe patent application tracking # P376,
 * entitled 'Method for calculating CJK emboxes in fonts',
 * invented by Nathaniel McCully
 * Issued US Patent 7,071,941 on July 4, 2006.
 *
 * Adobe patent application tracking # P376,
 * entitled 'A LINE COMPOSITION CONTROLLABLE DTP SYSTEM, A LINE
 * COMPOSITION CONTROLLING METHOD, A LINE COMPOSITION CONTROL 
 * PROGRAM AND A RECORDING MEDIUM STORING THE SAME',
 * invented by Nathaniel McCully
 * Issued Japanese Patent 3708828 on August 12, 2005.
 *
 * Adobe patent application tracking # P377,
 * entitled 'LINE PREEMPT CONTROLLABLE DTP SYSTEM, A LINE
 * PREEMPT CONTROL METHOD, A LINE PREEMPT CONTROL PROGRAM
 * AND A RECORDING MEDIUM STORING THE SAME'
 * invented by Nathaniel McCully
 * Issued Japanese Patent 3598070 on September 17, 2004.
 */

package com.adobe.fontengine.font;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Set;

import com.adobe.agl.util.ULocale;
import com.adobe.fontengine.fontmanagement.CacheSupportInfo;
import com.adobe.fontengine.fontmanagement.Platform;
import com.adobe.fontengine.fontmanagement.fxg.FXGFontDescription;
import com.adobe.fontengine.fontmanagement.platform.PlatformFontDescription;
import com.adobe.fontengine.fontmanagement.postscript.PostscriptFontDescription;
import com.adobe.fontengine.inlineformatting.css20.CSS20FontDescription;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute.CSSStretchValue;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute.CSSStyleValue;
import com.adobe.fontengine.inlineformatting.css20.CSS20Attribute.CSSVariantValue;

/**
 * The base class for all FontData objects.
 * 
 * This class provides the basic functionality common to all Fonts.
 * 
 * 

Synchronization

* *

These objects are immutable.

*/ abstract public class FontData { //---------------------------------------------------------------- members --- private final byte[] containerFingerprint; //----------------------------------------------------------- constructors --- public FontData (byte[] digest) { // store a clone of the digest, so that malicious // code can not modify our copy containerFingerprint = (digest == null ? null : (byte[]) digest.clone ()); } //--------------------------------------------------------- general access --- public byte[] getContainerFingerprint () { // return a clone of our bytes, so that malicious // code can not modify them if (containerFingerprint == null) return new byte[0]; return containerFingerprint.clone (); } /** Return the number of glyphs in this font. */ abstract public int getNumGlyphs () throws InvalidFontException, UnsupportedFontException; /** Tell whether this font is symbolic. * @throws UnsupportedFontException * @throws InvalidFontException */ public boolean isSymbolic () throws UnsupportedFontException, InvalidFontException { // most font formats do not have a notion of symbolic fonts. return false; } //------------------------------------------------------------- unitsPerEm --- abstract public double getUnitsPerEmX () throws UnsupportedFontException, InvalidFontException; abstract public double getUnitsPerEmY () throws UnsupportedFontException, InvalidFontException; public double getCoolTypeUnitsPerEm () throws UnsupportedFontException, InvalidFontException { // most formats use that value (and ignore the font matrix); // only OpenType fonts do something else return 1000.0d; } //----------------------------------------------------------------- bboxes --- abstract public Rect getFontBBox () throws InvalidFontException, UnsupportedFontException; // CoolType does not use the same bbox as returned by getFontBBox // First, in OpenType font, the bbox recorded in the CFF table (if present) // is preferred over the bbox recorded in the head table. // Second, "strange" bboxes are "corrected". // // getCoolTypeRawFontBBox takes care of the first part, and depends on // the font type, while the second difference is accounted for in // getCoolTypeFontBBox. abstract protected Rect getCoolTypeRawFontBBox () throws InvalidFontException, UnsupportedFontException; public Rect getCoolTypeFontBBox () throws InvalidFontException, UnsupportedFontException { Rect r = getCoolTypeRawFontBBox (); if (r.xmin == r.xmax || r.ymin == r.ymax) { double unitsPerEmX = getUnitsPerEmX (); double unitsPerEmY = getUnitsPerEmY (); return new Rect (-0.5d * unitsPerEmX, -0.5d * unitsPerEmY, 1.5d * unitsPerEmX, 1.0d * unitsPerEmY); } else { return r; } } //----------------------------------------------------------------- script --- abstract public CoolTypeScript getCoolTypeScript () throws UnsupportedFontException, InvalidFontException; protected boolean useCoolTypeCJKHeuristics () throws UnsupportedFontException, InvalidFontException { CoolTypeScript ctScript = getCoolTypeScript (); return ( ctScript == CoolTypeScript.JAPANESE || ctScript == CoolTypeScript.SIMPLIFIED_CHINESE || ctScript == CoolTypeScript.TRADITIONAL_CHINESE || ctScript == CoolTypeScript.KOREAN); } //-------------------------------------------------------------- capHeight --- /** Returns the CoolTypeCapHeight of this font from typical glyphs. * @return Double.NaN if the value cannot be determined */ protected double getCoolTypeCapHeightFromGlyphs () throws UnsupportedFontException, InvalidFontException { int gidO = getCoolTypeGlyphForChar ('O'); int gidH = getCoolTypeGlyphForChar ('H'); if (gidO != 0 && gidH != 0) { Rect bboxO = getGlyphBBox (gidO); Rect bboxH = getGlyphBBox (gidH); if (! bboxO.equals (Rect.emptyRect) && ! bboxH.equals (Rect.emptyRect)) { return Math.min (bboxO.ymax, bboxH.ymax); }} return Double.NaN; } protected double getCoolTypeXHeightFromGlyphs() throws UnsupportedFontException, InvalidFontException { int gidx = getCoolTypeGlyphForChar ('x'); if (gidx != 0) { Rect bboxX = getGlyphBBox (gidx); if (! bboxX.equals (Rect.emptyRect)) { return bboxX.ymax; } } return Double.NaN; } /** Returns the CoolTypeCapHeight of this font. * @return Double.NaN if the value cannot be determined */ public double getCoolTypeCapHeight () throws UnsupportedFontException, InvalidFontException { return getCoolTypeCapHeightFromGlyphs (); } public double getCoolTypeXHeight() throws UnsupportedFontException, InvalidFontException { return getCoolTypeXHeightFromGlyphs(); } //-------------------------------------------------------------- ideoEmBox --- protected Rect snapToKnownIdeoEmBox (double ymin, double ymax) throws UnsupportedFontException, InvalidFontException { double unitsPerEmX = getUnitsPerEmX (); double unitsPerEmY = getUnitsPerEmY (); if (unitsPerEmY == 1000 && Math.abs (ymin - (-120)) <= .004 * unitsPerEmY && Math.abs (ymax - 880) <= 0.004 * unitsPerEmY) { return new Rect (0, -0.120 * unitsPerEmY, unitsPerEmX, 0.880 * unitsPerEmY); } if (unitsPerEmY == 256 && Math.abs (ymin - (-36)) <= .0045 * unitsPerEmY && Math.abs (ymax - 220) <= 0.0045 * unitsPerEmY) { return new Rect (0, -0.140625 * unitsPerEmY, unitsPerEmX, 0.859375 * unitsPerEmY); } return null; } // When the font data is absent or deemed unreliable, we // use the bounding boxes of a number of typical glyphs protected static final int[] typicalCharactersForIdeoEmBoxComputation = {'\u6c38', // U+6C38 CJK UNIFIED IDEOGRAPH-6C38 '\u9b31', // U+9B31 CJK UNIFIED IDEOGRAPH-9B31 '\uad2c', // U+AD2C HANGUL SYLLABLE GWAESS '\u30fb', // U+30FB KATAKANA MIDDLE DOT }; protected Rect getCoolTypeIdeoEmBoxFromFullBoxCharacter () throws InvalidFontException, UnsupportedFontException { int gid = getCoolTypeGlyphForChar ('\u253C'); // U+253C ? BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL if (gid != 0) { Rect bbox = getGlyphBBox (gid); double unitsPerEmY = getUnitsPerEmY (); if (! bbox.equals (Rect.emptyRect)) { if (bbox.ymax - bbox.ymin != unitsPerEmY) { return null; } return snapToKnownIdeoEmBox (bbox.ymin, bbox.ymax); }} return null; } protected Rect getCoolTypeIdeoEmBoxFromTypicalCharacter () throws InvalidFontException, UnsupportedFontException { double unitsPerEmX = getUnitsPerEmX (); double unitsPerEmY = getUnitsPerEmY (); for (int i = 0; i < typicalCharactersForIdeoEmBoxComputation.length; i++) { int gid = getCoolTypeGlyphForChar (typicalCharactersForIdeoEmBoxComputation [i]); if (gid != 0) { Rect bbox = getGlyphBBox (gid); if (! bbox.equals (Rect.emptyRect)) { double yMax = bbox.ymax + (unitsPerEmY - (bbox.ymax - bbox.ymin)) / 2.0; Rect r = snapToKnownIdeoEmBox (yMax - unitsPerEmY, yMax); if (r != null) { return r; } else { return new Rect (0, yMax - unitsPerEmY, unitsPerEmX, yMax); }}}} return null; } abstract public Rect getCoolTypeIdeoEmBox () throws UnsupportedFontException, InvalidFontException; //----------------------------------------------------------------- icfBox --- protected static final int[] typicalCharactersForIcfBoxComputation = {'\u6c38', // U+6C38 CJK UNIFIED IDEOGRAPH-6C38 '\u9b31', // U+9B31 CJK UNIFIED IDEOGRAPH-9B31 '\uad2c', // U+AD2C HANGUL SYLLABLE GWAESS '\u30db', // U+30DB KATAKANA LETTER HO }; /** Compute the IcfBox from typical characters. * In this heuristic, the IcfBox is slightly smaller than the IdeoEmBox, * and centered into it. The margin between the two boxes is constant * all around, and is determined from the position of typical characters * in the IdeoEmBox. */ protected Rect getCoolTypeIcfBoxFromTypicalCharacter (Rect ideoEmBox) throws InvalidFontException, UnsupportedFontException { for (int i = 0; i < typicalCharactersForIcfBoxComputation.length; i++) { int gid = getCoolTypeGlyphForChar (typicalCharactersForIcfBoxComputation [i]); if (gid != 0) { Rect bbox = getGlyphBBox (gid); if (! bbox.equals (Rect.emptyRect)) { double margin = ((ideoEmBox.ymax - bbox.ymax) + (bbox.ymin - ideoEmBox.ymin)) / 2.0d; if (margin < 0 && useCoolTypeCJKHeuristics ()) { return null; } else { return new Rect (ideoEmBox.xmin + margin, ideoEmBox.ymin + margin, ideoEmBox.xmax - margin, ideoEmBox.ymax - margin); }}}} return null; } /** * Compute the IcfBox from IdeoEmBox. In this heuristic, the IcfBox is * slightly smaller than the IdeoEmBox, and centered into it. The margin * between the two boxes is constant all around and is equal to the margin * obtained by centering a square of area .9 square em inside a square of area * 1 square em. */ protected Rect getCoolTypeIcfBoxFromIdeoEmBox (Rect ideoEmBox) throws InvalidFontException, UnsupportedFontException { double marginInEm = (1.0d - Math.sqrt (0.9d)) / 2.0d; double marginXInMetricUnits = marginInEm * getUnitsPerEmX (); double marginYInMetricUnits = marginInEm * getUnitsPerEmY (); return new Rect (ideoEmBox.xmin + marginXInMetricUnits, ideoEmBox.ymin + marginYInMetricUnits, ideoEmBox.xmax - marginXInMetricUnits, ideoEmBox.ymax - marginYInMetricUnits); } abstract public Rect getCoolTypeIcfBox () throws UnsupportedFontException, InvalidFontException; //----------------------------------------------------------- line metrics --- /** A number of heuristics eventually compute the line metrics * from the font bbox. */ protected LineMetrics getCoolTypeLineMetricsFromFontBbox () throws UnsupportedFontException, InvalidFontException { Rect bbox = getCoolTypeFontBBox (); if (bbox == null) { return null; } double ascender; double descender; // The algorithm used in CoolType performs different computations // based on the vertical extent of the font bbox. There is one computation // for bboxes smaller than 1em, another computation for bboxes between // 1em and 1.2em, and another computation for bboxes larger than 1.2em. // These computations are discontinous, in the following sense: for fonts // with a bbox height between 1em and 1.2em, the descender is aligned // with the bottom of the bbox, and for fonts above 1.2em, the descender // is significantly not aligned with the bottom of the bbox. So two // fonts which have very similar bboxes can end up with fairly different // ascender and descender. // This discontinuity is probably problematic on its own, but we don't // really care about that: we promise to do the same thing as CoolType, // whatever that may be. // But it makes our task a bit more difficult: let's say that CoolType // determines the vertical extent to be just above the 1.2em threshold, // and that AFE determines it to be just below that threshold: we // end up with significantly different results. So our emulation has // to be fairly precise, up to the point where AFE selects the same // computation as CoolType. // How difficult is that? CoolType internally represents metrics using // 16.16 fixed numbers, expressed in em. For example, the quantity // 1/4 em is represented as 0x00004000. // Let's consider a font at 1000 units per em, with a vertical extent // of (-250, 950), i.e. exactly 1.2em. These numbers, as seen by CoolType, // are 0xfffc000 and 0xf333, for a vertical extent of 0x13333. // That triggers the [1.0em, 1.2em[ computation. // Let's consider another font also at 1000 units per em, with a // vertical extent of (-238, 962), also exactly 1.2em high; these numbers, // as seen by CoolType, are 0xfffc312 and 0xf646, for a vertical extent // of 0x1334. and that triggers the >1.2em computation! // What is happening here is that with 1000 units per em, not all // integral metrics can be represented exactly in a 16.16 fixed number, // so some rounding happens; and the combined rounding of ymin and ymax // leads to a very slightly different extent (a 65,636th of an em!), // but that is enough to select different computations. // By the way, those two fonts are not hypothetical. An example of the // first is 0e01a3f7f82952d5c7b24cb30f02ae1f4e1d1db1, and example of the // second is 003383b2ad7501a9b63db36cbc10860c75bff14e. // Because AFE works essentially with numbers expressed in design units, // the two fonts are not distinguishable, and the same computation would // normally be applied to those two fonts. And because of the discontinuity, // one of the two fonts would have line metrics significantly different // from CoolType. // To ensure that AFE selects the same computation as CoolType, we need // to compute the vertical extent with the same precision (or lack of) as // CoolType. We achieve that by rounding to the same precision: double unitsPerEmY = getUnitsPerEmY (); long yMinCT = Math.round (65536.0d * bbox.ymin / unitsPerEmY); long yMaxCT = Math.round (65536.0d * bbox.ymax / unitsPerEmY); long extentCT = yMaxCT - yMinCT; if (extentCT > 0x13333) { ascender = bbox.ymax * unitsPerEmY / (bbox.ymax - bbox.ymin); descender = ascender - unitsPerEmY; } else if (extentCT >= 0x10000) { descender = bbox.ymin; ascender = descender + unitsPerEmY; } else { ascender = bbox.ymax; descender = ascender - unitsPerEmY; } return new LineMetrics (ascender, descender, unitsPerEmY / 5); } public Rect getCoolTypeGlyphBBox(int glyphID) throws UnsupportedFontException, InvalidFontException { // for most font types, cooltype does the same thing as our getGlyphBBox. return getGlyphBBox(glyphID); } /** Emulates the CoolType API CTFontDict:GetHorizontalMetrics CoolType API. * *

See also the {@link #getLineMetrics()} method. * * @throws UnsupportedFontException * @throws InvalidFontException */ abstract public LineMetrics getCoolTypeLineMetrics () throws UnsupportedFontException, InvalidFontException; /** Return the line metrics for this font. * *

Some font formats do not support the notion of line metrics, * and in those cases, this method returns null. * *

See also the {@link #getCoolTypeLineMetrics()} method. * * @throws UnsupportedFontException * @throws InvalidFontException */ public LineMetrics getLineMetrics () throws UnsupportedFontException, InvalidFontException { // most font formats doe not have a notion of line metrics, // subclasses for the appropriate formats to override. return null; } //------------------------------------------------------ underline metrics --- abstract public UnderlineMetrics getCoolTypeUnderlineMetrics () throws UnsupportedFontException, InvalidFontException; //----------------------------------------------------------- proportional --- protected static final int[][] typicalCharactersForProportionalRomanDecision = { {'\u0020', /* space */ '\u002e', /* period */ '\u004d', /* latin capital M */ }, {'\u0020', '\u002e', '\u039c', /* Greek Mu...for symbol fonts */ }, {'\u0020', '\u002e', '\ufb03', /* ffi for expert fonts */ } }; /** Determines whether a font has proportional roman, as defined by CoolType. * * @return whether the font is proportional * @throws InvalidFontException * @throws UnsupportedFontException */ public boolean hasCoolTypeProportionalRoman() throws InvalidFontException, UnsupportedFontException { final int notdef = 0; int i; double firstWidth = 0; boolean gotFirstWidth = false; int[] gids = new int [typicalCharactersForProportionalRomanDecision[0].length]; for (i = 0; i < typicalCharactersForProportionalRomanDecision.length; i++) { int glyphsFound = 0; for (int j = 0; j < typicalCharactersForProportionalRomanDecision[i].length; j++) { gids[j] = this.getGlyphForChar(typicalCharactersForProportionalRomanDecision[i][j]); if (gids[j] != notdef) { glyphsFound++; }} if (glyphsFound == typicalCharactersForProportionalRomanDecision[i].length) { break; }} // only roman glyphs are considered by cooltype for cjk fonts. if (i > 0 && useCoolTypeCJKHeuristics()) { return false; } if (i < typicalCharactersForProportionalRomanDecision.length) { double thisWidth; for (int j = 0; j < typicalCharactersForProportionalRomanDecision[i].length; j++) { thisWidth = getHorizontalAdvance (gids[j]); if (!gotFirstWidth) { if (Math.round (thisWidth) == 0) { continue; } gotFirstWidth = true; firstWidth = thisWidth; } else if (Math.abs (firstWidth - thisWidth) > 1) { return true; }} return false; } return getCoolTypeProportionalRomanFromFontProperties (); } abstract public boolean getCoolTypeProportionalRomanFromFontProperties () throws InvalidFontException; //------------------------------------------------------------------- cmap --- /** Return the glyph to use to display a character. * * Depending on the layout technology of the font, the returned gid * may be further processed. * * @param unicodeScalarValue the Unicode scalar value of the character; * (by definition, surrogate code points are not Unicode scalar values). * @return the gid of the glyph to use */ abstract public int getGlyphForChar (int unicodeScalarValue) throws InvalidFontException, UnsupportedFontException; /** Return the glyph used by CoolType for a character. * * This is slightly different from getGlyphForChar, and intend to be closer * to the behavior of CoolType. The main use of this version is for other * functions that emulate CoolType, such as the methods that compute * metrics based on the charateristics of some glyphs. * * In most font types, this is the same as getGlyphForChar, so we * provide a default implementation here. */ public int getCoolTypeGlyphForChar (int unicodeScalarValue) throws InvalidFontException, UnsupportedFontException { return getGlyphForChar (unicodeScalarValue); } //-------------------------------------------------- individual glyph data --- /** Get the horizontal advance of a glyph. * The returned value is in metric space. */ abstract public double getHorizontalAdvance (int gid) throws InvalidGlyphException, UnsupportedFontException, InvalidFontException; /** Send a glyph's outline to an OutlineConsumer. */ abstract public void getGlyphOutline (int gid, OutlineConsumer consumer) throws InvalidFontException, UnsupportedFontException; /** Get the bounding box of a glyph. * The returned value is in metric space. */ abstract public Rect getGlyphBBox (int gid) throws UnsupportedFontException, InvalidFontException; //---------------------------------------------------------------------------- /** Get a scaler for this font. * This scaler uses to the most appropriate scan converter for the font. */ public Scaler getScaler () throws InvalidFontException, UnsupportedFontException { return getScaler (null); } /** Get a scaler for this font, using a specific scan converter. */ abstract public Scaler getScaler (ScanConverter c) throws InvalidFontException, UnsupportedFontException; //--------------------------------------------------------- swf generation --- public abstract SWFFontDescription getSWFFontDescription (boolean wasEmbedded) throws UnsupportedFontException, InvalidFontException; public abstract SWFFont4Description getSWFFont4Description (boolean wasEmbedded) throws UnsupportedFontException, InvalidFontException; //--------------------------------------------------------- pdf generation --- public abstract Permission getEmbeddingPermission (boolean wasEmbedded) throws InvalidFontException, UnsupportedFontException; public abstract PDFFontDescription getPDFFontDescription (Font font) throws UnsupportedFontException, InvalidFontException; public abstract XDCFontDescription getXDCFontDescription (Font font) throws UnsupportedFontException, InvalidFontException; /** Create a subset for this font. */ abstract public Subset createSubset () throws InvalidFontException, UnsupportedFontException; /** Subset and stream this font for PDF use. * The stream is either a TrueType stream or a CID-keyed CFF stream. * @param out the OutputStream to which the bytes are streamed * @param preserveROS tells whether to preserve the cid -> gid mapping */ abstract public void subsetAndStream (Subset subset, OutputStream out, boolean preserveROS) throws InvalidFontException, UnsupportedFontException, IOException; /** * Evaluate the font to determine if it is a small cap font. This method assumes the font is NOT an allcap font. * * This uses heuristics determined for CoolType. * * @param gid1 The glyphID for a glyph that would contain an ascender in a typical font...such as 'h'. * @param xHeight The height of glyphs that don't contain ascenders. This must be in metric space. * @return true iff the font appears to have small caps. */ protected boolean isSmallCapFont(int gid1, double xHeight) throws UnsupportedFontException, InvalidFontException { if (gid1 == 0) return false; Rect bbox1 = getGlyphBBox(gid1); double ppemY = getUnitsPerEmY(); // if the height of the bboxes are within 20/1000's, call them close enough to be the same if (bbox1.ymax > 0 && Math.abs(bbox1.ymax - xHeight) / ppemY < 20/1000.0 ) return true; return false; } protected boolean isAllCapFont(int gid1, int gid2) throws InvalidFontException, UnsupportedFontException { if (gid1 == 0 || gid2 == 0) return false; IteratingOutlineConsumer consumer1 = new IteratingOutlineConsumer(); this.getGlyphOutline(gid1, consumer1); ComparingOutlineConsumer consumer2 = new ComparingOutlineConsumer(consumer1); this.getGlyphOutline(gid2, consumer2); return consumer2.hasOutlines && consumer2.compares; } /** * Evaluate the bitmap associated with up to 2 glyphs to heuristically determine if a font is a serif font. * * This uses heuristics developed for cooltype. * * @param gidForl The glyphID for the primary candidate for serif evaluation...such as 'l'. * @param gidForI The glyphID for the secondary candidate for serif evalues...such as 'I' * @param italicAngle The italic angle for the font. * @return true iff the glyphs appear to have serifs. */ protected boolean isSerifFont(int gidForl, int gidForI, double italicAngle) throws InvalidFontException, UnsupportedFontException { if (gidForl == 0 && gidForI == 0) return false; final int scale = 250; boolean serifFound = true; SerifEvaluator evaluator = new SerifEvaluator(scale, italicAngle); Scaler rasterizer = getScaler (); rasterizer.setScale (scale, scale, scale, 0, 0); try { if (gidForl != 0) { evaluator.startBitmap(gidForl); rasterizer.getBitmap (gidForl, evaluator); serifFound = evaluator.hasSerif(); evaluator.endBitmap(); } } catch (InvalidGlyphException e) {} if (serifFound && gidForI != 0) { evaluator.startBitmap(gidForI); rasterizer.getBitmap (gidForI, evaluator); serifFound = evaluator.hasSerif(); evaluator.endBitmap(); } return serifFound; } /** * Evaluates the runs associated with a glyph to heuristically determine if the glyph has serifs. * * The heuristics assume we are looking at a glyph that has 1 primary vertical stem. Compares * the width of the middle of the stem with the width along the rest of the stem. If it is wide enough and * left enough, it is a serif... * * Instances of this class are not threadsafe. They can be used sequentially for multiple glyphs in a given font * provided startBitmap is always called between glyphs. */ static class SerifEvaluator implements BitmapConsumer { int[][] runs = null; int minY, minMarked, maxMarked; /** A "magic" number. We are looking for a width that is this much wider than the width of the middle scanline. */ final double widthProportion; final int scale; /** A "magic" number. We are looking for a serif on the left of the glyph. This is the minimum * distance to the left of the middle scanline we need to be to call something a serif. */ final double minDistanceFromVertical; /** If the font is italic, we still want to calculate things as if the stem were perfectly vertical. * This is the multiple that gets us back to vertical. */ final double tangent; /** * @param scale the size of the bitmap we are evaluating. We want it to be big enough that we have * enough pixels to definitively say we are looking at a serif without wasting alot of computation/memory * @param italicAngle the italic angle for the font. */ SerifEvaluator(int scale, double italicAngle) { this.scale = scale; this.widthProportion = scale * .005; this.minDistanceFromVertical = scale * .008; this.tangent = Math.tan(Math.toRadians(-italicAngle)); } boolean hasSerif() { if (runs == null) return false; int middle = minMarked + (maxMarked - minMarked) / 2; double minWidth = (int)Math.floor((runs[middle][1] - runs[middle][0]) * 1.2); for (int i = minMarked; i <= maxMarked; i++) { int width = runs[i][1] - runs[i][0]; // if the run is wide enough, look for a serif on the left if (width > minWidth) { int middleXMoved = (int)Math.floor(runs[middle][0] + (i - middle) * tangent); // if the run is left enough, it is a left serif... if (middleXMoved - runs[i][0] > minDistanceFromVertical) return true; } } return false; } public void addRun(double xOn, double xOff, double y) { int i; if (runs == null) { minY = (int)y-scale; runs = new int[scale*2][2]; for (int j = 0; j < scale*2; j++) { runs[j][0] = Integer.MAX_VALUE; runs[j][1] = 0; } i = scale; minMarked = maxMarked = i; runs[i][0] = (int)xOn; runs[i][1] = (int)xOff; } else if ((int)y < minY) { int[][] newRuns = new int[minY - (int)y + runs.length][2]; for (int j = 0; j < minY; j++) { newRuns[j][0] = Integer.MAX_VALUE; newRuns[j][1] = 0; } minMarked += minY - (int)y; maxMarked += minY - (int)y; System.arraycopy(runs, 0, newRuns, minY - (int)y, runs.length); runs = newRuns; minY = (int)y; i = 0; runs[i][0] = (int)xOn; runs[i][1] = (int)xOff; } else { i = (int)y - minY; if ((int)y >= minY + runs.length) { int[][] newRuns = new int[(int)y - minY+1][2]; for (int j = runs.length; j <= (int)y-minY; j++) { newRuns[j][0] = 0; newRuns[j][1] = 0; } System.arraycopy(runs, 0, newRuns, 0, runs.length); runs = newRuns; runs[i][0] = (int)xOn; runs[i][1] = (int)xOff; } else { // look for the left-most run. if (xOn <= runs[i][0]) { if (xOff < runs[i][0]) runs[i][1] = (int)xOff; runs[i][0] = (int)xOn; } else if (xOn <= runs[i][1]) { if (xOff > runs[i][1]) { runs[i][1] = (int)xOff; } } } } if (i < minMarked) minMarked = i; if (i > maxMarked) maxMarked = i; } public void endBitmap() { } public void startBitmap(int n) { runs = null; } } //------------------------------------------------------------ CSS support --- /** Return the set of CSS font-family names that this font matches. * Members of the returned Set are String objects. */ abstract protected Set getCSSFamilyNames() throws InvalidFontException, UnsupportedFontException; /** Return the preferred name to be used in CSS. * This is the name that authors should put in their documents. */ abstract protected String getPreferredCSSFamilyName() throws InvalidFontException, UnsupportedFontException; /** Tell if the font matches the CSS font-style normal. */ abstract protected boolean isCSSStyleNormal () throws InvalidFontException, UnsupportedFontException; /** Tell if the font matches the CSS font-style italic. */ abstract protected boolean isCSSStyleItalic () throws InvalidFontException, UnsupportedFontException; /** Tell if the font matches the CSS font-style oblique. */ abstract protected boolean isCSSStyleOblique () throws InvalidFontException, UnsupportedFontException; /** Tell if the font matches the CSS font-variant normal. */ abstract protected boolean isCSSVariantNormal () throws InvalidFontException, UnsupportedFontException; /** Tell if the font matches the CSS font-style small-caps. */ abstract protected boolean isCSSVariantSmallCaps () throws InvalidFontException, UnsupportedFontException; /** Return the CSS weight of this font. */ abstract protected int getCSSWeight () throws InvalidFontException, UnsupportedFontException; /** Return the CSS fontstretch of this font. */ abstract protected CSSStretchValue getCSSStretchValue () throws InvalidFontException, UnsupportedFontException; /** Return the CacheSupportInfo that this font matches */ public abstract CacheSupportInfo getCacheSupportInfo () throws UnsupportedFontException, InvalidFontException; /** Return the postscript descriptions that this font matches. */ abstract public PostscriptFontDescription[] getPostscriptFontDescription () throws InvalidFontException, UnsupportedFontException; /** Return the FXG descriptions for this font. */ abstract public FXGFontDescription[] getFXGFontDescription(Platform platform, ULocale locale) throws InvalidFontException, UnsupportedFontException; /** Return the platform descriptions for this font. */ abstract public PlatformFontDescription[] getPlatformFontDescription(Platform platform, ULocale locale) throws InvalidFontException, UnsupportedFontException; /** Return the range of point sizes for which this font has been designed. * * @return an array with exactly two elements. The first is the smallest * intended point size (inclusive), the second is the largest * intended point size (exclusive). Both numbers are in decipoints. */ public double[] getPointSizeRange () throws InvalidFontException, UnsupportedFontException { return new double[] {0d, Double.POSITIVE_INFINITY}; } public CSS20FontDescription[] getCSS20FontDescription () throws InvalidFontException, UnsupportedFontException { int numNames; Set names = getCSSFamilyNames(); numNames = names.size(); int numVariants = 0; CSSVariantValue[] variants = new CSSVariantValue[2]; if (isCSSVariantSmallCaps()) variants[numVariants++] = CSSVariantValue.SMALL_CAPS; if (isCSSVariantNormal()) variants[numVariants++] = CSSVariantValue.NORMAL; int numStyles = 0; CSSStyleValue[] styles = new CSSStyleValue[3]; if (isCSSStyleOblique()) styles[numStyles++] = CSSStyleValue.OBLIQUE; if (isCSSStyleItalic()) styles[numStyles++] = CSSStyleValue.ITALIC; if (isCSSStyleNormal()) styles[numStyles++] = CSSStyleValue.NORMAL; int cssWeight = getCSSWeight(); CSSStretchValue stretch = getCSSStretchValue(); int numDescriptions = numNames * numStyles * numVariants; CSS20FontDescription[] retval = new CSS20FontDescription[numDescriptions]; Iterator nameIter = names.iterator(); int currIndex = 0; double[] pointSizeRange = getPointSizeRange (); for (int i = 0; i < numNames; i++) { String familyName = (String)nameIter.next(); for (int j = 0; j < numStyles; j++) { for (int k = 0; k < numVariants; k++) { retval[currIndex++] = new CSS20FontDescription(familyName, styles[j], variants[k], stretch, cssWeight, pointSizeRange [0] / 10.0d, pointSizeRange [1] / 10.0d); } } } return retval; } public CSS20FontDescription getPreferredCSS20FontDescription () throws InvalidFontException, UnsupportedFontException { String name = getPreferredCSSFamilyName(); if (name == null) return null; CSSVariantValue variant; if (isCSSVariantNormal()) variant = CSSVariantValue.NORMAL; else if (isCSSVariantSmallCaps()) variant = CSSVariantValue.SMALL_CAPS; else return null; CSSStyleValue style; if (isCSSStyleNormal()) style = CSSStyleValue.NORMAL; else if (isCSSStyleItalic()) style = CSSStyleValue.ITALIC; else if (isCSSStyleOblique()) style = CSSStyleValue.OBLIQUE; else return null; int cssWeight = getCSSWeight(); CSSStretchValue stretch = getCSSStretchValue(); double[] pointSizeRange = getPointSizeRange (); CSS20FontDescription retval = new CSS20FontDescription(name, style, variant, stretch, cssWeight, pointSizeRange [0] / 10.0d, pointSizeRange [1] / 10.0d); return retval; } //---------------------------------------------------------------------------- abstract public CatalogDescription getSelectionDescription () throws InvalidFontException, UnsupportedFontException; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy