You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FOPGVTGlyphVector.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.svg.font;
  19. import java.awt.Graphics2D;
  20. import java.awt.Rectangle;
  21. import java.awt.Shape;
  22. import java.awt.font.FontRenderContext;
  23. import java.awt.font.GlyphJustificationInfo;
  24. import java.awt.font.GlyphMetrics;
  25. import java.awt.geom.AffineTransform;
  26. import java.awt.geom.GeneralPath;
  27. import java.awt.geom.Point2D;
  28. import java.awt.geom.Rectangle2D;
  29. import java.text.AttributedCharacterIterator;
  30. import java.text.CharacterIterator;
  31. import java.text.StringCharacterIterator;
  32. import java.util.Arrays;
  33. import org.apache.batik.gvt.font.GVTFont;
  34. import org.apache.batik.gvt.font.GVTGlyphMetrics;
  35. import org.apache.batik.gvt.font.GVTGlyphVector;
  36. import org.apache.batik.gvt.font.GVTLineMetrics;
  37. import org.apache.fop.fonts.Font;
  38. import org.apache.fop.fonts.FontMetrics;
  39. import org.apache.fop.fonts.GlyphMapping;
  40. import org.apache.fop.fonts.TextFragment;
  41. import org.apache.fop.traits.MinOptMax;
  42. class FOPGVTGlyphVector implements GVTGlyphVector {
  43. private final CharacterIterator charIter;
  44. private final FOPGVTFont font;
  45. private final int fontSize;
  46. private final FontMetrics fontMetrics;
  47. private final FontRenderContext frc;
  48. private int[] glyphs;
  49. private float[] positions;
  50. private Rectangle2D[] boundingBoxes;
  51. private GeneralPath outline;
  52. private AffineTransform[] glyphTransforms;
  53. private boolean[] glyphVisibilities;
  54. private Rectangle2D logicalBounds;
  55. FOPGVTGlyphVector(FOPGVTFont font, final CharacterIterator iter, FontRenderContext frc) {
  56. this.charIter = iter;
  57. this.font = font;
  58. Font f = font.getFont();
  59. this.fontSize = f.getFontSize();
  60. this.fontMetrics = f.getFontMetrics();
  61. this.frc = frc;
  62. }
  63. public void performDefaultLayout() {
  64. Font f = font.getFont();
  65. TextFragment text = new SVGTextFragment(charIter);
  66. MinOptMax letterSpaceIPD = MinOptMax.ZERO;
  67. MinOptMax[] letterSpaceAdjustments = new MinOptMax[charIter.getEndIndex() - charIter.getBeginIndex()];
  68. GlyphMapping mapping = GlyphMapping.doGlyphMapping(text, charIter.getBeginIndex(), charIter.getEndIndex(),
  69. f, letterSpaceIPD, letterSpaceAdjustments, '\0', '\0', false, 0 /* TODO */);
  70. glyphs = buildGlyphs(f, mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : charIter);
  71. buildGlyphPositions(mapping, letterSpaceAdjustments);
  72. this.glyphVisibilities = new boolean[this.glyphs.length];
  73. Arrays.fill(glyphVisibilities, true);
  74. this.glyphTransforms = new AffineTransform[this.glyphs.length];
  75. }
  76. private static class SVGTextFragment implements TextFragment {
  77. private final CharacterIterator charIter;
  78. SVGTextFragment(CharacterIterator charIter) {
  79. this.charIter = charIter;
  80. }
  81. public CharSequence subSequence(int startIndex, int endIndex) {
  82. StringBuilder sb = new StringBuilder();
  83. for (char c = charIter.first(); c != CharacterIterator.DONE; c = charIter.next()) {
  84. sb.append(c);
  85. }
  86. return sb.toString();
  87. }
  88. public String getScript() {
  89. return "DFLT"; // TODO pass on script value from SVG
  90. }
  91. public String getLanguage() {
  92. return "dflt"; // TODO pass on language value from SVG
  93. }
  94. public char charAt(int index) {
  95. return charIter.setIndex(index - charIter.getBeginIndex());
  96. }
  97. }
  98. private int[] buildGlyphs(Font font, final CharacterIterator charIter) {
  99. int[] glyphs = new int[charIter.getEndIndex() - charIter.getBeginIndex()];
  100. int index = 0;
  101. for (char c = charIter.first(); c != CharacterIterator.DONE; c = charIter.next()) {
  102. glyphs[index] = font.mapChar(c);
  103. index++;
  104. }
  105. return glyphs;
  106. }
  107. private void buildGlyphPositions(GlyphMapping ai, MinOptMax[] letterSpaceAdjustments) {
  108. positions = new float[2 * glyphs.length + 2];
  109. if (ai.gposAdjustments != null) {
  110. assert ai.gposAdjustments.length == glyphs.length;
  111. for (int glyphIndex = 0; glyphIndex < glyphs.length; glyphIndex++) {
  112. int n = 2 * glyphIndex;
  113. if (ai.gposAdjustments[glyphIndex] != null) {
  114. for (int p = 0; p < 4; p++) {
  115. positions[n + p] += ai.gposAdjustments[glyphIndex][p] / 1000f;
  116. }
  117. }
  118. positions[n + 2] += positions[n] + getGlyphWidth(glyphIndex);
  119. }
  120. } else {
  121. for (int i = 0, n = 2; i < glyphs.length; i++, n += 2) {
  122. int kern = i < glyphs.length - 1 && letterSpaceAdjustments[i + 1] != null
  123. ? letterSpaceAdjustments[i + 1].getOpt()
  124. : 0;
  125. positions[n] = positions[n - 2] + getGlyphWidth(i) + kern / 1000f;
  126. positions[n + 1] = 0;
  127. }
  128. }
  129. }
  130. private float getGlyphWidth(int index) {
  131. return fontMetrics.getWidth(glyphs[index], fontSize) / 1000000f;
  132. }
  133. public GVTFont getFont() {
  134. return font;
  135. }
  136. public FontRenderContext getFontRenderContext() {
  137. return frc;
  138. }
  139. public int getGlyphCode(int glyphIndex) {
  140. return glyphs[glyphIndex];
  141. }
  142. public int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
  143. int[] codeReturn) {
  144. if (codeReturn == null) {
  145. codeReturn = new int[numEntries];
  146. }
  147. System.arraycopy(glyphs, beginGlyphIndex, codeReturn, 0, numEntries);
  148. return codeReturn;
  149. }
  150. public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) {
  151. // TODO Auto-generated method stub
  152. throw new UnsupportedOperationException();
  153. }
  154. public Shape getGlyphLogicalBounds(int glyphIndex) {
  155. GVTGlyphMetrics metrics = getGlyphMetrics(glyphIndex);
  156. Point2D pos = getGlyphPosition(glyphIndex);
  157. GVTLineMetrics fontMetrics = font.getLineMetrics(0);
  158. Rectangle2D bounds = new Rectangle2D.Float(0, -fontMetrics.getDescent(), metrics.getHorizontalAdvance(),
  159. fontMetrics.getAscent() + fontMetrics.getDescent());
  160. AffineTransform t = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
  161. AffineTransform transf = getGlyphTransform(glyphIndex);
  162. if (transf != null) {
  163. t.concatenate(transf);
  164. }
  165. t.scale(1, -1); // Translate from glyph coordinate system to user
  166. return t.createTransformedShape(bounds);
  167. }
  168. public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) {
  169. Rectangle2D bbox = getBoundingBoxes()[glyphIndex];
  170. return new GVTGlyphMetrics(positions[2 * (glyphIndex + 1)] - positions[2 * glyphIndex],
  171. (fontMetrics.getAscender(fontSize) - fontMetrics.getDescender(fontSize)) / 1000000f,
  172. bbox, GlyphMetrics.STANDARD);
  173. }
  174. public Shape getGlyphOutline(int glyphIndex) {
  175. Shape glyphBox = getBoundingBoxes()[glyphIndex];
  176. AffineTransform tr = AffineTransform.getTranslateInstance(positions[glyphIndex * 2],
  177. positions[glyphIndex * 2 + 1]);
  178. AffineTransform glyphTransform = getGlyphTransform(glyphIndex);
  179. if (glyphTransform != null) {
  180. tr.concatenate(glyphTransform);
  181. }
  182. return tr.createTransformedShape(glyphBox);
  183. }
  184. public Rectangle2D getGlyphCellBounds(int glyphIndex) {
  185. // TODO Auto-generated method stub
  186. throw new UnsupportedOperationException();
  187. }
  188. public Point2D getGlyphPosition(int glyphIndex) {
  189. int positionIndex = glyphIndex * 2;
  190. return new Point2D.Float(positions[positionIndex], positions[positionIndex + 1]);
  191. }
  192. public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, float[] positionReturn) {
  193. if (positionReturn == null) {
  194. positionReturn = new float[numEntries * 2];
  195. }
  196. System.arraycopy(positions, beginGlyphIndex * 2, positionReturn, 0, numEntries * 2);
  197. return positionReturn;
  198. }
  199. public AffineTransform getGlyphTransform(int glyphIndex) {
  200. return glyphTransforms[glyphIndex];
  201. }
  202. public Shape getGlyphVisualBounds(int glyphIndex) {
  203. Rectangle2D bbox = getBoundingBoxes()[glyphIndex];
  204. Point2D pos = getGlyphPosition(glyphIndex);
  205. AffineTransform t = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
  206. AffineTransform transf = getGlyphTransform(glyphIndex);
  207. if (transf != null) {
  208. t.concatenate(transf);
  209. }
  210. return t.createTransformedShape(bbox);
  211. }
  212. public Rectangle2D getLogicalBounds() {
  213. if (logicalBounds == null) {
  214. GeneralPath logicalBoundsPath = new GeneralPath();
  215. for (int i = 0; i < getNumGlyphs(); i++) {
  216. Shape glyphLogicalBounds = getGlyphLogicalBounds(i);
  217. logicalBoundsPath.append(glyphLogicalBounds, false);
  218. }
  219. logicalBounds = logicalBoundsPath.getBounds2D();
  220. }
  221. return logicalBounds;
  222. }
  223. public int getNumGlyphs() {
  224. return glyphs.length;
  225. }
  226. public Shape getOutline() {
  227. if (outline == null) {
  228. outline = new GeneralPath();
  229. for (int i = 0; i < glyphs.length; i++) {
  230. outline.append(getGlyphOutline(i), false);
  231. }
  232. }
  233. return outline;
  234. }
  235. public Shape getOutline(float x, float y) {
  236. // TODO Auto-generated method stub
  237. throw new UnsupportedOperationException();
  238. }
  239. public Rectangle2D getGeometricBounds() {
  240. // TODO Auto-generated method stub
  241. throw new UnsupportedOperationException();
  242. }
  243. public Rectangle2D getBounds2D(AttributedCharacterIterator aci) {
  244. return getOutline().getBounds2D();
  245. }
  246. public void setGlyphPosition(int glyphIndex, Point2D newPos) {
  247. int idx = glyphIndex * 2;
  248. positions[idx] = (float) newPos.getX();
  249. positions[idx + 1] = (float) newPos.getY();
  250. }
  251. public void setGlyphTransform(int glyphIndex, AffineTransform newTX) {
  252. glyphTransforms[glyphIndex] = newTX;
  253. }
  254. public void setGlyphVisible(int glyphIndex, boolean visible) {
  255. glyphVisibilities[glyphIndex] = visible;
  256. }
  257. public boolean isGlyphVisible(int glyphIndex) {
  258. return glyphVisibilities[glyphIndex];
  259. }
  260. public int getCharacterCount(int startGlyphIndex, int endGlyphIndex) {
  261. // TODO Not that simple if complex scripts are involved
  262. return endGlyphIndex - startGlyphIndex + 1;
  263. }
  264. public void draw(Graphics2D graphics2d, AttributedCharacterIterator aci) {
  265. // NOP
  266. }
  267. private Rectangle2D[] getBoundingBoxes() {
  268. if (boundingBoxes == null) {
  269. buildBoundingBoxes();
  270. }
  271. return boundingBoxes;
  272. }
  273. private void buildBoundingBoxes() {
  274. boundingBoxes = new Rectangle2D[glyphs.length];
  275. for (int i = 0; i < glyphs.length; i++) {
  276. Rectangle bbox = fontMetrics.getBoundingBox(glyphs[i], fontSize);
  277. boundingBoxes[i] = new Rectangle2D.Float(bbox.x / 1000000f, -(bbox.y + bbox.height) / 1000000f,
  278. bbox.width / 1000000f, bbox.height / 1000000f);
  279. }
  280. }
  281. }