8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 1999, 2017, 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 java.awt.font;
import java.text.CharacterIterator;
class CharArrayIterator implements CharacterIterator {
private char[] chars;
private int pos;
private int begin;
CharArrayIterator(char[] chars) {
reset(chars, 0);
}
CharArrayIterator(char[] chars, int begin) {
reset(chars, begin);
}
/**
* Sets the position to getBeginIndex() and returns the character at that
* position.
* @return the first character in the text, or DONE if the text is empty
* @see #getBeginIndex
*/
public char first() {
pos = 0;
return current();
}
/**
* Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
* and returns the character at that position.
* @return the last character in the text, or DONE if the text is empty
* @see #getEndIndex
*/
public char last() {
if (chars.length > 0) {
pos = chars.length-1;
}
else {
pos = 0;
}
return current();
}
/**
* Gets the character at the current position (as returned by getIndex()).
* @return the character at the current position or DONE if the current
* position is off the end of the text.
* @see #getIndex
*/
public char current() {
if (pos >= 0 && pos < chars.length) {
return chars[pos];
}
else {
return DONE;
}
}
/**
* Increments the iterator's index by one and returns the character
* at the new index. If the resulting index is greater or equal
* to getEndIndex(), the current index is reset to getEndIndex() and
* a value of DONE is returned.
* @return the character at the new position or DONE if the new
* position is off the end of the text range.
*/
public char next() {
if (pos < chars.length-1) {
pos++;
return chars[pos];
}
else {
pos = chars.length;
return DONE;
}
}
/**
* Decrements the iterator's index by one and returns the character
* at the new index. If the current index is getBeginIndex(), the index
* remains at getBeginIndex() and a value of DONE is returned.
* @return the character at the new position or DONE if the current
* position is equal to getBeginIndex().
*/
public char previous() {
if (pos > 0) {
pos--;
return chars[pos];
}
else {
pos = 0;
return DONE;
}
}
/**
* Sets the position to the specified position in the text and returns that
* character.
* @param position the position within the text. Valid values range from
* getBeginIndex() to getEndIndex(). An IllegalArgumentException is thrown
* if an invalid value is supplied.
* @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
*/
public char setIndex(int position) {
position -= begin;
if (position < 0 || position > chars.length) {
throw new IllegalArgumentException("Invalid index");
}
pos = position;
return current();
}
/**
* Returns the start index of the text.
* @return the index at which the text begins.
*/
public int getBeginIndex() {
return begin;
}
/**
* Returns the end index of the text. This index is the index of the first
* character following the end of the text.
* @return the index after the last character in the text
*/
public int getEndIndex() {
return begin+chars.length;
}
/**
* Returns the current index.
* @return the current index.
*/
public int getIndex() {
return begin+pos;
}
/**
* Create a copy of this iterator
* @return A copy of this
*/
public Object clone() {
CharArrayIterator c = new CharArrayIterator(chars, begin);
c.pos = this.pos;
return c;
}
void reset(char[] chars) {
reset(chars, 0);
}
void reset(char[] chars, int begin) {
this.chars = chars;
this.begin = begin;
pos = 0;
}
}

View file

@ -0,0 +1,357 @@
/*
* Copyright (c) 1998, 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.
*/
/*
* @author Charlton Innovations, Inc.
*/
package java.awt.font;
import java.awt.RenderingHints;
import static java.awt.RenderingHints.*;
import java.awt.geom.AffineTransform;
/**
* The {@code FontRenderContext} class is a container for the
* information needed to correctly measure text. The measurement of text
* can vary because of rules that map outlines to pixels, and rendering
* hints provided by an application.
* <p>
* One such piece of information is a transform that scales
* typographical points to pixels. (A point is defined to be exactly 1/72
* of an inch, which is slightly different than
* the traditional mechanical measurement of a point.) A character that
* is rendered at 12pt on a 600dpi device might have a different size
* than the same character rendered at 12pt on a 72dpi device because of
* such factors as rounding to pixel boundaries and hints that the font
* designer may have specified.
* <p>
* Anti-aliasing and Fractional-metrics specified by an application can also
* affect the size of a character because of rounding to pixel
* boundaries.
* <p>
* Typically, instances of {@code FontRenderContext} are
* obtained from a {@link java.awt.Graphics2D Graphics2D} object. A
* {@code FontRenderContext} which is directly constructed will
* most likely not represent any actual graphics device, and may lead
* to unexpected or incorrect results.
* @see java.awt.RenderingHints#KEY_TEXT_ANTIALIASING
* @see java.awt.RenderingHints#KEY_FRACTIONALMETRICS
* @see java.awt.Graphics2D#getFontRenderContext()
* @see java.awt.font.LineMetrics
*/
public class FontRenderContext {
private transient AffineTransform tx;
private transient Object aaHintValue;
private transient Object fmHintValue;
private transient boolean defaulting;
/**
* Constructs a new {@code FontRenderContext}
* object.
*
*/
protected FontRenderContext() {
aaHintValue = VALUE_TEXT_ANTIALIAS_DEFAULT;
fmHintValue = VALUE_FRACTIONALMETRICS_DEFAULT;
defaulting = true;
}
/**
* Constructs a {@code FontRenderContext} object from an
* optional {@link AffineTransform} and two {@code boolean}
* values that determine if the newly constructed object has
* anti-aliasing or fractional metrics.
* In each case the boolean values {@code true} and {@code false}
* correspond to the rendering hint values {@code ON} and
* {@code OFF} respectively.
* <p>
* To specify other hint values, use the constructor which
* specifies the rendering hint values as parameters :
* {@link #FontRenderContext(AffineTransform, Object, Object)}.
* @param tx the transform which is used to scale typographical points
* to pixels in this {@code FontRenderContext}. If null, an
* identity transform is used.
* @param isAntiAliased determines if the newly constructed object
* has anti-aliasing.
* @param usesFractionalMetrics determines if the newly constructed
* object has fractional metrics.
*/
public FontRenderContext(AffineTransform tx,
boolean isAntiAliased,
boolean usesFractionalMetrics) {
if (tx != null && !tx.isIdentity()) {
this.tx = new AffineTransform(tx);
}
if (isAntiAliased) {
aaHintValue = VALUE_TEXT_ANTIALIAS_ON;
} else {
aaHintValue = VALUE_TEXT_ANTIALIAS_OFF;
}
if (usesFractionalMetrics) {
fmHintValue = VALUE_FRACTIONALMETRICS_ON;
} else {
fmHintValue = VALUE_FRACTIONALMETRICS_OFF;
}
}
/**
* Constructs a {@code FontRenderContext} object from an
* optional {@link AffineTransform} and two {@code Object}
* values that determine if the newly constructed object has
* anti-aliasing or fractional metrics.
* @param tx the transform which is used to scale typographical points
* to pixels in this {@code FontRenderContext}. If null, an
* identity transform is used.
* @param aaHint - one of the text antialiasing rendering hint values
* defined in {@link java.awt.RenderingHints java.awt.RenderingHints}.
* Any other value will throw {@code IllegalArgumentException}.
* {@link java.awt.RenderingHints#VALUE_TEXT_ANTIALIAS_DEFAULT VALUE_TEXT_ANTIALIAS_DEFAULT}
* may be specified, in which case the mode used is implementation
* dependent.
* @param fmHint - one of the text fractional rendering hint values defined
* in {@link java.awt.RenderingHints java.awt.RenderingHints}.
* {@link java.awt.RenderingHints#VALUE_FRACTIONALMETRICS_DEFAULT VALUE_FRACTIONALMETRICS_DEFAULT}
* may be specified, in which case the mode used is implementation
* dependent.
* Any other value will throw {@code IllegalArgumentException}
* @throws IllegalArgumentException if the hints are not one of the
* legal values.
* @since 1.6
*/
public FontRenderContext(AffineTransform tx, Object aaHint, Object fmHint){
if (tx != null && !tx.isIdentity()) {
this.tx = new AffineTransform(tx);
}
try {
if (KEY_TEXT_ANTIALIASING.isCompatibleValue(aaHint)) {
aaHintValue = aaHint;
} else {
throw new IllegalArgumentException("AA hint:" + aaHint);
}
} catch (Exception e) {
throw new IllegalArgumentException("AA hint:" +aaHint);
}
try {
if (KEY_FRACTIONALMETRICS.isCompatibleValue(fmHint)) {
fmHintValue = fmHint;
} else {
throw new IllegalArgumentException("FM hint:" + fmHint);
}
} catch (Exception e) {
throw new IllegalArgumentException("FM hint:" +fmHint);
}
}
/**
* Indicates whether or not this {@code FontRenderContext} object
* measures text in a transformed render context.
* @return {@code true} if this {@code FontRenderContext}
* object has a non-identity AffineTransform attribute.
* {@code false} otherwise.
* @see java.awt.font.FontRenderContext#getTransform
* @since 1.6
*/
public boolean isTransformed() {
if (!defaulting) {
return tx != null;
} else {
return !getTransform().isIdentity();
}
}
/**
* Returns the integer type of the affine transform for this
* {@code FontRenderContext} as specified by
* {@link java.awt.geom.AffineTransform#getType()}
* @return the type of the transform.
* @see AffineTransform
* @since 1.6
*/
public int getTransformType() {
if (!defaulting) {
if (tx == null) {
return AffineTransform.TYPE_IDENTITY;
} else {
return tx.getType();
}
} else {
return getTransform().getType();
}
}
/**
* Gets the transform that is used to scale typographical points
* to pixels in this {@code FontRenderContext}.
* @return the {@code AffineTransform} of this
* {@code FontRenderContext}.
* @see AffineTransform
*/
public AffineTransform getTransform() {
return (tx == null) ? new AffineTransform() : new AffineTransform(tx);
}
/**
* Returns a boolean which indicates whether or not some form of
* antialiasing is specified by this {@code FontRenderContext}.
* Call {@link #getAntiAliasingHint() getAntiAliasingHint()}
* for the specific rendering hint value.
* @return {@code true}, if text is anti-aliased in this
* {@code FontRenderContext}; {@code false} otherwise.
* @see java.awt.RenderingHints#KEY_TEXT_ANTIALIASING
* @see #FontRenderContext(AffineTransform,boolean,boolean)
* @see #FontRenderContext(AffineTransform,Object,Object)
*/
public boolean isAntiAliased() {
return !(aaHintValue == VALUE_TEXT_ANTIALIAS_OFF ||
aaHintValue == VALUE_TEXT_ANTIALIAS_DEFAULT);
}
/**
* Returns a boolean which whether text fractional metrics mode
* is used in this {@code FontRenderContext}.
* Call {@link #getFractionalMetricsHint() getFractionalMetricsHint()}
* to obtain the corresponding rendering hint value.
* @return {@code true}, if layout should be performed with
* fractional metrics; {@code false} otherwise.
* in this {@code FontRenderContext}.
* @see java.awt.RenderingHints#KEY_FRACTIONALMETRICS
* @see #FontRenderContext(AffineTransform,boolean,boolean)
* @see #FontRenderContext(AffineTransform,Object,Object)
*/
public boolean usesFractionalMetrics() {
return !(fmHintValue == VALUE_FRACTIONALMETRICS_OFF ||
fmHintValue == VALUE_FRACTIONALMETRICS_DEFAULT);
}
/**
* Return the text anti-aliasing rendering mode hint used in this
* {@code FontRenderContext}.
* This will be one of the text antialiasing rendering hint values
* defined in {@link java.awt.RenderingHints java.awt.RenderingHints}.
* @return text anti-aliasing rendering mode hint used in this
* {@code FontRenderContext}.
* @since 1.6
*/
public Object getAntiAliasingHint() {
if (defaulting) {
if (isAntiAliased()) {
return VALUE_TEXT_ANTIALIAS_ON;
} else {
return VALUE_TEXT_ANTIALIAS_OFF;
}
}
return aaHintValue;
}
/**
* Return the text fractional metrics rendering mode hint used in this
* {@code FontRenderContext}.
* This will be one of the text fractional metrics rendering hint values
* defined in {@link java.awt.RenderingHints java.awt.RenderingHints}.
* @return the text fractional metrics rendering mode hint used in this
* {@code FontRenderContext}.
* @since 1.6
*/
public Object getFractionalMetricsHint() {
if (defaulting) {
if (usesFractionalMetrics()) {
return VALUE_FRACTIONALMETRICS_ON;
} else {
return VALUE_FRACTIONALMETRICS_OFF;
}
}
return fmHintValue;
}
/**
* Return true if obj is an instance of FontRenderContext and has the same
* transform, antialiasing, and fractional metrics values as this.
* @param obj the object to test for equality
* @return {@code true} if the specified object is equal to
* this {@code FontRenderContext}; {@code false}
* otherwise.
*/
public boolean equals(Object obj) {
try {
return equals((FontRenderContext)obj);
}
catch (ClassCastException e) {
return false;
}
}
/**
* Return true if rhs has the same transform, antialiasing,
* and fractional metrics values as this.
* @param rhs the {@code FontRenderContext} to test for equality
* @return {@code true} if {@code rhs} is equal to
* this {@code FontRenderContext}; {@code false}
* otherwise.
* @since 1.4
*/
public boolean equals(FontRenderContext rhs) {
if (this == rhs) {
return true;
}
if (rhs == null) {
return false;
}
/* if neither instance is a subclass, reference values directly. */
if (!rhs.defaulting && !defaulting) {
if (rhs.aaHintValue == aaHintValue &&
rhs.fmHintValue == fmHintValue) {
return tx == null ? rhs.tx == null : tx.equals(rhs.tx);
}
return false;
} else {
return
rhs.getAntiAliasingHint() == getAntiAliasingHint() &&
rhs.getFractionalMetricsHint() == getFractionalMetricsHint() &&
rhs.getTransform().equals(getTransform());
}
}
/**
* Return a hashcode for this FontRenderContext.
*/
public int hashCode() {
int hash = tx == null ? 0 : tx.hashCode();
/* SunHints value objects have identity hashcode, so we can rely on
* this to ensure that two equal FRC's have the same hashcode.
*/
if (defaulting) {
hash += getAntiAliasingHint().hashCode();
hash += getFractionalMetricsHint().hashCode();
} else {
hash += aaHintValue.hashCode();
hash += fmHintValue.hashCode();
}
return hash;
}
}

View file

@ -0,0 +1,212 @@
/*
* Copyright (c) 1997, 1999, 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package java.awt.font;
/**
* The {@code GlyphJustificationInfo} class represents information
* about the justification properties of a glyph. A glyph is the visual
* representation of one or more characters. Many different glyphs can
* be used to represent a single character or combination of characters.
* The four justification properties represented by
* {@code GlyphJustificationInfo} are weight, priority, absorb and
* limit.
* <p>
* Weight is the overall 'weight' of the glyph in the line. Generally it is
* proportional to the size of the font. Glyphs with larger weight are
* allocated a correspondingly larger amount of the change in space.
* <p>
* Priority determines the justification phase in which this glyph is used.
* All glyphs of the same priority are examined before glyphs of the next
* priority. If all the change in space can be allocated to these glyphs
* without exceeding their limits, then glyphs of the next priority are not
* examined. There are four priorities, kashida, whitespace, interchar,
* and none. KASHIDA is the first priority examined. NONE is the last
* priority examined.
* <p>
* Absorb determines whether a glyph absorbs all change in space. Within a
* given priority, some glyphs may absorb all the change in space. If any of
* these glyphs are present, no glyphs of later priority are examined.
* <p>
* Limit determines the maximum or minimum amount by which the glyph can
* change. Left and right sides of the glyph can have different limits.
* <p>
* Each {@code GlyphJustificationInfo} represents two sets of
* metrics, which are <i>growing</i> and <i>shrinking</i>. Growing
* metrics are used when the glyphs on a line are to be
* spread apart to fit a larger width. Shrinking metrics are used when
* the glyphs are to be moved together to fit a smaller width.
*/
public final class GlyphJustificationInfo {
/**
* Constructs information about the justification properties of a
* glyph.
* @param weight the weight of this glyph when allocating space. Must be non-negative.
* @param growAbsorb if {@code true} this glyph absorbs
* all extra space at this priority and lower priority levels when it
* grows
* @param growPriority the priority level of this glyph when it
* grows
* @param growLeftLimit the maximum amount by which the left side of this
* glyph can grow. Must be non-negative.
* @param growRightLimit the maximum amount by which the right side of this
* glyph can grow. Must be non-negative.
* @param shrinkAbsorb if {@code true}, this glyph absorbs all
* remaining shrinkage at this and lower priority levels when it
* shrinks
* @param shrinkPriority the priority level of this glyph when
* it shrinks
* @param shrinkLeftLimit the maximum amount by which the left side of this
* glyph can shrink. Must be non-negative.
* @param shrinkRightLimit the maximum amount by which the right side
* of this glyph can shrink. Must be non-negative.
*/
public GlyphJustificationInfo(float weight,
boolean growAbsorb,
int growPriority,
float growLeftLimit,
float growRightLimit,
boolean shrinkAbsorb,
int shrinkPriority,
float shrinkLeftLimit,
float shrinkRightLimit)
{
if (weight < 0) {
throw new IllegalArgumentException("weight is negative");
}
if (!priorityIsValid(growPriority)) {
throw new IllegalArgumentException("Invalid grow priority");
}
if (growLeftLimit < 0) {
throw new IllegalArgumentException("growLeftLimit is negative");
}
if (growRightLimit < 0) {
throw new IllegalArgumentException("growRightLimit is negative");
}
if (!priorityIsValid(shrinkPriority)) {
throw new IllegalArgumentException("Invalid shrink priority");
}
if (shrinkLeftLimit < 0) {
throw new IllegalArgumentException("shrinkLeftLimit is negative");
}
if (shrinkRightLimit < 0) {
throw new IllegalArgumentException("shrinkRightLimit is negative");
}
this.weight = weight;
this.growAbsorb = growAbsorb;
this.growPriority = growPriority;
this.growLeftLimit = growLeftLimit;
this.growRightLimit = growRightLimit;
this.shrinkAbsorb = shrinkAbsorb;
this.shrinkPriority = shrinkPriority;
this.shrinkLeftLimit = shrinkLeftLimit;
this.shrinkRightLimit = shrinkRightLimit;
}
private static boolean priorityIsValid(int priority) {
return priority >= PRIORITY_KASHIDA && priority <= PRIORITY_NONE;
}
/** The highest justification priority. */
public static final int PRIORITY_KASHIDA = 0;
/** The second highest justification priority. */
public static final int PRIORITY_WHITESPACE = 1;
/** The second lowest justification priority. */
public static final int PRIORITY_INTERCHAR = 2;
/** The lowest justification priority. */
public static final int PRIORITY_NONE = 3;
/**
* The weight of this glyph.
*/
public final float weight;
/**
* The priority level of this glyph as it is growing.
*/
public final int growPriority;
/**
* If {@code true}, this glyph absorbs all extra
* space at this and lower priority levels when it grows.
*/
public final boolean growAbsorb;
/**
* The maximum amount by which the left side of this glyph can grow.
*/
public final float growLeftLimit;
/**
* The maximum amount by which the right side of this glyph can grow.
*/
public final float growRightLimit;
/**
* The priority level of this glyph as it is shrinking.
*/
public final int shrinkPriority;
/**
* If {@code true},this glyph absorbs all remaining shrinkage at
* this and lower priority levels as it shrinks.
*/
public final boolean shrinkAbsorb;
/**
* The maximum amount by which the left side of this glyph can shrink
* (a positive number).
*/
public final float shrinkLeftLimit;
/**
* The maximum amount by which the right side of this glyph can shrink
* (a positive number).
*/
public final float shrinkRightLimit;
}

View file

@ -0,0 +1,324 @@
/*
* Copyright (c) 1997, 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.awt.geom.Rectangle2D;
/**
* The {@code GlyphMetrics} class represents information for a
* single glyph. A glyph is the visual representation of one or more
* characters. Many different glyphs can be used to represent a single
* character or combination of characters. {@code GlyphMetrics}
* instances are produced by {@link java.awt.Font Font} and are applicable
* to a specific glyph in a particular {@code Font}.
* <p>
* Glyphs are either STANDARD, LIGATURE, COMBINING, or COMPONENT.
* <ul>
* <li>STANDARD glyphs are commonly used to represent single characters.
* <li>LIGATURE glyphs are used to represent sequences of characters.
* <li>COMPONENT glyphs in a {@link GlyphVector} do not correspond to a
* particular character in a text model. Instead, COMPONENT glyphs are
* added for typographical reasons, such as Arabic justification.
* <li>COMBINING glyphs embellish STANDARD or LIGATURE glyphs, such
* as accent marks. Carets do not appear before COMBINING glyphs.
* </ul>
* <p>
* Other metrics available through {@code GlyphMetrics} are the
* components of the advance, the visual bounds, and the left and right
* side bearings.
* <p>
* Glyphs for a rotated font, or obtained from a {@code GlyphVector}
* which has applied a rotation to the glyph, can have advances that
* contain both X and Y components. Usually the advance only has one
* component.
* <p>
* The advance of a glyph is the distance from the glyph's origin to the
* origin of the next glyph along the baseline, which is either vertical
* or horizontal. Note that, in a {@code GlyphVector},
* the distance from a glyph to its following glyph might not be the
* glyph's advance, because of kerning or other positioning adjustments.
* <p>
* The bounds is the smallest rectangle that completely contains the
* outline of the glyph. The bounds rectangle is relative to the
* glyph's origin. The left-side bearing is the distance from the glyph
* origin to the left of its bounds rectangle. If the left-side bearing is
* negative, part of the glyph is drawn to the left of its origin. The
* right-side bearing is the distance from the right side of the bounds
* rectangle to the next glyph origin (the origin plus the advance). If
* negative, part of the glyph is drawn to the right of the next glyph's
* origin. Note that the bounds does not necessarily enclose all the pixels
* affected when rendering the glyph, because of rasterization and pixel
* adjustment effects.
* <p>
* Although instances of {@code GlyphMetrics} can be directly
* constructed, they are almost always obtained from a
* {@code GlyphVector}. Once constructed, {@code GlyphMetrics}
* objects are immutable.
* <p>
* <strong>Example</strong>:<p>
* Querying a {@code Font} for glyph information
* <blockquote><pre>
* Font font = ...;
* int glyphIndex = ...;
* GlyphMetrics metrics = GlyphVector.getGlyphMetrics(glyphIndex);
* int isStandard = metrics.isStandard();
* float glyphAdvance = metrics.getAdvance();
* </pre></blockquote>
* @see java.awt.Font
* @see GlyphVector
*/
public final class GlyphMetrics {
/**
* Indicates whether the metrics are for a horizontal or vertical baseline.
*/
private boolean horizontal;
/**
* The x-component of the advance.
*/
private float advanceX;
/**
* The y-component of the advance.
*/
private float advanceY;
/**
* The bounds of the associated glyph.
*/
private Rectangle2D.Float bounds;
/**
* Additional information about the glyph encoded as a byte.
*/
private byte glyphType;
/**
* Indicates a glyph that represents a single standard
* character.
*/
public static final byte STANDARD = 0;
/**
* Indicates a glyph that represents multiple characters
* as a ligature, for example 'fi' or 'ffi'. It is followed by
* filler glyphs for the remaining characters. Filler and combining
* glyphs can be intermixed to control positioning of accent marks
* on the logically preceding ligature.
*/
public static final byte LIGATURE = 1;
/**
* Indicates a glyph that represents a combining character,
* such as an umlaut. There is no caret position between this glyph
* and the preceding glyph.
*/
public static final byte COMBINING = 2;
/**
* Indicates a glyph with no corresponding character in the
* backing store. The glyph is associated with the character
* represented by the logically preceding non-component glyph. This
* is used for kashida justification or other visual modifications to
* existing glyphs. There is no caret position between this glyph
* and the preceding glyph.
*/
public static final byte COMPONENT = 3;
/**
* Indicates a glyph with no visual representation. It can
* be added to the other code values to indicate an invisible glyph.
*/
public static final byte WHITESPACE = 4;
/**
* Constructs a {@code GlyphMetrics} object.
* @param advance the advance width of the glyph
* @param bounds the black box bounds of the glyph
* @param glyphType the type of the glyph
*/
public GlyphMetrics(float advance, Rectangle2D bounds, byte glyphType) {
this.horizontal = true;
this.advanceX = advance;
this.advanceY = 0;
this.bounds = new Rectangle2D.Float();
this.bounds.setRect(bounds);
this.glyphType = glyphType;
}
/**
* Constructs a {@code GlyphMetrics} object.
* @param horizontal if true, metrics are for a horizontal baseline,
* otherwise they are for a vertical baseline
* @param advanceX the X-component of the glyph's advance
* @param advanceY the Y-component of the glyph's advance
* @param bounds the visual bounds of the glyph
* @param glyphType the type of the glyph
* @since 1.4
*/
public GlyphMetrics(boolean horizontal, float advanceX, float advanceY,
Rectangle2D bounds, byte glyphType) {
this.horizontal = horizontal;
this.advanceX = advanceX;
this.advanceY = advanceY;
this.bounds = new Rectangle2D.Float();
this.bounds.setRect(bounds);
this.glyphType = glyphType;
}
/**
* Returns the advance of the glyph along the baseline (either
* horizontal or vertical).
* @return the advance of the glyph
*/
public float getAdvance() {
return horizontal ? advanceX : advanceY;
}
/**
* Returns the x-component of the advance of the glyph.
* @return the x-component of the advance of the glyph
* @since 1.4
*/
public float getAdvanceX() {
return advanceX;
}
/**
* Returns the y-component of the advance of the glyph.
* @return the y-component of the advance of the glyph
* @since 1.4
*/
public float getAdvanceY() {
return advanceY;
}
/**
* Returns the bounds of the glyph. This is the bounding box of the glyph outline.
* Because of rasterization and pixel alignment effects, it does not necessarily
* enclose the pixels that are affected when rendering the glyph.
* @return a {@link Rectangle2D} that is the bounds of the glyph.
*/
public Rectangle2D getBounds2D() {
return new Rectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height);
}
/**
* Returns the left (top) side bearing of the glyph.
* <p>
* This is the distance from 0,&nbsp;0 to the left (top) of the glyph
* bounds. If the bounds of the glyph is to the left of (above) the
* origin, the LSB is negative.
* @return the left side bearing of the glyph.
*/
public float getLSB() {
return horizontal ? bounds.x : bounds.y;
}
/**
* Returns the right (bottom) side bearing of the glyph.
* <p>
* This is the distance from the right (bottom) of the glyph bounds to
* the advance. If the bounds of the glyph is to the right of (below)
* the advance, the RSB is negative.
* @return the right side bearing of the glyph.
*/
public float getRSB() {
return horizontal ?
advanceX - bounds.x - bounds.width :
advanceY - bounds.y - bounds.height;
}
/**
* Returns the raw glyph type code.
* @return the raw glyph type code.
*/
public int getType() {
return glyphType;
}
/**
* Returns {@code true} if this is a standard glyph.
* @return {@code true} if this is a standard glyph;
* {@code false} otherwise.
*/
public boolean isStandard() {
return (glyphType & 0x3) == STANDARD;
}
/**
* Returns {@code true} if this is a ligature glyph.
* @return {@code true} if this is a ligature glyph;
* {@code false} otherwise.
*/
public boolean isLigature() {
return (glyphType & 0x3) == LIGATURE;
}
/**
* Returns {@code true} if this is a combining glyph.
* @return {@code true} if this is a combining glyph;
* {@code false} otherwise.
*/
public boolean isCombining() {
return (glyphType & 0x3) == COMBINING;
}
/**
* Returns {@code true} if this is a component glyph.
* @return {@code true} if this is a component glyph;
* {@code false} otherwise.
*/
public boolean isComponent() {
return (glyphType & 0x3) == COMPONENT;
}
/**
* Returns {@code true} if this is a whitespace glyph.
* @return {@code true} if this is a whitespace glyph;
* {@code false} otherwise.
*/
public boolean isWhitespace() {
return (glyphType & 0x4) == WHITESPACE;
}
}

View file

@ -0,0 +1,611 @@
/*
* Copyright (c) 1998, 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.
*/
/*
* @author Charlton Innovations, Inc.
*/
package java.awt.font;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.Polygon; // remind - need a floating point version
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
import java.awt.Shape;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphJustificationInfo;
/**
* A {@code GlyphVector} object is a collection of glyphs
* containing geometric information for the placement of each glyph
* in a transformed coordinate space which corresponds to the
* device on which the {@code GlyphVector} is ultimately
* displayed.
* <p>
* The {@code GlyphVector} does not attempt any interpretation of
* the sequence of glyphs it contains. Relationships between adjacent
* glyphs in sequence are solely used to determine the placement of
* the glyphs in the visual coordinate space.
* <p>
* Instances of {@code GlyphVector} are created by a {@link Font}.
* <p>
* In a text processing application that can cache intermediate
* representations of text, creation and subsequent caching of a
* {@code GlyphVector} for use during rendering is the fastest
* method to present the visual representation of characters to a user.
* <p>
* A {@code GlyphVector} is associated with exactly one
* {@code Font}, and can provide data useful only in relation to
* this {@code Font}. In addition, metrics obtained from a
* {@code GlyphVector} are not generally geometrically scalable
* since the pixelization and spacing are dependent on grid-fitting
* algorithms within a {@code Font}. To facilitate accurate
* measurement of a {@code GlyphVector} and its component
* glyphs, you must specify a scaling transform, anti-alias mode, and
* fractional metrics mode when creating the {@code GlyphVector}.
* These characteristics can be derived from the destination device.
* <p>
* For each glyph in the {@code GlyphVector}, you can obtain:
* <ul>
* <li>the position of the glyph
* <li>the transform associated with the glyph
* <li>the metrics of the glyph in the context of the
* {@code GlyphVector}. The metrics of the glyph may be
* different under different transforms, application specified
* rendering hints, and the specific instance of the glyph within
* the {@code GlyphVector}.
* </ul>
* <p>
* Altering the data used to create the {@code GlyphVector} does not
* alter the state of the {@code GlyphVector}.
* <p>
* Methods are provided to adjust the positions of the glyphs
* within the {@code GlyphVector}. These methods are most
* appropriate for applications that are performing justification
* operations for the presentation of the glyphs.
* <p>
* Methods are provided to transform individual glyphs within the
* {@code GlyphVector}. These methods are primarily useful for
* special effects.
* <p>
* Methods are provided to return both the visual, logical, and pixel bounds
* of the entire {@code GlyphVector} or of individual glyphs within
* the {@code GlyphVector}.
* <p>
* Methods are provided to return a {@link Shape} for the
* {@code GlyphVector}, and for individual glyphs within the
* {@code GlyphVector}.
* @see Font
* @see GlyphMetrics
* @see TextLayout
* @author Charlton Innovations, Inc.
*/
public abstract class GlyphVector implements Cloneable {
//
// methods associated with creation-time state
//
/**
* Returns the {@code Font} associated with this
* {@code GlyphVector}.
* @return {@code Font} used to create this
* {@code GlyphVector}.
* @see Font
*/
public abstract Font getFont();
/**
* Returns the {@link FontRenderContext} associated with this
* {@code GlyphVector}.
* @return {@code FontRenderContext} used to create this
* {@code GlyphVector}.
* @see FontRenderContext
* @see Font
*/
public abstract FontRenderContext getFontRenderContext();
//
// methods associated with the GlyphVector as a whole
//
/**
* Assigns default positions to each glyph in this
* {@code GlyphVector}. This can destroy information
* generated during initial layout of this {@code GlyphVector}.
*/
public abstract void performDefaultLayout();
/**
* Returns the number of glyphs in this {@code GlyphVector}.
* @return number of glyphs in this {@code GlyphVector}.
*/
public abstract int getNumGlyphs();
/**
* Returns the glyphcode of the specified glyph.
* This return value is meaningless to anything other
* than the {@code Font} object that created this
* {@code GlyphVector}.
* @param glyphIndex the index into this {@code GlyphVector}
* that corresponds to the glyph from which to retrieve the
* glyphcode.
* @return the glyphcode of the glyph at the specified
* {@code glyphIndex}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the
* number of glyphs in this {@code GlyphVector}
*/
public abstract int getGlyphCode(int glyphIndex);
/**
* Returns an array of glyphcodes for the specified glyphs.
* The contents of this return value are meaningless to anything other
* than the {@code Font} used to create this
* {@code GlyphVector}. This method is used
* for convenience and performance when processing glyphcodes.
* If no array is passed in, a new array is created.
* @param beginGlyphIndex the index into this
* {@code GlyphVector} at which to start retrieving glyphcodes
* @param numEntries the number of glyphcodes to retrieve
* @param codeReturn the array that receives the glyphcodes and is
* then returned
* @return an array of glyphcodes for the specified glyphs.
* @throws IllegalArgumentException if {@code numEntries} is
* less than 0
* @throws IndexOutOfBoundsException if {@code beginGlyphIndex}
* is less than 0
* @throws IndexOutOfBoundsException if the sum of
* {@code beginGlyphIndex} and {@code numEntries} is
* greater than the number of glyphs in this
* {@code GlyphVector}
*/
public abstract int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
int[] codeReturn);
/**
* Returns the character index of the specified glyph.
* The character index is the index of the first logical
* character represented by the glyph. The default
* implementation assumes a one-to-one, left-to-right mapping
* of glyphs to characters.
* @param glyphIndex the index of the glyph
* @return the index of the first character represented by the glyph
* @since 1.4
*/
public int getGlyphCharIndex(int glyphIndex) {
return glyphIndex;
}
/**
* Returns the character indices of the specified glyphs.
* The character index is the index of the first logical
* character represented by the glyph. Indices are returned
* in glyph order. The default implementation invokes
* getGlyphCharIndex for each glyph, and subclassers will probably
* want to override this implementation for performance reasons.
* Use this method for convenience and performance
* in processing of glyphcodes. If no array is passed in,
* a new array is created.
* @param beginGlyphIndex the index of the first glyph
* @param numEntries the number of glyph indices
* @param codeReturn the array into which to return the character indices
* @return an array of character indices, one per glyph.
* @since 1.4
*/
public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries,
int[] codeReturn) {
if (codeReturn == null) {
codeReturn = new int[numEntries];
}
for (int i = 0, j = beginGlyphIndex; i < numEntries; ++i, ++j) {
codeReturn[i] = getGlyphCharIndex(j);
}
return codeReturn;
}
/**
* Returns the logical bounds of this {@code GlyphVector}.
* This method is used when positioning this {@code GlyphVector}
* in relation to visually adjacent {@code GlyphVector} objects.
* @return a {@link Rectangle2D} that is the logical bounds of this
* {@code GlyphVector}.
*/
public abstract Rectangle2D getLogicalBounds();
/**
* Returns the visual bounds of this {@code GlyphVector}
* The visual bounds is the bounding box of the outline of this
* {@code GlyphVector}. Because of rasterization and
* alignment of pixels, it is possible that this box does not
* enclose all pixels affected by rendering this {@code GlyphVector}.
* @return a {@code Rectangle2D} that is the bounding box
* of this {@code GlyphVector}.
*/
public abstract Rectangle2D getVisualBounds();
/**
* Returns the pixel bounds of this {@code GlyphVector} when
* rendered in a graphics with the given
* {@code FontRenderContext} at the given location. The
* renderFRC need not be the same as the
* {@code FontRenderContext} of this
* {@code GlyphVector}, and can be null. If it is null, the
* {@code FontRenderContext} of this {@code GlyphVector}
* is used. The default implementation returns the visual bounds,
* offset to x, y and rounded out to the next integer value (i.e. returns an
* integer rectangle which encloses the visual bounds) and
* ignores the FRC. Subclassers should override this method.
* @param renderFRC the {@code FontRenderContext} of the {@code Graphics}.
* @param x the x-coordinate at which to render this {@code GlyphVector}.
* @param y the y-coordinate at which to render this {@code GlyphVector}.
* @return a {@code Rectangle} bounding the pixels that would be affected.
* @since 1.4
*/
public Rectangle getPixelBounds(FontRenderContext renderFRC, float x, float y) {
Rectangle2D rect = getVisualBounds();
int l = (int)Math.floor(rect.getX() + x);
int t = (int)Math.floor(rect.getY() + y);
int r = (int)Math.ceil(rect.getMaxX() + x);
int b = (int)Math.ceil(rect.getMaxY() + y);
return new Rectangle(l, t, r - l, b - t);
}
/**
* Returns a {@code Shape} whose interior corresponds to the
* visual representation of this {@code GlyphVector}.
* @return a {@code Shape} that is the outline of this
* {@code GlyphVector}.
*/
public abstract Shape getOutline();
/**
* Returns a {@code Shape} whose interior corresponds to the
* visual representation of this {@code GlyphVector} when
* rendered at x,&nbsp;y.
* @param x the X coordinate of this {@code GlyphVector}.
* @param y the Y coordinate of this {@code GlyphVector}.
* @return a {@code Shape} that is the outline of this
* {@code GlyphVector} when rendered at the specified
* coordinates.
*/
public abstract Shape getOutline(float x, float y);
/**
* Returns a {@code Shape} whose interior corresponds to the
* visual representation of the specified glyph
* within this {@code GlyphVector}.
* The outline returned by this method is positioned around the
* origin of each individual glyph.
* @param glyphIndex the index into this {@code GlyphVector}
* @return a {@code Shape} that is the outline of the glyph
* at the specified {@code glyphIndex} of this
* {@code GlyphVector}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
*/
public abstract Shape getGlyphOutline(int glyphIndex);
/**
* Returns a {@code Shape} whose interior corresponds to the
* visual representation of the specified glyph
* within this {@code GlyphVector}, offset to x,&nbsp;y.
* The outline returned by this method is positioned around the
* origin of each individual glyph.
* @param glyphIndex the index into this {@code GlyphVector}
* @param x the X coordinate of the location of this {@code GlyphVector}
* @param y the Y coordinate of the location of this {@code GlyphVector}
* @return a {@code Shape} that is the outline of the glyph
* at the specified {@code glyphIndex} of this
* {@code GlyphVector} when rendered at the specified
* coordinates.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
* @since 1.4
*/
public Shape getGlyphOutline(int glyphIndex, float x, float y) {
Shape s = getGlyphOutline(glyphIndex);
AffineTransform at = AffineTransform.getTranslateInstance(x,y);
return at.createTransformedShape(s);
}
/**
* Returns the position of the specified glyph relative to the
* origin of this {@code GlyphVector}.
* If {@code glyphIndex} equals the number of glyphs in
* this {@code GlyphVector}, this method returns the position after
* the last glyph. This position is used to define the advance of
* the entire {@code GlyphVector}.
* @param glyphIndex the index into this {@code GlyphVector}
* @return a {@link Point2D} object that is the position of the glyph
* at the specified {@code glyphIndex}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than the number of glyphs
* in this {@code GlyphVector}
* @see #setGlyphPosition
*/
public abstract Point2D getGlyphPosition(int glyphIndex);
/**
* Sets the position of the specified glyph within this
* {@code GlyphVector}.
* If {@code glyphIndex} equals the number of glyphs in
* this {@code GlyphVector}, this method sets the position after
* the last glyph. This position is used to define the advance of
* the entire {@code GlyphVector}.
* @param glyphIndex the index into this {@code GlyphVector}
* @param newPos the {@code Point2D} at which to position the
* glyph at the specified {@code glyphIndex}
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than the number of glyphs
* in this {@code GlyphVector}
* @see #getGlyphPosition
*/
public abstract void setGlyphPosition(int glyphIndex, Point2D newPos);
/**
* Returns the transform of the specified glyph within this
* {@code GlyphVector}. The transform is relative to the
* glyph position. If no special transform has been applied,
* {@code null} can be returned. A null return indicates
* an identity transform.
* @param glyphIndex the index into this {@code GlyphVector}
* @return an {@link AffineTransform} that is the transform of
* the glyph at the specified {@code glyphIndex}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
* @see #setGlyphTransform
*/
public abstract AffineTransform getGlyphTransform(int glyphIndex);
/**
* Sets the transform of the specified glyph within this
* {@code GlyphVector}. The transform is relative to the glyph
* position. A {@code null} argument for {@code newTX}
* indicates that no special transform is applied for the specified
* glyph.
* This method can be used to rotate, mirror, translate and scale the
* glyph. Adding a transform can result in significant performance changes.
* @param glyphIndex the index into this {@code GlyphVector}
* @param newTX the new transform of the glyph at {@code glyphIndex}
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
* @see #getGlyphTransform
*/
public abstract void setGlyphTransform(int glyphIndex, AffineTransform newTX);
/**
* Returns flags describing the global state of the GlyphVector.
* Flags not described below are reserved. The default
* implementation returns 0 (meaning false) for the position adjustments,
* transforms, rtl, and complex flags.
* Subclassers should override this method, and make sure
* it correctly describes the GlyphVector and corresponds
* to the results of related calls.
* @return an int containing the flags describing the state
* @see #FLAG_HAS_POSITION_ADJUSTMENTS
* @see #FLAG_HAS_TRANSFORMS
* @see #FLAG_RUN_RTL
* @see #FLAG_COMPLEX_GLYPHS
* @see #FLAG_MASK
* @since 1.4
*/
public int getLayoutFlags() {
return 0;
}
/**
* A flag used with getLayoutFlags that indicates that this {@code GlyphVector} has
* per-glyph transforms.
* @since 1.4
*/
public static final int FLAG_HAS_TRANSFORMS = 1;
/**
* A flag used with getLayoutFlags that indicates that this {@code GlyphVector} has
* position adjustments. When this is true, the glyph positions don't match the
* accumulated default advances of the glyphs (for example, if kerning has been done).
* @since 1.4
*/
public static final int FLAG_HAS_POSITION_ADJUSTMENTS = 2;
/**
* A flag used with getLayoutFlags that indicates that this {@code GlyphVector} has
* a right-to-left run direction. This refers to the glyph-to-char mapping and does
* not imply that the visual locations of the glyphs are necessarily in this order,
* although generally they will be.
* @since 1.4
*/
public static final int FLAG_RUN_RTL = 4;
/**
* A flag used with getLayoutFlags that indicates that this {@code GlyphVector} has
* a complex glyph-to-char mapping (one that does not map glyphs to chars one-to-one in
* strictly ascending or descending order matching the run direction).
* @since 1.4
*/
public static final int FLAG_COMPLEX_GLYPHS = 8;
/**
* A mask for supported flags from getLayoutFlags. Only bits covered by the mask
* should be tested.
* @since 1.4
*/
public static final int FLAG_MASK =
FLAG_HAS_TRANSFORMS |
FLAG_HAS_POSITION_ADJUSTMENTS |
FLAG_RUN_RTL |
FLAG_COMPLEX_GLYPHS;
/**
* Returns an array of glyph positions for the specified glyphs.
* This method is used for convenience and performance when
* processing glyph positions.
* If no array is passed in, a new array is created.
* Even numbered array entries beginning with position zero are the X
* coordinates of the glyph numbered {@code beginGlyphIndex + position/2}.
* Odd numbered array entries beginning with position one are the Y
* coordinates of the glyph numbered {@code beginGlyphIndex + (position-1)/2}.
* If {@code beginGlyphIndex} equals the number of glyphs in
* this {@code GlyphVector}, this method gets the position after
* the last glyph and this position is used to define the advance of
* the entire {@code GlyphVector}.
* @param beginGlyphIndex the index at which to begin retrieving
* glyph positions
* @param numEntries the number of glyphs to retrieve
* @param positionReturn the array that receives the glyph positions
* and is then returned.
* @return an array of glyph positions specified by
* {@code beginGlyphIndex} and {@code numEntries}.
* @throws IllegalArgumentException if {@code numEntries} is
* less than 0
* @throws IndexOutOfBoundsException if {@code beginGlyphIndex}
* is less than 0
* @throws IndexOutOfBoundsException if the sum of
* {@code beginGlyphIndex} and {@code numEntries}
* is greater than the number of glyphs in this
* {@code GlyphVector} plus one
*/
public abstract float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
float[] positionReturn);
/**
* Returns the logical bounds of the specified glyph within this
* {@code GlyphVector}.
* These logical bounds have a total of four edges, with two edges
* parallel to the baseline under the glyph's transform and the other two
* edges are shared with adjacent glyphs if they are present. This
* method is useful for hit-testing of the specified glyph,
* positioning of a caret at the leading or trailing edge of a glyph,
* and for drawing a highlight region around the specified glyph.
* @param glyphIndex the index into this {@code GlyphVector}
* that corresponds to the glyph from which to retrieve its logical
* bounds
* @return a {@code Shape} that is the logical bounds of the
* glyph at the specified {@code glyphIndex}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
* @see #getGlyphVisualBounds
*/
public abstract Shape getGlyphLogicalBounds(int glyphIndex);
/**
* Returns the visual bounds of the specified glyph within the
* {@code GlyphVector}.
* The bounds returned by this method is positioned around the
* origin of each individual glyph.
* @param glyphIndex the index into this {@code GlyphVector}
* that corresponds to the glyph from which to retrieve its visual
* bounds
* @return a {@code Shape} that is the visual bounds of the
* glyph at the specified {@code glyphIndex}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
* @see #getGlyphLogicalBounds
*/
public abstract Shape getGlyphVisualBounds(int glyphIndex);
/**
* Returns the pixel bounds of the glyph at index when this
* {@code GlyphVector} is rendered in a {@code Graphics} with the
* given {@code FontRenderContext} at the given location. The
* renderFRC need not be the same as the
* {@code FontRenderContext} of this
* {@code GlyphVector}, and can be null. If it is null, the
* {@code FontRenderContext} of this {@code GlyphVector}
* is used. The default implementation returns the visual bounds of the glyph,
* offset to x, y and rounded out to the next integer value, and
* ignores the FRC. Subclassers should override this method.
* @param index the index of the glyph.
* @param renderFRC the {@code FontRenderContext} of the {@code Graphics}.
* @param x the X position at which to render this {@code GlyphVector}.
* @param y the Y position at which to render this {@code GlyphVector}.
* @return a {@code Rectangle} bounding the pixels that would be affected.
* @since 1.4
*/
public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) {
Rectangle2D rect = getGlyphVisualBounds(index).getBounds2D();
int l = (int)Math.floor(rect.getX() + x);
int t = (int)Math.floor(rect.getY() + y);
int r = (int)Math.ceil(rect.getMaxX() + x);
int b = (int)Math.ceil(rect.getMaxY() + y);
return new Rectangle(l, t, r - l, b - t);
}
/**
* Returns the metrics of the glyph at the specified index into
* this {@code GlyphVector}.
* @param glyphIndex the index into this {@code GlyphVector}
* that corresponds to the glyph from which to retrieve its metrics
* @return a {@link GlyphMetrics} object that represents the
* metrics of the glyph at the specified {@code glyphIndex}
* into this {@code GlyphVector}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
*/
public abstract GlyphMetrics getGlyphMetrics(int glyphIndex);
/**
* Returns the justification information for the glyph at
* the specified index into this {@code GlyphVector}.
* @param glyphIndex the index into this {@code GlyphVector}
* that corresponds to the glyph from which to retrieve its
* justification properties
* @return a {@link GlyphJustificationInfo} object that
* represents the justification properties of the glyph at the
* specified {@code glyphIndex} into this
* {@code GlyphVector}.
* @throws IndexOutOfBoundsException if {@code glyphIndex}
* is less than 0 or greater than or equal to the number
* of glyphs in this {@code GlyphVector}
*/
public abstract GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex);
//
// general utility methods
//
/**
* Tests if the specified {@code GlyphVector} exactly
* equals this {@code GlyphVector}.
* @param set the specified {@code GlyphVector} to test
* @return {@code true} if the specified
* {@code GlyphVector} equals this {@code GlyphVector};
* {@code false} otherwise.
*/
public abstract boolean equals(GlyphVector set);
}

View file

@ -0,0 +1,221 @@
/*
* 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
* This class is used with the CHAR_REPLACEMENT attribute.
* <p>
* The {@code GraphicAttribute} class represents a graphic embedded
* in text. Clients subclass this class to implement their own char
* replacement graphics. Clients wishing to embed shapes and images in
* text need not subclass this class. Instead, clients can use the
* {@link ShapeGraphicAttribute} and {@link ImageGraphicAttribute}
* classes.
* <p>
* Subclasses must ensure that their objects are immutable once they
* are constructed. Mutating a {@code GraphicAttribute} that
* is used in a {@link TextLayout} results in undefined behavior from the
* {@code TextLayout}.
*/
public abstract class GraphicAttribute {
private int fAlignment;
/**
* Aligns top of graphic to top of line.
*/
public static final int TOP_ALIGNMENT = -1;
/**
* Aligns bottom of graphic to bottom of line.
*/
public static final int BOTTOM_ALIGNMENT = -2;
/**
* Aligns origin of graphic to roman baseline of line.
*/
public static final int ROMAN_BASELINE = Font.ROMAN_BASELINE;
/**
* Aligns origin of graphic to center baseline of line.
*/
public static final int CENTER_BASELINE = Font.CENTER_BASELINE;
/**
* Aligns origin of graphic to hanging baseline of line.
*/
public static final int HANGING_BASELINE = Font.HANGING_BASELINE;
/**
* Constructs a {@code GraphicAttribute}.
* Subclasses use this to define the alignment of the graphic.
* @param alignment an int representing one of the
* {@code GraphicAttribute} alignment fields
* @throws IllegalArgumentException if alignment is not one of the
* five defined values.
*/
protected GraphicAttribute(int alignment) {
if (alignment < BOTTOM_ALIGNMENT || alignment > HANGING_BASELINE) {
throw new IllegalArgumentException("bad alignment");
}
fAlignment = alignment;
}
/**
* Returns the ascent of this {@code GraphicAttribute}. A
* graphic can be rendered above its ascent.
* @return the ascent of this {@code GraphicAttribute}.
* @see #getBounds()
*/
public abstract float getAscent();
/**
* Returns the descent of this {@code GraphicAttribute}. A
* graphic can be rendered below its descent.
* @return the descent of this {@code GraphicAttribute}.
* @see #getBounds()
*/
public abstract float getDescent();
/**
* Returns the advance of this {@code GraphicAttribute}. The
* {@code GraphicAttribute} object's advance is the distance
* from the point at which the graphic is rendered and the point where
* the next character or graphic is rendered. A graphic can be
* rendered beyond its advance
* @return the advance of this {@code GraphicAttribute}.
* @see #getBounds()
*/
public abstract float getAdvance();
/**
* Returns a {@link Rectangle2D} that encloses all of the
* bits drawn by this {@code GraphicAttribute} relative to the
* rendering position.
* A graphic may be rendered beyond its origin, ascent, descent,
* or advance; but if it is, this method's implementation must
* indicate where the graphic is rendered.
* Default bounds is the rectangle (0, -ascent, advance, ascent+descent).
* @return a {@code Rectangle2D} that encloses all of the bits
* rendered by this {@code GraphicAttribute}.
*/
public Rectangle2D getBounds() {
float ascent = getAscent();
return new Rectangle2D.Float(0, -ascent,
getAdvance(), ascent+getDescent());
}
/**
* Return a {@link java.awt.Shape} that represents the region that
* this {@code GraphicAttribute} renders. This is used when a
* {@link TextLayout} is requested to return the outline of the text.
* The (untransformed) shape must not extend outside the rectangular
* bounds returned by {@code getBounds}.
* The default implementation returns the rectangle returned by
* {@link #getBounds}, transformed by the provided {@link AffineTransform}
* if present.
* @param tx an optional {@link AffineTransform} to apply to the
* outline of this {@code GraphicAttribute}. This can be null.
* @return a {@code Shape} representing this graphic attribute,
* suitable for stroking or filling.
* @since 1.6
*/
public Shape getOutline(AffineTransform tx) {
Shape b = getBounds();
if (tx != null) {
b = tx.createTransformedShape(b);
}
return b;
}
/**
* Renders this {@code GraphicAttribute} at the specified
* location.
* @param graphics the {@link Graphics2D} into which to render the
* graphic
* @param x the user-space X coordinate where the graphic is rendered
* @param y the user-space Y coordinate where the graphic is rendered
*/
public abstract void draw(Graphics2D graphics, float x, float y);
/**
* Returns the alignment of this {@code GraphicAttribute}.
* Alignment can be to a particular baseline, or to the absolute top
* or bottom of a line.
* @return the alignment of this {@code GraphicAttribute}.
*/
public final int getAlignment() {
return fAlignment;
}
/**
* Returns the justification information for this
* {@code GraphicAttribute}. Subclasses
* can override this method to provide different justification
* information.
* @return a {@link GlyphJustificationInfo} object that contains the
* justification information for this {@code GraphicAttribute}.
*/
public GlyphJustificationInfo getJustificationInfo() {
// should we cache this?
float advance = getAdvance();
return new GlyphJustificationInfo(
advance, // weight
false, // growAbsorb
2, // growPriority
advance/3, // growLeftLimit
advance/3, // growRightLimit
false, // shrinkAbsorb
1, // shrinkPriority
0, // shrinkLeftLimit
0); // shrinkRightLimit
}
}

View file

@ -0,0 +1,227 @@
/*
* 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.awt.Image;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
/**
* The {@code ImageGraphicAttribute} class is an implementation of
* {@link GraphicAttribute} which draws images in
* a {@link TextLayout}.
* @see GraphicAttribute
*/
public final class ImageGraphicAttribute extends GraphicAttribute {
private Image fImage;
private float fImageWidth, fImageHeight;
private float fOriginX, fOriginY;
/**
* Constructs an {@code ImageGraphicAttribute} from the specified
* {@link Image}. The origin is at (0,&nbsp;0).
* @param image the {@code Image} rendered by this
* {@code ImageGraphicAttribute}.
* This object keeps a reference to {@code image}.
* @param alignment one of the alignments from this
* {@code ImageGraphicAttribute}
*/
public ImageGraphicAttribute(Image image, int alignment) {
this(image, alignment, 0, 0);
}
/**
* Constructs an {@code ImageGraphicAttribute} from the specified
* {@code Image}. The point
* ({@code originX},&nbsp;{@code originY}) in the
* {@code Image} appears at the origin of the
* {@code ImageGraphicAttribute} within the text.
* @param image the {@code Image} rendered by this
* {@code ImageGraphicAttribute}.
* This object keeps a reference to {@code image}.
* @param alignment one of the alignments from this
* {@code ImageGraphicAttribute}
* @param originX the X coordinate of the point within
* the {@code Image} that appears at the origin of the
* {@code ImageGraphicAttribute} in the text line.
* @param originY the Y coordinate of the point within
* the {@code Image} that appears at the origin of the
* {@code ImageGraphicAttribute} in the text line.
*/
public ImageGraphicAttribute(Image image,
int alignment,
float originX,
float originY) {
super(alignment);
// Can't clone image
// fImage = (Image) image.clone();
fImage = image;
fImageWidth = image.getWidth(null);
fImageHeight = image.getHeight(null);
// ensure origin is in Image?
fOriginX = originX;
fOriginY = originY;
}
/**
* Returns the ascent of this {@code ImageGraphicAttribute}. The
* ascent of an {@code ImageGraphicAttribute} is the distance
* from the top of the image to the origin.
* @return the ascent of this {@code ImageGraphicAttribute}.
*/
public float getAscent() {
return Math.max(0, fOriginY);
}
/**
* Returns the descent of this {@code ImageGraphicAttribute}.
* The descent of an {@code ImageGraphicAttribute} is the
* distance from the origin to the bottom of the image.
* @return the descent of this {@code ImageGraphicAttribute}.
*/
public float getDescent() {
return Math.max(0, fImageHeight-fOriginY);
}
/**
* Returns the advance of this {@code ImageGraphicAttribute}.
* The advance of an {@code ImageGraphicAttribute} is the
* distance from the origin to the right edge of the image.
* @return the advance of this {@code ImageGraphicAttribute}.
*/
public float getAdvance() {
return Math.max(0, fImageWidth-fOriginX);
}
/**
* Returns a {@link Rectangle2D} that encloses all of the
* bits rendered by this {@code ImageGraphicAttribute}, relative
* to the rendering position. A graphic can be rendered beyond its
* origin, ascent, descent, or advance; but if it is, this
* method's implementation must indicate where the graphic is rendered.
* @return a {@code Rectangle2D} that encloses all of the bits
* rendered by this {@code ImageGraphicAttribute}.
*/
public Rectangle2D getBounds() {
return new Rectangle2D.Float(
-fOriginX, -fOriginY, fImageWidth, fImageHeight);
}
/**
* {@inheritDoc}
*/
public void draw(Graphics2D graphics, float x, float y) {
graphics.drawImage(fImage, (int) (x-fOriginX), (int) (y-fOriginY), null);
}
/**
* Returns a hashcode for this {@code ImageGraphicAttribute}.
* @return a hash code value for this object.
*/
public int hashCode() {
return fImage.hashCode();
}
/**
* Compares this {@code ImageGraphicAttribute} to the specified
* {@link Object}.
* @param rhs the {@code Object} to compare for equality
* @return {@code true} if this
* {@code ImageGraphicAttribute} equals {@code rhs};
* {@code false} otherwise.
*/
public boolean equals(Object rhs) {
try {
return equals((ImageGraphicAttribute) rhs);
}
catch(ClassCastException e) {
return false;
}
}
/**
* Compares this {@code ImageGraphicAttribute} to the specified
* {@code ImageGraphicAttribute}.
* @param rhs the {@code ImageGraphicAttribute} to compare for
* equality
* @return {@code true} if this
* {@code ImageGraphicAttribute} equals {@code rhs};
* {@code false} otherwise.
*/
public boolean equals(ImageGraphicAttribute rhs) {
if (rhs == null) {
return false;
}
if (this == rhs) {
return true;
}
if (fOriginX != rhs.fOriginX || fOriginY != rhs.fOriginY) {
return false;
}
if (getAlignment() != rhs.getAlignment()) {
return false;
}
if (!fImage.equals(rhs.fImage)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2014, 2016, 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 java.awt.font;
import jdk.internal.misc.JavaAWTFontAccess;
class JavaAWTFontAccessImpl implements JavaAWTFontAccess {
// java.awt.font.TextAttribute constants
public Object getTextAttributeConstant(String name) {
switch (name) {
case "RUN_DIRECTION":
return TextAttribute.RUN_DIRECTION;
case "NUMERIC_SHAPING":
return TextAttribute.NUMERIC_SHAPING;
case "BIDI_EMBEDDING":
return TextAttribute.BIDI_EMBEDDING;
case "RUN_DIRECTION_LTR":
return TextAttribute.RUN_DIRECTION_LTR;
default:
throw new AssertionError("Constant name is not recognized");
}
}
// java.awt.font.NumericShaper
public void shape(Object shaper, char[] text, int start, int count) {
assert shaper instanceof NumericShaper;
((NumericShaper)shaper).shape(text, start,count);
}
}

View file

@ -0,0 +1,84 @@
/*
* 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.
*/
package java.awt.font;
import java.awt.geom.Point2D;
/**
* LayoutPath provides a mapping between locations relative to the
* baseline and points in user space. Locations consist of an advance
* along the baseline, and an offset perpendicular to the baseline at
* the advance. Positive values along the perpendicular are in the
* direction that is 90 degrees clockwise from the baseline vector.
* Locations are represented as a {@code Point2D}, where x is the advance and
* y is the offset.
*
* @since 1.6
*/
public abstract class LayoutPath {
/**
* Convert a point in user space to a location relative to the
* path. The location is chosen so as to minimize the distance
* from the point to the path (e.g., the magnitude of the offset
* will be smallest). If there is more than one such location,
* the location with the smallest advance is chosen.
* @param point the point to convert. If it is not the same
* object as location, point will remain unmodified by this call.
* @param location a {@code Point2D} to hold the returned location.
* It can be the same object as point.
* @return true if the point is associated with the portion of the
* path preceding the location, false if it is associated with
* the portion following. The default, if the location is not at
* a break or sharp bend in the path, is to return true.
* @throws NullPointerException if point or location is null
* @since 1.6
*/
public abstract boolean pointToPath(Point2D point, Point2D location);
/**
* Convert a location relative to the path to a point in user
* coordinates. The path might bend abruptly or be disjoint at
* the location's advance. If this is the case, the value of
* 'preceding' is used to disambiguate the portion of the path
* whose location and slope is to be used to interpret the offset.
* @param location a {@code Point2D} representing the advance (in x) and
* offset (in y) of a location relative to the path. If location
* is not the same object as point, location will remain
* unmodified by this call.
* @param preceding if true, the portion preceding the advance
* should be used, if false the portion after should be used.
* This has no effect if the path does not break or bend sharply
* at the advance.
* @param point a {@code Point2D} to hold the returned point. It can be
* the same object as location.
* @throws NullPointerException if location or point is null
* @since 1.6
*/
public abstract void pathToPoint(Point2D location, boolean preceding,
Point2D point);
}

View file

@ -0,0 +1,534 @@
/*
* Copyright (c) 1998, 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.text.BreakIterator;
import java.text.CharacterIterator;
import java.text.AttributedCharacterIterator;
import java.awt.font.FontRenderContext;
/**
* The {@code LineBreakMeasurer} class allows styled text to be
* broken into lines (or segments) that fit within a particular visual
* advance. This is useful for clients who wish to display a paragraph of
* text that fits within a specific width, called the <b>wrapping
* width</b>.
* <p>
* {@code LineBreakMeasurer} is constructed with an iterator over
* styled text. The iterator's range should be a single paragraph in the
* text.
* {@code LineBreakMeasurer} maintains a position in the text for the
* start of the next text segment. Initially, this position is the
* start of text. Paragraphs are assigned an overall direction (either
* left-to-right or right-to-left) according to the bidirectional
* formatting rules. All segments obtained from a paragraph have the
* same direction as the paragraph.
* <p>
* Segments of text are obtained by calling the method
* {@code nextLayout}, which returns a {@link TextLayout}
* representing the text that fits within the wrapping width.
* The {@code nextLayout} method moves the current position
* to the end of the layout returned from {@code nextLayout}.
* <p>
* {@code LineBreakMeasurer} implements the most commonly used
* line-breaking policy: Every word that fits within the wrapping
* width is placed on the line. If the first word does not fit, then all
* of the characters that fit within the wrapping width are placed on the
* line. At least one character is placed on each line.
* <p>
* The {@code TextLayout} instances returned by
* {@code LineBreakMeasurer} treat tabs like 0-width spaces. Clients
* who wish to obtain tab-delimited segments for positioning should use
* the overload of {@code nextLayout} which takes a limiting offset
* in the text.
* The limiting offset should be the first character after the tab.
* The {@code TextLayout} objects returned from this method end
* at the limit provided (or before, if the text between the current
* position and the limit won't fit entirely within the wrapping
* width).
* <p>
* Clients who are laying out tab-delimited text need a slightly
* different line-breaking policy after the first segment has been
* placed on a line. Instead of fitting partial words in the
* remaining space, they should place words which don't fit in the
* remaining space entirely on the next line. This change of policy
* can be requested in the overload of {@code nextLayout} which
* takes a {@code boolean} parameter. If this parameter is
* {@code true}, {@code nextLayout} returns
* {@code null} if the first word won't fit in
* the given space. See the tab sample below.
* <p>
* In general, if the text used to construct the
* {@code LineBreakMeasurer} changes, a new
* {@code LineBreakMeasurer} must be constructed to reflect
* the change. (The old {@code LineBreakMeasurer} continues to
* function properly, but it won't be aware of the text change.)
* Nevertheless, if the text change is the insertion or deletion of a
* single character, an existing {@code LineBreakMeasurer} can be
* 'updated' by calling {@code insertChar} or
* {@code deleteChar}. Updating an existing
* {@code LineBreakMeasurer} is much faster than creating a new one.
* Clients who modify text based on user typing should take advantage
* of these methods.
* <p>
* <strong>Examples</strong>:<p>
* Rendering a paragraph in a component
* <blockquote>
* <pre>{@code
* public void paint(Graphics graphics) {
*
* float dx = 0f, dy = 5f;
* Graphics2D g2d = (Graphics2D)graphics;
* FontRenderContext frc = g2d.getFontRenderContext();
*
* AttributedString text = new AttributedString(".....");
* AttributedCharacterIterator paragraph = text.getIterator();
*
* LineBreakMeasurer measurer = new LineBreakMeasurer(paragraph, frc);
* measurer.setPosition(paragraph.getBeginIndex());
* float wrappingWidth = (float)getSize().width;
*
* while (measurer.getPosition() < paragraph.getEndIndex()) {
*
* TextLayout layout = measurer.nextLayout(wrappingWidth);
*
* dy += (layout.getAscent());
* float dx = layout.isLeftToRight() ?
* 0 : (wrappingWidth - layout.getAdvance());
*
* layout.draw(graphics, dx, dy);
* dy += layout.getDescent() + layout.getLeading();
* }
* }
* }</pre>
* </blockquote>
* <p>
* Rendering text with tabs. For simplicity, the overall text
* direction is assumed to be left-to-right
* <blockquote>
* <pre>{@code
* public void paint(Graphics graphics) {
*
* float leftMargin = 10, rightMargin = 310;
* float[] tabStops = { 100, 250 };
*
* // assume styledText is an AttributedCharacterIterator, and the number
* // of tabs in styledText is tabCount
*
* int[] tabLocations = new int[tabCount+1];
*
* int i = 0;
* for (char c = styledText.first(); c != styledText.DONE; c = styledText.next()) {
* if (c == '\t') {
* tabLocations[i++] = styledText.getIndex();
* }
* }
* tabLocations[tabCount] = styledText.getEndIndex() - 1;
*
* // Now tabLocations has an entry for every tab's offset in
* // the text. For convenience, the last entry is tabLocations
* // is the offset of the last character in the text.
*
* LineBreakMeasurer measurer = new LineBreakMeasurer(styledText);
* int currentTab = 0;
* float verticalPos = 20;
*
* while (measurer.getPosition() < styledText.getEndIndex()) {
*
* // Lay out and draw each line. All segments on a line
* // must be computed before any drawing can occur, since
* // we must know the largest ascent on the line.
* // TextLayouts are computed and stored in a Vector;
* // their horizontal positions are stored in a parallel
* // Vector.
*
* // lineContainsText is true after first segment is drawn
* boolean lineContainsText = false;
* boolean lineComplete = false;
* float maxAscent = 0, maxDescent = 0;
* float horizontalPos = leftMargin;
* Vector layouts = new Vector(1);
* Vector penPositions = new Vector(1);
*
* while (!lineComplete) {
* float wrappingWidth = rightMargin - horizontalPos;
* TextLayout layout =
* measurer.nextLayout(wrappingWidth,
* tabLocations[currentTab]+1,
* lineContainsText);
*
* // layout can be null if lineContainsText is true
* if (layout != null) {
* layouts.addElement(layout);
* penPositions.addElement(new Float(horizontalPos));
* horizontalPos += layout.getAdvance();
* maxAscent = Math.max(maxAscent, layout.getAscent());
* maxDescent = Math.max(maxDescent,
* layout.getDescent() + layout.getLeading());
* } else {
* lineComplete = true;
* }
*
* lineContainsText = true;
*
* if (measurer.getPosition() == tabLocations[currentTab]+1) {
* currentTab++;
* }
*
* if (measurer.getPosition() == styledText.getEndIndex())
* lineComplete = true;
* else if (horizontalPos >= tabStops[tabStops.length-1])
* lineComplete = true;
*
* if (!lineComplete) {
* // move to next tab stop
* int j;
* for (j=0; horizontalPos >= tabStops[j]; j++) {}
* horizontalPos = tabStops[j];
* }
* }
*
* verticalPos += maxAscent;
*
* Enumeration layoutEnum = layouts.elements();
* Enumeration positionEnum = penPositions.elements();
*
* // now iterate through layouts and draw them
* while (layoutEnum.hasMoreElements()) {
* TextLayout nextLayout = (TextLayout) layoutEnum.nextElement();
* Float nextPosition = (Float) positionEnum.nextElement();
* nextLayout.draw(graphics, nextPosition.floatValue(), verticalPos);
* }
*
* verticalPos += maxDescent;
* }
* }
* }</pre>
* </blockquote>
* @see TextLayout
*/
public final class LineBreakMeasurer {
private BreakIterator breakIter;
private int start;
private int pos;
private int limit;
private TextMeasurer measurer;
private CharArrayIterator charIter;
/**
* Constructs a {@code LineBreakMeasurer} for the specified text.
*
* @param text the text for which this {@code LineBreakMeasurer}
* produces {@code TextLayout} objects; the text must contain
* at least one character; if the text available through
* {@code iter} changes, further calls to this
* {@code LineBreakMeasurer} instance are undefined (except,
* in some cases, when {@code insertChar} or
* {@code deleteChar} are invoked afterward - see below)
* @param frc contains information about a graphics device which is
* needed to measure the text correctly;
* text measurements can vary slightly depending on the
* device resolution, and attributes such as antialiasing; this
* parameter does not specify a translation between the
* {@code LineBreakMeasurer} and user space
* @see LineBreakMeasurer#insertChar
* @see LineBreakMeasurer#deleteChar
*/
public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
this(text, BreakIterator.getLineInstance(), frc);
}
/**
* Constructs a {@code LineBreakMeasurer} for the specified text.
*
* @param text the text for which this {@code LineBreakMeasurer}
* produces {@code TextLayout} objects; the text must contain
* at least one character; if the text available through
* {@code iter} changes, further calls to this
* {@code LineBreakMeasurer} instance are undefined (except,
* in some cases, when {@code insertChar} or
* {@code deleteChar} are invoked afterward - see below)
* @param breakIter the {@link BreakIterator} which defines line
* breaks
* @param frc contains information about a graphics device which is
* needed to measure the text correctly;
* text measurements can vary slightly depending on the
* device resolution, and attributes such as antialiasing; this
* parameter does not specify a translation between the
* {@code LineBreakMeasurer} and user space
* @throws IllegalArgumentException if the text has less than one character
* @see LineBreakMeasurer#insertChar
* @see LineBreakMeasurer#deleteChar
*/
public LineBreakMeasurer(AttributedCharacterIterator text,
BreakIterator breakIter,
FontRenderContext frc) {
if (text.getEndIndex() - text.getBeginIndex() < 1) {
throw new IllegalArgumentException("Text must contain at least one character.");
}
this.breakIter = breakIter;
this.measurer = new TextMeasurer(text, frc);
this.limit = text.getEndIndex();
this.pos = this.start = text.getBeginIndex();
charIter = new CharArrayIterator(measurer.getChars(), this.start);
this.breakIter.setText(charIter);
}
/**
* Returns the position at the end of the next layout. Does NOT
* update the current position of this {@code LineBreakMeasurer}.
*
* @param wrappingWidth the maximum visible advance permitted for
* the text in the next layout
* @return an offset in the text representing the limit of the
* next {@code TextLayout}.
*/
public int nextOffset(float wrappingWidth) {
return nextOffset(wrappingWidth, limit, false);
}
/**
* Returns the position at the end of the next layout. Does NOT
* update the current position of this {@code LineBreakMeasurer}.
*
* @param wrappingWidth the maximum visible advance permitted for
* the text in the next layout
* @param offsetLimit the first character that can not be included
* in the next layout, even if the text after the limit would fit
* within the wrapping width; {@code offsetLimit} must be
* greater than the current position
* @param requireNextWord if {@code true}, the current position
* that is returned if the entire next word does not fit within
* {@code wrappingWidth}; if {@code false}, the offset
* returned is at least one greater than the current position
* @return an offset in the text representing the limit of the
* next {@code TextLayout}
*/
public int nextOffset(float wrappingWidth, int offsetLimit,
boolean requireNextWord) {
int nextOffset = pos;
if (pos < limit) {
if (offsetLimit <= pos) {
throw new IllegalArgumentException("offsetLimit must be after current position");
}
int charAtMaxAdvance =
measurer.getLineBreakIndex(pos, wrappingWidth);
if (charAtMaxAdvance == limit) {
nextOffset = limit;
}
else if (Character.isWhitespace(measurer.getChars()[charAtMaxAdvance-start])) {
nextOffset = breakIter.following(charAtMaxAdvance);
}
else {
// Break is in a word; back up to previous break.
// NOTE: I think that breakIter.preceding(limit) should be
// equivalent to breakIter.last(), breakIter.previous() but
// the authors of BreakIterator thought otherwise...
// If they were equivalent then the first branch would be
// unnecessary.
int testPos = charAtMaxAdvance + 1;
if (testPos == limit) {
breakIter.last();
nextOffset = breakIter.previous();
}
else {
nextOffset = breakIter.preceding(testPos);
}
if (nextOffset <= pos) {
// first word doesn't fit on line
if (requireNextWord) {
nextOffset = pos;
}
else {
nextOffset = Math.max(pos+1, charAtMaxAdvance);
}
}
}
}
if (nextOffset > offsetLimit) {
nextOffset = offsetLimit;
}
return nextOffset;
}
/**
* Returns the next layout, and updates the current position.
*
* @param wrappingWidth the maximum visible advance permitted for
* the text in the next layout
* @return a {@code TextLayout}, beginning at the current
* position, which represents the next line fitting within
* {@code wrappingWidth}
*/
public TextLayout nextLayout(float wrappingWidth) {
return nextLayout(wrappingWidth, limit, false);
}
/**
* Returns the next layout, and updates the current position.
*
* @param wrappingWidth the maximum visible advance permitted
* for the text in the next layout
* @param offsetLimit the first character that can not be
* included in the next layout, even if the text after the limit
* would fit within the wrapping width; {@code offsetLimit}
* must be greater than the current position
* @param requireNextWord if {@code true}, and if the entire word
* at the current position does not fit within the wrapping width,
* {@code null} is returned. If {@code false}, a valid
* layout is returned that includes at least the character at the
* current position
* @return a {@code TextLayout}, beginning at the current
* position, that represents the next line fitting within
* {@code wrappingWidth}. If the current position is at the end
* of the text used by this {@code LineBreakMeasurer},
* {@code null} is returned
*/
public TextLayout nextLayout(float wrappingWidth, int offsetLimit,
boolean requireNextWord) {
if (pos < limit) {
int layoutLimit = nextOffset(wrappingWidth, offsetLimit, requireNextWord);
if (layoutLimit == pos) {
return null;
}
TextLayout result = measurer.getLayout(pos, layoutLimit);
pos = layoutLimit;
return result;
} else {
return null;
}
}
/**
* Returns the current position of this {@code LineBreakMeasurer}.
*
* @return the current position of this {@code LineBreakMeasurer}
* @see #setPosition
*/
public int getPosition() {
return pos;
}
/**
* Sets the current position of this {@code LineBreakMeasurer}.
*
* @param newPosition the current position of this
* {@code LineBreakMeasurer}; the position should be within the
* text used to construct this {@code LineBreakMeasurer} (or in
* the text most recently passed to {@code insertChar}
* or {@code deleteChar}
* @see #getPosition
*/
public void setPosition(int newPosition) {
if (newPosition < start || newPosition > limit) {
throw new IllegalArgumentException("position is out of range");
}
pos = newPosition;
}
/**
* Updates this {@code LineBreakMeasurer} after a single
* character is inserted into the text, and sets the current
* position to the beginning of the paragraph.
*
* @param newParagraph the text after the insertion
* @param insertPos the position in the text at which the character
* is inserted
* @throws IndexOutOfBoundsException if {@code insertPos} is less
* than the start of {@code newParagraph} or greater than
* or equal to the end of {@code newParagraph}
* @throws NullPointerException if {@code newParagraph} is
* {@code null}
* @see #deleteChar
*/
public void insertChar(AttributedCharacterIterator newParagraph,
int insertPos) {
measurer.insertChar(newParagraph, insertPos);
limit = newParagraph.getEndIndex();
pos = start = newParagraph.getBeginIndex();
charIter.reset(measurer.getChars(), newParagraph.getBeginIndex());
breakIter.setText(charIter);
}
/**
* Updates this {@code LineBreakMeasurer} after a single
* character is deleted from the text, and sets the current
* position to the beginning of the paragraph.
* @param newParagraph the text after the deletion
* @param deletePos the position in the text at which the character
* is deleted
* @throws IndexOutOfBoundsException if {@code deletePos} is
* less than the start of {@code newParagraph} or greater
* than the end of {@code newParagraph}
* @throws NullPointerException if {@code newParagraph} is
* {@code null}
* @see #insertChar
*/
public void deleteChar(AttributedCharacterIterator newParagraph,
int deletePos) {
measurer.deleteChar(newParagraph, deletePos);
limit = newParagraph.getEndIndex();
pos = start = newParagraph.getBeginIndex();
charIter.reset(measurer.getChars(), start);
breakIter.setText(charIter);
}
}

View file

@ -0,0 +1,143 @@
/*
* 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.
*/
package java.awt.font;
/**
* The {@code LineMetrics} class allows access to the
* metrics needed to layout characters along a line
* and to layout of a set of lines. A {@code LineMetrics}
* object encapsulates the measurement information associated
* with a run of text.
* <p>
* Fonts can have different metrics for different ranges of
* characters. The {@code getLineMetrics} methods of
* {@link java.awt.Font Font} take some text as an argument
* and return a {@code LineMetrics} object describing the
* metrics of the initial number of characters in that text, as
* returned by {@link #getNumChars}.
*/
public abstract class LineMetrics {
/**
* Returns the number of characters ({@code char} values) in the text whose
* metrics are encapsulated by this {@code LineMetrics}
* object.
* @return the number of characters ({@code char} values) in the text with which
* this {@code LineMetrics} was created.
*/
public abstract int getNumChars();
/**
* Returns the ascent of the text. The ascent
* is the distance from the baseline
* to the ascender line. The ascent usually represents the
* the height of the capital letters of the text. Some characters
* can extend above the ascender line.
* @return the ascent of the text.
*/
public abstract float getAscent();
/**
* Returns the descent of the text. The descent
* is the distance from the baseline
* to the descender line. The descent usually represents
* the distance to the bottom of lower case letters like
* 'p'. Some characters can extend below the descender
* line.
* @return the descent of the text.
*/
public abstract float getDescent();
/**
* Returns the leading of the text. The
* leading is the recommended
* distance from the bottom of the descender line to the
* top of the next line.
* @return the leading of the text.
*/
public abstract float getLeading();
/**
* Returns the height of the text. The
* height is equal to the sum of the ascent, the
* descent and the leading.
* @return the height of the text.
*/
public abstract float getHeight();
/**
* Returns the baseline index of the text.
* The index is one of
* {@link java.awt.Font#ROMAN_BASELINE ROMAN_BASELINE},
* {@link java.awt.Font#CENTER_BASELINE CENTER_BASELINE},
* {@link java.awt.Font#HANGING_BASELINE HANGING_BASELINE}.
* @return the baseline of the text.
*/
public abstract int getBaselineIndex();
/**
* Returns the baseline offsets of the text,
* relative to the baseline of the text. The
* offsets are indexed by baseline index. For
* example, if the baseline index is
* {@code CENTER_BASELINE} then
* {@code offsets[HANGING_BASELINE]} is usually
* negative, {@code offsets[CENTER_BASELINE]}
* is zero, and {@code offsets[ROMAN_BASELINE]}
* is usually positive.
* @return the baseline offsets of the text.
*/
public abstract float[] getBaselineOffsets();
/**
* Returns the position of the strike-through line
* relative to the baseline.
* @return the position of the strike-through line.
*/
public abstract float getStrikethroughOffset();
/**
* Returns the thickness of the strike-through line.
* @return the thickness of the strike-through line.
*/
public abstract float getStrikethroughThickness();
/**
* Returns the position of the underline relative to
* the baseline.
* @return the position of the underline.
*/
public abstract float getUnderlineOffset();
/**
* Returns the thickness of the underline.
* @return the thickness of the underline.
*/
public abstract float getUnderlineThickness();
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 1997, 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 java.awt.font;
import java.awt.Font;
/**
* The {@code MultipleMaster} interface represents Type 1
* Multiple Master fonts.
* A particular {@link Font} object can implement this interface.
*/
public interface MultipleMaster {
/**
* Returns the number of multiple master design controls.
* Design axes include things like width, weight and optical scaling.
* @return the number of multiple master design controls
*/
public int getNumDesignAxes();
/**
* Returns an array of design limits interleaved in the form [from&rarr;to]
* for each axis. For example,
* design limits for weight could be from 0.1 to 1.0. The values are
* returned in the same order returned by
* {@code getDesignAxisNames}.
* @return an array of design limits for each axis.
*/
public float[] getDesignAxisRanges();
/**
* Returns an array of default design values for each axis. For example,
* the default value for weight could be 1.6. The values are returned
* in the same order returned by {@code getDesignAxisNames}.
* @return an array of default design values for each axis.
*/
public float[] getDesignAxisDefaults();
/**
* Returns the name for each design axis. This also determines the order in
* which the values for each axis are returned.
* @return an array containing the names of each design axis.
*/
public String[] getDesignAxisNames();
/**
* Creates a new instance of a multiple master font based on the design
* axis values contained in the specified array. The size of the array
* must correspond to the value returned from
* {@code getNumDesignAxes} and the values of the array elements
* must fall within limits specified by
* {@code getDesignAxesLimits}. In case of an error,
* {@code null} is returned.
* @param axes an array containing axis values
* @return a {@link Font} object that is an instance of
* {@code MultipleMaster} and is based on the design axis values
* provided by {@code axes}.
*/
public Font deriveMMFont(float[] axes);
/**
* Creates a new instance of a multiple master font based on detailed metric
* information. In case of an error, {@code null} is returned.
* @param glyphWidths an array of floats representing the desired width
* of each glyph in font space
* @param avgStemWidth the average stem width for the overall font in
* font space
* @param typicalCapHeight the height of a typical upper case char
* @param typicalXHeight the height of a typical lower case char
* @param italicAngle the angle at which the italics lean, in degrees
* counterclockwise from vertical
* @return a {@code Font} object that is an instance of
* {@code MultipleMaster} and is based on the specified metric
* information.
*/
public Font deriveMMFont(
float[] glyphWidths,
float avgStemWidth,
float typicalCapHeight,
float typicalXHeight,
float italicAngle);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,432 @@
/*
* Copyright (c) 1997, 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 java.awt.font;
/**
* The {@code OpenType} interface represents OpenType and
* TrueType fonts. This interface makes it possible to obtain
* <i>sfnt</i> tables from the font. A particular
* {@code Font} object can implement this interface.
* <p>
* For more information on TrueType and OpenType fonts, see the
* OpenType specification.
* ( <a href="http://www.microsoft.com/typography/otspec/">http://www.microsoft.com/typography/otspec/</a> ).
*/
public interface OpenType {
/* 51 tag types so far */
/**
* Character to glyph mapping. Table tag "cmap" in the Open
* Type Specification.
*/
public static final int TAG_CMAP = 0x636d6170;
/**
* Font header. Table tag "head" in the Open
* Type Specification.
*/
public static final int TAG_HEAD = 0x68656164;
/**
* Naming table. Table tag "name" in the Open
* Type Specification.
*/
public static final int TAG_NAME = 0x6e616d65;
/**
* Glyph data. Table tag "glyf" in the Open
* Type Specification.
*/
public static final int TAG_GLYF = 0x676c7966;
/**
* Maximum profile. Table tag "maxp" in the Open
* Type Specification.
*/
public static final int TAG_MAXP = 0x6d617870;
/**
* CVT preprogram. Table tag "prep" in the Open
* Type Specification.
*/
public static final int TAG_PREP = 0x70726570;
/**
* Horizontal metrics. Table tag "hmtx" in the Open
* Type Specification.
*/
public static final int TAG_HMTX = 0x686d7478;
/**
* Kerning. Table tag "kern" in the Open
* Type Specification.
*/
public static final int TAG_KERN = 0x6b65726e;
/**
* Horizontal device metrics. Table tag "hdmx" in the Open
* Type Specification.
*/
public static final int TAG_HDMX = 0x68646d78;
/**
* Index to location. Table tag "loca" in the Open
* Type Specification.
*/
public static final int TAG_LOCA = 0x6c6f6361;
/**
* PostScript Information. Table tag "post" in the Open
* Type Specification.
*/
public static final int TAG_POST = 0x706f7374;
/**
* OS/2 and Windows specific metrics. Table tag "OS/2"
* in the Open Type Specification.
*/
public static final int TAG_OS2 = 0x4f532f32;
/**
* Control value table. Table tag "cvt "
* in the Open Type Specification.
*/
public static final int TAG_CVT = 0x63767420;
/**
* Grid-fitting and scan conversion procedure. Table tag
* "gasp" in the Open Type Specification.
*/
public static final int TAG_GASP = 0x67617370;
/**
* Vertical device metrics. Table tag "VDMX" in the Open
* Type Specification.
*/
public static final int TAG_VDMX = 0x56444d58;
/**
* Vertical metrics. Table tag "vmtx" in the Open
* Type Specification.
*/
public static final int TAG_VMTX = 0x766d7478;
/**
* Vertical metrics header. Table tag "vhea" in the Open
* Type Specification.
*/
public static final int TAG_VHEA = 0x76686561;
/**
* Horizontal metrics header. Table tag "hhea" in the Open
* Type Specification.
*/
public static final int TAG_HHEA = 0x68686561;
/**
* Adobe Type 1 font data. Table tag "typ1" in the Open
* Type Specification.
*/
public static final int TAG_TYP1 = 0x74797031;
/**
* Baseline table. Table tag "bsln" in the Open
* Type Specification.
*/
public static final int TAG_BSLN = 0x62736c6e;
/**
* Glyph substitution. Table tag "GSUB" in the Open
* Type Specification.
*/
public static final int TAG_GSUB = 0x47535542;
/**
* Digital signature. Table tag "DSIG" in the Open
* Type Specification.
*/
public static final int TAG_DSIG = 0x44534947;
/**
* Font program. Table tag "fpgm" in the Open
* Type Specification.
*/
public static final int TAG_FPGM = 0x6670676d;
/**
* Font variation. Table tag "fvar" in the Open
* Type Specification.
*/
public static final int TAG_FVAR = 0x66766172;
/**
* Glyph variation. Table tag "gvar" in the Open
* Type Specification.
*/
public static final int TAG_GVAR = 0x67766172;
/**
* Compact font format (Type1 font). Table tag
* "CFF " in the Open Type Specification.
*/
public static final int TAG_CFF = 0x43464620;
/**
* Multiple master supplementary data. Table tag
* "MMSD" in the Open Type Specification.
*/
public static final int TAG_MMSD = 0x4d4d5344;
/**
* Multiple master font metrics. Table tag
* "MMFX" in the Open Type Specification.
*/
public static final int TAG_MMFX = 0x4d4d4658;
/**
* Baseline data. Table tag "BASE" in the Open
* Type Specification.
*/
public static final int TAG_BASE = 0x42415345;
/**
* Glyph definition. Table tag "GDEF" in the Open
* Type Specification.
*/
public static final int TAG_GDEF = 0x47444546;
/**
* Glyph positioning. Table tag "GPOS" in the Open
* Type Specification.
*/
public static final int TAG_GPOS = 0x47504f53;
/**
* Justification. Table tag "JSTF" in the Open
* Type Specification.
*/
public static final int TAG_JSTF = 0x4a535446;
/**
* Embedded bitmap data. Table tag "EBDT" in the Open
* Type Specification.
*/
public static final int TAG_EBDT = 0x45424454;
/**
* Embedded bitmap location. Table tag "EBLC" in the Open
* Type Specification.
*/
public static final int TAG_EBLC = 0x45424c43;
/**
* Embedded bitmap scaling. Table tag "EBSC" in the Open
* Type Specification.
*/
public static final int TAG_EBSC = 0x45425343;
/**
* Linear threshold. Table tag "LTSH" in the Open
* Type Specification.
*/
public static final int TAG_LTSH = 0x4c545348;
/**
* PCL 5 data. Table tag "PCLT" in the Open
* Type Specification.
*/
public static final int TAG_PCLT = 0x50434c54;
/**
* Accent attachment. Table tag "acnt" in the Open
* Type Specification.
*/
public static final int TAG_ACNT = 0x61636e74;
/**
* Axis variation. Table tag "avar" in the Open
* Type Specification.
*/
public static final int TAG_AVAR = 0x61766172;
/**
* Bitmap data. Table tag "bdat" in the Open
* Type Specification.
*/
public static final int TAG_BDAT = 0x62646174;
/**
* Bitmap location. Table tag "bloc" in the Open
* Type Specification.
*/
public static final int TAG_BLOC = 0x626c6f63;
/**
* CVT variation. Table tag "cvar" in the Open
* Type Specification.
*/
public static final int TAG_CVAR = 0x63766172;
/**
* Feature name. Table tag "feat" in the Open
* Type Specification.
*/
public static final int TAG_FEAT = 0x66656174;
/**
* Font descriptors. Table tag "fdsc" in the Open
* Type Specification.
*/
public static final int TAG_FDSC = 0x66647363;
/**
* Font metrics. Table tag "fmtx" in the Open
* Type Specification.
*/
public static final int TAG_FMTX = 0x666d7478;
/**
* Justification. Table tag "just" in the Open
* Type Specification.
*/
public static final int TAG_JUST = 0x6a757374;
/**
* Ligature caret. Table tag "lcar" in the Open
* Type Specification.
*/
public static final int TAG_LCAR = 0x6c636172;
/**
* Glyph metamorphosis. Table tag "mort" in the Open
* Type Specification.
*/
public static final int TAG_MORT = 0x6d6f7274;
/**
* Optical bounds. Table tag "opbd" in the Open
* Type Specification.
*/
public static final int TAG_OPBD = 0x6F706264;
/**
* Glyph properties. Table tag "prop" in the Open
* Type Specification.
*/
public static final int TAG_PROP = 0x70726f70;
/**
* Tracking. Table tag "trak" in the Open
* Type Specification.
*/
public static final int TAG_TRAK = 0x7472616b;
/**
* Returns the version of the {@code OpenType} font.
* 1.0 is represented as 0x00010000.
* @return the version of the {@code OpenType} font.
*/
public int getVersion();
/**
* Returns the table as an array of bytes for a specified tag.
* Tags for sfnt tables include items like <i>cmap</i>,
* <i>name</i> and <i>head</i>. The {@code byte} array
* returned is a copy of the font data in memory.
* @param sfntTag a four-character code as a 32-bit integer
* @return a {@code byte} array that is the table that
* contains the font data corresponding to the specified
* tag.
*/
public byte[] getFontTable(int sfntTag);
/**
* Returns the table as an array of bytes for a specified tag.
* Tags for sfnt tables include items like <i>cmap</i>,
* <i>name</i> and <i>head</i>. The byte array returned is a
* copy of the font data in memory.
* @param strSfntTag a four-character code as a
* {@code String}
* @return a {@code byte} array that is the table that
* contains the font data corresponding to the specified
* tag.
*/
public byte[] getFontTable(String strSfntTag);
/**
* Returns a subset of the table as an array of bytes
* for a specified tag. Tags for sfnt tables include
* items like <i>cmap</i>, <i>name</i> and <i>head</i>.
* The byte array returned is a copy of the font data in
* memory.
* @param sfntTag a four-character code as a 32-bit integer
* @param offset index of first byte to return from table
* @param count number of bytes to return from table
* @return a subset of the table corresponding to
* {@code sfntTag} and containing the bytes
* starting at {@code offset} byte and including
* {@code count} bytes.
*/
public byte[] getFontTable(int sfntTag, int offset, int count);
/**
* Returns a subset of the table as an array of bytes
* for a specified tag. Tags for sfnt tables include items
* like <i>cmap</i>, <i>name</i> and <i>head</i>. The
* {@code byte} array returned is a copy of the font
* data in memory.
* @param strSfntTag a four-character code as a
* {@code String}
* @param offset index of first byte to return from table
* @param count number of bytes to return from table
* @return a subset of the table corresponding to
* {@code strSfntTag} and containing the bytes
* starting at {@code offset} byte and including
* {@code count} bytes.
*/
public byte[] getFontTable(String strSfntTag, int offset, int count);
/**
* Returns the size of the table for a specified tag. Tags for sfnt
* tables include items like <i>cmap</i>, <i>name</i> and <i>head</i>.
* @param sfntTag a four-character code as a 32-bit integer
* @return the size of the table corresponding to the specified
* tag.
*/
public int getFontTableSize(int sfntTag);
/**
* Returns the size of the table for a specified tag. Tags for sfnt
* tables include items like <i>cmap</i>, <i>name</i> and <i>head</i>.
* @param strSfntTag a four-character code as a
* {@code String}
* @return the size of the table corresponding to the specified tag.
*/
public int getFontTableSize(String strSfntTag);
}

View file

@ -0,0 +1,256 @@
/*
* 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.awt.Shape;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
* The {@code ShapeGraphicAttribute} class is an implementation of
* {@link GraphicAttribute} that draws shapes in a {@link TextLayout}.
* @see GraphicAttribute
*/
public final class ShapeGraphicAttribute extends GraphicAttribute {
private Shape fShape;
private boolean fStroke;
/**
* A key indicating the shape should be stroked with a 1-pixel wide stroke.
*/
public static final boolean STROKE = true;
/**
* A key indicating the shape should be filled.
*/
public static final boolean FILL = false;
// cache shape bounds, since GeneralPath doesn't
private Rectangle2D fShapeBounds;
/**
* Constructs a {@code ShapeGraphicAttribute} for the specified
* {@link Shape}.
* @param shape the {@code Shape} to render. The
* {@code Shape} is rendered with its origin at the origin of
* this {@code ShapeGraphicAttribute} in the
* host {@code TextLayout}. This object maintains a reference to
* {@code shape}.
* @param alignment one of the alignments from this
* {@code ShapeGraphicAttribute}.
* @param stroke {@code true} if the {@code Shape} should be
* stroked; {@code false} if the {@code Shape} should be
* filled.
*/
public ShapeGraphicAttribute(Shape shape,
int alignment,
boolean stroke) {
super(alignment);
fShape = shape;
fStroke = stroke;
fShapeBounds = fShape.getBounds2D();
}
/**
* Returns the ascent of this {@code ShapeGraphicAttribute}. The
* ascent of a {@code ShapeGraphicAttribute} is the positive
* distance from the origin of its {@code Shape} to the top of
* bounds of its {@code Shape}.
* @return the ascent of this {@code ShapeGraphicAttribute}.
*/
public float getAscent() {
return (float) Math.max(0, -fShapeBounds.getMinY());
}
/**
* Returns the descent of this {@code ShapeGraphicAttribute}.
* The descent of a {@code ShapeGraphicAttribute} is the distance
* from the origin of its {@code Shape} to the bottom of the
* bounds of its {@code Shape}.
* @return the descent of this {@code ShapeGraphicAttribute}.
*/
public float getDescent() {
return (float) Math.max(0, fShapeBounds.getMaxY());
}
/**
* Returns the advance of this {@code ShapeGraphicAttribute}.
* The advance of a {@code ShapeGraphicAttribute} is the distance
* from the origin of its {@code Shape} to the right side of the
* bounds of its {@code Shape}.
* @return the advance of this {@code ShapeGraphicAttribute}.
*/
public float getAdvance() {
return (float) Math.max(0, fShapeBounds.getMaxX());
}
/**
* {@inheritDoc}
*/
public void draw(Graphics2D graphics, float x, float y) {
// translating graphics to draw Shape !!!
graphics.translate((int)x, (int)y);
try {
if (fStroke == STROKE) {
// REMIND: set stroke to correct size
graphics.draw(fShape);
}
else {
graphics.fill(fShape);
}
}
finally {
graphics.translate(-(int)x, -(int)y);
}
}
/**
* Returns a {@link Rectangle2D} that encloses all of the
* bits drawn by this {@code ShapeGraphicAttribute} relative to
* the rendering position. A graphic can be rendered beyond its
* origin, ascent, descent, or advance; but if it does, this method's
* implementation should indicate where the graphic is rendered.
* @return a {@code Rectangle2D} that encloses all of the bits
* rendered by this {@code ShapeGraphicAttribute}.
*/
public Rectangle2D getBounds() {
Rectangle2D.Float bounds = new Rectangle2D.Float();
bounds.setRect(fShapeBounds);
if (fStroke == STROKE) {
++bounds.width;
++bounds.height;
}
return bounds;
}
/**
* Return a {@link java.awt.Shape} that represents the region that
* this {@code ShapeGraphicAttribute} renders. This is used when a
* {@link TextLayout} is requested to return the outline of the text.
* The (untransformed) shape must not extend outside the rectangular
* bounds returned by {@code getBounds}.
* @param tx an optional {@link AffineTransform} to apply to the
* this {@code ShapeGraphicAttribute}. This can be null.
* @return the {@code Shape} representing this graphic attribute,
* suitable for stroking or filling.
* @since 1.6
*/
public Shape getOutline(AffineTransform tx) {
return tx == null ? fShape : tx.createTransformedShape(fShape);
}
/**
* Returns a hashcode for this {@code ShapeGraphicAttribute}.
* @return a hash code value for this
* {@code ShapeGraphicAttribute}.
*/
public int hashCode() {
return fShape.hashCode();
}
/**
* Compares this {@code ShapeGraphicAttribute} to the specified
* {@code Object}.
* @param rhs the {@code Object} to compare for equality
* @return {@code true} if this
* {@code ShapeGraphicAttribute} equals {@code rhs};
* {@code false} otherwise.
*/
public boolean equals(Object rhs) {
try {
return equals((ShapeGraphicAttribute) rhs);
}
catch(ClassCastException e) {
return false;
}
}
/**
* Compares this {@code ShapeGraphicAttribute} to the specified
* {@code ShapeGraphicAttribute}.
* @param rhs the {@code ShapeGraphicAttribute} to compare for
* equality
* @return {@code true} if this
* {@code ShapeGraphicAttribute} equals {@code rhs};
* {@code false} otherwise.
*/
public boolean equals(ShapeGraphicAttribute rhs) {
if (rhs == null) {
return false;
}
if (this == rhs) {
return true;
}
if (fStroke != rhs.fStroke) {
return false;
}
if (getAlignment() != rhs.getAlignment()) {
return false;
}
if (!fShape.equals(rhs.fShape)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,499 @@
/*
* Copyright (c) 1999, 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, All rights reserved.
*/
package java.awt.font;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.im.InputMethodHighlight;
import java.text.Annotation;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.util.Vector;
import java.util.HashMap;
import java.util.Map;
import sun.font.CodePointIterator;
import sun.font.Decoration;
import sun.font.FontResolver;
/**
* This class stores Font, GraphicAttribute, and Decoration intervals
* on a paragraph of styled text.
* <p>
* Currently, this class is optimized for a small number of intervals
* (preferably 1).
*/
final class StyledParagraph {
// the length of the paragraph
private int length;
// If there is a single Decoration for the whole paragraph, it
// is stored here. Otherwise this field is ignored.
private Decoration decoration;
// If there is a single Font or GraphicAttribute for the whole
// paragraph, it is stored here. Otherwise this field is ignored.
private Object font;
// If there are multiple Decorations in the paragraph, they are
// stored in this Vector, in order. Otherwise this vector and
// the decorationStarts array are null.
private Vector<Decoration> decorations;
// If there are multiple Decorations in the paragraph,
// decorationStarts[i] contains the index where decoration i
// starts. For convenience, there is an extra entry at the
// end of this array with the length of the paragraph.
int[] decorationStarts;
// If there are multiple Fonts/GraphicAttributes in the paragraph,
// they are
// stored in this Vector, in order. Otherwise this vector and
// the fontStarts array are null.
private Vector<Object> fonts;
// If there are multiple Fonts/GraphicAttributes in the paragraph,
// fontStarts[i] contains the index where decoration i
// starts. For convenience, there is an extra entry at the
// end of this array with the length of the paragraph.
int[] fontStarts;
private static int INITIAL_SIZE = 8;
/**
* Create a new StyledParagraph over the given styled text.
* @param aci an iterator over the text
* @param chars the characters extracted from aci
*/
public StyledParagraph(AttributedCharacterIterator aci,
char[] chars) {
int start = aci.getBeginIndex();
int end = aci.getEndIndex();
length = end - start;
int index = start;
aci.first();
do {
final int nextRunStart = aci.getRunLimit();
final int localIndex = index-start;
Map<? extends Attribute, ?> attributes = aci.getAttributes();
attributes = addInputMethodAttrs(attributes);
Decoration d = Decoration.getDecoration(attributes);
addDecoration(d, localIndex);
Object f = getGraphicOrFont(attributes);
if (f == null) {
addFonts(chars, attributes, localIndex, nextRunStart-start);
}
else {
addFont(f, localIndex);
}
aci.setIndex(nextRunStart);
index = nextRunStart;
} while (index < end);
// Add extra entries to starts arrays with the length
// of the paragraph. 'this' is used as a dummy value
// in the Vector.
if (decorations != null) {
decorationStarts = addToVector(this, length, decorations, decorationStarts);
}
if (fonts != null) {
fontStarts = addToVector(this, length, fonts, fontStarts);
}
}
/**
* Adjust indices in starts to reflect an insertion after pos.
* Any index in starts greater than pos will be increased by 1.
*/
private static void insertInto(int pos, int[] starts, int numStarts) {
while (starts[--numStarts] > pos) {
starts[numStarts] += 1;
}
}
/**
* Return a StyledParagraph reflecting the insertion of a single character
* into the text. This method will attempt to reuse the given paragraph,
* but may create a new paragraph.
* @param aci an iterator over the text. The text should be the same as the
* text used to create (or most recently update) oldParagraph, with
* the exception of inserting a single character at insertPos.
* @param chars the characters in aci
* @param insertPos the index of the new character in aci
* @param oldParagraph a StyledParagraph for the text in aci before the
* insertion
*/
public static StyledParagraph insertChar(AttributedCharacterIterator aci,
char[] chars,
int insertPos,
StyledParagraph oldParagraph) {
// If the styles at insertPos match those at insertPos-1,
// oldParagraph will be reused. Otherwise we create a new
// paragraph.
char ch = aci.setIndex(insertPos);
int relativePos = Math.max(insertPos - aci.getBeginIndex() - 1, 0);
Map<? extends Attribute, ?> attributes =
addInputMethodAttrs(aci.getAttributes());
Decoration d = Decoration.getDecoration(attributes);
if (!oldParagraph.getDecorationAt(relativePos).equals(d)) {
return new StyledParagraph(aci, chars);
}
Object f = getGraphicOrFont(attributes);
if (f == null) {
FontResolver resolver = FontResolver.getInstance();
int fontIndex = resolver.getFontIndex(ch);
f = resolver.getFont(fontIndex, attributes);
}
if (!oldParagraph.getFontOrGraphicAt(relativePos).equals(f)) {
return new StyledParagraph(aci, chars);
}
// insert into existing paragraph
oldParagraph.length += 1;
if (oldParagraph.decorations != null) {
insertInto(relativePos,
oldParagraph.decorationStarts,
oldParagraph.decorations.size());
}
if (oldParagraph.fonts != null) {
insertInto(relativePos,
oldParagraph.fontStarts,
oldParagraph.fonts.size());
}
return oldParagraph;
}
/**
* Adjust indices in starts to reflect a deletion after deleteAt.
* Any index in starts greater than deleteAt will be increased by 1.
* It is the caller's responsibility to make sure that no 0-length
* runs result.
*/
private static void deleteFrom(int deleteAt, int[] starts, int numStarts) {
while (starts[--numStarts] > deleteAt) {
starts[numStarts] -= 1;
}
}
/**
* Return a StyledParagraph reflecting the insertion of a single character
* into the text. This method will attempt to reuse the given paragraph,
* but may create a new paragraph.
* @param aci an iterator over the text. The text should be the same as the
* text used to create (or most recently update) oldParagraph, with
* the exception of deleting a single character at deletePos.
* @param chars the characters in aci
* @param deletePos the index where a character was removed
* @param oldParagraph a StyledParagraph for the text in aci before the
* insertion
*/
public static StyledParagraph deleteChar(AttributedCharacterIterator aci,
char[] chars,
int deletePos,
StyledParagraph oldParagraph) {
// We will reuse oldParagraph unless there was a length-1 run
// at deletePos. We could do more work and check the individual
// Font and Decoration runs, but we don't right now...
deletePos -= aci.getBeginIndex();
if (oldParagraph.decorations == null && oldParagraph.fonts == null) {
oldParagraph.length -= 1;
return oldParagraph;
}
if (oldParagraph.getRunLimit(deletePos) == deletePos+1) {
if (deletePos == 0 || oldParagraph.getRunLimit(deletePos-1) == deletePos) {
return new StyledParagraph(aci, chars);
}
}
oldParagraph.length -= 1;
if (oldParagraph.decorations != null) {
deleteFrom(deletePos,
oldParagraph.decorationStarts,
oldParagraph.decorations.size());
}
if (oldParagraph.fonts != null) {
deleteFrom(deletePos,
oldParagraph.fontStarts,
oldParagraph.fonts.size());
}
return oldParagraph;
}
/**
* Return the index at which there is a different Font, GraphicAttribute, or
* Decoration than at the given index.
* @param index a valid index in the paragraph
* @return the first index where there is a change in attributes from
* those at index
*/
public int getRunLimit(int index) {
if (index < 0 || index >= length) {
throw new IllegalArgumentException("index out of range");
}
int limit1 = length;
if (decorations != null) {
int run = findRunContaining(index, decorationStarts);
limit1 = decorationStarts[run+1];
}
int limit2 = length;
if (fonts != null) {
int run = findRunContaining(index, fontStarts);
limit2 = fontStarts[run+1];
}
return Math.min(limit1, limit2);
}
/**
* Return the Decoration in effect at the given index.
* @param index a valid index in the paragraph
* @return the Decoration at index.
*/
public Decoration getDecorationAt(int index) {
if (index < 0 || index >= length) {
throw new IllegalArgumentException("index out of range");
}
if (decorations == null) {
return decoration;
}
int run = findRunContaining(index, decorationStarts);
return decorations.elementAt(run);
}
/**
* Return the Font or GraphicAttribute in effect at the given index.
* The client must test the type of the return value to determine what
* it is.
* @param index a valid index in the paragraph
* @return the Font or GraphicAttribute at index.
*/
public Object getFontOrGraphicAt(int index) {
if (index < 0 || index >= length) {
throw new IllegalArgumentException("index out of range");
}
if (fonts == null) {
return font;
}
int run = findRunContaining(index, fontStarts);
return fonts.elementAt(run);
}
/**
* Return i such that starts[i] &lt;= index &lt; starts[i+1]. starts
* must be in increasing order, with at least one element greater
* than index.
*/
private static int findRunContaining(int index, int[] starts) {
for (int i=1; true; i++) {
if (starts[i] > index) {
return i-1;
}
}
}
/**
* Append the given Object to the given Vector. Add
* the given index to the given starts array. If the
* starts array does not have room for the index, a
* new array is created and returned.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private static int[] addToVector(Object obj,
int index,
Vector v,
int[] starts) {
if (!v.lastElement().equals(obj)) {
v.addElement(obj);
int count = v.size();
if (starts.length == count) {
int[] temp = new int[starts.length*2];
System.arraycopy(starts, 0, temp, 0, starts.length);
starts = temp;
}
starts[count-1] = index;
}
return starts;
}
/**
* Add a new Decoration run with the given Decoration at the
* given index.
*/
private void addDecoration(Decoration d, int index) {
if (decorations != null) {
decorationStarts = addToVector(d,
index,
decorations,
decorationStarts);
}
else if (decoration == null) {
decoration = d;
}
else {
if (!decoration.equals(d)) {
decorations = new Vector<Decoration>(INITIAL_SIZE);
decorations.addElement(decoration);
decorations.addElement(d);
decorationStarts = new int[INITIAL_SIZE];
decorationStarts[0] = 0;
decorationStarts[1] = index;
}
}
}
/**
* Add a new Font/GraphicAttribute run with the given object at the
* given index.
*/
private void addFont(Object f, int index) {
if (fonts != null) {
fontStarts = addToVector(f, index, fonts, fontStarts);
}
else if (font == null) {
font = f;
}
else {
if (!font.equals(f)) {
fonts = new Vector<Object>(INITIAL_SIZE);
fonts.addElement(font);
fonts.addElement(f);
fontStarts = new int[INITIAL_SIZE];
fontStarts[0] = 0;
fontStarts[1] = index;
}
}
}
/**
* Resolve the given chars into Fonts using FontResolver, then add
* font runs for each.
*/
private void addFonts(char[] chars, Map<? extends Attribute, ?> attributes,
int start, int limit) {
FontResolver resolver = FontResolver.getInstance();
CodePointIterator iter = CodePointIterator.create(chars, start, limit);
for (int runStart = iter.charIndex(); runStart < limit; runStart = iter.charIndex()) {
int fontIndex = resolver.nextFontRunIndex(iter);
addFont(resolver.getFont(fontIndex, attributes), runStart);
}
}
/**
* Return a Map with entries from oldStyles, as well as input
* method entries, if any.
*/
static Map<? extends Attribute, ?>
addInputMethodAttrs(Map<? extends Attribute, ?> oldStyles) {
Object value = oldStyles.get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
try {
if (value != null) {
if (value instanceof Annotation) {
value = ((Annotation)value).getValue();
}
InputMethodHighlight hl;
hl = (InputMethodHighlight) value;
Map<? extends Attribute, ?> imStyles = null;
try {
imStyles = hl.getStyle();
} catch (NoSuchMethodError e) {
}
if (imStyles == null) {
Toolkit tk = Toolkit.getDefaultToolkit();
imStyles = tk.mapInputMethodHighlight(hl);
}
if (imStyles != null) {
HashMap<Attribute, Object>
newStyles = new HashMap<>(5, (float)0.9);
newStyles.putAll(oldStyles);
newStyles.putAll(imStyles);
return newStyles;
}
}
}
catch(ClassCastException e) {
}
return oldStyles;
}
/**
* Extract a GraphicAttribute or Font from the given attributes.
* If attributes does not contain a GraphicAttribute, Font, or
* Font family entry this method returns null.
*/
private static Object getGraphicOrFont(
Map<? extends Attribute, ?> attributes) {
Object value = attributes.get(TextAttribute.CHAR_REPLACEMENT);
if (value != null) {
return value;
}
value = attributes.get(TextAttribute.FONT);
if (value != null) {
return value;
}
if (attributes.get(TextAttribute.FAMILY) != null) {
return Font.getFont(attributes);
}
else {
return null;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,247 @@
/*
* Copyright (c) 1997, 1998, 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.lang.String;
/**
* The {@code TextHitInfo} class represents a character position in a
* text model, and a <b>bias</b>, or "side," of the character. Biases are
* either <EM>leading</EM> (the left edge, for a left-to-right character)
* or <EM>trailing</EM> (the right edge, for a left-to-right character).
* Instances of {@code TextHitInfo} are used to specify caret and
* insertion positions within text.
* <p>
* For example, consider the text "abc". TextHitInfo.trailing(1)
* corresponds to the right side of the 'b' in the text.
* <p>
* {@code TextHitInfo} is used primarily by {@link TextLayout} and
* clients of {@code TextLayout}. Clients of {@code TextLayout}
* query {@code TextHitInfo} instances for an insertion offset, where
* new text is inserted into the text model. The insertion offset is equal
* to the character position in the {@code TextHitInfo} if the bias
* is leading, and one character after if the bias is trailing. The
* insertion offset for TextHitInfo.trailing(1) is 2.
* <p>
* Sometimes it is convenient to construct a {@code TextHitInfo} with
* the same insertion offset as an existing one, but on the opposite
* character. The {@code getOtherHit} method constructs a new
* {@code TextHitInfo} with the same insertion offset as an existing
* one, with a hit on the character on the other side of the insertion offset.
* Calling {@code getOtherHit} on trailing(1) would return leading(2).
* In general, {@code getOtherHit} for trailing(n) returns
* leading(n+1) and {@code getOtherHit} for leading(n)
* returns trailing(n-1).
* <p>
* <strong>Example</strong>:<p>
* Converting a graphical point to an insertion point within a text
* model
* <blockquote><pre>
* TextLayout layout = ...;
* Point2D.Float hitPoint = ...;
* TextHitInfo hitInfo = layout.hitTestChar(hitPoint.x, hitPoint.y);
* int insPoint = hitInfo.getInsertionIndex();
* // insPoint is relative to layout; may need to adjust for use
* // in a text model
* </pre></blockquote>
*
* @see TextLayout
*/
public final class TextHitInfo {
private int charIndex;
private boolean isLeadingEdge;
/**
* Constructs a new {@code TextHitInfo}.
* @param charIndex the index of the character hit
* @param isLeadingEdge {@code true} if the leading edge of the
* character was hit
*/
private TextHitInfo(int charIndex, boolean isLeadingEdge) {
this.charIndex = charIndex;
this.isLeadingEdge = isLeadingEdge;
}
/**
* Returns the index of the character hit.
* @return the index of the character hit.
*/
public int getCharIndex() {
return charIndex;
}
/**
* Returns {@code true} if the leading edge of the character was
* hit.
* @return {@code true} if the leading edge of the character was
* hit; {@code false} otherwise.
*/
public boolean isLeadingEdge() {
return isLeadingEdge;
}
/**
* Returns the insertion index. This is the character index if
* the leading edge of the character was hit, and one greater
* than the character index if the trailing edge was hit.
* @return the insertion index.
*/
public int getInsertionIndex() {
return isLeadingEdge ? charIndex : charIndex + 1;
}
/**
* Returns the hash code.
* @return the hash code of this {@code TextHitInfo}, which is
* also the {@code charIndex} of this {@code TextHitInfo}.
*/
public int hashCode() {
return charIndex;
}
/**
* Returns {@code true} if the specified {@code Object} is a
* {@code TextHitInfo} and equals this {@code TextHitInfo}.
* @param obj the {@code Object} to test for equality
* @return {@code true} if the specified {@code Object}
* equals this {@code TextHitInfo}; {@code false} otherwise.
*/
public boolean equals(Object obj) {
return (obj instanceof TextHitInfo) && equals((TextHitInfo)obj);
}
/**
* Returns {@code true} if the specified {@code TextHitInfo}
* has the same {@code charIndex} and {@code isLeadingEdge}
* as this {@code TextHitInfo}. This is not the same as having
* the same insertion offset.
* @param hitInfo a specified {@code TextHitInfo}
* @return {@code true} if the specified {@code TextHitInfo}
* has the same {@code charIndex} and {@code isLeadingEdge}
* as this {@code TextHitInfo}.
*/
public boolean equals(TextHitInfo hitInfo) {
return hitInfo != null && charIndex == hitInfo.charIndex &&
isLeadingEdge == hitInfo.isLeadingEdge;
}
/**
* Returns a {@code String} representing the hit for debugging
* use only.
* @return a {@code String} representing this
* {@code TextHitInfo}.
*/
public String toString() {
return "TextHitInfo[" + charIndex + (isLeadingEdge ? "L" : "T")+"]";
}
/**
* Creates a {@code TextHitInfo} on the leading edge of the
* character at the specified {@code charIndex}.
* @param charIndex the index of the character hit
* @return a {@code TextHitInfo} on the leading edge of the
* character at the specified {@code charIndex}.
*/
public static TextHitInfo leading(int charIndex) {
return new TextHitInfo(charIndex, true);
}
/**
* Creates a hit on the trailing edge of the character at
* the specified {@code charIndex}.
* @param charIndex the index of the character hit
* @return a {@code TextHitInfo} on the trailing edge of the
* character at the specified {@code charIndex}.
*/
public static TextHitInfo trailing(int charIndex) {
return new TextHitInfo(charIndex, false);
}
/**
* Creates a {@code TextHitInfo} at the specified offset,
* associated with the character before the offset.
* @param offset an offset associated with the character before
* the offset
* @return a {@code TextHitInfo} at the specified offset.
*/
public static TextHitInfo beforeOffset(int offset) {
return new TextHitInfo(offset-1, false);
}
/**
* Creates a {@code TextHitInfo} at the specified offset,
* associated with the character after the offset.
* @param offset an offset associated with the character after
* the offset
* @return a {@code TextHitInfo} at the specified offset.
*/
public static TextHitInfo afterOffset(int offset) {
return new TextHitInfo(offset, true);
}
/**
* Creates a {@code TextHitInfo} on the other side of the
* insertion point. This {@code TextHitInfo} remains unchanged.
* @return a {@code TextHitInfo} on the other side of the
* insertion point.
*/
public TextHitInfo getOtherHit() {
if (isLeadingEdge) {
return trailing(charIndex - 1);
} else {
return leading(charIndex + 1);
}
}
/**
* Creates a {@code TextHitInfo} whose character index is offset
* by {@code delta} from the {@code charIndex} of this
* {@code TextHitInfo}. This {@code TextHitInfo} remains
* unchanged.
* @param delta the value to offset this {@code charIndex}
* @return a {@code TextHitInfo} whose {@code charIndex} is
* offset by {@code delta} from the {@code charIndex} of
* this {@code TextHitInfo}.
*/
public TextHitInfo getOffsetHit(int delta) {
return new TextHitInfo(charIndex + delta, isLeadingEdge);
}
}

View file

@ -0,0 +1,240 @@
/*
* Copyright (c) 1997, 1999, 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
/*
* one info for each side of each glyph
* separate infos for grow and shrink case
* !!! this doesn't really need to be a separate class. If we keep it
* separate, probably the newJustify code from TextLayout belongs here as well.
*/
class TextJustifier {
private GlyphJustificationInfo[] info;
private int start;
private int limit;
static boolean DEBUG = false;
/**
* Initialize the justifier with an array of infos corresponding to each
* glyph. Start and limit indicate the range of the array to examine.
*/
TextJustifier(GlyphJustificationInfo[] info, int start, int limit) {
this.info = info;
this.start = start;
this.limit = limit;
if (DEBUG) {
System.out.println("start: " + start + ", limit: " + limit);
for (int i = start; i < limit; i++) {
GlyphJustificationInfo gji = info[i];
System.out.println("w: " + gji.weight + ", gp: " +
gji.growPriority + ", gll: " +
gji.growLeftLimit + ", grl: " +
gji.growRightLimit);
}
}
}
public static final int MAX_PRIORITY = 3;
/**
* Return an array of deltas twice as long as the original info array,
* indicating the amount by which each side of each glyph should grow
* or shrink.
*
* Delta should be positive to expand the line, and negative to compress it.
*/
public float[] justify(float delta) {
float[] deltas = new float[info.length * 2];
boolean grow = delta > 0;
if (DEBUG)
System.out.println("delta: " + delta);
// make separate passes through glyphs in order of decreasing priority
// until justifyDelta is zero or we run out of priorities.
int fallbackPriority = -1;
for (int p = 0; delta != 0; p++) {
/*
* special case 'fallback' iteration, set flag and recheck
* highest priority
*/
boolean lastPass = p > MAX_PRIORITY;
if (lastPass)
p = fallbackPriority;
// pass through glyphs, first collecting weights and limits
float weight = 0;
float gslimit = 0;
float absorbweight = 0;
for (int i = start; i < limit; i++) {
GlyphJustificationInfo gi = info[i];
if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
if (fallbackPriority == -1) {
fallbackPriority = p;
}
if (i != start) { // ignore left of first character
weight += gi.weight;
if (grow) {
gslimit += gi.growLeftLimit;
if (gi.growAbsorb) {
absorbweight += gi.weight;
}
} else {
gslimit += gi.shrinkLeftLimit;
if (gi.shrinkAbsorb) {
absorbweight += gi.weight;
}
}
}
if (i + 1 != limit) { // ignore right of last character
weight += gi.weight;
if (grow) {
gslimit += gi.growRightLimit;
if (gi.growAbsorb) {
absorbweight += gi.weight;
}
} else {
gslimit += gi.shrinkRightLimit;
if (gi.shrinkAbsorb) {
absorbweight += gi.weight;
}
}
}
}
}
// did we hit the limit?
if (!grow) {
gslimit = -gslimit; // negative for negative deltas
}
boolean hitLimit = (weight == 0) || (!lastPass && ((delta < 0) == (delta < gslimit)));
boolean absorbing = hitLimit && absorbweight > 0;
// predivide delta by weight
float weightedDelta = delta / weight; // not used if weight == 0
float weightedAbsorb = 0;
if (hitLimit && absorbweight > 0) {
weightedAbsorb = (delta - gslimit) / absorbweight;
}
if (DEBUG) {
System.out.println("pass: " + p +
", d: " + delta +
", l: " + gslimit +
", w: " + weight +
", aw: " + absorbweight +
", wd: " + weightedDelta +
", wa: " + weightedAbsorb +
", hit: " + (hitLimit ? "y" : "n"));
}
// now allocate this based on ratio of weight to total weight
int n = start * 2;
for (int i = start; i < limit; i++) {
GlyphJustificationInfo gi = info[i];
if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
if (i != start) { // ignore left
float d;
if (hitLimit) {
// factor in sign
d = grow ? gi.growLeftLimit : -gi.shrinkLeftLimit;
if (absorbing) {
// sign factored in already
d += gi.weight * weightedAbsorb;
}
} else {
// sign factored in already
d = gi.weight * weightedDelta;
}
deltas[n] += d;
}
n++;
if (i + 1 != limit) { // ignore right
float d;
if (hitLimit) {
d = grow ? gi.growRightLimit : -gi.shrinkRightLimit;
if (absorbing) {
d += gi.weight * weightedAbsorb;
}
} else {
d = gi.weight * weightedDelta;
}
deltas[n] += d;
}
n++;
} else {
n += 2;
}
}
if (!lastPass && hitLimit && !absorbing) {
delta -= gslimit;
} else {
delta = 0; // stop iteration
}
}
if (DEBUG) {
float total = 0;
for (int i = 0; i < deltas.length; i++) {
total += deltas[i];
System.out.print(deltas[i] + ", ");
if (i % 20 == 9) {
System.out.println();
}
}
System.out.println("\ntotal: " + total);
System.out.println();
}
return deltas;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,764 @@
/*
* Copyright (c) 1997, 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.
*/
/*
* (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.awt.Font;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.AttributedString;
import java.text.Bidi;
import java.text.BreakIterator;
import java.text.CharacterIterator;
import java.awt.font.FontRenderContext;
import java.util.Hashtable;
import java.util.Map;
import sun.font.AttributeValues;
import sun.font.BidiUtils;
import sun.font.TextLineComponent;
import sun.font.TextLabelFactory;
import sun.font.FontResolver;
/**
* The {@code TextMeasurer} class provides the primitive operations
* needed for line break: measuring up to a given advance, determining the
* advance of a range of characters, and generating a
* {@code TextLayout} for a range of characters. It also provides
* methods for incremental editing of paragraphs.
* <p>
* A {@code TextMeasurer} object is constructed with an
* {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
* representing a single paragraph of text. The value returned by the
* {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
* method of {@code AttributedCharacterIterator}
* defines the absolute index of the first character. The value
* returned by the
* {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
* method of {@code AttributedCharacterIterator} defines the index
* past the last character. These values define the range of indexes to
* use in calls to the {@code TextMeasurer}. For example, calls to
* get the advance of a range of text or the line break of a range of text
* must use indexes between the beginning and end index values. Calls to
* {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
* and
* {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
* reset the {@code TextMeasurer} to use the beginning index and end
* index of the {@code AttributedCharacterIterator} passed in those calls.
* <p>
* Most clients will use the more convenient {@code LineBreakMeasurer},
* which implements the standard line break policy (placing as many words
* as will fit on each line).
*
* @author John Raley
* @see LineBreakMeasurer
* @since 1.3
*/
public final class TextMeasurer implements Cloneable {
// Number of lines to format to.
private static float EST_LINES = (float) 2.1;
/*
static {
String s = System.getProperty("estLines");
if (s != null) {
try {
Float f = Float.valueOf(s);
EST_LINES = f.floatValue();
}
catch(NumberFormatException e) {
}
}
//System.out.println("EST_LINES="+EST_LINES);
}
*/
private FontRenderContext fFrc;
private int fStart;
// characters in source text
private char[] fChars;
// Bidi for this paragraph
private Bidi fBidi;
// Levels array for chars in this paragraph - needed to reorder
// trailing counterdirectional whitespace
private byte[] fLevels;
// line components in logical order
private TextLineComponent[] fComponents;
// index where components begin
private int fComponentStart;
// index where components end
private int fComponentLimit;
private boolean haveLayoutWindow;
// used to find valid starting points for line components
private BreakIterator fLineBreak = null;
private CharArrayIterator charIter = null;
int layoutCount = 0;
int layoutCharCount = 0;
// paragraph, with resolved fonts and styles
private StyledParagraph fParagraph;
// paragraph data - same across all layouts
private boolean fIsDirectionLTR;
private byte fBaseline;
private float[] fBaselineOffsets;
private float fJustifyRatio = 1;
/**
* Constructs a {@code TextMeasurer} from the source text.
* The source text should be a single entire paragraph.
* @param text the source paragraph. Cannot be null.
* @param frc the information about a graphics device which is needed
* to measure the text correctly. Cannot be null.
*/
public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
fFrc = frc;
initAll(text);
}
protected Object clone() {
TextMeasurer other;
try {
other = (TextMeasurer) super.clone();
}
catch(CloneNotSupportedException e) {
throw new Error();
}
if (fComponents != null) {
other.fComponents = fComponents.clone();
}
return other;
}
private void invalidateComponents() {
fComponentStart = fComponentLimit = fChars.length;
fComponents = null;
haveLayoutWindow = false;
}
/**
* Initialize state, including fChars array, direction, and
* fBidi.
*/
private void initAll(AttributedCharacterIterator text) {
fStart = text.getBeginIndex();
// extract chars
fChars = new char[text.getEndIndex() - fStart];
int n = 0;
for (char c = text.first();
c != CharacterIterator.DONE;
c = text.next())
{
fChars[n++] = c;
}
text.first();
fBidi = new Bidi(text);
if (fBidi.isLeftToRight()) {
fBidi = null;
}
text.first();
Map<? extends Attribute, ?> paragraphAttrs = text.getAttributes();
NumericShaper shaper = AttributeValues.getNumericShaping(paragraphAttrs);
if (shaper != null) {
shaper.shape(fChars, 0, fChars.length);
}
fParagraph = new StyledParagraph(text, fChars);
// set paragraph attributes
{
// If there's an embedded graphic at the start of the
// paragraph, look for the first non-graphic character
// and use it and its font to initialize the paragraph.
// If not, use the first graphic to initialize.
fJustifyRatio = AttributeValues.getJustification(paragraphAttrs);
boolean haveFont = TextLine.advanceToFirstFont(text);
if (haveFont) {
Font defaultFont = TextLine.getFontAtCurrentPos(text);
int charsStart = text.getIndex() - text.getBeginIndex();
LineMetrics lm = defaultFont.getLineMetrics(fChars, charsStart, charsStart+1, fFrc);
fBaseline = (byte) lm.getBaselineIndex();
fBaselineOffsets = lm.getBaselineOffsets();
}
else {
// hmmm what to do here? Just try to supply reasonable
// values I guess.
GraphicAttribute graphic = (GraphicAttribute)
paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT);
fBaseline = TextLayout.getBaselineFromGraphic(graphic);
Hashtable<Attribute, ?> fmap = new Hashtable<>(5, (float)0.9);
Font dummyFont = new Font(fmap);
LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1, fFrc);
fBaselineOffsets = lm.getBaselineOffsets();
}
fBaselineOffsets = TextLine.getNormalizedOffsets(fBaselineOffsets, fBaseline);
}
invalidateComponents();
}
/**
* Generate components for the paragraph. fChars, fBidi should have been
* initialized already.
*/
private void generateComponents(int startingAt, int endingAt) {
if (collectStats) {
formattedChars += (endingAt-startingAt);
}
int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
TextLabelFactory factory = new TextLabelFactory(fFrc, fChars, fBidi, layoutFlags);
int[] charsLtoV = null;
if (fBidi != null) {
fLevels = BidiUtils.getLevels(fBidi);
int[] charsVtoL = BidiUtils.createVisualToLogicalMap(fLevels);
charsLtoV = BidiUtils.createInverseMap(charsVtoL);
fIsDirectionLTR = fBidi.baseIsLeftToRight();
}
else {
fLevels = null;
fIsDirectionLTR = true;
}
try {
fComponents = TextLine.getComponents(
fParagraph, fChars, startingAt, endingAt, charsLtoV, fLevels, factory);
}
catch(IllegalArgumentException e) {
System.out.println("startingAt="+startingAt+"; endingAt="+endingAt);
System.out.println("fComponentLimit="+fComponentLimit);
throw e;
}
fComponentStart = startingAt;
fComponentLimit = endingAt;
//debugFormatCount += (endingAt-startingAt);
}
private int calcLineBreak(final int pos, final float maxAdvance) {
// either of these statements removes the bug:
//generateComponents(0, fChars.length);
//generateComponents(pos, fChars.length);
int startPos = pos;
float width = maxAdvance;
int tlcIndex;
int tlcStart = fComponentStart;
for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
if (gaLimit > startPos) {
break;
}
else {
tlcStart = gaLimit;
}
}
// tlcStart is now the start of the tlc at tlcIndex
for (; tlcIndex < fComponents.length; tlcIndex++) {
TextLineComponent tlc = fComponents[tlcIndex];
int numCharsInGa = tlc.getNumCharacters();
int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart, width);
if (lineBreak == numCharsInGa && tlcIndex < fComponents.length) {
width -= tlc.getAdvanceBetween(startPos - tlcStart, lineBreak);
tlcStart += numCharsInGa;
startPos = tlcStart;
}
else {
return tlcStart + lineBreak;
}
}
if (fComponentLimit < fChars.length) {
// format more text and try again
//if (haveLayoutWindow) {
// outOfWindow++;
//}
generateComponents(pos, fChars.length);
return calcLineBreak(pos, maxAdvance);
}
return fChars.length;
}
/**
* According to the Unicode Bidirectional Behavior specification
* (Unicode Standard 2.0, section 3.11), whitespace at the ends
* of lines which would naturally flow against the base direction
* must be made to flow with the line direction, and moved to the
* end of the line. This method returns the start of the sequence
* of trailing whitespace characters to move to the end of a
* line taken from the given range.
*/
private int trailingCdWhitespaceStart(int startPos, int limitPos) {
if (fLevels != null) {
// Back up over counterdirectional whitespace
final byte baseLevel = (byte) (fIsDirectionLTR? 0 : 1);
for (int cdWsStart = limitPos; --cdWsStart >= startPos;) {
if ((fLevels[cdWsStart] % 2) == baseLevel ||
Character.getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) {
return ++cdWsStart;
}
}
}
return startPos;
}
private TextLineComponent[] makeComponentsOnRange(int startPos,
int limitPos) {
// sigh I really hate to do this here since it's part of the
// bidi algorithm.
// cdWsStart is the start of the trailing counterdirectional
// whitespace
final int cdWsStart = trailingCdWhitespaceStart(startPos, limitPos);
int tlcIndex;
int tlcStart = fComponentStart;
for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
if (gaLimit > startPos) {
break;
}
else {
tlcStart = gaLimit;
}
}
// tlcStart is now the start of the tlc at tlcIndex
int componentCount;
{
boolean split = false;
int compStart = tlcStart;
int lim=tlcIndex;
for (boolean cont=true; cont; lim++) {
int gaLimit = compStart + fComponents[lim].getNumCharacters();
if (cdWsStart > Math.max(compStart, startPos)
&& cdWsStart < Math.min(gaLimit, limitPos)) {
split = true;
}
if (gaLimit >= limitPos) {
cont=false;
}
else {
compStart = gaLimit;
}
}
componentCount = lim-tlcIndex;
if (split) {
componentCount++;
}
}
TextLineComponent[] components = new TextLineComponent[componentCount];
int newCompIndex = 0;
int linePos = startPos;
int breakPt = cdWsStart;
int subsetFlag;
if (breakPt == startPos) {
subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
TextLineComponent.RIGHT_TO_LEFT;
breakPt = limitPos;
}
else {
subsetFlag = TextLineComponent.UNCHANGED;
}
while (linePos < limitPos) {
int compLength = fComponents[tlcIndex].getNumCharacters();
int tlcLimit = tlcStart + compLength;
int start = Math.max(linePos, tlcStart);
int limit = Math.min(breakPt, tlcLimit);
components[newCompIndex++] = fComponents[tlcIndex].getSubset(
start-tlcStart,
limit-tlcStart,
subsetFlag);
linePos += (limit-start);
if (linePos == breakPt) {
breakPt = limitPos;
subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
TextLineComponent.RIGHT_TO_LEFT;
}
if (linePos == tlcLimit) {
tlcIndex++;
tlcStart = tlcLimit;
}
}
return components;
}
private TextLine makeTextLineOnRange(int startPos, int limitPos) {
int[] charsLtoV = null;
byte[] charLevels = null;
if (fBidi != null) {
Bidi lineBidi = fBidi.createLineBidi(startPos, limitPos);
charLevels = BidiUtils.getLevels(lineBidi);
int[] charsVtoL = BidiUtils.createVisualToLogicalMap(charLevels);
charsLtoV = BidiUtils.createInverseMap(charsVtoL);
}
TextLineComponent[] components = makeComponentsOnRange(startPos, limitPos);
return new TextLine(fFrc,
components,
fBaselineOffsets,
fChars,
startPos,
limitPos,
charsLtoV,
charLevels,
fIsDirectionLTR);
}
private void ensureComponents(int start, int limit) {
if (start < fComponentStart || limit > fComponentLimit) {
generateComponents(start, limit);
}
}
private void makeLayoutWindow(int localStart) {
int compStart = localStart;
int compLimit = fChars.length;
// If we've already gone past the layout window, format to end of paragraph
if (layoutCount > 0 && !haveLayoutWindow) {
float avgLineLength = Math.max(layoutCharCount / layoutCount, 1);
compLimit = Math.min(localStart + (int)(avgLineLength*EST_LINES), fChars.length);
}
if (localStart > 0 || compLimit < fChars.length) {
if (charIter == null) {
charIter = new CharArrayIterator(fChars);
}
else {
charIter.reset(fChars);
}
if (fLineBreak == null) {
fLineBreak = BreakIterator.getLineInstance();
}
fLineBreak.setText(charIter);
if (localStart > 0) {
if (!fLineBreak.isBoundary(localStart)) {
compStart = fLineBreak.preceding(localStart);
}
}
if (compLimit < fChars.length) {
if (!fLineBreak.isBoundary(compLimit)) {
compLimit = fLineBreak.following(compLimit);
}
}
}
ensureComponents(compStart, compLimit);
haveLayoutWindow = true;
}
/**
* Returns the index of the first character which will not fit on
* on a line beginning at {@code start} and possible
* measuring up to {@code maxAdvance} in graphical width.
*
* @param start the character index at which to start measuring.
* {@code start} is an absolute index, not relative to the
* start of the paragraph
* @param maxAdvance the graphical width in which the line must fit
* @return the index after the last character that will fit
* on a line beginning at {@code start}, which is not longer
* than {@code maxAdvance} in graphical width
* @throws IllegalArgumentException if {@code start} is
* less than the beginning of the paragraph.
*/
public int getLineBreakIndex(int start, float maxAdvance) {
int localStart = start - fStart;
if (!haveLayoutWindow ||
localStart < fComponentStart ||
localStart >= fComponentLimit) {
makeLayoutWindow(localStart);
}
return calcLineBreak(localStart, maxAdvance) + fStart;
}
/**
* Returns the graphical width of a line beginning at {@code start}
* and including characters up to {@code limit}.
* {@code start} and {@code limit} are absolute indices,
* not relative to the start of the paragraph.
*
* @param start the character index at which to start measuring
* @param limit the character index at which to stop measuring
* @return the graphical width of a line beginning at {@code start}
* and including characters up to {@code limit}
* @throws IndexOutOfBoundsException if {@code limit} is less
* than {@code start}
* @throws IllegalArgumentException if {@code start} or
* {@code limit} is not between the beginning of
* the paragraph and the end of the paragraph.
*/
public float getAdvanceBetween(int start, int limit) {
int localStart = start - fStart;
int localLimit = limit - fStart;
ensureComponents(localStart, localLimit);
TextLine line = makeTextLineOnRange(localStart, localLimit);
return line.getMetrics().advance;
// could cache line in case getLayout is called with same start, limit
}
/**
* Returns a {@code TextLayout} on the given character range.
*
* @param start the index of the first character
* @param limit the index after the last character. Must be greater
* than {@code start}
* @return a {@code TextLayout} for the characters beginning at
* {@code start} up to (but not including) {@code limit}
* @throws IndexOutOfBoundsException if {@code limit} is less
* than {@code start}
* @throws IllegalArgumentException if {@code start} or
* {@code limit} is not between the beginning of
* the paragraph and the end of the paragraph.
*/
public TextLayout getLayout(int start, int limit) {
int localStart = start - fStart;
int localLimit = limit - fStart;
ensureComponents(localStart, localLimit);
TextLine textLine = makeTextLineOnRange(localStart, localLimit);
if (localLimit < fChars.length) {
layoutCharCount += limit-start;
layoutCount++;
}
return new TextLayout(textLine,
fBaseline,
fBaselineOffsets,
fJustifyRatio);
}
private int formattedChars = 0;
private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
private boolean collectStats = false;
private void printStats() {
System.out.println("formattedChars: " + formattedChars);
//formattedChars = 0;
collectStats = false;
}
/**
* Updates the {@code TextMeasurer} after a single character has
* been inserted
* into the paragraph currently represented by this
* {@code TextMeasurer}. After this call, this
* {@code TextMeasurer} is equivalent to a new
* {@code TextMeasurer} created from the text; however, it will
* usually be more efficient to update an existing
* {@code TextMeasurer} than to create a new one from scratch.
*
* @param newParagraph the text of the paragraph after performing
* the insertion. Cannot be null.
* @param insertPos the position in the text where the character was
* inserted. Must not be less than the start of
* {@code newParagraph}, and must be less than the end of
* {@code newParagraph}.
* @throws IndexOutOfBoundsException if {@code insertPos} is less
* than the start of {@code newParagraph} or greater than
* or equal to the end of {@code newParagraph}
* @throws NullPointerException if {@code newParagraph} is
* {@code null}
*/
public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
if (collectStats) {
printStats();
}
if (wantStats) {
collectStats = true;
}
fStart = newParagraph.getBeginIndex();
int end = newParagraph.getEndIndex();
if (end - fStart != fChars.length+1) {
initAll(newParagraph);
}
char[] newChars = new char[end-fStart];
int newCharIndex = insertPos - fStart;
System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
char newChar = newParagraph.setIndex(insertPos);
newChars[newCharIndex] = newChar;
System.arraycopy(fChars,
newCharIndex,
newChars,
newCharIndex+1,
end-insertPos-1);
fChars = newChars;
if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) ||
newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
fBidi = new Bidi(newParagraph);
if (fBidi.isLeftToRight()) {
fBidi = null;
}
}
fParagraph = StyledParagraph.insertChar(newParagraph,
fChars,
insertPos,
fParagraph);
invalidateComponents();
}
/**
* Updates the {@code TextMeasurer} after a single character has
* been deleted
* from the paragraph currently represented by this
* {@code TextMeasurer}. After this call, this
* {@code TextMeasurer} is equivalent to a new {@code TextMeasurer}
* created from the text; however, it will usually be more efficient
* to update an existing {@code TextMeasurer} than to create a new one
* from scratch.
*
* @param newParagraph the text of the paragraph after performing
* the deletion. Cannot be null.
* @param deletePos the position in the text where the character was removed.
* Must not be less than
* the start of {@code newParagraph}, and must not be greater than the
* end of {@code newParagraph}.
* @throws IndexOutOfBoundsException if {@code deletePos} is
* less than the start of {@code newParagraph} or greater
* than the end of {@code newParagraph}
* @throws NullPointerException if {@code newParagraph} is
* {@code null}
*/
public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
fStart = newParagraph.getBeginIndex();
int end = newParagraph.getEndIndex();
if (end - fStart != fChars.length-1) {
initAll(newParagraph);
}
char[] newChars = new char[end-fStart];
int changedIndex = deletePos-fStart;
System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart);
System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos);
fChars = newChars;
if (fBidi != null) {
fBidi = new Bidi(newParagraph);
if (fBidi.isLeftToRight()) {
fBidi = null;
}
}
fParagraph = StyledParagraph.deleteChar(newParagraph,
fChars,
deletePos,
fParagraph);
invalidateComponents();
}
/**
* NOTE: This method is only for LineBreakMeasurer's use. It is package-
* private because it returns internal data.
*/
char[] getChars() {
return fChars;
}
}

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 1998, 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 Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.awt.geom.AffineTransform;
import java.io.Serializable;
import java.io.ObjectStreamException;
/**
* The {@code TransformAttribute} class provides an immutable
* wrapper for a transform so that it is safe to use as an attribute.
*/
public final class TransformAttribute implements Serializable {
/**
* The {@code AffineTransform} for this
* {@code TransformAttribute}, or {@code null}
* if {@code AffineTransform} is the identity transform.
*/
private AffineTransform transform;
/**
* Wraps the specified transform. The transform is cloned and a
* reference to the clone is kept. The original transform is unchanged.
* If null is passed as the argument, this constructor behaves as though
* it were the identity transform. (Note that it is preferable to use
* {@link #IDENTITY} in this case.)
* @param transform the specified {@link AffineTransform} to be wrapped,
* or null.
*/
public TransformAttribute(AffineTransform transform) {
if (transform != null && !transform.isIdentity()) {
this.transform = new AffineTransform(transform);
}
}
/**
* Returns a copy of the wrapped transform.
* @return an {@code AffineTransform} that is a copy of the wrapped
* transform of this {@code TransformAttribute}.
*/
public AffineTransform getTransform() {
AffineTransform at = transform;
return (at == null) ? new AffineTransform() : new AffineTransform(at);
}
/**
* Returns {@code true} if the wrapped transform is
* an identity transform.
* @return {@code true} if the wrapped transform is
* an identity transform; {@code false} otherwise.
* @since 1.4
*/
public boolean isIdentity() {
return transform == null;
}
/**
* A {@code TransformAttribute} representing the identity transform.
* @since 1.6
*/
public static final TransformAttribute IDENTITY = new TransformAttribute(null);
private void writeObject(java.io.ObjectOutputStream s)
throws java.lang.ClassNotFoundException,
java.io.IOException
{
// sigh -- 1.3 expects transform is never null, so we need to always write one out
if (this.transform == null) {
this.transform = new AffineTransform();
}
s.defaultWriteObject();
}
/*
* @since 1.6
*/
private Object readResolve() throws ObjectStreamException {
if (transform == null || transform.isIdentity()) {
return IDENTITY;
}
return this;
}
// Added for serial backwards compatibility (4348425)
static final long serialVersionUID = 3356247357827709530L;
/**
* @since 1.6
*/
public int hashCode() {
return transform == null ? 0 : transform.hashCode();
}
/**
* Returns {@code true} if rhs is a {@code TransformAttribute}
* whose transform is equal to this {@code TransformAttribute}'s
* transform.
* @param rhs the object to compare to
* @return {@code true} if the argument is a {@code TransformAttribute}
* whose transform is equal to this {@code TransformAttribute}'s
* transform.
* @since 1.6
*/
public boolean equals(Object rhs) {
if (rhs != null) {
try {
TransformAttribute that = (TransformAttribute)rhs;
if (transform == null) {
return that.transform == null;
}
return transform.equals(that.transform);
}
catch (ClassCastException e) {
}
}
return false;
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 1998, 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.
*/
/**
* Provides classes and interface relating to fonts. It contains support for
* representing Type 1, Type 1 Multiple Master fonts, OpenType fonts, and
* TrueType fonts.
*
* @since 1.2
*/
package java.awt.font;