![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.fop.svg.font.FOPGVTGlyphVector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.fop Show documentation
Show all versions of org.apache.fop Show documentation
The core maven build properties
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id: FOPGVTGlyphVector.java 1831346 2018-05-10 14:30:38Z matthias $ */
package org.apache.fop.svg.font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphJustificationInfo;
import java.awt.font.GlyphMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Arrays;
import java.util.List;
import org.apache.batik.gvt.font.GVTFont;
import org.apache.batik.gvt.font.GVTGlyphMetrics;
import org.apache.batik.gvt.font.GVTGlyphVector;
import org.apache.batik.gvt.font.GVTLineMetrics;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.fonts.GlyphMapping;
import org.apache.fop.fonts.TextFragment;
import org.apache.fop.traits.MinOptMax;
public class FOPGVTGlyphVector implements GVTGlyphVector {
protected final TextFragment text;
protected final FOPGVTFont font;
private final int fontSize;
private final FontMetrics fontMetrics;
private final FontRenderContext frc;
protected int[] glyphs;
protected List associations;
protected int[][] gposAdjustments;
protected float[] positions;
protected Rectangle2D[] boundingBoxes;
protected GeneralPath outline;
protected AffineTransform[] glyphTransforms;
protected boolean[] glyphVisibilities;
protected Rectangle2D logicalBounds;
FOPGVTGlyphVector(FOPGVTFont font, final CharacterIterator iter, FontRenderContext frc) {
this.text = new SVGTextFragment(iter);
this.font = font;
Font f = font.getFont();
this.fontSize = f.getFontSize();
this.fontMetrics = f.getFontMetrics();
this.frc = frc;
}
public void performDefaultLayout() {
Font f = font.getFont();
MinOptMax letterSpaceIPD = MinOptMax.ZERO;
MinOptMax[] letterSpaceAdjustments = new MinOptMax[text.getEndIndex()];
boolean retainControls = false;
GlyphMapping mapping = GlyphMapping.doGlyphMapping(text, text.getBeginIndex(), text.getEndIndex(),
f, letterSpaceIPD, letterSpaceAdjustments, '\0', '\0',
false, text.getBidiLevel(), true, true, retainControls);
CharacterIterator glyphAsCharIter =
mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : text.getIterator();
this.glyphs = buildGlyphs(f, glyphAsCharIter);
this.associations = mapping.associations;
this.gposAdjustments = mapping.gposAdjustments;
if (text.getBeginIndex() > 0) {
int arrlen = text.getEndIndex() - text.getBeginIndex();
MinOptMax[] letterSpaceAdjustmentsNew = new MinOptMax[arrlen];
System.arraycopy(letterSpaceAdjustments, text.getBeginIndex(), letterSpaceAdjustmentsNew,
0, arrlen);
letterSpaceAdjustments = letterSpaceAdjustmentsNew;
}
this.positions = buildGlyphPositions(glyphAsCharIter, mapping.gposAdjustments, letterSpaceAdjustments);
this.glyphVisibilities = new boolean[this.glyphs.length];
Arrays.fill(glyphVisibilities, true);
this.glyphTransforms = new AffineTransform[this.glyphs.length];
}
private static class SVGTextFragment implements TextFragment {
private final CharacterIterator charIter;
private String script;
private String language;
private int level = -1;
SVGTextFragment(CharacterIterator charIter) {
this.charIter = charIter;
if (charIter instanceof AttributedCharacterIterator) {
AttributedCharacterIterator aci = (AttributedCharacterIterator) charIter;
aci.first();
this.script = (String) aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.SCRIPT);
this.language = (String) aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.LANGUAGE);
Integer level = (Integer) aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL);
if (level != null) {
this.level = level;
}
}
}
public CharacterIterator getIterator() {
return charIter;
}
public int getBeginIndex() {
return charIter.getBeginIndex();
}
public int getEndIndex() {
return charIter.getEndIndex();
}
// TODO - [GA] the following appears to be broken because it ignores
// sttart and end index arguments
public CharSequence subSequence(int startIndex, int endIndex) {
StringBuilder sb = new StringBuilder();
for (char c = charIter.first(); c != CharacterIterator.DONE; c = charIter.next()) {
sb.append(c);
}
return sb.toString();
}
public String getScript() {
if (script != null) {
return script;
} else {
return "auto";
}
}
public String getLanguage() {
if (language != null) {
return language;
} else {
return "none";
}
}
public int getBidiLevel() {
return level;
}
public char charAt(int index) {
return charIter.setIndex(index - charIter.getBeginIndex());
}
}
private int[] buildGlyphs(Font font, final CharacterIterator glyphAsCharIter) {
int[] glyphs = new int[glyphAsCharIter.getEndIndex() - glyphAsCharIter.getBeginIndex()];
int index = 0;
for (char c = glyphAsCharIter.first(); c != CharacterIterator.DONE; c = glyphAsCharIter.next()) {
glyphs[index] = font.mapChar(c);
index++;
}
return glyphs;
}
private static final int[] PA_ZERO = new int[4];
/**
* Build glyph position array.
* @param glyphAsCharIter iterator for mapped glyphs as char codes (not glyph codes)
* @param dp optionally null glyph position adjustments array
* @param lsa optionally null letter space adjustments array
* @return array of floats that denote [X,Y] position pairs for each glyph including
* including an implied subsequent glyph; i.e., returned array contains one more pair
* than the numbers of glyphs, where the position denoted by this last pair represents
* the position after the last glyph has incurred advancement
*/
private float[] buildGlyphPositions(final CharacterIterator glyphAsCharIter, int[][] dp, MinOptMax[] lsa) {
int numGlyphs = glyphAsCharIter.getEndIndex() - glyphAsCharIter.getBeginIndex();
float[] positions = new float[2 * (numGlyphs + 1)];
float xc = 0f;
float yc = 0f;
if (dp != null) {
for (int i = 0; i < numGlyphs + 1; ++i) {
int[] pa = ((i >= dp.length) || (dp[i] == null)) ? PA_ZERO : dp[i];
float xo = xc + ((float) pa[0]) / 1000f;
float yo = yc - ((float) pa[1]) / 1000f;
float xa = getGlyphWidth(i) + ((float) pa[2]) / 1000f;
float ya = ((float) pa[3]) / 1000f;
int k = 2 * i;
positions[k + 0] = xo;
positions[k + 1] = yo;
xc += xa;
yc += ya;
}
} else if (lsa != null) {
for (int i = 0; i < numGlyphs + 1; ++i) {
MinOptMax sa = (((i + 1) >= lsa.length) || (lsa[i + 1] == null)) ? MinOptMax.ZERO : lsa[i + 1];
float xo = xc;
float yo = yc;
float xa = getGlyphWidth(i) + sa.getOpt() / 1000f;
float ya = 0;
int k = 2 * i;
positions[k + 0] = xo;
positions[k + 1] = yo;
xc += xa;
yc += ya;
}
}
return positions;
}
private float getGlyphWidth(int index) {
if (index < glyphs.length) {
return fontMetrics.getWidth(glyphs[index], fontSize) / 1000000f;
} else {
return 0f;
}
}
public GVTFont getFont() {
return font;
}
public FontRenderContext getFontRenderContext() {
return frc;
}
public void setGlyphCode(int glyphIndex, int glyphCode) {
glyphs[glyphIndex] = glyphCode;
}
public int getGlyphCode(int glyphIndex) {
return glyphs[glyphIndex];
}
public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
int[] codeReturn) {
if (codeReturn == null) {
codeReturn = new int[numEntries];
}
System.arraycopy(glyphs, beginGlyphIndex, codeReturn, 0, numEntries);
return codeReturn;
}
public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public Shape getGlyphLogicalBounds(int glyphIndex) {
GVTGlyphMetrics metrics = getGlyphMetrics(glyphIndex);
Point2D pos = getGlyphPosition(glyphIndex);
GVTLineMetrics fontMetrics = font.getLineMetrics(0);
Rectangle2D bounds = new Rectangle2D.Float(0, -fontMetrics.getDescent(), metrics.getHorizontalAdvance(),
fontMetrics.getAscent() + fontMetrics.getDescent());
AffineTransform t = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
AffineTransform transf = getGlyphTransform(glyphIndex);
if (transf != null) {
t.concatenate(transf);
}
t.scale(1, -1); // Translate from glyph coordinate system to user
return t.createTransformedShape(bounds);
}
public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) {
Rectangle2D bbox = getBoundingBoxes()[glyphIndex];
return new GVTGlyphMetrics(positions[2 * (glyphIndex + 1)] - positions[2 * glyphIndex],
(fontMetrics.getAscender(fontSize) - fontMetrics.getDescender(fontSize)) / 1000000f,
bbox, GlyphMetrics.STANDARD);
}
public Shape getGlyphOutline(int glyphIndex) {
Shape glyphBox = getBoundingBoxes()[glyphIndex];
AffineTransform tr = AffineTransform.getTranslateInstance(positions[glyphIndex * 2],
positions[glyphIndex * 2 + 1]);
AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
if (glyphTransform != null) {
tr.concatenate(glyphTransform);
}
return tr.createTransformedShape(glyphBox);
}
public Rectangle2D getGlyphCellBounds(int glyphIndex) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public int[][] getGlyphPositionAdjustments() {
return gposAdjustments;
}
public Point2D getGlyphPosition(int glyphIndex) {
int positionIndex = glyphIndex * 2;
return new Point2D.Float(positions[positionIndex], positions[positionIndex + 1]);
}
public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, float[] positionReturn) {
if (positionReturn == null) {
positionReturn = new float[numEntries * 2];
}
System.arraycopy(positions, beginGlyphIndex * 2, positionReturn, 0, numEntries * 2);
return positionReturn;
}
public AffineTransform getGlyphTransform(int glyphIndex) {
return glyphTransforms[glyphIndex];
}
public Shape getGlyphVisualBounds(int glyphIndex) {
Rectangle2D bbox = getBoundingBoxes()[glyphIndex];
Point2D pos = getGlyphPosition(glyphIndex);
AffineTransform t = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
AffineTransform transf = getGlyphTransform(glyphIndex);
if (transf != null) {
t.concatenate(transf);
}
return t.createTransformedShape(bbox);
}
public Rectangle2D getLogicalBounds() {
if (logicalBounds == null) {
GeneralPath logicalBoundsPath = new GeneralPath();
for (int i = 0; i < getNumGlyphs(); i++) {
Shape glyphLogicalBounds = getGlyphLogicalBounds(i);
logicalBoundsPath.append(glyphLogicalBounds, false);
}
logicalBounds = logicalBoundsPath.getBounds2D();
}
return logicalBounds;
}
public int getNumGlyphs() {
return glyphs.length;
}
public Shape getOutline() {
if (outline == null) {
outline = new GeneralPath();
for (int i = 0; i < glyphs.length; i++) {
outline.append(getGlyphOutline(i), false);
}
}
return outline;
}
public Shape getOutline(float x, float y) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public Rectangle2D getGeometricBounds() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public Rectangle2D getBounds2D(AttributedCharacterIterator aci) {
return getOutline().getBounds2D();
}
public void setGlyphPosition(int glyphIndex, Point2D newPos) {
int idx = glyphIndex * 2;
positions[idx] = (float) newPos.getX();
positions[idx + 1] = (float) newPos.getY();
}
public void setGlyphTransform(int glyphIndex, AffineTransform newTX) {
glyphTransforms[glyphIndex] = newTX;
}
public void setGlyphVisible(int glyphIndex, boolean visible) {
glyphVisibilities[glyphIndex] = visible;
}
public boolean isGlyphVisible(int glyphIndex) {
return glyphVisibilities[glyphIndex];
}
public int getCharacterCount(int startGlyphIndex, int endGlyphIndex) {
// TODO Not that simple if complex scripts are involved
return endGlyphIndex - startGlyphIndex + 1;
}
public boolean isReversed() {
return false;
}
public void maybeReverse(boolean mirror) {
}
public void draw(Graphics2D graphics2d, AttributedCharacterIterator aci) {
// NOP
}
private Rectangle2D[] getBoundingBoxes() {
if (boundingBoxes == null) {
buildBoundingBoxes();
}
return boundingBoxes;
}
private void buildBoundingBoxes() {
boundingBoxes = new Rectangle2D[glyphs.length];
for (int i = 0; i < glyphs.length; i++) {
Rectangle bbox = fontMetrics.getBoundingBox(glyphs[i], fontSize);
boundingBoxes[i] = new Rectangle2D.Float(bbox.x / 1000000f, -(bbox.y + bbox.height) / 1000000f,
bbox.width / 1000000f, bbox.height / 1000000f);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy