feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
101
jdkSrc/jdk8/sun/font/AttributeMap.java
Normal file
101
jdkSrc/jdk8/sun/font/AttributeMap.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
What is the dead simplest thing to do?
|
||||
Extend AbstractMap and don't optimize for anything.
|
||||
|
||||
The only new api is 'getValues()' which returns the values struct as
|
||||
long as no map api has been called. If any map api is called,
|
||||
create a real map and forward to it, and nuke values because of the
|
||||
possibility that the map has been changed. This is easier than
|
||||
trying to create a map that only clears values if the map has been
|
||||
changed, or implementing the map API directly on top of the values
|
||||
struct. We can always do that later if need be.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Paint;
|
||||
import java.awt.font.GraphicAttribute;
|
||||
import java.awt.font.NumericShaper;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.font.TransformAttribute;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.im.InputMethodHighlight;
|
||||
import java.text.AttributedCharacterIterator.Attribute;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static sun.font.AttributeValues.*;
|
||||
|
||||
public final class AttributeMap extends AbstractMap<TextAttribute, Object> {
|
||||
private AttributeValues values;
|
||||
private Map<TextAttribute, Object> delegateMap;
|
||||
|
||||
public AttributeMap(AttributeValues values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public Set<Entry<TextAttribute, Object>> entrySet() {
|
||||
return delegate().entrySet();
|
||||
}
|
||||
|
||||
public Object put(TextAttribute key, Object value) {
|
||||
return delegate().put(key, value);
|
||||
}
|
||||
|
||||
// internal API
|
||||
public AttributeValues getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
private static boolean first = false; // debug
|
||||
private Map<TextAttribute, Object> delegate() {
|
||||
if (delegateMap == null) {
|
||||
if (first) {
|
||||
first = false;
|
||||
Thread.dumpStack();
|
||||
}
|
||||
delegateMap = values.toMap(new HashMap<TextAttribute, Object>(27));
|
||||
|
||||
// nuke values, once map is accessible it might be mutated and values would
|
||||
// no longer reflect its contents
|
||||
values = null;
|
||||
}
|
||||
|
||||
return delegateMap;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (values != null) {
|
||||
return "map of " + values.toString();
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
907
jdkSrc/jdk8/sun/font/AttributeValues.java
Normal file
907
jdkSrc/jdk8/sun/font/AttributeValues.java
Normal file
@@ -0,0 +1,907 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2005 - All Rights Reserved
|
||||
*
|
||||
* The original version of this source code and documentation is
|
||||
* copyrighted and owned by IBM. These materials are provided
|
||||
* under terms of a License Agreement between IBM and Sun.
|
||||
* This technology is protected by multiple US and International
|
||||
* patents. This notice and attribution to IBM may not be removed.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import static sun.font.EAttribute.*;
|
||||
import static java.lang.Math.*;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Paint;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.font.GraphicAttribute;
|
||||
import java.awt.font.NumericShaper;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.font.TransformAttribute;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.im.InputMethodHighlight;
|
||||
import java.io.Serializable;
|
||||
import java.text.Annotation;
|
||||
import java.text.AttributedCharacterIterator.Attribute;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public final class AttributeValues implements Cloneable {
|
||||
private int defined;
|
||||
private int nondefault;
|
||||
|
||||
private String family = "Default";
|
||||
private float weight = 1f;
|
||||
private float width = 1f;
|
||||
private float posture; // 0f
|
||||
private float size = 12f;
|
||||
private float tracking; // 0f
|
||||
private NumericShaper numericShaping; // null
|
||||
private AffineTransform transform; // null == identity
|
||||
private GraphicAttribute charReplacement; // null
|
||||
private Paint foreground; // null
|
||||
private Paint background; // null
|
||||
private float justification = 1f;
|
||||
private Object imHighlight; // null
|
||||
// (can be either Attribute wrapping IMH, or IMH itself
|
||||
private Font font; // here for completeness, don't actually use
|
||||
private byte imUnderline = -1; // same default as underline
|
||||
private byte superscript; // 0
|
||||
private byte underline = -1; // arrgh, value for ON is 0
|
||||
private byte runDirection = -2; // BIDI.DIRECTION_DEFAULT_LEFT_TO_RIGHT
|
||||
private byte bidiEmbedding; // 0
|
||||
private byte kerning; // 0
|
||||
private byte ligatures; // 0
|
||||
private boolean strikethrough; // false
|
||||
private boolean swapColors; // false
|
||||
|
||||
private AffineTransform baselineTransform; // derived from transform
|
||||
private AffineTransform charTransform; // derived from transform
|
||||
|
||||
private static final AttributeValues DEFAULT = new AttributeValues();
|
||||
|
||||
// type-specific API
|
||||
public String getFamily() { return family; }
|
||||
public void setFamily(String f) { this.family = f; update(EFAMILY); }
|
||||
|
||||
public float getWeight() { return weight; }
|
||||
public void setWeight(float f) { this.weight = f; update(EWEIGHT); }
|
||||
|
||||
public float getWidth() { return width; }
|
||||
public void setWidth(float f) { this.width = f; update(EWIDTH); }
|
||||
|
||||
public float getPosture() { return posture; }
|
||||
public void setPosture(float f) { this.posture = f; update(EPOSTURE); }
|
||||
|
||||
public float getSize() { return size; }
|
||||
public void setSize(float f) { this.size = f; update(ESIZE); }
|
||||
|
||||
public AffineTransform getTransform() { return transform; }
|
||||
public void setTransform(AffineTransform f) {
|
||||
this.transform = (f == null || f.isIdentity())
|
||||
? DEFAULT.transform
|
||||
: new AffineTransform(f);
|
||||
updateDerivedTransforms();
|
||||
update(ETRANSFORM);
|
||||
}
|
||||
public void setTransform(TransformAttribute f) {
|
||||
this.transform = (f == null || f.isIdentity())
|
||||
? DEFAULT.transform
|
||||
: f.getTransform();
|
||||
updateDerivedTransforms();
|
||||
update(ETRANSFORM);
|
||||
}
|
||||
|
||||
public int getSuperscript() { return superscript; }
|
||||
public void setSuperscript(int f) {
|
||||
this.superscript = (byte)f; update(ESUPERSCRIPT); }
|
||||
|
||||
public Font getFont() { return font; }
|
||||
public void setFont(Font f) { this.font = f; update(EFONT); }
|
||||
|
||||
public GraphicAttribute getCharReplacement() { return charReplacement; }
|
||||
public void setCharReplacement(GraphicAttribute f) {
|
||||
this.charReplacement = f; update(ECHAR_REPLACEMENT); }
|
||||
|
||||
public Paint getForeground() { return foreground; }
|
||||
public void setForeground(Paint f) {
|
||||
this.foreground = f; update(EFOREGROUND); }
|
||||
|
||||
public Paint getBackground() { return background; }
|
||||
public void setBackground(Paint f) {
|
||||
this.background = f; update(EBACKGROUND); }
|
||||
|
||||
public int getUnderline() { return underline; }
|
||||
public void setUnderline(int f) {
|
||||
this.underline = (byte)f; update(EUNDERLINE); }
|
||||
|
||||
public boolean getStrikethrough() { return strikethrough; }
|
||||
public void setStrikethrough(boolean f) {
|
||||
this.strikethrough = f; update(ESTRIKETHROUGH); }
|
||||
|
||||
public int getRunDirection() { return runDirection; }
|
||||
public void setRunDirection(int f) {
|
||||
this.runDirection = (byte)f; update(ERUN_DIRECTION); }
|
||||
|
||||
public int getBidiEmbedding() { return bidiEmbedding; }
|
||||
public void setBidiEmbedding(int f) {
|
||||
this.bidiEmbedding = (byte)f; update(EBIDI_EMBEDDING); }
|
||||
|
||||
public float getJustification() { return justification; }
|
||||
public void setJustification(float f) {
|
||||
this.justification = f; update(EJUSTIFICATION); }
|
||||
|
||||
public Object getInputMethodHighlight() { return imHighlight; }
|
||||
public void setInputMethodHighlight(Annotation f) {
|
||||
this.imHighlight = f; update(EINPUT_METHOD_HIGHLIGHT); }
|
||||
public void setInputMethodHighlight(InputMethodHighlight f) {
|
||||
this.imHighlight = f; update(EINPUT_METHOD_HIGHLIGHT); }
|
||||
|
||||
public int getInputMethodUnderline() { return imUnderline; }
|
||||
public void setInputMethodUnderline(int f) {
|
||||
this.imUnderline = (byte)f; update(EINPUT_METHOD_UNDERLINE); }
|
||||
|
||||
public boolean getSwapColors() { return swapColors; }
|
||||
public void setSwapColors(boolean f) {
|
||||
this.swapColors = f; update(ESWAP_COLORS); }
|
||||
|
||||
public NumericShaper getNumericShaping() { return numericShaping; }
|
||||
public void setNumericShaping(NumericShaper f) {
|
||||
this.numericShaping = f; update(ENUMERIC_SHAPING); }
|
||||
|
||||
public int getKerning() { return kerning; }
|
||||
public void setKerning(int f) {
|
||||
this.kerning = (byte)f; update(EKERNING); }
|
||||
|
||||
public float getTracking() { return tracking; }
|
||||
public void setTracking(float f) {
|
||||
this.tracking = (byte)f; update(ETRACKING); }
|
||||
|
||||
public int getLigatures() { return ligatures; }
|
||||
public void setLigatures(int f) {
|
||||
this.ligatures = (byte)f; update(ELIGATURES); }
|
||||
|
||||
|
||||
public AffineTransform getBaselineTransform() { return baselineTransform; }
|
||||
public AffineTransform getCharTransform() { return charTransform; }
|
||||
|
||||
// mask api
|
||||
|
||||
public static int getMask(EAttribute att) {
|
||||
return att.mask;
|
||||
}
|
||||
|
||||
public static int getMask(EAttribute ... atts) {
|
||||
int mask = 0;
|
||||
for (EAttribute a: atts) {
|
||||
mask |= a.mask;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
public static final int MASK_ALL =
|
||||
getMask(EAttribute.class.getEnumConstants());
|
||||
|
||||
public void unsetDefault() {
|
||||
defined &= nondefault;
|
||||
}
|
||||
|
||||
public void defineAll(int mask) {
|
||||
defined |= mask;
|
||||
if ((defined & EBASELINE_TRANSFORM.mask) != 0) {
|
||||
throw new InternalError("can't define derived attribute");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allDefined(int mask) {
|
||||
return (defined & mask) == mask;
|
||||
}
|
||||
|
||||
public boolean anyDefined(int mask) {
|
||||
return (defined & mask) != 0;
|
||||
}
|
||||
|
||||
public boolean anyNonDefault(int mask) {
|
||||
return (nondefault & mask) != 0;
|
||||
}
|
||||
|
||||
// generic EAttribute API
|
||||
|
||||
public boolean isDefined(EAttribute a) {
|
||||
return (defined & a.mask) != 0;
|
||||
}
|
||||
|
||||
public boolean isNonDefault(EAttribute a) {
|
||||
return (nondefault & a.mask) != 0;
|
||||
}
|
||||
|
||||
public void setDefault(EAttribute a) {
|
||||
if (a.att == null) {
|
||||
throw new InternalError("can't set default derived attribute: " + a);
|
||||
}
|
||||
i_set(a, DEFAULT);
|
||||
defined |= a.mask;
|
||||
nondefault &= ~a.mask;
|
||||
}
|
||||
|
||||
public void unset(EAttribute a) {
|
||||
if (a.att == null) {
|
||||
throw new InternalError("can't unset derived attribute: " + a);
|
||||
}
|
||||
i_set(a, DEFAULT);
|
||||
defined &= ~a.mask;
|
||||
nondefault &= ~a.mask;
|
||||
}
|
||||
|
||||
public void set(EAttribute a, AttributeValues src) {
|
||||
if (a.att == null) {
|
||||
throw new InternalError("can't set derived attribute: " + a);
|
||||
}
|
||||
if (src == null || src == DEFAULT) {
|
||||
setDefault(a);
|
||||
} else {
|
||||
if ((src.defined & a.mask) != 0) {
|
||||
i_set(a, src);
|
||||
update(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set(EAttribute a, Object o) {
|
||||
if (a.att == null) {
|
||||
throw new InternalError("can't set derived attribute: " + a);
|
||||
}
|
||||
if (o != null) {
|
||||
try {
|
||||
i_set(a, o);
|
||||
update(a);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
setDefault(a);
|
||||
}
|
||||
|
||||
public Object get(EAttribute a) {
|
||||
if (a.att == null) {
|
||||
throw new InternalError("can't get derived attribute: " + a);
|
||||
}
|
||||
if ((nondefault & a.mask) != 0) {
|
||||
return i_get(a);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// merging
|
||||
|
||||
public AttributeValues merge(Map<? extends Attribute, ?>map) {
|
||||
return merge(map, MASK_ALL);
|
||||
}
|
||||
|
||||
public AttributeValues merge(Map<? extends Attribute, ?>map,
|
||||
int mask) {
|
||||
if (map instanceof AttributeMap &&
|
||||
((AttributeMap) map).getValues() != null) {
|
||||
merge(((AttributeMap)map).getValues(), mask);
|
||||
} else if (map != null && !map.isEmpty()) {
|
||||
for (Map.Entry<? extends Attribute, ?> e: map.entrySet()) {
|
||||
try {
|
||||
EAttribute ea = EAttribute.forAttribute(e.getKey());
|
||||
if (ea!= null && (mask & ea.mask) != 0) {
|
||||
set(ea, e.getValue());
|
||||
}
|
||||
} catch (ClassCastException cce) {
|
||||
// IGNORED
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AttributeValues merge(AttributeValues src) {
|
||||
return merge(src, MASK_ALL);
|
||||
}
|
||||
|
||||
public AttributeValues merge(AttributeValues src, int mask) {
|
||||
int m = mask & src.defined;
|
||||
for (EAttribute ea: EAttribute.atts) {
|
||||
if (m == 0) {
|
||||
break;
|
||||
}
|
||||
if ((m & ea.mask) != 0) {
|
||||
m &= ~ea.mask;
|
||||
i_set(ea, src);
|
||||
update(ea);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// creation API
|
||||
|
||||
public static AttributeValues fromMap(Map<? extends Attribute, ?> map) {
|
||||
return fromMap(map, MASK_ALL);
|
||||
}
|
||||
|
||||
public static AttributeValues fromMap(Map<? extends Attribute, ?> map,
|
||||
int mask) {
|
||||
return new AttributeValues().merge(map, mask);
|
||||
}
|
||||
|
||||
public Map<TextAttribute, Object> toMap(Map<TextAttribute, Object> fill) {
|
||||
if (fill == null) {
|
||||
fill = new HashMap<TextAttribute, Object>();
|
||||
}
|
||||
|
||||
for (int m = defined, i = 0; m != 0; ++i) {
|
||||
EAttribute ea = EAttribute.atts[i];
|
||||
if ((m & ea.mask) != 0) {
|
||||
m &= ~ea.mask;
|
||||
fill.put(ea.att, get(ea));
|
||||
}
|
||||
}
|
||||
|
||||
return fill;
|
||||
}
|
||||
|
||||
// key must be serializable, so use String, not Object
|
||||
private static final String DEFINED_KEY =
|
||||
"sun.font.attributevalues.defined_key";
|
||||
|
||||
public static boolean is16Hashtable(Hashtable<Object, Object> ht) {
|
||||
return ht.containsKey(DEFINED_KEY);
|
||||
}
|
||||
|
||||
public static AttributeValues
|
||||
fromSerializableHashtable(Hashtable<Object, Object> ht)
|
||||
{
|
||||
AttributeValues result = new AttributeValues();
|
||||
if (ht != null && !ht.isEmpty()) {
|
||||
for (Map.Entry<Object, Object> e: ht.entrySet()) {
|
||||
Object key = e.getKey();
|
||||
Object val = e.getValue();
|
||||
if (key.equals(DEFINED_KEY)) {
|
||||
result.defineAll(((Integer)val).intValue());
|
||||
} else {
|
||||
try {
|
||||
EAttribute ea =
|
||||
EAttribute.forAttribute((Attribute)key);
|
||||
if (ea != null) {
|
||||
result.set(ea, val);
|
||||
}
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Hashtable<Object, Object> toSerializableHashtable() {
|
||||
Hashtable ht = new Hashtable();
|
||||
int hashkey = defined;
|
||||
for (int m = defined, i = 0; m != 0; ++i) {
|
||||
EAttribute ea = EAttribute.atts[i];
|
||||
if ((m & ea.mask) != 0) {
|
||||
m &= ~ea.mask;
|
||||
Object o = get(ea);
|
||||
if (o == null) {
|
||||
// hashkey will handle it
|
||||
} else if (o instanceof Serializable) { // check all...
|
||||
ht.put(ea.att, o);
|
||||
} else {
|
||||
hashkey &= ~ea.mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
ht.put(DEFINED_KEY, Integer.valueOf(hashkey));
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
// boilerplate
|
||||
public int hashCode() {
|
||||
return defined << 8 ^ nondefault;
|
||||
}
|
||||
|
||||
public boolean equals(Object rhs) {
|
||||
try {
|
||||
return equals((AttributeValues)rhs);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean equals(AttributeValues rhs) {
|
||||
// test in order of most likely to differ and easiest to compare
|
||||
// also assumes we're generally calling this only if family,
|
||||
// size, weight, posture are the same
|
||||
|
||||
if (rhs == null) return false;
|
||||
if (rhs == this) return true;
|
||||
|
||||
return defined == rhs.defined
|
||||
&& nondefault == rhs.nondefault
|
||||
&& underline == rhs.underline
|
||||
&& strikethrough == rhs.strikethrough
|
||||
&& superscript == rhs.superscript
|
||||
&& width == rhs.width
|
||||
&& kerning == rhs.kerning
|
||||
&& tracking == rhs.tracking
|
||||
&& ligatures == rhs.ligatures
|
||||
&& runDirection == rhs.runDirection
|
||||
&& bidiEmbedding == rhs.bidiEmbedding
|
||||
&& swapColors == rhs.swapColors
|
||||
&& equals(transform, rhs.transform)
|
||||
&& equals(foreground, rhs.foreground)
|
||||
&& equals(background, rhs.background)
|
||||
&& equals(numericShaping, rhs.numericShaping)
|
||||
&& equals(justification, rhs.justification)
|
||||
&& equals(charReplacement, rhs.charReplacement)
|
||||
&& size == rhs.size
|
||||
&& weight == rhs.weight
|
||||
&& posture == rhs.posture
|
||||
&& equals(family, rhs.family)
|
||||
&& equals(font, rhs.font)
|
||||
&& imUnderline == rhs.imUnderline
|
||||
&& equals(imHighlight, rhs.imHighlight);
|
||||
}
|
||||
|
||||
public AttributeValues clone() {
|
||||
try {
|
||||
AttributeValues result = (AttributeValues)super.clone();
|
||||
if (transform != null) { // AffineTransform is mutable
|
||||
result.transform = new AffineTransform(transform);
|
||||
result.updateDerivedTransforms();
|
||||
}
|
||||
// if transform is null, derived transforms are null
|
||||
// so there's nothing to do
|
||||
return result;
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
// never happens
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('{');
|
||||
for (int m = defined, i = 0; m != 0; ++i) {
|
||||
EAttribute ea = EAttribute.atts[i];
|
||||
if ((m & ea.mask) != 0) {
|
||||
m &= ~ea.mask;
|
||||
if (b.length() > 1) {
|
||||
b.append(", ");
|
||||
}
|
||||
b.append(ea);
|
||||
b.append('=');
|
||||
switch (ea) {
|
||||
case EFAMILY: b.append('"');
|
||||
b.append(family);
|
||||
b.append('"'); break;
|
||||
case EWEIGHT: b.append(weight); break;
|
||||
case EWIDTH: b.append(width); break;
|
||||
case EPOSTURE: b.append(posture); break;
|
||||
case ESIZE: b.append(size); break;
|
||||
case ETRANSFORM: b.append(transform); break;
|
||||
case ESUPERSCRIPT: b.append(superscript); break;
|
||||
case EFONT: b.append(font); break;
|
||||
case ECHAR_REPLACEMENT: b.append(charReplacement); break;
|
||||
case EFOREGROUND: b.append(foreground); break;
|
||||
case EBACKGROUND: b.append(background); break;
|
||||
case EUNDERLINE: b.append(underline); break;
|
||||
case ESTRIKETHROUGH: b.append(strikethrough); break;
|
||||
case ERUN_DIRECTION: b.append(runDirection); break;
|
||||
case EBIDI_EMBEDDING: b.append(bidiEmbedding); break;
|
||||
case EJUSTIFICATION: b.append(justification); break;
|
||||
case EINPUT_METHOD_HIGHLIGHT: b.append(imHighlight); break;
|
||||
case EINPUT_METHOD_UNDERLINE: b.append(imUnderline); break;
|
||||
case ESWAP_COLORS: b.append(swapColors); break;
|
||||
case ENUMERIC_SHAPING: b.append(numericShaping); break;
|
||||
case EKERNING: b.append(kerning); break;
|
||||
case ELIGATURES: b.append(ligatures); break;
|
||||
case ETRACKING: b.append(tracking); break;
|
||||
default: throw new InternalError();
|
||||
}
|
||||
if ((nondefault & ea.mask) == 0) {
|
||||
b.append('*');
|
||||
}
|
||||
}
|
||||
}
|
||||
b.append("[btx=" + baselineTransform + ", ctx=" + charTransform + "]");
|
||||
b.append('}');
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
// internal utilities
|
||||
|
||||
private static boolean equals(Object lhs, Object rhs) {
|
||||
return lhs == null ? rhs == null : lhs.equals(rhs);
|
||||
}
|
||||
|
||||
private void update(EAttribute a) {
|
||||
defined |= a.mask;
|
||||
if (i_validate(a)) {
|
||||
if (i_equals(a, DEFAULT)) {
|
||||
nondefault &= ~a.mask;
|
||||
} else {
|
||||
nondefault |= a.mask;
|
||||
}
|
||||
} else {
|
||||
setDefault(a);
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch
|
||||
|
||||
private void i_set(EAttribute a, AttributeValues src) {
|
||||
switch (a) {
|
||||
case EFAMILY: family = src.family; break;
|
||||
case EWEIGHT: weight = src.weight; break;
|
||||
case EWIDTH: width = src.width; break;
|
||||
case EPOSTURE: posture = src.posture; break;
|
||||
case ESIZE: size = src.size; break;
|
||||
case ETRANSFORM: transform = src.transform; updateDerivedTransforms(); break;
|
||||
case ESUPERSCRIPT: superscript = src.superscript; break;
|
||||
case EFONT: font = src.font; break;
|
||||
case ECHAR_REPLACEMENT: charReplacement = src.charReplacement; break;
|
||||
case EFOREGROUND: foreground = src.foreground; break;
|
||||
case EBACKGROUND: background = src.background; break;
|
||||
case EUNDERLINE: underline = src.underline; break;
|
||||
case ESTRIKETHROUGH: strikethrough = src.strikethrough; break;
|
||||
case ERUN_DIRECTION: runDirection = src.runDirection; break;
|
||||
case EBIDI_EMBEDDING: bidiEmbedding = src.bidiEmbedding; break;
|
||||
case EJUSTIFICATION: justification = src.justification; break;
|
||||
case EINPUT_METHOD_HIGHLIGHT: imHighlight = src.imHighlight; break;
|
||||
case EINPUT_METHOD_UNDERLINE: imUnderline = src.imUnderline; break;
|
||||
case ESWAP_COLORS: swapColors = src.swapColors; break;
|
||||
case ENUMERIC_SHAPING: numericShaping = src.numericShaping; break;
|
||||
case EKERNING: kerning = src.kerning; break;
|
||||
case ELIGATURES: ligatures = src.ligatures; break;
|
||||
case ETRACKING: tracking = src.tracking; break;
|
||||
default: throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean i_equals(EAttribute a, AttributeValues src) {
|
||||
switch (a) {
|
||||
case EFAMILY: return equals(family, src.family);
|
||||
case EWEIGHT: return weight == src.weight;
|
||||
case EWIDTH: return width == src.width;
|
||||
case EPOSTURE: return posture == src.posture;
|
||||
case ESIZE: return size == src.size;
|
||||
case ETRANSFORM: return equals(transform, src.transform);
|
||||
case ESUPERSCRIPT: return superscript == src.superscript;
|
||||
case EFONT: return equals(font, src.font);
|
||||
case ECHAR_REPLACEMENT: return equals(charReplacement, src.charReplacement);
|
||||
case EFOREGROUND: return equals(foreground, src.foreground);
|
||||
case EBACKGROUND: return equals(background, src.background);
|
||||
case EUNDERLINE: return underline == src.underline;
|
||||
case ESTRIKETHROUGH: return strikethrough == src.strikethrough;
|
||||
case ERUN_DIRECTION: return runDirection == src.runDirection;
|
||||
case EBIDI_EMBEDDING: return bidiEmbedding == src.bidiEmbedding;
|
||||
case EJUSTIFICATION: return justification == src.justification;
|
||||
case EINPUT_METHOD_HIGHLIGHT: return equals(imHighlight, src.imHighlight);
|
||||
case EINPUT_METHOD_UNDERLINE: return imUnderline == src.imUnderline;
|
||||
case ESWAP_COLORS: return swapColors == src.swapColors;
|
||||
case ENUMERIC_SHAPING: return equals(numericShaping, src.numericShaping);
|
||||
case EKERNING: return kerning == src.kerning;
|
||||
case ELIGATURES: return ligatures == src.ligatures;
|
||||
case ETRACKING: return tracking == src.tracking;
|
||||
default: throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
private void i_set(EAttribute a, Object o) {
|
||||
switch (a) {
|
||||
case EFAMILY: family = ((String)o).trim(); break;
|
||||
case EWEIGHT: weight = ((Number)o).floatValue(); break;
|
||||
case EWIDTH: width = ((Number)o).floatValue(); break;
|
||||
case EPOSTURE: posture = ((Number)o).floatValue(); break;
|
||||
case ESIZE: size = ((Number)o).floatValue(); break;
|
||||
case ETRANSFORM: {
|
||||
if (o instanceof TransformAttribute) {
|
||||
TransformAttribute ta = (TransformAttribute)o;
|
||||
if (ta.isIdentity()) {
|
||||
transform = null;
|
||||
} else {
|
||||
transform = ta.getTransform();
|
||||
}
|
||||
} else {
|
||||
transform = new AffineTransform((AffineTransform)o);
|
||||
}
|
||||
updateDerivedTransforms();
|
||||
} break;
|
||||
case ESUPERSCRIPT: superscript = (byte)((Integer)o).intValue(); break;
|
||||
case EFONT: font = (Font)o; break;
|
||||
case ECHAR_REPLACEMENT: charReplacement = (GraphicAttribute)o; break;
|
||||
case EFOREGROUND: foreground = (Paint)o; break;
|
||||
case EBACKGROUND: background = (Paint)o; break;
|
||||
case EUNDERLINE: underline = (byte)((Integer)o).intValue(); break;
|
||||
case ESTRIKETHROUGH: strikethrough = ((Boolean)o).booleanValue(); break;
|
||||
case ERUN_DIRECTION: {
|
||||
if (o instanceof Boolean) {
|
||||
runDirection = (byte)(TextAttribute.RUN_DIRECTION_LTR.equals(o) ? 0 : 1);
|
||||
} else {
|
||||
runDirection = (byte)((Integer)o).intValue();
|
||||
}
|
||||
} break;
|
||||
case EBIDI_EMBEDDING: bidiEmbedding = (byte)((Integer)o).intValue(); break;
|
||||
case EJUSTIFICATION: justification = ((Number)o).floatValue(); break;
|
||||
case EINPUT_METHOD_HIGHLIGHT: {
|
||||
if (o instanceof Annotation) {
|
||||
Annotation at = (Annotation)o;
|
||||
imHighlight = (InputMethodHighlight)at.getValue();
|
||||
} else {
|
||||
imHighlight = (InputMethodHighlight)o;
|
||||
}
|
||||
} break;
|
||||
case EINPUT_METHOD_UNDERLINE: imUnderline = (byte)((Integer)o).intValue();
|
||||
break;
|
||||
case ESWAP_COLORS: swapColors = ((Boolean)o).booleanValue(); break;
|
||||
case ENUMERIC_SHAPING: numericShaping = (NumericShaper)o; break;
|
||||
case EKERNING: kerning = (byte)((Integer)o).intValue(); break;
|
||||
case ELIGATURES: ligatures = (byte)((Integer)o).intValue(); break;
|
||||
case ETRACKING: tracking = ((Number)o).floatValue(); break;
|
||||
default: throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
private Object i_get(EAttribute a) {
|
||||
switch (a) {
|
||||
case EFAMILY: return family;
|
||||
case EWEIGHT: return Float.valueOf(weight);
|
||||
case EWIDTH: return Float.valueOf(width);
|
||||
case EPOSTURE: return Float.valueOf(posture);
|
||||
case ESIZE: return Float.valueOf(size);
|
||||
case ETRANSFORM:
|
||||
return transform == null
|
||||
? TransformAttribute.IDENTITY
|
||||
: new TransformAttribute(transform);
|
||||
case ESUPERSCRIPT: return Integer.valueOf(superscript);
|
||||
case EFONT: return font;
|
||||
case ECHAR_REPLACEMENT: return charReplacement;
|
||||
case EFOREGROUND: return foreground;
|
||||
case EBACKGROUND: return background;
|
||||
case EUNDERLINE: return Integer.valueOf(underline);
|
||||
case ESTRIKETHROUGH: return Boolean.valueOf(strikethrough);
|
||||
case ERUN_DIRECTION: {
|
||||
switch (runDirection) {
|
||||
// todo: figure out a way to indicate this value
|
||||
// case -1: return Integer.valueOf(runDirection);
|
||||
case 0: return TextAttribute.RUN_DIRECTION_LTR;
|
||||
case 1: return TextAttribute.RUN_DIRECTION_RTL;
|
||||
default: return null;
|
||||
}
|
||||
} // not reachable
|
||||
case EBIDI_EMBEDDING: return Integer.valueOf(bidiEmbedding);
|
||||
case EJUSTIFICATION: return Float.valueOf(justification);
|
||||
case EINPUT_METHOD_HIGHLIGHT: return imHighlight;
|
||||
case EINPUT_METHOD_UNDERLINE: return Integer.valueOf(imUnderline);
|
||||
case ESWAP_COLORS: return Boolean.valueOf(swapColors);
|
||||
case ENUMERIC_SHAPING: return numericShaping;
|
||||
case EKERNING: return Integer.valueOf(kerning);
|
||||
case ELIGATURES: return Integer.valueOf(ligatures);
|
||||
case ETRACKING: return Float.valueOf(tracking);
|
||||
default: throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean i_validate(EAttribute a) {
|
||||
switch (a) {
|
||||
case EFAMILY: if (family == null || family.length() == 0)
|
||||
family = DEFAULT.family; return true;
|
||||
case EWEIGHT: return weight > 0 && weight < 10;
|
||||
case EWIDTH: return width >= .5f && width < 10;
|
||||
case EPOSTURE: return posture >= -1 && posture <= 1;
|
||||
case ESIZE: return size >= 0;
|
||||
case ETRANSFORM: if (transform != null && transform.isIdentity())
|
||||
transform = DEFAULT.transform; return true;
|
||||
case ESUPERSCRIPT: return superscript >= -7 && superscript <= 7;
|
||||
case EFONT: return true;
|
||||
case ECHAR_REPLACEMENT: return true;
|
||||
case EFOREGROUND: return true;
|
||||
case EBACKGROUND: return true;
|
||||
case EUNDERLINE: return underline >= -1 && underline < 6;
|
||||
case ESTRIKETHROUGH: return true;
|
||||
case ERUN_DIRECTION: return runDirection >= -2 && runDirection <= 1;
|
||||
case EBIDI_EMBEDDING: return bidiEmbedding >= -61 && bidiEmbedding < 62;
|
||||
case EJUSTIFICATION: justification = max(0, min (justification, 1));
|
||||
return true;
|
||||
case EINPUT_METHOD_HIGHLIGHT: return true;
|
||||
case EINPUT_METHOD_UNDERLINE: return imUnderline >= -1 && imUnderline < 6;
|
||||
case ESWAP_COLORS: return true;
|
||||
case ENUMERIC_SHAPING: return true;
|
||||
case EKERNING: return kerning >= 0 && kerning <= 1;
|
||||
case ELIGATURES: return ligatures >= 0 && ligatures <= 1;
|
||||
case ETRACKING: return tracking >= -1 && tracking <= 10;
|
||||
default: throw new InternalError("unknown attribute: " + a);
|
||||
}
|
||||
}
|
||||
|
||||
// Until textlayout is fixed to use AttributeValues, we'll end up
|
||||
// creating a map from the values for it. This is a compromise between
|
||||
// creating the whole map and just checking a particular value.
|
||||
// Plan to remove these.
|
||||
public static float getJustification(Map<?, ?> map) {
|
||||
if (map != null) {
|
||||
if (map instanceof AttributeMap &&
|
||||
((AttributeMap) map).getValues() != null) {
|
||||
return ((AttributeMap)map).getValues().justification;
|
||||
}
|
||||
Object obj = map.get(TextAttribute.JUSTIFICATION);
|
||||
if (obj != null && obj instanceof Number) {
|
||||
return max(0, min(1, ((Number)obj).floatValue()));
|
||||
}
|
||||
}
|
||||
return DEFAULT.justification;
|
||||
}
|
||||
|
||||
public static NumericShaper getNumericShaping(Map<?, ?> map) {
|
||||
if (map != null) {
|
||||
if (map instanceof AttributeMap &&
|
||||
((AttributeMap) map).getValues() != null) {
|
||||
return ((AttributeMap)map).getValues().numericShaping;
|
||||
}
|
||||
Object obj = map.get(TextAttribute.NUMERIC_SHAPING);
|
||||
if (obj != null && obj instanceof NumericShaper) {
|
||||
return (NumericShaper)obj;
|
||||
}
|
||||
}
|
||||
return DEFAULT.numericShaping;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this has an imHighlight, create copy of this with those attributes
|
||||
* applied to it. Otherwise return this unchanged.
|
||||
*/
|
||||
public AttributeValues applyIMHighlight() {
|
||||
if (imHighlight != null) {
|
||||
InputMethodHighlight hl = null;
|
||||
if (imHighlight instanceof InputMethodHighlight) {
|
||||
hl = (InputMethodHighlight)imHighlight;
|
||||
} else {
|
||||
hl = (InputMethodHighlight)((Annotation)imHighlight).getValue();
|
||||
}
|
||||
|
||||
Map imStyles = hl.getStyle();
|
||||
if (imStyles == null) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
imStyles = tk.mapInputMethodHighlight(hl);
|
||||
}
|
||||
|
||||
if (imStyles != null) {
|
||||
return clone().merge(imStyles);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static AffineTransform getBaselineTransform(Map<?, ?> map) {
|
||||
if (map != null) {
|
||||
AttributeValues av = null;
|
||||
if (map instanceof AttributeMap &&
|
||||
((AttributeMap) map).getValues() != null) {
|
||||
av = ((AttributeMap)map).getValues();
|
||||
} else if (map.get(TextAttribute.TRANSFORM) != null) {
|
||||
av = AttributeValues.fromMap((Map<Attribute, ?>)map); // yuck
|
||||
}
|
||||
if (av != null) {
|
||||
return av.baselineTransform;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AffineTransform getCharTransform(Map<?, ?> map) {
|
||||
if (map != null) {
|
||||
AttributeValues av = null;
|
||||
if (map instanceof AttributeMap &&
|
||||
((AttributeMap) map).getValues() != null) {
|
||||
av = ((AttributeMap)map).getValues();
|
||||
} else if (map.get(TextAttribute.TRANSFORM) != null) {
|
||||
av = AttributeValues.fromMap((Map<Attribute, ?>)map); // yuck
|
||||
}
|
||||
if (av != null) {
|
||||
return av.charTransform;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateDerivedTransforms() {
|
||||
// this also updates the mask for the baseline transform
|
||||
if (transform == null) {
|
||||
baselineTransform = null;
|
||||
charTransform = null;
|
||||
} else {
|
||||
charTransform = new AffineTransform(transform);
|
||||
baselineTransform = extractXRotation(charTransform, true);
|
||||
|
||||
if (charTransform.isIdentity()) {
|
||||
charTransform = null;
|
||||
}
|
||||
|
||||
if (baselineTransform.isIdentity()) {
|
||||
baselineTransform = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (baselineTransform == null) {
|
||||
nondefault &= ~EBASELINE_TRANSFORM.mask;
|
||||
} else {
|
||||
nondefault |= EBASELINE_TRANSFORM.mask;
|
||||
}
|
||||
}
|
||||
|
||||
public static AffineTransform extractXRotation(AffineTransform tx,
|
||||
boolean andTranslation) {
|
||||
return extractRotation(new Point2D.Double(1, 0), tx, andTranslation);
|
||||
}
|
||||
|
||||
public static AffineTransform extractYRotation(AffineTransform tx,
|
||||
boolean andTranslation) {
|
||||
return extractRotation(new Point2D.Double(0, 1), tx, andTranslation);
|
||||
}
|
||||
|
||||
private static AffineTransform extractRotation(Point2D.Double pt,
|
||||
AffineTransform tx, boolean andTranslation) {
|
||||
|
||||
tx.deltaTransform(pt, pt);
|
||||
AffineTransform rtx = AffineTransform.getRotateInstance(pt.x, pt.y);
|
||||
|
||||
try {
|
||||
AffineTransform rtxi = rtx.createInverse();
|
||||
double dx = tx.getTranslateX();
|
||||
double dy = tx.getTranslateY();
|
||||
tx.preConcatenate(rtxi);
|
||||
if (andTranslation) {
|
||||
if (dx != 0 || dy != 0) {
|
||||
tx.setTransform(tx.getScaleX(), tx.getShearY(),
|
||||
tx.getShearX(), tx.getScaleY(), 0, 0);
|
||||
rtx.setTransform(rtx.getScaleX(), rtx.getShearY(),
|
||||
rtx.getShearX(), rtx.getScaleY(), dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoninvertibleTransformException e) {
|
||||
return null;
|
||||
}
|
||||
return rtx;
|
||||
}
|
||||
}
|
||||
386
jdkSrc/jdk8/sun/font/BidiUtils.java
Normal file
386
jdkSrc/jdk8/sun/font/BidiUtils.java
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 1999-2000 - All Rights Reserved
|
||||
*
|
||||
* The original version of this source code and documentation is
|
||||
* copyrighted and owned by IBM. These materials are provided
|
||||
* under terms of a License Agreement between IBM and Sun.
|
||||
* This technology is protected by multiple US and International
|
||||
* patents. This notice and attribution to IBM may not be removed.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.text.Bidi;
|
||||
|
||||
public final class BidiUtils {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the level of each character into the levels array starting at start.
|
||||
* This is a convenience method for clients who prefer to use an explicit levels
|
||||
* array instead of iterating over the runs.
|
||||
*
|
||||
* @param levels the array to receive the character levels
|
||||
* @param start the starting offset into the the array
|
||||
* @throws IndexOutOfBoundsException if <code>start</code> is less than 0 or
|
||||
* <code>start + getLength()</code> is greater than <code>levels.length</code>.
|
||||
*/
|
||||
public static void getLevels(Bidi bidi, byte[] levels, int start) {
|
||||
int limit = start + bidi.getLength();
|
||||
|
||||
if (start < 0 || limit > levels.length) {
|
||||
throw new IndexOutOfBoundsException("levels.length = " + levels.length +
|
||||
" start: " + start + " limit: " + limit);
|
||||
}
|
||||
|
||||
int runCount = bidi.getRunCount();
|
||||
int p = start;
|
||||
for (int i = 0; i < runCount; ++i) {
|
||||
int rlimit = start + bidi.getRunLimit(i);
|
||||
byte rlevel = (byte)bidi.getRunLevel(i);
|
||||
|
||||
while (p < rlimit) {
|
||||
levels[p++] = rlevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array containing the resolved bidi level of each character, in logical order.
|
||||
* @return an array containing the level of each character, in logical order.
|
||||
*/
|
||||
public static byte[] getLevels(Bidi bidi) {
|
||||
byte[] levels = new byte[bidi.getLength()];
|
||||
getLevels(bidi, levels, 0);
|
||||
return levels;
|
||||
}
|
||||
|
||||
static final char NUMLEVELS = 62;
|
||||
|
||||
/**
|
||||
* Given level data, compute a a visual to logical mapping.
|
||||
* The leftmost (or topmost) character is at visual index zero. The
|
||||
* logical index of the character is derived from the visual index
|
||||
* by the expression <code>li = map[vi];</code>.
|
||||
* @param levels the levels array
|
||||
* @return the mapping array from visual to logical
|
||||
*/
|
||||
public static int[] createVisualToLogicalMap(byte[] levels) {
|
||||
int len = levels.length;
|
||||
int[] mapping = new int[len];
|
||||
|
||||
byte lowestOddLevel = (byte)(NUMLEVELS + 1);
|
||||
byte highestLevel = 0;
|
||||
|
||||
// initialize mapping and levels
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
mapping[i] = i;
|
||||
|
||||
byte level = levels[i];
|
||||
if (level > highestLevel) {
|
||||
highestLevel = level;
|
||||
}
|
||||
|
||||
if ((level & 0x01) != 0 && level < lowestOddLevel) {
|
||||
lowestOddLevel = level;
|
||||
}
|
||||
}
|
||||
|
||||
while (highestLevel >= lowestOddLevel) {
|
||||
int i = 0;
|
||||
for (;;) {
|
||||
while (i < len && levels[i] < highestLevel) {
|
||||
i++;
|
||||
}
|
||||
int begin = i++;
|
||||
|
||||
if (begin == levels.length) {
|
||||
break; // no more runs at this level
|
||||
}
|
||||
|
||||
while (i < len && levels[i] >= highestLevel) {
|
||||
i++;
|
||||
}
|
||||
int end = i - 1;
|
||||
|
||||
while (begin < end) {
|
||||
int temp = mapping[begin];
|
||||
mapping[begin] = mapping[end];
|
||||
mapping[end] = temp;
|
||||
++begin;
|
||||
--end;
|
||||
}
|
||||
}
|
||||
|
||||
--highestLevel;
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inverse position map. The source array must map one-to-one (each value
|
||||
* is distinct and the values run from zero to the length of the array minus one).
|
||||
* For example, if <code>values[i] = j</code>, then <code>inverse[j] = i</code>.
|
||||
* @param values the source ordering array
|
||||
* @return the inverse array
|
||||
*/
|
||||
public static int[] createInverseMap(int[] values) {
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] result = new int[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
result[values[i]] = i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an array containing contiguous values from 0 to length
|
||||
* having the same ordering as the source array. If this would be
|
||||
* a canonical ltr ordering, return null. The data in values[] is NOT
|
||||
* required to be a permutation, but elements in values are required
|
||||
* to be distinct.
|
||||
* @param values an array containing the discontiguous values
|
||||
* @return the contiguous values
|
||||
*/
|
||||
public static int[] createContiguousOrder(int[] values) {
|
||||
if (values != null) {
|
||||
return computeContiguousOrder(values, 0, values.length);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a contiguous order for the range start, limit.
|
||||
*/
|
||||
private static int[] computeContiguousOrder(int[] values, int start,
|
||||
int limit) {
|
||||
|
||||
int[] result = new int[limit-start];
|
||||
for (int i=0; i < result.length; i++) {
|
||||
result[i] = i + start;
|
||||
}
|
||||
|
||||
// now we'll sort result[], with the following comparison:
|
||||
// result[i] lessthan result[j] iff values[result[i]] < values[result[j]]
|
||||
|
||||
// selection sort for now; use more elaborate sorts if desired
|
||||
for (int i=0; i < result.length-1; i++) {
|
||||
int minIndex = i;
|
||||
int currentValue = values[result[minIndex]];
|
||||
for (int j=i; j < result.length; j++) {
|
||||
if (values[result[j]] < currentValue) {
|
||||
minIndex = j;
|
||||
currentValue = values[result[minIndex]];
|
||||
}
|
||||
}
|
||||
int temp = result[i];
|
||||
result[i] = result[minIndex];
|
||||
result[minIndex] = temp;
|
||||
}
|
||||
|
||||
// shift result by start:
|
||||
if (start != 0) {
|
||||
for (int i=0; i < result.length; i++) {
|
||||
result[i] -= start;
|
||||
}
|
||||
}
|
||||
|
||||
// next, check for canonical order:
|
||||
int k;
|
||||
for (k=0; k < result.length; k++) {
|
||||
if (result[k] != k) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (k == result.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// now return inverse of result:
|
||||
return createInverseMap(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array containing the data in the values array from start up to limit,
|
||||
* normalized to fall within the range from 0 up to limit - start.
|
||||
* If this would be a canonical ltr ordering, return null.
|
||||
* NOTE: This method assumes that values[] is a logical to visual map
|
||||
* generated from levels[].
|
||||
* @param values the source mapping
|
||||
* @param levels the levels corresponding to the values
|
||||
* @param start the starting offset in the values and levels arrays
|
||||
* @param limit the limiting offset in the values and levels arrays
|
||||
* @return the normlized map
|
||||
*/
|
||||
public static int[] createNormalizedMap(int[] values, byte[] levels,
|
||||
int start, int limit) {
|
||||
|
||||
if (values != null) {
|
||||
if (start != 0 || limit != values.length) {
|
||||
// levels optimization
|
||||
boolean copyRange, canonical;
|
||||
byte primaryLevel;
|
||||
|
||||
if (levels == null) {
|
||||
primaryLevel = (byte) 0x0;
|
||||
copyRange = true;
|
||||
canonical = true;
|
||||
}
|
||||
else {
|
||||
if (levels[start] == levels[limit-1]) {
|
||||
primaryLevel = levels[start];
|
||||
canonical = (primaryLevel & (byte)0x1) == 0;
|
||||
|
||||
// scan for levels below primary
|
||||
int i;
|
||||
for (i=start; i < limit; i++) {
|
||||
if (levels[i] < primaryLevel) {
|
||||
break;
|
||||
}
|
||||
if (canonical) {
|
||||
canonical = levels[i] == primaryLevel;
|
||||
}
|
||||
}
|
||||
|
||||
copyRange = (i == limit);
|
||||
}
|
||||
else {
|
||||
copyRange = false;
|
||||
|
||||
// these don't matter; but the compiler cares:
|
||||
primaryLevel = (byte) 0x0;
|
||||
canonical = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (copyRange) {
|
||||
if (canonical) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] result = new int[limit-start];
|
||||
int baseValue;
|
||||
|
||||
if ((primaryLevel & (byte)0x1) != 0) {
|
||||
baseValue = values[limit-1];
|
||||
} else {
|
||||
baseValue = values[start];
|
||||
}
|
||||
|
||||
if (baseValue == 0) {
|
||||
System.arraycopy(values, start, result, 0, limit-start);
|
||||
}
|
||||
else {
|
||||
for (int j=0; j < result.length; j++) {
|
||||
result[j] = values[j+start] - baseValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return computeContiguousOrder(values, start, limit);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder the objects in the array into visual order based on their levels.
|
||||
* This is a utility function to use when you have a collection of objects
|
||||
* representing runs of text in logical order, each run containing text
|
||||
* at a single level. The elements in the objects array will be reordered
|
||||
* into visual order assuming each run of text has the level provided
|
||||
* by the corresponding element in the levels array.
|
||||
* @param levels an array representing the bidi level of each object
|
||||
* @param objects the array of objects to be reordered into visual order
|
||||
*/
|
||||
public static void reorderVisually(byte[] levels, Object[] objects) {
|
||||
int len = levels.length;
|
||||
|
||||
byte lowestOddLevel = (byte)(NUMLEVELS + 1);
|
||||
byte highestLevel = 0;
|
||||
|
||||
// initialize mapping and levels
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
byte level = levels[i];
|
||||
if (level > highestLevel) {
|
||||
highestLevel = level;
|
||||
}
|
||||
|
||||
if ((level & 0x01) != 0 && level < lowestOddLevel) {
|
||||
lowestOddLevel = level;
|
||||
}
|
||||
}
|
||||
|
||||
while (highestLevel >= lowestOddLevel) {
|
||||
int i = 0;
|
||||
for (;;) {
|
||||
while (i < len && levels[i] < highestLevel) {
|
||||
i++;
|
||||
}
|
||||
int begin = i++;
|
||||
|
||||
if (begin == levels.length) {
|
||||
break; // no more runs at this level
|
||||
}
|
||||
|
||||
while (i < len && levels[i] >= highestLevel) {
|
||||
i++;
|
||||
}
|
||||
int end = i - 1;
|
||||
|
||||
while (begin < end) {
|
||||
Object temp = objects[begin];
|
||||
objects[begin] = objects[end];
|
||||
objects[end] = temp;
|
||||
++begin;
|
||||
--end;
|
||||
}
|
||||
}
|
||||
|
||||
--highestLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
1074
jdkSrc/jdk8/sun/font/CMap.java
Normal file
1074
jdkSrc/jdk8/sun/font/CMap.java
Normal file
File diff suppressed because it is too large
Load Diff
91
jdkSrc/jdk8/sun/font/CharToGlyphMapper.java
Normal file
91
jdkSrc/jdk8/sun/font/CharToGlyphMapper.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/*
|
||||
* NB the versions that take a char as an int are used by the opentype
|
||||
* layout engine. If that remains in native these methods may not be
|
||||
* needed in the Java class.
|
||||
*/
|
||||
public abstract class CharToGlyphMapper {
|
||||
|
||||
public static final int HI_SURROGATE_START = 0xD800;
|
||||
public static final int HI_SURROGATE_END = 0xDBFF;
|
||||
public static final int LO_SURROGATE_START = 0xDC00;
|
||||
public static final int LO_SURROGATE_END = 0xDFFF;
|
||||
|
||||
public static final int UNINITIALIZED_GLYPH = -1;
|
||||
public static final int INVISIBLE_GLYPH_ID = 0xffff;
|
||||
public static final int INVISIBLE_GLYPHS = 0xfffe; // and above
|
||||
|
||||
protected int missingGlyph = CharToGlyphMapper.UNINITIALIZED_GLYPH;
|
||||
|
||||
public int getMissingGlyphCode() {
|
||||
return missingGlyph;
|
||||
}
|
||||
|
||||
/* Default implementations of these methods may be overridden by
|
||||
* subclasses which have special requirements or optimisations
|
||||
*/
|
||||
|
||||
public boolean canDisplay(char ch) {
|
||||
int glyph = charToGlyph(ch);
|
||||
return glyph != missingGlyph;
|
||||
}
|
||||
|
||||
public boolean canDisplay(int cp) {
|
||||
int glyph = charToGlyph(cp);
|
||||
return glyph != missingGlyph;
|
||||
}
|
||||
|
||||
public int charToGlyph(char unicode) {
|
||||
char[] chars = new char[1];
|
||||
int[] glyphs = new int[1];
|
||||
chars[0] = unicode;
|
||||
charsToGlyphs(1, chars, glyphs);
|
||||
return glyphs[0];
|
||||
}
|
||||
|
||||
public int charToGlyph(int unicode) {
|
||||
int[] chars = new int[1];
|
||||
int [] glyphs = new int[1];
|
||||
chars[0] = unicode;
|
||||
charsToGlyphs(1, chars, glyphs);
|
||||
return glyphs[0];
|
||||
}
|
||||
|
||||
public abstract int getNumGlyphs();
|
||||
|
||||
public abstract void charsToGlyphs(int count,
|
||||
char[] unicodes, int[] glyphs);
|
||||
|
||||
public abstract boolean charsToGlyphsNS(int count,
|
||||
char[] unicodes, int[] glyphs);
|
||||
|
||||
public abstract void charsToGlyphs(int count,
|
||||
int[] unicodes, int[] glyphs);
|
||||
|
||||
}
|
||||
501
jdkSrc/jdk8/sun/font/CompositeFont.java
Normal file
501
jdkSrc/jdk8/sun/font/CompositeFont.java
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
/* Remind: need to enhance to extend component list with a fallback
|
||||
* list, which is not used in metrics or queries on the composite, but
|
||||
* is used in drawing primitives and queries which supply an actual string.
|
||||
* ie for a codepoint that is only in a fallback, font-wide queries such
|
||||
* as FontMetrics.getHeight() will not take it into account.
|
||||
* But getStringBounds(..) would take it into account.
|
||||
* Its fuzzier for queries such as "canDisplay". If this does not include
|
||||
* the fallback, then we probably want to add "canDisplayFallback()"
|
||||
* But its probably OK to include it so long as only composites include
|
||||
* fallbacks. If physicals do then it would be really confusing ..
|
||||
*/
|
||||
public final class CompositeFont extends Font2D {
|
||||
|
||||
private boolean[] deferredInitialisation;
|
||||
String[] componentFileNames;
|
||||
String[] componentNames;
|
||||
/* because components can be lazily initialised the components field is
|
||||
* private, to ensure all clients call getSlotFont()
|
||||
*/
|
||||
private PhysicalFont[] components;
|
||||
int numSlots;
|
||||
int numMetricsSlots;
|
||||
int[] exclusionRanges;
|
||||
int[] maxIndices;
|
||||
int numGlyphs = 0;
|
||||
int localeSlot = -1; // primary slot for this locale.
|
||||
|
||||
/* See isStdComposite() for when/how this is used */
|
||||
boolean isStdComposite = true;
|
||||
|
||||
public CompositeFont(String name, String[] compFileNames,
|
||||
String[] compNames, int metricsSlotCnt,
|
||||
int[] exclRanges, int[] maxIndexes,
|
||||
boolean defer, SunFontManager fm) {
|
||||
|
||||
handle = new Font2DHandle(this);
|
||||
fullName = name;
|
||||
componentFileNames = compFileNames;
|
||||
componentNames = compNames;
|
||||
if (compNames == null) {
|
||||
numSlots = componentFileNames.length;
|
||||
} else {
|
||||
numSlots = componentNames.length;
|
||||
}
|
||||
/* We will limit the number of slots to 254.
|
||||
* We store the slot for a glyph id in a byte and we may use one slot
|
||||
* for an EUDC font, and we may also create a composite
|
||||
* using this composite as a backup for a physical font.
|
||||
* So we want to leave space for the two additional slots.
|
||||
*/
|
||||
numSlots = (numSlots <= 254) ? numSlots : 254;
|
||||
|
||||
/* Only the first "numMetricsSlots" slots are used for font metrics.
|
||||
* the rest are considered "fallback" slots".
|
||||
*/
|
||||
numMetricsSlots = metricsSlotCnt;
|
||||
exclusionRanges = exclRanges;
|
||||
maxIndices = maxIndexes;
|
||||
|
||||
/*
|
||||
* See if this is a windows locale which has a system EUDC font.
|
||||
* If so add it as the final fallback component of the composite.
|
||||
* The caller could be responsible for this, but for now it seems
|
||||
* better that it is handled internally to the CompositeFont class.
|
||||
*/
|
||||
if (fm.getEUDCFont() != null) {
|
||||
int msCnt = numMetricsSlots;
|
||||
int fbCnt = numSlots - msCnt;
|
||||
numSlots++;
|
||||
if (componentNames != null) {
|
||||
componentNames = new String[numSlots];
|
||||
System.arraycopy(compNames, 0, componentNames, 0, msCnt);
|
||||
componentNames[msCnt] = fm.getEUDCFont().getFontName(null);
|
||||
System.arraycopy(compNames, msCnt,
|
||||
componentNames, msCnt+1, fbCnt);
|
||||
}
|
||||
if (componentFileNames != null) {
|
||||
componentFileNames = new String[numSlots];
|
||||
System.arraycopy(compFileNames, 0,
|
||||
componentFileNames, 0, msCnt);
|
||||
System.arraycopy(compFileNames, msCnt,
|
||||
componentFileNames, msCnt+1, fbCnt);
|
||||
}
|
||||
components = new PhysicalFont[numSlots];
|
||||
components[msCnt] = fm.getEUDCFont();
|
||||
deferredInitialisation = new boolean[numSlots];
|
||||
if (defer) {
|
||||
for (int i=0; i<numSlots-1; i++) {
|
||||
deferredInitialisation[i] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
components = new PhysicalFont[numSlots];
|
||||
deferredInitialisation = new boolean[numSlots];
|
||||
if (defer) {
|
||||
for (int i=0; i<numSlots; i++) {
|
||||
deferredInitialisation[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fontRank = Font2D.FONT_CONFIG_RANK;
|
||||
|
||||
int index = fullName.indexOf('.');
|
||||
if (index>0) {
|
||||
familyName = fullName.substring(0, index);
|
||||
/* composites don't call setStyle() as parsing the style
|
||||
* takes place at the same time as parsing the family name.
|
||||
* Do I really have to parse the style from the name?
|
||||
* Need to look into having the caller provide this. */
|
||||
if (index+1 < fullName.length()) {
|
||||
String styleStr = fullName.substring(index+1);
|
||||
if ("plain".equals(styleStr)) {
|
||||
style = Font.PLAIN;
|
||||
} else if ("bold".equals(styleStr)) {
|
||||
style = Font.BOLD;
|
||||
} else if ("italic".equals(styleStr)) {
|
||||
style = Font.ITALIC;
|
||||
} else if ("bolditalic".equals(styleStr)) {
|
||||
style = Font.BOLD | Font.ITALIC;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
familyName = fullName;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a composite from a set of individual slot fonts.
|
||||
*/
|
||||
CompositeFont(PhysicalFont[] slotFonts) {
|
||||
|
||||
isStdComposite = false;
|
||||
handle = new Font2DHandle(this);
|
||||
fullName = slotFonts[0].fullName;
|
||||
familyName = slotFonts[0].familyName;
|
||||
style = slotFonts[0].style;
|
||||
|
||||
numMetricsSlots = 1; /* Only the physical Font */
|
||||
numSlots = slotFonts.length;
|
||||
|
||||
components = new PhysicalFont[numSlots];
|
||||
System.arraycopy(slotFonts, 0, components, 0, numSlots);
|
||||
deferredInitialisation = new boolean[numSlots]; // all false.
|
||||
}
|
||||
|
||||
/* This method is currently intended to be called only from
|
||||
* FontManager.getCompositeFontUIResource(Font)
|
||||
* It creates a new CompositeFont with the contents of the Physical
|
||||
* one pre-pended as slot 0.
|
||||
*/
|
||||
CompositeFont(PhysicalFont physFont, CompositeFont compFont) {
|
||||
|
||||
isStdComposite = false;
|
||||
handle = new Font2DHandle(this);
|
||||
fullName = physFont.fullName;
|
||||
familyName = physFont.familyName;
|
||||
style = physFont.style;
|
||||
|
||||
numMetricsSlots = 1; /* Only the physical Font */
|
||||
numSlots = compFont.numSlots+1;
|
||||
|
||||
/* Ugly though it is, we synchronize here on the FontManager class
|
||||
* because it is the lock used to do deferred initialisation.
|
||||
* We need to ensure that the arrays have consistent information.
|
||||
* But it may be possible to dispense with the synchronisation if
|
||||
* it is harmless that we do not know a slot is already initialised
|
||||
* and just need to discover that and mark it so.
|
||||
*/
|
||||
synchronized (FontManagerFactory.getInstance()) {
|
||||
components = new PhysicalFont[numSlots];
|
||||
components[0] = physFont;
|
||||
System.arraycopy(compFont.components, 0,
|
||||
components, 1, compFont.numSlots);
|
||||
|
||||
if (compFont.componentNames != null) {
|
||||
componentNames = new String[numSlots];
|
||||
componentNames[0] = physFont.fullName;
|
||||
System.arraycopy(compFont.componentNames, 0,
|
||||
componentNames, 1, compFont.numSlots);
|
||||
}
|
||||
if (compFont.componentFileNames != null) {
|
||||
componentFileNames = new String[numSlots];
|
||||
componentFileNames[0] = null;
|
||||
System.arraycopy(compFont.componentFileNames, 0,
|
||||
componentFileNames, 1, compFont.numSlots);
|
||||
}
|
||||
deferredInitialisation = new boolean[numSlots];
|
||||
deferredInitialisation[0] = false;
|
||||
System.arraycopy(compFont.deferredInitialisation, 0,
|
||||
deferredInitialisation, 1, compFont.numSlots);
|
||||
}
|
||||
}
|
||||
|
||||
/* This is used for deferred initialisation, so that the components of
|
||||
* a logical font are initialised only when the font is used.
|
||||
* This can have a positive impact on start-up of most UI applications.
|
||||
* Note that this technique cannot be used with a TTC font as it
|
||||
* doesn't know which font in the collection is needed. The solution to
|
||||
* this is that the initialisation checks if the returned font is
|
||||
* really the one it wants by comparing the name against the name that
|
||||
* was passed in (if none was passed in then you aren't using a TTC
|
||||
* as you would have to specify the name in such a case).
|
||||
* Assuming there's only two or three fonts in a collection then it
|
||||
* may be sufficient to verify the returned name is the expected one.
|
||||
* But half the time it won't be. However since initialisation of the
|
||||
* TTC will initialise all its components then just do a findFont2D call
|
||||
* to locate the right one.
|
||||
* This code allows for initialisation of each slot on demand.
|
||||
* There are two issues with this.
|
||||
* 1) All metrics slots probably may be initialised anyway as many
|
||||
* apps will query the overall font metrics. However this is not an
|
||||
* absolute requirement
|
||||
* 2) Some font configuration files on Solaris reference two versions
|
||||
* of a TT font: a Latin-1 version, then a Pan-European version.
|
||||
* One from /usr/openwin/lib/X11/fonts/TrueType, the other from
|
||||
* a euro_fonts directory which is symlinked from numerous locations.
|
||||
* This is difficult to avoid because the two do not share XLFDs so
|
||||
* both will be consequently mapped by separate XLFDs needed by AWT.
|
||||
* The difficulty this presents for lazy initialisation is that if
|
||||
* all the components are not mapped at once, the smaller version may
|
||||
* have been used only to be replaced later, and what is the consequence
|
||||
* for a client that displayed the contents of this font already.
|
||||
* After some thought I think this will not be a problem because when
|
||||
* client tries to display a glyph only in the Euro font, the composite
|
||||
* will ask all components of this font for that glyph and will get
|
||||
* the euro one. Subsequent uses will all come from the 100% compatible
|
||||
* euro one.
|
||||
*/
|
||||
private void doDeferredInitialisation(int slot) {
|
||||
if (deferredInitialisation[slot] == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Synchronize on FontManager so that is the global lock
|
||||
* to update its static set of deferred fonts.
|
||||
* This global lock is rarely likely to be an issue as there
|
||||
* are only going to be a few calls into this code.
|
||||
*/
|
||||
SunFontManager fm = SunFontManager.getInstance();
|
||||
synchronized (fm) {
|
||||
if (componentNames == null) {
|
||||
componentNames = new String[numSlots];
|
||||
}
|
||||
if (components[slot] == null) {
|
||||
/* Warning: it is possible that the returned component is
|
||||
* not derived from the file name argument, this can happen if:
|
||||
* - the file can't be found
|
||||
* - the file has a bad font
|
||||
* - the font in the file is superseded by a more complete one
|
||||
* This should not be a problem for composite font as it will
|
||||
* make no further use of this file, but code debuggers/
|
||||
* maintainers need to be conscious of this possibility.
|
||||
*/
|
||||
if (componentFileNames != null &&
|
||||
componentFileNames[slot] != null) {
|
||||
components[slot] =
|
||||
fm.initialiseDeferredFont(componentFileNames[slot]);
|
||||
}
|
||||
|
||||
if (components[slot] == null) {
|
||||
components[slot] = fm.getDefaultPhysicalFont();
|
||||
}
|
||||
String name = components[slot].getFontName(null);
|
||||
if (componentNames[slot] == null) {
|
||||
componentNames[slot] = name;
|
||||
} else if (!componentNames[slot].equalsIgnoreCase(name)) {
|
||||
/* If a component specifies the file with a bad font,
|
||||
* the corresponding slot will be initialized by
|
||||
* default physical font. In such case findFont2D may
|
||||
* return composite font which cannot be casted to
|
||||
* physical font.
|
||||
*/
|
||||
try {
|
||||
components[slot] =
|
||||
(PhysicalFont) fm.findFont2D(componentNames[slot],
|
||||
style,
|
||||
FontManager.PHYSICAL_FALLBACK);
|
||||
} catch (ClassCastException cce) {
|
||||
/* Assign default physical font to the slot */
|
||||
components[slot] = fm.getDefaultPhysicalFont();
|
||||
}
|
||||
}
|
||||
}
|
||||
deferredInitialisation[slot] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* To called only by FontManager.replaceFont */
|
||||
void replaceComponentFont(PhysicalFont oldFont, PhysicalFont newFont) {
|
||||
if (components == null) {
|
||||
return;
|
||||
}
|
||||
for (int slot=0; slot<numSlots; slot++) {
|
||||
if (components[slot] == oldFont) {
|
||||
components[slot] = newFont;
|
||||
if (componentNames != null) {
|
||||
componentNames[slot] = newFont.getFontName(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExcludedChar(int slot, int charcode) {
|
||||
|
||||
if (exclusionRanges == null || maxIndices == null ||
|
||||
slot >= numMetricsSlots) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int minIndex = 0;
|
||||
int maxIndex = maxIndices[slot];
|
||||
if (slot > 0) {
|
||||
minIndex = maxIndices[slot - 1];
|
||||
}
|
||||
int curIndex = minIndex;
|
||||
while (maxIndex > curIndex) {
|
||||
if ((charcode >= exclusionRanges[curIndex])
|
||||
&& (charcode <= exclusionRanges[curIndex+1])) {
|
||||
return true; // excluded
|
||||
}
|
||||
curIndex += 2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
|
||||
PhysicalFont font = getSlotFont(0);
|
||||
if (font == null) { // possible?
|
||||
super.getStyleMetrics(pointSize, metrics, offset);
|
||||
} else {
|
||||
font.getStyleMetrics(pointSize, metrics, offset);
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumSlots() {
|
||||
return numSlots;
|
||||
}
|
||||
|
||||
public PhysicalFont getSlotFont(int slot) {
|
||||
/* This is essentially the runtime overhead for deferred font
|
||||
* initialisation: a boolean test on obtaining a slot font,
|
||||
* which will happen per slot, on initialisation of a strike
|
||||
* (as that is the only frequent call site of this method.
|
||||
*/
|
||||
if (deferredInitialisation[slot]) {
|
||||
doDeferredInitialisation(slot);
|
||||
}
|
||||
SunFontManager fm = SunFontManager.getInstance();
|
||||
try {
|
||||
PhysicalFont font = components[slot];
|
||||
if (font == null) {
|
||||
try {
|
||||
font = (PhysicalFont) fm.
|
||||
findFont2D(componentNames[slot], style,
|
||||
FontManager.PHYSICAL_FALLBACK);
|
||||
components[slot] = font;
|
||||
} catch (ClassCastException cce) {
|
||||
font = fm.getDefaultPhysicalFont();
|
||||
}
|
||||
}
|
||||
return font;
|
||||
} catch (Exception e) {
|
||||
return fm.getDefaultPhysicalFont();
|
||||
}
|
||||
}
|
||||
|
||||
FontStrike createStrike(FontStrikeDesc desc) {
|
||||
return new CompositeStrike(this, desc);
|
||||
}
|
||||
|
||||
/* This is set false when the composite is created using a specified
|
||||
* physical font as the first slot and called by code which
|
||||
* selects composites by locale preferences to know that this
|
||||
* isn't a font which should be adjusted.
|
||||
*/
|
||||
public boolean isStdComposite() {
|
||||
return isStdComposite;
|
||||
}
|
||||
|
||||
/* This isn't very efficient but its infrequently used.
|
||||
* StandardGlyphVector uses it when the client assigns the glyph codes.
|
||||
* These may not be valid. This validates them substituting the missing
|
||||
* glyph elsewhere.
|
||||
*/
|
||||
protected int getValidatedGlyphCode(int glyphCode) {
|
||||
int slot = glyphCode >>> 24;
|
||||
if (slot >= numSlots) {
|
||||
return getMapper().getMissingGlyphCode();
|
||||
}
|
||||
|
||||
int slotglyphCode = glyphCode & CompositeStrike.SLOTMASK;
|
||||
PhysicalFont slotFont = getSlotFont(slot);
|
||||
if (slotFont.getValidatedGlyphCode(slotglyphCode) ==
|
||||
slotFont.getMissingGlyphCode()) {
|
||||
return getMapper().getMissingGlyphCode();
|
||||
} else {
|
||||
return glyphCode;
|
||||
}
|
||||
}
|
||||
|
||||
public CharToGlyphMapper getMapper() {
|
||||
if (mapper == null) {
|
||||
mapper = new CompositeGlyphMapper(this);
|
||||
}
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public boolean hasSupplementaryChars() {
|
||||
for (int i=0; i<numSlots; i++) {
|
||||
if (getSlotFont(i).hasSupplementaryChars()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
if (numGlyphs == 0) {
|
||||
numGlyphs = getMapper().getNumGlyphs();
|
||||
}
|
||||
return numGlyphs;
|
||||
}
|
||||
|
||||
public int getMissingGlyphCode() {
|
||||
return getMapper().getMissingGlyphCode();
|
||||
}
|
||||
|
||||
public boolean canDisplay(char c) {
|
||||
return getMapper().canDisplay(c);
|
||||
}
|
||||
|
||||
public boolean useAAForPtSize(int ptsize) {
|
||||
/* Find the first slot that supports the default encoding and use
|
||||
* that to decide the "gasp" behaviour of the composite font.
|
||||
* REMIND "default encoding" isn't applicable to a Unicode locale
|
||||
* and we need to replace this with a better mechanism for deciding
|
||||
* if a font "supports" the user's language. See TrueTypeFont.java
|
||||
*/
|
||||
if (localeSlot == -1) {
|
||||
/* Ordinarily check numMetricsSlots, but non-standard composites
|
||||
* set that to "1" whilst not necessarily supporting the default
|
||||
* encoding with that first slot. In such a case check all slots.
|
||||
*/
|
||||
int numCoreSlots = numMetricsSlots;
|
||||
if (numCoreSlots == 1 && !isStdComposite()) {
|
||||
numCoreSlots = numSlots;
|
||||
}
|
||||
for (int slot=0; slot<numCoreSlots; slot++) {
|
||||
if (getSlotFont(slot).supportsEncoding(null)) {
|
||||
localeSlot = slot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (localeSlot == -1) {
|
||||
localeSlot = 0;
|
||||
}
|
||||
}
|
||||
return getSlotFont(localeSlot).useAAForPtSize(ptsize);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String ls = (String)java.security.AccessController.doPrivileged(
|
||||
new sun.security.action.GetPropertyAction("line.separator"));
|
||||
String componentsStr = "";
|
||||
for (int i=0; i<numSlots; i++) {
|
||||
componentsStr += " Slot["+i+"]="+getSlotFont(i)+ls;
|
||||
}
|
||||
return "** Composite Font: Family=" + familyName +
|
||||
" Name=" + fullName + " style=" + style + ls + componentsStr;
|
||||
}
|
||||
}
|
||||
92
jdkSrc/jdk8/sun/font/CompositeFontDescriptor.java
Normal file
92
jdkSrc/jdk8/sun/font/CompositeFontDescriptor.java
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/**
|
||||
* Encapsulates the information that 2D needs to create a composite font,
|
||||
* the runtime representation of a logical font.
|
||||
*/
|
||||
public class CompositeFontDescriptor {
|
||||
|
||||
private String faceName;
|
||||
private int coreComponentCount;
|
||||
private String[] componentFaceNames;
|
||||
private String[] componentFileNames;
|
||||
private int[] exclusionRanges;
|
||||
private int[] exclusionRangeLimits;
|
||||
|
||||
/**
|
||||
* Constructs a composite font descriptor.
|
||||
* @param faceName the font face name, i.e., the family name suffixed
|
||||
* with ".plain", ".bold", ".italic", ".bolditalic".
|
||||
* @param coreComponentCount the number of core fonts, i.e., the ones
|
||||
* derived from a non-fallback sequence.
|
||||
* @param componentFaceNames the face names for the component fonts
|
||||
* @param componentFileNames the file names for the component fonts
|
||||
* @param exclusionRanges an array holding lower and upper boundaries
|
||||
* for all exclusion ranges for all component fonts
|
||||
* @param exclusionRangeLimits an array holding the limits of the
|
||||
* sections for each component font within the previous
|
||||
* array
|
||||
*/
|
||||
public CompositeFontDescriptor(String faceName,
|
||||
int coreComponentCount,
|
||||
String[] componentFaceNames,
|
||||
String[] componentFileNames,
|
||||
int[] exclusionRanges,
|
||||
int[] exclusionRangeLimits) {
|
||||
this.faceName = faceName;
|
||||
this.coreComponentCount = coreComponentCount;
|
||||
this.componentFaceNames = componentFaceNames;
|
||||
this.componentFileNames = componentFileNames;
|
||||
this.exclusionRanges = exclusionRanges;
|
||||
this.exclusionRangeLimits = exclusionRangeLimits;
|
||||
}
|
||||
|
||||
public String getFaceName() {
|
||||
return faceName;
|
||||
}
|
||||
|
||||
public int getCoreComponentCount() {
|
||||
return coreComponentCount;
|
||||
}
|
||||
|
||||
public String[] getComponentFaceNames() {
|
||||
return componentFaceNames;
|
||||
}
|
||||
|
||||
public String[] getComponentFileNames() {
|
||||
return componentFileNames;
|
||||
}
|
||||
|
||||
public int[] getExclusionRanges() {
|
||||
return exclusionRanges;
|
||||
}
|
||||
|
||||
public int[] getExclusionRangeLimits() {
|
||||
return exclusionRangeLimits;
|
||||
}
|
||||
}
|
||||
273
jdkSrc/jdk8/sun/font/CompositeGlyphMapper.java
Normal file
273
jdkSrc/jdk8/sun/font/CompositeGlyphMapper.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/* remember that the API requires a Font use a
|
||||
* consistent glyph id. for a code point, and this is a
|
||||
* problem if a particular strike uses native scaler sometimes
|
||||
* and T2K others. That needs to be dealt with somewhere, but
|
||||
* here we can just always get the same glyph code without
|
||||
* needing a strike.
|
||||
*
|
||||
* The C implementation would cache the results of anything up
|
||||
* to the maximum surrogate pair code point.
|
||||
* This implementation will not cache as much, since the storage
|
||||
* requirements are not justifiable. Even so it still can use up
|
||||
* to 216*256*4 bytes of storage per composite font. If an app
|
||||
* calls canDisplay on this range for all 20 composite fonts that's
|
||||
* over 1Mb of cached data. May need to employ WeakReferences if
|
||||
* this appears to cause problems.
|
||||
*/
|
||||
|
||||
public class CompositeGlyphMapper extends CharToGlyphMapper {
|
||||
|
||||
public static final int SLOTMASK = 0xff000000;
|
||||
public static final int GLYPHMASK = 0x00ffffff;
|
||||
|
||||
public static final int NBLOCKS = 216;
|
||||
public static final int BLOCKSZ = 256;
|
||||
public static final int MAXUNICODE = NBLOCKS*BLOCKSZ;
|
||||
|
||||
|
||||
CompositeFont font;
|
||||
CharToGlyphMapper slotMappers[];
|
||||
int[][] glyphMaps;
|
||||
private boolean hasExcludes;
|
||||
|
||||
public CompositeGlyphMapper(CompositeFont compFont) {
|
||||
font = compFont;
|
||||
initMapper();
|
||||
/* This is often false which saves the overhead of a
|
||||
* per-mapped char method call.
|
||||
*/
|
||||
hasExcludes = compFont.exclusionRanges != null &&
|
||||
compFont.maxIndices != null;
|
||||
}
|
||||
|
||||
public final int compositeGlyphCode(int slot, int glyphCode) {
|
||||
return (slot << 24 | (glyphCode & GLYPHMASK));
|
||||
}
|
||||
|
||||
private final void initMapper() {
|
||||
if (missingGlyph == CharToGlyphMapper.UNINITIALIZED_GLYPH) {
|
||||
if (glyphMaps == null) {
|
||||
glyphMaps = new int[NBLOCKS][];
|
||||
}
|
||||
slotMappers = new CharToGlyphMapper[font.numSlots];
|
||||
/* This requires that slot 0 is never empty. */
|
||||
missingGlyph = font.getSlotFont(0).getMissingGlyphCode();
|
||||
missingGlyph = compositeGlyphCode(0, missingGlyph);
|
||||
}
|
||||
}
|
||||
|
||||
private int getCachedGlyphCode(int unicode) {
|
||||
if (unicode >= MAXUNICODE) {
|
||||
return UNINITIALIZED_GLYPH; // don't cache surrogates
|
||||
}
|
||||
int[] gmap;
|
||||
if ((gmap = glyphMaps[unicode >> 8]) == null) {
|
||||
return UNINITIALIZED_GLYPH;
|
||||
}
|
||||
return gmap[unicode & 0xff];
|
||||
}
|
||||
|
||||
private void setCachedGlyphCode(int unicode, int glyphCode) {
|
||||
if (unicode >= MAXUNICODE) {
|
||||
return; // don't cache surrogates
|
||||
}
|
||||
int index0 = unicode >> 8;
|
||||
if (glyphMaps[index0] == null) {
|
||||
glyphMaps[index0] = new int[BLOCKSZ];
|
||||
for (int i=0;i<BLOCKSZ;i++) {
|
||||
glyphMaps[index0][i] = UNINITIALIZED_GLYPH;
|
||||
}
|
||||
}
|
||||
glyphMaps[index0][unicode & 0xff] = glyphCode;
|
||||
}
|
||||
|
||||
private final CharToGlyphMapper getSlotMapper(int slot) {
|
||||
CharToGlyphMapper mapper = slotMappers[slot];
|
||||
if (mapper == null) {
|
||||
mapper = font.getSlotFont(slot).getMapper();
|
||||
slotMappers[slot] = mapper;
|
||||
}
|
||||
return mapper;
|
||||
}
|
||||
|
||||
private final int convertToGlyph(int unicode) {
|
||||
|
||||
for (int slot = 0; slot < font.numSlots; slot++) {
|
||||
if (!hasExcludes || !font.isExcludedChar(slot, unicode)) {
|
||||
CharToGlyphMapper mapper = getSlotMapper(slot);
|
||||
int glyphCode = mapper.charToGlyph(unicode);
|
||||
if (glyphCode != mapper.getMissingGlyphCode()) {
|
||||
glyphCode = compositeGlyphCode(slot, glyphCode);
|
||||
setCachedGlyphCode(unicode, glyphCode);
|
||||
return glyphCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return missingGlyph;
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
int numGlyphs = 0;
|
||||
/* The number of glyphs in a composite is affected by
|
||||
* exclusion ranges and duplicates (ie the same code point is
|
||||
* mapped by two different fonts) and also whether or not to
|
||||
* count fallback fonts. A nearly correct answer would be very
|
||||
* expensive to generate. A rough ballpark answer would
|
||||
* just count the glyphs in all the slots. However this would
|
||||
* initialize mappers for all slots when they aren't necessarily
|
||||
* needed. For now just use the first slot as JDK 1.4 did.
|
||||
*/
|
||||
for (int slot=0; slot<1 /*font.numSlots*/; slot++) {
|
||||
CharToGlyphMapper mapper = slotMappers[slot];
|
||||
if (mapper == null) {
|
||||
mapper = font.getSlotFont(slot).getMapper();
|
||||
slotMappers[slot] = mapper;
|
||||
}
|
||||
numGlyphs += mapper.getNumGlyphs();
|
||||
}
|
||||
return numGlyphs;
|
||||
}
|
||||
|
||||
public int charToGlyph(int unicode) {
|
||||
|
||||
int glyphCode = getCachedGlyphCode(unicode);
|
||||
if (glyphCode == UNINITIALIZED_GLYPH) {
|
||||
glyphCode = convertToGlyph(unicode);
|
||||
}
|
||||
return glyphCode;
|
||||
}
|
||||
|
||||
public int charToGlyph(int unicode, int prefSlot) {
|
||||
if (prefSlot >= 0) {
|
||||
CharToGlyphMapper mapper = getSlotMapper(prefSlot);
|
||||
int glyphCode = mapper.charToGlyph(unicode);
|
||||
if (glyphCode != mapper.getMissingGlyphCode()) {
|
||||
return compositeGlyphCode(prefSlot, glyphCode);
|
||||
}
|
||||
}
|
||||
return charToGlyph(unicode);
|
||||
}
|
||||
|
||||
public int charToGlyph(char unicode) {
|
||||
|
||||
int glyphCode = getCachedGlyphCode(unicode);
|
||||
if (glyphCode == UNINITIALIZED_GLYPH) {
|
||||
glyphCode = convertToGlyph(unicode);
|
||||
}
|
||||
return glyphCode;
|
||||
}
|
||||
|
||||
/* This variant checks if shaping is needed and immediately
|
||||
* returns true if it does. A caller of this method should be expecting
|
||||
* to check the return type because it needs to know how to handle
|
||||
* the character data for display.
|
||||
*/
|
||||
public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
int code = unicodes[i]; // char is unsigned.
|
||||
|
||||
if (code >= HI_SURROGATE_START &&
|
||||
code <= HI_SURROGATE_END && i < count - 1) {
|
||||
char low = unicodes[i + 1];
|
||||
|
||||
if (low >= LO_SURROGATE_START &&
|
||||
low <= LO_SURROGATE_END) {
|
||||
code = (code - HI_SURROGATE_START) *
|
||||
0x400 + low - LO_SURROGATE_START + 0x10000;
|
||||
glyphs[i + 1] = INVISIBLE_GLYPH_ID;
|
||||
}
|
||||
}
|
||||
|
||||
int gc = glyphs[i] = getCachedGlyphCode(code);
|
||||
if (gc == UNINITIALIZED_GLYPH) {
|
||||
glyphs[i] = convertToGlyph(code);
|
||||
}
|
||||
|
||||
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
|
||||
continue;
|
||||
}
|
||||
else if (FontUtilities.isComplexCharCode(code)) {
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x10000) {
|
||||
i += 1; // Empty glyph slot after surrogate
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The conversion is not very efficient - looping as it does, converting
|
||||
* one char at a time. However the cache should fill very rapidly.
|
||||
*/
|
||||
public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {
|
||||
for (int i=0; i<count; i++) {
|
||||
int code = unicodes[i]; // char is unsigned.
|
||||
|
||||
if (code >= HI_SURROGATE_START &&
|
||||
code <= HI_SURROGATE_END && i < count - 1) {
|
||||
char low = unicodes[i + 1];
|
||||
|
||||
if (low >= LO_SURROGATE_START &&
|
||||
low <= LO_SURROGATE_END) {
|
||||
code = (code - HI_SURROGATE_START) *
|
||||
0x400 + low - LO_SURROGATE_START + 0x10000;
|
||||
|
||||
int gc = glyphs[i] = getCachedGlyphCode(code);
|
||||
if (gc == UNINITIALIZED_GLYPH) {
|
||||
glyphs[i] = convertToGlyph(code);
|
||||
}
|
||||
i += 1; // Empty glyph slot after surrogate
|
||||
glyphs[i] = INVISIBLE_GLYPH_ID;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int gc = glyphs[i] = getCachedGlyphCode(code);
|
||||
if (gc == UNINITIALIZED_GLYPH) {
|
||||
glyphs[i] = convertToGlyph(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
|
||||
for (int i=0; i<count; i++) {
|
||||
int code = unicodes[i];
|
||||
|
||||
glyphs[i] = getCachedGlyphCode(code);
|
||||
if (glyphs[i] == UNINITIALIZED_GLYPH) {
|
||||
glyphs[i] = convertToGlyph(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
219
jdkSrc/jdk8/sun/font/CompositeStrike.java
Normal file
219
jdkSrc/jdk8/sun/font/CompositeStrike.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/*
|
||||
* performance:
|
||||
* it seems expensive that when using a composite font for
|
||||
* every char you have to find which "slot" can display it.
|
||||
* Just the fact that you need to check at all ..
|
||||
* A composite glyph code ducks this by encoding the slot into the
|
||||
* glyph code, but you still need to get from char to glyph code.
|
||||
*/
|
||||
public final class CompositeStrike extends FontStrike {
|
||||
|
||||
static final int SLOTMASK = 0xffffff;
|
||||
|
||||
private CompositeFont compFont;
|
||||
private PhysicalStrike[] strikes;
|
||||
int numGlyphs = 0;
|
||||
|
||||
CompositeStrike(CompositeFont font2D, FontStrikeDesc desc) {
|
||||
this.compFont = font2D;
|
||||
this.desc = desc;
|
||||
this.disposer = new FontStrikeDisposer(compFont, desc);
|
||||
if (desc.style != compFont.style) {
|
||||
algoStyle = true;
|
||||
if ((desc.style & Font.BOLD) == Font.BOLD &&
|
||||
((compFont.style & Font.BOLD) == 0)) {
|
||||
boldness = 1.33f;
|
||||
}
|
||||
if ((desc.style & Font.ITALIC) == Font.ITALIC &&
|
||||
(compFont.style & Font.ITALIC) == 0) {
|
||||
italic = 0.7f;
|
||||
}
|
||||
}
|
||||
strikes = new PhysicalStrike[compFont.numSlots];
|
||||
}
|
||||
|
||||
/* do I need this (see Strike::compositeStrikeForGlyph) */
|
||||
PhysicalStrike getStrikeForGlyph(int glyphCode) {
|
||||
return getStrikeForSlot(glyphCode >>> 24);
|
||||
}
|
||||
|
||||
PhysicalStrike getStrikeForSlot(int slot) {
|
||||
|
||||
if (slot >= strikes.length) {
|
||||
slot = 0;
|
||||
}
|
||||
|
||||
PhysicalStrike strike = strikes[slot];
|
||||
if (strike == null) {
|
||||
strike =
|
||||
(PhysicalStrike)(compFont.getSlotFont(slot).getStrike(desc));
|
||||
|
||||
strikes[slot] = strike;
|
||||
}
|
||||
return strike;
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
return compFont.getNumGlyphs();
|
||||
}
|
||||
|
||||
StrikeMetrics getFontMetrics() {
|
||||
if (strikeMetrics == null) {
|
||||
StrikeMetrics compMetrics = new StrikeMetrics();
|
||||
for (int s=0; s<compFont.numMetricsSlots; s++) {
|
||||
compMetrics.merge(getStrikeForSlot(s).getFontMetrics());
|
||||
}
|
||||
strikeMetrics = compMetrics;
|
||||
}
|
||||
return strikeMetrics;
|
||||
}
|
||||
|
||||
|
||||
/* Performance tweak: Slot 0 can often return all the glyphs
|
||||
* Note slot zero doesn't need to be masked.
|
||||
* Could go a step further and support getting a run of glyphs.
|
||||
* This would help many locales a little.
|
||||
*
|
||||
* Note that if a client constructs an invalid a composite glyph that
|
||||
* references an invalid slot, that the behaviour is currently
|
||||
* that this slot index falls through to CompositeFont.getSlotFont(int)
|
||||
* which will substitute a default font, from which to obtain the
|
||||
* strike. If its an invalid glyph code for a valid slot, then the
|
||||
* physical font for that slot will substitute the missing glyph.
|
||||
*/
|
||||
void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
|
||||
PhysicalStrike strike = getStrikeForSlot(0);
|
||||
int numptrs = strike.getSlot0GlyphImagePtrs(glyphCodes, images, len);
|
||||
if (numptrs == len) {
|
||||
return;
|
||||
}
|
||||
for (int i=numptrs; i< len; i++) {
|
||||
strike = getStrikeForGlyph(glyphCodes[i]);
|
||||
images[i] = strike.getGlyphImagePtr(glyphCodes[i] & SLOTMASK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long getGlyphImagePtr(int glyphCode) {
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
return strike.getGlyphImagePtr(glyphCode & SLOTMASK);
|
||||
}
|
||||
|
||||
void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
strike.getGlyphImageBounds(glyphCode & SLOTMASK, pt, result);
|
||||
}
|
||||
|
||||
Point2D.Float getGlyphMetrics(int glyphCode) {
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
return strike.getGlyphMetrics(glyphCode & SLOTMASK);
|
||||
}
|
||||
|
||||
Point2D.Float getCharMetrics(char ch) {
|
||||
return getGlyphMetrics(compFont.getMapper().charToGlyph(ch));
|
||||
}
|
||||
|
||||
float getGlyphAdvance(int glyphCode) {
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
return strike.getGlyphAdvance(glyphCode & SLOTMASK);
|
||||
}
|
||||
|
||||
/* REMIND where to cache?
|
||||
* The glyph advance is already cached by physical strikes and that's a lot
|
||||
* of the work.
|
||||
* Also FontDesignMetrics maintains a latin char advance cache, so don't
|
||||
* cache advances here as apps tend to hold onto metrics objects when
|
||||
* performance is sensitive to it. Revisit this assumption later.
|
||||
*/
|
||||
float getCodePointAdvance(int cp) {
|
||||
return getGlyphAdvance(compFont.getMapper().charToGlyph(cp));
|
||||
}
|
||||
|
||||
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
return strike.getGlyphOutlineBounds(glyphCode & SLOTMASK);
|
||||
}
|
||||
|
||||
GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
|
||||
|
||||
PhysicalStrike strike = getStrikeForGlyph(glyphCode);
|
||||
GeneralPath path = strike.getGlyphOutline(glyphCode & SLOTMASK, x, y);
|
||||
if (path == null) {
|
||||
return new GeneralPath();
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/* The physical font slot for each glyph is encoded in the glyph ID
|
||||
* To be as efficient as possible we find a run of glyphs from the
|
||||
* same slot and create a temporary array of these glyphs decoded
|
||||
* to the slot. The slot font is then queried for the GeneralPath
|
||||
* for that run of glyphs. GeneralPaths from each run are appended
|
||||
* to create the shape for the whole glyph array.
|
||||
*/
|
||||
GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
|
||||
GeneralPath path = null;
|
||||
GeneralPath gp;
|
||||
int glyphIndex = 0;
|
||||
int[] tmpGlyphs;
|
||||
|
||||
while (glyphIndex < glyphs.length) {
|
||||
int start = glyphIndex;
|
||||
int slot = glyphs[glyphIndex] >>> 24;
|
||||
while (glyphIndex < glyphs.length &&
|
||||
(glyphs[glyphIndex+1] >>> 24) == slot) {
|
||||
glyphIndex++;
|
||||
}
|
||||
int tmpLen = glyphIndex-start+1;
|
||||
tmpGlyphs = new int[tmpLen];
|
||||
for (int i=0;i<tmpLen;i++) {
|
||||
tmpGlyphs[i] = glyphs[i] & SLOTMASK;
|
||||
}
|
||||
gp = getStrikeForSlot(slot).getGlyphVectorOutline(tmpGlyphs, x, y);
|
||||
if (path == null) {
|
||||
path = gp;
|
||||
} else if (gp != null) {
|
||||
path.append(gp, false);
|
||||
}
|
||||
}
|
||||
if (path == null) {
|
||||
return new GeneralPath();
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
jdkSrc/jdk8/sun/font/CoreMetrics.java
Normal file
131
jdkSrc/jdk8/sun/font/CoreMetrics.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2003, All Rights Reserved
|
||||
*
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.font.LineMetrics;
|
||||
import java.awt.font.GraphicAttribute;
|
||||
|
||||
public final class CoreMetrics {
|
||||
|
||||
public CoreMetrics(float ascent,
|
||||
float descent,
|
||||
float leading,
|
||||
float height,
|
||||
int baselineIndex,
|
||||
float[] baselineOffsets,
|
||||
float strikethroughOffset,
|
||||
float strikethroughThickness,
|
||||
float underlineOffset,
|
||||
float underlineThickness,
|
||||
float ssOffset,
|
||||
float italicAngle) {
|
||||
this.ascent = ascent;
|
||||
this.descent = descent;
|
||||
this.leading = leading;
|
||||
this.height = height;
|
||||
this.baselineIndex = baselineIndex;
|
||||
this.baselineOffsets = baselineOffsets;
|
||||
this.strikethroughOffset = strikethroughOffset;
|
||||
this.strikethroughThickness = strikethroughThickness;
|
||||
this.underlineOffset = underlineOffset;
|
||||
this.underlineThickness = underlineThickness;
|
||||
this.ssOffset = ssOffset;
|
||||
this.italicAngle = italicAngle;
|
||||
}
|
||||
|
||||
public static CoreMetrics get(LineMetrics lm) {
|
||||
return ((FontLineMetrics)lm).cm;
|
||||
}
|
||||
|
||||
public final int hashCode() {
|
||||
return Float.floatToIntBits(ascent + ssOffset);
|
||||
}
|
||||
|
||||
public final boolean equals(Object rhs) {
|
||||
try {
|
||||
return equals((CoreMetrics)rhs);
|
||||
}
|
||||
catch(ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean equals(CoreMetrics rhs) {
|
||||
if (rhs != null) {
|
||||
if (this == rhs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ascent == rhs.ascent
|
||||
&& descent == rhs.descent
|
||||
&& leading == rhs.leading
|
||||
&& baselineIndex == rhs.baselineIndex
|
||||
&& baselineOffsets[0] == rhs.baselineOffsets[0]
|
||||
&& baselineOffsets[1] == rhs.baselineOffsets[1]
|
||||
&& baselineOffsets[2] == rhs.baselineOffsets[2]
|
||||
&& strikethroughOffset == rhs.strikethroughOffset
|
||||
&& strikethroughThickness == rhs.strikethroughThickness
|
||||
&& underlineOffset == rhs.underlineOffset
|
||||
&& underlineThickness == rhs.underlineThickness
|
||||
&& ssOffset == rhs.ssOffset
|
||||
&& italicAngle == rhs.italicAngle;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// fullOffsets is an array of 5 baseline offsets,
|
||||
// roman, center, hanging, bottom, and top in that order
|
||||
// this does NOT add the ssOffset
|
||||
public final float effectiveBaselineOffset(float[] fullOffsets) {
|
||||
switch (baselineIndex) {
|
||||
case GraphicAttribute.TOP_ALIGNMENT:
|
||||
return fullOffsets[4] + ascent;
|
||||
case GraphicAttribute.BOTTOM_ALIGNMENT:
|
||||
return fullOffsets[3] - descent;
|
||||
default:
|
||||
return fullOffsets[baselineIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public final float ascent;
|
||||
public final float descent;
|
||||
public final float leading;
|
||||
public final float height;
|
||||
public final int baselineIndex;
|
||||
public final float[] baselineOffsets; // !! this is a hole, don't expose this class
|
||||
public final float strikethroughOffset;
|
||||
public final float strikethroughThickness;
|
||||
public final float underlineOffset;
|
||||
public final float underlineThickness;
|
||||
public final float ssOffset;
|
||||
public final float italicAngle;
|
||||
}
|
||||
164
jdkSrc/jdk8/sun/font/CreatedFontTracker.java
Normal file
164
jdkSrc/jdk8/sun/font/CreatedFontTracker.java
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import sun.awt.AppContext;
|
||||
import sun.misc.ThreadGroupUtils;
|
||||
|
||||
public class CreatedFontTracker {
|
||||
|
||||
public static final int MAX_FILE_SIZE = 32 * 1024 * 1024;
|
||||
public static final int MAX_TOTAL_BYTES = 10 * MAX_FILE_SIZE;
|
||||
|
||||
static CreatedFontTracker tracker;
|
||||
int numBytes;
|
||||
|
||||
public static synchronized CreatedFontTracker getTracker() {
|
||||
if (tracker == null) {
|
||||
tracker = new CreatedFontTracker();
|
||||
}
|
||||
return tracker;
|
||||
}
|
||||
|
||||
private CreatedFontTracker() {
|
||||
numBytes = 0;
|
||||
}
|
||||
|
||||
public synchronized int getNumBytes() {
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
public synchronized void addBytes(int sz) {
|
||||
numBytes += sz;
|
||||
}
|
||||
|
||||
public synchronized void subBytes(int sz) {
|
||||
numBytes -= sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an AppContext-specific counting semaphore.
|
||||
*/
|
||||
private static synchronized Semaphore getCS() {
|
||||
final AppContext appContext = AppContext.getAppContext();
|
||||
Semaphore cs = (Semaphore) appContext.get(CreatedFontTracker.class);
|
||||
if (cs == null) {
|
||||
// Make a semaphore with 5 permits that obeys the first-in first-out
|
||||
// granting of permits.
|
||||
cs = new Semaphore(5, true);
|
||||
appContext.put(CreatedFontTracker.class, cs);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
public boolean acquirePermit() throws InterruptedException {
|
||||
// This does a timed-out wait.
|
||||
return getCS().tryAcquire(120, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void releasePermit() {
|
||||
getCS().release();
|
||||
}
|
||||
|
||||
public void add(File file) {
|
||||
TempFileDeletionHook.add(file);
|
||||
}
|
||||
|
||||
public void set(File file, OutputStream os) {
|
||||
TempFileDeletionHook.set(file, os);
|
||||
}
|
||||
|
||||
public void remove(File file) {
|
||||
TempFileDeletionHook.remove(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for cleanup of temp files created while processing fonts.
|
||||
* Note that this only applies to createFont() from an InputStream object.
|
||||
*/
|
||||
private static class TempFileDeletionHook {
|
||||
private static HashMap<File, OutputStream> files = new HashMap<>();
|
||||
|
||||
private static Thread t = null;
|
||||
static void init() {
|
||||
if (t == null) {
|
||||
// Add a shutdown hook to remove the temp file.
|
||||
AccessController.doPrivileged(
|
||||
(PrivilegedAction<Void>) () -> {
|
||||
/* The thread must be a member of a thread group
|
||||
* which will not get GCed before VM exit.
|
||||
* Make its parent the top-level thread group.
|
||||
*/
|
||||
ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
|
||||
t = new Thread(rootTG, TempFileDeletionHook::runHooks);
|
||||
t.setContextClassLoader(null);
|
||||
Runtime.getRuntime().addShutdownHook(t);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private TempFileDeletionHook() {}
|
||||
|
||||
static synchronized void add(File file) {
|
||||
init();
|
||||
files.put(file, null);
|
||||
}
|
||||
|
||||
static synchronized void set(File file, OutputStream os) {
|
||||
files.put(file, os);
|
||||
}
|
||||
|
||||
static synchronized void remove(File file) {
|
||||
files.remove(file);
|
||||
}
|
||||
|
||||
static synchronized void runHooks() {
|
||||
if (files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<File, OutputStream> entry : files.entrySet()) {
|
||||
// Close the associated output stream, and then delete the file.
|
||||
try {
|
||||
if (entry.getValue() != null) {
|
||||
entry.getValue().close();
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
entry.getKey().delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
443
jdkSrc/jdk8/sun/font/Decoration.java
Normal file
443
jdkSrc/jdk8/sun/font/Decoration.java
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 1999-2003, All Rights Reserved
|
||||
*
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Paint;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Shape;
|
||||
import java.awt.Stroke;
|
||||
|
||||
import java.awt.font.TextAttribute;
|
||||
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.GeneralPath;
|
||||
|
||||
import static sun.font.AttributeValues.*;
|
||||
import static sun.font.EAttribute.*;
|
||||
|
||||
/**
|
||||
* This class handles underlining, strikethrough, and foreground and
|
||||
* background styles on text. Clients simply acquire instances
|
||||
* of this class and hand them off to ExtendedTextLabels or GraphicComponents.
|
||||
*/
|
||||
public class Decoration {
|
||||
|
||||
/**
|
||||
* This interface is implemented by clients that use Decoration.
|
||||
* Unfortunately, interface methods have to public; ideally these
|
||||
* would be package-private.
|
||||
*/
|
||||
public interface Label {
|
||||
CoreMetrics getCoreMetrics();
|
||||
Rectangle2D getLogicalBounds();
|
||||
|
||||
void handleDraw(Graphics2D g2d, float x, float y);
|
||||
Rectangle2D handleGetCharVisualBounds(int index);
|
||||
Rectangle2D handleGetVisualBounds();
|
||||
Shape handleGetOutline(float x, float y);
|
||||
}
|
||||
|
||||
private Decoration() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Decoration which does nothing.
|
||||
*/
|
||||
public static Decoration getPlainDecoration() {
|
||||
|
||||
return PLAIN;
|
||||
}
|
||||
|
||||
private static final int VALUES_MASK =
|
||||
AttributeValues.getMask(EFOREGROUND, EBACKGROUND, ESWAP_COLORS,
|
||||
ESTRIKETHROUGH, EUNDERLINE, EINPUT_METHOD_HIGHLIGHT,
|
||||
EINPUT_METHOD_UNDERLINE);
|
||||
|
||||
public static Decoration getDecoration(AttributeValues values) {
|
||||
if (values == null || !values.anyDefined(VALUES_MASK)) {
|
||||
return PLAIN;
|
||||
}
|
||||
|
||||
values = values.applyIMHighlight();
|
||||
|
||||
return new DecorationImpl(values.getForeground(),
|
||||
values.getBackground(),
|
||||
values.getSwapColors(),
|
||||
values.getStrikethrough(),
|
||||
Underline.getUnderline(values.getUnderline()),
|
||||
Underline.getUnderline(values.getInputMethodUnderline()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Decoration appropriate for the the given Map.
|
||||
* @param attributes the Map used to determine the Decoration
|
||||
*/
|
||||
public static Decoration getDecoration(Map attributes) {
|
||||
if (attributes == null) {
|
||||
return PLAIN;
|
||||
}
|
||||
return getDecoration(AttributeValues.fromMap(attributes));
|
||||
}
|
||||
|
||||
public void drawTextAndDecorations(Label label,
|
||||
Graphics2D g2d,
|
||||
float x,
|
||||
float y) {
|
||||
|
||||
label.handleDraw(g2d, x, y);
|
||||
}
|
||||
|
||||
public Rectangle2D getVisualBounds(Label label) {
|
||||
|
||||
return label.handleGetVisualBounds();
|
||||
}
|
||||
|
||||
public Rectangle2D getCharVisualBounds(Label label, int index) {
|
||||
|
||||
return label.handleGetCharVisualBounds(index);
|
||||
}
|
||||
|
||||
Shape getOutline(Label label,
|
||||
float x,
|
||||
float y) {
|
||||
|
||||
return label.handleGetOutline(x, y);
|
||||
}
|
||||
|
||||
private static final Decoration PLAIN = new Decoration();
|
||||
|
||||
private static final class DecorationImpl extends Decoration {
|
||||
|
||||
private Paint fgPaint = null;
|
||||
private Paint bgPaint = null;
|
||||
private boolean swapColors = false;
|
||||
private boolean strikethrough = false;
|
||||
private Underline stdUnderline = null; // underline from TextAttribute.UNDERLINE_ON
|
||||
private Underline imUnderline = null; // input method underline
|
||||
|
||||
DecorationImpl(Paint foreground,
|
||||
Paint background,
|
||||
boolean swapColors,
|
||||
boolean strikethrough,
|
||||
Underline stdUnderline,
|
||||
Underline imUnderline) {
|
||||
|
||||
fgPaint = (Paint) foreground;
|
||||
bgPaint = (Paint) background;
|
||||
|
||||
this.swapColors = swapColors;
|
||||
this.strikethrough = strikethrough;
|
||||
|
||||
this.stdUnderline = stdUnderline;
|
||||
this.imUnderline = imUnderline;
|
||||
}
|
||||
|
||||
private static boolean areEqual(Object lhs, Object rhs) {
|
||||
|
||||
if (lhs == null) {
|
||||
return rhs == null;
|
||||
}
|
||||
else {
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object rhs) {
|
||||
|
||||
if (rhs == this) {
|
||||
return true;
|
||||
}
|
||||
if (rhs == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DecorationImpl other = null;
|
||||
try {
|
||||
other = (DecorationImpl) rhs;
|
||||
}
|
||||
catch(ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(swapColors == other.swapColors &&
|
||||
strikethrough == other.strikethrough)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!areEqual(stdUnderline, other.stdUnderline)) {
|
||||
return false;
|
||||
}
|
||||
if (!areEqual(fgPaint, other.fgPaint)) {
|
||||
return false;
|
||||
}
|
||||
if (!areEqual(bgPaint, other.bgPaint)) {
|
||||
return false;
|
||||
}
|
||||
return areEqual(imUnderline, other.imUnderline);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
|
||||
int hc = 1;
|
||||
if (strikethrough) {
|
||||
hc |= 2;
|
||||
}
|
||||
if (swapColors) {
|
||||
hc |= 4;
|
||||
}
|
||||
if (stdUnderline != null) {
|
||||
hc += stdUnderline.hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bottom of the Rectangle which encloses pixels
|
||||
* drawn by underlines.
|
||||
*/
|
||||
private float getUnderlineMaxY(CoreMetrics cm) {
|
||||
|
||||
float maxY = 0;
|
||||
if (stdUnderline != null) {
|
||||
|
||||
float ulBottom = cm.underlineOffset;
|
||||
ulBottom += stdUnderline.getLowerDrawLimit(cm.underlineThickness);
|
||||
maxY = Math.max(maxY, ulBottom);
|
||||
}
|
||||
|
||||
if (imUnderline != null) {
|
||||
|
||||
float ulBottom = cm.underlineOffset;
|
||||
ulBottom += imUnderline.getLowerDrawLimit(cm.underlineThickness);
|
||||
maxY = Math.max(maxY, ulBottom);
|
||||
}
|
||||
|
||||
return maxY;
|
||||
}
|
||||
|
||||
private void drawTextAndEmbellishments(Label label,
|
||||
Graphics2D g2d,
|
||||
float x,
|
||||
float y) {
|
||||
|
||||
label.handleDraw(g2d, x, y);
|
||||
|
||||
if (!strikethrough && stdUnderline == null && imUnderline == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float x1 = x;
|
||||
float x2 = x1 + (float)label.getLogicalBounds().getWidth();
|
||||
|
||||
CoreMetrics cm = label.getCoreMetrics();
|
||||
if (strikethrough) {
|
||||
Stroke savedStroke = g2d.getStroke();
|
||||
g2d.setStroke(new BasicStroke(cm.strikethroughThickness,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER));
|
||||
float strikeY = y + cm.strikethroughOffset;
|
||||
g2d.draw(new Line2D.Float(x1, strikeY, x2, strikeY));
|
||||
g2d.setStroke(savedStroke);
|
||||
}
|
||||
|
||||
float ulOffset = cm.underlineOffset;
|
||||
float ulThickness = cm.underlineThickness;
|
||||
|
||||
if (stdUnderline != null) {
|
||||
stdUnderline.drawUnderline(g2d, ulThickness, x1, x2, y + ulOffset);
|
||||
}
|
||||
|
||||
if (imUnderline != null) {
|
||||
imUnderline.drawUnderline(g2d, ulThickness, x1, x2, y + ulOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawTextAndDecorations(Label label,
|
||||
Graphics2D g2d,
|
||||
float x,
|
||||
float y) {
|
||||
|
||||
if (fgPaint == null && bgPaint == null && swapColors == false) {
|
||||
drawTextAndEmbellishments(label, g2d, x, y);
|
||||
}
|
||||
else {
|
||||
Paint savedPaint = g2d.getPaint();
|
||||
Paint foreground, background;
|
||||
|
||||
if (swapColors) {
|
||||
background = fgPaint==null? savedPaint : fgPaint;
|
||||
if (bgPaint == null) {
|
||||
if (background instanceof Color) {
|
||||
Color bg = (Color)background;
|
||||
// 30/59/11 is standard weights, tweaked a bit
|
||||
int brightness = 33 * bg.getRed()
|
||||
+ 53 * bg.getGreen()
|
||||
+ 14 * bg.getBlue();
|
||||
foreground = brightness > 18500 ? Color.BLACK : Color.WHITE;
|
||||
} else {
|
||||
foreground = Color.WHITE;
|
||||
}
|
||||
} else {
|
||||
foreground = bgPaint;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreground = fgPaint==null? savedPaint : fgPaint;
|
||||
background = bgPaint;
|
||||
}
|
||||
|
||||
if (background != null) {
|
||||
|
||||
Rectangle2D bgArea = label.getLogicalBounds();
|
||||
bgArea = new Rectangle2D.Float(x + (float)bgArea.getX(),
|
||||
y + (float)bgArea.getY(),
|
||||
(float)bgArea.getWidth(),
|
||||
(float)bgArea.getHeight());
|
||||
|
||||
g2d.setPaint(background);
|
||||
g2d.fill(bgArea);
|
||||
}
|
||||
|
||||
g2d.setPaint(foreground);
|
||||
drawTextAndEmbellishments(label, g2d, x, y);
|
||||
g2d.setPaint(savedPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle2D getVisualBounds(Label label) {
|
||||
|
||||
Rectangle2D visBounds = label.handleGetVisualBounds();
|
||||
|
||||
if (swapColors || bgPaint != null || strikethrough
|
||||
|| stdUnderline != null || imUnderline != null) {
|
||||
|
||||
float minX = 0;
|
||||
Rectangle2D lb = label.getLogicalBounds();
|
||||
|
||||
float minY = 0, maxY = 0;
|
||||
|
||||
if (swapColors || bgPaint != null) {
|
||||
|
||||
minY = (float)lb.getY();
|
||||
maxY = minY + (float)lb.getHeight();
|
||||
}
|
||||
|
||||
maxY = Math.max(maxY, getUnderlineMaxY(label.getCoreMetrics()));
|
||||
|
||||
Rectangle2D ab = new Rectangle2D.Float(minX, minY, (float)lb.getWidth(), maxY-minY);
|
||||
visBounds.add(ab);
|
||||
}
|
||||
|
||||
return visBounds;
|
||||
}
|
||||
|
||||
Shape getOutline(Label label,
|
||||
float x,
|
||||
float y) {
|
||||
|
||||
if (!strikethrough && stdUnderline == null && imUnderline == null) {
|
||||
return label.handleGetOutline(x, y);
|
||||
}
|
||||
|
||||
CoreMetrics cm = label.getCoreMetrics();
|
||||
|
||||
// NOTE: The performace of the following code may
|
||||
// be very poor.
|
||||
float ulThickness = cm.underlineThickness;
|
||||
float ulOffset = cm.underlineOffset;
|
||||
|
||||
Rectangle2D lb = label.getLogicalBounds();
|
||||
float x1 = x;
|
||||
float x2 = x1 + (float)lb.getWidth();
|
||||
|
||||
Area area = null;
|
||||
|
||||
if (stdUnderline != null) {
|
||||
Shape ul = stdUnderline.getUnderlineShape(ulThickness,
|
||||
x1, x2, y+ulOffset);
|
||||
area = new Area(ul);
|
||||
}
|
||||
|
||||
if (strikethrough) {
|
||||
Stroke stStroke = new BasicStroke(cm.strikethroughThickness,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER);
|
||||
float shiftY = y + cm.strikethroughOffset;
|
||||
Line2D line = new Line2D.Float(x1, shiftY, x2, shiftY);
|
||||
Area slArea = new Area(stStroke.createStrokedShape(line));
|
||||
if(area == null) {
|
||||
area = slArea;
|
||||
} else {
|
||||
area.add(slArea);
|
||||
}
|
||||
}
|
||||
|
||||
if (imUnderline != null) {
|
||||
Shape ul = imUnderline.getUnderlineShape(ulThickness,
|
||||
x1, x2, y+ulOffset);
|
||||
Area ulArea = new Area(ul);
|
||||
if (area == null) {
|
||||
area = ulArea;
|
||||
}
|
||||
else {
|
||||
area.add(ulArea);
|
||||
}
|
||||
}
|
||||
|
||||
// area won't be null here, since at least one underline exists.
|
||||
area.add(new Area(label.handleGetOutline(x, y)));
|
||||
|
||||
return new GeneralPath(area);
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append(super.toString());
|
||||
buf.append("[");
|
||||
if (fgPaint != null) buf.append("fgPaint: " + fgPaint);
|
||||
if (bgPaint != null) buf.append(" bgPaint: " + bgPaint);
|
||||
if (swapColors) buf.append(" swapColors: true");
|
||||
if (strikethrough) buf.append(" strikethrough: true");
|
||||
if (stdUnderline != null) buf.append(" stdUnderline: " + stdUnderline);
|
||||
if (imUnderline != null) buf.append(" imUnderline: " + imUnderline);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
86
jdkSrc/jdk8/sun/font/DelegatingShape.java
Normal file
86
jdkSrc/jdk8/sun/font/DelegatingShape.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
* To avoid people downcasting Shape to a known mutable subclass and
|
||||
* mucking with its internals, we need to interpose a subclass that
|
||||
* cannot be mutated or downcasted.
|
||||
*/
|
||||
public final class DelegatingShape implements Shape {
|
||||
Shape delegate;
|
||||
|
||||
public DelegatingShape(Shape delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
return delegate.getBounds(); // assumes all delegates are immutable via the returned Rectangle
|
||||
}
|
||||
|
||||
public Rectangle2D getBounds2D() {
|
||||
return delegate.getBounds2D(); // assumes all delegates are immutable via the returned Rectangle2D
|
||||
}
|
||||
|
||||
public boolean contains(double x, double y) {
|
||||
return delegate.contains(x, y);
|
||||
}
|
||||
|
||||
public boolean contains(Point2D p) {
|
||||
return delegate.contains(p);
|
||||
}
|
||||
|
||||
public boolean intersects(double x, double y, double w, double h) {
|
||||
return delegate.intersects(x, y, w, h);
|
||||
}
|
||||
|
||||
public boolean intersects(Rectangle2D r) {
|
||||
return delegate.intersects(r);
|
||||
}
|
||||
|
||||
public boolean contains(double x, double y, double w, double h) {
|
||||
return delegate.contains(x, y, w, h);
|
||||
}
|
||||
|
||||
public boolean contains(Rectangle2D r) {
|
||||
return delegate.contains(r);
|
||||
}
|
||||
|
||||
public PathIterator getPathIterator(AffineTransform at) {
|
||||
return delegate.getPathIterator(at);
|
||||
}
|
||||
|
||||
public PathIterator getPathIterator(AffineTransform at, double flatness) {
|
||||
return delegate.getPathIterator(at, flatness);
|
||||
}
|
||||
}
|
||||
92
jdkSrc/jdk8/sun/font/EAttribute.java
Normal file
92
jdkSrc/jdk8/sun/font/EAttribute.java
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2005 - All Rights Reserved
|
||||
*
|
||||
* The original version of this source code and documentation is
|
||||
* copyrighted and owned by IBM. These materials are provided
|
||||
* under terms of a License Agreement between IBM and Sun.
|
||||
* This technology is protected by multiple US and International
|
||||
* patents. This notice and attribution to IBM may not be removed.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.text.AttributedCharacterIterator.Attribute;
|
||||
|
||||
import static java.awt.font.TextAttribute.*;
|
||||
|
||||
public enum EAttribute {
|
||||
EFAMILY(FAMILY),
|
||||
EWEIGHT(WEIGHT),
|
||||
EWIDTH(WIDTH),
|
||||
EPOSTURE(POSTURE),
|
||||
ESIZE(SIZE),
|
||||
ETRANSFORM(TRANSFORM),
|
||||
ESUPERSCRIPT(SUPERSCRIPT),
|
||||
EFONT(FONT),
|
||||
ECHAR_REPLACEMENT(CHAR_REPLACEMENT),
|
||||
EFOREGROUND(FOREGROUND),
|
||||
EBACKGROUND(BACKGROUND),
|
||||
EUNDERLINE(UNDERLINE),
|
||||
ESTRIKETHROUGH(STRIKETHROUGH),
|
||||
ERUN_DIRECTION(RUN_DIRECTION),
|
||||
EBIDI_EMBEDDING(BIDI_EMBEDDING),
|
||||
EJUSTIFICATION(JUSTIFICATION),
|
||||
EINPUT_METHOD_HIGHLIGHT(INPUT_METHOD_HIGHLIGHT),
|
||||
EINPUT_METHOD_UNDERLINE(INPUT_METHOD_UNDERLINE),
|
||||
ESWAP_COLORS(SWAP_COLORS),
|
||||
ENUMERIC_SHAPING(NUMERIC_SHAPING),
|
||||
EKERNING(KERNING),
|
||||
ELIGATURES(LIGATURES),
|
||||
ETRACKING(TRACKING),
|
||||
EBASELINE_TRANSFORM(null);
|
||||
|
||||
/* package */ final int mask;
|
||||
/* package */ final TextAttribute att;
|
||||
|
||||
EAttribute(TextAttribute ta) {
|
||||
mask = 1 << ordinal();
|
||||
att = ta;
|
||||
}
|
||||
|
||||
/* package */ static final EAttribute[] atts = EAttribute.class.getEnumConstants();
|
||||
|
||||
public static EAttribute forAttribute(Attribute ta) {
|
||||
for (EAttribute ea: atts) {
|
||||
if (ea.att == ta) {
|
||||
return ea;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name().substring(1).toLowerCase();
|
||||
}
|
||||
}
|
||||
146
jdkSrc/jdk8/sun/font/ExtendedTextLabel.java
Normal file
146
jdkSrc/jdk8/sun/font/ExtendedTextLabel.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-2003- All Rights Reserved.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
import java.awt.font.GlyphJustificationInfo;
|
||||
import java.awt.font.LineMetrics;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
* An extension of TextLabel that maintains information
|
||||
* about characters.
|
||||
*/
|
||||
|
||||
public abstract class ExtendedTextLabel extends TextLabel
|
||||
implements TextLineComponent{
|
||||
/**
|
||||
* Return the number of characters represented by this label.
|
||||
*/
|
||||
public abstract int getNumCharacters();
|
||||
|
||||
/**
|
||||
* Return the line metrics for all text in this label.
|
||||
*/
|
||||
public abstract CoreMetrics getCoreMetrics();
|
||||
|
||||
/**
|
||||
* Return the x location of the character at the given logical index.
|
||||
*/
|
||||
public abstract float getCharX(int logicalIndex);
|
||||
|
||||
/**
|
||||
* Return the y location of the character at the given logical index.
|
||||
*/
|
||||
public abstract float getCharY(int logicalIndex);
|
||||
|
||||
/**
|
||||
* Return the advance of the character at the given logical index.
|
||||
*/
|
||||
public abstract float getCharAdvance(int logicalIndex);
|
||||
|
||||
/**
|
||||
* Return the visual bounds of the character at the given logical index.
|
||||
* This bounds encloses all the pixels of the character when the label is rendered
|
||||
* at x, y.
|
||||
*/
|
||||
public abstract Rectangle2D getCharVisualBounds(int logicalIndex, float x, float y);
|
||||
|
||||
/**
|
||||
* Return the visual index of the character at the given logical index.
|
||||
*/
|
||||
public abstract int logicalToVisual(int logicalIndex);
|
||||
|
||||
/**
|
||||
* Return the logical index of the character at the given visual index.
|
||||
*/
|
||||
public abstract int visualToLogical(int visualIndex);
|
||||
|
||||
/**
|
||||
* Return the logical index of the character, starting with the character at
|
||||
* logicalStart, whose accumulated advance exceeds width. If the advances of
|
||||
* all characters do not exceed width, return getNumCharacters. If width is
|
||||
* less than zero, return logicalStart - 1.
|
||||
*/
|
||||
public abstract int getLineBreakIndex(int logicalStart, float width);
|
||||
|
||||
/**
|
||||
* Return the accumulated advances of all characters between logicalStart and
|
||||
* logicalLimit.
|
||||
*/
|
||||
public abstract float getAdvanceBetween(int logicalStart, int logicalLimit);
|
||||
|
||||
/**
|
||||
* Return whether a caret can exist on the leading edge of the
|
||||
* character at offset. If the character is part of a ligature
|
||||
* (for example) a caret may not be appropriate at offset.
|
||||
*/
|
||||
public abstract boolean caretAtOffsetIsValid(int offset);
|
||||
|
||||
/**
|
||||
* A convenience overload of getCharVisualBounds that defaults the label origin
|
||||
* to 0, 0.
|
||||
*/
|
||||
public Rectangle2D getCharVisualBounds(int logicalIndex) {
|
||||
return getCharVisualBounds(logicalIndex, 0, 0);
|
||||
}
|
||||
|
||||
public abstract TextLineComponent getSubset(int start, int limit, int dir);
|
||||
|
||||
/**
|
||||
* Return the number of justification records this uses.
|
||||
*/
|
||||
public abstract int getNumJustificationInfos();
|
||||
|
||||
/**
|
||||
* Return GlyphJustificationInfo objects for the characters between
|
||||
* charStart and charLimit, starting at offset infoStart. Infos
|
||||
* will be in visual order. All positions between infoStart and
|
||||
* getNumJustificationInfos will be set. If a position corresponds
|
||||
* to a character outside the provided range, it is set to null.
|
||||
*/
|
||||
public abstract void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit);
|
||||
|
||||
/**
|
||||
* Apply deltas to the data in this component, starting at offset
|
||||
* deltaStart, and return the new component. There are two floats
|
||||
* for each justification info, for a total of 2 * getNumJustificationInfos.
|
||||
* The first delta is the left adjustment, the second is the right
|
||||
* adjustment.
|
||||
* <p>
|
||||
* If flags[0] is true on entry, rejustification is allowed. If
|
||||
* the new component requires rejustification (ligatures were
|
||||
* formed or split), flags[0] will be set on exit.
|
||||
*/
|
||||
public abstract TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags);
|
||||
}
|
||||
1082
jdkSrc/jdk8/sun/font/ExtendedTextSourceLabel.java
Normal file
1082
jdkSrc/jdk8/sun/font/ExtendedTextSourceLabel.java
Normal file
File diff suppressed because it is too large
Load Diff
334
jdkSrc/jdk8/sun/font/FileFont.java
Normal file
334
jdkSrc/jdk8/sun/font/FileFont.java
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
import sun.java2d.Disposer;
|
||||
import sun.java2d.DisposerRecord;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
public abstract class FileFont extends PhysicalFont {
|
||||
|
||||
protected boolean useJavaRasterizer = true;
|
||||
|
||||
/* I/O and file operations are always synchronized on the font
|
||||
* object. Two threads can be accessing the font and retrieving
|
||||
* information, and synchronized only to the extent that filesystem
|
||||
* operations require.
|
||||
* A limited number of files can be open at a time, to limit the
|
||||
* absorption of file descriptors. If a file needs to be opened
|
||||
* when there are none free, then the synchronization of all I/O
|
||||
* ensures that any in progress operation will complete before some
|
||||
* other thread closes the descriptor in order to allocate another one.
|
||||
*/
|
||||
// NB consider using a RAF. FIS has finalize method so may take a
|
||||
// little longer to be GC'd. We don't use this stream at all anyway.
|
||||
// In fact why increase the size of a FileFont object if the stream
|
||||
// isn't needed ..
|
||||
//protected FileInputStream stream;
|
||||
//protected FileChannel channel;
|
||||
protected int fileSize;
|
||||
|
||||
protected FontScaler scaler;
|
||||
|
||||
/* The following variables are used, (and in the case of the arrays,
|
||||
* only initialised) for select fonts where a native scaler may be
|
||||
* used to get glyph images and metrics.
|
||||
* glyphToCharMap is filled in on the fly and used to do a reverse
|
||||
* lookup when a FileFont needs to get the charcode back from a glyph
|
||||
* code so it can re-map via a NativeGlyphMapper to get a native glyph.
|
||||
* This isn't a big hit in time, since a boolean test is sufficient
|
||||
* to choose the usual default path, nor in memory for fonts which take
|
||||
* the native path, since fonts have contiguous zero-based glyph indexes,
|
||||
* and these obviously do all exist in the font.
|
||||
*/
|
||||
protected boolean checkedNatives;
|
||||
protected boolean useNatives;
|
||||
protected NativeFont[] nativeFonts;
|
||||
protected char[] glyphToCharMap;
|
||||
/*
|
||||
* @throws FontFormatException - if the font can't be opened
|
||||
*/
|
||||
FileFont(String platname, Object nativeNames)
|
||||
throws FontFormatException {
|
||||
|
||||
super(platname, nativeNames);
|
||||
}
|
||||
|
||||
FontStrike createStrike(FontStrikeDesc desc) {
|
||||
if (!checkedNatives) {
|
||||
checkUseNatives();
|
||||
}
|
||||
return new FileFontStrike(this, desc);
|
||||
}
|
||||
|
||||
protected boolean checkUseNatives() {
|
||||
checkedNatives = true;
|
||||
return useNatives;
|
||||
}
|
||||
|
||||
/* This method needs to be accessible to FontManager if there is
|
||||
* file pool management. It may be a no-op.
|
||||
*/
|
||||
protected abstract void close();
|
||||
|
||||
|
||||
/*
|
||||
* This is the public interface. The subclasses need to implement
|
||||
* this. The returned block may be longer than the requested length.
|
||||
*/
|
||||
abstract ByteBuffer readBlock(int offset, int length);
|
||||
|
||||
public boolean canDoStyle(int style) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void setFileToRemove(File file, CreatedFontTracker tracker) {
|
||||
Disposer.addObjectRecord(this,
|
||||
new CreatedFontFileDisposerRecord(file, tracker));
|
||||
}
|
||||
|
||||
// MACOSX begin -- Make this static so that we can pass in CFont
|
||||
static void setFileToRemove(Object font, File file, CreatedFontTracker tracker) {
|
||||
Disposer.addObjectRecord(font,
|
||||
new CreatedFontFileDisposerRecord(file, tracker));
|
||||
}
|
||||
// MACOSX - end
|
||||
|
||||
/* This is called when a font scaler is determined to
|
||||
* be unusable (ie bad).
|
||||
* We want to replace current scaler with NullFontScaler, so
|
||||
* we never try to use same font scaler again.
|
||||
* Scaler native resources could have already been disposed
|
||||
* or they will be eventually by Java2D disposer.
|
||||
* However, it should be safe to call dispose() explicitly here.
|
||||
*
|
||||
* For safety we also invalidate all strike's scaler context.
|
||||
* So, in case they cache pointer to native scaler
|
||||
* it will not ever be used.
|
||||
*
|
||||
* It also appears desirable to remove all the entries from the
|
||||
* cache so no other code will pick them up. But we can't just
|
||||
* 'delete' them as code may be using them. And simply dropping
|
||||
* the reference to the cache will make the reference objects
|
||||
* unreachable and so they will not get disposed.
|
||||
* Since a strike may hold (via java arrays) native pointers to many
|
||||
* rasterised glyphs, this would be a memory leak.
|
||||
* The solution is :
|
||||
* - to move all the entries to another map where they
|
||||
* are no longer locatable
|
||||
* - update FontStrikeDisposer to be able to distinguish which
|
||||
* map they are held in via a boolean flag
|
||||
* Since this isn't expected to be anything other than an extremely
|
||||
* rare maybe it is not worth doing this last part.
|
||||
*/
|
||||
synchronized void deregisterFontAndClearStrikeCache() {
|
||||
SunFontManager fm = SunFontManager.getInstance();
|
||||
fm.deRegisterBadFont(this);
|
||||
|
||||
for (Reference strikeRef : strikeCache.values()) {
|
||||
if (strikeRef != null) {
|
||||
/* NB we know these are all FileFontStrike instances
|
||||
* because the cache is on this FileFont
|
||||
*/
|
||||
FileFontStrike strike = (FileFontStrike)strikeRef.get();
|
||||
if (strike != null && strike.pScalerContext != 0L) {
|
||||
scaler.invalidateScalerContext(strike.pScalerContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scaler != null) {
|
||||
scaler.disposeScaler();
|
||||
}
|
||||
scaler = FontScaler.getNullScaler();
|
||||
}
|
||||
|
||||
StrikeMetrics getFontMetrics(long pScalerContext) {
|
||||
try {
|
||||
return getScaler().getFontMetrics(pScalerContext);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getFontMetrics(pScalerContext);
|
||||
}
|
||||
}
|
||||
|
||||
float getGlyphAdvance(long pScalerContext, int glyphCode) {
|
||||
try {
|
||||
return getScaler().getGlyphAdvance(pScalerContext, glyphCode);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphAdvance(pScalerContext, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) {
|
||||
try {
|
||||
getScaler().getGlyphMetrics(pScalerContext, glyphCode, metrics);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
getGlyphMetrics(pScalerContext, glyphCode, metrics);
|
||||
}
|
||||
}
|
||||
|
||||
long getGlyphImage(long pScalerContext, int glyphCode) {
|
||||
try {
|
||||
return getScaler().getGlyphImage(pScalerContext, glyphCode);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphImage(pScalerContext, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) {
|
||||
try {
|
||||
return getScaler().getGlyphOutlineBounds(pScalerContext, glyphCode);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphOutlineBounds(pScalerContext, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) {
|
||||
try {
|
||||
return getScaler().getGlyphOutline(pScalerContext, glyphCode, x, y);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphOutline(pScalerContext, glyphCode, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) {
|
||||
try {
|
||||
return getScaler().getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y);
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/* T1 & TT implementation differ so this method is abstract.
|
||||
NB: null should not be returned here! */
|
||||
protected abstract FontScaler getScaler();
|
||||
|
||||
protected long getUnitsPerEm() {
|
||||
return getScaler().getUnitsPerEm();
|
||||
}
|
||||
|
||||
private static class CreatedFontFileDisposerRecord
|
||||
implements DisposerRecord {
|
||||
|
||||
File fontFile = null;
|
||||
CreatedFontTracker tracker;
|
||||
|
||||
private CreatedFontFileDisposerRecord(File file,
|
||||
CreatedFontTracker tracker) {
|
||||
fontFile = file;
|
||||
this.tracker = tracker;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction() {
|
||||
public Object run() {
|
||||
if (fontFile != null) {
|
||||
try {
|
||||
if (tracker != null) {
|
||||
tracker.subBytes((int)fontFile.length());
|
||||
}
|
||||
/* REMIND: is it possible that the file is
|
||||
* still open? It will be closed when the
|
||||
* font2D is disposed but could this code
|
||||
* execute first? If so the file would not
|
||||
* be deleted on MS-windows.
|
||||
*/
|
||||
fontFile.delete();
|
||||
/* remove from delete on exit hook list : */
|
||||
// FIXME: still need to be refactored
|
||||
SunFontManager.getInstance().tmpFontFiles.remove(fontFile);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected String getPublicFileName() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null) {
|
||||
return platName;
|
||||
}
|
||||
boolean canReadProperty = true;
|
||||
|
||||
try {
|
||||
sm.checkPropertyAccess("java.io.tmpdir");
|
||||
} catch (SecurityException e) {
|
||||
canReadProperty = false;
|
||||
}
|
||||
|
||||
if (canReadProperty) {
|
||||
return platName;
|
||||
}
|
||||
|
||||
final File f = new File(platName);
|
||||
|
||||
Boolean isTmpFile = Boolean.FALSE;
|
||||
try {
|
||||
isTmpFile = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<Boolean>() {
|
||||
public Boolean run() {
|
||||
File tmp = new File(System.getProperty("java.io.tmpdir"));
|
||||
try {
|
||||
String tpath = tmp.getCanonicalPath();
|
||||
String fpath = f.getCanonicalPath();
|
||||
|
||||
return (fpath == null) || fpath.startsWith(tpath);
|
||||
} catch (IOException e) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (PrivilegedActionException e) {
|
||||
// unable to verify whether value of java.io.tempdir will be
|
||||
// exposed, so return only a name of the font file.
|
||||
isTmpFile = Boolean.TRUE;
|
||||
}
|
||||
|
||||
return isTmpFile ? "temp file" : platName;
|
||||
}
|
||||
}
|
||||
985
jdkSrc/jdk8/sun/font/FileFontStrike.java
Normal file
985
jdkSrc/jdk8/sun/font/FileFontStrike.java
Normal file
@@ -0,0 +1,985 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import static sun.awt.SunHints.*;
|
||||
|
||||
|
||||
public class FileFontStrike extends PhysicalStrike {
|
||||
|
||||
/* fffe and ffff are values we specially interpret as meaning
|
||||
* invisible glyphs.
|
||||
*/
|
||||
static final int INVISIBLE_GLYPHS = 0x0fffe;
|
||||
|
||||
private FileFont fileFont;
|
||||
|
||||
/* REMIND: replace this scheme with one that installs a cache
|
||||
* instance of the appropriate type. It will require changes in
|
||||
* FontStrikeDisposer and NativeStrike etc.
|
||||
*/
|
||||
private static final int UNINITIALISED = 0;
|
||||
private static final int INTARRAY = 1;
|
||||
private static final int LONGARRAY = 2;
|
||||
private static final int SEGINTARRAY = 3;
|
||||
private static final int SEGLONGARRAY = 4;
|
||||
|
||||
private volatile int glyphCacheFormat = UNINITIALISED;
|
||||
|
||||
/* segmented arrays are blocks of 32 */
|
||||
private static final int SEGSHIFT = 5;
|
||||
private static final int SEGSIZE = 1 << SEGSHIFT;
|
||||
|
||||
private boolean segmentedCache;
|
||||
private int[][] segIntGlyphImages;
|
||||
private long[][] segLongGlyphImages;
|
||||
|
||||
/* The "metrics" information requested by clients is usually nothing
|
||||
* more than the horizontal advance of the character.
|
||||
* In most cases this advance and other metrics information is stored
|
||||
* in the glyph image cache.
|
||||
* But in some cases we do not automatically retrieve the glyph
|
||||
* image when the advance is requested. In those cases we want to
|
||||
* cache the advances since this has been shown to be important for
|
||||
* performance.
|
||||
* The segmented cache is used in cases when the single array
|
||||
* would be too large.
|
||||
*/
|
||||
private float[] horizontalAdvances;
|
||||
private float[][] segHorizontalAdvances;
|
||||
|
||||
/* Outline bounds are used when printing and when drawing outlines
|
||||
* to the screen. On balance the relative rarity of these cases
|
||||
* and the fact that getting this requires generating a path at
|
||||
* the scaler level means that its probably OK to store these
|
||||
* in a Java-level hashmap as the trade-off between time and space.
|
||||
* Later can revisit whether to cache these at all, or elsewhere.
|
||||
* Should also profile whether subsequent to getting the bounds, the
|
||||
* outline itself is also requested. The 1.4 implementation doesn't
|
||||
* cache outlines so you could generate the path twice - once to get
|
||||
* the bounds and again to return the outline to the client.
|
||||
* If the two uses are coincident then also look into caching outlines.
|
||||
* One simple optimisation is that we could store the last single
|
||||
* outline retrieved. This assumes that bounds then outline will always
|
||||
* be retrieved for a glyph rather than retrieving bounds for all glyphs
|
||||
* then outlines for all glyphs.
|
||||
*/
|
||||
ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap;
|
||||
SoftReference<ConcurrentHashMap<Integer, Point2D.Float>>
|
||||
glyphMetricsMapRef;
|
||||
|
||||
AffineTransform invertDevTx;
|
||||
|
||||
boolean useNatives;
|
||||
NativeStrike[] nativeStrikes;
|
||||
|
||||
/* Used only for communication to native layer */
|
||||
private int intPtSize;
|
||||
|
||||
/* Perform global initialisation needed for Windows native rasterizer */
|
||||
private static native boolean initNative();
|
||||
private static boolean isXPorLater = false;
|
||||
static {
|
||||
if (FontUtilities.isWindows && !FontUtilities.useT2K &&
|
||||
!GraphicsEnvironment.isHeadless()) {
|
||||
isXPorLater = initNative();
|
||||
}
|
||||
}
|
||||
|
||||
FileFontStrike(FileFont fileFont, FontStrikeDesc desc) {
|
||||
super(fileFont, desc);
|
||||
this.fileFont = fileFont;
|
||||
|
||||
if (desc.style != fileFont.style) {
|
||||
/* If using algorithmic styling, the base values are
|
||||
* boldness = 1.0, italic = 0.0. The superclass constructor
|
||||
* initialises these.
|
||||
*/
|
||||
if ((desc.style & Font.ITALIC) == Font.ITALIC &&
|
||||
(fileFont.style & Font.ITALIC) == 0) {
|
||||
algoStyle = true;
|
||||
italic = 0.7f;
|
||||
}
|
||||
if ((desc.style & Font.BOLD) == Font.BOLD &&
|
||||
((fileFont.style & Font.BOLD) == 0)) {
|
||||
algoStyle = true;
|
||||
boldness = 1.33f;
|
||||
}
|
||||
}
|
||||
double[] matrix = new double[4];
|
||||
AffineTransform at = desc.glyphTx;
|
||||
at.getMatrix(matrix);
|
||||
if (!desc.devTx.isIdentity() &&
|
||||
desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
|
||||
try {
|
||||
invertDevTx = desc.devTx.createInverse();
|
||||
} catch (NoninvertibleTransformException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Amble fonts are better rendered unhinted although there's the
|
||||
* inevitable fuzziness that accompanies this due to no longer
|
||||
* snapping stems to the pixel grid. The exception is that in B&W
|
||||
* mode they are worse without hinting. The down side to that is that
|
||||
* B&W metrics will differ which normally isn't the case, although
|
||||
* since AA mode is part of the measuring context that should be OK.
|
||||
* We don't expect Amble to be installed in the Windows fonts folder.
|
||||
* If we were to, then we'd also might want to disable using the
|
||||
* native rasteriser path which is used for LCD mode for platform
|
||||
* fonts. since we have no way to disable hinting by GDI.
|
||||
* In the case of Amble, since its 'gasp' table says to disable
|
||||
* hinting, I'd expect GDI to follow that, so likely it should
|
||||
* all be consistent even if GDI used.
|
||||
*/
|
||||
boolean disableHinting = desc.aaHint != INTVAL_TEXT_ANTIALIAS_OFF &&
|
||||
fileFont.familyName.startsWith("Amble");
|
||||
|
||||
/* If any of the values is NaN then substitute the null scaler context.
|
||||
* This will return null images, zero advance, and empty outlines
|
||||
* as no rendering need take place in this case.
|
||||
* We pass in the null scaler as the singleton null context
|
||||
* requires it. However
|
||||
*/
|
||||
if (Double.isNaN(matrix[0]) || Double.isNaN(matrix[1]) ||
|
||||
Double.isNaN(matrix[2]) || Double.isNaN(matrix[3]) ||
|
||||
fileFont.getScaler() == null) {
|
||||
pScalerContext = NullFontScaler.getNullScalerContext();
|
||||
} else {
|
||||
pScalerContext = fileFont.getScaler().createScalerContext(matrix,
|
||||
desc.aaHint, desc.fmHint,
|
||||
boldness, italic, disableHinting);
|
||||
}
|
||||
|
||||
mapper = fileFont.getMapper();
|
||||
int numGlyphs = mapper.getNumGlyphs();
|
||||
|
||||
/* Always segment for fonts with > 256 glyphs, but also for smaller
|
||||
* fonts with non-typical sizes and transforms.
|
||||
* Segmenting for all non-typical pt sizes helps to minimize memory
|
||||
* usage when very many distinct strikes are created.
|
||||
* The size range of 0->5 and 37->INF for segmenting is arbitrary
|
||||
* but the intention is that typical GUI integer point sizes (6->36)
|
||||
* should not segment unless there's another reason to do so.
|
||||
*/
|
||||
float ptSize = (float)matrix[3]; // interpreted only when meaningful.
|
||||
int iSize = intPtSize = (int)ptSize;
|
||||
boolean isSimpleTx = (at.getType() & complexTX) == 0;
|
||||
segmentedCache =
|
||||
(numGlyphs > SEGSIZE << 3) ||
|
||||
((numGlyphs > SEGSIZE << 1) &&
|
||||
(!isSimpleTx || ptSize != iSize || iSize < 6 || iSize > 36));
|
||||
|
||||
/* This can only happen if we failed to allocate memory for context.
|
||||
* NB: in such case we may still have some memory in java heap
|
||||
* but subsequent attempt to allocate null scaler context
|
||||
* may fail too (cause it is allocate in the native heap).
|
||||
* It is not clear how to make this more robust but on the
|
||||
* other hand getting NULL here seems to be extremely unlikely.
|
||||
*/
|
||||
if (pScalerContext == 0L) {
|
||||
/* REMIND: when the code is updated to install cache objects
|
||||
* rather than using a switch this will be more efficient.
|
||||
*/
|
||||
this.disposer = new FontStrikeDisposer(fileFont, desc);
|
||||
initGlyphCache();
|
||||
pScalerContext = NullFontScaler.getNullScalerContext();
|
||||
SunFontManager.getInstance().deRegisterBadFont(fileFont);
|
||||
return;
|
||||
}
|
||||
/* First, see if native code should be used to create the glyph.
|
||||
* GDI will return the integer metrics, not fractional metrics, which
|
||||
* may be requested for this strike, so we would require here that :
|
||||
* desc.fmHint != INTVAL_FRACTIONALMETRICS_ON
|
||||
* except that the advance returned by GDI is always overwritten by
|
||||
* the JDK rasteriser supplied one (see getGlyphImageFromWindows()).
|
||||
*/
|
||||
if (FontUtilities.isWindows && isXPorLater &&
|
||||
!FontUtilities.useT2K &&
|
||||
!GraphicsEnvironment.isHeadless() &&
|
||||
!fileFont.useJavaRasterizer &&
|
||||
(desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
|
||||
desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) &&
|
||||
(matrix[1] == 0.0 && matrix[2] == 0.0 &&
|
||||
matrix[0] == matrix[3] &&
|
||||
matrix[0] >= 3.0 && matrix[0] <= 100.0) &&
|
||||
!((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) {
|
||||
useNatives = true;
|
||||
}
|
||||
else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) {
|
||||
/* Check its a simple scale of a pt size in the range
|
||||
* where native bitmaps typically exist (6-36 pts) */
|
||||
if (matrix[1] == 0.0 && matrix[2] == 0.0 &&
|
||||
matrix[0] >= 6.0 && matrix[0] <= 36.0 &&
|
||||
matrix[0] == matrix[3]) {
|
||||
useNatives = true;
|
||||
int numNatives = fileFont.nativeFonts.length;
|
||||
nativeStrikes = new NativeStrike[numNatives];
|
||||
/* Maybe initialise these strikes lazily?. But we
|
||||
* know we need at least one
|
||||
*/
|
||||
for (int i=0; i<numNatives; i++) {
|
||||
nativeStrikes[i] =
|
||||
new NativeStrike(fileFont.nativeFonts[i], desc, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FontUtilities.isLogging() && FontUtilities.isWindows) {
|
||||
FontUtilities.getLogger().info
|
||||
("Strike for " + fileFont + " at size = " + intPtSize +
|
||||
" use natives = " + useNatives +
|
||||
" useJavaRasteriser = " + fileFont.useJavaRasterizer +
|
||||
" AAHint = " + desc.aaHint +
|
||||
" Has Embedded bitmaps = " +
|
||||
((TrueTypeFont)fileFont).
|
||||
useEmbeddedBitmapsForSize(intPtSize));
|
||||
}
|
||||
this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext);
|
||||
|
||||
/* Always get the image and the advance together for smaller sizes
|
||||
* that are likely to be important to rendering performance.
|
||||
* The pixel size of 48.0 can be thought of as
|
||||
* "maximumSizeForGetImageWithAdvance".
|
||||
* This should be no greater than OutlineTextRender.THRESHOLD.
|
||||
*/
|
||||
double maxSz = 48.0;
|
||||
getImageWithAdvance =
|
||||
Math.abs(at.getScaleX()) <= maxSz &&
|
||||
Math.abs(at.getScaleY()) <= maxSz &&
|
||||
Math.abs(at.getShearX()) <= maxSz &&
|
||||
Math.abs(at.getShearY()) <= maxSz;
|
||||
|
||||
/* Some applications request advance frequently during layout.
|
||||
* If we are not getting and caching the image with the advance,
|
||||
* there is a potentially significant performance penalty if the
|
||||
* advance is repeatedly requested before requesting the image.
|
||||
* We should at least cache the horizontal advance.
|
||||
* REMIND: could use info in the font, eg hmtx, to retrieve some
|
||||
* advances. But still want to cache it here.
|
||||
*/
|
||||
|
||||
if (!getImageWithAdvance) {
|
||||
if (!segmentedCache) {
|
||||
horizontalAdvances = new float[numGlyphs];
|
||||
/* use max float as uninitialised advance */
|
||||
for (int i=0; i<numGlyphs; i++) {
|
||||
horizontalAdvances[i] = Float.MAX_VALUE;
|
||||
}
|
||||
} else {
|
||||
int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
|
||||
segHorizontalAdvances = new float[numSegments][];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A number of methods are delegated by the strike to the scaler
|
||||
* context which is a shared resource on a physical font.
|
||||
*/
|
||||
|
||||
public int getNumGlyphs() {
|
||||
return fileFont.getNumGlyphs();
|
||||
}
|
||||
|
||||
long getGlyphImageFromNative(int glyphCode) {
|
||||
if (FontUtilities.isWindows) {
|
||||
return getGlyphImageFromWindows(glyphCode);
|
||||
} else {
|
||||
return getGlyphImageFromX11(glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
/* There's no global state conflicts, so this method is not
|
||||
* presently synchronized.
|
||||
*/
|
||||
private native long _getGlyphImageFromWindows(String family,
|
||||
int style,
|
||||
int size,
|
||||
int glyphCode,
|
||||
boolean fracMetrics,
|
||||
int fontDataSize);
|
||||
|
||||
long getGlyphImageFromWindows(int glyphCode) {
|
||||
String family = fileFont.getFamilyName(null);
|
||||
int style = desc.style & Font.BOLD | desc.style & Font.ITALIC
|
||||
| fileFont.getStyle();
|
||||
int size = intPtSize;
|
||||
long ptr = _getGlyphImageFromWindows
|
||||
(family, style, size, glyphCode,
|
||||
desc.fmHint == INTVAL_FRACTIONALMETRICS_ON,
|
||||
((TrueTypeFont)fileFont).fontDataSize);
|
||||
if (ptr != 0) {
|
||||
/* Get the advance from the JDK rasterizer. This is mostly
|
||||
* necessary for the fractional metrics case, but there are
|
||||
* also some very small number (<0.25%) of marginal cases where
|
||||
* there is some rounding difference between windows and JDK.
|
||||
* After these are resolved, we can restrict this extra
|
||||
* work to the FM case.
|
||||
*/
|
||||
float advance = getGlyphAdvance(glyphCode, false);
|
||||
StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset,
|
||||
advance);
|
||||
return ptr;
|
||||
} else {
|
||||
if (FontUtilities.isLogging()) {
|
||||
FontUtilities.getLogger().warning(
|
||||
"Failed to render glyph using GDI: code=" + glyphCode
|
||||
+ ", fontFamily=" + family + ", style=" + style
|
||||
+ ", size=" + size);
|
||||
}
|
||||
return fileFont.getGlyphImage(pScalerContext, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try the native strikes first, then try the fileFont strike */
|
||||
long getGlyphImageFromX11(int glyphCode) {
|
||||
long glyphPtr;
|
||||
char charCode = fileFont.glyphToCharMap[glyphCode];
|
||||
for (int i=0;i<nativeStrikes.length;i++) {
|
||||
CharToGlyphMapper mapper = fileFont.nativeFonts[i].getMapper();
|
||||
int gc = mapper.charToGlyph(charCode)&0xffff;
|
||||
if (gc != mapper.getMissingGlyphCode()) {
|
||||
glyphPtr = nativeStrikes[i].getGlyphImagePtrNoCache(gc);
|
||||
if (glyphPtr != 0L) {
|
||||
return glyphPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileFont.getGlyphImage(pScalerContext, glyphCode);
|
||||
}
|
||||
|
||||
long getGlyphImagePtr(int glyphCode) {
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
return StrikeCache.invisibleGlyphPtr;
|
||||
}
|
||||
long glyphPtr = 0L;
|
||||
if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) {
|
||||
return glyphPtr;
|
||||
} else {
|
||||
if (useNatives) {
|
||||
glyphPtr = getGlyphImageFromNative(glyphCode);
|
||||
if (glyphPtr == 0L && FontUtilities.isLogging()) {
|
||||
FontUtilities.getLogger().info
|
||||
("Strike for " + fileFont +
|
||||
" at size = " + intPtSize +
|
||||
" couldn't get native glyph for code = " + glyphCode);
|
||||
}
|
||||
} if (glyphPtr == 0L) {
|
||||
glyphPtr = fileFont.getGlyphImage(pScalerContext,
|
||||
glyphCode);
|
||||
}
|
||||
return setCachedGlyphPtr(glyphCode, glyphPtr);
|
||||
}
|
||||
}
|
||||
|
||||
void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
|
||||
|
||||
for (int i=0; i<len; i++) {
|
||||
int glyphCode = glyphCodes[i];
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
images[i] = StrikeCache.invisibleGlyphPtr;
|
||||
continue;
|
||||
} else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
|
||||
continue;
|
||||
} else {
|
||||
long glyphPtr = 0L;
|
||||
if (useNatives) {
|
||||
glyphPtr = getGlyphImageFromNative(glyphCode);
|
||||
} if (glyphPtr == 0L) {
|
||||
glyphPtr = fileFont.getGlyphImage(pScalerContext,
|
||||
glyphCode);
|
||||
}
|
||||
images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The following method is called from CompositeStrike as a special case.
|
||||
*/
|
||||
int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
|
||||
|
||||
int convertedCnt = 0;
|
||||
|
||||
for (int i=0; i<len; i++) {
|
||||
int glyphCode = glyphCodes[i];
|
||||
if (glyphCode >>> 24 != 0) {
|
||||
return convertedCnt;
|
||||
} else {
|
||||
convertedCnt++;
|
||||
}
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
images[i] = StrikeCache.invisibleGlyphPtr;
|
||||
continue;
|
||||
} else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
|
||||
continue;
|
||||
} else {
|
||||
long glyphPtr = 0L;
|
||||
if (useNatives) {
|
||||
glyphPtr = getGlyphImageFromNative(glyphCode);
|
||||
}
|
||||
if (glyphPtr == 0L) {
|
||||
glyphPtr = fileFont.getGlyphImage(pScalerContext,
|
||||
glyphCode);
|
||||
}
|
||||
images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
|
||||
}
|
||||
}
|
||||
return convertedCnt;
|
||||
}
|
||||
|
||||
/* Only look in the cache */
|
||||
long getCachedGlyphPtr(int glyphCode) {
|
||||
try {
|
||||
return getCachedGlyphPtrInternal(glyphCode);
|
||||
} catch (Exception e) {
|
||||
NullFontScaler nullScaler =
|
||||
(NullFontScaler)FontScaler.getNullScaler();
|
||||
long nullSC = NullFontScaler.getNullScalerContext();
|
||||
return nullScaler.getGlyphImage(nullSC, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
private long getCachedGlyphPtrInternal(int glyphCode) {
|
||||
switch (glyphCacheFormat) {
|
||||
case INTARRAY:
|
||||
return intGlyphImages[glyphCode] & INTMASK;
|
||||
case SEGINTARRAY:
|
||||
int segIndex = glyphCode >> SEGSHIFT;
|
||||
if (segIntGlyphImages[segIndex] != null) {
|
||||
int subIndex = glyphCode % SEGSIZE;
|
||||
return segIntGlyphImages[segIndex][subIndex] & INTMASK;
|
||||
} else {
|
||||
return 0L;
|
||||
}
|
||||
case LONGARRAY:
|
||||
return longGlyphImages[glyphCode];
|
||||
case SEGLONGARRAY:
|
||||
segIndex = glyphCode >> SEGSHIFT;
|
||||
if (segLongGlyphImages[segIndex] != null) {
|
||||
int subIndex = glyphCode % SEGSIZE;
|
||||
return segLongGlyphImages[segIndex][subIndex];
|
||||
} else {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
/* If reach here cache is UNINITIALISED. */
|
||||
return 0L;
|
||||
}
|
||||
|
||||
private synchronized long setCachedGlyphPtr(int glyphCode, long glyphPtr) {
|
||||
try {
|
||||
return setCachedGlyphPtrInternal(glyphCode, glyphPtr);
|
||||
} catch (Exception e) {
|
||||
switch (glyphCacheFormat) {
|
||||
case INTARRAY:
|
||||
case SEGINTARRAY:
|
||||
StrikeCache.freeIntPointer((int)glyphPtr);
|
||||
break;
|
||||
case LONGARRAY:
|
||||
case SEGLONGARRAY:
|
||||
StrikeCache.freeLongPointer(glyphPtr);
|
||||
break;
|
||||
}
|
||||
NullFontScaler nullScaler =
|
||||
(NullFontScaler)FontScaler.getNullScaler();
|
||||
long nullSC = NullFontScaler.getNullScalerContext();
|
||||
return nullScaler.getGlyphImage(nullSC, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
private long setCachedGlyphPtrInternal(int glyphCode, long glyphPtr) {
|
||||
switch (glyphCacheFormat) {
|
||||
case INTARRAY:
|
||||
if (intGlyphImages[glyphCode] == 0) {
|
||||
intGlyphImages[glyphCode] = (int)glyphPtr;
|
||||
return glyphPtr;
|
||||
} else {
|
||||
StrikeCache.freeIntPointer((int)glyphPtr);
|
||||
return intGlyphImages[glyphCode] & INTMASK;
|
||||
}
|
||||
|
||||
case SEGINTARRAY:
|
||||
int segIndex = glyphCode >> SEGSHIFT;
|
||||
int subIndex = glyphCode % SEGSIZE;
|
||||
if (segIntGlyphImages[segIndex] == null) {
|
||||
segIntGlyphImages[segIndex] = new int[SEGSIZE];
|
||||
}
|
||||
if (segIntGlyphImages[segIndex][subIndex] == 0) {
|
||||
segIntGlyphImages[segIndex][subIndex] = (int)glyphPtr;
|
||||
return glyphPtr;
|
||||
} else {
|
||||
StrikeCache.freeIntPointer((int)glyphPtr);
|
||||
return segIntGlyphImages[segIndex][subIndex] & INTMASK;
|
||||
}
|
||||
|
||||
case LONGARRAY:
|
||||
if (longGlyphImages[glyphCode] == 0L) {
|
||||
longGlyphImages[glyphCode] = glyphPtr;
|
||||
return glyphPtr;
|
||||
} else {
|
||||
StrikeCache.freeLongPointer(glyphPtr);
|
||||
return longGlyphImages[glyphCode];
|
||||
}
|
||||
|
||||
case SEGLONGARRAY:
|
||||
segIndex = glyphCode >> SEGSHIFT;
|
||||
subIndex = glyphCode % SEGSIZE;
|
||||
if (segLongGlyphImages[segIndex] == null) {
|
||||
segLongGlyphImages[segIndex] = new long[SEGSIZE];
|
||||
}
|
||||
if (segLongGlyphImages[segIndex][subIndex] == 0L) {
|
||||
segLongGlyphImages[segIndex][subIndex] = glyphPtr;
|
||||
return glyphPtr;
|
||||
} else {
|
||||
StrikeCache.freeLongPointer(glyphPtr);
|
||||
return segLongGlyphImages[segIndex][subIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/* Reach here only when the cache is not initialised which is only
|
||||
* for the first glyph to be initialised in the strike.
|
||||
* Initialise it and recurse. Note that we are already synchronized.
|
||||
*/
|
||||
initGlyphCache();
|
||||
return setCachedGlyphPtr(glyphCode, glyphPtr);
|
||||
}
|
||||
|
||||
/* Called only from synchronized code or constructor */
|
||||
private synchronized void initGlyphCache() {
|
||||
|
||||
int numGlyphs = mapper.getNumGlyphs();
|
||||
int tmpFormat = UNINITIALISED;
|
||||
if (segmentedCache) {
|
||||
int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
|
||||
if (longAddresses) {
|
||||
tmpFormat = SEGLONGARRAY;
|
||||
segLongGlyphImages = new long[numSegments][];
|
||||
this.disposer.segLongGlyphImages = segLongGlyphImages;
|
||||
} else {
|
||||
tmpFormat = SEGINTARRAY;
|
||||
segIntGlyphImages = new int[numSegments][];
|
||||
this.disposer.segIntGlyphImages = segIntGlyphImages;
|
||||
}
|
||||
} else {
|
||||
if (longAddresses) {
|
||||
tmpFormat = LONGARRAY;
|
||||
longGlyphImages = new long[numGlyphs];
|
||||
this.disposer.longGlyphImages = longGlyphImages;
|
||||
} else {
|
||||
tmpFormat = INTARRAY;
|
||||
intGlyphImages = new int[numGlyphs];
|
||||
this.disposer.intGlyphImages = intGlyphImages;
|
||||
}
|
||||
}
|
||||
glyphCacheFormat = tmpFormat;
|
||||
}
|
||||
|
||||
float getGlyphAdvance(int glyphCode) {
|
||||
return getGlyphAdvance(glyphCode, true);
|
||||
}
|
||||
|
||||
/* Metrics info is always retrieved. If the GlyphInfo address is non-zero
|
||||
* then metrics info there is valid and can just be copied.
|
||||
* This is in user space coordinates unless getUserAdv == false.
|
||||
* Device space advance should not be propagated out of this class.
|
||||
*/
|
||||
private float getGlyphAdvance(int glyphCode, boolean getUserAdv) {
|
||||
float advance;
|
||||
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/* Notes on the (getUserAdv == false) case.
|
||||
*
|
||||
* Setting getUserAdv == false is internal to this class.
|
||||
* If there's no graphics transform we can let
|
||||
* getGlyphAdvance take its course, and potentially caching in
|
||||
* advances arrays, except for signalling that
|
||||
* getUserAdv == false means there is no need to create an image.
|
||||
* It is possible that code already calculated the user advance,
|
||||
* and it is desirable to take advantage of that work.
|
||||
* But, if there's a transform and we want device advance, we
|
||||
* can't use any values cached in the advances arrays - unless
|
||||
* first re-transform them into device space using 'desc.devTx'.
|
||||
* invertDevTx is null if the graphics transform is identity,
|
||||
* a translate, or non-invertible. The latter case should
|
||||
* not ever occur in the getUserAdv == false path.
|
||||
* In other words its either null, or the inversion of a
|
||||
* simple uniform scale. If its null, we can populate and
|
||||
* use the advance caches as normal.
|
||||
*
|
||||
* If we don't find a cached value, obtain the device advance and
|
||||
* return it. This will get stashed on the image by the caller and any
|
||||
* subsequent metrics calls will be able to use it as is the case
|
||||
* whenever an image is what is initially requested.
|
||||
*
|
||||
* Don't query if there's a value cached on the image, since this
|
||||
* getUserAdv==false code path is entered solely when none exists.
|
||||
*/
|
||||
if (horizontalAdvances != null) {
|
||||
advance = horizontalAdvances[glyphCode];
|
||||
if (advance != Float.MAX_VALUE) {
|
||||
if (!getUserAdv && invertDevTx != null) {
|
||||
Point2D.Float metrics = new Point2D.Float(advance, 0f);
|
||||
desc.devTx.deltaTransform(metrics, metrics);
|
||||
return metrics.x;
|
||||
} else {
|
||||
return advance;
|
||||
}
|
||||
}
|
||||
} else if (segmentedCache && segHorizontalAdvances != null) {
|
||||
int segIndex = glyphCode >> SEGSHIFT;
|
||||
float[] subArray = segHorizontalAdvances[segIndex];
|
||||
if (subArray != null) {
|
||||
advance = subArray[glyphCode % SEGSIZE];
|
||||
if (advance != Float.MAX_VALUE) {
|
||||
if (!getUserAdv && invertDevTx != null) {
|
||||
Point2D.Float metrics = new Point2D.Float(advance, 0f);
|
||||
desc.devTx.deltaTransform(metrics, metrics);
|
||||
return metrics.x;
|
||||
} else {
|
||||
return advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!getUserAdv && invertDevTx != null) {
|
||||
Point2D.Float metrics = new Point2D.Float();
|
||||
fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
|
||||
return metrics.x;
|
||||
}
|
||||
|
||||
if (invertDevTx != null || !getUserAdv) {
|
||||
/* If there is a device transform need x & y advance to
|
||||
* transform back into user space.
|
||||
*/
|
||||
advance = getGlyphMetrics(glyphCode, getUserAdv).x;
|
||||
} else {
|
||||
long glyphPtr;
|
||||
if (getImageWithAdvance) {
|
||||
/* A heuristic optimisation says that for most cases its
|
||||
* worthwhile retrieving the image at the same time as the
|
||||
* advance. So here we get the image data even if its not
|
||||
* already cached.
|
||||
*/
|
||||
glyphPtr = getGlyphImagePtr(glyphCode);
|
||||
} else {
|
||||
glyphPtr = getCachedGlyphPtr(glyphCode);
|
||||
}
|
||||
if (glyphPtr != 0L) {
|
||||
advance = StrikeCache.unsafe.getFloat
|
||||
(glyphPtr + StrikeCache.xAdvanceOffset);
|
||||
|
||||
} else {
|
||||
advance = fileFont.getGlyphAdvance(pScalerContext, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (horizontalAdvances != null) {
|
||||
horizontalAdvances[glyphCode] = advance;
|
||||
} else if (segmentedCache && segHorizontalAdvances != null) {
|
||||
int segIndex = glyphCode >> SEGSHIFT;
|
||||
int subIndex = glyphCode % SEGSIZE;
|
||||
if (segHorizontalAdvances[segIndex] == null) {
|
||||
segHorizontalAdvances[segIndex] = new float[SEGSIZE];
|
||||
for (int i=0; i<SEGSIZE; i++) {
|
||||
segHorizontalAdvances[segIndex][i] = Float.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
segHorizontalAdvances[segIndex][subIndex] = advance;
|
||||
}
|
||||
return advance;
|
||||
}
|
||||
|
||||
float getCodePointAdvance(int cp) {
|
||||
return getGlyphAdvance(mapper.charToGlyph(cp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Result and pt are both in device space.
|
||||
*/
|
||||
void getGlyphImageBounds(int glyphCode, Point2D.Float pt,
|
||||
Rectangle result) {
|
||||
|
||||
long ptr = getGlyphImagePtr(glyphCode);
|
||||
float topLeftX, topLeftY;
|
||||
|
||||
/* With our current design NULL ptr is not possible
|
||||
but if we eventually allow scalers to return NULL pointers
|
||||
this check might be actually useful. */
|
||||
if (ptr == 0L) {
|
||||
result.x = (int) Math.floor(pt.x);
|
||||
result.y = (int) Math.floor(pt.y);
|
||||
result.width = result.height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
topLeftX = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftXOffset);
|
||||
topLeftY = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftYOffset);
|
||||
|
||||
result.x = (int)Math.floor(pt.x + topLeftX);
|
||||
result.y = (int)Math.floor(pt.y + topLeftY);
|
||||
result.width =
|
||||
StrikeCache.unsafe.getShort(ptr+StrikeCache.widthOffset) &0x0ffff;
|
||||
result.height =
|
||||
StrikeCache.unsafe.getShort(ptr+StrikeCache.heightOffset) &0x0ffff;
|
||||
|
||||
/* HRGB LCD text may have padding that is empty. This is almost always
|
||||
* going to be when topLeftX is -2 or less.
|
||||
* Try to return a tighter bounding box in that case.
|
||||
* If the first three bytes of every row are all zero, then
|
||||
* add 1 to "x" and reduce "width" by 1.
|
||||
*/
|
||||
if ((desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
|
||||
desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR)
|
||||
&& topLeftX <= -2.0f) {
|
||||
int minx = getGlyphImageMinX(ptr, (int)result.x);
|
||||
if (minx > result.x) {
|
||||
result.x += 1;
|
||||
result.width -=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getGlyphImageMinX(long ptr, int origMinX) {
|
||||
|
||||
int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);
|
||||
int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);
|
||||
int rowBytes =
|
||||
StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);
|
||||
|
||||
if (rowBytes == width) {
|
||||
return origMinX;
|
||||
}
|
||||
|
||||
long pixelData =
|
||||
StrikeCache.unsafe.getAddress(ptr + StrikeCache.pixelDataOffset);
|
||||
|
||||
if (pixelData == 0L) {
|
||||
return origMinX;
|
||||
}
|
||||
|
||||
for (int y=0;y<height;y++) {
|
||||
for (int x=0;x<3;x++) {
|
||||
if (StrikeCache.unsafe.getByte(pixelData+y*rowBytes+x) != 0) {
|
||||
return origMinX;
|
||||
}
|
||||
}
|
||||
}
|
||||
return origMinX+1;
|
||||
}
|
||||
|
||||
/* These 3 metrics methods below should be implemented to return
|
||||
* values in user space.
|
||||
*/
|
||||
StrikeMetrics getFontMetrics() {
|
||||
if (strikeMetrics == null) {
|
||||
strikeMetrics =
|
||||
fileFont.getFontMetrics(pScalerContext);
|
||||
if (invertDevTx != null) {
|
||||
strikeMetrics.convertToUserSpace(invertDevTx);
|
||||
}
|
||||
}
|
||||
return strikeMetrics;
|
||||
}
|
||||
|
||||
Point2D.Float getGlyphMetrics(int glyphCode) {
|
||||
return getGlyphMetrics(glyphCode, true);
|
||||
}
|
||||
|
||||
private Point2D.Float getGlyphMetrics(int glyphCode, boolean getImage) {
|
||||
Point2D.Float metrics = new Point2D.Float();
|
||||
|
||||
// !!! or do we force sgv user glyphs?
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
return metrics;
|
||||
}
|
||||
long glyphPtr;
|
||||
if (getImageWithAdvance && getImage) {
|
||||
/* A heuristic optimisation says that for most cases its
|
||||
* worthwhile retrieving the image at the same time as the
|
||||
* metrics. So here we get the image data even if its not
|
||||
* already cached.
|
||||
*/
|
||||
glyphPtr = getGlyphImagePtr(glyphCode);
|
||||
} else {
|
||||
glyphPtr = getCachedGlyphPtr(glyphCode);
|
||||
}
|
||||
if (glyphPtr != 0L) {
|
||||
metrics = new Point2D.Float();
|
||||
metrics.x = StrikeCache.unsafe.getFloat
|
||||
(glyphPtr + StrikeCache.xAdvanceOffset);
|
||||
metrics.y = StrikeCache.unsafe.getFloat
|
||||
(glyphPtr + StrikeCache.yAdvanceOffset);
|
||||
/* advance is currently in device space, need to convert back
|
||||
* into user space.
|
||||
* This must not include the translation component. */
|
||||
if (invertDevTx != null) {
|
||||
invertDevTx.deltaTransform(metrics, metrics);
|
||||
}
|
||||
} else {
|
||||
/* We sometimes cache these metrics as they are expensive to
|
||||
* generate for large glyphs.
|
||||
* We never reach this path if we obtain images with advances.
|
||||
* But if we do not obtain images with advances its possible that
|
||||
* we first obtain this information, then the image, and never
|
||||
* will access this value again.
|
||||
*/
|
||||
Integer key = Integer.valueOf(glyphCode);
|
||||
Point2D.Float value = null;
|
||||
ConcurrentHashMap<Integer, Point2D.Float> glyphMetricsMap = null;
|
||||
if (glyphMetricsMapRef != null) {
|
||||
glyphMetricsMap = glyphMetricsMapRef.get();
|
||||
}
|
||||
if (glyphMetricsMap != null) {
|
||||
value = glyphMetricsMap.get(key);
|
||||
if (value != null) {
|
||||
metrics.x = value.x;
|
||||
metrics.y = value.y;
|
||||
/* already in user space */
|
||||
return metrics;
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
|
||||
/* advance is currently in device space, need to convert back
|
||||
* into user space.
|
||||
*/
|
||||
if (invertDevTx != null) {
|
||||
invertDevTx.deltaTransform(metrics, metrics);
|
||||
}
|
||||
value = new Point2D.Float(metrics.x, metrics.y);
|
||||
/* We aren't synchronizing here so it is possible to
|
||||
* overwrite the map with another one but this is harmless.
|
||||
*/
|
||||
if (glyphMetricsMap == null) {
|
||||
glyphMetricsMap =
|
||||
new ConcurrentHashMap<Integer, Point2D.Float>();
|
||||
glyphMetricsMapRef =
|
||||
new SoftReference<ConcurrentHashMap<Integer,
|
||||
Point2D.Float>>(glyphMetricsMap);
|
||||
}
|
||||
glyphMetricsMap.put(key, value);
|
||||
}
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
Point2D.Float getCharMetrics(char ch) {
|
||||
return getGlyphMetrics(mapper.charToGlyph(ch));
|
||||
}
|
||||
|
||||
/* The caller of this can be trusted to return a copy of this
|
||||
* return value rectangle to public API. In fact frequently it
|
||||
* can't use use this return value directly anyway.
|
||||
* This returns bounds in device space. Currently the only
|
||||
* caller is SGV and it converts back to user space.
|
||||
* We could change things so that this code does the conversion so
|
||||
* that all coords coming out of the font system are converted back
|
||||
* into user space even if they were measured in device space.
|
||||
* The same applies to the other methods that return outlines (below)
|
||||
* But it may make particular sense for this method that caches its
|
||||
* results.
|
||||
* There'd be plenty of exceptions, to this too, eg getGlyphPoint needs
|
||||
* device coords as its called from native layout and getGlyphImageBounds
|
||||
* is used by GlyphVector.getGlyphPixelBounds which is specified to
|
||||
* return device coordinates, the image pointers aren't really used
|
||||
* up in Java code either.
|
||||
*/
|
||||
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
|
||||
|
||||
if (boundsMap == null) {
|
||||
boundsMap = new ConcurrentHashMap<Integer, Rectangle2D.Float>();
|
||||
}
|
||||
|
||||
Integer key = Integer.valueOf(glyphCode);
|
||||
Rectangle2D.Float bounds = boundsMap.get(key);
|
||||
|
||||
if (bounds == null) {
|
||||
bounds = fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
|
||||
boundsMap.put(key, bounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public Rectangle2D getOutlineBounds(int glyphCode) {
|
||||
return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
|
||||
}
|
||||
|
||||
private
|
||||
WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;
|
||||
|
||||
GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
|
||||
|
||||
GeneralPath gp = null;
|
||||
ConcurrentHashMap<Integer, GeneralPath> outlineMap = null;
|
||||
|
||||
if (outlineMapRef != null) {
|
||||
outlineMap = outlineMapRef.get();
|
||||
if (outlineMap != null) {
|
||||
gp = (GeneralPath)outlineMap.get(glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (gp == null) {
|
||||
gp = fileFont.getGlyphOutline(pScalerContext, glyphCode, 0, 0);
|
||||
if (outlineMap == null) {
|
||||
outlineMap = new ConcurrentHashMap<Integer, GeneralPath>();
|
||||
outlineMapRef =
|
||||
new WeakReference
|
||||
<ConcurrentHashMap<Integer,GeneralPath>>(outlineMap);
|
||||
}
|
||||
outlineMap.put(glyphCode, gp);
|
||||
}
|
||||
gp = (GeneralPath)gp.clone(); // mutable!
|
||||
if (x != 0f || y != 0f) {
|
||||
gp.transform(AffineTransform.getTranslateInstance(x, y));
|
||||
}
|
||||
return gp;
|
||||
}
|
||||
|
||||
GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
|
||||
return fileFont.getGlyphVectorOutline(pScalerContext,
|
||||
glyphs, glyphs.length, x, y);
|
||||
}
|
||||
|
||||
protected void adjustPoint(Point2D.Float pt) {
|
||||
if (invertDevTx != null) {
|
||||
invertDevTx.deltaTransform(pt, pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
570
jdkSrc/jdk8/sun/font/Font2D.java
Normal file
570
jdkSrc/jdk8/sun/font/Font2D.java
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class Font2D {
|
||||
|
||||
/* Note: JRE and FONT_CONFIG ranks are identical. I don't know of a reason
|
||||
* to distingish these. Possibly if a user adds fonts to the JRE font
|
||||
* directory that are the same font as the ones specified in the font
|
||||
* configuration but that is more likely to be the legitimate intention
|
||||
* than a problem. One reason why these should be the same is that on
|
||||
* Linux the JRE fonts ARE the font configuration fonts, and although I
|
||||
* believe all are assigned FONT_CONFIG rank, it is conceivable that if
|
||||
* this were not so, that some JRE font would not be allowed to joint the
|
||||
* family of its siblings which were assigned FONT_CONFIG rank. Giving
|
||||
* them the same rank is the easy solution for now at least.
|
||||
*/
|
||||
public static final int FONT_CONFIG_RANK = 2;
|
||||
public static final int JRE_RANK = 2;
|
||||
public static final int TTF_RANK = 3;
|
||||
public static final int TYPE1_RANK = 4;
|
||||
public static final int NATIVE_RANK = 5;
|
||||
public static final int UNKNOWN_RANK = 6;
|
||||
public static final int DEFAULT_RANK = 4;
|
||||
|
||||
private static final String[] boldNames = {
|
||||
"bold", "demibold", "demi-bold", "demi bold", "negreta", "demi", };
|
||||
|
||||
private static final String[] italicNames = {
|
||||
"italic", "cursiva", "oblique", "inclined", };
|
||||
|
||||
private static final String[] boldItalicNames = {
|
||||
"bolditalic", "bold-italic", "bold italic",
|
||||
"boldoblique", "bold-oblique", "bold oblique",
|
||||
"demibold italic", "negreta cursiva","demi oblique", };
|
||||
|
||||
private static final FontRenderContext DEFAULT_FRC =
|
||||
new FontRenderContext(null, false, false);
|
||||
|
||||
public Font2DHandle handle;
|
||||
protected String familyName; /* Family font name (english) */
|
||||
protected String fullName; /* Full font name (english) */
|
||||
protected int style = Font.PLAIN;
|
||||
protected FontFamily family;
|
||||
protected int fontRank = DEFAULT_RANK;
|
||||
|
||||
/*
|
||||
* A mapper can be independent of the strike.
|
||||
* Perhaps the reference to the mapper ought to be held on the
|
||||
* scaler, as it may be implemented via scaler functionality anyway
|
||||
* and so the mapper would be useless if its native portion was
|
||||
* freed when the scaler was GC'd.
|
||||
*/
|
||||
protected CharToGlyphMapper mapper;
|
||||
|
||||
/*
|
||||
* The strike cache is maintained per "Font2D" as that is the
|
||||
* principal object by which you look up fonts.
|
||||
* It means more Hashmaps, but look ups can be quicker because
|
||||
* the map will have fewer entries, and there's no need to try to
|
||||
* make the Font2D part of the key.
|
||||
*/
|
||||
protected ConcurrentHashMap<FontStrikeDesc, Reference>
|
||||
strikeCache = new ConcurrentHashMap<FontStrikeDesc, Reference>();
|
||||
|
||||
/* Store the last Strike in a Reference object.
|
||||
* Similarly to the strike that was stored on a C++ font object,
|
||||
* this is an optimisation which helps if multiple clients (ie
|
||||
* typically SunGraphics2D instances) are using the same font, then
|
||||
* as may be typical of many UIs, they are probably using it in the
|
||||
* same style, so it can be a win to first quickly check if the last
|
||||
* strike obtained from this Font2D satifies the needs of the next
|
||||
* client too.
|
||||
* This pre-supposes that a FontStrike is a shareable object, which
|
||||
* it should.
|
||||
*/
|
||||
protected Reference<FontStrike> lastFontStrike = new WeakReference<>(null);
|
||||
|
||||
/*
|
||||
* if useWeak is true, proactively clear the cache after this
|
||||
* many strikes are present. 0 means leave it alone.
|
||||
*/
|
||||
private int strikeCacheMax = 0;
|
||||
/*
|
||||
* Whether to use weak refs for this font, even if soft refs is the default.
|
||||
*/
|
||||
private boolean useWeak;
|
||||
|
||||
void setUseWeakRefs(boolean weak, int maxStrikes) {
|
||||
this.useWeak = weak;
|
||||
this.strikeCacheMax = weak && maxStrikes > 0 ? maxStrikes : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* POSSIBLE OPTIMISATION:
|
||||
* Array of length 1024 elements of 64 bits indicating if a font
|
||||
* contains these. This kind of information can be shared between
|
||||
* all point sizes.
|
||||
* if corresponding bit in knownBitmaskMap is set then canDisplayBitmaskMap
|
||||
* is valid. This is 16Kbytes of data per composite font style.
|
||||
* What about UTF-32 and surrogates?
|
||||
* REMIND: This is too much storage. Probably can only cache this
|
||||
* information for latin range, although possibly OK to store all
|
||||
* for just the "logical" fonts.
|
||||
* Or instead store arrays of subranges of 1024 bits (128 bytes) in
|
||||
* the range below surrogate pairs.
|
||||
*/
|
||||
// protected long[] knownBitmaskMap;
|
||||
// protected long[] canDisplayBitmaskMap;
|
||||
|
||||
/* Returns the "real" style of this Font2D. Eg the font face
|
||||
* Lucida Sans Bold" has a real style of Font.BOLD, even though
|
||||
* it may be able to used to simulate bold italic
|
||||
*/
|
||||
public int getStyle() {
|
||||
return style;
|
||||
}
|
||||
protected void setStyle() {
|
||||
|
||||
String fName = fullName.toLowerCase();
|
||||
|
||||
for (int i=0; i < boldItalicNames.length; i++) {
|
||||
if (fName.indexOf(boldItalicNames[i]) != -1) {
|
||||
style = Font.BOLD|Font.ITALIC;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i < italicNames.length; i++) {
|
||||
if (fName.indexOf(italicNames[i]) != -1) {
|
||||
style = Font.ITALIC;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i < boldNames.length; i++) {
|
||||
if (fName.indexOf(boldNames[i]) != -1 ) {
|
||||
style = Font.BOLD;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final int FWIDTH_NORMAL = 5; // OS/2 usWidthClass
|
||||
public static final int FWEIGHT_NORMAL = 400; // OS/2 usWeightClass
|
||||
public static final int FWEIGHT_BOLD = 700; // OS/2 usWeightClass
|
||||
|
||||
public int getWidth() {
|
||||
return FWIDTH_NORMAL;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
if ((style & Font.BOLD) !=0) {
|
||||
return FWEIGHT_BOLD;
|
||||
} else {
|
||||
return FWEIGHT_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
int getRank() {
|
||||
return fontRank;
|
||||
}
|
||||
|
||||
void setRank(int rank) {
|
||||
fontRank = rank;
|
||||
}
|
||||
|
||||
abstract CharToGlyphMapper getMapper();
|
||||
|
||||
|
||||
|
||||
/* This isn't very efficient but its infrequently used.
|
||||
* StandardGlyphVector uses it when the client assigns the glyph codes.
|
||||
* These may not be valid. This validates them substituting the missing
|
||||
* glyph elsewhere.
|
||||
*/
|
||||
protected int getValidatedGlyphCode(int glyphCode) {
|
||||
if (glyphCode < 0 || glyphCode >= getMapper().getNumGlyphs()) {
|
||||
glyphCode = getMapper().getMissingGlyphCode();
|
||||
}
|
||||
return glyphCode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an appropriate strike for the Font2D subclass
|
||||
*/
|
||||
abstract FontStrike createStrike(FontStrikeDesc desc);
|
||||
|
||||
/* this may be useful for APIs like canDisplay where the answer
|
||||
* is dependent on the font and its scaler, but not the strike.
|
||||
* If no strike has ever been returned, then create a one that matches
|
||||
* this font with the default FRC. It will become the lastStrike and
|
||||
* there's a good chance that the next call will be to get exactly that
|
||||
* strike.
|
||||
*/
|
||||
public FontStrike getStrike(Font font) {
|
||||
FontStrike strike = (FontStrike)lastFontStrike.get();
|
||||
if (strike != null) {
|
||||
return strike;
|
||||
} else {
|
||||
return getStrike(font, DEFAULT_FRC);
|
||||
}
|
||||
}
|
||||
|
||||
/* SunGraphics2D has font, tx, aa and fm. From this info
|
||||
* can get a Strike object from the cache, creating it if necessary.
|
||||
* This code is designed for multi-threaded access.
|
||||
* For that reason it creates a local FontStrikeDesc rather than filling
|
||||
* in a shared one. Up to two AffineTransforms and one FontStrikeDesc will
|
||||
* be created by every lookup. This appears to perform more than
|
||||
* adequately. But it may make sense to expose FontStrikeDesc
|
||||
* as a parameter so a caller can use its own.
|
||||
* In such a case if a FontStrikeDesc is stored as a key then
|
||||
* we would need to use a private copy.
|
||||
*
|
||||
* Note that this code doesn't prevent two threads from creating
|
||||
* two different FontStrike instances and having one of the threads
|
||||
* overwrite the other in the map. This is likely to be a rare
|
||||
* occurrence and the only consequence is that these callers will have
|
||||
* different instances of the strike, and there'd be some duplication of
|
||||
* population of the strikes. However since users of these strikes are
|
||||
* transient, then the one that was overwritten would soon be freed.
|
||||
* If there is any problem then a small synchronized block would be
|
||||
* required with its attendant consequences for MP scaleability.
|
||||
*/
|
||||
public FontStrike getStrike(Font font, AffineTransform devTx,
|
||||
int aa, int fm) {
|
||||
|
||||
/* Create the descriptor which is used to identify a strike
|
||||
* in the strike cache/map. A strike is fully described by
|
||||
* the attributes of this descriptor.
|
||||
*/
|
||||
/* REMIND: generating garbage and doing computation here in order
|
||||
* to include pt size in the tx just for a lookup! Figure out a
|
||||
* better way.
|
||||
*/
|
||||
double ptSize = font.getSize2D();
|
||||
AffineTransform glyphTx = (AffineTransform)devTx.clone();
|
||||
glyphTx.scale(ptSize, ptSize);
|
||||
if (font.isTransformed()) {
|
||||
glyphTx.concatenate(font.getTransform());
|
||||
}
|
||||
if (glyphTx.getTranslateX() != 0 || glyphTx.getTranslateY() != 0) {
|
||||
glyphTx.setTransform(glyphTx.getScaleX(),
|
||||
glyphTx.getShearY(),
|
||||
glyphTx.getShearX(),
|
||||
glyphTx.getScaleY(),
|
||||
0.0, 0.0);
|
||||
}
|
||||
FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
|
||||
font.getStyle(), aa, fm);
|
||||
return getStrike(desc, false);
|
||||
}
|
||||
|
||||
public FontStrike getStrike(Font font, AffineTransform devTx,
|
||||
AffineTransform glyphTx,
|
||||
int aa, int fm) {
|
||||
|
||||
/* Create the descriptor which is used to identify a strike
|
||||
* in the strike cache/map. A strike is fully described by
|
||||
* the attributes of this descriptor.
|
||||
*/
|
||||
FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
|
||||
font.getStyle(), aa, fm);
|
||||
return getStrike(desc, false);
|
||||
}
|
||||
|
||||
public FontStrike getStrike(Font font, FontRenderContext frc) {
|
||||
|
||||
AffineTransform at = frc.getTransform();
|
||||
double ptSize = font.getSize2D();
|
||||
at.scale(ptSize, ptSize);
|
||||
if (font.isTransformed()) {
|
||||
at.concatenate(font.getTransform());
|
||||
if (at.getTranslateX() != 0 || at.getTranslateY() != 0) {
|
||||
at.setTransform(at.getScaleX(),
|
||||
at.getShearY(),
|
||||
at.getShearX(),
|
||||
at.getScaleY(),
|
||||
0.0, 0.0);
|
||||
}
|
||||
}
|
||||
int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);
|
||||
int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());
|
||||
FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),
|
||||
at, font.getStyle(),
|
||||
aa, fm);
|
||||
return getStrike(desc, false);
|
||||
}
|
||||
|
||||
void updateLastStrikeRef(FontStrike strike) {
|
||||
lastFontStrike.clear();
|
||||
if (useWeak) {
|
||||
lastFontStrike = new WeakReference<>(strike);
|
||||
} else {
|
||||
lastFontStrike = new SoftReference<>(strike);
|
||||
}
|
||||
}
|
||||
|
||||
FontStrike getStrike(FontStrikeDesc desc) {
|
||||
return getStrike(desc, true);
|
||||
}
|
||||
|
||||
private FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
|
||||
/* Before looking in the map, see if the descriptor matches the
|
||||
* last strike returned from this Font2D. This should often be a win
|
||||
* since its common for the same font, in the same size to be
|
||||
* used frequently, for example in many parts of a UI.
|
||||
*
|
||||
* If its not the same then we use the descriptor to locate a
|
||||
* Reference to the strike. If it exists and points to a strike,
|
||||
* then we update the last strike to refer to that and return it.
|
||||
*
|
||||
* If the key isn't in the map, or its reference object has been
|
||||
* collected, then we create a new strike, put it in the map and
|
||||
* set it to be the last strike.
|
||||
*/
|
||||
FontStrike strike = (FontStrike)lastFontStrike.get();
|
||||
if (strike != null && desc.equals(strike.desc)) {
|
||||
return strike;
|
||||
} else {
|
||||
Reference strikeRef = strikeCache.get(desc);
|
||||
if (strikeRef != null) {
|
||||
strike = (FontStrike)strikeRef.get();
|
||||
if (strike != null) {
|
||||
updateLastStrikeRef(strike);
|
||||
StrikeCache.refStrike(strike);
|
||||
return strike;
|
||||
}
|
||||
}
|
||||
/* When we create a new FontStrike instance, we *must*
|
||||
* ask the StrikeCache for a reference. We must then ensure
|
||||
* this reference remains reachable, by storing it in the
|
||||
* Font2D's strikeCache map.
|
||||
* So long as the Reference is there (reachable) then if the
|
||||
* reference is cleared, it will be enqueued for disposal.
|
||||
* If for some reason we explicitly remove this reference, it
|
||||
* must only be done when holding a strong reference to the
|
||||
* referent (the FontStrike), or if the reference is cleared,
|
||||
* then we must explicitly "dispose" of the native resources.
|
||||
* The only place this currently happens is in this same method,
|
||||
* where we find a cleared reference and need to overwrite it
|
||||
* here with a new reference.
|
||||
* Clearing the whilst holding a strong reference, should only
|
||||
* be done if the
|
||||
*/
|
||||
if (copy) {
|
||||
desc = new FontStrikeDesc(desc);
|
||||
}
|
||||
strike = createStrike(desc);
|
||||
//StrikeCache.addStrike();
|
||||
/* If we are creating many strikes on this font which
|
||||
* involve non-quadrant rotations, or more general
|
||||
* transforms which include shears, then force the use
|
||||
* of weak references rather than soft references.
|
||||
* This means that it won't live much beyond the next GC,
|
||||
* which is what we want for what is likely a transient strike.
|
||||
*/
|
||||
int txType = desc.glyphTx.getType();
|
||||
if (useWeak ||
|
||||
txType == AffineTransform.TYPE_GENERAL_TRANSFORM ||
|
||||
(txType & AffineTransform.TYPE_GENERAL_ROTATION) != 0 &&
|
||||
strikeCache.size() > 10) {
|
||||
strikeRef = StrikeCache.getStrikeRef(strike, true);
|
||||
} else {
|
||||
strikeRef = StrikeCache.getStrikeRef(strike, useWeak);
|
||||
}
|
||||
strikeCache.put(desc, strikeRef);
|
||||
updateLastStrikeRef(strike);
|
||||
StrikeCache.refStrike(strike);
|
||||
return strike;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the metrics array must be >= 8. This method will
|
||||
* store the following elements in that array before returning:
|
||||
* metrics[0]: ascent
|
||||
* metrics[1]: descent
|
||||
* metrics[2]: leading
|
||||
* metrics[3]: max advance
|
||||
* metrics[4]: strikethrough offset
|
||||
* metrics[5]: strikethrough thickness
|
||||
* metrics[6]: underline offset
|
||||
* metrics[7]: underline thickness
|
||||
*/
|
||||
public void getFontMetrics(Font font, AffineTransform at,
|
||||
Object aaHint, Object fmHint,
|
||||
float metrics[]) {
|
||||
/* This is called in just one place in Font with "at" == identity.
|
||||
* Perhaps this can be eliminated.
|
||||
*/
|
||||
int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, font.getSize());
|
||||
int fm = FontStrikeDesc.getFMHintIntVal(fmHint);
|
||||
FontStrike strike = getStrike(font, at, aa, fm);
|
||||
StrikeMetrics strikeMetrics = strike.getFontMetrics();
|
||||
metrics[0] = strikeMetrics.getAscent();
|
||||
metrics[1] = strikeMetrics.getDescent();
|
||||
metrics[2] = strikeMetrics.getLeading();
|
||||
metrics[3] = strikeMetrics.getMaxAdvance();
|
||||
|
||||
getStyleMetrics(font.getSize2D(), metrics, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the metrics array must be >= offset+4, and offset must be
|
||||
* >= 0. Typically offset is 4. This method will
|
||||
* store the following elements in that array before returning:
|
||||
* metrics[off+0]: strikethrough offset
|
||||
* metrics[off+1]: strikethrough thickness
|
||||
* metrics[off+2]: underline offset
|
||||
* metrics[off+3]: underline thickness
|
||||
*
|
||||
* Note that this implementation simply returns default values;
|
||||
* subclasses can override this method to provide more accurate values.
|
||||
*/
|
||||
public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
|
||||
metrics[offset] = -metrics[0] / 2.5f;
|
||||
metrics[offset+1] = pointSize / 12;
|
||||
metrics[offset+2] = metrics[offset+1] / 1.5f;
|
||||
metrics[offset+3] = metrics[offset+1];
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the metrics array must be >= 4. This method will
|
||||
* store the following elements in that array before returning:
|
||||
* metrics[0]: ascent
|
||||
* metrics[1]: descent
|
||||
* metrics[2]: leading
|
||||
* metrics[3]: max advance
|
||||
*/
|
||||
public void getFontMetrics(Font font, FontRenderContext frc,
|
||||
float metrics[]) {
|
||||
StrikeMetrics strikeMetrics = getStrike(font, frc).getFontMetrics();
|
||||
metrics[0] = strikeMetrics.getAscent();
|
||||
metrics[1] = strikeMetrics.getDescent();
|
||||
metrics[2] = strikeMetrics.getLeading();
|
||||
metrics[3] = strikeMetrics.getMaxAdvance();
|
||||
}
|
||||
|
||||
/* Currently the layout code calls this. May be better for layout code
|
||||
* to check the font class before attempting to run, rather than needing
|
||||
* to promote this method up from TrueTypeFont
|
||||
*/
|
||||
protected byte[] getTableBytes(int tag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* implemented for fonts backed by an sfnt that has
|
||||
* OpenType or AAT layout tables.
|
||||
*/
|
||||
protected long getLayoutTableCache() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/* for layout code */
|
||||
protected long getUnitsPerEm() {
|
||||
return 2048;
|
||||
}
|
||||
|
||||
boolean supportsEncoding(String encoding) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canDoStyle(int style) {
|
||||
return (style == this.style);
|
||||
}
|
||||
|
||||
/*
|
||||
* All the important subclasses override this which is principally for
|
||||
* the TrueType 'gasp' table.
|
||||
*/
|
||||
public boolean useAAForPtSize(int ptsize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasSupplementaryChars() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The following methods implement public methods on java.awt.Font */
|
||||
public String getPostscriptName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getFontName(Locale l) {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getFamilyName(Locale l) {
|
||||
return familyName;
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
return getMapper().getNumGlyphs();
|
||||
}
|
||||
|
||||
public int charToGlyph(int wchar) {
|
||||
return getMapper().charToGlyph(wchar);
|
||||
}
|
||||
|
||||
public int getMissingGlyphCode() {
|
||||
return getMapper().getMissingGlyphCode();
|
||||
}
|
||||
|
||||
public boolean canDisplay(char c) {
|
||||
return getMapper().canDisplay(c);
|
||||
}
|
||||
|
||||
public boolean canDisplay(int cp) {
|
||||
return getMapper().canDisplay(cp);
|
||||
}
|
||||
|
||||
public byte getBaselineFor(char c) {
|
||||
return Font.ROMAN_BASELINE;
|
||||
}
|
||||
|
||||
public float getItalicAngle(Font font, AffineTransform at,
|
||||
Object aaHint, Object fmHint) {
|
||||
/* hardwire psz=12 as that's typical and AA vs non-AA for 'gasp' mode
|
||||
* isn't important for the caret slope of this rarely used API.
|
||||
*/
|
||||
int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, 12);
|
||||
int fm = FontStrikeDesc.getFMHintIntVal(fmHint);
|
||||
FontStrike strike = getStrike(font, at, aa, fm);
|
||||
StrikeMetrics metrics = strike.getFontMetrics();
|
||||
if (metrics.ascentY == 0 || metrics.ascentX == 0) {
|
||||
return 0f;
|
||||
} else {
|
||||
/* ascent is "up" from the baseline so its typically
|
||||
* a negative value, so we need to compensate
|
||||
*/
|
||||
return metrics.ascentX/-metrics.ascentY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
72
jdkSrc/jdk8/sun/font/Font2DHandle.java
Normal file
72
jdkSrc/jdk8/sun/font/Font2DHandle.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/*
|
||||
* This class is used so that a java.awt.Font does not directly
|
||||
* reference a Font2D object. This introduces occasional minor
|
||||
* de-referencing overhead but increases robustness of the
|
||||
* implementation when "bad fonts" are encountered.
|
||||
* A handle is created by a Font2D constructor and references
|
||||
* the Font2D itself. In the event that the Font2D implementation
|
||||
* determines it the font resource has errors (a bad font file)
|
||||
* it makes its handle point at another "stable" Font2D.
|
||||
* Once all referers no longer have a reference to the Font2D it
|
||||
* may be GC'd and its resources freed.
|
||||
* This does not immediately help in the case that objects are
|
||||
* already using a bad Font2D (ie have already dereferenced the
|
||||
* handle) so there is a window for more problems. However this
|
||||
* is already the case as this is the code which must detect the
|
||||
* problem.
|
||||
* However there is also the possibility of intercepting problems
|
||||
* even when a font2D reference is already directly held. Certain
|
||||
* validation points may check that font2Dhandle.font2D == font2D
|
||||
* If this is not true, then this font2D is not valid. Arguably
|
||||
* this check also just needs to be a de-referencing assignment :
|
||||
* font2D = font2DHandle.font2D.
|
||||
* The net effect of these steps is that very soon after a font
|
||||
* is identified as bad, that references and uses of it will be
|
||||
* eliminated.
|
||||
* In the initial implementation a Font2DHandle is what is held by
|
||||
* - java.awt.Font
|
||||
* - FontManager.initialisedFonts map
|
||||
* Font2D is held by
|
||||
* - FontFamily objects
|
||||
* - FontManager.registeredFonts map
|
||||
* - FontInfo object on a SunGraphics2D
|
||||
*
|
||||
* On discovering a bad font, all but the latter remove references to
|
||||
* the font. See FontManager.deRegisterBadFont(Font2D)
|
||||
*/
|
||||
|
||||
public final class Font2DHandle {
|
||||
|
||||
public Font2D font2D;
|
||||
|
||||
public Font2DHandle(Font2D font) {
|
||||
font2D = font;
|
||||
}
|
||||
}
|
||||
48
jdkSrc/jdk8/sun/font/FontAccess.java
Normal file
48
jdkSrc/jdk8/sun/font/FontAccess.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
public abstract class FontAccess {
|
||||
|
||||
private static FontAccess access;
|
||||
public static synchronized void setFontAccess(FontAccess acc) {
|
||||
if (access != null) {
|
||||
throw new InternalError("Attempt to set FontAccessor twice");
|
||||
}
|
||||
access = acc;
|
||||
}
|
||||
|
||||
public static synchronized FontAccess getFontAccess() {
|
||||
return access;
|
||||
}
|
||||
|
||||
public abstract Font2D getFont2D(Font f);
|
||||
public abstract void setFont2D(Font f, Font2DHandle h);
|
||||
public abstract void setCreatedFont(Font f);
|
||||
public abstract boolean isCreatedFont(Font f);
|
||||
}
|
||||
590
jdkSrc/jdk8/sun/font/FontDesignMetrics.java
Normal file
590
jdkSrc/jdk8/sun/font/FontDesignMetrics.java
Normal file
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.TextLayout;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.java2d.Disposer;
|
||||
import sun.java2d.DisposerRecord;
|
||||
|
||||
/*
|
||||
* This class provides a summary of the glyph measurements for a Font
|
||||
* and a set of hints that guide their display. It provides more metrics
|
||||
* information for the Font than the java.awt.FontMetrics class. There
|
||||
* is also some redundancy with that class.
|
||||
* <p>
|
||||
* The design metrics for a Font are obtained from Font.getDesignMetrics().
|
||||
* The FontDesignMetrics object returned will be independent of the
|
||||
* point size of the Font.
|
||||
* Most users are familiar with the idea of using <i>point size</i> to
|
||||
* specify the size of glyphs in a font. This point size defines a
|
||||
* measurement between the baseline of one line to the baseline of the
|
||||
* following line in a single spaced text document. The point size is
|
||||
* based on <i>typographic points</i>, approximately 1/72 of an inch.
|
||||
* <p>
|
||||
* The Java2D API adopts the convention that one point is equivalent
|
||||
* to one unit in user coordinates. When using a normalized transform
|
||||
* for converting user space coordinates to device space coordinates (see
|
||||
* GraphicsConfiguration.getDefaultTransform() and
|
||||
* GraphicsConfiguration.getNormalizingTransform()), 72 user space units
|
||||
* equal 1 inch in device space. In this case one point is 1/72 of an inch.
|
||||
* <p>
|
||||
* The FontDesignMetrics class expresses font metrics in terms of arbitrary
|
||||
* <i>typographic units</i> (not points) chosen by the font supplier
|
||||
* and used in the underlying platform font representations. These units are
|
||||
* defined by dividing the em-square into a grid. The em-sqaure is the
|
||||
* theoretical square whose dimensions are the full body height of the
|
||||
* font. A typographic unit is the smallest measurable unit in the
|
||||
* em-square. The number of units-per-em is determined by the font
|
||||
* designer. The greater the units-per-em, the greater the precision
|
||||
* in metrics. For example, Type 1 fonts divide the em-square into a
|
||||
* 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 2048
|
||||
* grid. The scale of these units can be obtained by calling
|
||||
* getUnitsPerEm().
|
||||
* <p>
|
||||
* Typographic units are relative -- their absolute size changes as the
|
||||
* size of the of the em-square changes. An em-square is 9 points high
|
||||
* in a 9-point font. Because typographic units are relative to the
|
||||
* em-square, a given location on a glyph will have the same coordinates
|
||||
* in typographic units regardless of the point size.
|
||||
* <p>
|
||||
* Converting typographic units to pixels requires computing pixels-per-em
|
||||
* (ppem). This can be computed as:
|
||||
* <pre>
|
||||
ppem = device_resolution * (inches-per-point) * pointSize
|
||||
* </pre>
|
||||
* where device resolution could be measured in pixels/inch and the point
|
||||
* size of a font is effectively points/em. Using a normalized transform
|
||||
* from user space to device space (see above), results in 1/72 inch/point.
|
||||
* In this case, ppem is equal to the point size on a 72 dpi monitor, so
|
||||
* that an N point font displays N pixels high. In general,
|
||||
* <pre>
|
||||
pixel_units = typographic_units * (ppem / units_per_em)
|
||||
* </pre>
|
||||
* @see java.awt.Font
|
||||
* @see java.awt.GraphicsConfiguration#getDefaultTransform
|
||||
* @see java.awt.GraphicsConfiguration#getNormalizingTransform
|
||||
*/
|
||||
|
||||
public final class FontDesignMetrics extends FontMetrics {
|
||||
|
||||
static final long serialVersionUID = 4480069578560887773L;
|
||||
|
||||
private static final float UNKNOWN_WIDTH = -1;
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
|
||||
// height, ascent, descent, leading are reported to the client
|
||||
// as an integer this value is added to the true fp value to
|
||||
// obtain a value which is usually going to result in a round up
|
||||
// to the next integer except for very marginal cases.
|
||||
private static float roundingUpValue = 0.95f;
|
||||
|
||||
// These fields are all part of the old serialization representation
|
||||
private Font font;
|
||||
private float ascent;
|
||||
private float descent;
|
||||
private float leading;
|
||||
private float maxAdvance;
|
||||
private double[] matrix;
|
||||
private int[] cache; // now unused, still here only for serialization
|
||||
// End legacy serialization fields
|
||||
|
||||
private int serVersion = 0; // If 1 in readObject, these fields are on the input stream:
|
||||
private boolean isAntiAliased;
|
||||
private boolean usesFractionalMetrics;
|
||||
private AffineTransform frcTx;
|
||||
|
||||
private transient float[] advCache; // transient since values could change across runtimes
|
||||
private transient int height = -1;
|
||||
|
||||
private transient FontRenderContext frc;
|
||||
|
||||
private transient double[] devmatrix = null;
|
||||
|
||||
private transient FontStrike fontStrike;
|
||||
|
||||
private static FontRenderContext DEFAULT_FRC = null;
|
||||
|
||||
private static FontRenderContext getDefaultFrc() {
|
||||
|
||||
if (DEFAULT_FRC == null) {
|
||||
AffineTransform tx;
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
tx = new AffineTransform();
|
||||
} else {
|
||||
tx = GraphicsEnvironment
|
||||
.getLocalGraphicsEnvironment()
|
||||
.getDefaultScreenDevice()
|
||||
.getDefaultConfiguration()
|
||||
.getDefaultTransform();
|
||||
}
|
||||
DEFAULT_FRC = new FontRenderContext(tx, false, false);
|
||||
}
|
||||
return DEFAULT_FRC;
|
||||
}
|
||||
|
||||
/* Strongly cache up to 5 most recently requested FontMetrics objects,
|
||||
* and softly cache as many as GC allows. In practice this means we
|
||||
* should keep references around until memory gets low.
|
||||
* We key the cache either by a Font or a combination of the Font and
|
||||
* and FRC. A lot of callers use only the font so although there's code
|
||||
* duplication, we allow just a font to be a key implying a default FRC.
|
||||
* Also we put the references on a queue so that if they do get nulled
|
||||
* out we can clear the keys from the table.
|
||||
*/
|
||||
private static class KeyReference extends SoftReference
|
||||
implements DisposerRecord, Disposer.PollDisposable {
|
||||
|
||||
static ReferenceQueue queue = Disposer.getQueue();
|
||||
|
||||
Object key;
|
||||
|
||||
KeyReference(Object key, Object value) {
|
||||
super(value, queue);
|
||||
this.key = key;
|
||||
Disposer.addReference(this, this);
|
||||
}
|
||||
|
||||
/* It is possible that since this reference object has been
|
||||
* enqueued, that a new metrics has been put into the table
|
||||
* for the same key value. So we'll test to see if the table maps
|
||||
* to THIS reference. If its a new one, we'll leave it alone.
|
||||
* It is possible that a new entry comes in after our test, but
|
||||
* it is unlikely and if this were a problem we would need to
|
||||
* synchronize all 'put' and 'remove' accesses to the cache which
|
||||
* I would prefer not to do.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (metricsCache.get(key) == this) {
|
||||
metricsCache.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MetricsKey {
|
||||
Font font;
|
||||
FontRenderContext frc;
|
||||
int hash;
|
||||
|
||||
MetricsKey() {
|
||||
}
|
||||
|
||||
MetricsKey(Font font, FontRenderContext frc) {
|
||||
init(font, frc);
|
||||
}
|
||||
|
||||
void init(Font font, FontRenderContext frc) {
|
||||
this.font = font;
|
||||
this.frc = frc;
|
||||
this.hash = font.hashCode() + frc.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object key) {
|
||||
if (!(key instanceof MetricsKey)) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
font.equals(((MetricsKey)key).font) &&
|
||||
frc.equals(((MetricsKey)key).frc);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Synchronize access to this on the class */
|
||||
static final MetricsKey key = new MetricsKey();
|
||||
}
|
||||
|
||||
/* All accesses to a CHM do not in general need to be synchronized,
|
||||
* as incomplete operations on another thread would just lead to
|
||||
* harmless cache misses.
|
||||
*/
|
||||
private static final ConcurrentHashMap<Object, KeyReference>
|
||||
metricsCache = new ConcurrentHashMap<Object, KeyReference>();
|
||||
|
||||
private static final int MAXRECENT = 5;
|
||||
private static final FontDesignMetrics[]
|
||||
recentMetrics = new FontDesignMetrics[MAXRECENT];
|
||||
private static int recentIndex = 0;
|
||||
|
||||
public static FontDesignMetrics getMetrics(Font font) {
|
||||
return getMetrics(font, getDefaultFrc());
|
||||
}
|
||||
|
||||
public static FontDesignMetrics getMetrics(Font font,
|
||||
FontRenderContext frc) {
|
||||
|
||||
|
||||
/* When using alternate composites, can't cache based just on
|
||||
* the java.awt.Font. Since this is rarely used and we can still
|
||||
* cache the physical fonts, its not a problem to just return a
|
||||
* new instance in this case.
|
||||
* Note that currently Swing native L&F composites are not handled
|
||||
* by this code as they use the metrics of the physical anyway.
|
||||
*/
|
||||
SunFontManager fm = SunFontManager.getInstance();
|
||||
if (fm.maybeUsingAlternateCompositeFonts() &&
|
||||
FontUtilities.getFont2D(font) instanceof CompositeFont) {
|
||||
return new FontDesignMetrics(font, frc);
|
||||
}
|
||||
|
||||
FontDesignMetrics m = null;
|
||||
KeyReference r;
|
||||
|
||||
/* There are 2 possible keys used to perform lookups in metricsCache.
|
||||
* If the FRC is set to all defaults, we just use the font as the key.
|
||||
* If the FRC is non-default in any way, we construct a hybrid key
|
||||
* that combines the font and FRC.
|
||||
*/
|
||||
boolean usefontkey = frc.equals(getDefaultFrc());
|
||||
|
||||
if (usefontkey) {
|
||||
r = metricsCache.get(font);
|
||||
} else /* use hybrid key */ {
|
||||
// NB synchronization is not needed here because of updates to
|
||||
// the metrics cache but is needed for the shared key.
|
||||
synchronized (MetricsKey.class) {
|
||||
MetricsKey.key.init(font, frc);
|
||||
r = metricsCache.get(MetricsKey.key);
|
||||
}
|
||||
}
|
||||
|
||||
if (r != null) {
|
||||
m = (FontDesignMetrics)r.get();
|
||||
}
|
||||
|
||||
if (m == null) {
|
||||
/* either there was no reference, or it was cleared. Need a new
|
||||
* metrics instance. The key to use in the map is a new
|
||||
* MetricsKey instance when we've determined the FRC is
|
||||
* non-default. Its constructed from local vars so we are
|
||||
* thread-safe - no need to worry about the shared key changing.
|
||||
*/
|
||||
m = new FontDesignMetrics(font, frc);
|
||||
if (usefontkey) {
|
||||
metricsCache.put(font, new KeyReference(font, m));
|
||||
} else /* use hybrid key */ {
|
||||
MetricsKey newKey = new MetricsKey(font, frc);
|
||||
metricsCache.put(newKey, new KeyReference(newKey, m));
|
||||
}
|
||||
}
|
||||
|
||||
/* Here's where we keep the recent metrics */
|
||||
for (int i=0; i<recentMetrics.length; i++) {
|
||||
if (recentMetrics[i]==m) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (recentMetrics) {
|
||||
recentMetrics[recentIndex++] = m;
|
||||
if (recentIndex == MAXRECENT) {
|
||||
recentIndex = 0;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a new FontDesignMetrics object for the given Font.
|
||||
* Its private to enable caching - call getMetrics() instead.
|
||||
* @param font a Font object.
|
||||
*/
|
||||
|
||||
private FontDesignMetrics(Font font) {
|
||||
|
||||
this(font, getDefaultFrc());
|
||||
}
|
||||
|
||||
/* private to enable caching - call getMetrics() instead. */
|
||||
private FontDesignMetrics(Font font, FontRenderContext frc) {
|
||||
super(font);
|
||||
this.font = font;
|
||||
this.frc = frc;
|
||||
|
||||
this.isAntiAliased = frc.isAntiAliased();
|
||||
this.usesFractionalMetrics = frc.usesFractionalMetrics();
|
||||
|
||||
frcTx = frc.getTransform();
|
||||
|
||||
matrix = new double[4];
|
||||
initMatrixAndMetrics();
|
||||
|
||||
initAdvCache();
|
||||
}
|
||||
|
||||
private void initMatrixAndMetrics() {
|
||||
|
||||
Font2D font2D = FontUtilities.getFont2D(font);
|
||||
fontStrike = font2D.getStrike(font, frc);
|
||||
StrikeMetrics metrics = fontStrike.getFontMetrics();
|
||||
this.ascent = metrics.getAscent();
|
||||
this.descent = metrics.getDescent();
|
||||
this.leading = metrics.getLeading();
|
||||
this.maxAdvance = metrics.getMaxAdvance();
|
||||
|
||||
devmatrix = new double[4];
|
||||
frcTx.getMatrix(devmatrix);
|
||||
}
|
||||
|
||||
private void initAdvCache() {
|
||||
advCache = new float[256];
|
||||
// 0 is a valid metric so force it to -1
|
||||
for (int i = 0; i < 256; i++) {
|
||||
advCache[i] = UNKNOWN_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
in.defaultReadObject();
|
||||
if (serVersion != CURRENT_VERSION) {
|
||||
frc = getDefaultFrc();
|
||||
isAntiAliased = frc.isAntiAliased();
|
||||
usesFractionalMetrics = frc.usesFractionalMetrics();
|
||||
frcTx = frc.getTransform();
|
||||
}
|
||||
else {
|
||||
frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics);
|
||||
}
|
||||
|
||||
// when deserialized, members are set to their default values for their type--
|
||||
// not to the values assigned during initialization before the constructor
|
||||
// body!
|
||||
height = -1;
|
||||
|
||||
cache = null;
|
||||
|
||||
initMatrixAndMetrics();
|
||||
initAdvCache();
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
|
||||
cache = new int[256];
|
||||
for (int i=0; i < 256; i++) {
|
||||
cache[i] = -1;
|
||||
}
|
||||
serVersion = CURRENT_VERSION;
|
||||
|
||||
out.defaultWriteObject();
|
||||
|
||||
cache = null;
|
||||
}
|
||||
|
||||
private float handleCharWidth(int ch) {
|
||||
return fontStrike.getCodePointAdvance(ch); // x-component of result only
|
||||
}
|
||||
|
||||
// Uses advCache to get character width
|
||||
// It is incorrect to call this method for ch > 255
|
||||
private float getLatinCharWidth(char ch) {
|
||||
|
||||
float w = advCache[ch];
|
||||
if (w == UNKNOWN_WIDTH) {
|
||||
w = handleCharWidth(ch);
|
||||
advCache[ch] = w;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
/* Override of FontMetrics.getFontRenderContext() */
|
||||
public FontRenderContext getFontRenderContext() {
|
||||
return frc;
|
||||
}
|
||||
|
||||
public int charWidth(char ch) {
|
||||
// default metrics for compatibility with legacy code
|
||||
float w;
|
||||
if (ch < 0x100) {
|
||||
w = getLatinCharWidth(ch);
|
||||
}
|
||||
else {
|
||||
w = handleCharWidth(ch);
|
||||
}
|
||||
return (int)(0.5 + w);
|
||||
}
|
||||
|
||||
public int charWidth(int ch) {
|
||||
if (!Character.isValidCodePoint(ch)) {
|
||||
ch = 0xffff;
|
||||
}
|
||||
|
||||
float w = handleCharWidth(ch);
|
||||
|
||||
return (int)(0.5 + w);
|
||||
}
|
||||
|
||||
public int stringWidth(String str) {
|
||||
|
||||
float width = 0;
|
||||
if (font.hasLayoutAttributes()) {
|
||||
/* TextLayout throws IAE for null, so throw NPE explicitly */
|
||||
if (str == null) {
|
||||
throw new NullPointerException("str is null");
|
||||
}
|
||||
if (str.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
width = new TextLayout(str, font, frc).getAdvance();
|
||||
} else {
|
||||
int length = str.length();
|
||||
for (int i=0; i < length; i++) {
|
||||
char ch = str.charAt(i);
|
||||
if (ch < 0x100) {
|
||||
width += getLatinCharWidth(ch);
|
||||
} else if (FontUtilities.isNonSimpleChar(ch)) {
|
||||
width = new TextLayout(str, font, frc).getAdvance();
|
||||
break;
|
||||
} else {
|
||||
width += handleCharWidth(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int) (0.5 + width);
|
||||
}
|
||||
|
||||
public int charsWidth(char data[], int off, int len) {
|
||||
|
||||
float width = 0;
|
||||
if (font.hasLayoutAttributes()) {
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
String str = new String(data, off, len);
|
||||
width = new TextLayout(str, font, frc).getAdvance();
|
||||
} else {
|
||||
/* Explicit test needed to satisfy superclass spec */
|
||||
if (len < 0) {
|
||||
throw new IndexOutOfBoundsException("len="+len);
|
||||
}
|
||||
int limit = off + len;
|
||||
for (int i=off; i < limit; i++) {
|
||||
char ch = data[i];
|
||||
if (ch < 0x100) {
|
||||
width += getLatinCharWidth(ch);
|
||||
} else if (FontUtilities.isNonSimpleChar(ch)) {
|
||||
String str = new String(data, off, len);
|
||||
width = new TextLayout(str, font, frc).getAdvance();
|
||||
break;
|
||||
} else {
|
||||
width += handleCharWidth(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int) (0.5 + width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the advance widths of the first 256 characters in the
|
||||
* <code>Font</code>. The advance is the
|
||||
* distance from the leftmost point to the rightmost point on the
|
||||
* character's baseline. Note that the advance of a
|
||||
* <code>String</code> is not necessarily the sum of the advances
|
||||
* of its characters.
|
||||
* @return an array storing the advance widths of the
|
||||
* characters in the <code>Font</code>
|
||||
* described by this <code>FontMetrics</code> object.
|
||||
*/
|
||||
// More efficient than base class implementation - reuses existing cache
|
||||
public int[] getWidths() {
|
||||
int[] widths = new int[256];
|
||||
for (char ch = 0 ; ch < 256 ; ch++) {
|
||||
float w = advCache[ch];
|
||||
if (w == UNKNOWN_WIDTH) {
|
||||
w = advCache[ch] = handleCharWidth(ch);
|
||||
}
|
||||
widths[ch] = (int) (0.5 + w);
|
||||
}
|
||||
return widths;
|
||||
}
|
||||
|
||||
public int getMaxAdvance() {
|
||||
return (int)(0.99f + this.maxAdvance);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the typographic ascent of the font. This is the maximum distance
|
||||
* glyphs in this font extend above the base line (measured in typographic
|
||||
* units).
|
||||
*/
|
||||
public int getAscent() {
|
||||
return (int)(roundingUpValue + this.ascent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the typographic descent of the font. This is the maximum distance
|
||||
* glyphs in this font extend below the base line.
|
||||
*/
|
||||
public int getDescent() {
|
||||
return (int)(roundingUpValue + this.descent);
|
||||
}
|
||||
|
||||
public int getLeading() {
|
||||
// nb this ensures the sum of the results of the public methods
|
||||
// for leading, ascent & descent sum to height.
|
||||
// if the calculations in any other methods change this needs
|
||||
// to be changed too.
|
||||
// the 0.95 value used here and in the other methods allows some
|
||||
// tiny fraction of leeway before rouding up. A higher value (0.99)
|
||||
// caused some excessive rounding up.
|
||||
return
|
||||
(int)(roundingUpValue + descent + leading) -
|
||||
(int)(roundingUpValue + descent);
|
||||
}
|
||||
|
||||
// height is calculated as the sum of two separately rounded up values
|
||||
// because typically clients use ascent to determine the y location to
|
||||
// pass to drawString etc and we need to ensure that the height has enough
|
||||
// space below the baseline to fully contain any descender.
|
||||
public int getHeight() {
|
||||
|
||||
if (height < 0) {
|
||||
height = getAscent() + (int)(roundingUpValue + descent + leading);
|
||||
}
|
||||
return height;
|
||||
}
|
||||
}
|
||||
451
jdkSrc/jdk8/sun/font/FontFamily.java
Normal file
451
jdkSrc/jdk8/sun/font/FontFamily.java
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.io.File;
|
||||
import java.awt.Font;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FontFamily {
|
||||
|
||||
private static ConcurrentHashMap<String, FontFamily>
|
||||
familyNameMap = new ConcurrentHashMap<String, FontFamily>();
|
||||
private static HashMap<String, FontFamily> allLocaleNames;
|
||||
|
||||
protected String familyName;
|
||||
protected Font2D plain;
|
||||
protected Font2D bold;
|
||||
protected Font2D italic;
|
||||
protected Font2D bolditalic;
|
||||
protected boolean logicalFont = false;
|
||||
protected int familyRank;
|
||||
|
||||
public static FontFamily getFamily(String name) {
|
||||
return familyNameMap.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public static String[] getAllFamilyNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Only for use by FontManager.deRegisterBadFont(..).
|
||||
* If this was the only font in the family, the family is removed
|
||||
* from the map
|
||||
*/
|
||||
static void remove(Font2D font2D) {
|
||||
|
||||
String name = font2D.getFamilyName(Locale.ENGLISH);
|
||||
FontFamily family = getFamily(name);
|
||||
if (family == null) {
|
||||
return;
|
||||
}
|
||||
if (family.plain == font2D) {
|
||||
family.plain = null;
|
||||
}
|
||||
if (family.bold == font2D) {
|
||||
family.bold = null;
|
||||
}
|
||||
if (family.italic == font2D) {
|
||||
family.italic = null;
|
||||
}
|
||||
if (family.bolditalic == font2D) {
|
||||
family.bolditalic = null;
|
||||
}
|
||||
if (family.plain == null && family.bold == null &&
|
||||
family.plain == null && family.bold == null) {
|
||||
familyNameMap.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public FontFamily(String name, boolean isLogFont, int rank) {
|
||||
logicalFont = isLogFont;
|
||||
familyName = name;
|
||||
familyRank = rank;
|
||||
familyNameMap.put(name.toLowerCase(Locale.ENGLISH), this);
|
||||
}
|
||||
|
||||
/* Create a family for created fonts which aren't listed in the
|
||||
* main map.
|
||||
*/
|
||||
FontFamily(String name) {
|
||||
logicalFont = false;
|
||||
familyName = name;
|
||||
familyRank = Font2D.DEFAULT_RANK;
|
||||
}
|
||||
|
||||
public String getFamilyName() {
|
||||
return familyName;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return familyRank;
|
||||
}
|
||||
|
||||
private boolean isFromSameSource(Font2D font) {
|
||||
if (!(font instanceof FileFont)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileFont existingFont = null;
|
||||
if (plain instanceof FileFont) {
|
||||
existingFont = (FileFont)plain;
|
||||
} else if (bold instanceof FileFont) {
|
||||
existingFont = (FileFont)bold;
|
||||
} else if (italic instanceof FileFont) {
|
||||
existingFont = (FileFont)italic;
|
||||
} else if (bolditalic instanceof FileFont) {
|
||||
existingFont = (FileFont)bolditalic;
|
||||
}
|
||||
// A family isn't created until there's a font.
|
||||
// So if we didn't find a file font it means this
|
||||
// isn't a file-based family.
|
||||
if (existingFont == null) {
|
||||
return false;
|
||||
}
|
||||
File existDir = (new File(existingFont.platName)).getParentFile();
|
||||
|
||||
FileFont newFont = (FileFont)font;
|
||||
File newDir = (new File(newFont.platName)).getParentFile();
|
||||
if (existDir != null) {
|
||||
try {
|
||||
existDir = existDir.getCanonicalFile();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
if (newDir != null) {
|
||||
try {
|
||||
newDir = newDir.getCanonicalFile();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
return java.util.Objects.equals(newDir, existDir);
|
||||
}
|
||||
|
||||
/*
|
||||
* We want a family to be of the same width and prefer medium/normal width.
|
||||
* Once we find a particular width we accept more of the same width
|
||||
* until we find one closer to normal when we 'evict' all existing fonts.
|
||||
* So once we see a 'normal' width font we evict all members that are not
|
||||
* normal width and then accept only new ones that are normal width.
|
||||
*
|
||||
* Once a font passes the width test we subject it to the weight test.
|
||||
* For Plain we target the weight the closest that is <= NORMAL (400)
|
||||
* For Bold we target the weight that is closest to BOLD (700).
|
||||
*
|
||||
* In the future, rather than discarding these fonts, we should
|
||||
* extend the family to include these so lookups on these properties
|
||||
* can locate them, as presently they will only be located by full name
|
||||
* based lookup.
|
||||
*/
|
||||
|
||||
private int familyWidth = 0;
|
||||
private boolean preferredWidth(Font2D font) {
|
||||
|
||||
int newWidth = font.getWidth();
|
||||
|
||||
if (familyWidth == 0) {
|
||||
familyWidth = newWidth;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (newWidth == familyWidth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Math.abs(Font2D.FWIDTH_NORMAL - newWidth) <
|
||||
Math.abs(Font2D.FWIDTH_NORMAL - familyWidth))
|
||||
{
|
||||
if (FontUtilities.debugFonts()) {
|
||||
FontUtilities.getLogger().info(
|
||||
"Found more preferred width. New width = " + newWidth +
|
||||
" Old width = " + familyWidth + " in font " + font +
|
||||
" nulling out fonts plain: " + plain + " bold: " + bold +
|
||||
" italic: " + italic + " bolditalic: " + bolditalic);
|
||||
}
|
||||
familyWidth = newWidth;
|
||||
plain = bold = italic = bolditalic = null;
|
||||
return true;
|
||||
} else if (FontUtilities.debugFonts()) {
|
||||
FontUtilities.getLogger().info(
|
||||
"Family rejecting font " + font +
|
||||
" of less preferred width " + newWidth);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean closerWeight(Font2D currFont, Font2D font, int style) {
|
||||
if (familyWidth != font.getWidth()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currFont == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FontUtilities.debugFonts()) {
|
||||
FontUtilities.getLogger().info(
|
||||
"New weight for style " + style + ". Curr.font=" + currFont +
|
||||
" New font="+font+" Curr.weight="+ + currFont.getWeight()+
|
||||
" New weight="+font.getWeight());
|
||||
}
|
||||
|
||||
int newWeight = font.getWeight();
|
||||
switch (style) {
|
||||
case Font.PLAIN:
|
||||
case Font.ITALIC:
|
||||
return (newWeight <= Font2D.FWEIGHT_NORMAL &&
|
||||
newWeight > currFont.getWeight());
|
||||
|
||||
case Font.BOLD:
|
||||
case Font.BOLD|Font.ITALIC:
|
||||
return (Math.abs(newWeight - Font2D.FWEIGHT_BOLD) <
|
||||
Math.abs(currFont.getWeight() - Font2D.FWEIGHT_BOLD));
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setFont(Font2D font, int style) {
|
||||
|
||||
if (FontUtilities.isLogging()) {
|
||||
String msg;
|
||||
if (font instanceof CompositeFont) {
|
||||
msg = "Request to add " + font.getFamilyName(null) +
|
||||
" with style " + style + " to family " + familyName;
|
||||
} else {
|
||||
msg = "Request to add " + font +
|
||||
" with style " + style + " to family " + this;
|
||||
}
|
||||
FontUtilities.getLogger().info(msg);
|
||||
}
|
||||
/* Allow a lower-rank font only if its a file font
|
||||
* from the exact same source as any previous font.
|
||||
*/
|
||||
if ((font.getRank() > familyRank) && !isFromSameSource(font)) {
|
||||
if (FontUtilities.isLogging()) {
|
||||
FontUtilities.getLogger()
|
||||
.warning("Rejecting adding " + font +
|
||||
" of lower rank " + font.getRank() +
|
||||
" to family " + this +
|
||||
" of rank " + familyRank);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (style) {
|
||||
|
||||
case Font.PLAIN:
|
||||
if (preferredWidth(font) && closerWeight(plain, font, style)) {
|
||||
plain = font;
|
||||
}
|
||||
break;
|
||||
|
||||
case Font.BOLD:
|
||||
if (preferredWidth(font) && closerWeight(bold, font, style)) {
|
||||
bold = font;
|
||||
}
|
||||
break;
|
||||
|
||||
case Font.ITALIC:
|
||||
if (preferredWidth(font) && closerWeight(italic, font, style)) {
|
||||
italic = font;
|
||||
}
|
||||
break;
|
||||
|
||||
case Font.BOLD|Font.ITALIC:
|
||||
if (preferredWidth(font) && closerWeight(bolditalic, font, style)) {
|
||||
bolditalic = font;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Font2D getFontWithExactStyleMatch(int style) {
|
||||
|
||||
switch (style) {
|
||||
|
||||
case Font.PLAIN:
|
||||
return plain;
|
||||
|
||||
case Font.BOLD:
|
||||
return bold;
|
||||
|
||||
case Font.ITALIC:
|
||||
return italic;
|
||||
|
||||
case Font.BOLD|Font.ITALIC:
|
||||
return bolditalic;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* REMIND: if the callers of this method are operating in an
|
||||
* environment in which not all fonts are registered, the returned
|
||||
* font may be a algorithmically styled one, where in fact if loadfonts
|
||||
* were executed, a styled font may be located. Our present "solution"
|
||||
* to this is to register all fonts in a directory and assume that this
|
||||
* registered all the styles of a font, since they would all be in the
|
||||
* same location.
|
||||
*/
|
||||
public Font2D getFont(int style) {
|
||||
|
||||
switch (style) {
|
||||
|
||||
case Font.PLAIN:
|
||||
return plain;
|
||||
|
||||
case Font.BOLD:
|
||||
if (bold != null) {
|
||||
return bold;
|
||||
} else if (plain != null && plain.canDoStyle(style)) {
|
||||
return plain;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
case Font.ITALIC:
|
||||
if (italic != null) {
|
||||
return italic;
|
||||
} else if (plain != null && plain.canDoStyle(style)) {
|
||||
return plain;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
case Font.BOLD|Font.ITALIC:
|
||||
if (bolditalic != null) {
|
||||
return bolditalic;
|
||||
} else if (bold != null && bold.canDoStyle(style)) {
|
||||
return bold;
|
||||
} else if (italic != null && italic.canDoStyle(style)) {
|
||||
return italic;
|
||||
} else if (plain != null && plain.canDoStyle(style)) {
|
||||
return plain;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only to be called if getFont(style) returns null
|
||||
* This method will only return null if the family is completely empty!
|
||||
* Note that it assumes the font of the style you need isn't in the
|
||||
* family. The logic here is that if we must substitute something
|
||||
* it might as well be from the same family.
|
||||
*/
|
||||
Font2D getClosestStyle(int style) {
|
||||
|
||||
switch (style) {
|
||||
/* if you ask for a plain font try to return a non-italic one,
|
||||
* then a italic one, finally a bold italic one */
|
||||
case Font.PLAIN:
|
||||
if (bold != null) {
|
||||
return bold;
|
||||
} else if (italic != null) {
|
||||
return italic;
|
||||
} else {
|
||||
return bolditalic;
|
||||
}
|
||||
|
||||
/* if you ask for a bold font try to return a non-italic one,
|
||||
* then a bold italic one, finally an italic one */
|
||||
case Font.BOLD:
|
||||
if (plain != null) {
|
||||
return plain;
|
||||
} else if (bolditalic != null) {
|
||||
return bolditalic;
|
||||
} else {
|
||||
return italic;
|
||||
}
|
||||
|
||||
/* if you ask for a italic font try to return a bold italic one,
|
||||
* then a plain one, finally an bold one */
|
||||
case Font.ITALIC:
|
||||
if (bolditalic != null) {
|
||||
return bolditalic;
|
||||
} else if (plain != null) {
|
||||
return plain;
|
||||
} else {
|
||||
return bold;
|
||||
}
|
||||
|
||||
case Font.BOLD|Font.ITALIC:
|
||||
if (italic != null) {
|
||||
return italic;
|
||||
} else if (bold != null) {
|
||||
return bold;
|
||||
} else {
|
||||
return plain;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Font may have localized names. Store these in a separate map, so
|
||||
* that only clients who use these names need be affected.
|
||||
*/
|
||||
static synchronized void addLocaleNames(FontFamily family, String[] names){
|
||||
if (allLocaleNames == null) {
|
||||
allLocaleNames = new HashMap<String, FontFamily>();
|
||||
}
|
||||
for (int i=0; i<names.length; i++) {
|
||||
allLocaleNames.put(names[i].toLowerCase(), family);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized FontFamily getLocaleFamily(String name) {
|
||||
if (allLocaleNames == null) {
|
||||
return null;
|
||||
}
|
||||
return allLocaleNames.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
public static FontFamily[] getAllFontFamilies() {
|
||||
Collection<FontFamily> families = familyNameMap.values();
|
||||
return families.toArray(new FontFamily[0]);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return
|
||||
"Font family: " + familyName +
|
||||
" plain="+plain+
|
||||
" bold=" + bold +
|
||||
" italic=" + italic +
|
||||
" bolditalic=" + bolditalic;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
119
jdkSrc/jdk8/sun/font/FontLineMetrics.java
Normal file
119
jdkSrc/jdk8/sun/font/FontLineMetrics.java
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2003, All Rights Reserved
|
||||
*
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.LineMetrics;
|
||||
|
||||
/**
|
||||
* Metrics from a font for layout of characters along a line
|
||||
* and layout of set of lines.
|
||||
* This and CoreMetrics replace what was previously a private internal class of Font
|
||||
*/
|
||||
public final class FontLineMetrics extends LineMetrics implements Cloneable {
|
||||
public int numchars; // mutated by Font
|
||||
public final CoreMetrics cm;
|
||||
public final FontRenderContext frc;
|
||||
|
||||
public FontLineMetrics(int numchars, CoreMetrics cm, FontRenderContext frc) {
|
||||
this.numchars = numchars;
|
||||
this.cm = cm;
|
||||
this.frc = frc;
|
||||
}
|
||||
|
||||
public final int getNumChars() {
|
||||
return numchars;
|
||||
}
|
||||
|
||||
public final float getAscent() {
|
||||
return cm.ascent;
|
||||
}
|
||||
|
||||
public final float getDescent() {
|
||||
return cm.descent;
|
||||
}
|
||||
|
||||
public final float getLeading() {
|
||||
return cm.leading;
|
||||
}
|
||||
|
||||
public final float getHeight() {
|
||||
return cm.height;
|
||||
}
|
||||
|
||||
public final int getBaselineIndex() {
|
||||
return cm.baselineIndex;
|
||||
}
|
||||
|
||||
public final float[] getBaselineOffsets() {
|
||||
return (float[])cm.baselineOffsets.clone();
|
||||
}
|
||||
|
||||
public final float getStrikethroughOffset() {
|
||||
return cm.strikethroughOffset;
|
||||
}
|
||||
|
||||
public final float getStrikethroughThickness() {
|
||||
return cm.strikethroughThickness;
|
||||
}
|
||||
|
||||
public final float getUnderlineOffset() {
|
||||
return cm.underlineOffset;
|
||||
}
|
||||
|
||||
public final float getUnderlineThickness() {
|
||||
return cm.underlineThickness;
|
||||
}
|
||||
|
||||
public final int hashCode() {
|
||||
return cm.hashCode();
|
||||
}
|
||||
|
||||
public final boolean equals(Object rhs) {
|
||||
try {
|
||||
return cm.equals(((FontLineMetrics)rhs).cm);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public final Object clone() {
|
||||
// frc, cm do not need deep clone
|
||||
try {
|
||||
return super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
jdkSrc/jdk8/sun/font/FontManager.java
Normal file
146
jdkSrc/jdk8/sun/font/FontManager.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontFormatException;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
|
||||
|
||||
/**
|
||||
* Interface between Java Fonts (java.awt.Font) and the underlying
|
||||
* font files/native font resources and the Java and native font scalers.
|
||||
*/
|
||||
public interface FontManager {
|
||||
|
||||
// These constants are used in findFont().
|
||||
public static final int NO_FALLBACK = 0;
|
||||
public static final int PHYSICAL_FALLBACK = 1;
|
||||
public static final int LOGICAL_FALLBACK = 2;
|
||||
|
||||
/**
|
||||
* Register a new font. Please, note that {@code null} is not a valid
|
||||
* argument, and it's caller's responsibility to ensure that, but to keep
|
||||
* compatibility, if {@code null} is passed as an argument, {@code false}
|
||||
* is returned, and no {@link NullPointerException}
|
||||
* is thrown.
|
||||
*
|
||||
* As additional note, an implementation should ensure that this font
|
||||
* cannot override existing installed fonts.
|
||||
*
|
||||
* @param font
|
||||
* @return {@code true} is the font is successfully registered,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean registerFont(Font font);
|
||||
|
||||
public void deRegisterBadFont(Font2D font2D);
|
||||
|
||||
/**
|
||||
* The client supplies a name and a style.
|
||||
* The name could be a family name, or a full name.
|
||||
* A font may exist with the specified style, or it may
|
||||
* exist only in some other style. For non-native fonts the scaler
|
||||
* may be able to emulate the required style.
|
||||
*/
|
||||
public Font2D findFont2D(String name, int style, int fallback);
|
||||
|
||||
/**
|
||||
* Creates a Font2D for the specified font file, that is expected
|
||||
* to be in the specified font format (according to the constants
|
||||
* in java.awt.Font). The parameter {@code isCopy} is set to true
|
||||
* when the specified font file is actually a copy of the font data
|
||||
* and needs to be deleted afterwards. This method is called
|
||||
* for the Font.createFont() methods.
|
||||
*
|
||||
* @param fontFile the file holding the font data
|
||||
* @param fontFormat the expected font format
|
||||
* @param isCopy {@code true} if the file is a copy and needs to be
|
||||
* deleted, {@code false} otherwise
|
||||
*
|
||||
* @return the created Font2D instance
|
||||
*/
|
||||
public Font2D createFont2D(File fontFile, int fontFormat,
|
||||
boolean isCopy, CreatedFontTracker tracker)
|
||||
throws FontFormatException;
|
||||
|
||||
/**
|
||||
* If usingPerAppContextComposites is true, we are in "applet"
|
||||
* (eg browser) environment and at least one context has selected
|
||||
* an alternate composite font behaviour.
|
||||
*/
|
||||
public boolean usingPerAppContextComposites();
|
||||
|
||||
/**
|
||||
* Creates a derived composite font from the specified font (handle).
|
||||
*
|
||||
* @param family the font family of the derived font
|
||||
* @param style the font style of the derived font
|
||||
* @param handle the original font (handle)
|
||||
*
|
||||
* @return the handle for the derived font
|
||||
*/
|
||||
public Font2DHandle getNewComposite(String family, int style,
|
||||
Font2DHandle handle);
|
||||
|
||||
/**
|
||||
* Indicates a preference for locale-specific fonts in the mapping of
|
||||
* logical fonts to physical fonts. Calling this method indicates that font
|
||||
* rendering should primarily use fonts specific to the primary writing
|
||||
* system (the one indicated by the default encoding and the initial
|
||||
* default locale). For example, if the primary writing system is
|
||||
* Japanese, then characters should be rendered using a Japanese font
|
||||
* if possible, and other fonts should only be used for characters for
|
||||
* which the Japanese font doesn't have glyphs.
|
||||
* <p>
|
||||
* The actual change in font rendering behavior resulting from a call
|
||||
* to this method is implementation dependent; it may have no effect at
|
||||
* all, or the requested behavior may already match the default behavior.
|
||||
* The behavior may differ between font rendering in lightweight
|
||||
* and peered components. Since calling this method requests a
|
||||
* different font, clients should expect different metrics, and may need
|
||||
* to recalculate window sizes and layout. Therefore this method should
|
||||
* be called before user interface initialisation.
|
||||
*
|
||||
* @see #preferProportionalFonts()
|
||||
* @since 1.5
|
||||
*/
|
||||
public void preferLocaleFonts();
|
||||
|
||||
/**
|
||||
* preferLocaleFonts() and preferProportionalFonts() are called to inform
|
||||
* that the application could be using an alternate set of composite
|
||||
* fonts, and so the implementation should try to create a CompositeFonts
|
||||
* with this directive in mind.
|
||||
*
|
||||
* @see #preferLocaleFonts()
|
||||
*/
|
||||
public void preferProportionalFonts();
|
||||
|
||||
}
|
||||
96
jdkSrc/jdk8/sun/font/FontManagerFactory.java
Normal file
96
jdkSrc/jdk8/sun/font/FontManagerFactory.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.AWTError;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Toolkit;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
|
||||
/**
|
||||
* Factory class used to retrieve a valid FontManager instance for the current
|
||||
* platform.
|
||||
*
|
||||
* A default implementation is given for Linux, Solaris and Windows.
|
||||
* You can alter the behaviour of the {@link #getInstance()} method by setting
|
||||
* the {@code sun.font.fontmanager} property. For example:
|
||||
* {@code sun.font.fontmanager=sun.awt.X11FontManager}
|
||||
*/
|
||||
public final class FontManagerFactory {
|
||||
|
||||
/** Our singleton instance. */
|
||||
private static FontManager instance = null;
|
||||
|
||||
private static final String DEFAULT_CLASS;
|
||||
static {
|
||||
if (FontUtilities.isWindows) {
|
||||
DEFAULT_CLASS = "sun.awt.Win32FontManager";
|
||||
} else if (FontUtilities.isMacOSX) {
|
||||
DEFAULT_CLASS = "sun.font.CFontManager";
|
||||
} else {
|
||||
DEFAULT_CLASS = "sun.awt.X11FontManager";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a valid FontManager implementation for the current platform.
|
||||
*
|
||||
* @return a valid FontManager instance for the current platform
|
||||
*/
|
||||
public static synchronized FontManager getInstance() {
|
||||
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
AccessController.doPrivileged(new PrivilegedAction() {
|
||||
|
||||
public Object run() {
|
||||
try {
|
||||
String fmClassName =
|
||||
System.getProperty("sun.font.fontmanager",
|
||||
DEFAULT_CLASS);
|
||||
ClassLoader cl = ClassLoader.getSystemClassLoader();
|
||||
Class fmClass = Class.forName(fmClassName, true, cl);
|
||||
instance = (FontManager) fmClass.newInstance();
|
||||
} catch (ClassNotFoundException |
|
||||
InstantiationException |
|
||||
IllegalAccessException ex) {
|
||||
throw new InternalError(ex);
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
64
jdkSrc/jdk8/sun/font/FontManagerForSGE.java
Normal file
64
jdkSrc/jdk8/sun/font/FontManagerForSGE.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* This is an extension of the {@link FontManager} interface which has to
|
||||
* be implemented on systems that want to use SunGraphicsEnvironment. It
|
||||
* adds a couple of methods that are only required by SGE. Graphics
|
||||
* implementations that use their own GraphicsEnvironment are not required
|
||||
* to implement this and can use plain FontManager instead.
|
||||
*/
|
||||
public interface FontManagerForSGE extends FontManager {
|
||||
|
||||
/**
|
||||
* Return an array of created Fonts, or null, if no fonts were created yet.
|
||||
*/
|
||||
public Font[] getCreatedFonts();
|
||||
|
||||
/**
|
||||
* Similar to getCreatedFonts, but returns a TreeMap of fonts by family name.
|
||||
*/
|
||||
public TreeMap<String, String> getCreatedFontFamilyNames();
|
||||
|
||||
/**
|
||||
* Returns all fonts installed in this environment.
|
||||
*/
|
||||
public Font[] getAllInstalledFonts();
|
||||
|
||||
public String[] getInstalledFontFamilyNames(Locale requestedLocale);
|
||||
|
||||
/* Modifies the behaviour of a subsequent call to preferLocaleFonts()
|
||||
* to use Mincho instead of Gothic for dialoginput in JA locales
|
||||
* on windows. Not needed on other platforms.
|
||||
*/
|
||||
public void useAlternateFontforJALocales();
|
||||
|
||||
}
|
||||
77
jdkSrc/jdk8/sun/font/FontManagerNativeLibrary.java
Normal file
77
jdkSrc/jdk8/sun/font/FontManagerNativeLibrary.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import sun.java2d.SunGraphicsEnvironment;
|
||||
|
||||
public class FontManagerNativeLibrary {
|
||||
static {
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction() {
|
||||
public Object run() {
|
||||
/* REMIND do we really have to load awt here? */
|
||||
System.loadLibrary("awt");
|
||||
if (FontUtilities.isOpenJDK &&
|
||||
System.getProperty("os.name").startsWith("Windows")) {
|
||||
/* Ideally fontmanager library should not depend on
|
||||
particular implementation of the font scaler.
|
||||
However, freetype scaler is basically small wrapper on
|
||||
top of freetype library (that is used in binary form).
|
||||
|
||||
This wrapper is compiled into fontmanager and this make
|
||||
fontmanger library depending on freetype library.
|
||||
|
||||
On Windows DLL's in the JRE's BIN directory cannot be
|
||||
found by windows DLL loading as that directory is not
|
||||
on the Windows PATH.
|
||||
|
||||
To avoid link error we have to load freetype explicitly
|
||||
before we load fontmanager.
|
||||
|
||||
Note that we do not need to do this for T2K because
|
||||
fontmanager.dll does not depend on t2k.dll.
|
||||
|
||||
NB: consider moving freetype wrapper part to separate
|
||||
shared library in order to avoid dependency. */
|
||||
System.loadLibrary("freetype");
|
||||
}
|
||||
System.loadLibrary("fontmanager");
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this method to ensure libraries are loaded.
|
||||
*
|
||||
* Method acts as trigger to ensure this class is loaded
|
||||
* (and therefore initializer code is executed).
|
||||
* Actual loading is performed by static initializer.
|
||||
* (no need to execute doPrivilledged block more than once)
|
||||
*/
|
||||
public static void load() {}
|
||||
}
|
||||
246
jdkSrc/jdk8/sun/font/FontResolver.java
Normal file
246
jdkSrc/jdk8/sun/font/FontResolver.java
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 1999, All rights reserved.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import sun.text.CodePointIterator;
|
||||
|
||||
/**
|
||||
* This class maps an individual character to a Font family which can
|
||||
* display it. The character-to-Font mapping does not depend on the
|
||||
* character's context, so a particular character will be mapped to the
|
||||
* same font family each time.
|
||||
* <p>
|
||||
* Typically, clients will call getIndexFor(char) for each character
|
||||
* in a style run. When getIndexFor() returns a different value from
|
||||
* ones seen previously, the characters up to that point will be assigned
|
||||
* a font obtained from getFont().
|
||||
*/
|
||||
public final class FontResolver {
|
||||
|
||||
// An array of all fonts available to the runtime. The fonts
|
||||
// will be searched in order.
|
||||
private Font[] allFonts;
|
||||
private Font[] supplementaryFonts;
|
||||
private int[] supplementaryIndices;
|
||||
|
||||
// Default size of Fonts (if created from an empty Map, for instance).
|
||||
private static final int DEFAULT_SIZE = 12; // from Font
|
||||
|
||||
private Font defaultFont = new Font(Font.DIALOG, Font.PLAIN, DEFAULT_SIZE);
|
||||
|
||||
// The results of previous lookups are cached in a two-level
|
||||
// table. The value for a character c is found in:
|
||||
// blocks[c>>SHIFT][c&MASK]
|
||||
// although the second array is only allocated when needed.
|
||||
// A 0 value means the character's font has not been looked up.
|
||||
// A positive value means the character's font is in the allFonts
|
||||
// array at index (value-1).
|
||||
private static final int SHIFT = 9;
|
||||
private static final int BLOCKSIZE = 1<<(16-SHIFT);
|
||||
private static final int MASK = BLOCKSIZE-1;
|
||||
private int[][] blocks = new int[1<<SHIFT][];
|
||||
|
||||
private FontResolver() {
|
||||
}
|
||||
|
||||
private Font[] getAllFonts() {
|
||||
if (allFonts == null) {
|
||||
allFonts =
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
for (int i=0; i < allFonts.length; i++) {
|
||||
allFonts[i] = allFonts[i].deriveFont((float)DEFAULT_SIZE);
|
||||
}
|
||||
}
|
||||
return allFonts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search fonts in order, and return "1" to indicate its in the default
|
||||
* font, (or not found at all), or the index of the first font
|
||||
* which can display the given character, plus 2, if it is not
|
||||
* in the default font.
|
||||
*/
|
||||
private int getIndexFor(char c) {
|
||||
|
||||
if (defaultFont.canDisplay(c)) {
|
||||
return 1;
|
||||
}
|
||||
for (int i=0; i < getAllFonts().length; i++) {
|
||||
if (allFonts[i].canDisplay(c)) {
|
||||
return i+2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private Font [] getAllSCFonts() {
|
||||
|
||||
if (supplementaryFonts == null) {
|
||||
ArrayList<Font> fonts = new ArrayList<Font>();
|
||||
ArrayList<Integer> indices = new ArrayList<Integer>();
|
||||
|
||||
for (int i=0; i<getAllFonts().length; i++) {
|
||||
Font font = allFonts[i];
|
||||
Font2D font2D = FontUtilities.getFont2D(font);
|
||||
if (font2D.hasSupplementaryChars()) {
|
||||
fonts.add(font);
|
||||
indices.add(Integer.valueOf(i));
|
||||
}
|
||||
}
|
||||
|
||||
int len = fonts.size();
|
||||
supplementaryIndices = new int[len];
|
||||
for (int i=0; i<len; i++) {
|
||||
supplementaryIndices[i] = indices.get(i);
|
||||
}
|
||||
supplementaryFonts = fonts.toArray(new Font[len]);
|
||||
}
|
||||
return supplementaryFonts;
|
||||
}
|
||||
|
||||
/* This method is called only for character codes >= 0x10000 - which
|
||||
* are assumed to be legal supplementary characters.
|
||||
* It looks first at the default font (to avoid calling getAllFonts if at
|
||||
* all possible) and if that doesn't map the code point, it scans
|
||||
* just the fonts that may contain supplementary characters.
|
||||
* The index that is returned is into the "allFonts" array so that
|
||||
* callers see the same value for both supplementary and base chars.
|
||||
*/
|
||||
private int getIndexFor(int cp) {
|
||||
|
||||
if (defaultFont.canDisplay(cp)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < getAllSCFonts().length; i++) {
|
||||
if (supplementaryFonts[i].canDisplay(cp)) {
|
||||
return supplementaryIndices[i]+2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an index for the given character. The index identifies a
|
||||
* font family to getFont(), and has no other inherent meaning.
|
||||
* @param c the character to map
|
||||
* @return a value for consumption by getFont()
|
||||
* @see #getFont
|
||||
*/
|
||||
public int getFontIndex(char c) {
|
||||
|
||||
int blockIndex = c>>SHIFT;
|
||||
int[] block = blocks[blockIndex];
|
||||
if (block == null) {
|
||||
block = new int[BLOCKSIZE];
|
||||
blocks[blockIndex] = block;
|
||||
}
|
||||
|
||||
int index = c & MASK;
|
||||
if (block[index] == 0) {
|
||||
block[index] = getIndexFor(c);
|
||||
}
|
||||
return block[index];
|
||||
}
|
||||
|
||||
public int getFontIndex(int cp) {
|
||||
if (cp < 0x10000) {
|
||||
return getFontIndex((char)cp);
|
||||
}
|
||||
return getIndexFor(cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the font index for the code point at the current position in the
|
||||
* iterator, then advances the iterator to the first code point that has
|
||||
* a different index or until the iterator is DONE, and returns the font index.
|
||||
* @param iter a code point iterator, this will be advanced past any code
|
||||
* points that have the same font index
|
||||
* @return the font index for the initial code point found, or 1 if the iterator
|
||||
* was empty.
|
||||
*/
|
||||
public int nextFontRunIndex(CodePointIterator iter) {
|
||||
int cp = iter.next();
|
||||
int fontIndex = 1;
|
||||
if (cp != CodePointIterator.DONE) {
|
||||
fontIndex = getFontIndex(cp);
|
||||
|
||||
while ((cp = iter.next()) != CodePointIterator.DONE) {
|
||||
if (getFontIndex(cp) != fontIndex) {
|
||||
iter.prev();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fontIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Font from a given font index with properties
|
||||
* from attributes. The font index, which should have been produced
|
||||
* by getFontIndex(), determines a font family. The size and style
|
||||
* of the Font reflect the properties in attributes. Any Font or
|
||||
* font family specifications in attributes are ignored, on the
|
||||
* assumption that clients have already handled them.
|
||||
* @param index an index from getFontIndex() which determines the
|
||||
* font family
|
||||
* @param attributes a Map from which the size and style of the Font
|
||||
* are determined. The default size is 12 and the default style
|
||||
* is Font.PLAIN
|
||||
* @see #getFontIndex
|
||||
*/
|
||||
public Font getFont(int index, Map attributes) {
|
||||
Font font = defaultFont;
|
||||
|
||||
if (index >= 2) {
|
||||
font = allFonts[index-2];
|
||||
}
|
||||
|
||||
return font.deriveFont(attributes);
|
||||
}
|
||||
|
||||
private static FontResolver INSTANCE;
|
||||
|
||||
/**
|
||||
* Return a shared instance of FontResolver.
|
||||
*/
|
||||
public static FontResolver getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new FontResolver();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
171
jdkSrc/jdk8/sun/font/FontRunIterator.java
Normal file
171
jdkSrc/jdk8/sun/font/FontRunIterator.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2003 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/**
|
||||
* Iterates over runs of fonts in a CompositeFont, optionally taking script runs into account.
|
||||
*/
|
||||
public final class FontRunIterator {
|
||||
CompositeFont font;
|
||||
char[] text;
|
||||
int start;
|
||||
int limit;
|
||||
|
||||
CompositeGlyphMapper mapper; // handy cache
|
||||
|
||||
int slot = -1;
|
||||
int pos;
|
||||
|
||||
public void init(CompositeFont font, char[] text, int start, int limit) {
|
||||
if (font == null || text == null || start < 0 || limit < start || limit > text.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
this.font = font;
|
||||
this.text = text;
|
||||
this.start = start;
|
||||
this.limit = limit;
|
||||
|
||||
this.mapper = (CompositeGlyphMapper)font.getMapper();
|
||||
this.slot = -1;
|
||||
this.pos = start;
|
||||
}
|
||||
|
||||
public PhysicalFont getFont() {
|
||||
return slot == -1 ? null : font.getSlotFont(slot);
|
||||
}
|
||||
|
||||
public int getGlyphMask() {
|
||||
return slot << 24;
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* characters that are in the 'common' script become part of the
|
||||
* surrounding script run. we want to fetch these from the same font
|
||||
* used to get surrounding characters, where possible. but we don't
|
||||
* want to force non-common characters to come from other than their
|
||||
* standard font.
|
||||
*
|
||||
* what we really want to do is this:
|
||||
* 1) fetch a code point from the text.
|
||||
* 2) get its 'native' script code
|
||||
* 3) determine its 'resolved' script code
|
||||
* 4) if its native script is COMMON, and its resolved script is the same as the previous
|
||||
* code point's, then see if the previous font supports this code point. if so, use it.
|
||||
* 5) otherwise resolve the font as usual
|
||||
* 6) break the run when either the physical font or the resolved script changes.
|
||||
*
|
||||
* problems: we optimize latin-1 and cjk text assuming a fixed
|
||||
* width for each character. since latin-1 digits and punctuation
|
||||
* are common, following this algorithm they will change to match
|
||||
* the fonts used for the preceding text, and potentially change metrics.
|
||||
*
|
||||
* this also seems to have the potential for changing arbitrary runs of text, e.g.
|
||||
* any number of digits and spaces can change depending on the preceding (or following!)
|
||||
* non-COMMON character's font assignment. this is not good.
|
||||
*
|
||||
* since the goal is to enable layout to be performed using as few physical fonts as
|
||||
* possible, and the primary cause of switching fonts is to handle spaces, perhaps
|
||||
* we should just special-case spaces and assign them from the current font, whatever
|
||||
* it may be.
|
||||
*
|
||||
* One could also argue that the job of the composite font is to assign physical fonts
|
||||
* to text runs, however it wishes. we don't necessarily have to provide script info
|
||||
* to let it do this. it can determine based on whatever. so having a special 'next'
|
||||
* function that takes script (and limit) is redundant. It can fetch the script again
|
||||
* if need be.
|
||||
*
|
||||
* both this and the script iterator are turning char sequences into code point
|
||||
* sequences. maybe it would be better to feed a single code point into each iterator-- push
|
||||
* the data instead of pull it?
|
||||
*/
|
||||
|
||||
public boolean next(int script, int lim) {
|
||||
if (pos == lim) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int ch = nextCodePoint(lim);
|
||||
int sl = mapper.charToGlyph(ch) & CompositeGlyphMapper.SLOTMASK;
|
||||
slot = sl >>> 24;
|
||||
while ((ch = nextCodePoint(lim)) != DONE && (mapper.charToGlyph(ch) & CompositeGlyphMapper.SLOTMASK) == sl);
|
||||
pushback(ch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean next() {
|
||||
return next(Script.COMMON, limit);
|
||||
}
|
||||
|
||||
static final int SURROGATE_START = 0x10000;
|
||||
static final int LEAD_START = 0xd800;
|
||||
static final int LEAD_LIMIT = 0xdc00;
|
||||
static final int TAIL_START = 0xdc00;
|
||||
static final int TAIL_LIMIT = 0xe000;
|
||||
static final int LEAD_SURROGATE_SHIFT = 10;
|
||||
static final int SURROGATE_OFFSET = SURROGATE_START - (LEAD_START << LEAD_SURROGATE_SHIFT) - TAIL_START;
|
||||
|
||||
static final int DONE = -1;
|
||||
|
||||
final int nextCodePoint() {
|
||||
return nextCodePoint(limit);
|
||||
}
|
||||
|
||||
final int nextCodePoint(int lim) {
|
||||
if (pos >= lim) {
|
||||
return DONE;
|
||||
}
|
||||
int ch = text[pos++];
|
||||
if (ch >= LEAD_START && ch < LEAD_LIMIT && pos < lim) {
|
||||
int nch = text[pos];
|
||||
if (nch >= TAIL_START && nch < TAIL_LIMIT) {
|
||||
++pos;
|
||||
ch = (ch << LEAD_SURROGATE_SHIFT) + nch + SURROGATE_OFFSET;
|
||||
}
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
final void pushback(int ch) {
|
||||
if (ch >= 0) {
|
||||
if (ch >= 0x10000) {
|
||||
pos -= 2;
|
||||
} else {
|
||||
pos -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
262
jdkSrc/jdk8/sun/font/FontScaler.java
Normal file
262
jdkSrc/jdk8/sun/font/FontScaler.java
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import sun.java2d.Disposer;
|
||||
import sun.java2d.DisposerRecord;
|
||||
|
||||
/* FontScaler is "internal interface" to font rasterizer library.
|
||||
*
|
||||
* Access to native rasterizers without going through this interface is
|
||||
* strongly discouraged. In particular, this is important because native
|
||||
* data could be disposed due to runtime font processing error at any time.
|
||||
*
|
||||
* FontScaler represents combination of particular rasterizer implementation
|
||||
* and particular font. It does not include rasterization attributes such as
|
||||
* transform. These attributes are part of native scalerContext object.
|
||||
* This approach allows to share same scaler for different requests related
|
||||
* to the same font file.
|
||||
*
|
||||
* Note that scaler may throw FontScalerException on any operation.
|
||||
* Generally this means that runtime error had happened and scaler is not
|
||||
* usable. Subsequent calls to this scaler should not cause crash but will
|
||||
* likely cause exceptions to be thrown again.
|
||||
*
|
||||
* It is recommended that callee should replace its reference to the scaler
|
||||
* with something else. For instance it could be FontManager.getNullScaler().
|
||||
* Note that NullScaler is trivial and will not actually rasterize anything.
|
||||
*
|
||||
* Alternatively, callee can use more sophisticated error recovery strategies
|
||||
* and for instance try to substitute failed scaler with new scaler instance
|
||||
* using another font.
|
||||
*
|
||||
* Note that in case of error there is no need to call dispose(). Moreover,
|
||||
* dispose() generally is called by Disposer thread and explicit calls to
|
||||
* dispose might have unexpected sideeffects because scaler can be shared.
|
||||
*
|
||||
* Current disposing logic is the following:
|
||||
* - scaler is registered in the Disposer by the FontManager (on creation)
|
||||
* - scalers are disposed when associated Font2D object (e.g. TruetypeFont)
|
||||
* is garbage collected. That's why this object implements DisposerRecord
|
||||
* interface directly (as it is not used as indicator when it is safe
|
||||
* to release native state) and that's why we have to use WeakReference
|
||||
* to Font internally.
|
||||
* - Majority of Font2D objects are linked from various mapping arrays
|
||||
* (e.g. FontManager.localeFullNamesToFont). So, they are not collected.
|
||||
* This logic only works for fonts created with Font.createFont()
|
||||
*
|
||||
* Notes:
|
||||
* - Eventually we may consider releasing some of the scaler resources if
|
||||
* it was not used for a while but we do not want to be too aggressive on
|
||||
* this (and this is probably more important for Type1 fonts).
|
||||
*/
|
||||
public abstract class FontScaler implements DisposerRecord {
|
||||
|
||||
private static FontScaler nullScaler = null;
|
||||
private static Constructor<FontScaler> scalerConstructor = null;
|
||||
|
||||
//Find preferred font scaler
|
||||
//
|
||||
//NB: we can allow property based preferences
|
||||
// (theoretically logic can be font type specific)
|
||||
static {
|
||||
Class scalerClass = null;
|
||||
Class arglst[] = new Class[] {Font2D.class, int.class,
|
||||
boolean.class, int.class};
|
||||
|
||||
try {
|
||||
if (FontUtilities.isOpenJDK) {
|
||||
scalerClass = Class.forName("sun.font.FreetypeFontScaler");
|
||||
} else {
|
||||
scalerClass = Class.forName("sun.font.T2KFontScaler");
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
scalerClass = NullFontScaler.class;
|
||||
}
|
||||
|
||||
//NB: rewrite using factory? constructor is ugly way
|
||||
try {
|
||||
scalerConstructor = scalerClass.getConstructor(arglst);
|
||||
} catch (NoSuchMethodException e) {
|
||||
//should not happen
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the only place to instantiate new FontScaler.
|
||||
* Therefore this is very convinient place to register
|
||||
* scaler with Disposer as well as trigger deregistring bad font
|
||||
* in case when scaler reports this.
|
||||
*/
|
||||
public static FontScaler getScaler(Font2D font,
|
||||
int indexInCollection,
|
||||
boolean supportsCJK,
|
||||
int filesize) {
|
||||
FontScaler scaler = null;
|
||||
|
||||
try {
|
||||
Object args[] = new Object[] {font, indexInCollection,
|
||||
supportsCJK, filesize};
|
||||
scaler = scalerConstructor.newInstance(args);
|
||||
Disposer.addObjectRecord(font, scaler);
|
||||
} catch (Throwable e) {
|
||||
scaler = nullScaler;
|
||||
|
||||
//if we can not instantiate scaler assume bad font
|
||||
//NB: technically it could be also because of internal scaler
|
||||
// error but here we are assuming scaler is ok.
|
||||
FontManager fm = FontManagerFactory.getInstance();
|
||||
fm.deRegisterBadFont(font);
|
||||
}
|
||||
return scaler;
|
||||
}
|
||||
|
||||
/*
|
||||
* At the moment it is harmless to create 2 null scalers so, technically,
|
||||
* syncronized keyword is not needed.
|
||||
*
|
||||
* But it is safer to keep it to avoid subtle problems if we will be adding
|
||||
* checks like whether scaler is null scaler.
|
||||
*/
|
||||
public static synchronized FontScaler getNullScaler() {
|
||||
if (nullScaler == null) {
|
||||
nullScaler = new NullFontScaler();
|
||||
}
|
||||
return nullScaler;
|
||||
}
|
||||
|
||||
protected WeakReference<Font2D> font = null;
|
||||
protected long nativeScaler = 0; //used by decendants
|
||||
//that have native state
|
||||
protected boolean disposed = false;
|
||||
|
||||
abstract StrikeMetrics getFontMetrics(long pScalerContext)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract float getGlyphAdvance(long pScalerContext, int glyphCode)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract void getGlyphMetrics(long pScalerContext, int glyphCode,
|
||||
Point2D.Float metrics)
|
||||
throws FontScalerException;
|
||||
|
||||
/*
|
||||
* Returns pointer to native GlyphInfo object.
|
||||
* Callee is responsible for freeing this memory.
|
||||
*
|
||||
* Note:
|
||||
* currently this method has to return not 0L but pointer to valid
|
||||
* GlyphInfo object. Because Strike and drawing releated logic does
|
||||
* expect that.
|
||||
* In the future we may want to rework this to allow 0L here.
|
||||
*/
|
||||
abstract long getGlyphImage(long pScalerContext, int glyphCode)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract Rectangle2D.Float getGlyphOutlineBounds(long pContext,
|
||||
int glyphCode)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract GeneralPath getGlyphOutline(long pScalerContext, int glyphCode,
|
||||
float x, float y)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs,
|
||||
int numGlyphs, float x, float y)
|
||||
throws FontScalerException;
|
||||
|
||||
/* Used by Java2D disposer to ensure native resources are released.
|
||||
Note: this method does not release any of created
|
||||
scaler context objects! */
|
||||
public void dispose() {}
|
||||
|
||||
/**
|
||||
* Used when the native resources held by the scaler need
|
||||
* to be released before the 2D disposer runs.
|
||||
*/
|
||||
public void disposeScaler() {}
|
||||
|
||||
/* At the moment these 3 methods are needed for Type1 fonts only.
|
||||
* For Truetype fonts we extract required info outside of scaler
|
||||
* on java layer.
|
||||
*/
|
||||
abstract int getNumGlyphs() throws FontScalerException;
|
||||
abstract int getMissingGlyphCode() throws FontScalerException;
|
||||
abstract int getGlyphCode(char charCode) throws FontScalerException;
|
||||
|
||||
/* This method returns table cache used by native layout engine.
|
||||
* This cache is essentially just small collection of
|
||||
* pointers to various truetype tables. See definition of TTLayoutTableCache
|
||||
* in the fontscalerdefs.h for more details.
|
||||
*
|
||||
* Note that tables themselves have same format as defined in the truetype
|
||||
* specification, i.e. font scaler do not need to perform any preprocessing.
|
||||
*
|
||||
* Probably it is better to have API to request pointers to each table
|
||||
* separately instead of requesting pointer to some native structure.
|
||||
* (then there is not need to share its definition by different
|
||||
* implementations of scaler).
|
||||
* However, this means multiple JNI calls and potential impact on performance.
|
||||
*
|
||||
* Note: return value 0 is legal.
|
||||
* This means tables are not available (e.g. type1 font).
|
||||
*/
|
||||
abstract long getLayoutTableCache() throws FontScalerException;
|
||||
|
||||
/* Used by the OpenType engine for mark positioning. */
|
||||
abstract Point2D.Float getGlyphPoint(long pScalerContext,
|
||||
int glyphCode, int ptNumber)
|
||||
throws FontScalerException;
|
||||
|
||||
abstract long getUnitsPerEm();
|
||||
|
||||
/* Returns pointer to native structure describing rasterization attributes.
|
||||
Format of this structure is scaler-specific.
|
||||
|
||||
Callee is responsible for freeing scaler context (using free()).
|
||||
|
||||
Note:
|
||||
Context is tightly associated with strike and it is actually
|
||||
freed when corresponding strike is being released.
|
||||
*/
|
||||
abstract long createScalerContext(double[] matrix,
|
||||
int aa, int fm,
|
||||
float boldness, float italic,
|
||||
boolean disableHinting);
|
||||
|
||||
/* Marks context as invalid because native scaler is invalid.
|
||||
Notes:
|
||||
- pointer itself is still valid and has to be released
|
||||
- if pointer to native scaler was cached it
|
||||
should not be neither disposed nor used.
|
||||
it is very likely it is already disposed by this moment. */
|
||||
abstract void invalidateScalerContext(long ppScalerContext);
|
||||
}
|
||||
36
jdkSrc/jdk8/sun/font/FontScalerException.java
Normal file
36
jdkSrc/jdk8/sun/font/FontScalerException.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
public class FontScalerException extends Exception {
|
||||
public FontScalerException() {
|
||||
super("Font scaler encountered runtime problem.");
|
||||
}
|
||||
|
||||
public FontScalerException(String reason) {
|
||||
super (reason);
|
||||
}
|
||||
}
|
||||
80
jdkSrc/jdk8/sun/font/FontStrike.java
Normal file
80
jdkSrc/jdk8/sun/font/FontStrike.java
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
public abstract class FontStrike {
|
||||
|
||||
|
||||
protected FontStrikeDisposer disposer;
|
||||
protected FontStrikeDesc desc;
|
||||
protected StrikeMetrics strikeMetrics;
|
||||
protected boolean algoStyle = false;
|
||||
protected float boldness = 1f;
|
||||
protected float italic = 0f;
|
||||
/*
|
||||
* lastLookupTime is updated by Font2D.getStrike and can be used to
|
||||
* choose strikes that have not been newly referenced for purging when
|
||||
* memory usage gets too high. Active strikes will never be purged
|
||||
* because purging is via GC of WeakReferences.
|
||||
*/
|
||||
//protected long lastlookupTime/* = System.currentTimeMillis()*/;
|
||||
|
||||
public abstract int getNumGlyphs();
|
||||
|
||||
abstract StrikeMetrics getFontMetrics();
|
||||
|
||||
abstract void getGlyphImagePtrs(int[] glyphCodes, long[] images,int len);
|
||||
|
||||
abstract long getGlyphImagePtr(int glyphcode);
|
||||
|
||||
// pt, result in device space
|
||||
abstract void getGlyphImageBounds(int glyphcode,
|
||||
Point2D.Float pt,
|
||||
Rectangle result);
|
||||
|
||||
abstract Point2D.Float getGlyphMetrics(int glyphcode);
|
||||
|
||||
abstract Point2D.Float getCharMetrics(char ch);
|
||||
|
||||
abstract float getGlyphAdvance(int glyphCode);
|
||||
|
||||
abstract float getCodePointAdvance(int cp);
|
||||
|
||||
abstract Rectangle2D.Float getGlyphOutlineBounds(int glyphCode);
|
||||
|
||||
abstract GeneralPath
|
||||
getGlyphOutline(int glyphCode, float x, float y);
|
||||
|
||||
abstract GeneralPath
|
||||
getGlyphVectorOutline(int[] glyphs, float x, float y);
|
||||
|
||||
|
||||
}
|
||||
266
jdkSrc/jdk8/sun/font/FontStrikeDesc.java
Normal file
266
jdkSrc/jdk8/sun/font/FontStrikeDesc.java
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import static sun.awt.SunHints.*;
|
||||
|
||||
/*
|
||||
* This class encapsulates every thing needed that distinguishes a strike.
|
||||
* It can be used as a key to locate a FontStrike in a Hashmap/cache.
|
||||
* It is not mutatable, but contains mutatable AffineTransform objects,
|
||||
* which for performance reasons it does not keep private copies of.
|
||||
* Therefore code constructing these must pass in transforms it guarantees
|
||||
* not to mutate.
|
||||
*/
|
||||
public class FontStrikeDesc {
|
||||
|
||||
/* Values to use as a mask that is used for faster comparison of
|
||||
* two strikes using just an int equality test.
|
||||
* The ones we don't use are listed here but commented out.
|
||||
* ie style is already built and hint "OFF" values are zero.
|
||||
* Note that this is used as a strike key and the same strike is used
|
||||
* for HRGB and HBGR, so only the orientation needed (H or V) is needed
|
||||
* to construct and distinguish a FontStrikeDesc. The rgb ordering
|
||||
* needed for rendering is stored in the graphics state.
|
||||
*/
|
||||
// static final int STYLE_PLAIN = Font.PLAIN; // 0x0000
|
||||
// static final int STYLE_BOLD = Font.BOLD; // 0x0001
|
||||
// static final int STYLE_ITALIC = Font.ITALIC; // 0x0002
|
||||
// static final int STYLE_BOLDITALIC = Font.BOLD|Font.ITALIC; // 0x0003
|
||||
// static final int AA_OFF = 0x0000;
|
||||
static final int AA_ON = 0x0010;
|
||||
static final int AA_LCD_H = 0x0020;
|
||||
static final int AA_LCD_V = 0x0040;
|
||||
// static final int FRAC_METRICS_OFF = 0x0000;
|
||||
static final int FRAC_METRICS_ON = 0x0100;
|
||||
static final int FRAC_METRICS_SP = 0x0200;
|
||||
|
||||
/* devTx is to get an inverse transform to get user space values
|
||||
* for metrics. Its not used otherwise, as the glyphTx is the important
|
||||
* one. But it does mean that a strike representing a 6pt font and identity
|
||||
* graphics transform is not equal to one for a 12 pt font and 2x scaled
|
||||
* graphics transform. Its likely to be very rare that this causes
|
||||
* duplication.
|
||||
*/
|
||||
AffineTransform devTx;
|
||||
AffineTransform glyphTx; // all of ptSize, Font tx and Graphics tx.
|
||||
int style;
|
||||
int aaHint;
|
||||
int fmHint;
|
||||
private int hashCode;
|
||||
private int valuemask;
|
||||
|
||||
public int hashCode() {
|
||||
/* Can cache hashcode since a strike(desc) is immutable.*/
|
||||
if (hashCode == 0) {
|
||||
hashCode = glyphTx.hashCode() + devTx.hashCode() + valuemask;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
FontStrikeDesc desc = (FontStrikeDesc)obj;
|
||||
return (desc.valuemask == this.valuemask &&
|
||||
desc.glyphTx.equals(this.glyphTx) &&
|
||||
desc.devTx.equals(this.devTx));
|
||||
} catch (Exception e) {
|
||||
/* class cast or NP exceptions should not happen often, if ever,
|
||||
* and I am hoping that this is faster than an instanceof check.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FontStrikeDesc() {
|
||||
// used with init
|
||||
}
|
||||
|
||||
|
||||
/* This maps a public text AA hint value into one of the subset of values
|
||||
* used to index strikes. For the purpose of the strike cache there are
|
||||
* only 4 values : OFF, ON, LCD_HRGB, LCD_VRGB.
|
||||
* Font and ptSize are needed to resolve the 'gasp' table. The ptSize
|
||||
* must therefore include device and font transforms.
|
||||
*/
|
||||
public static int getAAHintIntVal(Object aa, Font2D font2D, int ptSize) {
|
||||
|
||||
if (FontUtilities.isMacOSX14 &&
|
||||
(aa == VALUE_TEXT_ANTIALIAS_OFF ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_DEFAULT ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_ON ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_GASP))
|
||||
{
|
||||
return INTVAL_TEXT_ANTIALIAS_ON;
|
||||
}
|
||||
|
||||
if (aa == VALUE_TEXT_ANTIALIAS_OFF ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_DEFAULT) {
|
||||
return INTVAL_TEXT_ANTIALIAS_OFF;
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_ON) {
|
||||
return INTVAL_TEXT_ANTIALIAS_ON;
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_GASP) {
|
||||
if (font2D.useAAForPtSize(ptSize)) {
|
||||
return INTVAL_TEXT_ANTIALIAS_ON;
|
||||
} else {
|
||||
return INTVAL_TEXT_ANTIALIAS_OFF;
|
||||
}
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
|
||||
return INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_LCD_VRGB ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_LCD_VBGR) {
|
||||
return INTVAL_TEXT_ANTIALIAS_LCD_VRGB;
|
||||
} else {
|
||||
return INTVAL_TEXT_ANTIALIAS_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* This maps a public text AA hint value into one of the subset of values
|
||||
* used to index strikes. For the purpose of the strike cache there are
|
||||
* only 4 values : OFF, ON, LCD_HRGB, LCD_VRGB.
|
||||
* Font and FontRenderContext are needed to resolve the 'gasp' table.
|
||||
* This is similar to the method above, but used by callers which have not
|
||||
* already calculated the glyph device point size.
|
||||
*/
|
||||
public static int getAAHintIntVal(Font2D font2D, Font font,
|
||||
FontRenderContext frc) {
|
||||
Object aa = frc.getAntiAliasingHint();
|
||||
|
||||
if (FontUtilities.isMacOSX14 &&
|
||||
(aa == VALUE_TEXT_ANTIALIAS_OFF ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_DEFAULT ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_ON ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_GASP))
|
||||
{
|
||||
return INTVAL_TEXT_ANTIALIAS_ON;
|
||||
}
|
||||
|
||||
if (aa == VALUE_TEXT_ANTIALIAS_OFF ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_DEFAULT) {
|
||||
return INTVAL_TEXT_ANTIALIAS_OFF;
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_ON) {
|
||||
return INTVAL_TEXT_ANTIALIAS_ON;
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_GASP) {
|
||||
/* FRC.isIdentity() would have been useful */
|
||||
int ptSize;
|
||||
AffineTransform tx = frc.getTransform();
|
||||
if (tx.isIdentity() && !font.isTransformed()) {
|
||||
ptSize = font.getSize();
|
||||
} else {
|
||||
/* one or both transforms is not identity */
|
||||
float size = font.getSize2D();
|
||||
if (tx.isIdentity()) {
|
||||
tx = font.getTransform();
|
||||
tx.scale(size, size);
|
||||
} else {
|
||||
tx.scale(size, size);
|
||||
if (font.isTransformed()) {
|
||||
tx.concatenate(font.getTransform());
|
||||
}
|
||||
}
|
||||
double shearx = tx.getShearX();
|
||||
double scaley = tx.getScaleY();
|
||||
if (shearx != 0) {
|
||||
scaley = Math.sqrt(shearx * shearx + scaley * scaley);
|
||||
}
|
||||
ptSize = (int)(Math.abs(scaley)+0.5);
|
||||
}
|
||||
if (font2D.useAAForPtSize(ptSize)) {
|
||||
return INTVAL_TEXT_ANTIALIAS_ON;
|
||||
} else {
|
||||
return INTVAL_TEXT_ANTIALIAS_OFF;
|
||||
}
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
|
||||
return INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
|
||||
} else if (aa == VALUE_TEXT_ANTIALIAS_LCD_VRGB ||
|
||||
aa == VALUE_TEXT_ANTIALIAS_LCD_VBGR) {
|
||||
return INTVAL_TEXT_ANTIALIAS_LCD_VRGB;
|
||||
} else {
|
||||
return INTVAL_TEXT_ANTIALIAS_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getFMHintIntVal(Object fm) {
|
||||
if (fm == VALUE_FRACTIONALMETRICS_OFF ||
|
||||
fm == VALUE_FRACTIONALMETRICS_DEFAULT) {
|
||||
return INTVAL_FRACTIONALMETRICS_OFF;
|
||||
} else {
|
||||
return INTVAL_FRACTIONALMETRICS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
public FontStrikeDesc(AffineTransform devAt, AffineTransform at,
|
||||
int fStyle, int aa, int fm) {
|
||||
devTx = devAt;
|
||||
glyphTx = at; // not cloning glyphTx. Callers trusted to not mutate it.
|
||||
style = fStyle;
|
||||
aaHint = aa;
|
||||
fmHint = fm;
|
||||
valuemask = fStyle;
|
||||
switch (aa) {
|
||||
case INTVAL_TEXT_ANTIALIAS_OFF :
|
||||
break;
|
||||
case INTVAL_TEXT_ANTIALIAS_ON :
|
||||
valuemask |= AA_ON;
|
||||
break;
|
||||
case INTVAL_TEXT_ANTIALIAS_LCD_HRGB :
|
||||
case INTVAL_TEXT_ANTIALIAS_LCD_HBGR :
|
||||
valuemask |= AA_LCD_H;
|
||||
break;
|
||||
case INTVAL_TEXT_ANTIALIAS_LCD_VRGB :
|
||||
case INTVAL_TEXT_ANTIALIAS_LCD_VBGR :
|
||||
valuemask |= AA_LCD_V;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (fm == INTVAL_FRACTIONALMETRICS_ON) {
|
||||
valuemask |= FRAC_METRICS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
FontStrikeDesc(FontStrikeDesc desc) {
|
||||
devTx = desc.devTx;
|
||||
// Clone the TX in this case as this is called when its known
|
||||
// that "desc" is being re-used by its creator.
|
||||
glyphTx = (AffineTransform)desc.glyphTx.clone();
|
||||
style = desc.style;
|
||||
aaHint = desc.aaHint;
|
||||
fmHint = desc.fmHint;
|
||||
hashCode = desc.hashCode;
|
||||
valuemask = desc.valuemask;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return "FontStrikeDesc: Style="+style+ " AA="+aaHint+ " FM="+fmHint+
|
||||
" devTx="+devTx+ " devTx.FontTx.ptSize="+glyphTx;
|
||||
}
|
||||
}
|
||||
110
jdkSrc/jdk8/sun/font/FontStrikeDisposer.java
Normal file
110
jdkSrc/jdk8/sun/font/FontStrikeDisposer.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import sun.java2d.Disposer;
|
||||
import sun.java2d.DisposerRecord;
|
||||
|
||||
/*
|
||||
* This keeps track of data that needs to be cleaned up once a
|
||||
* strike is freed.
|
||||
* a) The native memory that is the glyph image cache.
|
||||
* b) removing the "desc" key from the strike's map.
|
||||
* This is safe to do because this disposer is invoked only when the
|
||||
* reference object has been cleared, which means the value indexed by
|
||||
* this key is just an empty reference object.
|
||||
* It is possible that a new FontStrike has been created that would
|
||||
* be referenced by the same (equals) key. If it is placed in the map
|
||||
* before this disposer is executed, then we do not want to remove that
|
||||
* object. We should only remove an object where the value is null.
|
||||
* So we first verify that the key still points to a cleared reference.
|
||||
* Updates to the map thus need to be synchronized.
|
||||
*
|
||||
* A WeakHashmap will automatically clean up, but we might maintain a
|
||||
* reference to the "desc" key in the FontStrike (value) which would
|
||||
* prevent the keys from being discarded. And since the strike is the only
|
||||
* place is likely we would maintain such a strong reference, then the map
|
||||
* entries would be removed much more promptly than we need.
|
||||
*/
|
||||
|
||||
class FontStrikeDisposer
|
||||
implements DisposerRecord, Disposer.PollDisposable {
|
||||
|
||||
ConcurrentHashMap<FontStrikeDesc, Reference> strikeCache;
|
||||
FontStrikeDesc desc;
|
||||
long[] longGlyphImages;
|
||||
int [] intGlyphImages;
|
||||
int [][] segIntGlyphImages;
|
||||
long[][] segLongGlyphImages;
|
||||
long pScalerContext = 0L;
|
||||
boolean disposed = false;
|
||||
boolean comp = false;
|
||||
|
||||
public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
|
||||
long pContext, int[] images) {
|
||||
this.strikeCache = font2D.strikeCache;
|
||||
this.desc = desc;
|
||||
this.pScalerContext = pContext;
|
||||
this.intGlyphImages = images;
|
||||
}
|
||||
|
||||
public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
|
||||
long pContext, long[] images) {
|
||||
this.strikeCache = font2D.strikeCache;
|
||||
this.desc = desc;
|
||||
this.pScalerContext = pContext;
|
||||
this.longGlyphImages = images;
|
||||
}
|
||||
|
||||
public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
|
||||
long pContext) {
|
||||
this.strikeCache = font2D.strikeCache;
|
||||
this.desc = desc;
|
||||
this.pScalerContext = pContext;
|
||||
}
|
||||
|
||||
public FontStrikeDisposer(Font2D font2D, FontStrikeDesc desc) {
|
||||
this.strikeCache = font2D.strikeCache;
|
||||
this.desc = desc;
|
||||
this.comp = true;
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
if (!disposed) {
|
||||
Reference<FontStrike> ref = strikeCache.get(desc);
|
||||
if (ref != null) {
|
||||
Object o = ref.get();
|
||||
if (o == null) {
|
||||
strikeCache.remove(desc);
|
||||
}
|
||||
}
|
||||
StrikeCache.disposeStrike(this);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
jdkSrc/jdk8/sun/font/FontSubstitution.java
Normal file
38
jdkSrc/jdk8/sun/font/FontSubstitution.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.font;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Interface that indicates a Font2D that is not a Composite but has the
|
||||
* property that it internally behaves like one, substituting glyphs
|
||||
* from another font at render time.
|
||||
* In this case the Font must provide a way to behave like a regular
|
||||
* composite when that behaviour is not wanted.
|
||||
*/
|
||||
public interface FontSubstitution {
|
||||
public CompositeFont getCompositeFont2D();
|
||||
}
|
||||
536
jdkSrc/jdk8/sun/font/FontUtilities.java
Normal file
536
jdkSrc/jdk8/sun/font/FontUtilities.java
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.security.AccessController;
|
||||
|
||||
import java.security.PrivilegedAction;
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
/**
|
||||
* A collection of utility methods.
|
||||
*/
|
||||
public final class FontUtilities {
|
||||
|
||||
public static boolean isSolaris;
|
||||
|
||||
public static boolean isLinux;
|
||||
|
||||
public static boolean isMacOSX;
|
||||
public static boolean isMacOSX14;
|
||||
|
||||
public static boolean isSolaris8;
|
||||
|
||||
public static boolean isSolaris9;
|
||||
|
||||
public static boolean isOpenSolaris;
|
||||
|
||||
public static boolean useT2K;
|
||||
|
||||
public static boolean isWindows;
|
||||
|
||||
public static boolean isOpenJDK;
|
||||
|
||||
static final String LUCIDA_FILE_NAME = "LucidaSansRegular.ttf";
|
||||
|
||||
private static boolean debugFonts = false;
|
||||
private static PlatformLogger logger = null;
|
||||
private static boolean logging;
|
||||
|
||||
// This static initializer block figures out the OS constants.
|
||||
static {
|
||||
|
||||
AccessController.doPrivileged(new PrivilegedAction () {
|
||||
public Object run() {
|
||||
String osName = System.getProperty("os.name", "unknownOS");
|
||||
isSolaris = osName.startsWith("SunOS");
|
||||
|
||||
isLinux = osName.startsWith("Linux");
|
||||
|
||||
isMacOSX = osName.contains("OS X"); // TODO: MacOSX
|
||||
if (isMacOSX) {
|
||||
// os.version has values like 10.13.6, 10.14.6
|
||||
// If it is not positively recognised as 10.13 or less,
|
||||
// assume it means 10.14 or some later version.
|
||||
isMacOSX14 = true;
|
||||
String version = System.getProperty("os.version", "");
|
||||
if (version.startsWith("10.")) {
|
||||
version = version.substring(3);
|
||||
int periodIndex = version.indexOf('.');
|
||||
if (periodIndex != -1) {
|
||||
version = version.substring(0, periodIndex);
|
||||
}
|
||||
try {
|
||||
int v = Integer.parseInt(version);
|
||||
isMacOSX14 = (v >= 14);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String t2kStr = System.getProperty("sun.java2d.font.scaler");
|
||||
if (t2kStr != null) {
|
||||
useT2K = "t2k".equals(t2kStr);
|
||||
} else {
|
||||
useT2K = false;
|
||||
}
|
||||
if (isSolaris) {
|
||||
String version = System.getProperty("os.version", "0.0");
|
||||
isSolaris8 = version.startsWith("5.8");
|
||||
isSolaris9 = version.startsWith("5.9");
|
||||
float ver = Float.parseFloat(version);
|
||||
if (ver > 5.10f) {
|
||||
File f = new File("/etc/release");
|
||||
String line = null;
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(f);
|
||||
InputStreamReader isr = new InputStreamReader(
|
||||
fis, "ISO-8859-1");
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
line = br.readLine();
|
||||
fis.close();
|
||||
} catch (Exception ex) {
|
||||
// Nothing to do here.
|
||||
}
|
||||
if (line != null && line.indexOf("OpenSolaris") >= 0) {
|
||||
isOpenSolaris = true;
|
||||
} else {
|
||||
isOpenSolaris = false;
|
||||
}
|
||||
} else {
|
||||
isOpenSolaris = false;
|
||||
}
|
||||
} else {
|
||||
isSolaris8 = false;
|
||||
isSolaris9 = false;
|
||||
isOpenSolaris = false;
|
||||
}
|
||||
isWindows = osName.startsWith("Windows");
|
||||
String jreLibDirName = System.getProperty("java.home", "")
|
||||
+ File.separator + "lib";
|
||||
String jreFontDirName =
|
||||
jreLibDirName + File.separator + "fonts";
|
||||
File lucidaFile = new File(jreFontDirName + File.separator
|
||||
+ LUCIDA_FILE_NAME);
|
||||
isOpenJDK = !lucidaFile.exists();
|
||||
|
||||
String debugLevel =
|
||||
System.getProperty("sun.java2d.debugfonts");
|
||||
|
||||
if (debugLevel != null && !debugLevel.equals("false")) {
|
||||
debugFonts = true;
|
||||
logger = PlatformLogger.getLogger("sun.java2d");
|
||||
if (debugLevel.equals("warning")) {
|
||||
logger.setLevel(PlatformLogger.Level.WARNING);
|
||||
} else if (debugLevel.equals("severe")) {
|
||||
logger.setLevel(PlatformLogger.Level.SEVERE);
|
||||
}
|
||||
}
|
||||
|
||||
if (debugFonts) {
|
||||
logger = PlatformLogger.getLogger("sun.java2d");
|
||||
logging = logger.isEnabled();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Referenced by code in the JDK which wants to test for the
|
||||
* minimum char code for which layout may be required.
|
||||
* Note that even basic latin text can benefit from ligatures,
|
||||
* eg "ffi" but we presently apply those only if explicitly
|
||||
* requested with TextAttribute.LIGATURES_ON.
|
||||
* The value here indicates the lowest char code for which failing
|
||||
* to invoke layout would prevent acceptable rendering.
|
||||
*/
|
||||
public static final int MIN_LAYOUT_CHARCODE = 0x0300;
|
||||
|
||||
/**
|
||||
* Referenced by code in the JDK which wants to test for the
|
||||
* maximum char code for which layout may be required.
|
||||
* Note this does not account for supplementary characters
|
||||
* where the caller interprets 'layout' to mean any case where
|
||||
* one 'char' (ie the java type char) does not map to one glyph
|
||||
*/
|
||||
public static final int MAX_LAYOUT_CHARCODE = 0x206F;
|
||||
|
||||
/**
|
||||
* Calls the private getFont2D() method in java.awt.Font objects.
|
||||
*
|
||||
* @param font the font object to call
|
||||
*
|
||||
* @return the Font2D object returned by Font.getFont2D()
|
||||
*/
|
||||
public static Font2D getFont2D(Font font) {
|
||||
return FontAccess.getFontAccess().getFont2D(font);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is anything in the text which triggers a case
|
||||
* where char->glyph does not map 1:1 in straightforward
|
||||
* left->right ordering, then this method returns true.
|
||||
* Scripts which might require it but are not treated as such
|
||||
* due to JDK implementations will not return true.
|
||||
* ie a 'true' return is an indication of the treatment by
|
||||
* the implementation.
|
||||
* Whether supplementary characters should be considered is dependent
|
||||
* on the needs of the caller. Since this method accepts the 'char' type
|
||||
* then such chars are always represented by a pair. From a rendering
|
||||
* perspective these will all (in the cases I know of) still be one
|
||||
* unicode character -> one glyph. But if a caller is using this to
|
||||
* discover any case where it cannot make naive assumptions about
|
||||
* the number of chars, and how to index through them, then it may
|
||||
* need the option to have a 'true' return in such a case.
|
||||
*/
|
||||
public static boolean isComplexText(char [] chs, int start, int limit) {
|
||||
|
||||
for (int i = start; i < limit; i++) {
|
||||
if (chs[i] < MIN_LAYOUT_CHARCODE) {
|
||||
continue;
|
||||
}
|
||||
else if (isNonSimpleChar(chs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is almost the same as the method above, except it takes a
|
||||
* char which means it may include undecoded surrogate pairs.
|
||||
* The distinction is made so that code which needs to identify all
|
||||
* cases in which we do not have a simple mapping from
|
||||
* char->unicode character->glyph can be be identified.
|
||||
* For example measurement cannot simply sum advances of 'chars',
|
||||
* the caret in editable text cannot advance one 'char' at a time, etc.
|
||||
* These callers really are asking for more than whether 'layout'
|
||||
* needs to be run, they need to know if they can assume 1->1
|
||||
* char->glyph mapping.
|
||||
*/
|
||||
public static boolean isNonSimpleChar(char ch) {
|
||||
return
|
||||
isComplexCharCode(ch) ||
|
||||
(ch >= CharToGlyphMapper.HI_SURROGATE_START &&
|
||||
ch <= CharToGlyphMapper.LO_SURROGATE_END);
|
||||
}
|
||||
|
||||
/* If the character code falls into any of a number of unicode ranges
|
||||
* where we know that simple left->right layout mapping chars to glyphs
|
||||
* 1:1 and accumulating advances is going to produce incorrect results,
|
||||
* we want to know this so the caller can use a more intelligent layout
|
||||
* approach. A caller who cares about optimum performance may want to
|
||||
* check the first case and skip the method call if its in that range.
|
||||
* Although there's a lot of tests in here, knowing you can skip
|
||||
* CTL saves a great deal more. The rest of the checks are ordered
|
||||
* so that rather than checking explicitly if (>= start & <= end)
|
||||
* which would mean all ranges would need to be checked so be sure
|
||||
* CTL is not needed, the method returns as soon as it recognises
|
||||
* the code point is outside of a CTL ranges.
|
||||
* NOTE: Since this method accepts an 'int' it is asssumed to properly
|
||||
* represent a CHARACTER. ie it assumes the caller has already
|
||||
* converted surrogate pairs into supplementary characters, and so
|
||||
* can handle this case and doesn't need to be told such a case is
|
||||
* 'complex'.
|
||||
*/
|
||||
public static boolean isComplexCharCode(int code) {
|
||||
|
||||
if (code < MIN_LAYOUT_CHARCODE || code > MAX_LAYOUT_CHARCODE) {
|
||||
return false;
|
||||
}
|
||||
else if (code <= 0x036f) {
|
||||
// Trigger layout for combining diacriticals 0x0300->0x036f
|
||||
return true;
|
||||
}
|
||||
else if (code < 0x0590) {
|
||||
// No automatic layout for Greek, Cyrillic, Armenian.
|
||||
return false;
|
||||
}
|
||||
else if (code <= 0x06ff) {
|
||||
// Hebrew 0590 - 05ff
|
||||
// Arabic 0600 - 06ff
|
||||
return true;
|
||||
}
|
||||
else if (code < 0x0900) {
|
||||
return false; // Syriac and Thaana
|
||||
}
|
||||
else if (code <= 0x0e7f) {
|
||||
// if Indic, assume shaping for conjuncts, reordering:
|
||||
// 0900 - 097F Devanagari
|
||||
// 0980 - 09FF Bengali
|
||||
// 0A00 - 0A7F Gurmukhi
|
||||
// 0A80 - 0AFF Gujarati
|
||||
// 0B00 - 0B7F Oriya
|
||||
// 0B80 - 0BFF Tamil
|
||||
// 0C00 - 0C7F Telugu
|
||||
// 0C80 - 0CFF Kannada
|
||||
// 0D00 - 0D7F Malayalam
|
||||
// 0D80 - 0DFF Sinhala
|
||||
// 0E00 - 0E7F if Thai, assume shaping for vowel, tone marks
|
||||
return true;
|
||||
}
|
||||
else if (code < 0x0f00) {
|
||||
return false;
|
||||
}
|
||||
else if (code <= 0x0fff) { // U+0F00 - U+0FFF Tibetan
|
||||
return true;
|
||||
}
|
||||
else if (code < 0x1100) {
|
||||
return false;
|
||||
}
|
||||
else if (code < 0x11ff) { // U+1100 - U+11FF Old Hangul
|
||||
return true;
|
||||
}
|
||||
else if (code < 0x1780) {
|
||||
return false;
|
||||
}
|
||||
else if (code <= 0x17ff) { // 1780 - 17FF Khmer
|
||||
return true;
|
||||
}
|
||||
else if (code < 0x200c) {
|
||||
return false;
|
||||
}
|
||||
else if (code <= 0x200d) { // zwj or zwnj
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x202a && code <= 0x202e) { // directional control
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x206a && code <= 0x206f) { // directional control
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static PlatformLogger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public static boolean isLogging() {
|
||||
return logging;
|
||||
}
|
||||
|
||||
public static boolean debugFonts() {
|
||||
return debugFonts;
|
||||
}
|
||||
|
||||
|
||||
// The following methods are used by Swing.
|
||||
|
||||
/* Revise the implementation to in fact mean "font is a composite font.
|
||||
* This ensures that Swing components will always benefit from the
|
||||
* fall back fonts
|
||||
*/
|
||||
public static boolean fontSupportsDefaultEncoding(Font font) {
|
||||
return getFont2D(font) instanceof CompositeFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is provided for internal and exclusive use by Swing.
|
||||
*
|
||||
* It may be used in conjunction with fontSupportsDefaultEncoding(Font)
|
||||
* In the event that a desktop properties font doesn't directly
|
||||
* support the default encoding, (ie because the host OS supports
|
||||
* adding support for the current locale automatically for native apps),
|
||||
* then Swing calls this method to get a font which uses the specified
|
||||
* font for the code points it covers, but also supports this locale
|
||||
* just as the standard composite fonts do.
|
||||
* Note: this will over-ride any setting where an application
|
||||
* specifies it prefers locale specific composite fonts.
|
||||
* The logic for this, is that this method is used only where the user or
|
||||
* application has specified that the native L&F be used, and that
|
||||
* we should honour that request to use the same font as native apps use.
|
||||
*
|
||||
* The behaviour of this method is to construct a new composite
|
||||
* Font object that uses the specified physical font as its first
|
||||
* component, and adds all the components of "dialog" as fall back
|
||||
* components.
|
||||
* The method currently assumes that only the size and style attributes
|
||||
* are set on the specified font. It doesn't copy the font transform or
|
||||
* other attributes because they aren't set on a font created from
|
||||
* the desktop. This will need to be fixed if use is broadened.
|
||||
*
|
||||
* Operations such as Font.deriveFont will work properly on the
|
||||
* font returned by this method for deriving a different point size.
|
||||
* Additionally it tries to support a different style by calling
|
||||
* getNewComposite() below. That also supports replacing slot zero
|
||||
* with a different physical font but that is expected to be "rare".
|
||||
* Deriving with a different style is needed because its been shown
|
||||
* that some applications try to do this for Swing FontUIResources.
|
||||
* Also operations such as new Font(font.getFontName(..), Font.PLAIN, 14);
|
||||
* will NOT yield the same result, as the new underlying CompositeFont
|
||||
* cannot be "looked up" in the font registry.
|
||||
* This returns a FontUIResource as that is the Font sub-class needed
|
||||
* by Swing.
|
||||
* Suggested usage is something like :
|
||||
* FontUIResource fuir;
|
||||
* Font desktopFont = getDesktopFont(..);
|
||||
* // NOTE even if fontSupportsDefaultEncoding returns true because
|
||||
* // you get Tahoma and are running in an English locale, you may
|
||||
* // still want to just call getCompositeFontUIResource() anyway
|
||||
* // as only then will you get fallback fonts - eg for CJK.
|
||||
* if (FontManager.fontSupportsDefaultEncoding(desktopFont)) {
|
||||
* fuir = new FontUIResource(..);
|
||||
* } else {
|
||||
* fuir = FontManager.getCompositeFontUIResource(desktopFont);
|
||||
* }
|
||||
* return fuir;
|
||||
*/
|
||||
private static volatile
|
||||
SoftReference<ConcurrentHashMap<PhysicalFont, CompositeFont>>
|
||||
compMapRef = new SoftReference(null);
|
||||
|
||||
public static FontUIResource getCompositeFontUIResource(Font font) {
|
||||
|
||||
FontUIResource fuir = new FontUIResource(font);
|
||||
Font2D font2D = FontUtilities.getFont2D(font);
|
||||
|
||||
if (!(font2D instanceof PhysicalFont)) {
|
||||
/* Swing should only be calling this when a font is obtained
|
||||
* from desktop properties, so should generally be a physical font,
|
||||
* an exception might be for names like "MS Serif" which are
|
||||
* automatically mapped to "Serif", so there's no need to do
|
||||
* anything special in that case. But note that suggested usage
|
||||
* is first to call fontSupportsDefaultEncoding(Font) and this
|
||||
* method should not be called if that were to return true.
|
||||
*/
|
||||
return fuir;
|
||||
}
|
||||
|
||||
FontManager fm = FontManagerFactory.getInstance();
|
||||
Font2D dialog = fm.findFont2D("dialog", font.getStyle(), FontManager.NO_FALLBACK);
|
||||
// Should never be null, but MACOSX fonts are not CompositeFonts
|
||||
if (dialog == null || !(dialog instanceof CompositeFont)) {
|
||||
return fuir;
|
||||
}
|
||||
CompositeFont dialog2D = (CompositeFont)dialog;
|
||||
PhysicalFont physicalFont = (PhysicalFont)font2D;
|
||||
ConcurrentHashMap<PhysicalFont, CompositeFont> compMap = compMapRef.get();
|
||||
if (compMap == null) { // Its been collected.
|
||||
compMap = new ConcurrentHashMap<PhysicalFont, CompositeFont>();
|
||||
compMapRef = new SoftReference(compMap);
|
||||
}
|
||||
CompositeFont compFont = compMap.get(physicalFont);
|
||||
if (compFont == null) {
|
||||
compFont = new CompositeFont(physicalFont, dialog2D);
|
||||
compMap.put(physicalFont, compFont);
|
||||
}
|
||||
FontAccess.getFontAccess().setFont2D(fuir, compFont.handle);
|
||||
/* marking this as a created font is needed as only created fonts
|
||||
* copy their creator's handles.
|
||||
*/
|
||||
FontAccess.getFontAccess().setCreatedFont(fuir);
|
||||
return fuir;
|
||||
}
|
||||
|
||||
/* A small "map" from GTK/fontconfig names to the equivalent JDK
|
||||
* logical font name.
|
||||
*/
|
||||
private static final String[][] nameMap = {
|
||||
{"sans", "sansserif"},
|
||||
{"sans-serif", "sansserif"},
|
||||
{"serif", "serif"},
|
||||
{"monospace", "monospaced"}
|
||||
};
|
||||
|
||||
public static String mapFcName(String name) {
|
||||
for (int i = 0; i < nameMap.length; i++) {
|
||||
if (name.equals(nameMap[i][0])) {
|
||||
return nameMap[i][1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* This is called by Swing passing in a fontconfig family name
|
||||
* such as "sans". In return Swing gets a FontUIResource instance
|
||||
* that has queried fontconfig to resolve the font(s) used for this.
|
||||
* Fontconfig will if asked return a list of fonts to give the largest
|
||||
* possible code point coverage.
|
||||
* For now we use only the first font returned by fontconfig, and
|
||||
* back it up with the most closely matching JDK logical font.
|
||||
* Essentially this means pre-pending what we return now with fontconfig's
|
||||
* preferred physical font. This could lead to some duplication in cases,
|
||||
* if we already included that font later. We probably should remove such
|
||||
* duplicates, but it is not a significant problem. It can be addressed
|
||||
* later as part of creating a Composite which uses more of the
|
||||
* same fonts as fontconfig. At that time we also should pay more
|
||||
* attention to the special rendering instructions fontconfig returns,
|
||||
* such as whether we should prefer embedded bitmaps over antialiasing.
|
||||
* There's no way to express that via a Font at present.
|
||||
*/
|
||||
public static FontUIResource getFontConfigFUIR(String fcFamily,
|
||||
int style, int size) {
|
||||
|
||||
String mapped = mapFcName(fcFamily);
|
||||
if (mapped == null) {
|
||||
mapped = "sansserif";
|
||||
}
|
||||
|
||||
FontUIResource fuir;
|
||||
FontManager fm = FontManagerFactory.getInstance();
|
||||
if (fm instanceof SunFontManager) {
|
||||
SunFontManager sfm = (SunFontManager) fm;
|
||||
fuir = sfm.getFontConfigFUIR(mapped, style, size);
|
||||
} else {
|
||||
fuir = new FontUIResource(mapped, style, size);
|
||||
}
|
||||
return fuir;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used by windows printing to assess if a font is likely to
|
||||
* be layout compatible with JDK
|
||||
* TrueType fonts should be, but if they have no GPOS table,
|
||||
* but do have a GSUB table, then they are probably older
|
||||
* fonts GDI handles differently.
|
||||
*/
|
||||
public static boolean textLayoutIsCompatible(Font font) {
|
||||
|
||||
Font2D font2D = getFont2D(font);
|
||||
if (font2D instanceof TrueTypeFont) {
|
||||
TrueTypeFont ttf = (TrueTypeFont) font2D;
|
||||
return
|
||||
ttf.getDirectoryEntry(TrueTypeFont.GSUBTag) == null ||
|
||||
ttf.getDirectoryEntry(TrueTypeFont.GPOSTag) != null;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
280
jdkSrc/jdk8/sun/font/FreetypeFontScaler.java
Normal file
280
jdkSrc/jdk8/sun/font/FreetypeFontScaler.java
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/* This is Freetype based implementation of FontScaler.
|
||||
*
|
||||
* Note that in case of runtime error it is expected that
|
||||
* native code will release all native resources and
|
||||
* call invalidateScaler() (that will throw FontScalerException).
|
||||
*
|
||||
* Note that callee is responsible for releasing native scaler context.
|
||||
*/
|
||||
class FreetypeFontScaler extends FontScaler {
|
||||
/* constants aligned with native code */
|
||||
private static final int TRUETYPE_FONT = 1;
|
||||
private static final int TYPE1_FONT = 2;
|
||||
|
||||
static {
|
||||
/* At the moment fontmanager library depends on freetype library
|
||||
and therefore no need to load it explicitly here */
|
||||
FontManagerNativeLibrary.load();
|
||||
initIDs(FreetypeFontScaler.class);
|
||||
}
|
||||
|
||||
private static native void initIDs(Class FFS);
|
||||
|
||||
private void invalidateScaler() throws FontScalerException {
|
||||
nativeScaler = 0;
|
||||
font = null;
|
||||
throw new FontScalerException();
|
||||
}
|
||||
|
||||
public FreetypeFontScaler(Font2D font, int indexInCollection,
|
||||
boolean supportsCJK, int filesize) {
|
||||
int fonttype = TRUETYPE_FONT;
|
||||
if (font instanceof Type1Font) {
|
||||
fonttype = TYPE1_FONT;
|
||||
}
|
||||
nativeScaler = initNativeScaler(font,
|
||||
fonttype,
|
||||
indexInCollection,
|
||||
supportsCJK,
|
||||
filesize);
|
||||
this.font = new WeakReference(font);
|
||||
}
|
||||
|
||||
synchronized StrikeMetrics getFontMetrics(long pScalerContext)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getFontMetricsNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler);
|
||||
}
|
||||
return FontScaler.getNullScaler().getFontMetrics(0L);
|
||||
}
|
||||
|
||||
synchronized float getGlyphAdvance(long pScalerContext, int glyphCode)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphAdvanceNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphCode);
|
||||
}
|
||||
return FontScaler.getNullScaler().
|
||||
getGlyphAdvance(0L, glyphCode);
|
||||
}
|
||||
|
||||
synchronized void getGlyphMetrics(long pScalerContext,
|
||||
int glyphCode, Point2D.Float metrics)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
getGlyphMetricsNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphCode,
|
||||
metrics);
|
||||
return;
|
||||
}
|
||||
FontScaler.getNullScaler().
|
||||
getGlyphMetrics(0L, glyphCode, metrics);
|
||||
}
|
||||
|
||||
synchronized long getGlyphImage(long pScalerContext, int glyphCode)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphImageNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphCode);
|
||||
}
|
||||
return FontScaler.getNullScaler().
|
||||
getGlyphImage(0L, glyphCode);
|
||||
}
|
||||
|
||||
synchronized Rectangle2D.Float getGlyphOutlineBounds(
|
||||
long pScalerContext, int glyphCode)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphOutlineBoundsNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphCode);
|
||||
}
|
||||
return FontScaler.getNullScaler().
|
||||
getGlyphOutlineBounds(0L,glyphCode);
|
||||
}
|
||||
|
||||
synchronized GeneralPath getGlyphOutline(
|
||||
long pScalerContext, int glyphCode, float x, float y)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphOutlineNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphCode,
|
||||
x, y);
|
||||
}
|
||||
return FontScaler.getNullScaler().
|
||||
getGlyphOutline(0L, glyphCode, x,y);
|
||||
}
|
||||
|
||||
synchronized GeneralPath getGlyphVectorOutline(
|
||||
long pScalerContext, int[] glyphs, int numGlyphs,
|
||||
float x, float y) throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphVectorOutlineNative(font.get(),
|
||||
pScalerContext,
|
||||
nativeScaler,
|
||||
glyphs,
|
||||
numGlyphs,
|
||||
x, y);
|
||||
}
|
||||
return FontScaler
|
||||
.getNullScaler().getGlyphVectorOutline(0L, glyphs, numGlyphs, x, y);
|
||||
}
|
||||
|
||||
synchronized long getLayoutTableCache() throws FontScalerException {
|
||||
return getLayoutTableCacheNative(nativeScaler);
|
||||
}
|
||||
|
||||
/* This method should not be called directly, in case
|
||||
* it is being invoked from a thread with a native context.
|
||||
*/
|
||||
public synchronized void dispose() {
|
||||
if (nativeScaler != 0L) {
|
||||
disposeNativeScaler(font.get(), nativeScaler);
|
||||
nativeScaler = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void disposeScaler() {
|
||||
if (nativeScaler != 0L) {
|
||||
/*
|
||||
* The current thread may be calling this method from the context
|
||||
* of a JNI up-call. It will hold the native lock from the
|
||||
* original down-call so can directly enter dispose and free
|
||||
* the resources. So we need to schedule the disposal to happen
|
||||
* only once we've returned from native. So by running the dispose
|
||||
* on another thread which does nothing except that disposal we
|
||||
* are sure that this is safe.
|
||||
*/
|
||||
new Thread(null, () -> dispose(), "free scaler", 0).start();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized int getNumGlyphs() throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getNumGlyphsNative(nativeScaler);
|
||||
}
|
||||
return FontScaler.getNullScaler().getNumGlyphs();
|
||||
}
|
||||
|
||||
synchronized int getMissingGlyphCode() throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getMissingGlyphCodeNative(nativeScaler);
|
||||
}
|
||||
return FontScaler.getNullScaler().getMissingGlyphCode();
|
||||
}
|
||||
|
||||
synchronized int getGlyphCode(char charCode) throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphCodeNative(font.get(), nativeScaler, charCode);
|
||||
}
|
||||
return FontScaler.getNullScaler().getGlyphCode(charCode);
|
||||
}
|
||||
|
||||
synchronized Point2D.Float getGlyphPoint(long pScalerContext,
|
||||
int glyphCode, int ptNumber)
|
||||
throws FontScalerException {
|
||||
if (nativeScaler != 0L) {
|
||||
return getGlyphPointNative(font.get(), pScalerContext,
|
||||
nativeScaler, glyphCode, ptNumber);
|
||||
}
|
||||
return FontScaler.getNullScaler().getGlyphPoint(
|
||||
pScalerContext, glyphCode, ptNumber);
|
||||
}
|
||||
|
||||
synchronized long getUnitsPerEm() {
|
||||
return getUnitsPerEMNative(nativeScaler);
|
||||
}
|
||||
|
||||
synchronized long createScalerContext(double[] matrix,
|
||||
int aa, int fm, float boldness, float italic,
|
||||
boolean disableHinting) {
|
||||
if (nativeScaler != 0L) {
|
||||
return createScalerContextNative(nativeScaler, matrix,
|
||||
aa, fm, boldness, italic);
|
||||
}
|
||||
return NullFontScaler.getNullScalerContext();
|
||||
}
|
||||
|
||||
//Note: native methods can throw RuntimeException if processing fails
|
||||
private native long initNativeScaler(Font2D font, int type,
|
||||
int indexInCollection, boolean supportsCJK, int filesize);
|
||||
private native StrikeMetrics getFontMetricsNative(Font2D font,
|
||||
long pScalerContext, long pScaler);
|
||||
private native float getGlyphAdvanceNative(Font2D font,
|
||||
long pScalerContext, long pScaler, int glyphCode);
|
||||
private native void getGlyphMetricsNative(Font2D font,
|
||||
long pScalerContext, long pScaler,
|
||||
int glyphCode, Point2D.Float metrics);
|
||||
private native long getGlyphImageNative(Font2D font,
|
||||
long pScalerContext, long pScaler, int glyphCode);
|
||||
private native Rectangle2D.Float getGlyphOutlineBoundsNative(Font2D font,
|
||||
long pScalerContext, long pScaler, int glyphCode);
|
||||
private native GeneralPath getGlyphOutlineNative(Font2D font,
|
||||
long pScalerContext, long pScaler,
|
||||
int glyphCode, float x, float y);
|
||||
private native GeneralPath getGlyphVectorOutlineNative(Font2D font,
|
||||
long pScalerContext, long pScaler,
|
||||
int[] glyphs, int numGlyphs, float x, float y);
|
||||
private native Point2D.Float getGlyphPointNative(Font2D font,
|
||||
long pScalerContext, long pScaler, int glyphCode, int ptNumber);
|
||||
|
||||
private native long getLayoutTableCacheNative(long pScaler);
|
||||
|
||||
private native void disposeNativeScaler(Font2D font2D, long pScaler);
|
||||
|
||||
private native int getGlyphCodeNative(Font2D font, long pScaler, char charCode);
|
||||
private native int getNumGlyphsNative(long pScaler);
|
||||
private native int getMissingGlyphCodeNative(long pScaler);
|
||||
|
||||
private native long getUnitsPerEMNative(long pScaler);
|
||||
|
||||
private native long createScalerContextNative(long pScaler, double[] matrix,
|
||||
int aa, int fm, float boldness, float italic);
|
||||
|
||||
/* Freetype scaler context does not contain any pointers that
|
||||
has to be invalidated if native scaler is bad */
|
||||
void invalidateScalerContext(long pScalerContext) {}
|
||||
}
|
||||
32
jdkSrc/jdk8/sun/font/GlyphDisposedListener.java
Normal file
32
jdkSrc/jdk8/sun/font/GlyphDisposedListener.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public interface GlyphDisposedListener {
|
||||
public void glyphDisposed(ArrayList<Long> glyphs);
|
||||
}
|
||||
689
jdkSrc/jdk8/sun/font/GlyphLayout.java
Normal file
689
jdkSrc/jdk8/sun/font/GlyphLayout.java
Normal file
@@ -0,0 +1,689 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1999-2003 - All Rights Reserved
|
||||
*
|
||||
* The original version of this source code and documentation is
|
||||
* copyrighted and owned by IBM. These materials are provided
|
||||
* under terms of a License Agreement between IBM and Sun.
|
||||
* This technology is protected by multiple US and International
|
||||
* patents. This notice and attribution to IBM may not be removed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* GlyphLayout is used to process a run of text into a run of run of
|
||||
* glyphs, optionally with position and char mapping info.
|
||||
*
|
||||
* The text has already been processed for numeric shaping and bidi.
|
||||
* The run of text that layout works on has a single bidi level. It
|
||||
* also has a single font/style. Some operations need context to work
|
||||
* on (shaping, script resolution) so context for the text run text is
|
||||
* provided. It is assumed that the text array contains sufficient
|
||||
* context, and the offset and count delimit the portion of the text
|
||||
* that needs to actually be processed.
|
||||
*
|
||||
* The font might be a composite font. Layout generally requires
|
||||
* tables from a single physical font to operate, and so it must
|
||||
* resolve the 'single' font run into runs of physical fonts.
|
||||
*
|
||||
* Some characters are supported by several fonts of a composite, and
|
||||
* in order to properly emulate the glyph substitution behavior of a
|
||||
* single physical font, these characters might need to be mapped to
|
||||
* different physical fonts. The script code that is assigned
|
||||
* characters normally considered 'common script' can be used to
|
||||
* resolve which physical font to use for these characters. The input
|
||||
* to the char to glyph mapper (which assigns physical fonts as it
|
||||
* processes the glyphs) should include the script code, and the
|
||||
* mapper should operate on runs of a single script.
|
||||
*
|
||||
* To perform layout, call get() to get a new (or reuse an old)
|
||||
* GlyphLayout, call layout on it, then call done(GlyphLayout) when
|
||||
* finished. There's no particular problem if you don't call done,
|
||||
* but it assists in reuse of the GlyphLayout.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.awt.Font;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static java.lang.Character.*;
|
||||
|
||||
public final class GlyphLayout {
|
||||
// data for glyph vector
|
||||
private GVData _gvdata;
|
||||
|
||||
// cached glyph layout data for reuse
|
||||
private static volatile GlyphLayout cache; // reusable
|
||||
|
||||
private LayoutEngineFactory _lef; // set when get is called, unset when done is called
|
||||
private TextRecord _textRecord; // the text we're working on, used by iterators
|
||||
private ScriptRun _scriptRuns; // iterator over script runs
|
||||
private FontRunIterator _fontRuns; // iterator over physical fonts in a composite
|
||||
private int _ercount;
|
||||
private ArrayList _erecords;
|
||||
private Point2D.Float _pt;
|
||||
private FontStrikeDesc _sd;
|
||||
private float[] _mat;
|
||||
private int _typo_flags;
|
||||
private int _offset;
|
||||
|
||||
public static final class LayoutEngineKey {
|
||||
private Font2D font;
|
||||
private int script;
|
||||
private int lang;
|
||||
|
||||
LayoutEngineKey() {
|
||||
}
|
||||
|
||||
LayoutEngineKey(Font2D font, int script, int lang) {
|
||||
init(font, script, lang);
|
||||
}
|
||||
|
||||
void init(Font2D font, int script, int lang) {
|
||||
this.font = font;
|
||||
this.script = script;
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
LayoutEngineKey copy() {
|
||||
return new LayoutEngineKey(font, script, lang);
|
||||
}
|
||||
|
||||
Font2D font() {
|
||||
return font;
|
||||
}
|
||||
|
||||
int script() {
|
||||
return script;
|
||||
}
|
||||
|
||||
int lang() {
|
||||
return lang;
|
||||
}
|
||||
|
||||
public boolean equals(Object rhs) {
|
||||
if (this == rhs) return true;
|
||||
if (rhs == null) return false;
|
||||
try {
|
||||
LayoutEngineKey that = (LayoutEngineKey)rhs;
|
||||
return this.script == that.script &&
|
||||
this.lang == that.lang &&
|
||||
this.font.equals(that.font);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return script ^ lang ^ font.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface LayoutEngineFactory {
|
||||
/**
|
||||
* Given a font, script, and language, determine a layout engine to use.
|
||||
*/
|
||||
public LayoutEngine getEngine(Font2D font, int script, int lang);
|
||||
|
||||
/**
|
||||
* Given a key, determine a layout engine to use.
|
||||
*/
|
||||
public LayoutEngine getEngine(LayoutEngineKey key);
|
||||
}
|
||||
|
||||
public static interface LayoutEngine {
|
||||
/**
|
||||
* Given a strike descriptor, text, rtl flag, and starting point, append information about
|
||||
* glyphs, positions, and character indices to the glyphvector data, and advance the point.
|
||||
*
|
||||
* If the GVData does not have room for the glyphs, throws an IndexOutOfBoundsException and
|
||||
* leave pt and the gvdata unchanged.
|
||||
*/
|
||||
public void layout(FontStrikeDesc sd, float[] mat, int gmask,
|
||||
int baseIndex, TextRecord text, int typo_flags, Point2D.Float pt, GVData data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of GlyphLayout, using the provided layout engine factory.
|
||||
* If null, the system layout engine factory will be used.
|
||||
*/
|
||||
public static GlyphLayout get(LayoutEngineFactory lef) {
|
||||
if (lef == null) {
|
||||
lef = SunLayoutEngine.instance();
|
||||
}
|
||||
GlyphLayout result = null;
|
||||
synchronized(GlyphLayout.class) {
|
||||
if (cache != null) {
|
||||
result = cache;
|
||||
cache = null;
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = new GlyphLayout();
|
||||
}
|
||||
result._lef = lef;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the old instance of GlyphLayout when you are done. This enables reuse
|
||||
* of GlyphLayout objects.
|
||||
*/
|
||||
public static void done(GlyphLayout gl) {
|
||||
gl._lef = null;
|
||||
cache = gl; // object reference assignment is thread safe, it says here...
|
||||
}
|
||||
|
||||
private static final class SDCache {
|
||||
public Font key_font;
|
||||
public FontRenderContext key_frc;
|
||||
|
||||
public AffineTransform dtx;
|
||||
public AffineTransform invdtx;
|
||||
public AffineTransform gtx;
|
||||
public Point2D.Float delta;
|
||||
public FontStrikeDesc sd;
|
||||
|
||||
private SDCache(Font font, FontRenderContext frc) {
|
||||
key_font = font;
|
||||
key_frc = frc;
|
||||
|
||||
// !!! add getVectorTransform and hasVectorTransform to frc? then
|
||||
// we could just skip this work...
|
||||
|
||||
dtx = frc.getTransform();
|
||||
dtx.setTransform(dtx.getScaleX(), dtx.getShearY(),
|
||||
dtx.getShearX(), dtx.getScaleY(),
|
||||
0, 0);
|
||||
if (!dtx.isIdentity()) {
|
||||
try {
|
||||
invdtx = dtx.createInverse();
|
||||
}
|
||||
catch (NoninvertibleTransformException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
float ptSize = font.getSize2D();
|
||||
if (font.isTransformed()) {
|
||||
gtx = font.getTransform();
|
||||
gtx.scale(ptSize, ptSize);
|
||||
delta = new Point2D.Float((float)gtx.getTranslateX(),
|
||||
(float)gtx.getTranslateY());
|
||||
gtx.setTransform(gtx.getScaleX(), gtx.getShearY(),
|
||||
gtx.getShearX(), gtx.getScaleY(),
|
||||
0, 0);
|
||||
gtx.preConcatenate(dtx);
|
||||
} else {
|
||||
delta = ZERO_DELTA;
|
||||
gtx = new AffineTransform(dtx);
|
||||
gtx.scale(ptSize, ptSize);
|
||||
}
|
||||
|
||||
/* Similar logic to that used in SunGraphics2D.checkFontInfo().
|
||||
* Whether a grey (AA) strike is needed is size dependent if
|
||||
* AA mode is 'gasp'.
|
||||
*/
|
||||
int aa =
|
||||
FontStrikeDesc.getAAHintIntVal(frc.getAntiAliasingHint(),
|
||||
FontUtilities.getFont2D(font),
|
||||
(int)Math.abs(ptSize));
|
||||
int fm = FontStrikeDesc.getFMHintIntVal
|
||||
(frc.getFractionalMetricsHint());
|
||||
sd = new FontStrikeDesc(dtx, gtx, font.getStyle(), aa, fm);
|
||||
}
|
||||
|
||||
private static final Point2D.Float ZERO_DELTA = new Point2D.Float();
|
||||
|
||||
private static
|
||||
SoftReference<ConcurrentHashMap<SDKey, SDCache>> cacheRef;
|
||||
|
||||
private static final class SDKey {
|
||||
private final Font font;
|
||||
private final FontRenderContext frc;
|
||||
private final int hash;
|
||||
|
||||
SDKey(Font font, FontRenderContext frc) {
|
||||
this.font = font;
|
||||
this.frc = frc;
|
||||
this.hash = font.hashCode() ^ frc.hashCode();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
try {
|
||||
SDKey rhs = (SDKey)o;
|
||||
return
|
||||
hash == rhs.hash &&
|
||||
font.equals(rhs.font) &&
|
||||
frc.equals(rhs.frc);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static SDCache get(Font font, FontRenderContext frc) {
|
||||
|
||||
// It is possible a translation component will be in the FRC.
|
||||
// It doesn't affect us except adversely as we would consider
|
||||
// FRC's which are really the same to be different. If we
|
||||
// detect a translation component, then we need to exclude it
|
||||
// by creating a new transform which excludes the translation.
|
||||
if (frc.isTransformed()) {
|
||||
AffineTransform transform = frc.getTransform();
|
||||
if (transform.getTranslateX() != 0 ||
|
||||
transform.getTranslateY() != 0) {
|
||||
transform = new AffineTransform(transform.getScaleX(),
|
||||
transform.getShearY(),
|
||||
transform.getShearX(),
|
||||
transform.getScaleY(),
|
||||
0, 0);
|
||||
frc = new FontRenderContext(transform,
|
||||
frc.getAntiAliasingHint(),
|
||||
frc.getFractionalMetricsHint()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SDKey key = new SDKey(font, frc); // garbage, yuck...
|
||||
ConcurrentHashMap<SDKey, SDCache> cache = null;
|
||||
SDCache res = null;
|
||||
if (cacheRef != null) {
|
||||
cache = cacheRef.get();
|
||||
if (cache != null) {
|
||||
res = cache.get(key);
|
||||
}
|
||||
}
|
||||
if (res == null) {
|
||||
res = new SDCache(font, frc);
|
||||
if (cache == null) {
|
||||
cache = new ConcurrentHashMap<SDKey, SDCache>(10);
|
||||
cacheRef = new
|
||||
SoftReference<ConcurrentHashMap<SDKey, SDCache>>(cache);
|
||||
} else if (cache.size() >= 512) {
|
||||
cache.clear();
|
||||
}
|
||||
cache.put(key, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a glyph vector.
|
||||
* @param font the font to use
|
||||
* @param frc the font render context
|
||||
* @param text the text, including optional context before start and after start + count
|
||||
* @param offset the start of the text to lay out
|
||||
* @param count the length of the text to lay out
|
||||
* @param flags bidi and context flags {@see #java.awt.Font}
|
||||
* @param result a StandardGlyphVector to modify, can be null
|
||||
* @return the layed out glyphvector, if result was passed in, it is returned
|
||||
*/
|
||||
public StandardGlyphVector layout(Font font, FontRenderContext frc,
|
||||
char[] text, int offset, int count,
|
||||
int flags, StandardGlyphVector result)
|
||||
{
|
||||
if (text == null || offset < 0 || count < 0 || (count > text.length - offset)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
init(count);
|
||||
|
||||
// need to set after init
|
||||
// go through the back door for this
|
||||
if (font.hasLayoutAttributes()) {
|
||||
AttributeValues values = ((AttributeMap)font.getAttributes()).getValues();
|
||||
if (values.getKerning() != 0) _typo_flags |= 0x1;
|
||||
if (values.getLigatures() != 0) _typo_flags |= 0x2;
|
||||
}
|
||||
|
||||
_offset = offset;
|
||||
|
||||
// use cache now - can we use the strike cache for this?
|
||||
|
||||
SDCache txinfo = SDCache.get(font, frc);
|
||||
_mat[0] = (float)txinfo.gtx.getScaleX();
|
||||
_mat[1] = (float)txinfo.gtx.getShearY();
|
||||
_mat[2] = (float)txinfo.gtx.getShearX();
|
||||
_mat[3] = (float)txinfo.gtx.getScaleY();
|
||||
_pt.setLocation(txinfo.delta);
|
||||
|
||||
int lim = offset + count;
|
||||
|
||||
int min = 0;
|
||||
int max = text.length;
|
||||
if (flags != 0) {
|
||||
if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) {
|
||||
_typo_flags |= 0x80000000; // RTL
|
||||
}
|
||||
|
||||
if ((flags & Font.LAYOUT_NO_START_CONTEXT) != 0) {
|
||||
min = offset;
|
||||
}
|
||||
|
||||
if ((flags & Font.LAYOUT_NO_LIMIT_CONTEXT) != 0) {
|
||||
max = lim;
|
||||
}
|
||||
}
|
||||
|
||||
int lang = -1; // default for now
|
||||
|
||||
Font2D font2D = FontUtilities.getFont2D(font);
|
||||
if (font2D instanceof FontSubstitution) {
|
||||
font2D = ((FontSubstitution)font2D).getCompositeFont2D();
|
||||
}
|
||||
|
||||
_textRecord.init(text, offset, lim, min, max);
|
||||
int start = offset;
|
||||
if (font2D instanceof CompositeFont) {
|
||||
_scriptRuns.init(text, offset, count); // ??? how to handle 'common' chars
|
||||
_fontRuns.init((CompositeFont)font2D, text, offset, lim);
|
||||
while (_scriptRuns.next()) {
|
||||
int limit = _scriptRuns.getScriptLimit();
|
||||
int script = _scriptRuns.getScriptCode();
|
||||
while (_fontRuns.next(script, limit)) {
|
||||
Font2D pfont = _fontRuns.getFont();
|
||||
/* layout can't deal with NativeFont instances. The
|
||||
* native font is assumed to know of a suitable non-native
|
||||
* substitute font. This currently works because
|
||||
* its consistent with the way NativeFonts delegate
|
||||
* in other cases too.
|
||||
*/
|
||||
if (pfont instanceof NativeFont) {
|
||||
pfont = ((NativeFont)pfont).getDelegateFont();
|
||||
}
|
||||
int gmask = _fontRuns.getGlyphMask();
|
||||
int pos = _fontRuns.getPos();
|
||||
nextEngineRecord(start, pos, script, lang, pfont, gmask);
|
||||
start = pos;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_scriptRuns.init(text, offset, count); // ??? don't worry about 'common' chars
|
||||
while (_scriptRuns.next()) {
|
||||
int limit = _scriptRuns.getScriptLimit();
|
||||
int script = _scriptRuns.getScriptCode();
|
||||
nextEngineRecord(start, limit, script, lang, font2D, 0);
|
||||
start = limit;
|
||||
}
|
||||
}
|
||||
|
||||
int ix = 0;
|
||||
int stop = _ercount;
|
||||
int dir = 1;
|
||||
|
||||
if (_typo_flags < 0) { // RTL
|
||||
ix = stop - 1;
|
||||
stop = -1;
|
||||
dir = -1;
|
||||
}
|
||||
|
||||
// _sd.init(dtx, gtx, font.getStyle(), frc.isAntiAliased(), frc.usesFractionalMetrics());
|
||||
_sd = txinfo.sd;
|
||||
for (;ix != stop; ix += dir) {
|
||||
EngineRecord er = (EngineRecord)_erecords.get(ix);
|
||||
for (;;) {
|
||||
try {
|
||||
er.layout();
|
||||
break;
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
if (_gvdata._count >=0) {
|
||||
_gvdata.grow();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Break out of the outer for loop if layout fails.
|
||||
if (_gvdata._count < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if (txinfo.invdtx != null) {
|
||||
// _gvdata.adjustPositions(txinfo.invdtx);
|
||||
// }
|
||||
|
||||
// If layout fails (negative glyph count) create an un-laid out GV instead.
|
||||
// ie default positions. This will be a lot better than the alternative of
|
||||
// a complete blank layout.
|
||||
StandardGlyphVector gv;
|
||||
if (_gvdata._count < 0) {
|
||||
gv = new StandardGlyphVector(font, text, offset, count, frc);
|
||||
if (FontUtilities.debugFonts()) {
|
||||
FontUtilities.getLogger().warning("OpenType layout failed on font: " +
|
||||
font);
|
||||
}
|
||||
} else {
|
||||
gv = _gvdata.createGlyphVector(font, frc, result);
|
||||
}
|
||||
// System.err.println("Layout returns: " + gv);
|
||||
return gv;
|
||||
}
|
||||
|
||||
//
|
||||
// private methods
|
||||
//
|
||||
|
||||
private GlyphLayout() {
|
||||
this._gvdata = new GVData();
|
||||
this._textRecord = new TextRecord();
|
||||
this._scriptRuns = new ScriptRun();
|
||||
this._fontRuns = new FontRunIterator();
|
||||
this._erecords = new ArrayList(10);
|
||||
this._pt = new Point2D.Float();
|
||||
this._sd = new FontStrikeDesc();
|
||||
this._mat = new float[4];
|
||||
}
|
||||
|
||||
private void init(int capacity) {
|
||||
this._typo_flags = 0;
|
||||
this._ercount = 0;
|
||||
this._gvdata.init(capacity);
|
||||
}
|
||||
|
||||
private void nextEngineRecord(int start, int limit, int script, int lang, Font2D font, int gmask) {
|
||||
EngineRecord er = null;
|
||||
if (_ercount == _erecords.size()) {
|
||||
er = new EngineRecord();
|
||||
_erecords.add(er);
|
||||
} else {
|
||||
er = (EngineRecord)_erecords.get(_ercount);
|
||||
}
|
||||
er.init(start, limit, font, script, lang, gmask);
|
||||
++_ercount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage for layout to build glyph vector data, then generate a real GlyphVector
|
||||
*/
|
||||
public static final class GVData {
|
||||
public int _count; // number of glyphs, >= number of chars
|
||||
public int _flags;
|
||||
public int[] _glyphs;
|
||||
public float[] _positions;
|
||||
public int[] _indices;
|
||||
|
||||
private static final int UNINITIALIZED_FLAGS = -1;
|
||||
|
||||
public void init(int size) {
|
||||
_count = 0;
|
||||
_flags = UNINITIALIZED_FLAGS;
|
||||
|
||||
if (_glyphs == null || _glyphs.length < size) {
|
||||
if (size < 20) {
|
||||
size = 20;
|
||||
}
|
||||
_glyphs = new int[size];
|
||||
_positions = new float[size * 2 + 2];
|
||||
_indices = new int[size];
|
||||
}
|
||||
}
|
||||
|
||||
public void grow() {
|
||||
grow(_glyphs.length / 4); // always grows because min length is 20
|
||||
}
|
||||
|
||||
public void grow(int delta) {
|
||||
int size = _glyphs.length + delta;
|
||||
int[] nglyphs = new int[size];
|
||||
System.arraycopy(_glyphs, 0, nglyphs, 0, _count);
|
||||
_glyphs = nglyphs;
|
||||
|
||||
float[] npositions = new float[size * 2 + 2];
|
||||
System.arraycopy(_positions, 0, npositions, 0, _count * 2 + 2);
|
||||
_positions = npositions;
|
||||
|
||||
int[] nindices = new int[size];
|
||||
System.arraycopy(_indices, 0, nindices, 0, _count);
|
||||
_indices = nindices;
|
||||
}
|
||||
|
||||
public void adjustPositions(AffineTransform invdtx) {
|
||||
invdtx.transform(_positions, 0, _positions, 0, _count);
|
||||
}
|
||||
|
||||
public StandardGlyphVector createGlyphVector(Font font, FontRenderContext frc, StandardGlyphVector result) {
|
||||
|
||||
// !!! default initialization until we let layout engines do it
|
||||
if (_flags == UNINITIALIZED_FLAGS) {
|
||||
_flags = 0;
|
||||
|
||||
if (_count > 1) { // if only 1 glyph assume LTR
|
||||
boolean ltr = true;
|
||||
boolean rtl = true;
|
||||
|
||||
int rtlix = _count; // rtl index
|
||||
for (int i = 0; i < _count && (ltr || rtl); ++i) {
|
||||
int cx = _indices[i];
|
||||
|
||||
ltr = ltr && (cx == i);
|
||||
rtl = rtl && (cx == --rtlix);
|
||||
}
|
||||
|
||||
if (rtl) _flags |= GlyphVector.FLAG_RUN_RTL;
|
||||
if (!rtl && !ltr) _flags |= GlyphVector.FLAG_COMPLEX_GLYPHS;
|
||||
}
|
||||
|
||||
// !!! layout engines need to tell us whether they performed
|
||||
// position adjustments. currently they don't tell us, so
|
||||
// we must assume they did
|
||||
_flags |= GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS;
|
||||
}
|
||||
|
||||
int[] glyphs = new int[_count];
|
||||
System.arraycopy(_glyphs, 0, glyphs, 0, _count);
|
||||
|
||||
float[] positions = null;
|
||||
if ((_flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {
|
||||
positions = new float[_count * 2 + 2];
|
||||
System.arraycopy(_positions, 0, positions, 0, positions.length);
|
||||
}
|
||||
|
||||
int[] indices = null;
|
||||
if ((_flags & GlyphVector.FLAG_COMPLEX_GLYPHS) != 0) {
|
||||
indices = new int[_count];
|
||||
System.arraycopy(_indices, 0, indices, 0, _count);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = new StandardGlyphVector(font, frc, glyphs, positions, indices, _flags);
|
||||
} else {
|
||||
result.initGlyphVector(font, frc, glyphs, positions, indices, _flags);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to keep track of script runs, which may have to be reordered rtl when we're
|
||||
* finished.
|
||||
*/
|
||||
private final class EngineRecord {
|
||||
private int start;
|
||||
private int limit;
|
||||
private int gmask;
|
||||
private int eflags;
|
||||
private LayoutEngineKey key;
|
||||
private LayoutEngine engine;
|
||||
|
||||
EngineRecord() {
|
||||
key = new LayoutEngineKey();
|
||||
}
|
||||
|
||||
void init(int start, int limit, Font2D font, int script, int lang, int gmask) {
|
||||
this.start = start;
|
||||
this.limit = limit;
|
||||
this.gmask = gmask;
|
||||
this.key.init(font, script, lang);
|
||||
this.eflags = 0;
|
||||
|
||||
// only request canonical substitution if we have combining marks
|
||||
for (int i = start; i < limit; ++i) {
|
||||
int ch = _textRecord.text[i];
|
||||
if (isHighSurrogate((char)ch) &&
|
||||
i < limit - 1 &&
|
||||
isLowSurrogate(_textRecord.text[i+1])) {
|
||||
// rare case
|
||||
ch = toCodePoint((char)ch,_textRecord.text[++i]); // inc
|
||||
}
|
||||
int gc = getType(ch);
|
||||
if (gc == NON_SPACING_MARK ||
|
||||
gc == ENCLOSING_MARK ||
|
||||
gc == COMBINING_SPACING_MARK) { // could do range test also
|
||||
|
||||
this.eflags = 0x4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.engine = _lef.getEngine(key); // flags?
|
||||
}
|
||||
|
||||
void layout() {
|
||||
_textRecord.start = start;
|
||||
_textRecord.limit = limit;
|
||||
engine.layout(_sd, _mat, gmask, start - _offset, _textRecord,
|
||||
_typo_flags | eflags, _pt, _gvdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
514
jdkSrc/jdk8/sun/font/GlyphList.java
Normal file
514
jdkSrc/jdk8/sun/font/GlyphList.java
Normal file
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import sun.java2d.loops.FontInfo;
|
||||
|
||||
/*
|
||||
* This class represents a list of actual renderable glyphs.
|
||||
* It can be constructed from a number of text sources, representing
|
||||
* the various ways in which a programmer can ask a Graphics2D object
|
||||
* to render some text. Once constructed, it provides a way of iterating
|
||||
* through the device metrics and graybits of the individual glyphs that
|
||||
* need to be rendered to the screen.
|
||||
*
|
||||
* Note that this class holds pointers to native data which must be
|
||||
* disposed. It is not marked as finalizable since it is intended
|
||||
* to be very lightweight and finalization is a comparitively expensive
|
||||
* procedure. The caller must specifically use try{} finally{} to
|
||||
* manually ensure that the object is disposed after use, otherwise
|
||||
* native data structures might be leaked.
|
||||
*
|
||||
* Here is a code sample for using this class:
|
||||
*
|
||||
* public void drawString(String str, FontInfo info, float x, float y) {
|
||||
* GlyphList gl = GlyphList.getInstance();
|
||||
* try {
|
||||
* gl.setFromString(info, str, x, y);
|
||||
* int strbounds[] = gl.getBounds();
|
||||
* int numglyphs = gl.getNumGlyphs();
|
||||
* for (int i = 0; i < numglyphs; i++) {
|
||||
* gl.setGlyphIndex(i);
|
||||
* int metrics[] = gl.getMetrics();
|
||||
* byte bits[] = gl.getGrayBits();
|
||||
* int glyphx = metrics[0];
|
||||
* int glyphy = metrics[1];
|
||||
* int glyphw = metrics[2];
|
||||
* int glyphh = metrics[3];
|
||||
* int off = 0;
|
||||
* for (int j = 0; j < glyphh; j++) {
|
||||
* for (int i = 0; i < glyphw; i++) {
|
||||
* int dx = glyphx + i;
|
||||
* int dy = glyphy + j;
|
||||
* int alpha = bits[off++];
|
||||
* drawPixel(alpha, dx, dy);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* } finally {
|
||||
* gl.dispose();
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public final class GlyphList {
|
||||
private static final int MINGRAYLENGTH = 1024;
|
||||
private static final int MAXGRAYLENGTH = 8192;
|
||||
private static final int DEFAULT_LENGTH = 32;
|
||||
|
||||
int glyphindex;
|
||||
int metrics[];
|
||||
byte graybits[];
|
||||
|
||||
/* A reference to the strike is needed for the case when the GlyphList
|
||||
* may be added to a queue for batch processing, (e.g. OpenGL) and we need
|
||||
* to be completely certain that the strike is still valid when the glyphs
|
||||
* images are later referenced. This does mean that if such code discards
|
||||
* GlyphList and places only the data it contains on the queue, that the
|
||||
* strike needs to be part of that data held by a strong reference.
|
||||
* In the cases of drawString() and drawChars(), this is a single strike,
|
||||
* although it may be a composite strike. In the case of
|
||||
* drawGlyphVector() it may be a single strike, or a list of strikes.
|
||||
*/
|
||||
Object strikelist; // hold multiple strikes during rendering of complex gv
|
||||
|
||||
/* In normal usage, the same GlyphList will get recycled, so
|
||||
* it makes sense to allocate arrays that will get reused along with
|
||||
* it, rather than generating garbage. Garbage will be generated only
|
||||
* in MP envts where multiple threads are executing. Throughput should
|
||||
* still be higher in those cases.
|
||||
*/
|
||||
int len = 0;
|
||||
int maxLen = 0;
|
||||
int maxPosLen = 0;
|
||||
int glyphData[];
|
||||
char chData[];
|
||||
long images[];
|
||||
float positions[];
|
||||
float x, y;
|
||||
float gposx, gposy;
|
||||
boolean usePositions;
|
||||
|
||||
/* lcdRGBOrder is used only by LCD text rendering. Its here because
|
||||
* the Graphics may have a different hint value than the one used
|
||||
* by a GlyphVector, so it has to be stored here - and is obtained
|
||||
* from the right FontInfo. Another approach would have been to have
|
||||
* install a separate pipe for that case but that's a lot of extra
|
||||
* code when a simple boolean will suffice. The overhead to non-LCD
|
||||
* text is a redundant boolean assign per call.
|
||||
*/
|
||||
boolean lcdRGBOrder;
|
||||
|
||||
/*
|
||||
* lcdSubPixPos is used only by LCD text rendering. Its here because
|
||||
* the Graphics may have a different hint value than the one used
|
||||
* by a GlyphVector, so it has to be stored here - and is obtained
|
||||
* from the right FontInfo. Its also needed by the code which
|
||||
* calculates glyph positions which already needs to access this
|
||||
* GlyphList and would otherwise need the FontInfo.
|
||||
* This is true only if LCD text and fractional metrics hints
|
||||
* are selected on the graphics.
|
||||
* When this is true and the glyph positions as determined by the
|
||||
* advances are non-integral, it requests adjustment of the positions.
|
||||
* Setting this for surfaces which do not support it through accelerated
|
||||
* loops may cause a slow-down as software loops are invoked instead.
|
||||
*/
|
||||
boolean lcdSubPixPos;
|
||||
|
||||
/* This scheme creates a singleton GlyphList which is checked out
|
||||
* for use. Callers who find its checked out create one that after use
|
||||
* is discarded. This means that in a MT-rendering environment,
|
||||
* there's no need to synchronise except for that one instance.
|
||||
* Fewer threads will then need to synchronise, perhaps helping
|
||||
* throughput on a MP system. If for some reason the reusable
|
||||
* GlyphList is checked out for a long time (or never returned?) then
|
||||
* we would end up always creating new ones. That situation should not
|
||||
* occur and if if did, it would just lead to some extra garbage being
|
||||
* created.
|
||||
*/
|
||||
private static GlyphList reusableGL = new GlyphList();
|
||||
private static boolean inUse;
|
||||
|
||||
|
||||
void ensureCapacity(int len) {
|
||||
/* Note len must not be -ve! only setFromChars should be capable
|
||||
* of passing down a -ve len, and this guards against it.
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = 0;
|
||||
}
|
||||
if (usePositions && len > maxPosLen) {
|
||||
positions = new float[len * 2 + 2];
|
||||
maxPosLen = len;
|
||||
}
|
||||
|
||||
if (maxLen == 0 || len > maxLen) {
|
||||
glyphData = new int[len];
|
||||
chData = new char[len];
|
||||
images = new long[len];
|
||||
maxLen = len;
|
||||
}
|
||||
}
|
||||
|
||||
private GlyphList() {
|
||||
// ensureCapacity(DEFAULT_LENGTH);
|
||||
}
|
||||
|
||||
// private GlyphList(int arraylen) {
|
||||
// ensureCapacity(arraylen);
|
||||
// }
|
||||
|
||||
public static GlyphList getInstance() {
|
||||
/* The following heuristic is that if the reusable instance is
|
||||
* in use, it probably still will be in a micro-second, so avoid
|
||||
* synchronising on the class and just allocate a new instance.
|
||||
* The cost is one extra boolean test for the normal case, and some
|
||||
* small number of cases where we allocate an extra object when
|
||||
* in fact the reusable one would be freed very soon.
|
||||
*/
|
||||
if (inUse) {
|
||||
return new GlyphList();
|
||||
} else {
|
||||
synchronized(GlyphList.class) {
|
||||
if (inUse) {
|
||||
return new GlyphList();
|
||||
} else {
|
||||
inUse = true;
|
||||
return reusableGL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In some cases the caller may be able to estimate the size of
|
||||
* array needed, and it will usually be long enough. This avoids
|
||||
* the unnecessary reallocation that occurs if our default
|
||||
* values are too small. This is useful because this object
|
||||
* will be discarded so the re-allocation overhead is high.
|
||||
*/
|
||||
// public static GlyphList getInstance(int sz) {
|
||||
// if (inUse) {
|
||||
// return new GlyphList(sz);
|
||||
// } else {
|
||||
// synchronized(GlyphList.class) {
|
||||
// if (inUse) {
|
||||
// return new GlyphList();
|
||||
// } else {
|
||||
// inUse = true;
|
||||
// return reusableGL;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/* GlyphList is in an invalid state until setFrom* method is called.
|
||||
* After obtaining a new GlyphList it is the caller's responsibility
|
||||
* that one of these methods is executed before handing off the
|
||||
* GlyphList
|
||||
*/
|
||||
|
||||
public boolean setFromString(FontInfo info, String str, float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.strikelist = info.fontStrike;
|
||||
this.lcdRGBOrder = info.lcdRGBOrder;
|
||||
this.lcdSubPixPos = info.lcdSubPixPos;
|
||||
len = str.length();
|
||||
ensureCapacity(len);
|
||||
str.getChars(0, len, chData, 0);
|
||||
return mapChars(info, len);
|
||||
}
|
||||
|
||||
public boolean setFromChars(FontInfo info, char[] chars, int off, int alen,
|
||||
float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.strikelist = info.fontStrike;
|
||||
this.lcdRGBOrder = info.lcdRGBOrder;
|
||||
this.lcdSubPixPos = info.lcdSubPixPos;
|
||||
len = alen;
|
||||
if (alen < 0) {
|
||||
len = 0;
|
||||
} else {
|
||||
len = alen;
|
||||
}
|
||||
ensureCapacity(len);
|
||||
System.arraycopy(chars, off, chData, 0, len);
|
||||
return mapChars(info, len);
|
||||
}
|
||||
|
||||
private final boolean mapChars(FontInfo info, int len) {
|
||||
/* REMIND.Is it worthwhile for the iteration to convert
|
||||
* chars to glyph ids to directly map to images?
|
||||
*/
|
||||
if (info.font2D.getMapper().charsToGlyphsNS(len, chData, glyphData)) {
|
||||
return false;
|
||||
}
|
||||
info.fontStrike.getGlyphImagePtrs(glyphData, images, len);
|
||||
glyphindex = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void setFromGlyphVector(FontInfo info, GlyphVector gv,
|
||||
float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.lcdRGBOrder = info.lcdRGBOrder;
|
||||
this.lcdSubPixPos = info.lcdSubPixPos;
|
||||
/* A GV may be rendered in different Graphics. It is possible it is
|
||||
* used for one case where LCD text is available, and another where
|
||||
* it is not. Pass in the "info". to ensure get a suitable one.
|
||||
*/
|
||||
StandardGlyphVector sgv = StandardGlyphVector.getStandardGV(gv, info);
|
||||
// call before ensureCapacity :-
|
||||
usePositions = sgv.needsPositions(info.devTx);
|
||||
len = sgv.getNumGlyphs();
|
||||
ensureCapacity(len);
|
||||
strikelist = sgv.setupGlyphImages(images,
|
||||
usePositions ? positions : null,
|
||||
info.devTx);
|
||||
glyphindex = -1;
|
||||
}
|
||||
|
||||
public int[] getBounds() {
|
||||
/* We co-opt the 5 element array that holds per glyph metrics in order
|
||||
* to return the bounds. So a caller must copy the data out of the
|
||||
* array before calling any other methods on this GlyphList
|
||||
*/
|
||||
if (glyphindex >= 0) {
|
||||
throw new InternalError("calling getBounds after setGlyphIndex");
|
||||
}
|
||||
if (metrics == null) {
|
||||
metrics = new int[5];
|
||||
}
|
||||
/* gposx and gposy are used to accumulate the advance.
|
||||
* Add 0.5f for consistent rounding to pixel position. */
|
||||
gposx = x + 0.5f;
|
||||
gposy = y + 0.5f;
|
||||
fillBounds(metrics);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/* This method now assumes "state", so must be called 0->len
|
||||
* The metrics it returns are accumulated on the fly
|
||||
* So it could be renamed "nextGlyph()".
|
||||
* Note that a laid out GlyphVector which has assigned glyph positions
|
||||
* doesn't have this stricture..
|
||||
*/
|
||||
public void setGlyphIndex(int i) {
|
||||
glyphindex = i;
|
||||
if (images[i] == 0L) {
|
||||
metrics[0] = (int)gposx;
|
||||
metrics[1] = (int)gposy;
|
||||
metrics[2] = 0;
|
||||
metrics[3] = 0;
|
||||
metrics[4] = 0;
|
||||
return;
|
||||
}
|
||||
float gx =
|
||||
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftXOffset);
|
||||
float gy =
|
||||
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftYOffset);
|
||||
|
||||
if (usePositions) {
|
||||
metrics[0] = (int)Math.floor(positions[(i<<1)] + gposx + gx);
|
||||
metrics[1] = (int)Math.floor(positions[(i<<1)+1] + gposy + gy);
|
||||
} else {
|
||||
metrics[0] = (int)Math.floor(gposx + gx);
|
||||
metrics[1] = (int)Math.floor(gposy + gy);
|
||||
/* gposx and gposy are used to accumulate the advance */
|
||||
gposx += StrikeCache.unsafe.getFloat
|
||||
(images[i]+StrikeCache.xAdvanceOffset);
|
||||
gposy += StrikeCache.unsafe.getFloat
|
||||
(images[i]+StrikeCache.yAdvanceOffset);
|
||||
}
|
||||
metrics[2] =
|
||||
StrikeCache.unsafe.getChar(images[i]+StrikeCache.widthOffset);
|
||||
metrics[3] =
|
||||
StrikeCache.unsafe.getChar(images[i]+StrikeCache.heightOffset);
|
||||
metrics[4] =
|
||||
StrikeCache.unsafe.getChar(images[i]+StrikeCache.rowBytesOffset);
|
||||
}
|
||||
|
||||
public int[] getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public byte[] getGrayBits() {
|
||||
int len = metrics[4] * metrics[3];
|
||||
if (graybits == null) {
|
||||
graybits = new byte[Math.max(len, MINGRAYLENGTH)];
|
||||
} else {
|
||||
if (len > graybits.length) {
|
||||
graybits = new byte[len];
|
||||
}
|
||||
}
|
||||
if (images[glyphindex] == 0L) {
|
||||
return graybits;
|
||||
}
|
||||
long pixelDataAddress =
|
||||
StrikeCache.unsafe.getAddress(images[glyphindex] +
|
||||
StrikeCache.pixelDataOffset);
|
||||
|
||||
if (pixelDataAddress == 0L) {
|
||||
return graybits;
|
||||
}
|
||||
/* unsafe is supposed to be fast, but I doubt if this loop can beat
|
||||
* a native call which does a getPrimitiveArrayCritical and a
|
||||
* memcpy for the typical amount of image data (30-150 bytes)
|
||||
* Consider a native method if there is a performance problem (which
|
||||
* I haven't seen so far).
|
||||
*/
|
||||
for (int i=0; i<len; i++) {
|
||||
graybits[i] = StrikeCache.unsafe.getByte(pixelDataAddress+i);
|
||||
}
|
||||
return graybits;
|
||||
}
|
||||
|
||||
public long[] getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
public boolean usePositions() {
|
||||
return usePositions;
|
||||
}
|
||||
|
||||
public float[] getPositions() {
|
||||
return positions;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Object getStrike() {
|
||||
return strikelist;
|
||||
}
|
||||
|
||||
public boolean isSubPixPos() {
|
||||
return lcdSubPixPos;
|
||||
}
|
||||
|
||||
public boolean isRGBOrder() {
|
||||
return lcdRGBOrder;
|
||||
}
|
||||
|
||||
/* There's a reference equality test overhead here, but it allows us
|
||||
* to avoid synchronizing for GL's that will just be GC'd. This
|
||||
* helps MP throughput.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (this == reusableGL) {
|
||||
if (graybits != null && graybits.length > MAXGRAYLENGTH) {
|
||||
graybits = null;
|
||||
}
|
||||
usePositions = false;
|
||||
strikelist = null; // remove reference to the strike list
|
||||
inUse = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* The value here is for use by the rendering engine as it reflects
|
||||
* the number of glyphs in the array to be blitted. Surrogates pairs
|
||||
* may have two slots (the second of these being a dummy entry of the
|
||||
* invisible glyph), whereas an application client would expect only
|
||||
* one glyph. In other words don't propagate this value up to client code.
|
||||
*
|
||||
* {dlf} an application client should have _no_ expectations about the
|
||||
* number of glyphs per char. This ultimately depends on the font
|
||||
* technology and layout process used, which in general clients will
|
||||
* know nothing about.
|
||||
*/
|
||||
public int getNumGlyphs() {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* We re-do all this work as we iterate through the glyphs
|
||||
* but it seems unavoidable without re-working the Java TextRenderers.
|
||||
*/
|
||||
private void fillBounds(int[] bounds) {
|
||||
/* Faster to access local variables in the for loop? */
|
||||
int xOffset = StrikeCache.topLeftXOffset;
|
||||
int yOffset = StrikeCache.topLeftYOffset;
|
||||
int wOffset = StrikeCache.widthOffset;
|
||||
int hOffset = StrikeCache.heightOffset;
|
||||
int xAdvOffset = StrikeCache.xAdvanceOffset;
|
||||
int yAdvOffset = StrikeCache.yAdvanceOffset;
|
||||
|
||||
if (len == 0) {
|
||||
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0;
|
||||
return;
|
||||
}
|
||||
float bx0, by0, bx1, by1;
|
||||
bx0 = by0 = Float.POSITIVE_INFINITY;
|
||||
bx1 = by1 = Float.NEGATIVE_INFINITY;
|
||||
|
||||
int posIndex = 0;
|
||||
float glx = x + 0.5f;
|
||||
float gly = y + 0.5f;
|
||||
char gw, gh;
|
||||
float gx, gy, gx0, gy0, gx1, gy1;
|
||||
for (int i=0; i<len; i++) {
|
||||
if (images[i] == 0L) {
|
||||
continue;
|
||||
}
|
||||
gx = StrikeCache.unsafe.getFloat(images[i]+xOffset);
|
||||
gy = StrikeCache.unsafe.getFloat(images[i]+yOffset);
|
||||
gw = StrikeCache.unsafe.getChar(images[i]+wOffset);
|
||||
gh = StrikeCache.unsafe.getChar(images[i]+hOffset);
|
||||
|
||||
if (usePositions) {
|
||||
gx0 = positions[posIndex++] + gx + glx;
|
||||
gy0 = positions[posIndex++] + gy + gly;
|
||||
} else {
|
||||
gx0 = glx + gx;
|
||||
gy0 = gly + gy;
|
||||
glx += StrikeCache.unsafe.getFloat(images[i]+xAdvOffset);
|
||||
gly += StrikeCache.unsafe.getFloat(images[i]+yAdvOffset);
|
||||
}
|
||||
gx1 = gx0 + gw;
|
||||
gy1 = gy0 + gh;
|
||||
if (bx0 > gx0) bx0 = gx0;
|
||||
if (by0 > gy0) by0 = gy0;
|
||||
if (bx1 < gx1) bx1 = gx1;
|
||||
if (by1 < gy1) by1 = gy1;
|
||||
}
|
||||
/* floor is safe and correct because all glyph widths, heights
|
||||
* and offsets are integers
|
||||
*/
|
||||
bounds[0] = (int)Math.floor(bx0);
|
||||
bounds[1] = (int)Math.floor(by0);
|
||||
bounds[2] = (int)Math.floor(bx1);
|
||||
bounds[3] = (int)Math.floor(by1);
|
||||
}
|
||||
}
|
||||
376
jdkSrc/jdk8/sun/font/GraphicComponent.java
Normal file
376
jdkSrc/jdk8/sun/font/GraphicComponent.java
Normal file
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
|
||||
*
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.LineMetrics;
|
||||
import java.awt.font.GraphicAttribute;
|
||||
import java.awt.font.GlyphJustificationInfo;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.text.Bidi;
|
||||
import java.util.Map;
|
||||
|
||||
public final class GraphicComponent implements TextLineComponent,
|
||||
Decoration.Label {
|
||||
|
||||
public static final float GRAPHIC_LEADING = 2;
|
||||
|
||||
private GraphicAttribute graphic;
|
||||
private int graphicCount;
|
||||
private int[] charsLtoV; // possibly null
|
||||
private byte[] levels; // possibly null
|
||||
|
||||
// evaluated in computeVisualBounds
|
||||
private Rectangle2D visualBounds = null;
|
||||
|
||||
// used everywhere so we'll cache it
|
||||
private float graphicAdvance;
|
||||
|
||||
private AffineTransform baseTx;
|
||||
|
||||
private CoreMetrics cm;
|
||||
private Decoration decorator;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new GraphicComponent. start and limit are indices
|
||||
* into charLtoV and levels. charsLtoV and levels may be adopted.
|
||||
*/
|
||||
public GraphicComponent(GraphicAttribute graphic,
|
||||
Decoration decorator,
|
||||
int[] charsLtoV,
|
||||
byte[] levels,
|
||||
int start,
|
||||
int limit,
|
||||
AffineTransform baseTx) {
|
||||
|
||||
if (limit <= start) {
|
||||
throw new IllegalArgumentException("0 or negative length in GraphicComponent");
|
||||
}
|
||||
this.graphic = graphic;
|
||||
this.graphicAdvance = graphic.getAdvance();
|
||||
this.decorator = decorator;
|
||||
this.cm = createCoreMetrics(graphic);
|
||||
this.baseTx = baseTx;
|
||||
|
||||
initLocalOrdering(charsLtoV, levels, start, limit);
|
||||
}
|
||||
|
||||
private GraphicComponent(GraphicComponent parent, int start, int limit, int dir) {
|
||||
|
||||
this.graphic = parent.graphic;
|
||||
this.graphicAdvance = parent.graphicAdvance;
|
||||
this.decorator = parent.decorator;
|
||||
this.cm = parent.cm;
|
||||
this.baseTx = parent.baseTx;
|
||||
|
||||
int[] charsLtoV = null;
|
||||
byte[] levels = null;
|
||||
|
||||
if (dir == UNCHANGED) {
|
||||
charsLtoV = parent.charsLtoV;
|
||||
levels = parent.levels;
|
||||
}
|
||||
else if (dir == LEFT_TO_RIGHT || dir == RIGHT_TO_LEFT) {
|
||||
limit -= start;
|
||||
start = 0;
|
||||
if (dir == RIGHT_TO_LEFT) {
|
||||
charsLtoV = new int[limit];
|
||||
levels = new byte[limit];
|
||||
for (int i=0; i < limit; i++) {
|
||||
charsLtoV[i] = limit-i-1;
|
||||
levels[i] = (byte) 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid direction flag");
|
||||
}
|
||||
|
||||
initLocalOrdering(charsLtoV, levels, start, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize graphicCount, also charsLtoV and levels arrays.
|
||||
*/
|
||||
private void initLocalOrdering(int[] charsLtoV,
|
||||
byte[] levels,
|
||||
int start,
|
||||
int limit) {
|
||||
|
||||
this.graphicCount = limit - start; // todo: should be codepoints?
|
||||
|
||||
if (charsLtoV == null || charsLtoV.length == graphicCount) {
|
||||
this.charsLtoV = charsLtoV;
|
||||
}
|
||||
else {
|
||||
this.charsLtoV = BidiUtils.createNormalizedMap(charsLtoV, levels, start, limit);
|
||||
}
|
||||
|
||||
if (levels == null || levels.length == graphicCount) {
|
||||
this.levels = levels;
|
||||
}
|
||||
else {
|
||||
this.levels = new byte[graphicCount];
|
||||
System.arraycopy(levels, start, this.levels, 0, graphicCount);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSimple() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
|
||||
throw new InternalError("do not call if isSimple returns false");
|
||||
}
|
||||
|
||||
public Rectangle2D handleGetVisualBounds() {
|
||||
|
||||
Rectangle2D bounds = graphic.getBounds();
|
||||
|
||||
float width = (float) bounds.getWidth() +
|
||||
graphicAdvance * (graphicCount-1);
|
||||
|
||||
return new Rectangle2D.Float((float) bounds.getX(),
|
||||
(float) bounds.getY(),
|
||||
width,
|
||||
(float) bounds.getHeight());
|
||||
}
|
||||
|
||||
public CoreMetrics getCoreMetrics() {
|
||||
return cm;
|
||||
}
|
||||
|
||||
public static CoreMetrics createCoreMetrics(GraphicAttribute graphic) {
|
||||
return new CoreMetrics(graphic.getAscent(),
|
||||
graphic.getDescent(),
|
||||
GRAPHIC_LEADING,
|
||||
graphic.getAscent() + graphic.getDescent() + GRAPHIC_LEADING,
|
||||
graphic.getAlignment(),
|
||||
new float[] { 0, -graphic.getAscent() / 2, -graphic.getAscent() },
|
||||
-graphic.getAscent() / 2,
|
||||
graphic.getAscent() / 12,
|
||||
graphic.getDescent() / 3,
|
||||
graphic.getAscent() / 12,
|
||||
0, // ss offset
|
||||
0); // italic angle -- need api for this
|
||||
}
|
||||
|
||||
public float getItalicAngle() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Rectangle2D getVisualBounds() {
|
||||
|
||||
if (visualBounds == null) {
|
||||
visualBounds = decorator.getVisualBounds(this);
|
||||
}
|
||||
Rectangle2D.Float bounds = new Rectangle2D.Float();
|
||||
bounds.setRect(visualBounds);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public Shape handleGetOutline(float x, float y) {
|
||||
double[] matrix = { 1, 0, 0, 1, x, y };
|
||||
|
||||
if (graphicCount == 1) {
|
||||
AffineTransform tx = new AffineTransform(matrix);
|
||||
return graphic.getOutline(tx);
|
||||
}
|
||||
|
||||
GeneralPath gp = new GeneralPath();
|
||||
for (int i = 0; i < graphicCount; ++i) {
|
||||
AffineTransform tx = new AffineTransform(matrix);
|
||||
gp.append(graphic.getOutline(tx), false);
|
||||
matrix[4] += graphicAdvance;
|
||||
}
|
||||
|
||||
return gp;
|
||||
}
|
||||
|
||||
public AffineTransform getBaselineTransform() {
|
||||
return baseTx;
|
||||
}
|
||||
|
||||
public Shape getOutline(float x, float y) {
|
||||
|
||||
return decorator.getOutline(this, x, y);
|
||||
}
|
||||
|
||||
public void handleDraw(Graphics2D g2d, float x, float y) {
|
||||
|
||||
for (int i=0; i < graphicCount; i++) {
|
||||
|
||||
graphic.draw(g2d, x, y);
|
||||
x += graphicAdvance;
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(Graphics2D g2d, float x, float y) {
|
||||
|
||||
decorator.drawTextAndDecorations(this, g2d, x, y);
|
||||
}
|
||||
|
||||
public Rectangle2D getCharVisualBounds(int index) {
|
||||
|
||||
return decorator.getCharVisualBounds(this, index);
|
||||
}
|
||||
|
||||
public int getNumCharacters() {
|
||||
|
||||
return graphicCount;
|
||||
}
|
||||
|
||||
public float getCharX(int index) {
|
||||
|
||||
int visIndex = charsLtoV==null? index : charsLtoV[index];
|
||||
return graphicAdvance * visIndex;
|
||||
}
|
||||
|
||||
public float getCharY(int index) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getCharAdvance(int index) {
|
||||
|
||||
return graphicAdvance;
|
||||
}
|
||||
|
||||
public boolean caretAtOffsetIsValid(int index) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Rectangle2D handleGetCharVisualBounds(int index) {
|
||||
|
||||
Rectangle2D bounds = graphic.getBounds();
|
||||
// don't modify their rectangle, just in case they don't copy
|
||||
|
||||
Rectangle2D.Float charBounds = new Rectangle2D.Float();
|
||||
charBounds.setRect(bounds);
|
||||
charBounds.x += graphicAdvance * index;
|
||||
|
||||
return charBounds;
|
||||
}
|
||||
|
||||
// measures characters in context, in logical order
|
||||
public int getLineBreakIndex(int start, float width) {
|
||||
|
||||
int index = (int) (width / graphicAdvance);
|
||||
if (index > graphicCount - start) {
|
||||
index = graphicCount - start;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// measures characters in context, in logical order
|
||||
public float getAdvanceBetween(int start, int limit) {
|
||||
|
||||
return graphicAdvance * (limit - start);
|
||||
}
|
||||
|
||||
public Rectangle2D getLogicalBounds() {
|
||||
|
||||
float left = 0;
|
||||
float top = -cm.ascent;
|
||||
float width = graphicAdvance * graphicCount;
|
||||
float height = cm.descent - top;
|
||||
|
||||
return new Rectangle2D.Float(left, top, width, height);
|
||||
}
|
||||
|
||||
public float getAdvance() {
|
||||
return graphicAdvance * graphicCount;
|
||||
}
|
||||
|
||||
public Rectangle2D getItalicBounds() {
|
||||
return getLogicalBounds();
|
||||
}
|
||||
|
||||
public TextLineComponent getSubset(int start, int limit, int dir) {
|
||||
|
||||
if (start < 0 || limit > graphicCount || start >= limit) {
|
||||
throw new IllegalArgumentException("Invalid range. start="
|
||||
+start+"; limit="+limit);
|
||||
}
|
||||
|
||||
if (start == 0 && limit == graphicCount && dir == UNCHANGED) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new GraphicComponent(this, start, limit, dir);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return "[graphic=" + graphic + ":count=" + getNumCharacters() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of justification records this uses.
|
||||
*/
|
||||
public int getNumJustificationInfos() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return GlyphJustificationInfo objects for the characters between
|
||||
* charStart and charLimit, starting at offset infoStart. Infos
|
||||
* will be in visual order. All positions between infoStart and
|
||||
* getNumJustificationInfos will be set. If a position corresponds
|
||||
* to a character outside the provided range, it is set to null.
|
||||
*/
|
||||
public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply deltas to the data in this component, starting at offset
|
||||
* deltaStart, and return the new component. There are two floats
|
||||
* for each justification info, for a total of 2 * getNumJustificationInfos.
|
||||
* The first delta is the left adjustment, the second is the right
|
||||
* adjustment.
|
||||
* <p>
|
||||
* If flags[0] is true on entry, rejustification is allowed. If
|
||||
* the new component requires rejustification (ligatures were
|
||||
* formed or split), flags[0] will be set on exit.
|
||||
*/
|
||||
public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
998
jdkSrc/jdk8/sun/font/LayoutPathImpl.java
Normal file
998
jdkSrc/jdk8/sun/font/LayoutPathImpl.java
Normal file
@@ -0,0 +1,998 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 2005, All Rights Reserved.
|
||||
*/
|
||||
package sun.font;
|
||||
|
||||
//
|
||||
// This is the 'simple' mapping implementation. It does things the most
|
||||
// straightforward way even if that is a bit slow. It won't
|
||||
// handle complex paths efficiently, and doesn't handle closed paths.
|
||||
//
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.LayoutPath;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.Formatter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static java.awt.geom.PathIterator.*;
|
||||
import static java.lang.Math.abs;
|
||||
import static java.lang.Math.sqrt;
|
||||
|
||||
public abstract class LayoutPathImpl extends LayoutPath {
|
||||
|
||||
//
|
||||
// Convenience APIs
|
||||
//
|
||||
|
||||
public Point2D pointToPath(double x, double y) {
|
||||
Point2D.Double pt = new Point2D.Double(x, y);
|
||||
pointToPath(pt, pt);
|
||||
return pt;
|
||||
}
|
||||
|
||||
public Point2D pathToPoint(double a, double o, boolean preceding) {
|
||||
Point2D.Double pt = new Point2D.Double(a, o);
|
||||
pathToPoint(pt, preceding, pt);
|
||||
return pt;
|
||||
}
|
||||
|
||||
public void pointToPath(double x, double y, Point2D pt) {
|
||||
pt.setLocation(x, y);
|
||||
pointToPath(pt, pt);
|
||||
}
|
||||
|
||||
public void pathToPoint(double a, double o, boolean preceding, Point2D pt) {
|
||||
pt.setLocation(a, o);
|
||||
pathToPoint(pt, preceding, pt);
|
||||
}
|
||||
|
||||
//
|
||||
// extra utility APIs
|
||||
//
|
||||
|
||||
public abstract double start();
|
||||
public abstract double end();
|
||||
public abstract double length();
|
||||
public abstract Shape mapShape(Shape s);
|
||||
|
||||
//
|
||||
// debugging flags
|
||||
//
|
||||
|
||||
private static final boolean LOGMAP = false;
|
||||
private static final Formatter LOG = new Formatter(System.out);
|
||||
|
||||
/**
|
||||
* Indicate how positions past the start and limit of the
|
||||
* path are treated. PINNED adjusts these positions so
|
||||
* as to be within start and limit. EXTENDED ignores the
|
||||
* start and limit and effectively extends the first and
|
||||
* last segments of the path 'infinitely'. CLOSED wraps
|
||||
* positions around the ends of the path.
|
||||
*/
|
||||
public static enum EndType {
|
||||
PINNED, EXTENDED, CLOSED;
|
||||
public boolean isPinned() { return this == PINNED; }
|
||||
public boolean isExtended() { return this == EXTENDED; }
|
||||
public boolean isClosed() { return this == CLOSED; }
|
||||
};
|
||||
|
||||
//
|
||||
// Top level construction.
|
||||
//
|
||||
|
||||
/**
|
||||
* Return a path representing the path from the origin through the points in order.
|
||||
*/
|
||||
public static LayoutPathImpl getPath(EndType etype, double ... coords) {
|
||||
if ((coords.length & 0x1) != 0) {
|
||||
throw new IllegalArgumentException("odd number of points not allowed");
|
||||
}
|
||||
|
||||
return SegmentPath.get(etype, coords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to build a SegmentPath. This takes the data and preanalyzes it for
|
||||
* information that the SegmentPath needs, then constructs a SegmentPath
|
||||
* from that. Mainly, this lets SegmentPath cache the lengths along
|
||||
* the path to each line segment, and so avoid calculating them over and over.
|
||||
*/
|
||||
public static final class SegmentPathBuilder {
|
||||
private double[] data;
|
||||
private int w;
|
||||
private double px;
|
||||
private double py;
|
||||
private double a;
|
||||
private boolean pconnect;
|
||||
|
||||
/**
|
||||
* Construct a SegmentPathBuilder.
|
||||
*/
|
||||
public SegmentPathBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the builder for a new path. Datalen is a hint of how many
|
||||
* points will be in the path, and the working buffer will be sized
|
||||
* to accommodate at least this number of points. If datalen is zero,
|
||||
* the working buffer is freed (it will be allocated on first use).
|
||||
*/
|
||||
public void reset(int datalen) {
|
||||
if (data == null || datalen > data.length) {
|
||||
data = new double[datalen];
|
||||
} else if (datalen == 0) {
|
||||
data = null;
|
||||
}
|
||||
w = 0;
|
||||
px = py = 0;
|
||||
pconnect = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically build from a list of points represented by pairs of
|
||||
* doubles. Initial advance is zero.
|
||||
*/
|
||||
public SegmentPath build(EndType etype, double... pts) {
|
||||
assert(pts.length % 2 == 0);
|
||||
|
||||
reset(pts.length / 2 * 3);
|
||||
|
||||
for (int i = 0; i < pts.length; i += 2) {
|
||||
nextPoint(pts[i], pts[i+1], i != 0);
|
||||
}
|
||||
|
||||
return complete(etype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to a new point. If there is no data, this will become the
|
||||
* first point. If there is data, and the previous call was a lineTo, this
|
||||
* point is checked against the previous point, and if different, this
|
||||
* starts a new segment at the same advance as the end of the last
|
||||
* segment. If there is data, and the previous call was a moveTo, this
|
||||
* replaces the point used for that previous call.
|
||||
*
|
||||
* Calling this is optional, lineTo will suffice and the initial point
|
||||
* will be set to 0, 0.
|
||||
*/
|
||||
public void moveTo(double x, double y) {
|
||||
nextPoint(x, y, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a new point. If there is no data, the previous point
|
||||
* is presumed to be 0, 0. This point is checked against
|
||||
* the previous point, and if different, this point is added to
|
||||
* the path and the advance extended. If this point is the same as the
|
||||
* previous point, the path remains unchanged.
|
||||
*/
|
||||
public void lineTo(double x, double y) {
|
||||
nextPoint(x, y, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new point, and increment advance if connect is true.
|
||||
*
|
||||
* This automatically rejects duplicate points and multiple disconnected points.
|
||||
*/
|
||||
private void nextPoint(double x, double y, boolean connect) {
|
||||
|
||||
// if zero length move or line, ignore
|
||||
if (x == px && y == py) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (w == 0) { // this is the first point, make sure we have space
|
||||
if (data == null) {
|
||||
data = new double[6];
|
||||
}
|
||||
if (connect) {
|
||||
w = 3; // default first point to 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
// if multiple disconnected move, just update position, leave advance alone
|
||||
if (w != 0 && !connect && !pconnect) {
|
||||
data[w-3] = px = x;
|
||||
data[w-2] = py = y;
|
||||
return;
|
||||
}
|
||||
|
||||
// grow data to deal with new point
|
||||
if (w == data.length) {
|
||||
double[] t = new double[w * 2];
|
||||
System.arraycopy(data, 0, t, 0, w);
|
||||
data = t;
|
||||
}
|
||||
|
||||
if (connect) {
|
||||
double dx = x - px;
|
||||
double dy = y - py;
|
||||
a += sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
// update data
|
||||
data[w++] = x;
|
||||
data[w++] = y;
|
||||
data[w++] = a;
|
||||
|
||||
// update state
|
||||
px = x;
|
||||
py = y;
|
||||
pconnect = connect;
|
||||
}
|
||||
|
||||
public SegmentPath complete() {
|
||||
return complete(EndType.EXTENDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete building a SegmentPath. Once this is called, the builder is restored
|
||||
* to its initial state and information about the previous path is released. The
|
||||
* end type indicates whether to treat the path as closed, extended, or pinned.
|
||||
*/
|
||||
public SegmentPath complete(EndType etype) {
|
||||
SegmentPath result;
|
||||
|
||||
if (data == null || w < 6) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (w == data.length) {
|
||||
result = new SegmentPath(data, etype);
|
||||
reset(0); // releases pointer to data
|
||||
} else {
|
||||
double[] dataToAdopt = new double[w];
|
||||
System.arraycopy(data, 0, dataToAdopt, 0, w);
|
||||
result = new SegmentPath(dataToAdopt, etype);
|
||||
reset(2); // reuses data, since we held on to it
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a path built from segments. Each segment is
|
||||
* represented by a triple: x, y, and cumulative advance.
|
||||
* These represent the end point of the segment. The start
|
||||
* point of the first segment is represented by the triple
|
||||
* at position 0.
|
||||
*
|
||||
* The path might have breaks in it, e.g. it is not connected.
|
||||
* These will be represented by pairs of triplets that share the
|
||||
* same advance.
|
||||
*
|
||||
* The path might be extended, pinned, or closed. If extended,
|
||||
* the initial and final segments are considered to extend
|
||||
* 'indefinitely' past the bounds of the advance. If pinned,
|
||||
* they end at the bounds of the advance. If closed,
|
||||
* advances before the start or after the end 'wrap around' the
|
||||
* path.
|
||||
*
|
||||
* The start of the path is the initial triple. This provides
|
||||
* the nominal advance at the given x, y position (typically
|
||||
* zero). The end of the path is the final triple. This provides
|
||||
* the advance at the end, the total length of the path is
|
||||
* thus the ending advance minus the starting advance.
|
||||
*
|
||||
* Note: We might want to cache more auxiliary data than the
|
||||
* advance, but this seems adequate for now.
|
||||
*/
|
||||
public static final class SegmentPath extends LayoutPathImpl {
|
||||
private double[] data; // triplets x, y, a
|
||||
EndType etype;
|
||||
|
||||
public static SegmentPath get(EndType etype, double... pts) {
|
||||
return new SegmentPathBuilder().build(etype, pts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal, use SegmentPathBuilder or one of the static
|
||||
* helper functions to construct a SegmentPath.
|
||||
*/
|
||||
SegmentPath(double[] data, EndType etype) {
|
||||
this.data = data;
|
||||
this.etype = etype;
|
||||
}
|
||||
|
||||
//
|
||||
// LayoutPath API
|
||||
//
|
||||
|
||||
public void pathToPoint(Point2D location, boolean preceding, Point2D point) {
|
||||
locateAndGetIndex(location, preceding, point);
|
||||
}
|
||||
|
||||
// the path consists of line segments, which i'll call
|
||||
// 'path vectors'. call each run of path vectors a 'path segment'.
|
||||
// no path vector in a path segment is zero length (in the
|
||||
// data, such vectors start a new path segment).
|
||||
//
|
||||
// for each path segment...
|
||||
//
|
||||
// for each path vector...
|
||||
//
|
||||
// we look at the dot product of the path vector and the vector from the
|
||||
// origin of the path vector to the test point. if <0 (case
|
||||
// A), the projection of the test point is before the start of
|
||||
// the path vector. if > the square of the length of the path vector
|
||||
// (case B), the projection is past the end point of the
|
||||
// path vector. otherwise (case C), it lies on the path vector.
|
||||
// determine the closeset point on the path vector. if case A, it
|
||||
// is the start of the path vector. if case B and this is the last
|
||||
// path vector in the path segment, it is the end of the path vector. If
|
||||
// case C, it is the projection onto the path vector. Otherwise
|
||||
// there is no closest point.
|
||||
//
|
||||
// if we have a closest point, compare the distance from it to
|
||||
// the test point against our current closest distance.
|
||||
// (culling should be fast, currently i am using distance
|
||||
// squared, but there's probably better ways). if we're
|
||||
// closer, save the new point as the current closest point,
|
||||
// and record the path vector index so we can determine the final
|
||||
// info if this turns out to be the closest point in the end.
|
||||
//
|
||||
// after we have processed all the segments we will have
|
||||
// tested each path vector and each endpoint. if our point is not on
|
||||
// an endpoint, we're done; we can compute the position and
|
||||
// offset again, or if we saved it off we can just use it. if
|
||||
// we're on an endpoint we need to see which path vector we should
|
||||
// associate with. if we're at the start or end of a path segment,
|
||||
// we're done-- the first or last vector of the segment is the
|
||||
// one we associate with. we project against that vector to
|
||||
// get the offset, and pin to that vector to get the length.
|
||||
//
|
||||
// otherwise, we compute the information as follows. if the
|
||||
// dot product (see above) with the following vector is zero,
|
||||
// we associate with that vector. otherwise, if the dot
|
||||
// product with the previous vector is zero, we associate with
|
||||
// that vector. otherwise we're beyond the end of the
|
||||
// previous vector and before the start of the current vector.
|
||||
// we project against both vectors and get the distance from
|
||||
// the test point to the projection (this will be the offset).
|
||||
// if they are the same, we take the following vector.
|
||||
// otherwise use the vector from which the test point is the
|
||||
// _farthest_ (this is because the point lies most clearly in
|
||||
// the half of the plane defined by extending that vector).
|
||||
//
|
||||
// the returned position is the path length to the (possibly
|
||||
// pinned) point, the offset is the projection onto the line
|
||||
// along the vector, and we have a boolean flag which if false
|
||||
// indicates that we associate with the previous vector at a
|
||||
// junction (which is necessary when projecting such a
|
||||
// location back to a point).
|
||||
|
||||
public boolean pointToPath(Point2D pt, Point2D result) {
|
||||
double x = pt.getX(); // test point
|
||||
double y = pt.getY();
|
||||
|
||||
double bx = data[0]; // previous point
|
||||
double by = data[1];
|
||||
double bl = data[2];
|
||||
|
||||
// start with defaults
|
||||
double cd2 = Double.MAX_VALUE; // current best distance from path, squared
|
||||
double cx = 0; // current best x
|
||||
double cy = 0; // current best y
|
||||
double cl = 0; // current best position along path
|
||||
int ci = 0; // current best index into data
|
||||
|
||||
for (int i = 3; i < data.length; i += 3) {
|
||||
double nx = data[i]; // current end point
|
||||
double ny = data[i+1];
|
||||
double nl = data[i+2];
|
||||
|
||||
double dx = nx - bx; // vector from previous to current
|
||||
double dy = ny - by;
|
||||
double dl = nl - bl;
|
||||
|
||||
double px = x - bx; // vector from previous to test point
|
||||
double py = y - by;
|
||||
|
||||
// determine sign of dot product of vectors from bx, by
|
||||
// if < 0, we're before the start of this vector
|
||||
|
||||
double dot = dx * px + dy * py; // dot product
|
||||
double vcx, vcy, vcl; // hold closest point on vector as x, y, l
|
||||
int vi; // hold index of line, is data.length if last point on path
|
||||
do { // use break below, lets us avoid initializing vcx, vcy...
|
||||
if (dl == 0 || // moveto, or
|
||||
(dot < 0 && // before path vector and
|
||||
(!etype.isExtended() ||
|
||||
i != 3))) { // closest point is start of vector
|
||||
vcx = bx;
|
||||
vcy = by;
|
||||
vcl = bl;
|
||||
vi = i;
|
||||
} else {
|
||||
double l2 = dl * dl; // aka dx * dx + dy * dy, square of length
|
||||
if (dot <= l2 || // closest point is not past end of vector, or
|
||||
(etype.isExtended() && // we're extended and at the last segment
|
||||
i == data.length - 3)) {
|
||||
double p = dot / l2; // get parametric along segment
|
||||
vcx = bx + p * dx; // compute closest point
|
||||
vcy = by + p * dy;
|
||||
vcl = bl + p * dl;
|
||||
vi = i;
|
||||
} else {
|
||||
if (i == data.length - 3) {
|
||||
vcx = nx; // special case, always test last point
|
||||
vcy = ny;
|
||||
vcl = nl;
|
||||
vi = data.length;
|
||||
} else {
|
||||
break; // typical case, skip point, we'll pick it up next iteration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double tdx = x - vcx; // compute distance from (usually pinned) projection to test point
|
||||
double tdy = y - vcy;
|
||||
double td2 = tdx * tdx + tdy * tdy;
|
||||
if (td2 <= cd2) { // new closest point, record info on it
|
||||
cd2 = td2;
|
||||
cx = vcx;
|
||||
cy = vcy;
|
||||
cl = vcl;
|
||||
ci = vi;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
bx = nx;
|
||||
by = ny;
|
||||
bl = nl;
|
||||
}
|
||||
|
||||
// we have our closest point, get the info
|
||||
bx = data[ci-3];
|
||||
by = data[ci-2];
|
||||
if (cx != bx || cy != by) { // not on endpoint, no need to resolve
|
||||
double nx = data[ci];
|
||||
double ny = data[ci+1];
|
||||
double co = sqrt(cd2); // have a true perpendicular, so can use distance
|
||||
if ((x-cx)*(ny-by) > (y-cy)*(nx-bx)) {
|
||||
co = -co; // determine sign of offset
|
||||
}
|
||||
result.setLocation(cl, co);
|
||||
return false;
|
||||
} else { // on endpoint, we need to resolve which segment
|
||||
boolean havePrev = ci != 3 && data[ci-1] != data[ci-4];
|
||||
boolean haveFoll = ci != data.length && data[ci-1] != data[ci+2];
|
||||
boolean doExtend = etype.isExtended() && (ci == 3 || ci == data.length);
|
||||
if (havePrev && haveFoll) {
|
||||
Point2D.Double pp = new Point2D.Double(x, y);
|
||||
calcoffset(ci - 3, doExtend, pp);
|
||||
Point2D.Double fp = new Point2D.Double(x, y);
|
||||
calcoffset(ci, doExtend, fp);
|
||||
if (abs(pp.y) > abs(fp.y)) {
|
||||
result.setLocation(pp);
|
||||
return true; // associate with previous
|
||||
} else {
|
||||
result.setLocation(fp);
|
||||
return false; // associate with following
|
||||
}
|
||||
} else if (havePrev) {
|
||||
result.setLocation(x, y);
|
||||
calcoffset(ci - 3, doExtend, result);
|
||||
return true;
|
||||
} else {
|
||||
result.setLocation(x, y);
|
||||
calcoffset(ci, doExtend, result);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location of the point passed in result as mapped to the
|
||||
* line indicated by index. If doExtend is true, extend the
|
||||
* x value without pinning to the ends of the line.
|
||||
* this assumes that index is valid and references a line that has
|
||||
* non-zero length.
|
||||
*/
|
||||
private void calcoffset(int index, boolean doExtend, Point2D result) {
|
||||
double bx = data[index-3];
|
||||
double by = data[index-2];
|
||||
double px = result.getX() - bx;
|
||||
double py = result.getY() - by;
|
||||
double dx = data[index] - bx;
|
||||
double dy = data[index+1] - by;
|
||||
double l = data[index+2] - data[index - 1];
|
||||
|
||||
// rx = A dot B / |B|
|
||||
// ry = A dot invB / |B|
|
||||
double rx = (px * dx + py * dy) / l;
|
||||
double ry = (px * -dy + py * dx) / l;
|
||||
if (!doExtend) {
|
||||
if (rx < 0) rx = 0;
|
||||
else if (rx > l) rx = l;
|
||||
}
|
||||
rx += data[index-1];
|
||||
result.setLocation(rx, ry);
|
||||
}
|
||||
|
||||
//
|
||||
// LayoutPathImpl API
|
||||
//
|
||||
|
||||
public Shape mapShape(Shape s) {
|
||||
return new Mapper().mapShape(s);
|
||||
}
|
||||
|
||||
public double start() {
|
||||
return data[2];
|
||||
}
|
||||
|
||||
public double end() {
|
||||
return data[data.length - 1];
|
||||
}
|
||||
|
||||
public double length() {
|
||||
return data[data.length-1] - data[2];
|
||||
}
|
||||
|
||||
//
|
||||
// Utilities
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the 'modulus' of an advance on a closed path.
|
||||
*/
|
||||
private double getClosedAdvance(double a, boolean preceding) {
|
||||
if (etype.isClosed()) {
|
||||
a -= data[2];
|
||||
int count = (int)(a/length());
|
||||
a -= count * length();
|
||||
if (a < 0 || (a == 0 && preceding)) {
|
||||
a += length();
|
||||
|
||||
}
|
||||
a += data[2];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of the segment associated with advance. This
|
||||
* points to the start of the triple and is a multiple of 3 between
|
||||
* 3 and data.length-3 inclusive. It never points to a 'moveto' triple.
|
||||
*
|
||||
* If the path is closed, 'a' is mapped to
|
||||
* a value between the start and end of the path, inclusive.
|
||||
* If preceding is true, and 'a' lies on a segment boundary,
|
||||
* return the index of the preceding segment, else return the index
|
||||
* of the current segment (if it is not a moveto segment) otherwise
|
||||
* the following segment (which is never a moveto segment).
|
||||
*
|
||||
* Note: if the path is not closed, the advance might not actually
|
||||
* lie on the returned segment-- it might be before the first, or
|
||||
* after the last. The first or last segment (as appropriate)
|
||||
* will be returned in this case.
|
||||
*/
|
||||
private int getSegmentIndexForAdvance(double a, boolean preceding) {
|
||||
// must have local advance
|
||||
a = getClosedAdvance(a, preceding);
|
||||
|
||||
// note we must avoid 'moveto' segments. the first segment is
|
||||
// always a moveto segment, so we always skip it.
|
||||
int i, lim;
|
||||
for (i = 5, lim = data.length-1; i < lim; i += 3) {
|
||||
double v = data[i];
|
||||
if (a < v || (a == v && preceding)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i-2; // adjust to start of segment
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a location based on the provided segment, returning in pt.
|
||||
* Seg must be a valid 'lineto' segment. Note: if the path is
|
||||
* closed, x must be within the start and end of the path.
|
||||
*/
|
||||
private void map(int seg, double a, double o, Point2D pt) {
|
||||
double dx = data[seg] - data[seg-3];
|
||||
double dy = data[seg+1] - data[seg-2];
|
||||
double dl = data[seg+2] - data[seg-1];
|
||||
|
||||
double ux = dx/dl; // could cache these, but is it worth it?
|
||||
double uy = dy/dl;
|
||||
|
||||
a -= data[seg-1];
|
||||
|
||||
pt.setLocation(data[seg-3] + a * ux - o * uy,
|
||||
data[seg-2] + a * uy + o * ux);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the point, and return the segment index.
|
||||
*/
|
||||
private int locateAndGetIndex(Point2D loc, boolean preceding, Point2D result) {
|
||||
double a = loc.getX();
|
||||
double o = loc.getY();
|
||||
int seg = getSegmentIndexForAdvance(a, preceding);
|
||||
map(seg, a, o, result);
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
//
|
||||
// Mapping classes.
|
||||
// Map the path onto each path segment.
|
||||
// Record points where the advance 'enters' and 'exits' the path segment, and connect successive
|
||||
// points when appropriate.
|
||||
//
|
||||
|
||||
/**
|
||||
* This represents a line segment from the iterator. Each target segment will
|
||||
* interpret it, and since this process needs slope along the line
|
||||
* segment, this lets us compute it once and pass it around easily.
|
||||
*/
|
||||
class LineInfo {
|
||||
double sx, sy; // start
|
||||
double lx, ly; // limit
|
||||
double m; // slope dy/dx
|
||||
|
||||
/**
|
||||
* Set the lineinfo to this line
|
||||
*/
|
||||
void set(double sx, double sy, double lx, double ly) {
|
||||
this.sx = sx;
|
||||
this.sy = sy;
|
||||
this.lx = lx;
|
||||
this.ly = ly;
|
||||
double dx = lx - sx;
|
||||
if (dx == 0) {
|
||||
m = 0; // we'll check for this elsewhere
|
||||
} else {
|
||||
double dy = ly - sy;
|
||||
m = dy / dx;
|
||||
}
|
||||
}
|
||||
|
||||
void set(LineInfo rhs) {
|
||||
this.sx = rhs.sx;
|
||||
this.sy = rhs.sy;
|
||||
this.lx = rhs.lx;
|
||||
this.ly = rhs.ly;
|
||||
this.m = rhs.m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we intersect the infinitely tall rectangle with
|
||||
* lo <= x < hi. If we do, also return the pinned portion of ourselves in
|
||||
* result.
|
||||
*/
|
||||
boolean pin(double lo, double hi, LineInfo result) {
|
||||
result.set(this);
|
||||
if (lx >= sx) {
|
||||
if (sx < hi && lx >= lo) {
|
||||
if (sx < lo) {
|
||||
if (m != 0) result.sy = sy + m * (lo - sx);
|
||||
result.sx = lo;
|
||||
}
|
||||
if (lx > hi) {
|
||||
if (m != 0) result.ly = ly + m * (hi - lx);
|
||||
result.lx = hi;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (lx < hi && sx >= lo) {
|
||||
if (lx < lo) {
|
||||
if (m != 0) result.ly = ly + m * (lo - lx);
|
||||
result.lx = lo;
|
||||
}
|
||||
if (sx > hi) {
|
||||
if (m != 0) result.sy = sy + m * (hi - sx);
|
||||
result.sx = hi;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we intersect the segment at ix. This takes
|
||||
* the path end type into account and computes the relevant
|
||||
* parameters to pass to pin(double, double, LineInfo).
|
||||
*/
|
||||
boolean pin(int ix, LineInfo result) {
|
||||
double lo = data[ix-1];
|
||||
double hi = data[ix+2];
|
||||
switch (SegmentPath.this.etype) {
|
||||
case PINNED:
|
||||
break;
|
||||
case EXTENDED:
|
||||
if (ix == 3) lo = Double.NEGATIVE_INFINITY;
|
||||
if (ix == data.length - 3) hi = Double.POSITIVE_INFINITY;
|
||||
break;
|
||||
case CLOSED:
|
||||
// not implemented
|
||||
break;
|
||||
}
|
||||
|
||||
return pin(lo, hi, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Each segment will construct its own general path, mapping the provided lines
|
||||
* into its own simple space.
|
||||
*/
|
||||
class Segment {
|
||||
final int ix; // index into data array for this segment
|
||||
final double ux, uy; // unit vector
|
||||
|
||||
final LineInfo temp; // working line info
|
||||
|
||||
boolean broken; // true if a moveto has occurred since we last added to our path
|
||||
double cx, cy; // last point in gp
|
||||
GeneralPath gp; // path built for this segment
|
||||
|
||||
Segment(int ix) {
|
||||
this.ix = ix;
|
||||
double len = data[ix+2] - data[ix-1];
|
||||
this.ux = (data[ix] - data[ix-3]) / len;
|
||||
this.uy = (data[ix+1] - data[ix-2]) / len;
|
||||
this.temp = new LineInfo();
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (LOGMAP) LOG.format("s(%d) init\n", ix);
|
||||
broken = true;
|
||||
cx = cy = Double.MIN_VALUE;
|
||||
this.gp = new GeneralPath();
|
||||
}
|
||||
|
||||
void move() {
|
||||
if (LOGMAP) LOG.format("s(%d) move\n", ix);
|
||||
broken = true;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (!broken) {
|
||||
if (LOGMAP) LOG.format("s(%d) close\n[cp]\n", ix);
|
||||
gp.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
void line(LineInfo li) {
|
||||
if (LOGMAP) LOG.format("s(%d) line %g, %g to %g, %g\n", ix, li.sx, li.sy, li.lx, li.ly);
|
||||
|
||||
if (li.pin(ix, temp)) {
|
||||
if (LOGMAP) LOG.format("pin: %g, %g to %g, %g\n", temp.sx, temp.sy, temp.lx, temp.ly);
|
||||
|
||||
temp.sx -= data[ix-1];
|
||||
double sx = data[ix-3] + temp.sx * ux - temp.sy * uy;
|
||||
double sy = data[ix-2] + temp.sx * uy + temp.sy * ux;
|
||||
temp.lx -= data[ix-1];
|
||||
double lx = data[ix-3] + temp.lx * ux - temp.ly * uy;
|
||||
double ly = data[ix-2] + temp.lx * uy + temp.ly * ux;
|
||||
|
||||
if (LOGMAP) LOG.format("points: %g, %g to %g, %g\n", sx, sy, lx, ly);
|
||||
|
||||
if (sx != cx || sy != cy) {
|
||||
if (broken) {
|
||||
if (LOGMAP) LOG.format("[mt %g, %g]\n", sx, sy);
|
||||
gp.moveTo((float)sx, (float)sy);
|
||||
} else {
|
||||
if (LOGMAP) LOG.format("[lt %g, %g]\n", sx, sy);
|
||||
gp.lineTo((float)sx, (float)sy);
|
||||
}
|
||||
}
|
||||
if (LOGMAP) LOG.format("[lt %g, %g]\n", lx, ly);
|
||||
gp.lineTo((float)lx, (float)ly);
|
||||
|
||||
broken = false;
|
||||
cx = lx;
|
||||
cy = ly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Mapper {
|
||||
final LineInfo li; // working line info
|
||||
final ArrayList<Segment> segments; // cache additional data on segments, working objects
|
||||
final Point2D.Double mpt; // last moveto source point
|
||||
final Point2D.Double cpt; // current source point
|
||||
boolean haveMT; // true when last op was a moveto
|
||||
|
||||
Mapper() {
|
||||
li = new LineInfo();
|
||||
segments = new ArrayList<Segment>();
|
||||
for (int i = 3; i < data.length; i += 3) {
|
||||
if (data[i+2] != data[i-1]) { // a new segment
|
||||
segments.add(new Segment(i));
|
||||
}
|
||||
}
|
||||
|
||||
mpt = new Point2D.Double();
|
||||
cpt = new Point2D.Double();
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (LOGMAP) LOG.format("init\n");
|
||||
haveMT = false;
|
||||
for (Segment s: segments) {
|
||||
s.init();
|
||||
}
|
||||
}
|
||||
|
||||
void moveTo(double x, double y) {
|
||||
if (LOGMAP) LOG.format("moveto %g, %g\n", x, y);
|
||||
mpt.x = x;
|
||||
mpt.y = y;
|
||||
haveMT = true;
|
||||
}
|
||||
|
||||
void lineTo(double x, double y) {
|
||||
if (LOGMAP) LOG.format("lineto %g, %g\n", x, y);
|
||||
|
||||
if (haveMT) {
|
||||
// prepare previous point for no-op check
|
||||
cpt.x = mpt.x;
|
||||
cpt.y = mpt.y;
|
||||
}
|
||||
|
||||
if (x == cpt.x && y == cpt.y) {
|
||||
// lineto is a no-op
|
||||
return;
|
||||
}
|
||||
|
||||
if (haveMT) {
|
||||
// current point is the most recent moveto point
|
||||
haveMT = false;
|
||||
for (Segment s: segments) {
|
||||
s.move();
|
||||
}
|
||||
}
|
||||
|
||||
li.set(cpt.x, cpt.y, x, y);
|
||||
for (Segment s: segments) {
|
||||
s.line(li);
|
||||
}
|
||||
|
||||
cpt.x = x;
|
||||
cpt.y = y;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (LOGMAP) LOG.format("close\n");
|
||||
lineTo(mpt.x, mpt.y);
|
||||
for (Segment s: segments) {
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
public Shape mapShape(Shape s) {
|
||||
if (LOGMAP) LOG.format("mapshape on path: %s\n", LayoutPathImpl.SegmentPath.this);
|
||||
PathIterator pi = s.getPathIterator(null, 1); // cheap way to handle curves.
|
||||
|
||||
if (LOGMAP) LOG.format("start\n");
|
||||
init();
|
||||
|
||||
final double[] coords = new double[2];
|
||||
while (!pi.isDone()) {
|
||||
switch (pi.currentSegment(coords)) {
|
||||
case SEG_CLOSE: close(); break;
|
||||
case SEG_MOVETO: moveTo(coords[0], coords[1]); break;
|
||||
case SEG_LINETO: lineTo(coords[0], coords[1]); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
pi.next();
|
||||
}
|
||||
if (LOGMAP) LOG.format("finish\n\n");
|
||||
|
||||
GeneralPath gp = new GeneralPath();
|
||||
for (Segment seg: segments) {
|
||||
gp.append(seg.gp, false);
|
||||
}
|
||||
return gp;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// for debugging
|
||||
//
|
||||
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("{");
|
||||
b.append(etype.toString());
|
||||
b.append(" ");
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
if (i > 0) {
|
||||
b.append(",");
|
||||
}
|
||||
float x = ((int)(data[i] * 100))/100.0f;
|
||||
float y = ((int)(data[i+1] * 100))/100.0f;
|
||||
float l = ((int)(data[i+2] * 10))/10.0f;
|
||||
b.append("{");
|
||||
b.append(x);
|
||||
b.append(",");
|
||||
b.append(y);
|
||||
b.append(",");
|
||||
b.append(l);
|
||||
b.append("}");
|
||||
}
|
||||
b.append("}");
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class EmptyPath extends LayoutPathImpl {
|
||||
private AffineTransform tx;
|
||||
|
||||
public EmptyPath(AffineTransform tx) {
|
||||
this.tx = tx;
|
||||
}
|
||||
|
||||
public void pathToPoint(Point2D location, boolean preceding, Point2D point) {
|
||||
if (tx != null) {
|
||||
tx.transform(location, point);
|
||||
} else {
|
||||
point.setLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pointToPath(Point2D pt, Point2D result) {
|
||||
result.setLocation(pt);
|
||||
if (tx != null) {
|
||||
try {
|
||||
tx.inverseTransform(pt, result);
|
||||
}
|
||||
catch (NoninvertibleTransformException ex) {
|
||||
}
|
||||
}
|
||||
return result.getX() > 0;
|
||||
}
|
||||
|
||||
public double start() { return 0; }
|
||||
|
||||
public double end() { return 0; }
|
||||
|
||||
public double length() { return 0; }
|
||||
|
||||
public Shape mapShape(Shape s) {
|
||||
if (tx != null) {
|
||||
return tx.createTransformedShape(s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
jdkSrc/jdk8/sun/font/NativeFont.java
Normal file
107
jdkSrc/jdk8/sun/font/NativeFont.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/*
|
||||
* This class should never be invoked on the windows implementation
|
||||
* So the constructor throws a FontFormatException, which is caught
|
||||
* and the font is ignored.
|
||||
*/
|
||||
|
||||
public class NativeFont extends PhysicalFont {
|
||||
|
||||
/**
|
||||
* Verifies native font is accessible.
|
||||
* @throws FontFormatException - if the font can't be located.
|
||||
*/
|
||||
public NativeFont(String platName, boolean isBitmapDelegate)
|
||||
throws FontFormatException {
|
||||
|
||||
throw new FontFormatException("NativeFont not used on Windows");
|
||||
}
|
||||
|
||||
static boolean hasExternalBitmaps(String platName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public CharToGlyphMapper getMapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
PhysicalFont getDelegateFont() {
|
||||
return null;
|
||||
}
|
||||
|
||||
FontStrike createStrike(FontStrikeDesc desc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StrikeMetrics getFontMetrics(long pScalerContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public GeneralPath getGlyphOutline(long pScalerContext,
|
||||
int glyphCode,
|
||||
float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public GeneralPath getGlyphVectorOutline(long pScalerContext,
|
||||
int[] glyphs, int numGlyphs,
|
||||
float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
long getGlyphImage(long pScalerContext, int glyphCode) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
void getGlyphMetrics(long pScalerContext, int glyphCode,
|
||||
Point2D.Float metrics) {
|
||||
}
|
||||
|
||||
|
||||
float getGlyphAdvance(long pScalerContext, int glyphCode) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
|
||||
int glyphCode) {
|
||||
return new Rectangle2D.Float(0f, 0f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
86
jdkSrc/jdk8/sun/font/NativeStrike.java
Normal file
86
jdkSrc/jdk8/sun/font/NativeStrike.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public class NativeStrike extends PhysicalStrike {
|
||||
|
||||
NativeFont nativeFont;
|
||||
|
||||
NativeStrike(NativeFont nativeFont, FontStrikeDesc desc) {
|
||||
super(nativeFont, desc);
|
||||
|
||||
throw new RuntimeException("NativeFont not used on Windows");
|
||||
}
|
||||
|
||||
NativeStrike(NativeFont nativeFont, FontStrikeDesc desc,
|
||||
boolean nocache) {
|
||||
super(nativeFont, desc);
|
||||
|
||||
throw new RuntimeException("NativeFont not used on Windows");
|
||||
}
|
||||
|
||||
|
||||
void getGlyphImagePtrs(int[] glyphCodes, long[] images,int len) {
|
||||
}
|
||||
|
||||
long getGlyphImagePtr(int glyphCode) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
long getGlyphImagePtrNoCache(int glyphCode) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
void getGlyphImageBounds(int glyphcode,
|
||||
Point2D.Float pt,
|
||||
Rectangle result) {
|
||||
}
|
||||
|
||||
Point2D.Float getGlyphMetrics(int glyphCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
float getGlyphAdvance(int glyphCode) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
|
||||
return null;
|
||||
}
|
||||
GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
106
jdkSrc/jdk8/sun/font/NullFontScaler.java
Normal file
106
jdkSrc/jdk8/sun/font/NullFontScaler.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
class NullFontScaler extends FontScaler {
|
||||
NullFontScaler() {}
|
||||
|
||||
public NullFontScaler(Font2D font, int indexInCollection,
|
||||
boolean supportsCJK, int filesize) {}
|
||||
|
||||
StrikeMetrics getFontMetrics(long pScalerContext) {
|
||||
return new StrikeMetrics(0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
|
||||
0xf0,0xf0,0xf0,0xf0);
|
||||
}
|
||||
|
||||
float getGlyphAdvance(long pScalerContext, int glyphCode) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void getGlyphMetrics(long pScalerContext, int glyphCode,
|
||||
Point2D.Float metrics) {
|
||||
metrics.x = 0;
|
||||
metrics.y = 0;
|
||||
}
|
||||
|
||||
Rectangle2D.Float getGlyphOutlineBounds(long pContext, int glyphCode) {
|
||||
return new Rectangle2D.Float(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
GeneralPath getGlyphOutline(long pScalerContext, int glyphCode,
|
||||
float x, float y) {
|
||||
return new GeneralPath();
|
||||
}
|
||||
|
||||
GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs,
|
||||
int numGlyphs, float x, float y) {
|
||||
return new GeneralPath();
|
||||
}
|
||||
|
||||
long getLayoutTableCache() {return 0L;}
|
||||
|
||||
long createScalerContext(double[] matrix, int aa,
|
||||
int fm, float boldness, float italic, boolean disableHinting) {
|
||||
return getNullScalerContext();
|
||||
}
|
||||
|
||||
void invalidateScalerContext(long ppScalerContext) {
|
||||
//nothing to do
|
||||
}
|
||||
|
||||
int getNumGlyphs() throws FontScalerException {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int getMissingGlyphCode() throws FontScalerException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getGlyphCode(char charCode) throws FontScalerException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long getUnitsPerEm() {
|
||||
return 2048;
|
||||
}
|
||||
|
||||
Point2D.Float getGlyphPoint(long pScalerContext,
|
||||
int glyphCode, int ptNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Ideally NullFontScaler should not have native code.
|
||||
However, at this moment we need these methods to be native because:
|
||||
- glyph cache code assumes null pointers to GlyphInfo structures
|
||||
- FileFontStrike needs native context
|
||||
*/
|
||||
static native long getNullScalerContext();
|
||||
native long getGlyphImage(long pScalerContext, int glyphCode);
|
||||
}
|
||||
107
jdkSrc/jdk8/sun/font/PhysicalFont.java
Normal file
107
jdkSrc/jdk8/sun/font/PhysicalFont.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.FileInputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public abstract class PhysicalFont extends Font2D {
|
||||
|
||||
protected String platName;
|
||||
// nativeNames is a String or a (possibly null) String[].
|
||||
protected Object nativeNames;
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o != null && o.getClass() == this.getClass() &&
|
||||
((Font2D)o).fullName.equals(this.fullName));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return fullName.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the file (temporarily) and does basic verification.
|
||||
* Initializes the CMAP
|
||||
* @throws FontFormatException - if the font can't be opened
|
||||
* or fails verification, or there's no usable cmap
|
||||
*/
|
||||
PhysicalFont(String platname, Object nativeNames)
|
||||
throws FontFormatException {
|
||||
|
||||
handle = new Font2DHandle(this);
|
||||
this.platName = platname;
|
||||
this.nativeNames = nativeNames;
|
||||
}
|
||||
|
||||
protected PhysicalFont() {
|
||||
handle = new Font2DHandle(this);
|
||||
}
|
||||
|
||||
/* The following methods are delegated to the font by the strike
|
||||
* for physical fonts as the PhysicalFont holds a shared reference
|
||||
* to the native resource, so all invocations need to be directed
|
||||
* through a synchronization point. Implementations of these methods
|
||||
* will typically be "synchronized native"
|
||||
*/
|
||||
|
||||
Point2D.Float getGlyphPoint(long pScalerContext,
|
||||
int glyphCode, int ptNumber) {
|
||||
return new Point2D.Float();
|
||||
}
|
||||
|
||||
/* These 3 metrics methods should be implemented to return
|
||||
* values in user space.
|
||||
*/
|
||||
abstract StrikeMetrics getFontMetrics(long pScalerContext);
|
||||
|
||||
abstract float getGlyphAdvance(long pScalerContext, int glyphCode);
|
||||
|
||||
abstract void getGlyphMetrics(long pScalerContext, int glyphCode,
|
||||
Point2D.Float metrics);
|
||||
|
||||
abstract long getGlyphImage(long pScalerContext, int glyphCode);
|
||||
|
||||
/* These 3 outline methods should be implemented to return
|
||||
* values in device space. Callers need to be aware of this
|
||||
* as typically Java client code will need to have them in user space.
|
||||
*/
|
||||
abstract Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
|
||||
int glyphCode);
|
||||
|
||||
abstract GeneralPath getGlyphOutline(long pScalerContext, int glyphCode,
|
||||
float x, float y);
|
||||
|
||||
abstract GeneralPath getGlyphVectorOutline(long pScalerContext,
|
||||
int[] glyphs, int numGlyphs,
|
||||
float x, float y);
|
||||
}
|
||||
147
jdkSrc/jdk8/sun/font/PhysicalStrike.java
Normal file
147
jdkSrc/jdk8/sun/font/PhysicalStrike.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
public abstract class PhysicalStrike extends FontStrike {
|
||||
|
||||
static final long INTMASK = 0xffffffffL;
|
||||
static boolean longAddresses;
|
||||
static {
|
||||
switch (StrikeCache.nativeAddressSize) {
|
||||
case 8: longAddresses = true; break;
|
||||
case 4: longAddresses = false; break;
|
||||
default: throw new RuntimeException("Unexpected address size");
|
||||
}
|
||||
}
|
||||
|
||||
private PhysicalFont physicalFont;
|
||||
protected CharToGlyphMapper mapper;
|
||||
/* the ScalerContext is a native structure pre-filled with the
|
||||
* info needed to setup the scaler for this strike. Its immutable
|
||||
* so we set it up when the strike is created and free it when the
|
||||
* strike is disposed. There's then no need to pass the info down
|
||||
* separately to native on every call to the scaler.
|
||||
*/
|
||||
protected long pScalerContext;
|
||||
|
||||
/* Only one of these two arrays is non-null.
|
||||
* use the one that matches size of an address (32 or 64 bits)
|
||||
*/
|
||||
protected long[] longGlyphImages;
|
||||
protected int[] intGlyphImages;
|
||||
|
||||
/* Used by the TrueTypeFont subclass, which is the only client
|
||||
* of getGlyphPoint(). The field and method are here because
|
||||
* there is no TrueTypeFontStrike subclass.
|
||||
* This map is a cache of the positions of points on the outline
|
||||
* of a TrueType glyph. It is used by the OpenType layout engine
|
||||
* to perform mark positioning. Without this cache every position
|
||||
* request involves scaling and hinting the glyph outline potentially
|
||||
* over and over again.
|
||||
*/
|
||||
ConcurrentHashMap<Integer, Point2D.Float> glyphPointMapCache;
|
||||
|
||||
protected boolean getImageWithAdvance;
|
||||
protected static final int complexTX =
|
||||
AffineTransform.TYPE_FLIP |
|
||||
AffineTransform.TYPE_GENERAL_SCALE |
|
||||
AffineTransform.TYPE_GENERAL_ROTATION |
|
||||
AffineTransform.TYPE_GENERAL_TRANSFORM |
|
||||
AffineTransform.TYPE_QUADRANT_ROTATION;
|
||||
|
||||
PhysicalStrike(PhysicalFont physicalFont, FontStrikeDesc desc) {
|
||||
this.physicalFont = physicalFont;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
protected PhysicalStrike() {
|
||||
}
|
||||
/* A number of methods are delegated by the strike to the scaler
|
||||
* context which is a shared resource on a physical font.
|
||||
*/
|
||||
|
||||
public int getNumGlyphs() {
|
||||
return physicalFont.getNumGlyphs();
|
||||
}
|
||||
|
||||
/* These 3 metrics methods below should be implemented to return
|
||||
* values in user space.
|
||||
*/
|
||||
StrikeMetrics getFontMetrics() {
|
||||
if (strikeMetrics == null) {
|
||||
strikeMetrics =
|
||||
physicalFont.getFontMetrics(pScalerContext);
|
||||
}
|
||||
return strikeMetrics;
|
||||
}
|
||||
|
||||
float getCodePointAdvance(int cp) {
|
||||
return getGlyphAdvance(physicalFont.getMapper().charToGlyph(cp));
|
||||
}
|
||||
|
||||
Point2D.Float getCharMetrics(char ch) {
|
||||
return getGlyphMetrics(physicalFont.getMapper().charToGlyph(ch));
|
||||
}
|
||||
|
||||
int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Used by the OpenType engine for mark positioning.
|
||||
*/
|
||||
Point2D.Float getGlyphPoint(int glyphCode, int ptNumber) {
|
||||
Point2D.Float gp = null;
|
||||
Integer ptKey = Integer.valueOf(glyphCode<<16|ptNumber);
|
||||
if (glyphPointMapCache == null) {
|
||||
synchronized (this) {
|
||||
if (glyphPointMapCache == null) {
|
||||
glyphPointMapCache =
|
||||
new ConcurrentHashMap<Integer, Point2D.Float>();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gp = glyphPointMapCache.get(ptKey);
|
||||
}
|
||||
|
||||
if (gp == null) {
|
||||
gp = (physicalFont.getGlyphPoint(pScalerContext, glyphCode, ptNumber));
|
||||
adjustPoint(gp);
|
||||
glyphPointMapCache.put(ptKey, gp);
|
||||
}
|
||||
return gp;
|
||||
}
|
||||
|
||||
protected void adjustPoint(Point2D.Float pt) {
|
||||
}
|
||||
}
|
||||
85
jdkSrc/jdk8/sun/font/Script.java
Normal file
85
jdkSrc/jdk8/sun/font/Script.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2003 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
public final class Script {
|
||||
|
||||
public static final int INVALID_CODE = -1;
|
||||
public static final int COMMON = 0; /* Zyyy */
|
||||
public static final int INHERITED = 1; /* Qaai */
|
||||
|
||||
public static final int ARABIC = 2; /* Arab */
|
||||
public static final int ARMENIAN = 3; /* Armn */
|
||||
public static final int BENGALI = 4; /* Beng */
|
||||
public static final int BOPOMOFO = 5; /* Bopo */
|
||||
public static final int CHEROKEE = 6; /* Cher */
|
||||
public static final int COPTIC = 7; /* Qaac */
|
||||
public static final int CYRILLIC = 8; /* Cyrl (Cyrs) */
|
||||
public static final int DESERET = 9; /* Dsrt */
|
||||
public static final int DEVANAGARI = 10; /* Deva */
|
||||
public static final int ETHIOPIC = 11; /* Ethi */
|
||||
public static final int GEORGIAN = 12; /* Geor (Geon; Geoa) */
|
||||
public static final int GOTHIC = 13; /* Goth */
|
||||
public static final int GREEK = 14; /* Grek */
|
||||
public static final int GUJARATI = 15; /* Gujr */
|
||||
public static final int GURMUKHI = 16; /* Guru */
|
||||
public static final int HAN = 17; /* Hani */
|
||||
public static final int HANGUL = 18; /* Hang */
|
||||
public static final int HEBREW = 19; /* Hebr */
|
||||
public static final int HIRAGANA = 20; /* Hira */
|
||||
public static final int KANNADA = 21; /* Knda */
|
||||
public static final int KATAKANA = 22; /* Kana */
|
||||
public static final int KHMER = 23; /* Khmr */
|
||||
public static final int LAO = 24; /* Laoo */
|
||||
public static final int LATIN = 25; /* Latn (Latf; Latg) */
|
||||
public static final int MALAYALAM = 26; /* Mlym */
|
||||
public static final int MONGOLIAN = 27; /* Mong */
|
||||
public static final int MYANMAR = 28; /* Mymr */
|
||||
public static final int OGHAM = 29; /* Ogam */
|
||||
public static final int OLD_ITALIC = 30; /* Ital */
|
||||
public static final int ORIYA = 31; /* Orya */
|
||||
public static final int RUNIC = 32; /* Runr */
|
||||
public static final int SINHALA = 33; /* Sinh */
|
||||
public static final int SYRIAC = 34; /* Syrc (Syrj; Syrn; Syre) */
|
||||
public static final int TAMIL = 35; /* Taml */
|
||||
public static final int TELUGU = 36; /* Telu */
|
||||
public static final int THAANA = 37; /* Thaa */
|
||||
public static final int THAI = 38; /* Thai */
|
||||
public static final int TIBETAN = 39; /* Tibt */
|
||||
public static final int CANADIAN_ABORIGINAL = 40; /* Cans */
|
||||
public static final int UCAS = CANADIAN_ABORIGINAL; /* Cans */
|
||||
public static final int YI = 41; /* Yiii */
|
||||
public static final int TAGALOG = 42; /* Tglg */
|
||||
public static final int HANUNOO = 43; /* Hano */
|
||||
public static final int BUHID = 44; /* Buhd */
|
||||
public static final int TAGBANWA = 45; /* Tagb */
|
||||
public static final int CODE_LIMIT = 46;
|
||||
}
|
||||
380
jdkSrc/jdk8/sun/font/ScriptRun.java
Normal file
380
jdkSrc/jdk8/sun/font/ScriptRun.java
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
*
|
||||
* Copyright (C) 1999-2003, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/**
|
||||
* <code>ScriptRun</code> is used to find runs of characters in
|
||||
* the same script, as defined in the <code>Script</code> class.
|
||||
* It implements a simple iterator over an array of characters.
|
||||
* The iterator will assign <code>COMMON</code> and <code>INHERITED</code>
|
||||
* characters to the same script as the preceding characters. If the
|
||||
* COMMON and INHERITED characters are first, they will be assigned to
|
||||
* the same script as the following characters.
|
||||
*
|
||||
* The iterator will try to match paired punctuation. If it sees an
|
||||
* opening punctuation character, it will remember the script that
|
||||
* was assigned to that character, and assign the same script to the
|
||||
* matching closing punctuation.
|
||||
*
|
||||
* No attempt is made to combine related scripts into a single run. In
|
||||
* particular, Hiragana, Katakana, and Han characters will appear in seperate
|
||||
* runs.
|
||||
|
||||
* Here is an example of how to iterate over script runs:
|
||||
* <pre>
|
||||
* void printScriptRuns(char[] text)
|
||||
* {
|
||||
* ScriptRun scriptRun = new ScriptRun(text, 0, text.length);
|
||||
*
|
||||
* while (scriptRun.next()) {
|
||||
* int start = scriptRun.getScriptStart();
|
||||
* int limit = scriptRun.getScriptLimit();
|
||||
* int script = scriptRun.getScriptCode();
|
||||
*
|
||||
* System.out.println("Script \"" + Script.getName(script) + "\" from " +
|
||||
* start + " to " + limit + ".");
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public final class ScriptRun
|
||||
{
|
||||
private char[] text; // fixed once set by constructor
|
||||
private int textStart;
|
||||
private int textLimit;
|
||||
|
||||
private int scriptStart; // change during iteration
|
||||
private int scriptLimit;
|
||||
private int scriptCode;
|
||||
|
||||
private int stack[]; // stack used to handle paired punctuation if encountered
|
||||
private int parenSP;
|
||||
|
||||
public ScriptRun() {
|
||||
// must call init later or we die.
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a <code>ScriptRun</code> object which iterates over a subrange
|
||||
* of the given characetrs.
|
||||
*
|
||||
* @param chars the array of characters over which to iterate.
|
||||
* @param start the index of the first character over which to iterate
|
||||
* @param count the number of characters over which to iterate
|
||||
*/
|
||||
public ScriptRun(char[] chars, int start, int count)
|
||||
{
|
||||
init(chars, start, count);
|
||||
}
|
||||
|
||||
public void init(char[] chars, int start, int count)
|
||||
{
|
||||
if (chars == null || start < 0 || count < 0 || count > chars.length - start) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
text = chars;
|
||||
textStart = start;
|
||||
textLimit = start + count;
|
||||
|
||||
scriptStart = textStart;
|
||||
scriptLimit = textStart;
|
||||
scriptCode = Script.INVALID_CODE;
|
||||
parenSP = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the starting index of the current script run.
|
||||
*
|
||||
* @return the index of the first character in the current script run.
|
||||
*/
|
||||
public final int getScriptStart() {
|
||||
return scriptStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the first character after the current script run.
|
||||
*
|
||||
* @return the index of the first character after the current script run.
|
||||
*/
|
||||
public final int getScriptLimit() {
|
||||
return scriptLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script code for the script of the current script run.
|
||||
*
|
||||
* @return the script code for the script of the current script run.
|
||||
* @see #Script
|
||||
*/
|
||||
public final int getScriptCode() {
|
||||
return scriptCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the next script run. Returns <code>false</code> if there
|
||||
* isn't another run, returns <code>true</code> if there is.
|
||||
*
|
||||
* @return <code>false</code> if there isn't another run, <code>true</code> if there is.
|
||||
*/
|
||||
public final boolean next() {
|
||||
int startSP = parenSP; // used to find the first new open character
|
||||
|
||||
// if we've fallen off the end of the text, we're done
|
||||
if (scriptLimit >= textLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scriptCode = Script.COMMON;
|
||||
scriptStart = scriptLimit;
|
||||
|
||||
int ch;
|
||||
|
||||
while ((ch = nextCodePoint()) != DONE) {
|
||||
int sc = ScriptRunData.getScript(ch);
|
||||
int pairIndex = sc == Script.COMMON ? getPairIndex(ch) : -1;
|
||||
|
||||
// Paired character handling:
|
||||
//
|
||||
// if it's an open character, push it onto the stack.
|
||||
// if it's a close character, find the matching open on the
|
||||
// stack, and use that script code. Any non-matching open
|
||||
// characters above it on the stack will be popped.
|
||||
if (pairIndex >= 0) {
|
||||
if ((pairIndex & 1) == 0) {
|
||||
if (stack == null) {
|
||||
stack = new int[32];
|
||||
} else if (parenSP == stack.length) {
|
||||
int[] newstack = new int[stack.length + 32];
|
||||
System.arraycopy(stack, 0, newstack, 0, stack.length);
|
||||
stack = newstack;
|
||||
}
|
||||
|
||||
stack[parenSP++] = pairIndex;
|
||||
stack[parenSP++] = scriptCode;
|
||||
} else if (parenSP > 0) {
|
||||
int pi = pairIndex & ~1;
|
||||
|
||||
while ((parenSP -= 2) >= 0 && stack[parenSP] != pi);
|
||||
|
||||
if (parenSP >= 0) {
|
||||
sc = stack[parenSP+1];
|
||||
} else {
|
||||
parenSP = 0;
|
||||
}
|
||||
if (parenSP < startSP) {
|
||||
startSP = parenSP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sameScript(scriptCode, sc)) {
|
||||
if (scriptCode <= Script.INHERITED && sc > Script.INHERITED) {
|
||||
scriptCode = sc;
|
||||
|
||||
// now that we have a final script code, fix any open
|
||||
// characters we pushed before we knew the script code.
|
||||
while (startSP < parenSP) {
|
||||
stack[startSP+1] = scriptCode;
|
||||
startSP += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// if this character is a close paired character,
|
||||
// pop it from the stack
|
||||
if (pairIndex > 0 && (pairIndex & 1) != 0 && parenSP > 0) {
|
||||
parenSP -= 2;
|
||||
}
|
||||
} else {
|
||||
// We've just seen the first character of
|
||||
// the next run. Back over it so we'll see
|
||||
// it again the next time.
|
||||
pushback(ch);
|
||||
|
||||
// we're outta here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static final int SURROGATE_START = 0x10000;
|
||||
static final int LEAD_START = 0xd800;
|
||||
static final int LEAD_LIMIT = 0xdc00;
|
||||
static final int TAIL_START = 0xdc00;
|
||||
static final int TAIL_LIMIT = 0xe000;
|
||||
static final int LEAD_SURROGATE_SHIFT = 10;
|
||||
static final int SURROGATE_OFFSET = SURROGATE_START - (LEAD_START << LEAD_SURROGATE_SHIFT) - TAIL_START;
|
||||
|
||||
static final int DONE = -1;
|
||||
|
||||
private final int nextCodePoint() {
|
||||
if (scriptLimit >= textLimit) {
|
||||
return DONE;
|
||||
}
|
||||
int ch = text[scriptLimit++];
|
||||
if (ch >= LEAD_START && ch < LEAD_LIMIT && scriptLimit < textLimit) {
|
||||
int nch = text[scriptLimit];
|
||||
if (nch >= TAIL_START && nch < TAIL_LIMIT) {
|
||||
++scriptLimit;
|
||||
ch = (ch << LEAD_SURROGATE_SHIFT) + nch + SURROGATE_OFFSET;
|
||||
}
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
private final void pushback(int ch) {
|
||||
if (ch >= 0) {
|
||||
if (ch >= 0x10000) {
|
||||
scriptLimit -= 2;
|
||||
} else {
|
||||
scriptLimit -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two script codes to see if they are in the same script. If one script is
|
||||
* a strong script, and the other is INHERITED or COMMON, it will compare equal.
|
||||
*
|
||||
* @param scriptOne one of the script codes.
|
||||
* @param scriptTwo the other script code.
|
||||
* @return <code>true</code> if the two scripts are the same.
|
||||
* @see com.ibm.icu.lang.Script
|
||||
*/
|
||||
private static boolean sameScript(int scriptOne, int scriptTwo) {
|
||||
return scriptOne == scriptTwo || scriptOne <= Script.INHERITED || scriptTwo <= Script.INHERITED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the highest bit that's set in a word. Uses a binary search through
|
||||
* the bits.
|
||||
*
|
||||
* @param n the word in which to find the highest bit that's set.
|
||||
* @return the bit number (counting from the low order bit) of the highest bit.
|
||||
*/
|
||||
private static final byte highBit(int n)
|
||||
{
|
||||
if (n <= 0) {
|
||||
return -32;
|
||||
}
|
||||
|
||||
byte bit = 0;
|
||||
|
||||
if (n >= 1 << 16) {
|
||||
n >>= 16;
|
||||
bit += 16;
|
||||
}
|
||||
|
||||
if (n >= 1 << 8) {
|
||||
n >>= 8;
|
||||
bit += 8;
|
||||
}
|
||||
|
||||
if (n >= 1 << 4) {
|
||||
n >>= 4;
|
||||
bit += 4;
|
||||
}
|
||||
|
||||
if (n >= 1 << 2) {
|
||||
n >>= 2;
|
||||
bit += 2;
|
||||
}
|
||||
|
||||
if (n >= 1 << 1) {
|
||||
n >>= 1;
|
||||
bit += 1;
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the pairedChars array for the given character.
|
||||
*
|
||||
* @param ch the character for which to search.
|
||||
* @return the index of the character in the table, or -1 if it's not there.
|
||||
*/
|
||||
private static int getPairIndex(int ch)
|
||||
{
|
||||
int probe = pairedCharPower;
|
||||
int index = 0;
|
||||
|
||||
if (ch >= pairedChars[pairedCharExtra]) {
|
||||
index = pairedCharExtra;
|
||||
}
|
||||
|
||||
while (probe > (1 << 0)) {
|
||||
probe >>= 1;
|
||||
|
||||
if (ch >= pairedChars[index + probe]) {
|
||||
index += probe;
|
||||
}
|
||||
}
|
||||
|
||||
if (pairedChars[index] != ch) {
|
||||
index = -1;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
// all common
|
||||
private static int pairedChars[] = {
|
||||
0x0028, 0x0029, // ascii paired punctuation // common
|
||||
0x003c, 0x003e, // common
|
||||
0x005b, 0x005d, // common
|
||||
0x007b, 0x007d, // common
|
||||
0x00ab, 0x00bb, // guillemets // common
|
||||
0x2018, 0x2019, // general punctuation // common
|
||||
0x201c, 0x201d, // common
|
||||
0x2039, 0x203a, // common
|
||||
0x3008, 0x3009, // chinese paired punctuation // common
|
||||
0x300a, 0x300b,
|
||||
0x300c, 0x300d,
|
||||
0x300e, 0x300f,
|
||||
0x3010, 0x3011,
|
||||
0x3014, 0x3015,
|
||||
0x3016, 0x3017,
|
||||
0x3018, 0x3019,
|
||||
0x301a, 0x301b
|
||||
};
|
||||
|
||||
private static final int pairedCharPower = 1 << highBit(pairedChars.length);
|
||||
private static final int pairedCharExtra = pairedChars.length - pairedCharPower;
|
||||
|
||||
}
|
||||
795
jdkSrc/jdk8/sun/font/ScriptRunData.java
Normal file
795
jdkSrc/jdk8/sun/font/ScriptRunData.java
Normal file
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2003, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
public final class ScriptRunData {
|
||||
private ScriptRunData() {}
|
||||
|
||||
private static final int CHAR_START = 0;
|
||||
private static final int CHAR_LIMIT = 0x110000;
|
||||
|
||||
private static int cache = 0;
|
||||
public static final int getScript(int cp) {
|
||||
// optimize for runs of characters in the same script
|
||||
if (cp >= data[cache] && cp < data[cache+2]) {
|
||||
return data[cache+1];
|
||||
}
|
||||
if ((cp >= CHAR_START) && (cp < CHAR_LIMIT)) {
|
||||
int probe = dataPower;
|
||||
int index = 0;
|
||||
|
||||
if (cp >= data[dataExtra]) {
|
||||
index = dataExtra;
|
||||
}
|
||||
|
||||
while (probe > 2) {
|
||||
probe >>= 1;
|
||||
if (cp >= data[index + probe]) {
|
||||
index += probe;
|
||||
}
|
||||
}
|
||||
|
||||
cache = index;
|
||||
return data[index+1];
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(Integer.toString(cp));
|
||||
}
|
||||
|
||||
private static final int[] data = {
|
||||
0x000000, 0x00,
|
||||
0x000041, 0x19, // 'latn' latin
|
||||
0x00005B, 0x00,
|
||||
0x000061, 0x19, // 'latn' latin
|
||||
0x00007B, 0x00,
|
||||
0x0000AA, 0x19, // 'latn' latin
|
||||
0x0000AB, 0x00,
|
||||
0x0000B5, 0x0E, // 'grek' greek
|
||||
0x0000B6, 0x00,
|
||||
0x0000BA, 0x19, // 'latn' latin
|
||||
0x0000BB, 0x00,
|
||||
0x0000C0, 0x19, // 'latn' latin
|
||||
0x0000D7, 0x00,
|
||||
0x0000D8, 0x19, // 'latn' latin
|
||||
0x0000F7, 0x00,
|
||||
0x0000F8, 0x19, // 'latn' latin
|
||||
0x000221, 0x00,
|
||||
0x000222, 0x19, // 'latn' latin
|
||||
0x000234, 0x00,
|
||||
0x000250, 0x19, // 'latn' latin
|
||||
0x0002AE, 0x00,
|
||||
0x0002B0, 0x19, // 'latn' latin
|
||||
0x0002B9, 0x00,
|
||||
0x0002E0, 0x19, // 'latn' latin
|
||||
0x0002E5, 0x00,
|
||||
0x000300, 0x01, // 'qaai' inherited
|
||||
0x000350, 0x00,
|
||||
0x000360, 0x01, // 'qaai' inherited
|
||||
0x000370, 0x00,
|
||||
0x00037A, 0x0E, // 'grek' greek
|
||||
0x00037B, 0x00,
|
||||
0x000386, 0x0E, // 'grek' greek
|
||||
0x000387, 0x00,
|
||||
0x000388, 0x0E, // 'grek' greek
|
||||
0x00038B, 0x00,
|
||||
0x00038C, 0x0E, // 'grek' greek
|
||||
0x00038D, 0x00,
|
||||
0x00038E, 0x0E, // 'grek' greek
|
||||
0x0003A2, 0x00,
|
||||
0x0003A3, 0x0E, // 'grek' greek
|
||||
0x0003CF, 0x00,
|
||||
0x0003D0, 0x0E, // 'grek' greek
|
||||
0x0003F6, 0x00,
|
||||
0x000400, 0x08, // 'cyrl' cyrillic
|
||||
0x000482, 0x00,
|
||||
0x000483, 0x08, // 'cyrl' cyrillic
|
||||
0x000487, 0x00,
|
||||
0x000488, 0x01, // 'qaai' inherited
|
||||
0x00048A, 0x08, // 'cyrl' cyrillic
|
||||
0x0004CF, 0x00,
|
||||
0x0004D0, 0x08, // 'cyrl' cyrillic
|
||||
0x0004F6, 0x00,
|
||||
0x0004F8, 0x08, // 'cyrl' cyrillic
|
||||
0x0004FA, 0x00,
|
||||
0x000500, 0x08, // 'cyrl' cyrillic
|
||||
0x000510, 0x00,
|
||||
0x000531, 0x03, // 'armn' armenian
|
||||
0x000557, 0x00,
|
||||
0x000559, 0x03, // 'armn' armenian
|
||||
0x00055A, 0x00,
|
||||
0x000561, 0x03, // 'armn' armenian
|
||||
0x000588, 0x00,
|
||||
0x000591, 0x01, // 'qaai' inherited
|
||||
0x0005A2, 0x00,
|
||||
0x0005A3, 0x01, // 'qaai' inherited
|
||||
0x0005BA, 0x00,
|
||||
0x0005BB, 0x01, // 'qaai' inherited
|
||||
0x0005BE, 0x00,
|
||||
0x0005BF, 0x01, // 'qaai' inherited
|
||||
0x0005C0, 0x00,
|
||||
0x0005C1, 0x01, // 'qaai' inherited
|
||||
0x0005C3, 0x00,
|
||||
0x0005C4, 0x01, // 'qaai' inherited
|
||||
0x0005C5, 0x00,
|
||||
0x0005D0, 0x13, // 'hebr' hebrew
|
||||
0x0005EB, 0x00,
|
||||
0x0005F0, 0x13, // 'hebr' hebrew
|
||||
0x0005F3, 0x00,
|
||||
0x000621, 0x02, // 'arab' arabic
|
||||
0x00063B, 0x00,
|
||||
0x000641, 0x02, // 'arab' arabic
|
||||
0x00064B, 0x01, // 'qaai' inherited
|
||||
0x000656, 0x00,
|
||||
0x00066E, 0x02, // 'arab' arabic
|
||||
0x000670, 0x01, // 'qaai' inherited
|
||||
0x000671, 0x02, // 'arab' arabic
|
||||
0x0006D4, 0x00,
|
||||
0x0006D5, 0x02, // 'arab' arabic
|
||||
0x0006D6, 0x01, // 'qaai' inherited
|
||||
0x0006E5, 0x02, // 'arab' arabic
|
||||
0x0006E7, 0x01, // 'qaai' inherited
|
||||
0x0006E9, 0x00,
|
||||
0x0006EA, 0x01, // 'qaai' inherited
|
||||
0x0006EE, 0x00,
|
||||
0x0006FA, 0x02, // 'arab' arabic
|
||||
0x0006FD, 0x00,
|
||||
0x000710, 0x22, // 'syrc' syriac
|
||||
0x00072D, 0x00,
|
||||
0x000730, 0x22, // 'syrc' syriac
|
||||
0x00074B, 0x00,
|
||||
0x000780, 0x25, // 'thaa' thaana
|
||||
0x0007B2, 0x00,
|
||||
0x000901, 0x0A, // 'deva' devanagari
|
||||
0x000904, 0x00,
|
||||
0x000905, 0x0A, // 'deva' devanagari
|
||||
0x00093A, 0x00,
|
||||
0x00093C, 0x0A, // 'deva' devanagari
|
||||
0x00094E, 0x00,
|
||||
0x000950, 0x0A, // 'deva' devanagari
|
||||
0x000955, 0x00,
|
||||
0x000958, 0x0A, // 'deva' devanagari
|
||||
0x000964, 0x00,
|
||||
0x000966, 0x0A, // 'deva' devanagari
|
||||
0x000970, 0x00,
|
||||
0x000981, 0x04, // 'beng' bengali
|
||||
0x000984, 0x00,
|
||||
0x000985, 0x04, // 'beng' bengali
|
||||
0x00098D, 0x00,
|
||||
0x00098F, 0x04, // 'beng' bengali
|
||||
0x000991, 0x00,
|
||||
0x000993, 0x04, // 'beng' bengali
|
||||
0x0009A9, 0x00,
|
||||
0x0009AA, 0x04, // 'beng' bengali
|
||||
0x0009B1, 0x00,
|
||||
0x0009B2, 0x04, // 'beng' bengali
|
||||
0x0009B3, 0x00,
|
||||
0x0009B6, 0x04, // 'beng' bengali
|
||||
0x0009BA, 0x00,
|
||||
0x0009BC, 0x04, // 'beng' bengali
|
||||
0x0009BD, 0x00,
|
||||
0x0009BE, 0x04, // 'beng' bengali
|
||||
0x0009C5, 0x00,
|
||||
0x0009C7, 0x04, // 'beng' bengali
|
||||
0x0009C9, 0x00,
|
||||
0x0009CB, 0x04, // 'beng' bengali
|
||||
0x0009CE, 0x00,
|
||||
0x0009D7, 0x04, // 'beng' bengali
|
||||
0x0009D8, 0x00,
|
||||
0x0009DC, 0x04, // 'beng' bengali
|
||||
0x0009DE, 0x00,
|
||||
0x0009DF, 0x04, // 'beng' bengali
|
||||
0x0009E4, 0x00,
|
||||
0x0009E6, 0x04, // 'beng' bengali
|
||||
0x0009F2, 0x00,
|
||||
0x000A02, 0x10, // 'guru' gurmukhi
|
||||
0x000A03, 0x00,
|
||||
0x000A05, 0x10, // 'guru' gurmukhi
|
||||
0x000A0B, 0x00,
|
||||
0x000A0F, 0x10, // 'guru' gurmukhi
|
||||
0x000A11, 0x00,
|
||||
0x000A13, 0x10, // 'guru' gurmukhi
|
||||
0x000A29, 0x00,
|
||||
0x000A2A, 0x10, // 'guru' gurmukhi
|
||||
0x000A31, 0x00,
|
||||
0x000A32, 0x10, // 'guru' gurmukhi
|
||||
0x000A34, 0x00,
|
||||
0x000A35, 0x10, // 'guru' gurmukhi
|
||||
0x000A37, 0x00,
|
||||
0x000A38, 0x10, // 'guru' gurmukhi
|
||||
0x000A3A, 0x00,
|
||||
0x000A3C, 0x10, // 'guru' gurmukhi
|
||||
0x000A3D, 0x00,
|
||||
0x000A3E, 0x10, // 'guru' gurmukhi
|
||||
0x000A43, 0x00,
|
||||
0x000A47, 0x10, // 'guru' gurmukhi
|
||||
0x000A49, 0x00,
|
||||
0x000A4B, 0x10, // 'guru' gurmukhi
|
||||
0x000A4E, 0x00,
|
||||
0x000A59, 0x10, // 'guru' gurmukhi
|
||||
0x000A5D, 0x00,
|
||||
0x000A5E, 0x10, // 'guru' gurmukhi
|
||||
0x000A5F, 0x00,
|
||||
0x000A66, 0x10, // 'guru' gurmukhi
|
||||
0x000A75, 0x00,
|
||||
0x000A81, 0x0F, // 'gujr' gujarati
|
||||
0x000A84, 0x00,
|
||||
0x000A85, 0x0F, // 'gujr' gujarati
|
||||
0x000A8C, 0x00,
|
||||
0x000A8D, 0x0F, // 'gujr' gujarati
|
||||
0x000A8E, 0x00,
|
||||
0x000A8F, 0x0F, // 'gujr' gujarati
|
||||
0x000A92, 0x00,
|
||||
0x000A93, 0x0F, // 'gujr' gujarati
|
||||
0x000AA9, 0x00,
|
||||
0x000AAA, 0x0F, // 'gujr' gujarati
|
||||
0x000AB1, 0x00,
|
||||
0x000AB2, 0x0F, // 'gujr' gujarati
|
||||
0x000AB4, 0x00,
|
||||
0x000AB5, 0x0F, // 'gujr' gujarati
|
||||
0x000ABA, 0x00,
|
||||
0x000ABC, 0x0F, // 'gujr' gujarati
|
||||
0x000AC6, 0x00,
|
||||
0x000AC7, 0x0F, // 'gujr' gujarati
|
||||
0x000ACA, 0x00,
|
||||
0x000ACB, 0x0F, // 'gujr' gujarati
|
||||
0x000ACE, 0x00,
|
||||
0x000AD0, 0x0F, // 'gujr' gujarati
|
||||
0x000AD1, 0x00,
|
||||
0x000AE0, 0x0F, // 'gujr' gujarati
|
||||
0x000AE1, 0x00,
|
||||
0x000AE6, 0x0F, // 'gujr' gujarati
|
||||
0x000AF0, 0x00,
|
||||
0x000B01, 0x1F, // 'orya' oriya
|
||||
0x000B04, 0x00,
|
||||
0x000B05, 0x1F, // 'orya' oriya
|
||||
0x000B0D, 0x00,
|
||||
0x000B0F, 0x1F, // 'orya' oriya
|
||||
0x000B11, 0x00,
|
||||
0x000B13, 0x1F, // 'orya' oriya
|
||||
0x000B29, 0x00,
|
||||
0x000B2A, 0x1F, // 'orya' oriya
|
||||
0x000B31, 0x00,
|
||||
0x000B32, 0x1F, // 'orya' oriya
|
||||
0x000B34, 0x00,
|
||||
0x000B36, 0x1F, // 'orya' oriya
|
||||
0x000B3A, 0x00,
|
||||
0x000B3C, 0x1F, // 'orya' oriya
|
||||
0x000B44, 0x00,
|
||||
0x000B47, 0x1F, // 'orya' oriya
|
||||
0x000B49, 0x00,
|
||||
0x000B4B, 0x1F, // 'orya' oriya
|
||||
0x000B4E, 0x00,
|
||||
0x000B56, 0x1F, // 'orya' oriya
|
||||
0x000B58, 0x00,
|
||||
0x000B5C, 0x1F, // 'orya' oriya
|
||||
0x000B5E, 0x00,
|
||||
0x000B5F, 0x1F, // 'orya' oriya
|
||||
0x000B62, 0x00,
|
||||
0x000B66, 0x1F, // 'orya' oriya
|
||||
0x000B70, 0x00,
|
||||
0x000B82, 0x23, // 'taml' tamil
|
||||
0x000B84, 0x00,
|
||||
0x000B85, 0x23, // 'taml' tamil
|
||||
0x000B8B, 0x00,
|
||||
0x000B8E, 0x23, // 'taml' tamil
|
||||
0x000B91, 0x00,
|
||||
0x000B92, 0x23, // 'taml' tamil
|
||||
0x000B96, 0x00,
|
||||
0x000B99, 0x23, // 'taml' tamil
|
||||
0x000B9B, 0x00,
|
||||
0x000B9C, 0x23, // 'taml' tamil
|
||||
0x000B9D, 0x00,
|
||||
0x000B9E, 0x23, // 'taml' tamil
|
||||
0x000BA0, 0x00,
|
||||
0x000BA3, 0x23, // 'taml' tamil
|
||||
0x000BA5, 0x00,
|
||||
0x000BA8, 0x23, // 'taml' tamil
|
||||
0x000BAB, 0x00,
|
||||
0x000BAE, 0x23, // 'taml' tamil
|
||||
0x000BB6, 0x00,
|
||||
0x000BB7, 0x23, // 'taml' tamil
|
||||
0x000BBA, 0x00,
|
||||
0x000BBE, 0x23, // 'taml' tamil
|
||||
0x000BC3, 0x00,
|
||||
0x000BC6, 0x23, // 'taml' tamil
|
||||
0x000BC9, 0x00,
|
||||
0x000BCA, 0x23, // 'taml' tamil
|
||||
0x000BCE, 0x00,
|
||||
0x000BD7, 0x23, // 'taml' tamil
|
||||
0x000BD8, 0x00,
|
||||
0x000BE7, 0x23, // 'taml' tamil
|
||||
0x000BF3, 0x00,
|
||||
0x000C01, 0x24, // 'telu' telugu
|
||||
0x000C04, 0x00,
|
||||
0x000C05, 0x24, // 'telu' telugu
|
||||
0x000C0D, 0x00,
|
||||
0x000C0E, 0x24, // 'telu' telugu
|
||||
0x000C11, 0x00,
|
||||
0x000C12, 0x24, // 'telu' telugu
|
||||
0x000C29, 0x00,
|
||||
0x000C2A, 0x24, // 'telu' telugu
|
||||
0x000C34, 0x00,
|
||||
0x000C35, 0x24, // 'telu' telugu
|
||||
0x000C3A, 0x00,
|
||||
0x000C3E, 0x24, // 'telu' telugu
|
||||
0x000C45, 0x00,
|
||||
0x000C46, 0x24, // 'telu' telugu
|
||||
0x000C49, 0x00,
|
||||
0x000C4A, 0x24, // 'telu' telugu
|
||||
0x000C4E, 0x00,
|
||||
0x000C55, 0x24, // 'telu' telugu
|
||||
0x000C57, 0x00,
|
||||
0x000C60, 0x24, // 'telu' telugu
|
||||
0x000C62, 0x00,
|
||||
0x000C66, 0x24, // 'telu' telugu
|
||||
0x000C70, 0x00,
|
||||
0x000C82, 0x15, // 'knda' kannada
|
||||
0x000C84, 0x00,
|
||||
0x000C85, 0x15, // 'knda' kannada
|
||||
0x000C8D, 0x00,
|
||||
0x000C8E, 0x15, // 'knda' kannada
|
||||
0x000C91, 0x00,
|
||||
0x000C92, 0x15, // 'knda' kannada
|
||||
0x000CA9, 0x00,
|
||||
0x000CAA, 0x15, // 'knda' kannada
|
||||
0x000CB4, 0x00,
|
||||
0x000CB5, 0x15, // 'knda' kannada
|
||||
0x000CBA, 0x00,
|
||||
0x000CBE, 0x15, // 'knda' kannada
|
||||
0x000CC5, 0x00,
|
||||
0x000CC6, 0x15, // 'knda' kannada
|
||||
0x000CC9, 0x00,
|
||||
0x000CCA, 0x15, // 'knda' kannada
|
||||
0x000CCE, 0x00,
|
||||
0x000CD5, 0x15, // 'knda' kannada
|
||||
0x000CD7, 0x00,
|
||||
0x000CDE, 0x15, // 'knda' kannada
|
||||
0x000CDF, 0x00,
|
||||
0x000CE0, 0x15, // 'knda' kannada
|
||||
0x000CE2, 0x00,
|
||||
0x000CE6, 0x15, // 'knda' kannada
|
||||
0x000CF0, 0x00,
|
||||
0x000D02, 0x1A, // 'mlym' malayalam
|
||||
0x000D04, 0x00,
|
||||
0x000D05, 0x1A, // 'mlym' malayalam
|
||||
0x000D0D, 0x00,
|
||||
0x000D0E, 0x1A, // 'mlym' malayalam
|
||||
0x000D11, 0x00,
|
||||
0x000D12, 0x1A, // 'mlym' malayalam
|
||||
0x000D29, 0x00,
|
||||
0x000D2A, 0x1A, // 'mlym' malayalam
|
||||
0x000D3A, 0x00,
|
||||
0x000D3E, 0x1A, // 'mlym' malayalam
|
||||
0x000D44, 0x00,
|
||||
0x000D46, 0x1A, // 'mlym' malayalam
|
||||
0x000D49, 0x00,
|
||||
0x000D4A, 0x1A, // 'mlym' malayalam
|
||||
0x000D4E, 0x00,
|
||||
0x000D57, 0x1A, // 'mlym' malayalam
|
||||
0x000D58, 0x00,
|
||||
0x000D60, 0x1A, // 'mlym' malayalam
|
||||
0x000D62, 0x00,
|
||||
0x000D66, 0x1A, // 'mlym' malayalam
|
||||
0x000D70, 0x00,
|
||||
0x000D82, 0x21, // 'sinh' sinhala
|
||||
0x000D84, 0x00,
|
||||
0x000D85, 0x21, // 'sinh' sinhala
|
||||
0x000D97, 0x00,
|
||||
0x000D9A, 0x21, // 'sinh' sinhala
|
||||
0x000DB2, 0x00,
|
||||
0x000DB3, 0x21, // 'sinh' sinhala
|
||||
0x000DBC, 0x00,
|
||||
0x000DBD, 0x21, // 'sinh' sinhala
|
||||
0x000DBE, 0x00,
|
||||
0x000DC0, 0x21, // 'sinh' sinhala
|
||||
0x000DC7, 0x00,
|
||||
0x000DCA, 0x21, // 'sinh' sinhala
|
||||
0x000DCB, 0x00,
|
||||
0x000DCF, 0x21, // 'sinh' sinhala
|
||||
0x000DD5, 0x00,
|
||||
0x000DD6, 0x21, // 'sinh' sinhala
|
||||
0x000DD7, 0x00,
|
||||
0x000DD8, 0x21, // 'sinh' sinhala
|
||||
0x000DE0, 0x00,
|
||||
0x000DF2, 0x21, // 'sinh' sinhala
|
||||
0x000DF4, 0x00,
|
||||
0x000E01, 0x26, // 'thai' thai
|
||||
0x000E3B, 0x00,
|
||||
0x000E40, 0x26, // 'thai' thai
|
||||
0x000E4F, 0x00,
|
||||
0x000E50, 0x26, // 'thai' thai
|
||||
0x000E5A, 0x00,
|
||||
0x000E81, 0x18, // 'laoo' lao
|
||||
0x000E83, 0x00,
|
||||
0x000E84, 0x18, // 'laoo' lao
|
||||
0x000E85, 0x00,
|
||||
0x000E87, 0x18, // 'laoo' lao
|
||||
0x000E89, 0x00,
|
||||
0x000E8A, 0x18, // 'laoo' lao
|
||||
0x000E8B, 0x00,
|
||||
0x000E8D, 0x18, // 'laoo' lao
|
||||
0x000E8E, 0x00,
|
||||
0x000E94, 0x18, // 'laoo' lao
|
||||
0x000E98, 0x00,
|
||||
0x000E99, 0x18, // 'laoo' lao
|
||||
0x000EA0, 0x00,
|
||||
0x000EA1, 0x18, // 'laoo' lao
|
||||
0x000EA4, 0x00,
|
||||
0x000EA5, 0x18, // 'laoo' lao
|
||||
0x000EA6, 0x00,
|
||||
0x000EA7, 0x18, // 'laoo' lao
|
||||
0x000EA8, 0x00,
|
||||
0x000EAA, 0x18, // 'laoo' lao
|
||||
0x000EAC, 0x00,
|
||||
0x000EAD, 0x18, // 'laoo' lao
|
||||
0x000EBA, 0x00,
|
||||
0x000EBB, 0x18, // 'laoo' lao
|
||||
0x000EBE, 0x00,
|
||||
0x000EC0, 0x18, // 'laoo' lao
|
||||
0x000EC5, 0x00,
|
||||
0x000EC6, 0x18, // 'laoo' lao
|
||||
0x000EC7, 0x00,
|
||||
0x000EC8, 0x18, // 'laoo' lao
|
||||
0x000ECE, 0x00,
|
||||
0x000ED0, 0x18, // 'laoo' lao
|
||||
0x000EDA, 0x00,
|
||||
0x000EDC, 0x18, // 'laoo' lao
|
||||
0x000EDE, 0x00,
|
||||
0x000F00, 0x27, // 'tibt' tibetan
|
||||
0x000F01, 0x00,
|
||||
0x000F18, 0x27, // 'tibt' tibetan
|
||||
0x000F1A, 0x00,
|
||||
0x000F20, 0x27, // 'tibt' tibetan
|
||||
0x000F34, 0x00,
|
||||
0x000F35, 0x27, // 'tibt' tibetan
|
||||
0x000F36, 0x00,
|
||||
0x000F37, 0x27, // 'tibt' tibetan
|
||||
0x000F38, 0x00,
|
||||
0x000F39, 0x27, // 'tibt' tibetan
|
||||
0x000F3A, 0x00,
|
||||
0x000F40, 0x27, // 'tibt' tibetan
|
||||
0x000F48, 0x00,
|
||||
0x000F49, 0x27, // 'tibt' tibetan
|
||||
0x000F6B, 0x00,
|
||||
0x000F71, 0x27, // 'tibt' tibetan
|
||||
0x000F85, 0x00,
|
||||
0x000F86, 0x27, // 'tibt' tibetan
|
||||
0x000F8C, 0x00,
|
||||
0x000F90, 0x27, // 'tibt' tibetan
|
||||
0x000F98, 0x00,
|
||||
0x000F99, 0x27, // 'tibt' tibetan
|
||||
0x000FBD, 0x00,
|
||||
0x000FC6, 0x27, // 'tibt' tibetan
|
||||
0x000FC7, 0x00,
|
||||
0x001000, 0x1C, // 'mymr' myanmar
|
||||
0x001022, 0x00,
|
||||
0x001023, 0x1C, // 'mymr' myanmar
|
||||
0x001028, 0x00,
|
||||
0x001029, 0x1C, // 'mymr' myanmar
|
||||
0x00102B, 0x00,
|
||||
0x00102C, 0x1C, // 'mymr' myanmar
|
||||
0x001033, 0x00,
|
||||
0x001036, 0x1C, // 'mymr' myanmar
|
||||
0x00103A, 0x00,
|
||||
0x001040, 0x1C, // 'mymr' myanmar
|
||||
0x00104A, 0x00,
|
||||
0x001050, 0x1C, // 'mymr' myanmar
|
||||
0x00105A, 0x00,
|
||||
0x0010A0, 0x0C, // 'geor' georgian
|
||||
0x0010C6, 0x00,
|
||||
0x0010D0, 0x0C, // 'geor' georgian
|
||||
0x0010F9, 0x00,
|
||||
0x001100, 0x12, // 'hang' hangul
|
||||
0x00115A, 0x00,
|
||||
0x00115F, 0x12, // 'hang' hangul
|
||||
0x0011A3, 0x00,
|
||||
0x0011A8, 0x12, // 'hang' hangul
|
||||
0x0011FA, 0x00,
|
||||
0x001200, 0x0B, // 'ethi' ethiopic
|
||||
0x001207, 0x00,
|
||||
0x001208, 0x0B, // 'ethi' ethiopic
|
||||
0x001247, 0x00,
|
||||
0x001248, 0x0B, // 'ethi' ethiopic
|
||||
0x001249, 0x00,
|
||||
0x00124A, 0x0B, // 'ethi' ethiopic
|
||||
0x00124E, 0x00,
|
||||
0x001250, 0x0B, // 'ethi' ethiopic
|
||||
0x001257, 0x00,
|
||||
0x001258, 0x0B, // 'ethi' ethiopic
|
||||
0x001259, 0x00,
|
||||
0x00125A, 0x0B, // 'ethi' ethiopic
|
||||
0x00125E, 0x00,
|
||||
0x001260, 0x0B, // 'ethi' ethiopic
|
||||
0x001287, 0x00,
|
||||
0x001288, 0x0B, // 'ethi' ethiopic
|
||||
0x001289, 0x00,
|
||||
0x00128A, 0x0B, // 'ethi' ethiopic
|
||||
0x00128E, 0x00,
|
||||
0x001290, 0x0B, // 'ethi' ethiopic
|
||||
0x0012AF, 0x00,
|
||||
0x0012B0, 0x0B, // 'ethi' ethiopic
|
||||
0x0012B1, 0x00,
|
||||
0x0012B2, 0x0B, // 'ethi' ethiopic
|
||||
0x0012B6, 0x00,
|
||||
0x0012B8, 0x0B, // 'ethi' ethiopic
|
||||
0x0012BF, 0x00,
|
||||
0x0012C0, 0x0B, // 'ethi' ethiopic
|
||||
0x0012C1, 0x00,
|
||||
0x0012C2, 0x0B, // 'ethi' ethiopic
|
||||
0x0012C6, 0x00,
|
||||
0x0012C8, 0x0B, // 'ethi' ethiopic
|
||||
0x0012CF, 0x00,
|
||||
0x0012D0, 0x0B, // 'ethi' ethiopic
|
||||
0x0012D7, 0x00,
|
||||
0x0012D8, 0x0B, // 'ethi' ethiopic
|
||||
0x0012EF, 0x00,
|
||||
0x0012F0, 0x0B, // 'ethi' ethiopic
|
||||
0x00130F, 0x00,
|
||||
0x001310, 0x0B, // 'ethi' ethiopic
|
||||
0x001311, 0x00,
|
||||
0x001312, 0x0B, // 'ethi' ethiopic
|
||||
0x001316, 0x00,
|
||||
0x001318, 0x0B, // 'ethi' ethiopic
|
||||
0x00131F, 0x00,
|
||||
0x001320, 0x0B, // 'ethi' ethiopic
|
||||
0x001347, 0x00,
|
||||
0x001348, 0x0B, // 'ethi' ethiopic
|
||||
0x00135B, 0x00,
|
||||
0x001369, 0x0B, // 'ethi' ethiopic
|
||||
0x00137D, 0x00,
|
||||
0x0013A0, 0x06, // 'cher' cherokee
|
||||
0x0013F5, 0x00,
|
||||
0x001401, 0x28, // 'cans' canadian_aboriginal
|
||||
0x00166D, 0x00,
|
||||
0x00166F, 0x28, // 'cans' canadian_aboriginal
|
||||
0x001677, 0x00,
|
||||
0x001681, 0x1D, // 'ogam' ogham
|
||||
0x00169B, 0x00,
|
||||
0x0016A0, 0x20, // 'runr' runic
|
||||
0x0016EB, 0x00,
|
||||
0x0016EE, 0x20, // 'runr' runic
|
||||
0x0016F1, 0x00,
|
||||
0x001700, 0x2A, // 'tglg' tagalog
|
||||
0x00170D, 0x00,
|
||||
0x00170E, 0x2A, // 'tglg' tagalog
|
||||
0x001715, 0x00,
|
||||
0x001720, 0x2B, // 'hano' hanunoo
|
||||
0x001735, 0x00,
|
||||
0x001740, 0x2C, // 'buhd' buhid
|
||||
0x001754, 0x00,
|
||||
0x001760, 0x2D, // 'tagb' tagbanwa
|
||||
0x00176D, 0x00,
|
||||
0x00176E, 0x2D, // 'tagb' tagbanwa
|
||||
0x001771, 0x00,
|
||||
0x001772, 0x2D, // 'tagb' tagbanwa
|
||||
0x001774, 0x00,
|
||||
0x001780, 0x17, // 'khmr' khmer
|
||||
0x0017D4, 0x00,
|
||||
0x0017E0, 0x17, // 'khmr' khmer
|
||||
0x0017EA, 0x00,
|
||||
0x00180B, 0x01, // 'qaai' inherited
|
||||
0x00180E, 0x00,
|
||||
0x001810, 0x1B, // 'mong' mongolian
|
||||
0x00181A, 0x00,
|
||||
0x001820, 0x1B, // 'mong' mongolian
|
||||
0x001878, 0x00,
|
||||
0x001880, 0x1B, // 'mong' mongolian
|
||||
0x0018AA, 0x00,
|
||||
0x001E00, 0x19, // 'latn' latin
|
||||
0x001E9C, 0x00,
|
||||
0x001EA0, 0x19, // 'latn' latin
|
||||
0x001EFA, 0x00,
|
||||
0x001F00, 0x0E, // 'grek' greek
|
||||
0x001F16, 0x00,
|
||||
0x001F18, 0x0E, // 'grek' greek
|
||||
0x001F1E, 0x00,
|
||||
0x001F20, 0x0E, // 'grek' greek
|
||||
0x001F46, 0x00,
|
||||
0x001F48, 0x0E, // 'grek' greek
|
||||
0x001F4E, 0x00,
|
||||
0x001F50, 0x0E, // 'grek' greek
|
||||
0x001F58, 0x00,
|
||||
0x001F59, 0x0E, // 'grek' greek
|
||||
0x001F5A, 0x00,
|
||||
0x001F5B, 0x0E, // 'grek' greek
|
||||
0x001F5C, 0x00,
|
||||
0x001F5D, 0x0E, // 'grek' greek
|
||||
0x001F5E, 0x00,
|
||||
0x001F5F, 0x0E, // 'grek' greek
|
||||
0x001F7E, 0x00,
|
||||
0x001F80, 0x0E, // 'grek' greek
|
||||
0x001FB5, 0x00,
|
||||
0x001FB6, 0x0E, // 'grek' greek
|
||||
0x001FBD, 0x00,
|
||||
0x001FBE, 0x0E, // 'grek' greek
|
||||
0x001FBF, 0x00,
|
||||
0x001FC2, 0x0E, // 'grek' greek
|
||||
0x001FC5, 0x00,
|
||||
0x001FC6, 0x0E, // 'grek' greek
|
||||
0x001FCD, 0x00,
|
||||
0x001FD0, 0x0E, // 'grek' greek
|
||||
0x001FD4, 0x00,
|
||||
0x001FD6, 0x0E, // 'grek' greek
|
||||
0x001FDC, 0x00,
|
||||
0x001FE0, 0x0E, // 'grek' greek
|
||||
0x001FED, 0x00,
|
||||
0x001FF2, 0x0E, // 'grek' greek
|
||||
0x001FF5, 0x00,
|
||||
0x001FF6, 0x0E, // 'grek' greek
|
||||
0x001FFD, 0x00,
|
||||
0x002071, 0x19, // 'latn' latin
|
||||
0x002072, 0x00,
|
||||
0x00207F, 0x19, // 'latn' latin
|
||||
0x002080, 0x00,
|
||||
0x0020D0, 0x01, // 'qaai' inherited
|
||||
0x0020EB, 0x00,
|
||||
0x002126, 0x0E, // 'grek' greek
|
||||
0x002127, 0x00,
|
||||
0x00212A, 0x19, // 'latn' latin
|
||||
0x00212C, 0x00,
|
||||
0x002E80, 0x11, // 'hani' han
|
||||
0x002E9A, 0x00,
|
||||
0x002E9B, 0x11, // 'hani' han
|
||||
0x002EF4, 0x00,
|
||||
0x002F00, 0x11, // 'hani' han
|
||||
0x002FD6, 0x00,
|
||||
0x003005, 0x11, // 'hani' han
|
||||
0x003006, 0x00,
|
||||
0x003007, 0x11, // 'hani' han
|
||||
0x003008, 0x00,
|
||||
0x003021, 0x11, // 'hani' han
|
||||
0x00302A, 0x01, // 'qaai' inherited
|
||||
0x003030, 0x00,
|
||||
0x003038, 0x11, // 'hani' han
|
||||
0x00303C, 0x00,
|
||||
0x003041, 0x14, // 'hira' hiragana
|
||||
0x003097, 0x00,
|
||||
0x003099, 0x01, // 'qaai' inherited
|
||||
0x00309B, 0x00,
|
||||
0x00309D, 0x14, // 'hira' hiragana
|
||||
0x0030A0, 0x00,
|
||||
0x0030A1, 0x16, // 'kana' katakana
|
||||
0x0030FB, 0x00,
|
||||
0x0030FD, 0x16, // 'kana' katakana
|
||||
0x003100, 0x00,
|
||||
0x003105, 0x05, // 'bopo' bopomofo
|
||||
0x00312D, 0x00,
|
||||
0x003131, 0x12, // 'hang' hangul
|
||||
0x00318F, 0x00,
|
||||
0x0031A0, 0x05, // 'bopo' bopomofo
|
||||
0x0031B8, 0x00,
|
||||
0x0031F0, 0x16, // 'kana' katakana
|
||||
0x003200, 0x00,
|
||||
0x003400, 0x11, // 'hani' han
|
||||
0x004DB6, 0x00,
|
||||
0x004E00, 0x11, // 'hani' han
|
||||
0x009FA6, 0x00,
|
||||
0x00A000, 0x29, // 'yiii' yi
|
||||
0x00A48D, 0x00,
|
||||
0x00A490, 0x29, // 'yiii' yi
|
||||
0x00A4A2, 0x00,
|
||||
0x00A4A4, 0x29, // 'yiii' yi
|
||||
0x00A4B4, 0x00,
|
||||
0x00A4B5, 0x29, // 'yiii' yi
|
||||
0x00A4C1, 0x00,
|
||||
0x00A4C2, 0x29, // 'yiii' yi
|
||||
0x00A4C5, 0x00,
|
||||
0x00A4C6, 0x29, // 'yiii' yi
|
||||
0x00A4C7, 0x00,
|
||||
0x00AC00, 0x12, // 'hang' hangul
|
||||
0x00D7A4, 0x00,
|
||||
0x00F900, 0x11, // 'hani' han
|
||||
0x00FA2E, 0x00,
|
||||
0x00FA30, 0x11, // 'hani' han
|
||||
0x00FA6B, 0x00,
|
||||
0x00FB00, 0x19, // 'latn' latin
|
||||
0x00FB07, 0x00,
|
||||
0x00FB13, 0x03, // 'armn' armenian
|
||||
0x00FB18, 0x00,
|
||||
0x00FB1D, 0x13, // 'hebr' hebrew
|
||||
0x00FB1E, 0x01, // 'qaai' inherited
|
||||
0x00FB1F, 0x13, // 'hebr' hebrew
|
||||
0x00FB29, 0x00,
|
||||
0x00FB2A, 0x13, // 'hebr' hebrew
|
||||
0x00FB37, 0x00,
|
||||
0x00FB38, 0x13, // 'hebr' hebrew
|
||||
0x00FB3D, 0x00,
|
||||
0x00FB3E, 0x13, // 'hebr' hebrew
|
||||
0x00FB3F, 0x00,
|
||||
0x00FB40, 0x13, // 'hebr' hebrew
|
||||
0x00FB42, 0x00,
|
||||
0x00FB43, 0x13, // 'hebr' hebrew
|
||||
0x00FB45, 0x00,
|
||||
0x00FB46, 0x13, // 'hebr' hebrew
|
||||
0x00FB50, 0x02, // 'arab' arabic
|
||||
0x00FBB2, 0x00,
|
||||
0x00FBD3, 0x02, // 'arab' arabic
|
||||
0x00FD3E, 0x00,
|
||||
0x00FD50, 0x02, // 'arab' arabic
|
||||
0x00FD90, 0x00,
|
||||
0x00FD92, 0x02, // 'arab' arabic
|
||||
0x00FDC8, 0x00,
|
||||
0x00FDF0, 0x02, // 'arab' arabic
|
||||
0x00FDFC, 0x00,
|
||||
0x00FE00, 0x01, // 'qaai' inherited
|
||||
0x00FE10, 0x00,
|
||||
0x00FE20, 0x01, // 'qaai' inherited
|
||||
0x00FE24, 0x00,
|
||||
0x00FE70, 0x02, // 'arab' arabic
|
||||
0x00FE75, 0x00,
|
||||
0x00FE76, 0x02, // 'arab' arabic
|
||||
0x00FEFD, 0x00,
|
||||
0x00FF21, 0x19, // 'latn' latin
|
||||
0x00FF3B, 0x00,
|
||||
0x00FF41, 0x19, // 'latn' latin
|
||||
0x00FF5B, 0x00,
|
||||
0x00FF66, 0x16, // 'kana' katakana
|
||||
0x00FF70, 0x00,
|
||||
0x00FF71, 0x16, // 'kana' katakana
|
||||
0x00FF9E, 0x00,
|
||||
0x00FFA0, 0x12, // 'hang' hangul
|
||||
0x00FFBF, 0x00,
|
||||
0x00FFC2, 0x12, // 'hang' hangul
|
||||
0x00FFC8, 0x00,
|
||||
0x00FFCA, 0x12, // 'hang' hangul
|
||||
0x00FFD0, 0x00,
|
||||
0x00FFD2, 0x12, // 'hang' hangul
|
||||
0x00FFD8, 0x00,
|
||||
0x00FFDA, 0x12, // 'hang' hangul
|
||||
0x00FFDD, 0x00,
|
||||
0x010300, 0x1E, // 'ital' old_italic
|
||||
0x01031F, 0x00,
|
||||
0x010330, 0x0D, // 'goth' gothic
|
||||
0x01034B, 0x00,
|
||||
0x010400, 0x09, // 'dsrt' deseret
|
||||
0x010426, 0x00,
|
||||
0x010428, 0x09, // 'dsrt' deseret
|
||||
0x01044E, 0x00,
|
||||
0x01D167, 0x01, // 'qaai' inherited
|
||||
0x01D16A, 0x00,
|
||||
0x01D17B, 0x01, // 'qaai' inherited
|
||||
0x01D183, 0x00,
|
||||
0x01D185, 0x01, // 'qaai' inherited
|
||||
0x01D18C, 0x00,
|
||||
0x01D1AA, 0x01, // 'qaai' inherited
|
||||
0x01D1AE, 0x00,
|
||||
0x020000, 0x11, // 'hani' han
|
||||
0x02A6D7, 0x00,
|
||||
0x02F800, 0x11, // 'hani' han
|
||||
0x02FA1E, 0x00,
|
||||
0x110000, -1, // (NO NAME)
|
||||
};
|
||||
|
||||
private static final int dataPower = 1 << 10;
|
||||
private static final int dataExtra = data.length - dataPower;
|
||||
}
|
||||
1944
jdkSrc/jdk8/sun/font/StandardGlyphVector.java
Normal file
1944
jdkSrc/jdk8/sun/font/StandardGlyphVector.java
Normal file
File diff suppressed because it is too large
Load Diff
224
jdkSrc/jdk8/sun/font/StandardTextSource.java
Normal file
224
jdkSrc/jdk8/sun/font/StandardTextSource.java
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-2003 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.LineMetrics;
|
||||
|
||||
final class StandardTextSource extends TextSource {
|
||||
|
||||
private final char[] chars;
|
||||
private final int start;
|
||||
private final int len;
|
||||
private final int cstart;
|
||||
private final int clen;
|
||||
private final int level; // assumed all uniform
|
||||
private final int flags; // see GlyphVector.java
|
||||
private final Font font;
|
||||
private final FontRenderContext frc;
|
||||
private final CoreMetrics cm;
|
||||
|
||||
/**
|
||||
* Create a simple implementation of a TextSource.
|
||||
*
|
||||
* Chars is an array containing clen chars in the context, in
|
||||
* logical order, contiguously starting at cstart. Start and len
|
||||
* represent that portion of the context representing the true
|
||||
* source; start, like cstart, is relative to the start of the
|
||||
* character array.
|
||||
*
|
||||
* Level is the bidi level (0-63 for the entire context. Flags is
|
||||
* the layout flags. Font is the font, frc is the render context,
|
||||
* and lm is the line metrics for the entire source text, but not
|
||||
* necessarily the context.
|
||||
*/
|
||||
StandardTextSource(char[] chars,
|
||||
int start,
|
||||
int len,
|
||||
int cstart,
|
||||
int clen,
|
||||
int level,
|
||||
int flags,
|
||||
Font font,
|
||||
FontRenderContext frc,
|
||||
CoreMetrics cm) {
|
||||
if (chars == null) {
|
||||
throw new IllegalArgumentException("bad chars: null");
|
||||
}
|
||||
if (cstart < 0) {
|
||||
throw new IllegalArgumentException("bad cstart: " + cstart);
|
||||
}
|
||||
if (start < cstart) {
|
||||
throw new IllegalArgumentException("bad start: " + start + " for cstart: " + cstart);
|
||||
}
|
||||
if (clen < 0) {
|
||||
throw new IllegalArgumentException("bad clen: " + clen);
|
||||
}
|
||||
if (cstart + clen > chars.length) {
|
||||
throw new IllegalArgumentException("bad clen: " + clen + " cstart: " + cstart + " for array len: " + chars.length);
|
||||
}
|
||||
if (len < 0) {
|
||||
throw new IllegalArgumentException("bad len: " + len);
|
||||
}
|
||||
if ((start + len) > (cstart + clen)) {
|
||||
throw new IllegalArgumentException("bad len: " + len + " start: " + start + " for cstart: " + cstart + " clen: " + clen);
|
||||
}
|
||||
if (font == null) {
|
||||
throw new IllegalArgumentException("bad font: null");
|
||||
}
|
||||
if (frc == null) {
|
||||
throw new IllegalArgumentException("bad frc: null");
|
||||
}
|
||||
|
||||
this.chars = chars;
|
||||
this.start = start;
|
||||
this.len = len;
|
||||
this.cstart = cstart;
|
||||
this.clen = clen;
|
||||
this.level = level;
|
||||
this.flags = flags;
|
||||
this.font = font;
|
||||
this.frc = frc;
|
||||
|
||||
if (cm != null) {
|
||||
this.cm = cm;
|
||||
} else {
|
||||
LineMetrics metrics = font.getLineMetrics(chars, cstart, clen, frc);
|
||||
this.cm = ((FontLineMetrics)metrics).cm;
|
||||
}
|
||||
}
|
||||
|
||||
// TextSource API
|
||||
|
||||
public char[] getChars() {
|
||||
return chars;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return len;
|
||||
}
|
||||
|
||||
public int getContextStart() {
|
||||
return cstart;
|
||||
}
|
||||
|
||||
public int getContextLength() {
|
||||
return clen;
|
||||
}
|
||||
|
||||
public int getLayoutFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public int getBidiLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public Font getFont() {
|
||||
return font;
|
||||
}
|
||||
|
||||
public FontRenderContext getFRC() {
|
||||
return frc;
|
||||
}
|
||||
|
||||
public CoreMetrics getCoreMetrics() {
|
||||
return cm;
|
||||
}
|
||||
|
||||
public TextSource getSubSource(int start, int length, int dir) {
|
||||
if (start < 0 || length < 0 || (start + length) > len) {
|
||||
throw new IllegalArgumentException("bad start (" + start + ") or length (" + length + ")");
|
||||
}
|
||||
|
||||
int level = this.level;
|
||||
if (dir != TextLineComponent.UNCHANGED) {
|
||||
boolean ltr = (flags & 0x8) == 0;
|
||||
if (!(dir == TextLineComponent.LEFT_TO_RIGHT && ltr) &&
|
||||
!(dir == TextLineComponent.RIGHT_TO_LEFT && !ltr)) {
|
||||
throw new IllegalArgumentException("direction flag is invalid");
|
||||
}
|
||||
level = ltr? 0 : 1;
|
||||
}
|
||||
|
||||
return new StandardTextSource(chars, this.start + start, length, cstart, clen, level, flags, font, frc, cm);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toString(WITH_CONTEXT);
|
||||
}
|
||||
|
||||
public String toString(boolean withContext) {
|
||||
StringBuffer buf = new StringBuffer(super.toString());
|
||||
buf.append("[start:");
|
||||
buf.append(start);
|
||||
buf.append(", len:" );
|
||||
buf.append(len);
|
||||
buf.append(", cstart:");
|
||||
buf.append(cstart);
|
||||
buf.append(", clen:" );
|
||||
buf.append(clen);
|
||||
buf.append(", chars:\"");
|
||||
int chStart, chLimit;
|
||||
if (withContext == WITH_CONTEXT) {
|
||||
chStart = cstart;
|
||||
chLimit = cstart + clen;
|
||||
}
|
||||
else {
|
||||
chStart = start;
|
||||
chLimit = start + len;
|
||||
}
|
||||
for (int i = chStart; i < chLimit; ++i) {
|
||||
if (i > chStart) {
|
||||
buf.append(" ");
|
||||
}
|
||||
buf.append(Integer.toHexString(chars[i]));
|
||||
}
|
||||
buf.append("\"");
|
||||
buf.append(", level:");
|
||||
buf.append(level);
|
||||
buf.append(", flags:");
|
||||
buf.append(flags);
|
||||
buf.append(", font:");
|
||||
buf.append(font);
|
||||
buf.append(", frc:");
|
||||
buf.append(frc);
|
||||
buf.append(", cm:");
|
||||
buf.append(cm);
|
||||
buf.append("]");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
444
jdkSrc/jdk8/sun/font/StrikeCache.java
Normal file
444
jdkSrc/jdk8/sun/font/StrikeCache.java
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
|
||||
import sun.java2d.Disposer;
|
||||
import sun.java2d.pipe.BufferedContext;
|
||||
import sun.java2d.pipe.RenderQueue;
|
||||
import sun.java2d.pipe.hw.AccelGraphicsConfig;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
/**
|
||||
|
||||
A FontStrike is the keeper of scaled glyph image data which is expensive
|
||||
to compute so needs to be cached.
|
||||
So long as that data may be being used it cannot be invalidated.
|
||||
Yet we also need to limit the amount of native memory and number of
|
||||
strike objects in use.
|
||||
For scaleability and ease of use, a key goal is multi-threaded read
|
||||
access to a strike, so that it may be shared by multiple client objects,
|
||||
potentially executing on different threads, with no special reference
|
||||
counting or "check-out/check-in" requirements which would pass on the
|
||||
burden of keeping track of strike references to the SG2D and other clients.
|
||||
|
||||
A cache of strikes is maintained via Reference objects.
|
||||
This helps in two ways :
|
||||
1. The VM will free references when memory is low or they have not been
|
||||
used in a long time.
|
||||
2. Reference queues provide a way to get notification of this so we can
|
||||
free native memory resources.
|
||||
|
||||
*/
|
||||
|
||||
public final class StrikeCache {
|
||||
|
||||
static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
static ReferenceQueue refQueue = Disposer.getQueue();
|
||||
|
||||
static ArrayList<GlyphDisposedListener> disposeListeners = new ArrayList<GlyphDisposedListener>(1);
|
||||
|
||||
|
||||
/* Reference objects may have their referents cleared when GC chooses.
|
||||
* During application client start-up there is typically at least one
|
||||
* GC which causes the hotspot VM to clear soft (not just weak) references
|
||||
* Thus not only is there a GC pause, but the work done do rasterise
|
||||
* glyphs that are fairly certain to be needed again almost immediately
|
||||
* is thrown away. So for performance reasons a simple optimisation is to
|
||||
* keep up to 8 strong references to strikes to reduce the chance of
|
||||
* GC'ing strikes that have been used recently. Note that this may not
|
||||
* suffice in Solaris UTF-8 locales where a single composite strike may be
|
||||
* composed of 15 individual strikes, plus the composite strike.
|
||||
* And this assumes the new architecture doesn't maintain strikes for
|
||||
* natively accessed bitmaps. It may be worth "tuning" the number of
|
||||
* strikes kept around for the platform or locale.
|
||||
* Since no attempt is made to ensure uniqueness or ensure synchronized
|
||||
* access there is no guarantee that this cache will ensure that unique
|
||||
* strikes are cached. Every time a strike is looked up it is added
|
||||
* to the current index in this cache. All this cache has to do to be
|
||||
* worthwhile is prevent excessive cache flushing of strikes that are
|
||||
* referenced frequently. The logic that adds references here could be
|
||||
* tweaked to keep only strikes that represent untransformed, screen
|
||||
* sizes as that's the typical performance case.
|
||||
*/
|
||||
static int MINSTRIKES = 8; // can be overridden by property
|
||||
static int recentStrikeIndex = 0;
|
||||
static FontStrike[] recentStrikes;
|
||||
static boolean cacheRefTypeWeak;
|
||||
|
||||
/*
|
||||
* Native sizes and offsets for glyph cache
|
||||
* There are 10 values.
|
||||
*/
|
||||
static int nativeAddressSize;
|
||||
static int glyphInfoSize;
|
||||
static int xAdvanceOffset;
|
||||
static int yAdvanceOffset;
|
||||
static int boundsOffset;
|
||||
static int widthOffset;
|
||||
static int heightOffset;
|
||||
static int rowBytesOffset;
|
||||
static int topLeftXOffset;
|
||||
static int topLeftYOffset;
|
||||
static int pixelDataOffset;
|
||||
static int cacheCellOffset;
|
||||
static int managedOffset;
|
||||
static long invisibleGlyphPtr;
|
||||
|
||||
/* Native method used to return information used for unsafe
|
||||
* access to native data.
|
||||
* return values as follows:-
|
||||
* arr[0] = size of an address/pointer.
|
||||
* arr[1] = size of a GlyphInfo
|
||||
* arr[2] = offset of advanceX
|
||||
* arr[3] = offset of advanceY
|
||||
* arr[4] = offset of width
|
||||
* arr[5] = offset of height
|
||||
* arr[6] = offset of rowBytes
|
||||
* arr[7] = offset of topLeftX
|
||||
* arr[8] = offset of topLeftY
|
||||
* arr[9] = offset of pixel data.
|
||||
* arr[10] = address of a GlyphImageRef representing the invisible glyph
|
||||
*/
|
||||
static native void getGlyphCacheDescription(long[] infoArray);
|
||||
|
||||
static {
|
||||
|
||||
long[] nativeInfo = new long[13];
|
||||
getGlyphCacheDescription(nativeInfo);
|
||||
//Can also get address size from Unsafe class :-
|
||||
//nativeAddressSize = unsafe.addressSize();
|
||||
nativeAddressSize = (int)nativeInfo[0];
|
||||
glyphInfoSize = (int)nativeInfo[1];
|
||||
xAdvanceOffset = (int)nativeInfo[2];
|
||||
yAdvanceOffset = (int)nativeInfo[3];
|
||||
widthOffset = (int)nativeInfo[4];
|
||||
heightOffset = (int)nativeInfo[5];
|
||||
rowBytesOffset = (int)nativeInfo[6];
|
||||
topLeftXOffset = (int)nativeInfo[7];
|
||||
topLeftYOffset = (int)nativeInfo[8];
|
||||
pixelDataOffset = (int)nativeInfo[9];
|
||||
invisibleGlyphPtr = nativeInfo[10];
|
||||
cacheCellOffset = (int) nativeInfo[11];
|
||||
managedOffset = (int) nativeInfo[12];
|
||||
|
||||
if (nativeAddressSize < 4) {
|
||||
throw new InternalError("Unexpected address size for font data: " +
|
||||
nativeAddressSize);
|
||||
}
|
||||
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction() {
|
||||
public Object run() {
|
||||
|
||||
/* Allow a client to override the reference type used to
|
||||
* cache strikes. The default is "soft" which hints to keep
|
||||
* the strikes around. This property allows the client to
|
||||
* override this to "weak" which hint to the GC to free
|
||||
* memory more aggressively.
|
||||
*/
|
||||
String refType =
|
||||
System.getProperty("sun.java2d.font.reftype", "soft");
|
||||
cacheRefTypeWeak = refType.equals("weak");
|
||||
|
||||
String minStrikesStr =
|
||||
System.getProperty("sun.java2d.font.minstrikes");
|
||||
if (minStrikesStr != null) {
|
||||
try {
|
||||
MINSTRIKES = Integer.parseInt(minStrikesStr);
|
||||
if (MINSTRIKES <= 0) {
|
||||
MINSTRIKES = 1;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
|
||||
recentStrikes = new FontStrike[MINSTRIKES];
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
static void refStrike(FontStrike strike) {
|
||||
int index = recentStrikeIndex;
|
||||
recentStrikes[index] = strike;
|
||||
index++;
|
||||
if (index == MINSTRIKES) {
|
||||
index = 0;
|
||||
}
|
||||
recentStrikeIndex = index;
|
||||
}
|
||||
|
||||
private static final void doDispose(FontStrikeDisposer disposer) {
|
||||
if (disposer.intGlyphImages != null) {
|
||||
freeCachedIntMemory(disposer.intGlyphImages,
|
||||
disposer.pScalerContext);
|
||||
} else if (disposer.longGlyphImages != null) {
|
||||
freeCachedLongMemory(disposer.longGlyphImages,
|
||||
disposer.pScalerContext);
|
||||
} else if (disposer.segIntGlyphImages != null) {
|
||||
/* NB Now making multiple JNI calls in this case.
|
||||
* But assuming that there's a reasonable amount of locality
|
||||
* rather than sparse references then it should be OK.
|
||||
*/
|
||||
for (int i=0; i<disposer.segIntGlyphImages.length; i++) {
|
||||
if (disposer.segIntGlyphImages[i] != null) {
|
||||
freeCachedIntMemory(disposer.segIntGlyphImages[i],
|
||||
disposer.pScalerContext);
|
||||
/* native will only free the scaler context once */
|
||||
disposer.pScalerContext = 0L;
|
||||
disposer.segIntGlyphImages[i] = null;
|
||||
}
|
||||
}
|
||||
/* This may appear inefficient but it should only be invoked
|
||||
* for a strike that never was asked to rasterise a glyph.
|
||||
*/
|
||||
if (disposer.pScalerContext != 0L) {
|
||||
freeCachedIntMemory(new int[0], disposer.pScalerContext);
|
||||
}
|
||||
} else if (disposer.segLongGlyphImages != null) {
|
||||
for (int i=0; i<disposer.segLongGlyphImages.length; i++) {
|
||||
if (disposer.segLongGlyphImages[i] != null) {
|
||||
freeCachedLongMemory(disposer.segLongGlyphImages[i],
|
||||
disposer.pScalerContext);
|
||||
disposer.pScalerContext = 0L;
|
||||
disposer.segLongGlyphImages[i] = null;
|
||||
}
|
||||
}
|
||||
if (disposer.pScalerContext != 0L) {
|
||||
freeCachedLongMemory(new long[0], disposer.pScalerContext);
|
||||
}
|
||||
} else if (disposer.pScalerContext != 0L) {
|
||||
/* Rarely a strike may have been created that never cached
|
||||
* any glyphs. In this case we still want to free the scaler
|
||||
* context.
|
||||
*/
|
||||
if (longAddresses()) {
|
||||
freeCachedLongMemory(new long[0], disposer.pScalerContext);
|
||||
} else {
|
||||
freeCachedIntMemory(new int[0], disposer.pScalerContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean longAddresses() {
|
||||
return nativeAddressSize == 8;
|
||||
}
|
||||
|
||||
static void disposeStrike(final FontStrikeDisposer disposer) {
|
||||
// we need to execute the strike disposal on the rendering thread
|
||||
// because they may be accessed on that thread at the time of the
|
||||
// disposal (for example, when the accel. cache is invalidated)
|
||||
|
||||
// Whilst this is a bit heavyweight, in most applications
|
||||
// strike disposal is a relatively infrequent operation, so it
|
||||
// doesn't matter. But in some tests that use vast numbers
|
||||
// of strikes, the switching back and forth is measurable.
|
||||
// So the "pollRemove" call is added to batch up the work.
|
||||
// If we are polling we know we've already been called back
|
||||
// and can directly dispose the record.
|
||||
// Also worrisome is the necessity of getting a GC here.
|
||||
|
||||
if (Disposer.pollingQueue) {
|
||||
doDispose(disposer);
|
||||
return;
|
||||
}
|
||||
|
||||
RenderQueue rq = null;
|
||||
GraphicsEnvironment ge =
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
if (!ge.isHeadless()) {
|
||||
GraphicsConfiguration gc =
|
||||
ge.getDefaultScreenDevice().getDefaultConfiguration();
|
||||
if (gc instanceof AccelGraphicsConfig) {
|
||||
AccelGraphicsConfig agc = (AccelGraphicsConfig)gc;
|
||||
BufferedContext bc = agc.getContext();
|
||||
if (bc != null) {
|
||||
rq = bc.getRenderQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rq != null) {
|
||||
rq.lock();
|
||||
try {
|
||||
rq.flushAndInvokeNow(new Runnable() {
|
||||
public void run() {
|
||||
doDispose(disposer);
|
||||
Disposer.pollRemove();
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
rq.unlock();
|
||||
}
|
||||
} else {
|
||||
doDispose(disposer);
|
||||
}
|
||||
}
|
||||
|
||||
static native void freeIntPointer(int ptr);
|
||||
static native void freeLongPointer(long ptr);
|
||||
private static native void freeIntMemory(int[] glyphPtrs, long pContext);
|
||||
private static native void freeLongMemory(long[] glyphPtrs, long pContext);
|
||||
|
||||
private static void freeCachedIntMemory(int[] glyphPtrs, long pContext) {
|
||||
synchronized(disposeListeners) {
|
||||
if (disposeListeners.size() > 0) {
|
||||
ArrayList<Long> gids = null;
|
||||
|
||||
for (int i = 0; i < glyphPtrs.length; i++) {
|
||||
if (glyphPtrs[i] != 0 && unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
|
||||
|
||||
if (gids == null) {
|
||||
gids = new ArrayList<Long>();
|
||||
}
|
||||
gids.add((long) glyphPtrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (gids != null) {
|
||||
// Any reference by the disposers to the native glyph ptrs
|
||||
// must be done before this returns.
|
||||
notifyDisposeListeners(gids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeIntMemory(glyphPtrs, pContext);
|
||||
}
|
||||
|
||||
private static void freeCachedLongMemory(long[] glyphPtrs, long pContext) {
|
||||
synchronized(disposeListeners) {
|
||||
if (disposeListeners.size() > 0) {
|
||||
ArrayList<Long> gids = null;
|
||||
|
||||
for (int i=0; i < glyphPtrs.length; i++) {
|
||||
if (glyphPtrs[i] != 0
|
||||
&& unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
|
||||
|
||||
if (gids == null) {
|
||||
gids = new ArrayList<Long>();
|
||||
}
|
||||
gids.add((long) glyphPtrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (gids != null) {
|
||||
// Any reference by the disposers to the native glyph ptrs
|
||||
// must be done before this returns.
|
||||
notifyDisposeListeners(gids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeLongMemory(glyphPtrs, pContext);
|
||||
}
|
||||
|
||||
public static void addGlyphDisposedListener(GlyphDisposedListener listener) {
|
||||
synchronized(disposeListeners) {
|
||||
disposeListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyDisposeListeners(ArrayList<Long> glyphs) {
|
||||
for (GlyphDisposedListener listener : disposeListeners) {
|
||||
listener.glyphDisposed(glyphs);
|
||||
}
|
||||
}
|
||||
|
||||
public static Reference getStrikeRef(FontStrike strike) {
|
||||
return getStrikeRef(strike, cacheRefTypeWeak);
|
||||
}
|
||||
|
||||
public static Reference getStrikeRef(FontStrike strike, boolean weak) {
|
||||
/* Some strikes may have no disposer as there's nothing
|
||||
* for them to free, as they allocated no native resource
|
||||
* eg, if they did not allocate resources because of a problem,
|
||||
* or they never hold native resources. So they create no disposer.
|
||||
* But any strike that reaches here that has a null disposer is
|
||||
* a potential memory leak.
|
||||
*/
|
||||
if (strike.disposer == null) {
|
||||
if (weak) {
|
||||
return new WeakReference(strike);
|
||||
} else {
|
||||
return new SoftReference(strike);
|
||||
}
|
||||
}
|
||||
|
||||
if (weak) {
|
||||
return new WeakDisposerRef(strike);
|
||||
} else {
|
||||
return new SoftDisposerRef(strike);
|
||||
}
|
||||
}
|
||||
|
||||
static interface DisposableStrike {
|
||||
FontStrikeDisposer getDisposer();
|
||||
}
|
||||
|
||||
static class SoftDisposerRef
|
||||
extends SoftReference implements DisposableStrike {
|
||||
|
||||
private FontStrikeDisposer disposer;
|
||||
|
||||
public FontStrikeDisposer getDisposer() {
|
||||
return disposer;
|
||||
}
|
||||
|
||||
SoftDisposerRef(FontStrike strike) {
|
||||
super(strike, StrikeCache.refQueue);
|
||||
disposer = strike.disposer;
|
||||
Disposer.addReference(this, disposer);
|
||||
}
|
||||
}
|
||||
|
||||
static class WeakDisposerRef
|
||||
extends WeakReference implements DisposableStrike {
|
||||
|
||||
private FontStrikeDisposer disposer;
|
||||
|
||||
public FontStrikeDisposer getDisposer() {
|
||||
return disposer;
|
||||
}
|
||||
|
||||
WeakDisposerRef(FontStrike strike) {
|
||||
super(strike, StrikeCache.refQueue);
|
||||
disposer = strike.disposer;
|
||||
Disposer.addReference(this, disposer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
199
jdkSrc/jdk8/sun/font/StrikeMetrics.java
Normal file
199
jdkSrc/jdk8/sun/font/StrikeMetrics.java
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
/* These are font metrics: they are in user space, not device space.
|
||||
* Hence they are not truly "strike" metrics. However it is convenient to
|
||||
* treat them as such since we need to have a scaler context to obtain them
|
||||
* and also to cache them. The old implementation obtained a C++ strike object
|
||||
* that matched the Font TX + pt size only. It was wasteful of strike objects.
|
||||
* This new implementation still has separate StrikeMetrics for 2 fonts that
|
||||
* are really the same but are used in different device transforms, but at
|
||||
* least it doesn't create a whole new strike just to get the metrics for
|
||||
* a strike in a transformed graphics.
|
||||
* So these metrics do not take into account the device transform. They
|
||||
* are considered inherent properties of the font. Hence it may be that we
|
||||
* should use the device transform to obtain the most accurate metrics, but
|
||||
* typically 1.1 APIs do not provide for this. So some APIs may want to
|
||||
* ignore the dev. tx and others may want to use it, and then apply an
|
||||
* inverse transform. For now we ignore the dev. tx.
|
||||
* "Font" metrics are representative of a typical glyph in the font.
|
||||
* Generally speaking these values are the choice of the font designer and
|
||||
* are stored in the font, from which we retrieve the values. They do
|
||||
* not necessarily equate to the maximum bounds of all glyphs in the font.
|
||||
* Note that the ascent fields are typically a -ve value as we use a top-left
|
||||
* origin user space, and text is positioned relative to its baseline.
|
||||
*/
|
||||
public final class StrikeMetrics {
|
||||
|
||||
public float ascentX;
|
||||
public float ascentY;
|
||||
public float descentX;
|
||||
public float descentY;
|
||||
public float baselineX;
|
||||
public float baselineY;
|
||||
public float leadingX;
|
||||
public float leadingY;
|
||||
public float maxAdvanceX;
|
||||
public float maxAdvanceY;
|
||||
|
||||
|
||||
/* The no-args constructor is used by CompositeStrike, which then
|
||||
* merges in the metrics of physical fonts.
|
||||
* The approach here is the same as earlier releases but it is flawed
|
||||
* take for example the following which ignores leading for simplicity.
|
||||
* Say we have a composite with an element asc=-9, dsc=2, and another with
|
||||
* asc=-7, dsc=3. The merged font is (-9,3) for height of -(-9)+3=12.
|
||||
* Suppose this same font has been derived with a 180% rotation
|
||||
* Now its signs for ascent/descent are reversed. Its (9,-2) and (7,-3)
|
||||
* Its merged values are (using the code in this class) (7,-2) for
|
||||
* a height of -(7)+-2 = =-9!
|
||||
* We need to have a more intelligent merging algorithm,
|
||||
* which so far as I can see needs to apply an inverse of the font
|
||||
* tx, do its merging, and then reapply the font tx.
|
||||
* This wouldn't often be a problem as there rarely is a font TX, and
|
||||
* the tricky part is getting the information. Probably the no-args
|
||||
* constructor needs to pass a TX in to be applied to all merges.
|
||||
* CompositeStrike would be left with the problem of figuring out what
|
||||
* tx to use.
|
||||
* But at least for now we are probably no worse than 1.4 ...
|
||||
* REMIND: FIX THIS.
|
||||
*/
|
||||
StrikeMetrics() {
|
||||
ascentX = ascentY = Integer.MAX_VALUE;
|
||||
descentX = descentY = leadingX = leadingY = Integer.MIN_VALUE;
|
||||
baselineX = baselineX = maxAdvanceX = maxAdvanceY = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
StrikeMetrics(float ax, float ay, float dx, float dy, float bx, float by,
|
||||
float lx, float ly, float mx, float my) {
|
||||
ascentX = ax;
|
||||
ascentY = ay;
|
||||
descentX = dx;
|
||||
descentY = dy;
|
||||
baselineX = bx;
|
||||
baselineY = by;
|
||||
leadingX = lx;
|
||||
leadingY = ly;
|
||||
maxAdvanceX = mx;
|
||||
maxAdvanceY = my;
|
||||
}
|
||||
|
||||
public float getAscent() {
|
||||
return -ascentY;
|
||||
}
|
||||
|
||||
public float getDescent() {
|
||||
return descentY;
|
||||
}
|
||||
|
||||
public float getLeading() {
|
||||
return leadingY;
|
||||
}
|
||||
|
||||
public float getMaxAdvance() {
|
||||
return maxAdvanceX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently only used to merge together slot metrics to create
|
||||
* the metrics for a composite font.
|
||||
*/
|
||||
void merge(StrikeMetrics other) {
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
if (other.ascentX < ascentX) {
|
||||
ascentX = other.ascentX;
|
||||
}
|
||||
if (other.ascentY < ascentY) {
|
||||
ascentY = other.ascentY;
|
||||
}
|
||||
if (other.descentX > descentX) {
|
||||
descentX = other.descentX;
|
||||
}
|
||||
if (other.descentY > descentY) {
|
||||
descentY = other.descentY;
|
||||
}
|
||||
if (other.baselineX > baselineX) {
|
||||
baselineX = other.baselineX;
|
||||
}
|
||||
if (other.baselineY > baselineY) {
|
||||
baselineY = other.baselineY;
|
||||
}
|
||||
if (other.leadingX > leadingX) {
|
||||
leadingX = other.leadingX;
|
||||
}
|
||||
if (other.leadingY > leadingY) {
|
||||
leadingY = other.leadingY;
|
||||
}
|
||||
if (other.maxAdvanceX > maxAdvanceX) {
|
||||
maxAdvanceX = other.maxAdvanceX;
|
||||
}
|
||||
if (other.maxAdvanceY > maxAdvanceY) {
|
||||
maxAdvanceY = other.maxAdvanceY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Used to transform the values back into user space.
|
||||
* This is done ONCE by the strike so clients should not need
|
||||
* to worry about this
|
||||
*/
|
||||
void convertToUserSpace(AffineTransform invTx) {
|
||||
Point2D.Float pt2D = new Point2D.Float();
|
||||
|
||||
pt2D.x = ascentX; pt2D.y = ascentY;
|
||||
invTx.deltaTransform(pt2D, pt2D);
|
||||
ascentX = pt2D.x; ascentY = pt2D.y;
|
||||
|
||||
pt2D.x = descentX; pt2D.y = descentY;
|
||||
invTx.deltaTransform(pt2D, pt2D);
|
||||
descentX = pt2D.x; descentY = pt2D.y;
|
||||
|
||||
pt2D.x = baselineX; pt2D.y = baselineY;
|
||||
invTx.deltaTransform(pt2D, pt2D);
|
||||
baselineX = pt2D.x; baselineY = pt2D.y;
|
||||
|
||||
pt2D.x = leadingX; pt2D.y = leadingY;
|
||||
invTx.deltaTransform(pt2D, pt2D);
|
||||
leadingX = pt2D.x; leadingY = pt2D.y;
|
||||
|
||||
pt2D.x = maxAdvanceX; pt2D.y = maxAdvanceY;
|
||||
invTx.deltaTransform(pt2D, pt2D);
|
||||
maxAdvanceX = pt2D.x; maxAdvanceY = pt2D.y;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ascent:x=" + ascentX + " y=" + ascentY +
|
||||
" descent:x=" + descentX + " y=" + descentY +
|
||||
" baseline:x=" + baselineX + " y=" + baselineY +
|
||||
" leading:x=" + leadingX + " y=" + leadingY +
|
||||
" maxAdvance:x=" + maxAdvanceX + " y=" + maxAdvanceY;
|
||||
}
|
||||
}
|
||||
3947
jdkSrc/jdk8/sun/font/SunFontManager.java
Normal file
3947
jdkSrc/jdk8/sun/font/SunFontManager.java
Normal file
File diff suppressed because it is too large
Load Diff
198
jdkSrc/jdk8/sun/font/SunLayoutEngine.java
Normal file
198
jdkSrc/jdk8/sun/font/SunLayoutEngine.java
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2003 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import sun.font.GlyphLayout.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/*
|
||||
* different ways to do this
|
||||
* 1) each physical font2d keeps a hashtable mapping scripts to layout
|
||||
* engines, we query and fill this cache.
|
||||
* 2) we keep a mapping independent of font using the key Most likely
|
||||
* few fonts will be used, so option 2 seems better
|
||||
*
|
||||
* Once we know which engine to use for a font, we always know, so we
|
||||
* shouldn't have to recheck each time we do layout. So the cache is
|
||||
* ok.
|
||||
*
|
||||
* Should we reuse engines? We could instantiate an engine for each
|
||||
* font/script pair. The engine would hold onto the table(s) from the
|
||||
* font that it needs. If we have multiple threads using the same
|
||||
* engine, we still need to keep the state separate, so the native
|
||||
* engines would still need to be allocated for each call, since they
|
||||
* keep their state in themselves. If they used the passed-in GVData
|
||||
* arrays directly (with some checks for space) then since each GVData
|
||||
* is different per thread, we could reuse the layout engines. This
|
||||
* still requires a separate layout engine per font, because of the
|
||||
* table state in the engine. If we pushed that out too and passed it
|
||||
* in with the native call as well, we'd be ok if the layout engines
|
||||
* keep all their process state on the stack, but I don't know if this
|
||||
* is true. Then we'd basically just be down to an engine index which
|
||||
* we pass into native and then invoke the engine code (now a
|
||||
* procedure call, not an object invocation) based on a switch on the
|
||||
* index. There would be only half a dozen engine objects then, not
|
||||
* potentially half a dozen per font. But we'd have to stack-allocate
|
||||
* some state that included the pointer to the required font tables.
|
||||
*
|
||||
* Seems for now that the way to do things is to come in with a
|
||||
* selector and the font. The selector indicates which engine to use,
|
||||
* the engine is stack allocated and initialized with the required
|
||||
* font tables (the selector indicates which). Then layout is called,
|
||||
* the contents are copied (or not), and the stack is destroyed on
|
||||
* exit. So the association is between the font/script (layout engine
|
||||
* desc) and and one of a few permanent engine objects, which are
|
||||
* handed the key when they need to process something. In the native
|
||||
* case, the engine holds an index, and just passes it together with
|
||||
* the key info down to native. Some default cases are the 'default
|
||||
* layout' case that just runs the c2gmapper, this stays in java and
|
||||
* just uses the mapper from the font/strike. Another default case
|
||||
* might be the unicode arabic shaper, since this doesn't care about
|
||||
* the font (or script or lang?) it wouldn't need to extract this
|
||||
* data. It could be (yikes) ported back to java even to avoid
|
||||
* upcalls to check if the font supports a particular unicode
|
||||
* character.
|
||||
*
|
||||
* I'd expect that the majority of scripts use the default mapper for
|
||||
* a particular font. Loading the hastable with 40 or so keys 30+ of
|
||||
* which all map to the same object is unfortunate. It might be worth
|
||||
* instead having a per-font list of 'scripts with non-default
|
||||
* engines', e.g. the factory has a hashtable mapping fonts to 'script
|
||||
* lists' (the factory has this since the design potentially has other
|
||||
* factories, though I admit there's no client for this yet and no
|
||||
* public api) and then the script list is queried for the script in
|
||||
* question. it can be preloaded at creation time with all the
|
||||
* scripts that don't have default engines-- either a list or a hash
|
||||
* table, so a null return from the table means 'default' and not 'i
|
||||
* don't know yet'.
|
||||
*
|
||||
* On the other hand, in most all cases the number of unique
|
||||
* script/font combinations will be small, so a flat hashtable should
|
||||
* suffice.
|
||||
* */
|
||||
public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory {
|
||||
private static native void initGVIDs();
|
||||
static {
|
||||
FontManagerNativeLibrary.load();
|
||||
initGVIDs();
|
||||
}
|
||||
|
||||
private LayoutEngineKey key;
|
||||
|
||||
private static LayoutEngineFactory instance;
|
||||
|
||||
public static LayoutEngineFactory instance() {
|
||||
if (instance == null) {
|
||||
instance = new SunLayoutEngine();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private SunLayoutEngine() {
|
||||
// actually a factory, key is null so layout cannot be called on it
|
||||
}
|
||||
|
||||
public LayoutEngine getEngine(Font2D font, int script, int lang) {
|
||||
return getEngine(new LayoutEngineKey(font, script, lang));
|
||||
}
|
||||
|
||||
// !!! don't need this unless we have more than one sun layout engine...
|
||||
public LayoutEngine getEngine(LayoutEngineKey key) {
|
||||
ConcurrentHashMap cache = (ConcurrentHashMap)cacheref.get();
|
||||
if (cache == null) {
|
||||
cache = new ConcurrentHashMap();
|
||||
cacheref = new SoftReference(cache);
|
||||
}
|
||||
|
||||
LayoutEngine e = (LayoutEngine)cache.get(key);
|
||||
if (e == null) {
|
||||
LayoutEngineKey copy = key.copy();
|
||||
e = new SunLayoutEngine(copy);
|
||||
cache.put(copy, e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
private SoftReference cacheref = new SoftReference(null);
|
||||
|
||||
private SunLayoutEngine(LayoutEngineKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
static WeakHashMap<Font2D, Boolean> aatInfo = new WeakHashMap<>();
|
||||
|
||||
private boolean isAAT(Font2D font) {
|
||||
Boolean aatObj;
|
||||
synchronized (aatInfo) {
|
||||
aatObj = aatInfo.get(font);
|
||||
}
|
||||
if (aatObj != null) {
|
||||
return aatObj.booleanValue();
|
||||
}
|
||||
boolean aat = false;
|
||||
if (font instanceof TrueTypeFont) {
|
||||
TrueTypeFont ttf = (TrueTypeFont)font;
|
||||
aat = ttf.getDirectoryEntry(TrueTypeFont.morxTag) != null ||
|
||||
ttf.getDirectoryEntry(TrueTypeFont.mortTag) != null;
|
||||
} else if (font instanceof PhysicalFont) {
|
||||
PhysicalFont pf = (PhysicalFont)font;
|
||||
aat = pf.getTableBytes(TrueTypeFont.morxTag) != null ||
|
||||
pf.getTableBytes(TrueTypeFont.mortTag) != null;
|
||||
}
|
||||
synchronized (aatInfo) {
|
||||
aatInfo.put(font, Boolean.valueOf(aat));
|
||||
}
|
||||
return aat;
|
||||
}
|
||||
|
||||
public void layout(FontStrikeDesc desc, float[] mat, int gmask,
|
||||
int baseIndex, TextRecord tr, int typo_flags,
|
||||
Point2D.Float pt, GVData data) {
|
||||
Font2D font = key.font();
|
||||
FontStrike strike = font.getStrike(desc);
|
||||
// Ignore layout tables for RTL AAT fonts due to lack of support in ICU
|
||||
long layoutTables = (((typo_flags & 0x80000000) != 0) && isAAT(font)) ? 0 :
|
||||
font.getLayoutTableCache();
|
||||
nativeLayout(font, strike, mat, gmask, baseIndex,
|
||||
tr.text, tr.start, tr.limit, tr.min, tr.max,
|
||||
key.script(), key.lang(), typo_flags, pt, data,
|
||||
font.getUnitsPerEm(), layoutTables);
|
||||
}
|
||||
|
||||
private static native void
|
||||
nativeLayout(Font2D font, FontStrike strike, float[] mat, int gmask,
|
||||
int baseIndex, char[] chars, int offset, int limit,
|
||||
int min, int max, int script, int lang, int typo_flags,
|
||||
Point2D.Float pt, GVData data, long upem, long layoutTables);
|
||||
}
|
||||
124
jdkSrc/jdk8/sun/font/TextLabel.java
Normal file
124
jdkSrc/jdk8/sun/font/TextLabel.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-2003 All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
* A label.
|
||||
* Visual bounds is a rect that encompasses the entire rendered area.
|
||||
* Logical bounds is a rect that defines how to position this next
|
||||
* to other objects.
|
||||
* Align bounds is a rect that defines how to align this to margins.
|
||||
* it generally allows some overhang that logical bounds would prevent.
|
||||
*/
|
||||
public abstract class TextLabel {
|
||||
|
||||
/**
|
||||
* Return a rectangle that surrounds the text outline when this label is rendered at x, y.
|
||||
*/
|
||||
public abstract Rectangle2D getVisualBounds(float x, float y);
|
||||
|
||||
/**
|
||||
* Return a rectangle that corresponds to the logical bounds of the text
|
||||
* when this label is rendered at x, y.
|
||||
* This rectangle is used when positioning text next to other text.
|
||||
*/
|
||||
public abstract Rectangle2D getLogicalBounds(float x, float y);
|
||||
|
||||
/**
|
||||
* Return a rectangle that corresponds to the alignment bounds of the text
|
||||
* when this label is rendered at x, y. This rectangle is used when positioning text next
|
||||
* to a margin. It differs from the logical bounds in that it does not include leading or
|
||||
* trailing whitespace.
|
||||
*/
|
||||
public abstract Rectangle2D getAlignBounds(float x, float y);
|
||||
|
||||
/**
|
||||
* Return a rectangle that corresponds to the logical bounds of the text, adjusted
|
||||
* to angle the leading and trailing edges by the italic angle.
|
||||
*/
|
||||
public abstract Rectangle2D getItalicBounds(float x, float y);
|
||||
|
||||
/**
|
||||
* Return an outline of the characters in the label when rendered at x, y.
|
||||
*/
|
||||
public abstract Shape getOutline(float x, float y);
|
||||
|
||||
/**
|
||||
* Render the label at x, y in the graphics.
|
||||
*/
|
||||
public abstract void draw(Graphics2D g, float x, float y);
|
||||
|
||||
/**
|
||||
* A convenience method that returns the visual bounds when rendered at 0, 0.
|
||||
*/
|
||||
public Rectangle2D getVisualBounds() {
|
||||
return getVisualBounds(0f, 0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that returns the logical bounds when rendered at 0, 0.
|
||||
*/
|
||||
public Rectangle2D getLogicalBounds() {
|
||||
return getLogicalBounds(0f, 0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that returns the align bounds when rendered at 0, 0.
|
||||
*/
|
||||
public Rectangle2D getAlignBounds() {
|
||||
return getAlignBounds(0f, 0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that returns the italic bounds when rendered at 0, 0.
|
||||
*/
|
||||
public Rectangle2D getItalicBounds() {
|
||||
return getItalicBounds(0f, 0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that returns the outline when rendered at 0, 0.
|
||||
*/
|
||||
public Shape getOutline() {
|
||||
return getOutline(0f, 0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that renders the label at 0, 0.
|
||||
*/
|
||||
public void draw(Graphics2D g) {
|
||||
draw(g, 0f, 0f);
|
||||
}
|
||||
}
|
||||
160
jdkSrc/jdk8/sun/font/TextLabelFactory.java
Normal file
160
jdkSrc/jdk8/sun/font/TextLabelFactory.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-2003 All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.text.Bidi;
|
||||
|
||||
/**
|
||||
* A factory for text labels. Basically this just holds onto the stuff that
|
||||
* doesn't change-- the render context, context, and bidi info for the context-- and gets
|
||||
* called for each subrange you want to create.
|
||||
*
|
||||
* @see Font
|
||||
* @see FontRenderContext
|
||||
* @see GlyphVector
|
||||
* @see TextLabel
|
||||
* @see ExtendedTextLabel
|
||||
* @see Bidi
|
||||
* @see TextLayout
|
||||
*/
|
||||
|
||||
public final class TextLabelFactory {
|
||||
private final FontRenderContext frc;
|
||||
private final char[] text;
|
||||
private final Bidi bidi;
|
||||
private Bidi lineBidi;
|
||||
private final int flags;
|
||||
private int lineStart;
|
||||
private int lineLimit;
|
||||
|
||||
/**
|
||||
* Initialize a factory to produce glyph arrays.
|
||||
* @param frc the FontRenderContext to use for the arrays to be produced.
|
||||
* @param text the text of the paragraph.
|
||||
* @param bidi the bidi information for the paragraph text, or null if the
|
||||
* entire text is left-to-right text.
|
||||
*/
|
||||
public TextLabelFactory(FontRenderContext frc,
|
||||
char[] text,
|
||||
Bidi bidi,
|
||||
int flags) {
|
||||
this.frc = frc;
|
||||
this.text = text.clone();
|
||||
this.bidi = bidi;
|
||||
this.flags = flags;
|
||||
this.lineBidi = bidi;
|
||||
this.lineStart = 0;
|
||||
this.lineLimit = text.length;
|
||||
}
|
||||
|
||||
public FontRenderContext getFontRenderContext() {
|
||||
return frc;
|
||||
}
|
||||
|
||||
public Bidi getLineBidi() {
|
||||
return lineBidi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a line context for the factory. Shaping only occurs on this line.
|
||||
* Characters are ordered as they would appear on this line.
|
||||
* @param lineStart the index within the text of the start of the line.
|
||||
* @param lineLimit the index within the text of the limit of the line.
|
||||
*/
|
||||
public void setLineContext(int lineStart, int lineLimit) {
|
||||
this.lineStart = lineStart;
|
||||
this.lineLimit = lineLimit;
|
||||
if (bidi != null) {
|
||||
lineBidi = bidi.createLineBidi(lineStart, lineLimit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an extended glyph array for the text between start and limit.
|
||||
*
|
||||
* @param font the font to use to generate glyphs and character positions.
|
||||
* @param start the start of the subrange for which to create the glyph array
|
||||
* @param limit the limit of the subrange for which to create glyph array
|
||||
*
|
||||
* Start and limit must be within the bounds of the current line. If no
|
||||
* line context has been set, the entire text is used as the current line.
|
||||
* The text between start and limit will be treated as though it all has
|
||||
* the same bidi level (and thus the same directionality) as the character
|
||||
* at start. Clients should ensure that all text between start and limit
|
||||
* has the same bidi level for the current line.
|
||||
*/
|
||||
public ExtendedTextLabel createExtended(Font font,
|
||||
CoreMetrics lm,
|
||||
Decoration decorator,
|
||||
int start,
|
||||
int limit) {
|
||||
|
||||
if (start >= limit || start < lineStart || limit > lineLimit) {
|
||||
throw new IllegalArgumentException("bad start: " + start + " or limit: " + limit);
|
||||
}
|
||||
|
||||
int level = lineBidi == null ? 0 : lineBidi.getLevelAt(start - lineStart);
|
||||
int linedir = (lineBidi == null || lineBidi.baseIsLeftToRight()) ? 0 : 1;
|
||||
int layoutFlags = flags & ~0x9; // remove bidi, line direction flags
|
||||
if ((level & 0x1) != 0) layoutFlags |= 1; // rtl
|
||||
if ((linedir & 0x1) != 0) layoutFlags |= 8; // line rtl
|
||||
|
||||
TextSource source = new StandardTextSource(text, start, limit - start, lineStart, lineLimit - lineStart, level, layoutFlags, font, frc, lm);
|
||||
return new ExtendedTextSourceLabel(source, decorator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simple glyph array for the text between start and limit.
|
||||
*
|
||||
* @param font the font to use to generate glyphs and character positions.
|
||||
* @param start the start of the subrange for which to create the glyph array
|
||||
* @param limit the limit of the subrange for which to create glyph array
|
||||
*/
|
||||
public TextLabel createSimple(Font font,
|
||||
CoreMetrics lm,
|
||||
int start,
|
||||
int limit) {
|
||||
|
||||
if (start >= limit || start < lineStart || limit > lineLimit) {
|
||||
throw new IllegalArgumentException("bad start: " + start + " or limit: " + limit);
|
||||
}
|
||||
|
||||
int level = lineBidi == null ? 0 : lineBidi.getLevelAt(start - lineStart);
|
||||
int linedir = (lineBidi == null || lineBidi.baseIsLeftToRight()) ? 0 : 1;
|
||||
int layoutFlags = flags & ~0x9; // remove bidi, line direction flags
|
||||
if ((level & 0x1) != 0) layoutFlags |= 1; // rtl
|
||||
if ((linedir & 0x1) != 0) layoutFlags |= 8; // line rtl
|
||||
TextSource source = new StandardTextSource(text, start, limit - start, lineStart, lineLimit - lineStart, level, layoutFlags, font, frc, lm);
|
||||
return new TextSourceLabel(source);
|
||||
}
|
||||
}
|
||||
126
jdkSrc/jdk8/sun/font/TextLineComponent.java
Normal file
126
jdkSrc/jdk8/sun/font/TextLineComponent.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 1998-2003 All Rights Reserved
|
||||
*
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.GlyphJustificationInfo;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.LineMetrics;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public interface TextLineComponent {
|
||||
|
||||
public CoreMetrics getCoreMetrics();
|
||||
public void draw(Graphics2D g2d, float x, float y);
|
||||
public Rectangle2D getCharVisualBounds(int index);
|
||||
public Rectangle2D getVisualBounds();
|
||||
public float getAdvance();
|
||||
public Shape getOutline(float x, float y);
|
||||
|
||||
public int getNumCharacters();
|
||||
|
||||
public float getCharX(int index);
|
||||
public float getCharY(int index);
|
||||
public float getCharAdvance(int index);
|
||||
public boolean caretAtOffsetIsValid(int index);
|
||||
|
||||
// measures characters in context, in logical order
|
||||
public int getLineBreakIndex(int start, float width);
|
||||
|
||||
// measures characters in context, in logical order
|
||||
public float getAdvanceBetween(int start, int limit);
|
||||
|
||||
public Rectangle2D getLogicalBounds();
|
||||
|
||||
public Rectangle2D getItalicBounds();
|
||||
|
||||
public AffineTransform getBaselineTransform();
|
||||
|
||||
// return true if this wraps a glyphvector with no baseline rotation and
|
||||
// has no styles requiring complex pixel bounds calculations.
|
||||
public boolean isSimple();
|
||||
|
||||
// return the pixel bounds if we wrap a glyphvector, else throw an
|
||||
// internal error
|
||||
public Rectangle getPixelBounds(FontRenderContext frc, float x, float y);
|
||||
|
||||
/**
|
||||
* Force subset characters to run left-to-right.
|
||||
*/
|
||||
public static final int LEFT_TO_RIGHT = 0;
|
||||
/**
|
||||
* Force subset characters to run right-to-left.
|
||||
*/
|
||||
public static final int RIGHT_TO_LEFT = 1;
|
||||
|
||||
/**
|
||||
* Leave subset character direction and ordering unchanged.
|
||||
*/
|
||||
public static final int UNCHANGED = 2;
|
||||
|
||||
/**
|
||||
* Return a TextLineComponent for the characters in the range
|
||||
* start, limit. The range is relative to this TextLineComponent
|
||||
* (ie, the first character is at 0).
|
||||
* @param dir one of the constants LEFT_TO_RIGHT, RIGHT_TO_LEFT, or UNCHANGED
|
||||
*/
|
||||
public TextLineComponent getSubset(int start, int limit, int dir);
|
||||
|
||||
/**
|
||||
* Return the number of justification records this uses.
|
||||
*/
|
||||
public int getNumJustificationInfos();
|
||||
|
||||
/**
|
||||
* Return GlyphJustificationInfo objects for the characters between
|
||||
* charStart and charLimit, starting at offset infoStart. Infos
|
||||
* will be in visual order. All positions between infoStart and
|
||||
* getNumJustificationInfos will be set. If a position corresponds
|
||||
* to a character outside the provided range, it is set to null.
|
||||
*/
|
||||
public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit);
|
||||
|
||||
/**
|
||||
* Apply deltas to the data in this component, starting at offset
|
||||
* deltaStart, and return the new component. There are two floats
|
||||
* for each justification info, for a total of 2 * getNumJustificationInfos.
|
||||
* The first delta is the left adjustment, the second is the right
|
||||
* adjustment.
|
||||
* <p>
|
||||
* If flags[0] is true on entry, rejustification is allowed. If
|
||||
* the new component requires rejustification (ligatures were
|
||||
* formed or split), flags[0] will be set on exit.
|
||||
*/
|
||||
public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags);
|
||||
}
|
||||
50
jdkSrc/jdk8/sun/font/TextRecord.java
Normal file
50
jdkSrc/jdk8/sun/font/TextRecord.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2003 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/**
|
||||
* Represents a region of text and context
|
||||
*/
|
||||
public final class TextRecord {
|
||||
public char[] text;
|
||||
public int start;
|
||||
public int limit;
|
||||
public int min;
|
||||
public int max;
|
||||
|
||||
public void init(char[] text, int start, int limit, int min, int max) {
|
||||
this.text = text;
|
||||
this.start = start;
|
||||
this.limit = limit;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
}
|
||||
88
jdkSrc/jdk8/sun/font/TextSource.java
Normal file
88
jdkSrc/jdk8/sun/font/TextSource.java
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-2003 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.LineMetrics;
|
||||
|
||||
/**
|
||||
* A text source represents text for rendering, plus context information.
|
||||
* All text in the source uses the same font, metrics, and render context,
|
||||
* and is at the same bidi level.
|
||||
*/
|
||||
|
||||
public abstract class TextSource {
|
||||
/** Source character data. */
|
||||
public abstract char[] getChars();
|
||||
|
||||
/** Start of source data in char array returned from getChars. */
|
||||
public abstract int getStart();
|
||||
|
||||
/** Length of source data. */
|
||||
public abstract int getLength();
|
||||
|
||||
/** Start of context data in char array returned from getChars. */
|
||||
public abstract int getContextStart();
|
||||
|
||||
/** Length of context data. */
|
||||
public abstract int getContextLength();
|
||||
|
||||
/** Return the layout flags */
|
||||
public abstract int getLayoutFlags();
|
||||
|
||||
/** Bidi level of all the characters in context. */
|
||||
public abstract int getBidiLevel();
|
||||
|
||||
/** Font for source data. */
|
||||
public abstract Font getFont();
|
||||
|
||||
/** Font render context to use when measuring or rendering source data. */
|
||||
public abstract FontRenderContext getFRC();
|
||||
|
||||
/** Line metrics for source data. */
|
||||
public abstract CoreMetrics getCoreMetrics();
|
||||
|
||||
/** Get subrange of this TextSource. dir is one of the TextLineComponent constants */
|
||||
public abstract TextSource getSubSource(int start, int length, int dir);
|
||||
|
||||
/** Constant for toString(boolean). Indicates that toString should not return info
|
||||
outside of the context of this instance. */
|
||||
public static final boolean WITHOUT_CONTEXT = false;
|
||||
|
||||
/** Constant for toString(boolean). Indicates that toString should return info
|
||||
outside of the context of this instance. */
|
||||
public static final boolean WITH_CONTEXT = true;
|
||||
|
||||
/** Get debugging info about this TextSource instance. Default implementation just
|
||||
returns toString. Subclasses should implement this to match the semantics of
|
||||
the toString constants. */
|
||||
public abstract String toString(boolean withContext);
|
||||
}
|
||||
173
jdkSrc/jdk8/sun/font/TextSourceLabel.java
Normal file
173
jdkSrc/jdk8/sun/font/TextSourceLabel.java
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998, 1999 - All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
* Implementation of TextLabel based on String.
|
||||
*/
|
||||
|
||||
public class TextSourceLabel extends TextLabel {
|
||||
TextSource source;
|
||||
|
||||
// caches
|
||||
Rectangle2D lb;
|
||||
Rectangle2D ab;
|
||||
Rectangle2D vb;
|
||||
Rectangle2D ib;
|
||||
GlyphVector gv;
|
||||
|
||||
public TextSourceLabel(TextSource source) {
|
||||
this(source, null, null, null);
|
||||
}
|
||||
|
||||
public TextSourceLabel(TextSource source, Rectangle2D lb, Rectangle2D ab, GlyphVector gv) {
|
||||
this.source = source;
|
||||
|
||||
this.lb = lb;
|
||||
this.ab = ab;
|
||||
this.gv = gv;
|
||||
}
|
||||
|
||||
public TextSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public final Rectangle2D getLogicalBounds(float x, float y) {
|
||||
if (lb == null) {
|
||||
lb = createLogicalBounds();
|
||||
}
|
||||
return new Rectangle2D.Float((float)(lb.getX() + x),
|
||||
(float)(lb.getY() + y),
|
||||
(float)lb.getWidth(),
|
||||
(float)lb.getHeight());
|
||||
}
|
||||
|
||||
public final Rectangle2D getVisualBounds(float x, float y) {
|
||||
if (vb == null) {
|
||||
vb = createVisualBounds();
|
||||
|
||||
}
|
||||
return new Rectangle2D.Float((float)(vb.getX() + x),
|
||||
(float)(vb.getY() + y),
|
||||
(float)vb.getWidth(),
|
||||
(float)vb.getHeight());
|
||||
}
|
||||
|
||||
public final Rectangle2D getAlignBounds(float x, float y) {
|
||||
if (ab == null) {
|
||||
ab = createAlignBounds();
|
||||
}
|
||||
return new Rectangle2D.Float((float)(ab.getX() + x),
|
||||
(float)(ab.getY() + y),
|
||||
(float)ab.getWidth(),
|
||||
(float)ab.getHeight());
|
||||
}
|
||||
|
||||
public Rectangle2D getItalicBounds(float x, float y) {
|
||||
if (ib == null) {
|
||||
ib = createItalicBounds();
|
||||
}
|
||||
return new Rectangle2D.Float((float)(ib.getX() + x),
|
||||
(float)(ib.getY() + y),
|
||||
(float)ib.getWidth(),
|
||||
(float)ib.getHeight());
|
||||
|
||||
}
|
||||
|
||||
public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
|
||||
return getGV().getPixelBounds(frc, x, y); // no cache
|
||||
}
|
||||
|
||||
public AffineTransform getBaselineTransform() {
|
||||
Font font = source.getFont();
|
||||
if (font.hasLayoutAttributes()) {
|
||||
return AttributeValues.getBaselineTransform(font.getAttributes());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Shape getOutline(float x, float y) {
|
||||
return getGV().getOutline(x, y);
|
||||
}
|
||||
|
||||
public void draw(Graphics2D g, float x, float y) {
|
||||
g.drawGlyphVector(getGV(), x, y);
|
||||
}
|
||||
|
||||
protected Rectangle2D createLogicalBounds() {
|
||||
return getGV().getLogicalBounds();
|
||||
}
|
||||
|
||||
protected Rectangle2D createVisualBounds() {
|
||||
return getGV().getVisualBounds();
|
||||
}
|
||||
|
||||
protected Rectangle2D createItalicBounds() {
|
||||
// !!! fix
|
||||
return getGV().getLogicalBounds();
|
||||
}
|
||||
|
||||
protected Rectangle2D createAlignBounds() {
|
||||
return createLogicalBounds();
|
||||
}
|
||||
|
||||
private final GlyphVector getGV() {
|
||||
if (gv == null) {
|
||||
gv = createGV();
|
||||
}
|
||||
|
||||
return gv;
|
||||
}
|
||||
|
||||
protected GlyphVector createGV() {
|
||||
Font font = source.getFont();
|
||||
FontRenderContext frc = source.getFRC();
|
||||
int flags = source.getLayoutFlags();
|
||||
char[] context = source.getChars();
|
||||
int start = source.getStart();
|
||||
int length = source.getLength();
|
||||
|
||||
GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines
|
||||
StandardGlyphVector gv = gl.layout(font, frc, context, start, length,
|
||||
flags, null); // ??? use textsource
|
||||
GlyphLayout.done(gl);
|
||||
|
||||
return gv;
|
||||
}
|
||||
}
|
||||
1772
jdkSrc/jdk8/sun/font/TrueTypeFont.java
Normal file
1772
jdkSrc/jdk8/sun/font/TrueTypeFont.java
Normal file
File diff suppressed because it is too large
Load Diff
273
jdkSrc/jdk8/sun/font/TrueTypeGlyphMapper.java
Normal file
273
jdkSrc/jdk8/sun/font/TrueTypeGlyphMapper.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Locale;
|
||||
|
||||
public class TrueTypeGlyphMapper extends CharToGlyphMapper {
|
||||
|
||||
static final char REVERSE_SOLIDUS = 0x005c; // the backslash char.
|
||||
static final char JA_YEN = 0x00a5;
|
||||
static final char JA_FULLWIDTH_TILDE_CHAR = 0xff5e;
|
||||
static final char JA_WAVE_DASH_CHAR = 0x301c;
|
||||
|
||||
/* if running on Solaris and default Locale is ja_JP then
|
||||
* we map need to remap reverse solidus (backslash) to Yen as
|
||||
* apparently expected there.
|
||||
*/
|
||||
static final boolean isJAlocale = Locale.JAPAN.equals(Locale.getDefault());
|
||||
private final boolean needsJAremapping;
|
||||
private boolean remapJAWaveDash;
|
||||
|
||||
TrueTypeFont font;
|
||||
CMap cmap;
|
||||
int numGlyphs;
|
||||
|
||||
public TrueTypeGlyphMapper(TrueTypeFont font) {
|
||||
this.font = font;
|
||||
try {
|
||||
cmap = CMap.initialize(font);
|
||||
} catch (Exception e) {
|
||||
cmap = null;
|
||||
}
|
||||
if (cmap == null) {
|
||||
handleBadCMAP();
|
||||
}
|
||||
missingGlyph = 0; /* standard for TrueType fonts */
|
||||
ByteBuffer buffer = font.getTableBuffer(TrueTypeFont.maxpTag);
|
||||
if (buffer != null && buffer.capacity() >= 6) {
|
||||
numGlyphs = buffer.getChar(4); // offset 4 bytes in MAXP table.
|
||||
} else {
|
||||
handleBadCMAP();
|
||||
}
|
||||
if (FontUtilities.isSolaris && isJAlocale && font.supportsJA()) {
|
||||
needsJAremapping = true;
|
||||
if (FontUtilities.isSolaris8 &&
|
||||
getGlyphFromCMAP(JA_WAVE_DASH_CHAR) == missingGlyph) {
|
||||
remapJAWaveDash = true;
|
||||
}
|
||||
} else {
|
||||
needsJAremapping = false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
return numGlyphs;
|
||||
}
|
||||
|
||||
private char getGlyphFromCMAP(int charCode) {
|
||||
try {
|
||||
char glyphCode = cmap.getGlyph(charCode);
|
||||
if (glyphCode < numGlyphs ||
|
||||
glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) {
|
||||
return glyphCode;
|
||||
} else {
|
||||
if (FontUtilities.isLogging()) {
|
||||
FontUtilities.getLogger().warning
|
||||
(font + " out of range glyph id=" +
|
||||
Integer.toHexString((int)glyphCode) +
|
||||
" for char " + Integer.toHexString(charCode));
|
||||
}
|
||||
return (char)missingGlyph;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
handleBadCMAP();
|
||||
return (char) missingGlyph;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBadCMAP() {
|
||||
if (FontUtilities.isLogging()) {
|
||||
FontUtilities.getLogger().severe("Null Cmap for " + font +
|
||||
"substituting for this font");
|
||||
}
|
||||
SunFontManager.getInstance().deRegisterBadFont(font);
|
||||
/* The next line is not really a solution, but might
|
||||
* reduce the exceptions until references to this font2D
|
||||
* are gone.
|
||||
*/
|
||||
cmap = CMap.theNullCmap;
|
||||
}
|
||||
|
||||
private final char remapJAChar(char unicode) {
|
||||
switch (unicode) {
|
||||
case REVERSE_SOLIDUS:
|
||||
return JA_YEN;
|
||||
/* This is a workaround for bug 4533422.
|
||||
* Japanese wave dash missing from Solaris JA TrueType fonts.
|
||||
*/
|
||||
case JA_WAVE_DASH_CHAR:
|
||||
if (remapJAWaveDash) {
|
||||
return JA_FULLWIDTH_TILDE_CHAR;
|
||||
}
|
||||
default: return unicode;
|
||||
}
|
||||
}
|
||||
private final int remapJAIntChar(int unicode) {
|
||||
switch (unicode) {
|
||||
case REVERSE_SOLIDUS:
|
||||
return JA_YEN;
|
||||
/* This is a workaround for bug 4533422.
|
||||
* Japanese wave dash missing from Solaris JA TrueType fonts.
|
||||
*/
|
||||
case JA_WAVE_DASH_CHAR:
|
||||
if (remapJAWaveDash) {
|
||||
return JA_FULLWIDTH_TILDE_CHAR;
|
||||
}
|
||||
default: return unicode;
|
||||
}
|
||||
}
|
||||
|
||||
public int charToGlyph(char unicode) {
|
||||
if (needsJAremapping) {
|
||||
unicode = remapJAChar(unicode);
|
||||
}
|
||||
int glyph = getGlyphFromCMAP(unicode);
|
||||
if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) {
|
||||
font.glyphToCharMap[glyph] = unicode;
|
||||
}
|
||||
return glyph;
|
||||
}
|
||||
|
||||
public int charToGlyph(int unicode) {
|
||||
if (needsJAremapping) {
|
||||
unicode = remapJAIntChar(unicode);
|
||||
}
|
||||
int glyph = getGlyphFromCMAP(unicode);
|
||||
if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) {
|
||||
font.glyphToCharMap[glyph] = (char)unicode;
|
||||
}
|
||||
return glyph;
|
||||
}
|
||||
|
||||
public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
|
||||
for (int i=0;i<count;i++) {
|
||||
if (needsJAremapping) {
|
||||
glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i]));
|
||||
} else {
|
||||
glyphs[i] = getGlyphFromCMAP(unicodes[i]);
|
||||
}
|
||||
if (font.checkUseNatives() &&
|
||||
glyphs[i] < font.glyphToCharMap.length) {
|
||||
font.glyphToCharMap[glyphs[i]] = (char)unicodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
int code;
|
||||
if (needsJAremapping) {
|
||||
code = remapJAChar(unicodes[i]);
|
||||
} else {
|
||||
code = unicodes[i]; // char is unsigned.
|
||||
}
|
||||
|
||||
if (code >= HI_SURROGATE_START &&
|
||||
code <= HI_SURROGATE_END && i < count - 1) {
|
||||
char low = unicodes[i + 1];
|
||||
|
||||
if (low >= LO_SURROGATE_START &&
|
||||
low <= LO_SURROGATE_END) {
|
||||
code = (code - HI_SURROGATE_START) *
|
||||
0x400 + low - LO_SURROGATE_START + 0x10000;
|
||||
|
||||
glyphs[i] = getGlyphFromCMAP(code);
|
||||
i += 1; // Empty glyph slot after surrogate
|
||||
glyphs[i] = INVISIBLE_GLYPH_ID;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
glyphs[i] = getGlyphFromCMAP(code);
|
||||
|
||||
if (font.checkUseNatives() &&
|
||||
glyphs[i] < font.glyphToCharMap.length) {
|
||||
font.glyphToCharMap[glyphs[i]] = (char)code;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* This variant checks if shaping is needed and immediately
|
||||
* returns true if it does. A caller of this method should be expecting
|
||||
* to check the return type because it needs to know how to handle
|
||||
* the character data for display.
|
||||
*/
|
||||
public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
int code;
|
||||
if (needsJAremapping) {
|
||||
code = remapJAChar(unicodes[i]);
|
||||
} else {
|
||||
code = unicodes[i]; // char is unsigned.
|
||||
}
|
||||
|
||||
if (code >= HI_SURROGATE_START &&
|
||||
code <= HI_SURROGATE_END && i < count - 1) {
|
||||
char low = unicodes[i + 1];
|
||||
|
||||
if (low >= LO_SURROGATE_START &&
|
||||
low <= LO_SURROGATE_END) {
|
||||
code = (code - HI_SURROGATE_START) *
|
||||
0x400 + low - LO_SURROGATE_START + 0x10000;
|
||||
glyphs[i + 1] = INVISIBLE_GLYPH_ID;
|
||||
}
|
||||
}
|
||||
|
||||
glyphs[i] = getGlyphFromCMAP(code);
|
||||
if (font.checkUseNatives() &&
|
||||
glyphs[i] < font.glyphToCharMap.length) {
|
||||
font.glyphToCharMap[glyphs[i]] = (char)code;
|
||||
}
|
||||
|
||||
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
|
||||
continue;
|
||||
}
|
||||
else if (FontUtilities.isComplexCharCode(code)) {
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x10000) {
|
||||
i += 1; // Empty glyph slot after surrogate
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A pretty good heuristic is that the cmap we are using
|
||||
* supports 32 bit character codes.
|
||||
*/
|
||||
boolean hasSupplementaryChars() {
|
||||
return
|
||||
cmap instanceof CMap.CMapFormat8 ||
|
||||
cmap instanceof CMap.CMapFormat10 ||
|
||||
cmap instanceof CMap.CMapFormat12;
|
||||
}
|
||||
}
|
||||
682
jdkSrc/jdk8/sun/font/Type1Font.java
Normal file
682
jdkSrc/jdk8/sun/font/Type1Font.java
Normal file
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.awt.FontFormatException;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import sun.java2d.Disposer;
|
||||
import sun.java2d.DisposerRecord;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.awt.Font;
|
||||
|
||||
/*
|
||||
* Adobe Technical Note 5040 details the format of PFB files.
|
||||
* the file is divided into ascii and binary sections. Each section
|
||||
* starts with a header
|
||||
* 0x8001 - start of binary data, is followed by 4 bytes length, then data
|
||||
* 0x8002 - start of ascii data, is followed by 4 bytes length, then data
|
||||
* 0x8003 - end of data segment
|
||||
* The length is organised as LSB->MSB.
|
||||
*
|
||||
* Note: I experimented with using a MappedByteBuffer and
|
||||
* there were two problems/questions.
|
||||
* 1. If a global buffer is used rather than one allocated in the calling
|
||||
* context, then we need to synchronize on all uses of that data, which
|
||||
* means more code would beed to be synchronized with probable repercussions
|
||||
* elsewhere.
|
||||
* 2. It is not clear whether to free the buffer when the file is closed.
|
||||
* If we have the contents in memory then why keep open files around?
|
||||
* The mmapped buffer doesn't need it.
|
||||
* Also regular GC is what frees the buffer. So closing the file and nulling
|
||||
* out the reference still needs to wait for the buffer to be GC'd to
|
||||
* reclaim the storage.
|
||||
* If the contents of the buffer are persistent there's no need
|
||||
* to worry about synchronization.
|
||||
* Perhaps could use a WeakReference, and when its referent is gone, and
|
||||
* need it can just reopen the file.
|
||||
* Type1 fonts thus don't use up file descriptor references, but can
|
||||
* use memory footprint in a way that's managed by the host O/S.
|
||||
* The main "pain" may be the different model means code needs to be written
|
||||
* without assumptions as to how this is handled by the different subclasses
|
||||
* of FileFont.
|
||||
*/
|
||||
public class Type1Font extends FileFont {
|
||||
|
||||
private static class T1DisposerRecord implements DisposerRecord {
|
||||
String fileName = null;
|
||||
|
||||
T1DisposerRecord(String name) {
|
||||
fileName = name;
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction() {
|
||||
public Object run() {
|
||||
|
||||
if (fileName != null) {
|
||||
(new java.io.File(fileName)).delete();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
WeakReference bufferRef = new WeakReference(null);
|
||||
|
||||
private String psName = null;
|
||||
|
||||
static private HashMap styleAbbreviationsMapping;
|
||||
static private HashSet styleNameTokes;
|
||||
|
||||
static {
|
||||
styleAbbreviationsMapping = new HashMap();
|
||||
styleNameTokes = new HashSet();
|
||||
|
||||
/* These abbreviation rules are taken from Appendix 1 of Adobe Technical Note #5088 */
|
||||
/* NB: this list is not complete - we did not include abbreviations which contain
|
||||
several capital letters because current expansion algorithm do not support this.
|
||||
(namely we have omited MM aka "Multiple Master", OsF aka "Oldstyle figures",
|
||||
OS aka "Oldstyle", SC aka "Small caps" and DS aka "Display" */
|
||||
String nm[] = {"Black", "Bold", "Book", "Demi", "Heavy", "Light",
|
||||
"Meduium", "Nord", "Poster", "Regular", "Super", "Thin",
|
||||
"Compressed", "Condensed", "Compact", "Extended", "Narrow",
|
||||
"Inclined", "Italic", "Kursiv", "Oblique", "Upright", "Sloped",
|
||||
"Semi", "Ultra", "Extra",
|
||||
"Alternate", "Alternate", "Deutsche Fraktur", "Expert", "Inline", "Ornaments",
|
||||
"Outline", "Roman", "Rounded", "Script", "Shaded", "Swash", "Titling", "Typewriter"};
|
||||
String abbrv[] = {"Blk", "Bd", "Bk", "Dm", "Hv", "Lt",
|
||||
"Md", "Nd", "Po", "Rg", "Su", "Th",
|
||||
"Cm", "Cn", "Ct", "Ex", "Nr",
|
||||
"Ic", "It", "Ks", "Obl", "Up", "Sl",
|
||||
"Sm", "Ult", "X",
|
||||
"A", "Alt", "Dfr", "Exp", "In", "Or",
|
||||
"Ou", "Rm", "Rd", "Scr", "Sh", "Sw", "Ti", "Typ"};
|
||||
/* This is only subset of names from nm[] because we want to distinguish things
|
||||
like "Lucida Sans TypeWriter Bold" and "Lucida Sans Bold".
|
||||
Names from "Design and/or special purpose" group are omitted. */
|
||||
String styleTokens[] = {"Black", "Bold", "Book", "Demi", "Heavy", "Light",
|
||||
"Medium", "Nord", "Poster", "Regular", "Super", "Thin",
|
||||
"Compressed", "Condensed", "Compact", "Extended", "Narrow",
|
||||
"Inclined", "Italic", "Kursiv", "Oblique", "Upright", "Sloped", "Slanted",
|
||||
"Semi", "Ultra", "Extra"};
|
||||
|
||||
for(int i=0; i<nm.length; i++) {
|
||||
styleAbbreviationsMapping.put(abbrv[i], nm[i]);
|
||||
}
|
||||
for(int i=0; i<styleTokens.length; i++) {
|
||||
styleNameTokes.add(styleTokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a Type1 Font.
|
||||
* @param platname - Platform identifier of the font. Typically file name.
|
||||
* @param nativeNames - Native names - typically XLFDs on Unix.
|
||||
*/
|
||||
public Type1Font(String platname, Object nativeNames)
|
||||
throws FontFormatException {
|
||||
|
||||
this(platname, nativeNames, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* - does basic verification of the file
|
||||
* - reads the names (full, family).
|
||||
* - determines the style of the font.
|
||||
* @throws FontFormatException - if the font can't be opened
|
||||
* or fails verification, or there's no usable cmap
|
||||
*/
|
||||
public Type1Font(String platname, Object nativeNames, boolean createdCopy)
|
||||
throws FontFormatException {
|
||||
super(platname, nativeNames);
|
||||
fontRank = Font2D.TYPE1_RANK;
|
||||
checkedNatives = true;
|
||||
try {
|
||||
verify();
|
||||
} catch (Throwable t) {
|
||||
if (createdCopy) {
|
||||
T1DisposerRecord ref = new T1DisposerRecord(platname);
|
||||
Disposer.addObjectRecord(bufferRef, ref);
|
||||
bufferRef = null;
|
||||
}
|
||||
if (t instanceof FontFormatException) {
|
||||
throw (FontFormatException)t;
|
||||
} else {
|
||||
throw new FontFormatException("Unexpected runtime exception.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized ByteBuffer getBuffer() throws FontFormatException {
|
||||
MappedByteBuffer mapBuf = (MappedByteBuffer)bufferRef.get();
|
||||
if (mapBuf == null) {
|
||||
//System.out.println("open T1 " + platName);
|
||||
try {
|
||||
RandomAccessFile raf = (RandomAccessFile)
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction() {
|
||||
public Object run() {
|
||||
try {
|
||||
return new RandomAccessFile(platName, "r");
|
||||
} catch (FileNotFoundException ffne) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
FileChannel fc = raf.getChannel();
|
||||
fileSize = (int)fc.size();
|
||||
mapBuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
|
||||
mapBuf.position(0);
|
||||
bufferRef = new WeakReference(mapBuf);
|
||||
fc.close();
|
||||
} catch (NullPointerException e) {
|
||||
throw new FontFormatException(e.toString());
|
||||
} catch (ClosedChannelException e) {
|
||||
/* NIO I/O is interruptible, recurse to retry operation.
|
||||
* Clear interrupts before recursing in case NIO didn't.
|
||||
*/
|
||||
Thread.interrupted();
|
||||
return getBuffer();
|
||||
} catch (IOException e) {
|
||||
throw new FontFormatException(e.toString());
|
||||
}
|
||||
}
|
||||
return mapBuf;
|
||||
}
|
||||
|
||||
protected void close() {
|
||||
}
|
||||
|
||||
/* called from native code to read file into a direct byte buffer */
|
||||
void readFile(ByteBuffer buffer) {
|
||||
RandomAccessFile raf = null;
|
||||
FileChannel fc;
|
||||
try {
|
||||
raf = (RandomAccessFile)
|
||||
java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction() {
|
||||
public Object run() {
|
||||
try {
|
||||
return new RandomAccessFile(platName, "r");
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
fc = raf.getChannel();
|
||||
while (buffer.remaining() > 0 && fc.read(buffer) != -1) {}
|
||||
} catch (NullPointerException npe) {
|
||||
} catch (ClosedChannelException e) {
|
||||
try {
|
||||
if (raf != null) {
|
||||
raf.close();
|
||||
raf = null;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
/* NIO I/O is interruptible, recurse to retry operation.
|
||||
* Clear interrupts before recursing in case NIO didn't.
|
||||
*/
|
||||
Thread.interrupted();
|
||||
readFile(buffer);
|
||||
} catch (IOException e) {
|
||||
} finally {
|
||||
if (raf != null) {
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized ByteBuffer readBlock(int offset, int length) {
|
||||
ByteBuffer mappedBuf = null;
|
||||
try {
|
||||
mappedBuf = getBuffer();
|
||||
if (offset > fileSize) {
|
||||
offset = fileSize;
|
||||
}
|
||||
mappedBuf.position(offset);
|
||||
return mappedBuf.slice();
|
||||
} catch (FontFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void verify() throws FontFormatException {
|
||||
/* Normal usage should not call getBuffer(), as its state
|
||||
* ie endianness, position etc, are shared. verify() can do
|
||||
* this as its called only from within the constructor before
|
||||
* there are other users of this object.
|
||||
*/
|
||||
ByteBuffer bb = getBuffer();
|
||||
if (bb.capacity() < 6) {
|
||||
throw new FontFormatException("short file");
|
||||
}
|
||||
int val = bb.get(0) & 0xff;
|
||||
if ((bb.get(0) & 0xff) == 0x80) {
|
||||
verifyPFB(bb);
|
||||
bb.position(6);
|
||||
} else {
|
||||
verifyPFA(bb);
|
||||
bb.position(0);
|
||||
}
|
||||
initNames(bb);
|
||||
if (familyName == null || fullName == null) {
|
||||
throw new FontFormatException("Font name not found");
|
||||
}
|
||||
setStyle();
|
||||
}
|
||||
|
||||
public int getFileSize() {
|
||||
if (fileSize == 0) {
|
||||
try {
|
||||
getBuffer();
|
||||
} catch (FontFormatException e) {
|
||||
}
|
||||
}
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
private void verifyPFA(ByteBuffer bb) throws FontFormatException {
|
||||
if (bb.getShort() != 0x2521) { // 0x2521 is %!
|
||||
throw new FontFormatException("bad pfa font");
|
||||
}
|
||||
// remind - additional verification needed?
|
||||
}
|
||||
|
||||
private void verifyPFB(ByteBuffer bb) throws FontFormatException {
|
||||
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
try {
|
||||
int segType = bb.getShort(pos) & 0xffff;
|
||||
if (segType == 0x8001 || segType == 0x8002) {
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
int segLen = bb.getInt(pos+2);
|
||||
bb.order(ByteOrder.BIG_ENDIAN);
|
||||
if (segLen <= 0) {
|
||||
throw new FontFormatException("bad segment length");
|
||||
}
|
||||
pos += segLen+6;
|
||||
} else if (segType == 0x8003) {
|
||||
return;
|
||||
} else {
|
||||
throw new FontFormatException("bad pfb file");
|
||||
}
|
||||
} catch (BufferUnderflowException bue) {
|
||||
throw new FontFormatException(bue.toString());
|
||||
} catch (Exception e) {
|
||||
throw new FontFormatException(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int PSEOFTOKEN = 0;
|
||||
private static final int PSNAMETOKEN = 1;
|
||||
private static final int PSSTRINGTOKEN = 2;
|
||||
|
||||
/* Need to parse the ascii contents of the Type1 font file,
|
||||
* looking for FullName, FamilyName and FontName.
|
||||
* If explicit names are not found then extract them from first text line.
|
||||
* Operating on bytes so can't use Java String utilities, which
|
||||
* is a large part of why this is a hack.
|
||||
*
|
||||
* Also check for mandatory FontType and verify if it is supported.
|
||||
*/
|
||||
private void initNames(ByteBuffer bb) throws FontFormatException {
|
||||
boolean eof = false;
|
||||
String fontType = null;
|
||||
try {
|
||||
//Parse font looking for explicit FullName, FamilyName and FontName
|
||||
// (according to Type1 spec they are optional)
|
||||
while ((fullName == null || familyName == null || psName == null || fontType == null) && !eof) {
|
||||
int tokenType = nextTokenType(bb);
|
||||
if (tokenType == PSNAMETOKEN) {
|
||||
int pos = bb.position();
|
||||
if (bb.get(pos) == 'F') {
|
||||
String s = getSimpleToken(bb);
|
||||
if ("FullName".equals(s)) {
|
||||
if (nextTokenType(bb)==PSSTRINGTOKEN) {
|
||||
fullName = getString(bb);
|
||||
}
|
||||
} else if ("FamilyName".equals(s)) {
|
||||
if (nextTokenType(bb)==PSSTRINGTOKEN) {
|
||||
familyName = getString(bb);
|
||||
}
|
||||
} else if ("FontName".equals(s)) {
|
||||
if (nextTokenType(bb)==PSNAMETOKEN) {
|
||||
psName = getSimpleToken(bb);
|
||||
}
|
||||
} else if ("FontType".equals(s)) {
|
||||
/* look for
|
||||
/FontType id def
|
||||
*/
|
||||
String token = getSimpleToken(bb);
|
||||
if ("def".equals(getSimpleToken(bb))) {
|
||||
fontType = token;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (bb.get() > ' '); // skip token
|
||||
}
|
||||
} else if (tokenType == PSEOFTOKEN) {
|
||||
eof = true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new FontFormatException(e.toString());
|
||||
}
|
||||
|
||||
/* Ignore all fonts besides Type1 (e.g. Type3 fonts) */
|
||||
if (!"1".equals(fontType)) {
|
||||
throw new FontFormatException("Unsupported font type");
|
||||
}
|
||||
|
||||
if (psName == null) { //no explicit FontName
|
||||
// Try to extract font name from the first text line.
|
||||
// According to Type1 spec first line consist of
|
||||
// "%!FontType1-SpecVersion: FontName FontVersion"
|
||||
// or
|
||||
// "%!PS-AdobeFont-1.0: FontName version"
|
||||
bb.position(0);
|
||||
if (bb.getShort() != 0x2521) { //if pfb (do not start with "%!")
|
||||
//skip segment header and "%!"
|
||||
bb.position(8);
|
||||
//NB: assume that first segment is ASCII one
|
||||
// (is it possible to have valid Type1 font with first binary segment?)
|
||||
}
|
||||
String formatType = getSimpleToken(bb);
|
||||
if (!formatType.startsWith("FontType1-") && !formatType.startsWith("PS-AdobeFont-")) {
|
||||
throw new FontFormatException("Unsupported font format [" + formatType + "]");
|
||||
}
|
||||
psName = getSimpleToken(bb);
|
||||
}
|
||||
|
||||
//if we got to the end of file then we did not find at least one of FullName or FamilyName
|
||||
//Try to deduce missing names from present ones
|
||||
//NB: At least psName must be already initialized by this moment
|
||||
if (eof) {
|
||||
//if we find fullName or familyName then use it as another name too
|
||||
if (fullName != null) {
|
||||
familyName = fullName2FamilyName(fullName);
|
||||
} else if (familyName != null) {
|
||||
fullName = familyName;
|
||||
} else { //fallback - use postscript font name to deduce full and family names
|
||||
fullName = psName2FullName(psName);
|
||||
familyName = psName2FamilyName(psName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String fullName2FamilyName(String name) {
|
||||
String res, token;
|
||||
int len, start, end; //length of family name part
|
||||
|
||||
//FamilyName is truncated version of FullName
|
||||
//Truncated tail must contain only style modifiers
|
||||
|
||||
end = name.length();
|
||||
|
||||
while (end > 0) {
|
||||
start = end - 1;
|
||||
while (start > 0 && name.charAt(start) != ' ')
|
||||
start--;
|
||||
//as soon as we meet first non style token truncate
|
||||
// current tail and return
|
||||
if (!isStyleToken(name.substring(start+1, end))) {
|
||||
return name.substring(0, end);
|
||||
}
|
||||
end = start;
|
||||
}
|
||||
|
||||
return name; //should not happen
|
||||
}
|
||||
|
||||
private String expandAbbreviation(String abbr) {
|
||||
if (styleAbbreviationsMapping.containsKey(abbr))
|
||||
return (String) styleAbbreviationsMapping.get(abbr);
|
||||
return abbr;
|
||||
}
|
||||
|
||||
private boolean isStyleToken(String token) {
|
||||
return styleNameTokes.contains(token);
|
||||
}
|
||||
|
||||
private String psName2FullName(String name) {
|
||||
String res;
|
||||
int pos;
|
||||
|
||||
//According to Adobe technical note #5088 psName (aka FontName) has form
|
||||
// <Family Name><VendorID>-<Weight><Width><Slant><Character Set>
|
||||
//where spaces are not allowed.
|
||||
|
||||
//Conversion: Expand abbreviations in style portion (everything after '-'),
|
||||
// replace '-' with space and insert missing spaces
|
||||
pos = name.indexOf("-");
|
||||
if (pos >= 0) {
|
||||
res = expandName(name.substring(0, pos), false);
|
||||
res += " " + expandName(name.substring(pos+1), true);
|
||||
} else {
|
||||
res = expandName(name, false);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private String psName2FamilyName(String name) {
|
||||
String tmp = name;
|
||||
|
||||
//According to Adobe technical note #5088 psName (aka FontName) has form
|
||||
// <Family Name><VendorID>-<Weight><Width><Slant><Character Set>
|
||||
//where spaces are not allowed.
|
||||
|
||||
//Conversion: Truncate style portion (everything after '-')
|
||||
// and insert missing spaces
|
||||
|
||||
if (tmp.indexOf("-") > 0) {
|
||||
tmp = tmp.substring(0, tmp.indexOf("-"));
|
||||
}
|
||||
|
||||
return expandName(tmp, false);
|
||||
}
|
||||
|
||||
private int nextCapitalLetter(String s, int off) {
|
||||
for (; (off >=0) && off < s.length(); off++) {
|
||||
if (s.charAt(off) >= 'A' && s.charAt(off) <= 'Z')
|
||||
return off;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String expandName(String s, boolean tryExpandAbbreviations) {
|
||||
StringBuffer res = new StringBuffer(s.length() + 10);
|
||||
int start=0, end;
|
||||
|
||||
while(start < s.length()) {
|
||||
end = nextCapitalLetter(s, start + 1);
|
||||
if (end < 0) {
|
||||
end = s.length();
|
||||
}
|
||||
|
||||
if (start != 0) {
|
||||
res.append(" ");
|
||||
}
|
||||
|
||||
if (tryExpandAbbreviations) {
|
||||
res.append(expandAbbreviation(s.substring(start, end)));
|
||||
} else {
|
||||
res.append(s.substring(start, end));
|
||||
}
|
||||
start = end;
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
/* skip lines beginning with "%" and leading white space on a line */
|
||||
private byte skip(ByteBuffer bb) {
|
||||
byte b = bb.get();
|
||||
while (b == '%') {
|
||||
while (true) {
|
||||
b = bb.get();
|
||||
if (b == '\r' || b == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (b <= ' ') {
|
||||
b = bb.get();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Token types:
|
||||
* PSNAMETOKEN - /
|
||||
* PSSTRINGTOKEN - literal text string
|
||||
*/
|
||||
private int nextTokenType(ByteBuffer bb) {
|
||||
|
||||
try {
|
||||
byte b = skip(bb);
|
||||
|
||||
while (true) {
|
||||
if (b == (byte)'/') { // PS defined name follows.
|
||||
return PSNAMETOKEN;
|
||||
} else if (b == (byte)'(') { // PS string follows
|
||||
return PSSTRINGTOKEN;
|
||||
} else if ((b == (byte)'\r') || (b == (byte)'\n')) {
|
||||
b = skip(bb);
|
||||
} else {
|
||||
b = bb.get();
|
||||
}
|
||||
}
|
||||
} catch (BufferUnderflowException e) {
|
||||
return PSEOFTOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read simple token (sequence of non-whitespace characters)
|
||||
starting from the current position.
|
||||
Skip leading whitespaces (if any). */
|
||||
private String getSimpleToken(ByteBuffer bb) {
|
||||
while (bb.get() <= ' ');
|
||||
int pos1 = bb.position()-1;
|
||||
while (bb.get() > ' ');
|
||||
int pos2 = bb.position();
|
||||
byte[] nameBytes = new byte[pos2-pos1-1];
|
||||
bb.position(pos1);
|
||||
bb.get(nameBytes);
|
||||
try {
|
||||
return new String(nameBytes, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return new String(nameBytes);
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(ByteBuffer bb) {
|
||||
int pos1 = bb.position();
|
||||
while (bb.get() != ')');
|
||||
int pos2 = bb.position();
|
||||
byte[] nameBytes = new byte[pos2-pos1-1];
|
||||
bb.position(pos1);
|
||||
bb.get(nameBytes);
|
||||
try {
|
||||
return new String(nameBytes, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return new String(nameBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getPostscriptName() {
|
||||
return psName;
|
||||
}
|
||||
|
||||
protected synchronized FontScaler getScaler() {
|
||||
if (scaler == null) {
|
||||
scaler = FontScaler.getScaler(this, 0, false, fileSize);
|
||||
}
|
||||
|
||||
return scaler;
|
||||
}
|
||||
|
||||
CharToGlyphMapper getMapper() {
|
||||
if (mapper == null) {
|
||||
mapper = new Type1GlyphMapper(this);
|
||||
}
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
try {
|
||||
return getScaler().getNumGlyphs();
|
||||
} catch (FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getNumGlyphs();
|
||||
}
|
||||
}
|
||||
|
||||
public int getMissingGlyphCode() {
|
||||
try {
|
||||
return getScaler().getMissingGlyphCode();
|
||||
} catch (FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getMissingGlyphCode();
|
||||
}
|
||||
}
|
||||
|
||||
public int getGlyphCode(char charCode) {
|
||||
try {
|
||||
return getScaler().getGlyphCode(charCode);
|
||||
} catch (FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getGlyphCode(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "** Type1 Font: Family="+familyName+ " Name="+fullName+
|
||||
" style="+style+" fileName="+getPublicFileName();
|
||||
}
|
||||
}
|
||||
177
jdkSrc/jdk8/sun/font/Type1GlyphMapper.java
Normal file
177
jdkSrc/jdk8/sun/font/Type1GlyphMapper.java
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
/*
|
||||
* This isn't a critical performance case, so don't do any
|
||||
* char->glyph map caching for Type1 fonts. The ones that are used
|
||||
* in composites will be cached there.
|
||||
*/
|
||||
|
||||
public final class Type1GlyphMapper extends CharToGlyphMapper {
|
||||
|
||||
Type1Font font;
|
||||
FontScaler scaler;
|
||||
|
||||
public Type1GlyphMapper(Type1Font font) {
|
||||
this.font = font;
|
||||
initMapper();
|
||||
}
|
||||
|
||||
private void initMapper() {
|
||||
scaler = font.getScaler();
|
||||
try {
|
||||
missingGlyph = scaler.getMissingGlyphCode();
|
||||
} catch (FontScalerException fe) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
try {
|
||||
missingGlyph = scaler.getMissingGlyphCode();
|
||||
} catch (FontScalerException e) { //should not happen
|
||||
missingGlyph = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumGlyphs() {
|
||||
try {
|
||||
return scaler.getNumGlyphs();
|
||||
} catch (FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return getNumGlyphs();
|
||||
}
|
||||
}
|
||||
|
||||
public int getMissingGlyphCode() {
|
||||
return missingGlyph;
|
||||
}
|
||||
|
||||
public boolean canDisplay(char ch) {
|
||||
try {
|
||||
return scaler.getGlyphCode(ch) != missingGlyph;
|
||||
} catch(FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return canDisplay(ch);
|
||||
}
|
||||
}
|
||||
|
||||
public int charToGlyph(char ch) {
|
||||
try {
|
||||
return scaler.getGlyphCode(ch);
|
||||
} catch (FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return charToGlyph(ch);
|
||||
}
|
||||
}
|
||||
|
||||
public int charToGlyph(int ch) {
|
||||
if (ch < 0 || ch > 0xffff) {
|
||||
return missingGlyph;
|
||||
} else {
|
||||
try {
|
||||
return scaler.getGlyphCode((char)ch);
|
||||
} catch (FontScalerException e) {
|
||||
scaler = FontScaler.getNullScaler();
|
||||
return charToGlyph(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {
|
||||
/* The conversion into surrogates is misleading.
|
||||
* The Type1 glyph mapper only accepts 16 bit unsigned shorts.
|
||||
* If its > not in the range it can use assign the missing glyph.
|
||||
*/
|
||||
for (int i=0; i<count; i++) {
|
||||
int code = unicodes[i]; // char is unsigned.
|
||||
|
||||
if (code >= HI_SURROGATE_START &&
|
||||
code <= HI_SURROGATE_END && i < count - 1) {
|
||||
char low = unicodes[i + 1];
|
||||
|
||||
if (low >= LO_SURROGATE_START &&
|
||||
low <= LO_SURROGATE_END) {
|
||||
code = (code - HI_SURROGATE_START) *
|
||||
0x400 + low - LO_SURROGATE_START + 0x10000;
|
||||
glyphs[i + 1] = 0xFFFF; // invisible glyph
|
||||
}
|
||||
}
|
||||
glyphs[i] = charToGlyph(code);
|
||||
if (code >= 0x10000) {
|
||||
i += 1; // Empty glyph slot after surrogate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
|
||||
/* I believe this code path is never exercised. Its there mainly
|
||||
* for surrogates and/or the opentype engine which aren't likely
|
||||
* to be an issue for Type1 fonts. So no need to optimise it.
|
||||
*/
|
||||
for (int i=0; i<count; i++) {
|
||||
glyphs[i] = charToGlyph(unicodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This variant checks if shaping is needed and immediately
|
||||
* returns true if it does. A caller of this method should be expecting
|
||||
* to check the return type because it needs to know how to handle
|
||||
* the character data for display.
|
||||
*/
|
||||
public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
int code = unicodes[i]; // char is unsigned.
|
||||
|
||||
if (code >= HI_SURROGATE_START &&
|
||||
code <= HI_SURROGATE_END && i < count - 1) {
|
||||
char low = unicodes[i + 1];
|
||||
|
||||
if (low >= LO_SURROGATE_START &&
|
||||
low <= LO_SURROGATE_END) {
|
||||
code = (code - HI_SURROGATE_START) *
|
||||
0x400 + low - LO_SURROGATE_START + 0x10000;
|
||||
glyphs[i + 1] = INVISIBLE_GLYPH_ID;
|
||||
}
|
||||
}
|
||||
|
||||
glyphs[i] = charToGlyph(code);
|
||||
|
||||
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
|
||||
continue;
|
||||
}
|
||||
else if (FontUtilities.isComplexCharCode(code)) {
|
||||
return true;
|
||||
}
|
||||
else if (code >= 0x10000) {
|
||||
i += 1; // Empty glyph slot after surrogate
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
308
jdkSrc/jdk8/sun/font/Underline.java
Normal file
308
jdkSrc/jdk8/sun/font/Underline.java
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998, All Rights Reserved
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.Stroke;
|
||||
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Line2D;
|
||||
|
||||
import java.awt.font.TextAttribute;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* This class provides drawing and bounds-measurement of
|
||||
* underlines. Additionally, it has a factory method for
|
||||
* obtaining underlines from values of underline attributes.
|
||||
*/
|
||||
|
||||
abstract class Underline {
|
||||
|
||||
/**
|
||||
* Draws the underline into g2d. The thickness should be obtained
|
||||
* from a LineMetrics object. Note that some underlines ignore the
|
||||
* thickness parameter.
|
||||
* The underline is drawn from (x1, y) to (x2, y).
|
||||
*/
|
||||
abstract void drawUnderline(Graphics2D g2d,
|
||||
float thickness,
|
||||
float x1,
|
||||
float x2,
|
||||
float y);
|
||||
|
||||
/**
|
||||
* Returns the bottom of the bounding rectangle for this underline.
|
||||
*/
|
||||
abstract float getLowerDrawLimit(float thickness);
|
||||
|
||||
/**
|
||||
* Returns a Shape representing the underline. The thickness should be obtained
|
||||
* from a LineMetrics object. Note that some underlines ignore the
|
||||
* thickness parameter.
|
||||
*/
|
||||
abstract Shape getUnderlineShape(float thickness,
|
||||
float x1,
|
||||
float x2,
|
||||
float y);
|
||||
|
||||
// Implementation of underline for standard and Input Method underlines.
|
||||
// These classes are private.
|
||||
|
||||
// IM Underlines ignore thickness param, and instead use
|
||||
// DEFAULT_THICKNESS
|
||||
private static final float DEFAULT_THICKNESS = 1.0f;
|
||||
|
||||
// StandardUnderline's constructor takes a boolean param indicating
|
||||
// whether to override the default thickness. These values clarify
|
||||
// the semantics of the parameter.
|
||||
private static final boolean USE_THICKNESS = true;
|
||||
private static final boolean IGNORE_THICKNESS = false;
|
||||
|
||||
// Implementation of standard underline and all input method underlines
|
||||
// except UNDERLINE_LOW_GRAY.
|
||||
private static final class StandardUnderline extends Underline {
|
||||
|
||||
// the amount by which to move the underline
|
||||
private float shift;
|
||||
|
||||
// the actual line thickness is this value times
|
||||
// the requested thickness
|
||||
private float thicknessMultiplier;
|
||||
|
||||
// if non-null, underline is drawn with a BasicStroke
|
||||
// with this dash pattern
|
||||
private float[] dashPattern;
|
||||
|
||||
// if false, all underlines are DEFAULT_THICKNESS thick
|
||||
// if true, use thickness param
|
||||
private boolean useThickness;
|
||||
|
||||
// cached BasicStroke
|
||||
private BasicStroke cachedStroke;
|
||||
|
||||
StandardUnderline(float shift,
|
||||
float thicknessMultiplier,
|
||||
float[] dashPattern,
|
||||
boolean useThickness) {
|
||||
|
||||
this.shift = shift;
|
||||
this.thicknessMultiplier = thicknessMultiplier;
|
||||
this.dashPattern = dashPattern;
|
||||
this.useThickness = useThickness;
|
||||
this.cachedStroke = null;
|
||||
}
|
||||
|
||||
private BasicStroke createStroke(float lineThickness) {
|
||||
|
||||
if (dashPattern == null) {
|
||||
return new BasicStroke(lineThickness,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER);
|
||||
}
|
||||
else {
|
||||
return new BasicStroke(lineThickness,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER,
|
||||
10.0f,
|
||||
dashPattern,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
private float getLineThickness(float thickness) {
|
||||
|
||||
if (useThickness) {
|
||||
return thickness * thicknessMultiplier;
|
||||
}
|
||||
else {
|
||||
return DEFAULT_THICKNESS * thicknessMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
private Stroke getStroke(float thickness) {
|
||||
|
||||
float lineThickness = getLineThickness(thickness);
|
||||
BasicStroke stroke = cachedStroke;
|
||||
if (stroke == null ||
|
||||
stroke.getLineWidth() != lineThickness) {
|
||||
|
||||
stroke = createStroke(lineThickness);
|
||||
cachedStroke = stroke;
|
||||
}
|
||||
|
||||
return stroke;
|
||||
}
|
||||
|
||||
void drawUnderline(Graphics2D g2d,
|
||||
float thickness,
|
||||
float x1,
|
||||
float x2,
|
||||
float y) {
|
||||
|
||||
|
||||
Stroke saveStroke = g2d.getStroke();
|
||||
g2d.setStroke(getStroke(thickness));
|
||||
g2d.draw(new Line2D.Float(x1, y + shift, x2, y + shift));
|
||||
g2d.setStroke(saveStroke);
|
||||
}
|
||||
|
||||
float getLowerDrawLimit(float thickness) {
|
||||
|
||||
return shift + getLineThickness(thickness);
|
||||
}
|
||||
|
||||
Shape getUnderlineShape(float thickness,
|
||||
float x1,
|
||||
float x2,
|
||||
float y) {
|
||||
|
||||
Stroke ulStroke = getStroke(thickness);
|
||||
Line2D line = new Line2D.Float(x1, y + shift, x2, y + shift);
|
||||
return ulStroke.createStrokedShape(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of UNDERLINE_LOW_GRAY.
|
||||
private static class IMGrayUnderline extends Underline {
|
||||
|
||||
private BasicStroke stroke;
|
||||
|
||||
IMGrayUnderline() {
|
||||
stroke = new BasicStroke(DEFAULT_THICKNESS,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER,
|
||||
10.0f,
|
||||
new float[] {1, 1},
|
||||
0);
|
||||
}
|
||||
|
||||
void drawUnderline(Graphics2D g2d,
|
||||
float thickness,
|
||||
float x1,
|
||||
float x2,
|
||||
float y) {
|
||||
|
||||
Stroke saveStroke = g2d.getStroke();
|
||||
g2d.setStroke(stroke);
|
||||
|
||||
Line2D.Float drawLine = new Line2D.Float(x1, y, x2, y);
|
||||
g2d.draw(drawLine);
|
||||
|
||||
drawLine.y1 += DEFAULT_THICKNESS;
|
||||
drawLine.y2 += DEFAULT_THICKNESS;
|
||||
drawLine.x1 += DEFAULT_THICKNESS;
|
||||
|
||||
g2d.draw(drawLine);
|
||||
|
||||
g2d.setStroke(saveStroke);
|
||||
}
|
||||
|
||||
float getLowerDrawLimit(float thickness) {
|
||||
|
||||
return DEFAULT_THICKNESS * 2;
|
||||
}
|
||||
|
||||
Shape getUnderlineShape(float thickness,
|
||||
float x1,
|
||||
float x2,
|
||||
float y) {
|
||||
|
||||
GeneralPath gp = new GeneralPath();
|
||||
|
||||
Line2D.Float line = new Line2D.Float(x1, y, x2, y);
|
||||
gp.append(stroke.createStrokedShape(line), false);
|
||||
|
||||
line.y1 += DEFAULT_THICKNESS;
|
||||
line.y2 += DEFAULT_THICKNESS;
|
||||
line.x1 += DEFAULT_THICKNESS;
|
||||
|
||||
gp.append(stroke.createStrokedShape(line), false);
|
||||
|
||||
return gp;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep a map of underlines, one for each type
|
||||
// of underline. The Underline objects are Flyweights
|
||||
// (shared across multiple clients), so they should be immutable.
|
||||
// If this implementation changes then clone underline
|
||||
// instances in getUnderline before returning them.
|
||||
private static final ConcurrentHashMap<Object, Underline>
|
||||
UNDERLINES = new ConcurrentHashMap<Object, Underline>(6);
|
||||
private static final Underline[] UNDERLINE_LIST;
|
||||
|
||||
static {
|
||||
Underline[] uls = new Underline[6];
|
||||
|
||||
uls[0] = new StandardUnderline(0, 1, null, USE_THICKNESS);
|
||||
UNDERLINES.put(TextAttribute.UNDERLINE_ON, uls[0]);
|
||||
|
||||
uls[1] = new StandardUnderline(1, 1, null, IGNORE_THICKNESS);
|
||||
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_ONE_PIXEL, uls[1]);
|
||||
|
||||
uls[2] = new StandardUnderline(1, 2, null, IGNORE_THICKNESS);
|
||||
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_TWO_PIXEL, uls[2]);
|
||||
|
||||
uls[3] = new StandardUnderline(1, 1, new float[] { 1, 1 }, IGNORE_THICKNESS);
|
||||
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_DOTTED, uls[3]);
|
||||
|
||||
uls[4] = new IMGrayUnderline();
|
||||
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_GRAY, uls[4]);
|
||||
|
||||
uls[5] = new StandardUnderline(1, 1, new float[] { 4, 4 }, IGNORE_THICKNESS);
|
||||
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_DASHED, uls[5]);
|
||||
|
||||
UNDERLINE_LIST = uls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Underline for the given value of
|
||||
* TextAttribute.INPUT_METHOD_UNDERLINE or
|
||||
* TextAttribute.UNDERLINE.
|
||||
* If value is not an input method underline value or
|
||||
* TextAttribute.UNDERLINE_ON, null is returned.
|
||||
*/
|
||||
static Underline getUnderline(Object value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Underline) UNDERLINES.get(value);
|
||||
}
|
||||
|
||||
static Underline getUnderline(int index) {
|
||||
return index < 0 ? null : UNDERLINE_LIST[index];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user