org.apache.batik.extension.svg.GlyphIterator Maven / Gradle / Ivy
/*
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.
*/
package org.apache.batik.extension.svg;
import java.awt.font.FontRenderContext;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.font.AWTGVTFont;
import org.apache.batik.gvt.font.GVTFont;
import org.apache.batik.gvt.font.GVTGlyphVector;
import org.apache.batik.gvt.font.GVTLineMetrics;
/**
*
* @version $Id: GlyphIterator.java 1733416 2016-03-03 07:07:13Z gadams $
*/
public class GlyphIterator {
public static final AttributedCharacterIterator.Attribute PREFORMATTED
= GVTAttributedCharacterIterator.TextAttribute.PREFORMATTED;
public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK
= GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK;
public static final AttributedCharacterIterator.Attribute TEXT_COMPOUND_ID
= GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_ID;
public static final
AttributedCharacterIterator.Attribute GVT_FONT
= GVTAttributedCharacterIterator.TextAttribute.GVT_FONT;
public static final char SOFT_HYPHEN = 0x00AD;
public static final char ZERO_WIDTH_SPACE = 0x200B;
public static final char ZERO_WIDTH_JOINER = 0x200D;
// Glyph index of current glyph
int idx = -1;
// Glyph index of last 'printing' glyph.
int chIdx = -1;
int lineIdx = -1;
// The ACI index of current glyph.
int aciIdx = -1;
// The number of characters in ACI for current Glyph.
int charCount = -1;
// The total advance for line including last non-space glyph
float adv = 0;
// The total advance for line including spaces at end of line.
float adj = 0;
// The runLimit for current font
int runLimit = 0;
// The runLimit for current line element (need a line break at end).
int lineBreakRunLimit = 0;
int lineBreakCount = 0;
GVTFont font = null;
int fontStart = 0;
float maxAscent = 0;
float maxDescent = 0;
float maxFontSize = 0;
float width = 0;
// The current char (from ACI)
char ch = 0;
// The number of glyphs in gv.
int numGlyphs = 0;
// The AttributedCharacterIterator.
AttributedCharacterIterator aci;
// The GVTGlyphVector for this Text chunk.
GVTGlyphVector gv;
// The GlyphPositions for this GlyphVector (Shared)
float [] gp;
// The font render context for this GylphVector.
FontRenderContext frc;
// The Indexes of new leftShift amounts (soft-hyphens)
int [] leftShiftIdx = null;
// The amount of new leftShifts (soft-hyphen adv)
float [] leftShiftAmt = null;
// The current left shift (inherited from previous line).
int leftShift = 0;
Point2D gvBase = null;
public GlyphIterator(AttributedCharacterIterator aci,
GVTGlyphVector gv) {
this.aci = aci;
this.gv = gv;
this.idx = 0;
this.chIdx = 0;
this.lineIdx = 0;
this.aciIdx = aci.getBeginIndex();
this.charCount = gv.getCharacterCount(idx, idx);
this.ch = aci.first();
this.frc = gv.getFontRenderContext();
this.font = (GVTFont)aci.getAttribute(GVT_FONT);
if (font == null) {
font = new AWTGVTFont(aci.getAttributes());
}
fontStart = aciIdx;
this.maxFontSize = -Float.MAX_VALUE;
this.maxAscent = -Float.MAX_VALUE;
this.maxDescent = -Float.MAX_VALUE;
// Figure out where the font size might change again...
this.runLimit = aci.getRunLimit(TEXT_COMPOUND_ID);
this.lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK);
Object o = aci.getAttribute(FLOW_LINE_BREAK);
this.lineBreakCount = (o == null)?0:1;
this.numGlyphs = gv.getNumGlyphs();
this.gp = gv.getGlyphPositions(0, this.numGlyphs+1, null);
this.gvBase = new Point2D.Float(gp[0], gp[1]);
this.adv = getCharWidth();
this.adj = getCharAdvance();
}
public GlyphIterator(GlyphIterator gi) {
gi.copy(this);
}
public GlyphIterator copy() {
return new GlyphIterator(this);
}
public GlyphIterator copy(GlyphIterator gi) {
if (gi == null)
return new GlyphIterator(this);
gi.idx = this.idx;
gi.chIdx = this.chIdx;
gi.aciIdx = this.aciIdx;
gi.charCount = this.charCount;
gi.adv = this.adv;
gi.adj = this.adj;
gi.runLimit = this.runLimit;
gi.ch = this.ch;
gi.numGlyphs = this.numGlyphs;
gi.gp = this.gp;
gi.gvBase = this.gvBase;
gi.lineBreakRunLimit = this.lineBreakRunLimit;
gi.lineBreakCount = this.lineBreakCount;
gi.frc = this.frc;
gi.font = this.font;
gi.fontStart = this.fontStart;
gi.maxAscent = this.maxAscent;
gi.maxDescent = this.maxDescent;
gi.maxFontSize = this.maxFontSize;
gi.leftShift = this.leftShift;
gi.leftShiftIdx = this.leftShiftIdx;
gi.leftShiftAmt = this.leftShiftAmt;
return gi;
}
/**
* @return The index into glyph vector for current character.
*/
public int getGlyphIndex() { return idx; }
/**
* @return the current character.
*/
public char getChar() { return ch; }
/**
* @return The index into Attributed Character iterator for
* current character.
*/
public int getACIIndex() { return aciIdx; }
/**
* @return The current advance for the line, this is the 'visual width'
* of the current line.
*/
public float getAdv() { return adv; }
/**
* @return The origin of the glyph vector (the point all glyphs are
* layed out with respect to).
*/
public Point2D getOrigin() { return gvBase; }
/**
* @return The current adjustment for the line. This is the ammount
* that needs to be subracted from the following line to get it back
* to the start of the next line.
*/
public float getAdj() { return adj; }
public float getMaxFontSize() {
if (aciIdx >= fontStart) {
int newFS = aciIdx + charCount;
updateLineMetrics(newFS);
fontStart = newFS;
}
return maxFontSize;
}
public float getMaxAscent() {
if (aciIdx >= fontStart) {
int newFS = aciIdx + charCount;
updateLineMetrics(newFS);
fontStart = newFS;
}
return maxAscent;
}
public float getMaxDescent() {
if (aciIdx >= fontStart) {
int newFS = aciIdx + charCount;
updateLineMetrics(newFS);
fontStart = newFS;
}
return maxDescent;
}
public boolean isLastChar() {
return (idx == (numGlyphs-1));
}
public boolean done() {
return (idx >= numGlyphs);
}
public boolean isBreakChar() {
switch (ch) {
case GlyphIterator.ZERO_WIDTH_SPACE: return true;
case GlyphIterator.ZERO_WIDTH_JOINER: return false;
case GlyphIterator.SOFT_HYPHEN: return true;
case ' ': case '\t': return true;
default: return false;
}
}
protected boolean isPrinting(char tstCH) {
switch (ch) {
case GlyphIterator.ZERO_WIDTH_SPACE: return false;
case GlyphIterator.ZERO_WIDTH_JOINER: return false;
case GlyphIterator.SOFT_HYPHEN: return true;
case ' ': case '\t': return false;
default: return true;
}
}
public int getLineBreaks() {
int ret = 0;
if (aciIdx+charCount >= lineBreakRunLimit) {
// Next char is outside this line element so break after
// the current char.
ret = lineBreakCount;
// Update the lineBreakRunLimit, this is a bit tricky since
// The attribute doesn't change until the next glyph...
aci.setIndex(aciIdx+charCount);
lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK);
aci.setIndex(aciIdx); // Restore location...
Object o = aci.getAttribute(FLOW_LINE_BREAK);
lineBreakCount = (o == null)?0:1;
}
return ret;
}
/**
* Move iterator to the next char.
*/
public void nextChar() {
if ((ch == SOFT_HYPHEN) ||
(ch == ZERO_WIDTH_SPACE) ||
(ch == ZERO_WIDTH_JOINER)) {
// Special handling for soft hyphens and zero-width spaces
gv.setGlyphVisible(idx, false);
float chAdv = getCharAdvance();
adj -= chAdv;
addLeftShift(idx, chAdv);
}
aciIdx += charCount;
ch = aci.setIndex(aciIdx);
idx++;
charCount = gv.getCharacterCount(idx,idx);
if (idx == numGlyphs) return;
if (aciIdx >= runLimit) {
updateLineMetrics(aciIdx);
runLimit = aci.getRunLimit(TEXT_COMPOUND_ID);
font = (GVTFont)aci.getAttribute(GVT_FONT);
if (font == null) {
font = new AWTGVTFont(aci.getAttributes());
}
fontStart = aciIdx;
}
float chAdv = getCharAdvance();
adj += chAdv;
if (isPrinting()) {
chIdx = idx;
float chW = getCharWidth();
adv = adj-(chAdv-chW);
}
}
protected void addLeftShift(int idx, float chAdv) {
if (leftShiftIdx == null) {
leftShiftIdx = new int[1];
leftShiftIdx[0] = idx;
leftShiftAmt = new float[1];
leftShiftAmt[0] = chAdv;
} else {
int [] newLeftShiftIdx = new int[leftShiftIdx.length+1];
System.arraycopy( leftShiftIdx, 0, newLeftShiftIdx, 0, leftShiftIdx.length );
newLeftShiftIdx[leftShiftIdx.length] = idx;
leftShiftIdx = newLeftShiftIdx;
float [] newLeftShiftAmt = new float[leftShiftAmt.length+1];
System.arraycopy( leftShiftAmt, 0, newLeftShiftAmt, 0, leftShiftAmt.length );
newLeftShiftAmt[leftShiftAmt.length] = chAdv;
leftShiftAmt = newLeftShiftAmt;
}
}
protected void updateLineMetrics(int end) {
GVTLineMetrics glm = font.getLineMetrics
(aci, fontStart, end, frc);
float ascent = glm.getAscent();
float descent = glm.getDescent();
float fontSz = font.getSize();
if (ascent > maxAscent) maxAscent = ascent;
if (descent > maxDescent) maxDescent = descent;
if (fontSz > maxFontSize) maxFontSize = fontSz;
}
public LineInfo newLine(Point2D.Float loc,
float lineWidth,
boolean partial,
Point2D.Float verticalAlignOffset) {
if (ch == SOFT_HYPHEN) {
gv.setGlyphVisible(idx, true);
}
int lsi = 0;
int nextLSI;
if (leftShiftIdx != null) nextLSI = leftShiftIdx[lsi];
else nextLSI = idx+1;
for (int ci=lineIdx; ci<=idx; ci++) {
if (ci == nextLSI) {
leftShift += leftShiftAmt[lsi++];
if (lsi < leftShiftIdx.length)
nextLSI = leftShiftIdx[lsi];
}
gv.setGlyphPosition(ci, new Point2D.Float(gp[2*ci]-leftShift,
gp[2*ci+1]));
}
leftShiftIdx = null;
leftShiftAmt = null;
float lineInfoChW;
int hideIdx;
// System.out.println("ChIdx: " + chIdx);
if ((chIdx != 0) || (isPrinting())) {
lineInfoChW = getCharWidth(chIdx);
hideIdx = chIdx+1;
} else {
lineInfoChW = 0;
hideIdx = 0;
}
int lineInfoIdx = idx+1;
float lineInfoAdv = adv;
float lineInfoAdj = adj;
while (!done()) {
adv=0;
adj=0;
if ((ch == ZERO_WIDTH_SPACE) ||
(ch == ZERO_WIDTH_JOINER))
gv.setGlyphVisible(idx, false);
ch = 0; // Disable soft-hyphen/ZWS advance adjustment.
nextChar();
if (isPrinting()) break;
lineInfoIdx = idx+1;
lineInfoAdj += adj;
}
// hide trailing spaces if any
for (int i = hideIdx; i
© 2015 - 2024 Weber Informatics LLC | Privacy Policy