You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-dev@xmlgraphics.apache.org by be...@apache.org on 2001/07/05 08:23:33 UTC
cvs commit: xml-batik/sources/org/apache/batik/gvt/font AWTGVTFont.java AWTGVTGlyphVector.java FontFamilyResolver.java GVTFont.java Glyph.java SVGGVTGlyphVector.java
bella 01/07/04 23:23:33
Modified: sources/org/apache/batik/gvt/font AWTGVTFont.java
AWTGVTGlyphVector.java FontFamilyResolver.java
GVTFont.java Glyph.java SVGGVTGlyphVector.java
Log:
- improved text highlight shape
- fixed up text decoration problems
- added support for the lang and orientation glyph attributes
- added supoort for Arabic shaping and ligatures
Revision Changes Path
1.6 +110 -21 xml-batik/sources/org/apache/batik/gvt/font/AWTGVTFont.java
Index: AWTGVTFont.java
===================================================================
RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/AWTGVTFont.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- AWTGVTFont.java 2001/06/12 23:49:39 1.5
+++ AWTGVTFont.java 2001/07/05 06:23:31 1.6
@@ -18,12 +18,19 @@
import java.util.HashMap;
import java.awt.geom.AffineTransform;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.text.StringCharacterIterator;
+import org.apache.batik.gvt.text.ArabicTextHandler;
+import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+
+
/**
* This is a wrapper class for a java.awt.Font instance.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
- * @version $Id: AWTGVTFont.java,v 1.5 2001/06/12 23:49:39 bella Exp $
+ * @version $Id: AWTGVTFont.java,v 1.6 2001/07/05 06:23:31 bella Exp $
*/
public final class AWTGVTFont implements GVTFont {
@@ -32,6 +39,8 @@
/**
* Creates a new AWTGVTFont that wraps the given Font.
+ *
+ * @param font The font object to wrap.
*/
public AWTGVTFont(Font font) {
awtFont = font;
@@ -40,6 +49,8 @@
/**
* Creates a new AWTGVTFont with the specified attributes.
+ *
+ * @param attributes Contains attributes of the font to create.
*/
public AWTGVTFont(Map attributes) {
awtFont = new Font(attributes);
@@ -48,6 +59,10 @@
/**
* Creates a new AWTGVTFont from the specified name, style and point size.
+ *
+ * @param name The name of the new font.
+ * @param style The required font style.
+ * @param size The required font size.
*/
public AWTGVTFont(String name, int style, int size) {
awtFont = new Font(name, style, size);
@@ -55,22 +70,32 @@
}
/**
- * Checks if this Font has a glyph for the specified character.
+ * Checks if this font can display the specified character.
+ *
+ * @param c The character to check.
+ * @return Whether or not the character can be displayed.
*/
public boolean canDisplay(char c) {
return awtFont.canDisplay(c);
}
/**
- * Indicates whether or not this Font can display the characters in the
- * specified text starting at start and ending at limit.
+ * Indicates whether or not this font can display the characters in the
+ * specified text starting at start and ending at limit.
+ *
+ * @param text An array containing the characters to check.
+ * @param start The index of the first character to check.
+ * @param limit The index of the last character to check.
+ *
+ * @return The index of the first char this font cannot display. Will be
+ * -1 if it can display all characters in the specified range.
*/
public int canDisplayUpTo(char[] text, int start, int limit) {
return awtFont.canDisplayUpTo(text, start, limit);
}
/**
- * Indicates whether or not this Font can display the the characters in
+ * Indicates whether or not this font can display the the characters in
* the specified CharacterIterator starting at start and ending at limit.
*/
public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
@@ -78,7 +103,7 @@
}
/**
- * Indicates whether or not this Font can display a specified String.
+ * Indicates whether or not this font can display a specified String.
*/
public int canDisplayUpTo(String str) {
return awtFont.canDisplayUpTo(str);
@@ -90,10 +115,18 @@
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
char[] chars) {
+
+ StringCharacterIterator sci = new StringCharacterIterator(new String(chars));
+
if (getSize() < 1) {
- return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, chars), new AWTGVTFont(tenPtFont), getSize()/10f);
+ // Because of a bug with GlyphVectors created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce it to its correct size.
+ // Have a look at AWTGVTGlyphVector.
+ return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, chars),
+ new AWTGVTFont(tenPtFont), getSize()/10f, sci);
}
- return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, chars), this, 1);
+ return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, chars), this, 1, sci);
}
/**
@@ -102,10 +135,29 @@
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
CharacterIterator ci) {
+
+ AWTGVTGlyphVector awtGlyphVector;
+
if (getSize() < 1) {
- return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, ci), new AWTGVTFont(tenPtFont), getSize()/10f);
+ // Because of a bug with GlyphVectors created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce it to its correct size.
+ // Have a look at AWTGVTGlyphVector.
+ awtGlyphVector = new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, ci),
+ new AWTGVTFont(tenPtFont), getSize()/10f, ci);
+ } else {
+ awtGlyphVector = new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, ci), this, 1, ci);
}
- return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, ci), this, 1);
+
+ if (ci instanceof AttributedCharacterIterator) {
+ AttributedCharacterIterator aci = (AttributedCharacterIterator)ci;
+ AttributedString as = new AttributedString(aci);
+ if (ArabicTextHandler.containsArabic(as)) {
+ String substString = ArabicTextHandler.createSubstituteString(aci);
+ return createGlyphVector(frc, substString);
+ }
+ }
+ return awtGlyphVector;
}
/**
@@ -113,12 +165,17 @@
* array and the specified FontRenderContext.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
- int[] glyphCodes) {
+ int[] glyphCodes, CharacterIterator ci) {
if (getSize() < 1) {
- return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, glyphCodes), new AWTGVTFont(tenPtFont), getSize()/10f);
+ // Because of a bug with GlyphVectors created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce it to its correct size.
+ // Have a look at AWTGVTGlyphVector.
+ return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, glyphCodes),
+ new AWTGVTFont(tenPtFont), getSize()/10f, ci);
}
return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, glyphCodes),
- this, 1);
+ this, 1, ci);
}
/**
@@ -126,10 +183,18 @@
* the specified FontRenderContext.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc, String str) {
+
+ StringCharacterIterator sci = new StringCharacterIterator(str);
+
if (getSize() < 1) {
- return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, str), new AWTGVTFont(tenPtFont), getSize()/10f);
+ // Because of a bug with GlyphVectors created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce it to its correct size.
+ // Have a look at AWTGVTGlyphVector.
+ return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, str),
+ new AWTGVTFont(tenPtFont), getSize()/10f, sci);
}
- return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, str), this, 1);
+ return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, str), this, 1, sci);
}
/**
@@ -147,7 +212,12 @@
public GVTLineMetrics getLineMetrics(char[] chars, int beginIndex, int limit,
FontRenderContext frc) {
if (getSize() < 1) {
- return new GVTLineMetrics(tenPtFont.getLineMetrics(chars, beginIndex, limit, frc), getSize()/10f);
+ // Because of a bug with LineMetrics created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce the metrics to their correct size.
+ // Have a look at GVTLineMetrics.
+ return new GVTLineMetrics(tenPtFont.getLineMetrics(chars, beginIndex, limit, frc),
+ getSize()/10f);
}
return new GVTLineMetrics(awtFont.getLineMetrics(chars, beginIndex, limit, frc));
}
@@ -158,7 +228,12 @@
public GVTLineMetrics getLineMetrics(CharacterIterator ci, int beginIndex,
int limit, FontRenderContext frc) {
if (getSize() < 1) {
- return new GVTLineMetrics(tenPtFont.getLineMetrics(ci, beginIndex, limit, frc), getSize()/10f);
+ // Because of a bug with LineMetrics created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce the metrics to their correct size.
+ // Have a look at GVTLineMetrics.
+ return new GVTLineMetrics(tenPtFont.getLineMetrics(ci, beginIndex, limit, frc),
+ getSize()/10f);
}
return new GVTLineMetrics(awtFont.getLineMetrics(ci, beginIndex, limit, frc));
}
@@ -169,7 +244,12 @@
*/
public GVTLineMetrics getLineMetrics(String str, FontRenderContext frc) {
if (getSize() < 1) {
- return new GVTLineMetrics(tenPtFont.getLineMetrics(str, frc), getSize()/10f);
+ // Because of a bug with LineMetrics created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce the metrics to their correct size.
+ // Have a look at GVTLineMetrics.
+ return new GVTLineMetrics(tenPtFont.getLineMetrics(str, frc),
+ getSize()/10f);
}
return new GVTLineMetrics(awtFont.getLineMetrics(str, frc));
}
@@ -180,7 +260,12 @@
public GVTLineMetrics getLineMetrics(String str, int beginIndex, int limit,
FontRenderContext frc) {
if (getSize() < 1) {
- return new GVTLineMetrics(tenPtFont.getLineMetrics(str, beginIndex, limit, frc), getSize()/10f);
+ // Because of a bug with LineMetrics created by fonts with
+ // sizes < 1pt, we need to use a 10pt font and then apply a scale
+ // factor to reduce the metrics to their correct size.
+ // Have a look at GVTLineMetrics.
+ return new GVTLineMetrics(tenPtFont.getLineMetrics(str, beginIndex, limit, frc),
+ getSize()/10f);
}
return new GVTLineMetrics(awtFont.getLineMetrics(str, beginIndex, limit, frc));
}
@@ -193,19 +278,23 @@
}
/**
- * Returns the horizontal kerning value of this glyph pair.
+ * Returns the horizontal kerning value for this glyph pair.
*/
public float getHKern(int glyphCode1, int glyphCode2) {
return 0f;
}
/**
- * Returns the vertical kerning value of this glyph pair.
+ * Returns the vertical kerning value for this glyph pair.
*/
public float getVKern(int glyphCode1, int glyphCode2) {
return 0f;
}
+ /**
+ * Returns a string representation of this font. This is for debugging
+ * purposes only.
+ */
public String toString() {
return awtFont.getFontName();
}
1.6 +155 -22 xml-batik/sources/org/apache/batik/gvt/font/AWTGVTGlyphVector.java
Index: AWTGVTGlyphVector.java
===================================================================
RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/AWTGVTGlyphVector.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- AWTGVTGlyphVector.java 2001/06/12 23:49:40 1.5
+++ AWTGVTGlyphVector.java 2001/07/05 06:23:31 1.6
@@ -22,19 +22,22 @@
import java.awt.Stroke;
import org.apache.batik.gvt.GraphicsNodeRenderContext;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.ArabicTextHandler;
import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
import java.awt.font.TextAttribute;
/**
* This is a wrapper class for a java.awt.font.GlyphVector instance.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
- * @version $Id: AWTGVTGlyphVector.java,v 1.5 2001/06/12 23:49:40 bella Exp $
+ * @version $Id: AWTGVTGlyphVector.java,v 1.6 2001/07/05 06:23:31 bella Exp $
*/
public final class AWTGVTGlyphVector implements GVTGlyphVector {
private final GlyphVector awtGlyphVector;
private final AWTGVTFont gvtFont;
+ private CharacterIterator ci;
private Point2D[] glyphPositions;
private Point2D[] defaultGlyphPositions;
@@ -54,11 +57,24 @@
/**
* Creates and new AWTGVTGlyphVector from the specified GlyphVector
* and AWTGVTFont objects.
+ *
+ * @param glyphVector The glyph vector that this one will be based upon.
+ * @param font The font that is creating this glyph vector.
+ * @param scaleFactor The scale factor to apply to the glyph vector.
+ * IMPORTANT: This is only required because the GlyphVector class doesn't
+ * handle font sizes less than 1 correctly. By using the scale factor we
+ * can use a GlyphVector created by a larger font and then scale it down to
+ * the correct size.
+ * @param ci The character string that this glyph vector represents.
*/
- public AWTGVTGlyphVector(GlyphVector glyphVector, AWTGVTFont font, float scaleFactor) {
- awtGlyphVector = glyphVector;
- gvtFont = font;
+ public AWTGVTGlyphVector(GlyphVector glyphVector, AWTGVTFont font,
+ float scaleFactor, CharacterIterator ci) {
+
+ this.awtGlyphVector = glyphVector;
+ this.gvtFont = font;
this.scaleFactor = scaleFactor;
+ this.ci = ci;
+
int numGlyphs = glyphVector.getNumGlyphs();
outline = null;
logicalBounds = null;
@@ -122,14 +138,22 @@
}
return glyphLogicalBounds[glyphIndex];
}
-
+ /**
+ * Calculates the logical bounds for each glyph. The logical bounds are
+ * what is used for highlighting the glyphs when selected.
+ */
private void computeGlyphLogicalBounds() {
GVTLineMetrics lineMetrics = gvtFont.getLineMetrics("By", awtGlyphVector.getFontRenderContext());
float ascent = lineMetrics.getAscent() * scaleFactor;
float descent = lineMetrics.getDescent() * scaleFactor;
+ Shape[] tempLogicalBounds = new Shape[getNumGlyphs()];
+ boolean[] rotated = new boolean[getNumGlyphs()];
+ double maxWidth = -1;
+ double maxHeight = -1;
+
for (int i = 0; i < getNumGlyphs(); i++) {
if (glyphVisible[i]) {
@@ -143,35 +167,127 @@
float glyphX = (float)(getGlyphPosition(i).getX());
float glyphY = (float)getGlyphPosition(i).getY() - ascent;
float glyphWidth = glyphMetrics.getHorizontalAdvance();
- if (i < getNumGlyphs()-1) {
- float nextY = (float)getGlyphPosition(i+1).getY() - ascent;
- if (glyphY == nextY) {
- float nextX = (float)getGlyphPosition(i+1).getX();
- glyphWidth = Math.max(glyphWidth, nextX - glyphX);
- }
- }
- float glyphHeight = ascent + descent;
+ float glyphHeight = glyphMetrics.getVerticalAdvance();
- glyphLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY,
- glyphWidth, glyphHeight);
+ tempLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY,
+ glyphWidth, glyphHeight);
+ if (glyphWidth > maxWidth) maxWidth = glyphWidth;
+ if (glyphHeight > maxHeight) maxHeight = glyphHeight;
+ rotated[i] = false;
- } else { // the glyph is transformed so just return the neat bounds
+ } else { // the glyph is transformed
Shape glyphOutline = awtGlyphVector.getGlyphOutline(i);
Rectangle2D glyphBounds = glyphOutline.getBounds2D();
+ // store three corner points so we can determine whether the glyph is rotated
+ Point2D p1 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMinY());
+ Point2D p2 = new Point2D.Double(glyphBounds.getMaxX(), glyphBounds.getMinY());
+ Point2D p3 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMaxY());
+
AffineTransform tr = AffineTransform.getTranslateInstance(getGlyphPosition(i).getX(),
- getGlyphPosition(i).getY());
+ getGlyphPosition(i).getY());
tr.concatenate(glyphTransform);
tr.scale(scaleFactor, scaleFactor);
- glyphLogicalBounds[i] = tr.createTransformedShape(glyphBounds);
+ tempLogicalBounds[i] = tr.createTransformedShape(glyphBounds);
+
+ Point2D tp1 = new Point2D.Double();
+ Point2D tp2 = new Point2D.Double();
+ Point2D tp3 = new Point2D.Double();
+ tr.transform(p1, tp1);
+ tr.transform(p2, tp2);
+ tr.transform(p3, tp3);
+
+ if ((Math.abs(tp1.getX() - tp2.getX()) < 0.001
+ || Math.abs(tp1.getX() - tp3.getX()) < 0.001)
+ && (Math.abs(tp1.getY() - tp2.getY()) < 0.001
+ || Math.abs(tp1.getY() - tp3.getY()) < 0.001)) {
+ rotated[i] = false;
+ } else {
+ rotated[i] = true;
+ }
+ if (glyphBounds.isEmpty()) {
+ if (i > 0) {
+ // can't tell if rotated or not, make it the same as the
+ // previous glyph
+ rotated[i] = rotated[i-1];
+ } else {
+ rotated[i] = true;
+ }
+ }
+
+ Rectangle2D rectBounds = tempLogicalBounds[i].getBounds2D();
+ if (rectBounds.getWidth() > maxWidth) maxWidth = rectBounds.getWidth();
+ if (rectBounds.getHeight() > maxHeight) maxHeight = rectBounds.getHeight();
}
} else {
// the glyph is not drawn
- glyphLogicalBounds[i] = null;
+ tempLogicalBounds[i] = null;
+ }
+ }
+
+ // if appropriate, join adjacent glyph logical bounds
+ GeneralPath logicalBoundsPath = new GeneralPath();
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ if (tempLogicalBounds[i] != null) {
+ logicalBoundsPath.append(tempLogicalBounds[i], false);
+ }
+ }
+ Rectangle2D fullBounds = logicalBoundsPath.getBounds2D();
+
+ if (fullBounds.getHeight() < maxHeight*1.5) {
+ // make all glyphs tops and bottoms the same as the full bounds
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ // first make sure that the glyph logical bounds are not rotated
+ if (!rotated[i] && tempLogicalBounds[i] != null) {
+ Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D();
+ double x = glyphBounds.getMinX();
+ double width = glyphBounds.getWidth();
+ if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) {
+ // make this glyph extend to the start of the next one
+ Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D();
+ if (nextGlyphBounds.getX() > x) { // going left to right
+ width = nextGlyphBounds.getX() - x;
+ } else {
+ double newGlyphX = nextGlyphBounds.getX() + nextGlyphBounds.getWidth();
+ width += (x - newGlyphX);
+ x = newGlyphX;
+ }
+ }
+ tempLogicalBounds[i] = new Rectangle2D.Double(x, fullBounds.getMinY(),
+ width, fullBounds.getHeight());
+ }
+ }
+ }
+ if (fullBounds.getWidth() < maxWidth*1.5) {
+ // make all glyphs left and right edges the same as the full bounds
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ // first make sure that the glyph logical bounds are not rotated
+ if (!rotated[i] && tempLogicalBounds[i] != null) {
+ Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D();
+ double y = glyphBounds.getMinY();
+ double height = glyphBounds.getHeight();
+ if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) {
+ // make this glyph extend to the start of the next one
+ Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D();
+ if (nextGlyphBounds.getY() > y) { // going top to bottom
+ height = nextGlyphBounds.getY() - y;
+ } else {
+ double newGlyphY = nextGlyphBounds.getY() + nextGlyphBounds.getHeight();
+ height += (y - newGlyphY);
+ y = newGlyphY;
+ }
+ }
+ tempLogicalBounds[i] = new Rectangle2D.Double(fullBounds.getMinX(), y,
+ fullBounds.getWidth(), height);
+ }
}
}
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ glyphLogicalBounds[i] = tempLogicalBounds[i];
+ }
+
}
@@ -203,7 +319,6 @@
AffineTransform tr = AffineTransform.getTranslateInstance(getGlyphPosition(glyphIndex).getX(),
getGlyphPosition(glyphIndex).getY());
-
AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
if (glyphTransform != null) {
tr.concatenate(glyphTransform);
@@ -344,15 +459,24 @@
awtGlyphVector.performDefaultLayout();
outline = null;
logicalBounds = null;
+ float shiftLeft = 0;
for (int i = 0; i < getNumGlyphs(); i++) {
Point2D glyphPos = awtGlyphVector.getGlyphPosition(i);
- glyphPositions[i] = new Point2D.Float((float)(glyphPos.getX() * scaleFactor),
+ glyphPositions[i] = new Point2D.Float((float)((glyphPos.getX()-shiftLeft) * scaleFactor),
(float)(glyphPos.getY() * scaleFactor));
defaultGlyphPositions[i] = glyphPositions[i];
glyphTransforms[i] = null;
glyphVisualBounds[i] = null;
glyphLogicalBounds[i] = null;
glyphOutlines[i] = null;
+
+ // if c is a transparent arabic char then need to shift the following
+ // glyphs left so that the current glyph is overwritten
+ char c = ci.setIndex(i + ci.getBeginIndex());
+ if (ArabicTextHandler.arabicCharTransparent(c)) {
+ GlyphMetrics gm = awtGlyphVector.getGlyphMetrics(i);
+ shiftLeft = gm.getAdvance();
+ }
}
}
@@ -405,8 +529,17 @@
}
if (endGlyphIndex > getNumGlyphs()-1) {
endGlyphIndex = getNumGlyphs()-1;
+ }
+ int charCount = 0;
+ for (int i = ci.getBeginIndex() + startGlyphIndex; i <= ci.getBeginIndex() + endGlyphIndex; i++) {
+ char c = ci.setIndex(i);
+ if (ArabicTextHandler.isLigature(c)) {
+ charCount += ArabicTextHandler.getNumChars(c);
+ } else {
+ charCount++;
+ }
}
- return endGlyphIndex - startGlyphIndex + 1;
+ return charCount;
}
1.4 +9 -4 xml-batik/sources/org/apache/batik/gvt/font/FontFamilyResolver.java
Index: FontFamilyResolver.java
===================================================================
RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/FontFamilyResolver.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- FontFamilyResolver.java 2001/06/12 23:49:40 1.3
+++ FontFamilyResolver.java 2001/07/05 06:23:31 1.4
@@ -20,7 +20,7 @@
* The is a utility class that is used for resolving UnresolvedFontFamilies.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
- * @version $Id: FontFamilyResolver.java,v 1.3 2001/06/12 23:49:40 bella Exp $
+ * @version $Id: FontFamilyResolver.java,v 1.4 2001/07/05 06:23:31 bella Exp $
*/
public class FontFamilyResolver {
@@ -34,7 +34,7 @@
* List of all available fonts on the current system, plus a few common
* alternatives.
*/
- protected final static Map fonts = new HashMap(11);
+ protected final static Map fonts = new HashMap();
protected final static Vector awtFontFamilies = new Vector();
protected final static Vector awtFonts = new Vector();
@@ -43,10 +43,10 @@
* This sets up the list of available fonts.
*/
static {
+ fonts.put("sans-serif", "SansSerif");
fonts.put("serif", "Serif");
fonts.put("Times", "Serif");
fonts.put("Times New Roman", "Serif");
- fonts.put("sans-serif", "SansSerif");
fonts.put("cursive", "Dialog");
fonts.put("fantasy", "Symbol");
fonts.put("monospace", "Monospaced");
@@ -79,6 +79,10 @@
}
}
+ // first add the default font
+ awtFontFamilies.add(defaultFont);
+ awtFonts.add(new AWTGVTFont(defaultFont.getFamilyName(), 0, 12));
+
Collection fontValues = fonts.values();
Iterator iter = fontValues.iterator();
while(iter.hasNext()) {
@@ -140,7 +144,8 @@
for (int i = 0; i < awtFontFamilies.size(); i++) {
AWTFontFamily fontFamily = (AWTFontFamily)awtFontFamilies.get(i);
AWTGVTFont font = (AWTGVTFont)awtFonts.get(i);
- if (font.canDisplay(c)) {
+ if (font.canDisplay(c) && fontFamily.getFamilyName().indexOf("Song") == -1) {
+ // the awt font for "MS Song" doesn't display chinese glyphs correctly
return fontFamily;
}
}
1.6 +3 -2 xml-batik/sources/org/apache/batik/gvt/font/GVTFont.java
Index: GVTFont.java
===================================================================
RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/GVTFont.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- GVTFont.java 2001/06/12 23:49:40 1.5
+++ GVTFont.java 2001/07/05 06:23:31 1.6
@@ -16,7 +16,7 @@
* An interface for all GVT font classes.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
- * @version $Id: GVTFont.java,v 1.5 2001/06/12 23:49:40 bella Exp $
+ * @version $Id: GVTFont.java,v 1.6 2001/07/05 06:23:31 bella Exp $
*/
public interface GVTFont {
@@ -59,7 +59,8 @@
* array and the specified FontRenderContext.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
- int[] glyphCodes);
+ int[] glyphCodes,
+ CharacterIterator ci);
/**
* Returns a new GlyphVector object created with the specified String and
* the specified FontRenderContext.
1.5 +28 -4 xml-batik/sources/org/apache/batik/gvt/font/Glyph.java
Index: Glyph.java
===================================================================
RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/Glyph.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- Glyph.java 2001/06/12 23:49:41 1.4
+++ Glyph.java 2001/07/05 06:23:32 1.5
@@ -10,6 +10,7 @@
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.GraphicsNodeRenderContext;
+import org.apache.batik.gvt.text.ArabicTextHandler;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.AffineTransform;
@@ -23,7 +24,7 @@
* attributes.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
- * @version $Id: Glyph.java,v 1.4 2001/06/12 23:49:41 bella Exp $
+ * @version $Id: Glyph.java,v 1.5 2001/07/05 06:23:32 bella Exp $
*/
public class Glyph {
@@ -33,6 +34,7 @@
private String orientation;
private String arabicForm;
private String lang;
+ private Point2D horizOrigin;
private Point2D vertOrigin;
private float horizAdvX;
private float vertAdvY;
@@ -56,6 +58,7 @@
* only, or empty which indicates that the glpyh can be use in both.
* @param arabicForm
* @param lang
+ * @param horizOrigin
* @param vertOrigin
* @param horizAdvX
* @param vertAdvY
@@ -64,8 +67,8 @@
*/
public Glyph(GraphicsNode glyphNode, String unicode, Vector names,
String orientation, String arabicForm, String lang,
- Point2D vertOrigin, float horizAdvX, float vertAdvY,
- int glyphCode, float kernScale) {
+ Point2D horizOrigin, Point2D vertOrigin, float horizAdvX,
+ float vertAdvY, int glyphCode, float kernScale) {
if (glyphNode == null) {
throw new IllegalArgumentException();
@@ -73,10 +76,12 @@
if (unicode == null) {
throw new IllegalArgumentException();
}
+ if (horizOrigin == null) {
+ throw new IllegalArgumentException();
+ }
if (vertOrigin == null) {
throw new IllegalArgumentException();
}
- // should really check all of the other paramters too
this.glyphNode = glyphNode;
this.unicode = unicode;
@@ -84,9 +89,19 @@
this.orientation = orientation;
this.arabicForm = arabicForm;
this.lang = lang;
+ this.horizOrigin = horizOrigin;
this.vertOrigin = vertOrigin;
this.horizAdvX = horizAdvX;
this.vertAdvY = vertAdvY;
+ if (this.unicode != null) {
+ if (this.unicode.length() > 0
+ && ArabicTextHandler.arabicCharTransparent(this.unicode.charAt(0))) {
+ // if this glyph is arabic and transparent,
+ // then it doesn't cause any advance
+ this.horizAdvX = 0;
+ this.vertAdvY = 0;
+ }
+ }
this.kernScale = kernScale;
this.glyphCode = glyphCode;
this.position = new Point2D.Float(0,0);
@@ -149,6 +164,15 @@
*/
public String getLang() {
return lang;
+ }
+
+ /**
+ * Returns the horizontal origin of this glyph.
+ *
+ * @return The horizontal origin.
+ */
+ public Point2D getHorizOrigin() {
+ return horizOrigin;
}
/**
1.6 +123 -16 xml-batik/sources/org/apache/batik/gvt/font/SVGGVTGlyphVector.java
Index: SVGGVTGlyphVector.java
===================================================================
RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/SVGGVTGlyphVector.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- SVGGVTGlyphVector.java 2001/06/12 23:49:42 1.5
+++ SVGGVTGlyphVector.java 2001/07/05 06:23:32 1.6
@@ -31,7 +31,7 @@
* A GVTGlyphVector class for SVG fonts.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
- * @version $Id: SVGGVTGlyphVector.java,v 1.5 2001/06/12 23:49:42 bella Exp $
+ * @version $Id: SVGGVTGlyphVector.java,v 1.6 2001/07/05 06:23:32 bella Exp $
*/
public final class SVGGVTGlyphVector implements GVTGlyphVector {
@@ -44,6 +44,14 @@
private Shape[] glyphLogicalBounds;
private boolean[] glyphVisible;
+ /**
+ * Constructs an SVGGVTGlyphVector.
+ *
+ * @param font The font that is creating this glyph vector.
+ * @param glyphs An array containing the glyphs that form the basis for this
+ * glyph vector.
+ * @param frc The current font render context.
+ */
public SVGGVTGlyphVector(GVTFont font, Glyph[] glyphs, FontRenderContext frc) {
this.font = font;
this.glyphs = glyphs;
@@ -142,7 +150,6 @@
float ascent = 0;
float descent = 0;
-
if (font != null) {
// font will only be null if this glyph vector is for an altGlyph
GVTLineMetrics lineMetrics = font.getLineMetrics("By", frc);
@@ -153,44 +160,144 @@
descent = -descent;
}
}
+ Shape[] tempLogicalBounds = new Shape[getNumGlyphs()];
+ boolean[] rotated = new boolean[getNumGlyphs()];
+
+ double maxWidth = -1;
+ double maxHeight = -1;
for (int i = 0; i < getNumGlyphs(); i++) {
if (glyphVisible[i]) {
+
AffineTransform glyphTransform = getGlyphTransform(i);
GVTGlyphMetrics glyphMetrics = getGlyphMetrics(i);
if (glyphTransform == null && ascent != 0) {
- float glyphX = (float)getGlyphPosition(i).getX();
+ float glyphX = (float)(getGlyphPosition(i).getX());
float glyphY = (float)getGlyphPosition(i).getY() - ascent;
float glyphWidth = glyphMetrics.getHorizontalAdvance();
- if (i < getNumGlyphs()-1) {
- float nextY = (float)getGlyphPosition(i+1).getY() - ascent;
- if (glyphY == nextY) {
- float nextX = (float)getGlyphPosition(i+1).getX();
- glyphWidth = Math.max(glyphWidth, nextX - glyphX);
- }
- }
float glyphHeight = ascent + descent;
+
+ tempLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY,
+ glyphWidth, glyphHeight);
+ if (glyphWidth > maxWidth) maxWidth = glyphWidth;
+ if (glyphHeight > maxHeight) maxHeight = glyphHeight;
+ rotated[i] = false;
- glyphLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY, glyphWidth, glyphHeight);
+ } else { // the glyph is transformed
- } else {
- Shape glyphBounds = glyphMetrics.getBounds2D();
+ Rectangle2D glyphBounds = glyphMetrics.getBounds2D();
AffineTransform tr = AffineTransform.getTranslateInstance(getGlyphPosition(i).getX(),
- getGlyphPosition(i).getY());
+ getGlyphPosition(i).getY());
if (glyphTransform != null) {
tr.concatenate(glyphTransform);
}
- glyphLogicalBounds[i] = tr.createTransformedShape(glyphBounds);
+
+ tempLogicalBounds[i] = tr.createTransformedShape(glyphBounds);
+
+ // store three corner points so we can determine whether the glyph is rotated
+ Point2D p1 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMinY());
+ Point2D p2 = new Point2D.Double(glyphBounds.getMaxX(), glyphBounds.getMinY());
+ Point2D p3 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMaxY());
+
+ Point2D tp1 = new Point2D.Double();
+ Point2D tp2 = new Point2D.Double();
+ Point2D tp3 = new Point2D.Double();
+ tr.transform(p1, tp1);
+ tr.transform(p2, tp2);
+ tr.transform(p3, tp3);
+
+ if ((Math.abs(tp1.getX() - tp2.getX()) < 0.001
+ || Math.abs(tp1.getX() - tp3.getX()) < 0.001)
+ && (Math.abs(tp1.getY() - tp2.getY()) < 0.001
+ || Math.abs(tp1.getY() - tp3.getY()) < 0.001)) {
+ rotated[i] = false;
+ } else {
+ rotated[i] = true;
+ }
+ if (glyphBounds.isEmpty()) {
+ if (i > 0) {
+ // can't tell if rotated or not, make it the same as the
+ // previous glyph
+ rotated[i] = rotated[i-1];
+ } else {
+ rotated[i] = true;
+ }
+ }
+
+ Rectangle2D rectBounds = tempLogicalBounds[i].getBounds2D();
+ if (rectBounds.getWidth() > maxWidth) maxWidth = rectBounds.getWidth();
+ if (rectBounds.getHeight() > maxHeight) maxHeight = rectBounds.getHeight();
}
} else {
// the glyph is not drawn
- glyphLogicalBounds[i] = null;
+ tempLogicalBounds[i] = null;
}
}
+ // if appropriate, join adjacent glyph logical bounds
+ GeneralPath logicalBoundsPath = new GeneralPath();
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ if (tempLogicalBounds[i] != null) {
+ logicalBoundsPath.append(tempLogicalBounds[i], false);
+ }
+ }
+ Rectangle2D fullBounds = logicalBoundsPath.getBounds2D();
+
+ if (fullBounds.getHeight() < maxHeight*1.5) {
+ // make all glyphs tops and bottoms the same as the full bounds
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ // first make sure that the glyph logical bounds are not rotated
+ if (!rotated[i] && tempLogicalBounds[i] != null) {
+ Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D();
+ double x = glyphBounds.getMinX();
+ double width = glyphBounds.getWidth();
+ if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) {
+ // make this glyph extend to the start of the next one
+ Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D();
+ if (nextGlyphBounds.getX() > x) { // going left to right
+ width = nextGlyphBounds.getX() - x;
+ } else {
+ double newGlyphX = nextGlyphBounds.getX() + nextGlyphBounds.getWidth();
+ width += (x - newGlyphX);
+ x = newGlyphX;
+ }
+ }
+ tempLogicalBounds[i] = new Rectangle2D.Double(x, fullBounds.getMinY(),
+ width, fullBounds.getHeight());
+ }
+ }
+ }
+ if (fullBounds.getWidth() < maxWidth*1.5) {
+ // make all glyphs left and right edges the same as the full bounds
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ // first make sure that the glyph logical bounds are not rotated
+ if (!rotated[i] && tempLogicalBounds[i] != null) {
+ Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D();
+ double y = glyphBounds.getMinY();
+ double height = glyphBounds.getHeight();
+ if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) {
+ // make this glyph extend to the start of the next one
+ Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D();
+ if (nextGlyphBounds.getY() > y) { // going top to bottom
+ height = nextGlyphBounds.getY() - y;
+ } else {
+ double newGlyphY = nextGlyphBounds.getY() + nextGlyphBounds.getHeight();
+ height += (y - newGlyphY);
+ y = newGlyphY;
+ }
+ }
+ tempLogicalBounds[i] = new Rectangle2D.Double(fullBounds.getMinX(), y,
+ fullBounds.getWidth(), height);
+ }
+ }
+ }
+
+ for (int i = 0; i < getNumGlyphs(); i++) {
+ glyphLogicalBounds[i] = tempLogicalBounds[i];
+ }
}
/**
---------------------------------------------------------------------
To unsubscribe, e-mail: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org