964e9790220e9d45c9bacd00a4db6e6f79ed8f80
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / util / TextObjectSpriteDrawer.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2010 - DIGITEO - Pierre Lando
4  * Copyright (C) 2012 - Scilab Enterprises - Bruno JOFRET
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  */
12
13 package org.scilab.modules.renderer.JoGLView.util;
14
15 import java.awt.Dimension;
16 import java.awt.Font;
17 import java.awt.font.TextLayout;
18 import java.awt.geom.Rectangle2D;
19
20 import javax.swing.Icon;
21
22 import org.scilab.forge.jlatexmath.TeXConstants;
23 import org.scilab.forge.jlatexmath.TeXFormula;
24 import org.scilab.forge.jlatexmath.TeXIcon;
25 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
26 import org.scilab.forge.scirenderer.shapes.appearance.Color;
27 import org.scilab.forge.scirenderer.texture.TextEntity;
28 import org.scilab.forge.scirenderer.texture.TextureDrawer;
29 import org.scilab.forge.scirenderer.texture.TextureDrawingTools;
30 import org.scilab.modules.console.utils.ScilabSpecialTextUtilities;
31 import org.scilab.modules.graphic_objects.figure.ColorMap;
32 import org.scilab.modules.graphic_objects.textObject.Text;
33 import org.scilab.modules.graphic_objects.textObject.TextObject;
34 import org.scilab.modules.jvm.LoadClassPath;
35 import org.scilab.modules.renderer.utils.textRendering.FontManager;
36
37 /**
38  * A {@see SpriteDrawer} who draw a Scilab {@see Text} object.
39  * @author Pierre Lando
40  */
41 public class TextObjectSpriteDrawer implements TextureDrawer {
42
43     /**
44      * Scilab text margin.
45      */
46     private static final int HMARGIN = 2;
47     private static final int VMARGIN = 2;
48     private static final int SPACEWIDTH = (int) Math.ceil(new TextEntity("_").getSize().getWidth()) - 2;
49
50     private Appearance appearance;
51     private int thickness;
52     private final Object[][] entities;
53     private float alignmentFactor;
54
55     private final int[] lineHeight;
56     private final int[] columnWidth;
57     private final float[] lineAscent;
58
59     private final int width;
60     private final int height;
61
62     private boolean latexSet = false;
63     private boolean mathmlSet = false;
64
65     /**
66      * Default constructor.
67      * @param colorMap the color map to use.
68      * @param textObject the scilab {@see Text} to draw.
69      */
70     public TextObjectSpriteDrawer(final ColorMap colorMap, final TextObject textObject) {
71         String[][] stringArray = computeTextData(textObject);
72         int columnNumber = -1;
73         for (String[] stringLine : stringArray) {
74             columnNumber = Math.max(stringLine.length, columnNumber);
75         }
76         int lineNumber = stringArray.length;
77
78         this.lineHeight = new int[lineNumber];
79         this.lineAscent = new float[lineNumber];
80         this.columnWidth = new int[columnNumber];
81         this.entities = new Object[columnNumber][lineNumber];
82
83         boolean fractionalFont = textObject.getFontFractional();
84         Color textColor = ColorFactory.createColor(colorMap, textObject.getFont().getColor());
85         Font font = computeFont(textObject);
86
87         loop: {
88             for (String[] textLine : stringArray) {
89                 for (String text : textLine) {
90                     if (text != null) {
91                         if (!latexSet && isLatex(text)) {
92                             latexSet = true;
93                             LoadClassPath.loadOnUse("graphics_latex_textrendering");
94                         } else if (!mathmlSet && isMathML(text)) {
95                             LoadClassPath.loadOnUse("graphics_mathml_textrendering");
96                         } else if (latexSet && mathmlSet) {
97                             break loop;
98                         }
99                     }
100                 }
101             }
102         }
103
104         fillEntityMatrix(stringArray, fractionalFont, textColor, font);
105
106         this.width  = sum(columnWidth) + HMARGIN * (columnNumber + 1) + 2 * thickness + SPACEWIDTH * (columnNumber - 1);
107         this.height = sum(lineHeight)  + VMARGIN * (lineNumber + 1) + 2 * thickness;
108     }
109
110     /**
111      * Constructor.
112      * Specifies a scale factor used to scale the text matrix.
113      * @param colorMap the color map to used.
114      * @param textObject the scilab {@see TextObject} to draw.
115      * @param scaleFactor the scale factor to apply.
116      */
117     public TextObjectSpriteDrawer(final ColorMap colorMap, final TextObject textObject, double scaleFactor) {
118         String[][] stringArray = computeTextData(textObject);
119         int columnNumber = -1;
120         for (String[] stringLine : stringArray) {
121             columnNumber = Math.max(stringLine.length, columnNumber);
122         }
123         int lineNumber = stringArray.length;
124
125         this.lineHeight = new int[lineNumber];
126         this.lineAscent = new float[lineNumber];
127         this.columnWidth = new int[columnNumber];
128         this.entities = new Object[columnNumber][lineNumber];
129
130         boolean fractionalFont = textObject.getFontFractional();
131         Color textColor = ColorFactory.createColor(colorMap, textObject.getFont().getColor());
132         Font font = computeFont(textObject, scaleFactor);
133
134         /* Fill the entity matrix */
135         fillEntityMatrix(stringArray, fractionalFont, textColor, font);
136
137         this.width  = (int)((double)sum(columnWidth) + scaleFactor * (double)(HMARGIN * (columnNumber + 1)) + 2 * thickness + scaleFactor * (double)(SPACEWIDTH * (columnNumber - 1)));
138         this.height = (int)((double)sum(lineHeight)  + scaleFactor * (double)(VMARGIN * (lineNumber + 1)) + 2 * thickness);
139     }
140
141     /**
142      * Fills the entity matrix
143      * @param stringArray the matrix of text strings used to fill the entity matrix.
144      * @param fractionalFont specifies whether a fractional font is used or not.
145      * @param textColor the text color.
146      * @param font the font to use.
147      */
148     protected void fillEntityMatrix(String[][] stringArray, boolean fractionalFont, Color textColor, Font font) {
149         int line = 0;
150         for (String[] textLine : stringArray) {
151             int column = 0;
152             for (String text : textLine) {
153                 if (text != null) {
154                     Dimension dimension = null;
155                     Icon icon = null;
156                     float ascent = 0;
157                     if (isLatex(text)) {
158                         try {
159                             TeXFormula formula = new TeXFormula(text.substring(1, text.length() - 1));
160                             formula.setColor(textColor);
161                             icon = formula.createTeXIcon(TeXConstants.STYLE_DISPLAY, font.getSize());
162                             ascent = ((TeXIcon) icon).getIconHeight() - ((TeXIcon) icon).getIconDepth();
163                         } catch (Exception e) { }
164                     } else if (isMathML(text)) {
165                         try {
166                             icon = ScilabSpecialTextUtilities.compileMathMLExpression(text, font.getSize(), textColor);
167                             ScilabSpecialTextUtilities.SpecialIcon si = (ScilabSpecialTextUtilities.SpecialIcon) icon;
168                             ascent = si.getIconHeight() - si.getIconDepth();
169                         } catch (Exception e) { }
170                     }
171
172                     if (icon != null) {
173                         dimension = new Dimension(icon.getIconWidth(), icon.getIconHeight());
174                         entities[column][line] = icon;
175                     } else {
176                         TextEntity textEntity = new TextEntity(text);
177                         textEntity.setTextUseFractionalMetrics(fractionalFont);
178                         textEntity.setTextAntiAliased(true);
179                         textEntity.setTextColor(textColor);
180                         textEntity.setFont(font);
181                         entities[column][line] = textEntity;
182                         dimension = textEntity.getSize();
183                         ascent = textEntity.isValid() ? textEntity.getLayout().getAscent() : 0;
184                     }
185
186                     lineAscent[line] = Math.max(lineAscent[line], ascent);
187
188                     if (dimension != null) {
189                         columnWidth[column] = Math.max(columnWidth[column], dimension.width);
190                         lineHeight[line] = Math.max(lineHeight[line], dimension.height);
191                     }
192                 }
193                 column++;
194             }
195             line++;
196         }
197     }
198
199     /**
200      * Return true if the given string represent a latex entity.
201      * @param string the given string.
202      * @return true if the given string represent a latex entity.
203      */
204     private boolean isLatex(String string) {
205         return (string.length() >= 2) && string.endsWith("$") && string.startsWith("$");
206     }
207
208     /**
209      * Return true if the given string represent a MathML entity.
210      * @param string the given string.
211      * @return true if the given string represent a MathML entity.
212      */
213     private boolean isMathML(String string) {
214         return (string.length() >= 2) && string.endsWith(">") && string.startsWith("<");
215     }
216
217     @Override
218     public void draw(TextureDrawingTools drawingTools) {
219         // Draw background.
220         if (appearance.getFillColor().getAlphaAsFloat() != 0) {
221             drawingTools.clear(appearance.getFillColor());
222         }
223
224         final int currentHMargin = getHMargin();
225         final int currentVMargin = getVMargin();
226         final int currentSpaceWidth = getSpaceWidth();
227
228         // Draw text.
229         int x = currentHMargin + thickness;
230         int column = 0;
231         for (Object[] entitiesLine : entities) {
232             int y = currentVMargin + thickness;
233             int line = 0;
234             for (Object entity : entitiesLine) {
235                 if (entity != null) {
236                     if (entity instanceof TextEntity) {
237                         TextEntity textEntity = (TextEntity) entity;
238                         float ascent = textEntity.isValid() ? textEntity.getLayout().getAscent() : 0.f;
239                         double deltaX = alignmentFactor * (columnWidth[column] - textEntity.getSize().getWidth());
240                         drawingTools.draw(textEntity, (int) (x + deltaX), Math.round(y - ascent + lineAscent[line]));
241                         y += lineHeight[line] + currentVMargin;
242                         line++;
243                     } else if (entity instanceof Icon) {
244                         Icon icon = (Icon) entity;
245                         double deltaX = alignmentFactor * (columnWidth[column] - icon.getIconWidth());
246                         if (latexSet && (icon instanceof TeXIcon)) {
247                             TeXIcon tex = (TeXIcon) icon;
248                             float ascent = tex.getIconHeight() - tex.getIconDepth();
249                             drawingTools.draw(icon, (int) (x + deltaX), Math.round(y - ascent + lineAscent[line]));
250                         } else {
251                             // MathML
252                             ScilabSpecialTextUtilities.SpecialIcon si = (ScilabSpecialTextUtilities.SpecialIcon) icon;
253                             int ascent = si.getIconHeight() - si.getIconDepth();
254                             drawingTools.draw(icon, (int) (x + deltaX), y - ascent + Math.round(lineAscent[line]));
255                         }
256                         y += lineHeight[line] + currentVMargin;
257                         line++;
258                     }
259                 }
260             }
261             x += columnWidth[column] + currentHMargin + currentSpaceWidth;
262             column++;
263         }
264
265         // Draw border lines.
266         if (appearance.getLineWidth() > 0) {
267             float hlw = appearance.getLineWidth() / 2;
268             int x1 = (int) hlw;
269             int y1 = (int) hlw;
270             int x2 = (int) (width - hlw);
271             int y2 = (int) (height - hlw);
272             drawingTools.drawPolyline(new int[] {x1, y1, x2, y1, x2, y2, x1, y2, x1, y1}, appearance);
273         }
274     }
275
276     @Override
277     public OriginPosition getOriginPosition() {
278         return OriginPosition.UPPER_LEFT;
279     }
280
281     @Override
282     public Dimension getTextureSize() {
283         return new Dimension(width, height);
284     }
285
286     /**
287      * Return the sprite width needed by this drawer.
288      * @return the sprite width needed by this drawer.
289      */
290     public int getWidth() {
291         return width;
292     }
293
294     /**
295      * Return the sprite height needed by this drawer.
296      * @return the sprite height needed by this drawer.
297      */
298     public int getHeight() {
299         return height;
300     }
301
302     protected void setAlignmentFactor(float alignmentFactor) {
303         this.alignmentFactor = alignmentFactor;
304     }
305
306     protected void setAppearance(Appearance appearance) {
307         this.appearance = appearance;
308     }
309
310     protected void setThickness(int thickness) {
311         this.thickness = thickness;
312     }
313
314     public int getHMargin() {
315         return HMARGIN;
316     }
317
318     public int getVMargin() {
319         return VMARGIN;
320     }
321
322     public int getSpaceWidth() {
323         return SPACEWIDTH;
324     }
325
326     /**
327      * Compute and return the matrix of text string from the given {@see Text} object.
328      * @param text the given {@see Text} object.
329      * @return the matrix of text string from the given {@see Text} object.
330      */
331     protected String[][] computeTextData(final TextObject text) {
332         String[] textString = text.getTextStrings();
333         Integer[] dimensions = text.getTextArrayDimensions();
334         String[][] texts = new String[dimensions[0]][dimensions[1]];
335         int i = 0;
336         for (int c = 0; c < dimensions[1]; c++) {
337             for (int l = 0; l < dimensions[0]; l++) {
338                 texts[l][c] = textString[i];
339                 i++;
340             }
341         }
342         return texts;
343     }
344
345     /**
346      * Compute and return the {@see Font} adapted to the given scilab text.
347      * @param text the given scilab text.
348      * @return the {@see Font} adapted to the given scilab text.
349      */
350     private Font computeFont(final TextObject text) {
351         return FontManager.getSciFontManager().getFontFromIndex(text.getFontStyle(), text.getFontSize());
352     }
353
354     /**
355      * Computes and returns the {@link Font} adapted to the given Scilab text, taking into account the scale factor.
356      * It takes the size 1 Font to derive a new Font whose size is increased according to the scale factor.
357      * @param text the given {@see Text} object.
358      * @param scaleFactor the scale factor to apply.
359      * @return the {@see Font} adapted to the given Scilab text.
360      */
361     private Font computeFont(final TextObject text, double scaleFactor) {
362         Font font = FontManager.getSciFontManager().getFontFromIndex(text.getFontStyle(), 1.0);
363         return font.deriveFont(font.getSize2D() * (float)scaleFactor);
364     }
365
366     /**
367      * Compute and return the alignment factor corresponding to the given scilab text.
368      * @param text the given scilab text.
369      * @return the alignment factor corresponding to the given scilab text.
370      */
371     protected float computeAlignmentFactor(Text text) {
372         switch (text.getAlignmentAsEnum()) {
373             case LEFT:
374                 return 0f;
375             case CENTER:
376                 return 1f / 2f;
377             case RIGHT:
378                 return 1f;
379             default:
380                 return 0f;
381         }
382     }
383
384     /**
385      * Util function.
386      * Return sum of the element of the given array.
387      * @param values the given array.
388      * @return sum of the element of the given array.
389      */
390     private int sum(int[] values) {
391         int sum = 0;
392         for (int value : values) {
393             sum += value;
394         }
395         return sum;
396     }
397 }