1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.jeuclid.elements.support.text;
20
21 import java.awt.Font;
22 import java.awt.Graphics2D;
23 import java.awt.font.FontRenderContext;
24 import java.awt.font.TextAttribute;
25 import java.awt.font.TextLayout;
26 import java.awt.geom.Rectangle2D;
27 import java.text.AttributedCharacterIterator;
28 import java.text.AttributedString;
29 import java.text.CharacterIterator;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Vector;
34
35 import net.sourceforge.jeuclid.MathBase;
36 import net.sourceforge.jeuclid.ParameterKey;
37 import net.sourceforge.jeuclid.elements.support.attributes.FontFamily;
38 import net.sourceforge.jeuclid.elements.support.attributes.MathVariant;
39
40
41
42
43
44
45
46 public final class StringUtil {
47
48 private static final int LOWERCASE_START = 0x61;
49
50 private static final int UPPERCASE_START = 0x41;
51
52 private static final int NUM_CHARS = 26;
53
54 private static final Map<Integer, CodePointAndVariant> HIGHPLANE_MAPPING = new HashMap<Integer, CodePointAndVariant>();
55
56 private static final Map<Integer, Integer> FRAKTUR_MAPPING = new HashMap<Integer, Integer>();
57
58 private static final Map<Integer, Integer> SCRIPT_MAPPING = new HashMap<Integer, Integer>();
59
60 private static final Map<Integer, Integer> DOUBLE_MAPPING = new HashMap<Integer, Integer>();
61
62 private StringUtil() {
63
64 }
65
66 private static class CodePointAndVariant {
67 private final int codePoint;
68
69 private final MathVariant variant;
70
71 protected CodePointAndVariant(final int icodePoint,
72 final MathVariant ivariant) {
73 this.codePoint = icodePoint;
74 this.variant = ivariant;
75 }
76
77
78
79
80 public final int getCodePoint() {
81 return this.codePoint;
82 }
83
84
85
86
87 public final MathVariant getVariant() {
88 return this.variant;
89 }
90
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 public static AttributedString convertStringtoAttributedString(
109 final String inputString, final MathVariant baseVariant,
110 final float fontSize, final MathBase base) {
111 final StringBuilder builder = new StringBuilder();
112 final List<MathVariant> variants = new Vector<MathVariant>();
113 final String plainString = CharConverter.convertLate(inputString);
114
115 for (int i = 0; i < plainString.length(); i++) {
116 if (!Character.isLowSurrogate(plainString.charAt(i))) {
117
118 CodePointAndVariant cpav = new CodePointAndVariant(
119 plainString.codePointAt(i), baseVariant);
120
121 cpav = StringUtil.mapHighCodepointToLowerCodepoints(cpav);
122 cpav = StringUtil.mapVariantsToStandardCodepoints(cpav);
123
124 final int codePoint = cpav.getCodePoint();
125 final MathVariant variant = cpav.getVariant();
126
127 builder.appendCodePoint(codePoint);
128 variants.add(variant);
129 if (Character.isSupplementaryCodePoint(codePoint)) {
130 variants.add(variant);
131 }
132 }
133 }
134
135 final AttributedString aString = new AttributedString(builder
136 .toString());
137
138 for (int i = 0; i < builder.length(); i++) {
139 final MathVariant variant = variants.get(i);
140 aString.addAttribute(TextAttribute.FONT, variant.createFont(
141 fontSize, builder.charAt(i), base), i, i + 1);
142 }
143 return aString;
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158 public static TextLayout createTextLayoutFromAttributedString(
159 final Graphics2D g, final AttributedString aString,
160 final MathBase mathBase) {
161 final AttributedCharacterIterator charIter = aString.getIterator();
162 final boolean empty = charIter.first() == CharacterIterator.DONE;
163 final FontRenderContext suggestedFontRenderContext = g
164 .getFontRenderContext();
165 boolean antialiasing = Boolean.parseBoolean(mathBase.getParams().get(
166 ParameterKey.AntiAlias));
167 final FontRenderContext realFontRenderContext = new FontRenderContext(
168 suggestedFontRenderContext.getTransform(), antialiasing,
169 false);
170
171 final TextLayout theLayout;
172 if (!empty) {
173 theLayout = new TextLayout(aString.getIterator(),
174 realFontRenderContext);
175 } else {
176 theLayout = new TextLayout(" ", new Font("", 0, 0),
177 realFontRenderContext);
178 }
179 return theLayout;
180 }
181
182
183
184
185
186
187
188
189
190
191
192
193 private static CodePointAndVariant mapVariantsToStandardCodepoints(
194 final CodePointAndVariant cpav) {
195 int codePoint = cpav.getCodePoint();
196 MathVariant variant = cpav.getVariant();
197 final int awtStyle = variant.getAwtStyle();
198 final FontFamily fontFamily = variant.getFontFamily();
199 if (FontFamily.FRAKTUR.equals(fontFamily)) {
200 final Integer mapping = StringUtil.FRAKTUR_MAPPING.get(codePoint);
201 if (mapping != null) {
202 codePoint = mapping;
203 variant = new MathVariant(awtStyle, FontFamily.SANSSERIF);
204 }
205 } else if (FontFamily.SCRIPT.equals(fontFamily)) {
206 final Integer mapping = StringUtil.SCRIPT_MAPPING.get(codePoint);
207 if (mapping != null) {
208 codePoint = mapping;
209 variant = new MathVariant(awtStyle, FontFamily.SANSSERIF);
210 }
211 } else if (FontFamily.DOUBLE_STRUCK.equals(fontFamily)) {
212 final Integer mapping = StringUtil.DOUBLE_MAPPING.get(codePoint);
213 if (mapping != null) {
214 codePoint = mapping;
215 variant = new MathVariant(awtStyle, FontFamily.SANSSERIF);
216 }
217
218 }
219 return new CodePointAndVariant(codePoint, variant);
220
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234 private static CodePointAndVariant mapHighCodepointToLowerCodepoints(
235 final CodePointAndVariant cpav) {
236 final int codePoint = cpav.getCodePoint();
237 final CodePointAndVariant mappedTo = StringUtil.HIGHPLANE_MAPPING
238 .get(codePoint);
239 if (mappedTo != null) {
240 return mappedTo;
241 } else {
242 return cpav;
243 }
244 }
245
246
247
248
249
250
251
252
253 public static float getWidthForTextLayout(final TextLayout layout) {
254 final Rectangle2D r2d = layout.getBounds();
255 float realWidth = (float) r2d.getWidth();
256 final float xo = (float) r2d.getX();
257 if (xo > 0) {
258 realWidth += xo;
259 }
260
261
262 final float invisibleAdvance = layout.getAdvance()
263 - layout.getVisibleAdvance();
264 return realWidth + invisibleAdvance;
265 }
266
267 private static void addHighMapping(final int codePointStart,
268 final MathVariant mapToVariant) {
269
270 for (int i = 0; i < StringUtil.NUM_CHARS; i++) {
271 StringUtil.HIGHPLANE_MAPPING.put(codePointStart + i,
272 new CodePointAndVariant(StringUtil.UPPERCASE_START + i,
273 mapToVariant));
274 }
275 for (int i = 0; i < StringUtil.NUM_CHARS; i++) {
276 StringUtil.HIGHPLANE_MAPPING.put(codePointStart
277 + StringUtil.NUM_CHARS + i, new CodePointAndVariant(
278 StringUtil.LOWERCASE_START + i, mapToVariant));
279 }
280 }
281
282 private static void initializeVariantToStandardMapping() {
283
284
285
286 StringUtil.FRAKTUR_MAPPING.put((int) 'C', 0x0212D);
287 StringUtil.FRAKTUR_MAPPING.put((int) 'H', 0x0210C);
288 StringUtil.FRAKTUR_MAPPING.put((int) 'I', 0x02111);
289 StringUtil.FRAKTUR_MAPPING.put((int) 'R', 0x0211C);
290 StringUtil.FRAKTUR_MAPPING.put((int) 'Z', 0x02128);
291
292
293 StringUtil.SCRIPT_MAPPING.put((int) 'B', 0x212C);
294 StringUtil.SCRIPT_MAPPING.put((int) 'E', 0x2130);
295 StringUtil.SCRIPT_MAPPING.put((int) 'e', 0x212F);
296 StringUtil.SCRIPT_MAPPING.put((int) 'F', 0x2131);
297 StringUtil.SCRIPT_MAPPING.put((int) 'g', 0x210A);
298 StringUtil.SCRIPT_MAPPING.put((int) 'H', 0x210B);
299 StringUtil.SCRIPT_MAPPING.put((int) 'I', 0x2110);
300 StringUtil.SCRIPT_MAPPING.put((int) 'L', 0x2112);
301 StringUtil.SCRIPT_MAPPING.put((int) 'M', 0x2133);
302 StringUtil.SCRIPT_MAPPING.put((int) 'o', 0x2134);
303 StringUtil.SCRIPT_MAPPING.put((int) 'R', 0x211B);
304
305
306 StringUtil.DOUBLE_MAPPING.put((int) 'C', 0x2102);
307 StringUtil.DOUBLE_MAPPING.put((int) 'H', 0x210D);
308 StringUtil.DOUBLE_MAPPING.put((int) 'N', 0x2115);
309 StringUtil.DOUBLE_MAPPING.put((int) 'P', 0x2119);
310 StringUtil.DOUBLE_MAPPING.put((int) 'Q', 0x211A);
311 StringUtil.DOUBLE_MAPPING.put((int) 'R', 0x211D);
312 StringUtil.DOUBLE_MAPPING.put((int) 'Z', 0x2124);
313
314 }
315
316 private static void initializeHighPlaneMappings() {
317
318 StringUtil.addHighMapping(0x1D400, MathVariant.BOLD);
319 StringUtil.addHighMapping(0x1D434, MathVariant.ITALIC);
320 StringUtil.addHighMapping(0x1D468, MathVariant.BOLD_ITALIC);
321 StringUtil.addHighMapping(0x1D49C, MathVariant.SCRIPT);
322 StringUtil.addHighMapping(0x1D4D0, MathVariant.BOLD_SCRIPT);
323 StringUtil.addHighMapping(0x1D504, MathVariant.FRAKTUR);
324 StringUtil.addHighMapping(0x1D538, MathVariant.DOUBLE_STRUCK);
325 StringUtil.addHighMapping(0x1D56C, MathVariant.BOLD_FRAKTUR);
326 StringUtil.addHighMapping(0x1D5A0, MathVariant.SANS_SERIF);
327 StringUtil.addHighMapping(0x1D5D4, MathVariant.BOLD_SANS_SERIF);
328 StringUtil.addHighMapping(0x1D608, MathVariant.SANS_SERIF_ITALIC);
329 StringUtil
330 .addHighMapping(0x1D63C, MathVariant.SANS_SERIF_BOLD_ITALIC);
331 StringUtil.addHighMapping(0x1D670, MathVariant.MONOSPACE);
332
333
334
335
336 }
337
338 static {
339 StringUtil.initializeVariantToStandardMapping();
340 StringUtil.initializeHighPlaneMappings();
341 }
342
343 }