001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge;
020:
021: import java.awt.Font;
022: import java.awt.font.FontRenderContext;
023: import java.text.AttributedCharacterIterator;
024: import java.text.CharacterIterator;
025: import java.text.StringCharacterIterator;
026: import java.util.StringTokenizer;
027: import java.util.List;
028: import java.util.ArrayList;
029:
030: import org.apache.batik.css.engine.SVGCSSEngine;
031: import org.apache.batik.css.engine.value.Value;
032: import org.apache.batik.dom.util.XMLSupport;
033: import org.apache.batik.gvt.font.GVTFont;
034: import org.apache.batik.gvt.font.GVTFontFace;
035: import org.apache.batik.gvt.font.GVTGlyphVector;
036: import org.apache.batik.gvt.font.GVTLineMetrics;
037: import org.apache.batik.gvt.font.Glyph;
038: import org.apache.batik.gvt.font.Kern;
039: import org.apache.batik.gvt.font.KerningTable;
040: import org.apache.batik.gvt.font.SVGGVTGlyphVector;
041: import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
042: import org.apache.batik.gvt.text.TextPaintInfo;
043: import org.apache.batik.util.SVGConstants;
044:
045: import org.w3c.dom.Element;
046:
047: /**
048: * Represents an SVG font.
049: *
050: * @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
051: * @version $Id: SVGGVTFont.java 489226 2006-12-21 00:05:36Z cam $
052: */
053: public final class SVGGVTFont implements GVTFont, SVGConstants {
054:
055: public static final AttributedCharacterIterator.Attribute PAINT_INFO = GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO;
056:
057: private float fontSize;
058: private GVTFontFace fontFace;
059: private String[] glyphUnicodes;
060: private String[] glyphNames;
061: private String[] glyphLangs;
062: private String[] glyphOrientations;
063: private String[] glyphForms;
064: private Element[] glyphElements;
065: private Element[] hkernElements;
066: private Element[] vkernElements;
067: private BridgeContext ctx;
068: private Element textElement;
069: private Element missingGlyphElement;
070: private KerningTable hKerningTable;
071: private KerningTable vKerningTable;
072: private String language;
073: private String orientation;
074: private float scale;
075: private GVTLineMetrics lineMetrics = null;
076:
077: /**
078: * Constructs a new SVGGVTFont of the specified size.
079: *
080: * @param fontSize The size of the font to create.
081: * @param fontFace The font face that describes the font.
082: * @param glyphUnicodes An array containing the unicode values for
083: * all the glyphs this font can display.
084: * @param glyphNames An array containing the names of all the
085: * glyphs this font can display.
086: * @param ctx The bridge context.
087: * @param glyphElements An array containing the children glyph
088: * elements of the SVG font.
089: * @param missingGlyphElement The missing glyph element for this
090: * font.
091: * @param hkernElements An array containing all hkern elements for
092: * this font.
093: * @param vkernElements An array containing all vkern elements for
094: * this font.
095: * @param textElement The text element that contains the text to
096: * be rendered using this font.
097: */
098: public SVGGVTFont(float fontSize, GVTFontFace fontFace,
099: String[] glyphUnicodes, String[] glyphNames,
100: String[] glyphLangs, String[] glyphOrientations,
101: String[] glyphForms, BridgeContext ctx,
102: Element[] glyphElements, Element missingGlyphElement,
103: Element[] hkernElements, Element[] vkernElements,
104: Element textElement) {
105: this .fontFace = fontFace;
106: this .fontSize = fontSize;
107: this .glyphUnicodes = glyphUnicodes;
108: this .glyphNames = glyphNames;
109: this .glyphLangs = glyphLangs;
110: this .glyphOrientations = glyphOrientations;
111: this .glyphForms = glyphForms;
112: this .ctx = ctx;
113: this .glyphElements = glyphElements;
114: this .missingGlyphElement = missingGlyphElement;
115: this .hkernElements = hkernElements;
116: this .vkernElements = vkernElements;
117: this .scale = fontSize / fontFace.getUnitsPerEm();
118: this .textElement = textElement;
119:
120: this .language = XMLSupport.getXMLLang(textElement);
121:
122: Value v = CSSUtilities.getComputedStyle(textElement,
123: SVGCSSEngine.WRITING_MODE_INDEX);
124: if (v.getStringValue().startsWith(CSS_TB_VALUE)) {
125: // top to bottom, so set orientation to "v"
126: this .orientation = SVG_V_VALUE;
127: } else {
128: this .orientation = SVG_H_VALUE;
129: }
130:
131: createKerningTables();
132: }
133:
134: /**
135: * Creates the kerning tables for this font. Two tables are created,
136: * horizontal and vertical. If there are not children vkern or hkern
137: * elements these tables will be empty.
138: */
139: private void createKerningTables() {
140:
141: Kern[] hEntries = new Kern[hkernElements.length];
142: for (int i = 0; i < hkernElements.length; i++) {
143: Element hkernElement = hkernElements[i];
144: SVGHKernElementBridge hkernBridge = (SVGHKernElementBridge) ctx
145: .getBridge(hkernElement);
146: Kern hkern = hkernBridge
147: .createKern(ctx, hkernElement, this );
148: hEntries[i] = hkern;
149: }
150: hKerningTable = new KerningTable(hEntries);
151:
152: Kern[] vEntries = new Kern[vkernElements.length];
153: for (int i = 0; i < vkernElements.length; i++) {
154: Element vkernElement = vkernElements[i];
155: SVGVKernElementBridge vkernBridge = (SVGVKernElementBridge) ctx
156: .getBridge(vkernElement);
157: Kern vkern = vkernBridge
158: .createKern(ctx, vkernElement, this );
159: vEntries[i] = vkern;
160: }
161: vKerningTable = new KerningTable(vEntries);
162:
163: }
164:
165: /**
166: * Returns the horizontal kerning value for the specified glyph pair.
167: * This will be zero if there is no explicit horizontal kerning value
168: * for this particular glyph pair.
169: *
170: * @param glyphCode1 The id of the first glyph.
171: * @param glyphCode2 The id of the second glyph.
172: *
173: * @return The horizontal kerning value.
174: */
175: public float getHKern(int glyphCode1, int glyphCode2) {
176: if (glyphCode1 < 0 || glyphCode1 >= glyphUnicodes.length
177: || glyphCode2 < 0 || glyphCode2 >= glyphUnicodes.length) {
178: return 0f;
179: }
180: float ret;
181: ret = hKerningTable.getKerningValue(glyphCode1, glyphCode2,
182: glyphUnicodes[glyphCode1], glyphUnicodes[glyphCode2]);
183: return ret * scale;
184: }
185:
186: /**
187: * Returns the vertical kerning value for the specified glyph pair.
188: * This will be zero if there is no explicit vertical kerning value for
189: * for this particular glyph pair.
190: *
191: * @param glyphCode1 The id of the first glyph.
192: * @param glyphCode2 The id of the second glyph.
193: *
194: * @return The vertical kerning value.
195: */
196: public float getVKern(int glyphCode1, int glyphCode2) {
197: if (glyphCode1 < 0 || glyphCode1 >= glyphUnicodes.length
198: || glyphCode2 < 0 || glyphCode2 >= glyphUnicodes.length) {
199: return 0f;
200: }
201: float ret;
202: ret = vKerningTable.getKerningValue(glyphCode1, glyphCode2,
203: glyphUnicodes[glyphCode1], glyphUnicodes[glyphCode2]);
204: return ret * scale;
205: }
206:
207: /**
208: * Returns an array of glyph codes (unique ids) of the glyphs with the
209: * specified name (there may be more than one).
210: *
211: * @param name The name of the glyph.
212: *
213: * @return An array of matching glyph codes. This may be empty.
214: */
215: public int[] getGlyphCodesForName(String name) {
216: List glyphCodes = new ArrayList();
217: for (int i = 0; i < glyphNames.length; i++) {
218: if (glyphNames[i] != null && glyphNames[i].equals(name)) {
219: glyphCodes.add(new Integer(i));
220: }
221: }
222: int[] glyphCodeArray = new int[glyphCodes.size()];
223: for (int i = 0; i < glyphCodes.size(); i++) {
224: glyphCodeArray[i] = ((Integer) glyphCodes.get(i))
225: .intValue();
226: }
227: return glyphCodeArray;
228: }
229:
230: /**
231: * Returns an array of glyph codes (unique ids) of the glyphs with the
232: * specified unicode value (there may be more than one).
233: *
234: * @param unicode The unicode value of the glyph.
235: *
236: * @return An array of matching glyph codes. This may be empty.
237: */
238: public int[] getGlyphCodesForUnicode(String unicode) {
239: List glyphCodes = new ArrayList();
240: for (int i = 0; i < glyphUnicodes.length; i++) {
241: if (glyphUnicodes[i] != null
242: && glyphUnicodes[i].equals(unicode)) {
243: glyphCodes.add(new Integer(i));
244: }
245: }
246: int[] glyphCodeArray = new int[glyphCodes.size()];
247: for (int i = 0; i < glyphCodes.size(); i++) {
248: glyphCodeArray[i] = ((Integer) glyphCodes.get(i))
249: .intValue();
250: }
251: return glyphCodeArray;
252: }
253:
254: /**
255: * Returns true if the glyph language matches the language of the
256: * text node to be rendered by this font. This will be the case
257: * if one of the languages in glyphLang matches exactly with the
258: * xml:lang attibute of the text node, or if the xml:lang
259: * attribute exactly equals a prefix of one glyph languages.
260: *
261: * @param glyphLang A comma separated list of languages that are associated
262: * with a glyph.
263: *
264: * @return Whether or not the glyph language matches the language of the
265: * text node.
266: */
267: private boolean languageMatches(String glyphLang) {
268: if (glyphLang == null || glyphLang.length() == 0) {
269: return true; // will match all languages
270: }
271: StringTokenizer st = new StringTokenizer(glyphLang, ",");
272: while (st.hasMoreTokens()) {
273: String s = st.nextToken();
274: if (s.equals(language)
275: || (s.startsWith(language)
276: && s.length() > language.length() && s
277: .charAt(language.length()) == '-')) {
278: return true;
279: }
280: }
281: return false;
282: }
283:
284: /**
285: * Returns true if the glyph orientation matches the orientation of the
286: * text node to be rendered by this font.
287: *
288: * @param glyphOrientation The glyph orientation attribute value. Will be
289: * "h", "v" or empty.
290: *
291: * @return Whether or not the glyph orientation matches the text to be
292: * rendered by this font object.
293: */
294: private boolean orientationMatches(String glyphOrientation) {
295: if (glyphOrientation == null || glyphOrientation.length() == 0) {
296: return true;
297: }
298: return glyphOrientation.equals(orientation);
299: }
300:
301: /**
302: * Returns true if the glyph form matches that of the current character in
303: * the aci.
304: *
305: * @param glyphUnicode The unicode value of the glyph.
306: * @param glyphForm The arabic-form glyph attribute.
307: * @param aci The aci containing the character to check.
308: * @param currentIndex The index of the character to check.
309: */
310: private boolean formMatches(String glyphUnicode, String glyphForm,
311: AttributedCharacterIterator aci, int currentIndex) {
312: if (aci == null || glyphForm == null || glyphForm.length() == 0) {
313: // there aren't any attributes attached to the text
314: // or the glyph doesn't have an arabic form
315: return true;
316: }
317:
318: char c = aci.setIndex(currentIndex);
319: Integer form = (Integer) aci
320: .getAttribute(GVTAttributedCharacterIterator.TextAttribute.ARABIC_FORM);
321:
322: if (form == null
323: || form
324: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_NONE)) {
325: // the glyph has an arabic form and the current character
326: // form is "none" so don't match
327: return false;
328: }
329:
330: // see if c is the start of an arabic ligature
331: if (glyphUnicode.length() > 1) {
332:
333: boolean matched = true;
334: for (int j = 1; j < glyphUnicode.length(); j++) {
335: c = aci.next();
336: if (glyphUnicode.charAt(j) != c) {
337: matched = false;
338: break;
339: }
340: }
341:
342: // reset the aci
343: aci.setIndex(currentIndex);
344:
345: if (matched) {
346:
347: // ligature matches, now check that the arabic forms are ok
348: aci.setIndex(currentIndex + glyphUnicode.length() - 1);
349: Integer lastForm = (Integer) aci
350: .getAttribute(GVTAttributedCharacterIterator.TextAttribute.ARABIC_FORM);
351:
352: // reset the aci again
353: aci.setIndex(currentIndex);
354:
355: if (form != null && lastForm != null) {
356: if (form
357: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_TERMINAL)
358: && lastForm
359: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_INITIAL)) {
360: // return true if the glyph form is isolated
361: return glyphForm
362: .equals(SVGConstants.SVG_ISOLATED_VALUE);
363:
364: } else if (form
365: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_TERMINAL)) {
366: // return true if the glyph form is terminal
367: return glyphForm
368: .equals(SVGConstants.SVG_TERMINAL_VALUE);
369:
370: } else if (form
371: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL)
372: && lastForm
373: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL)) {
374: // return true if the glyph form is medial
375: return glyphForm
376: .equals(SVGConstants.SVG_MEDIAL_VALUE);
377: }
378: // should test for other combos as well here
379: }
380: }
381: }
382:
383: if (form
384: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_ISOLATED)) {
385: return glyphForm.equals(SVGConstants.SVG_ISOLATED_VALUE);
386: }
387: if (form
388: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_TERMINAL)) {
389: return glyphForm.equals(SVGConstants.SVG_TERMINAL_VALUE);
390: }
391: if (form
392: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_INITIAL)) {
393: return glyphForm.equals(SVGConstants.SVG_INITIAL_VALUE);
394: }
395: if (form
396: .equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL)) {
397: return glyphForm.equals(SVGConstants.SVG_MEDIAL_VALUE);
398: }
399: return false;
400: }
401:
402: /**
403: * Indicates whether or not the specified glyph can be displayed by this
404: * font.
405: *
406: * @param name The name of the glyph to check.
407: *
408: * @return true if the glyph can be displayed.
409: */
410: public boolean canDisplayGivenName(String name) {
411: for (int i = 0; i < glyphNames.length; i++) {
412: if (glyphNames[i] != null && glyphNames[i].equals(name)
413: && languageMatches(glyphLangs[i])
414: && orientationMatches(glyphOrientations[i])) {
415: return true;
416: }
417: }
418: return false;
419: }
420:
421: /**
422: * Indicates whether or not the specified character can be
423: * displayed by this font.
424: *
425: * @param c The character to check.
426: *
427: * @return true if the character can be displayed.
428: */
429: public boolean canDisplay(char c) {
430: for (int i = 0; i < glyphUnicodes.length; i++) {
431: if (glyphUnicodes[i].indexOf(c) != -1
432: && languageMatches(glyphLangs[i])
433: && orientationMatches(glyphOrientations[i])) {
434: return true;
435: }
436: }
437: return false;
438: }
439:
440: /**
441: * Checks whether this Font can display the characters in the
442: * specified character array starting at start and ending at limit.
443: *
444: * @param text An array containing the characters to check.
445: * @param start The index of the first character to check.
446: * @param limit The index of the last character to check.
447: *
448: * @return The index of the first character it can't display or -1 if
449: * it can display the whole string.
450: */
451: public int canDisplayUpTo(char[] text, int start, int limit) {
452: StringCharacterIterator sci = new StringCharacterIterator(
453: new String(text));
454: return canDisplayUpTo(sci, start, limit);
455: }
456:
457: /**
458: * Checks whether this Font can display the characters in the
459: * specified character iterator starting at start and ending at limit.
460: *
461: * @param iter The iterator containing the characters to check.
462: * @param start The index of the first character to check.
463: * @param limit The index of the last character to check.
464: *
465: * @return The index of the first character it can't display or -1 if
466: * it can display the whole string.
467: */
468: public int canDisplayUpTo(CharacterIterator iter, int start,
469: int limit) {
470:
471: AttributedCharacterIterator aci = null;
472: if (iter instanceof AttributedCharacterIterator) {
473: aci = (AttributedCharacterIterator) iter;
474: }
475:
476: char c = iter.setIndex(start);
477: int currentIndex = start;
478:
479: while (c != CharacterIterator.DONE && currentIndex < limit) {
480:
481: boolean foundMatchingGlyph = false;
482:
483: for (int i = 0; i < glyphUnicodes.length; i++) {
484: if (glyphUnicodes[i].indexOf(c) == 0
485: && languageMatches(glyphLangs[i])
486: && orientationMatches(glyphOrientations[i])
487: && formMatches(glyphUnicodes[i], glyphForms[i],
488: aci, currentIndex)) {
489: // found a possible match
490:
491: if (glyphUnicodes[i].length() == 1) { // not a ligature
492: foundMatchingGlyph = true;
493: break;
494:
495: } else {
496: // glyphCodes[i] is a ligature so try and
497: // match the rest of the glyphCode chars
498: boolean matched = true;
499: for (int j = 1; j < glyphUnicodes[i].length(); j++) {
500: c = iter.next();
501: if (glyphUnicodes[i].charAt(j) != c) {
502: matched = false;
503: break;
504: }
505: }
506: if (matched) { // found a matching ligature!
507: foundMatchingGlyph = true;
508: break;
509:
510: } else {
511: // did not match ligature, keep looking
512: // for another glyph
513: c = iter.setIndex(currentIndex);
514: }
515: }
516: }
517: }
518: if (!foundMatchingGlyph) {
519: return currentIndex;
520: }
521: c = iter.next();
522: currentIndex = iter.getIndex();
523: }
524: return -1;
525: }
526:
527: /**
528: * Checks whether or not this font can display the characters in the
529: * specified String.
530: *
531: * @param str The string containing the characters to check.
532: *
533: * @return The index of the first character it can't display or -1 if
534: * it can display the whole string.
535: */
536: public int canDisplayUpTo(String str) {
537: StringCharacterIterator sci = new StringCharacterIterator(str);
538: return canDisplayUpTo(sci, 0, str.length());
539: }
540:
541: /**
542: * Returns a new GVTGlyphVector object for the specified array of
543: * characters.
544: *
545: * @param frc The current font render context.
546: * @param chars The array of chars that the glyph vector will represent.
547: *
548: * @return The new glyph vector.
549: */
550: public GVTGlyphVector createGlyphVector(FontRenderContext frc,
551: char[] chars) {
552: StringCharacterIterator sci = new StringCharacterIterator(
553: new String(chars));
554: return createGlyphVector(frc, sci);
555: }
556:
557: /**
558: * Returns a new GVTGlyphVector object for the characters in the
559: * specified character iterator.
560: *
561: * @param frc The current font render context.
562: * @param ci The character iterator that the glyph vector will represent.
563: *
564: * @return The new glyph vector.
565: */
566: public GVTGlyphVector createGlyphVector(FontRenderContext frc,
567: CharacterIterator ci) {
568:
569: AttributedCharacterIterator aci = null;
570: if (ci instanceof AttributedCharacterIterator) {
571: aci = (AttributedCharacterIterator) ci;
572: }
573:
574: List glyphs = new ArrayList();
575: char c = ci.first();
576: while (c != CharacterIterator.DONE) {
577: boolean foundMatchingGlyph = false;
578: for (int i = 0; i < glyphUnicodes.length; i++) {
579: if (glyphUnicodes[i].indexOf(c) == 0
580: && languageMatches(glyphLangs[i])
581: && orientationMatches(glyphOrientations[i])
582: && formMatches(glyphUnicodes[i], glyphForms[i],
583: aci, ci.getIndex())) { // found a possible match
584:
585: if (glyphUnicodes[i].length() == 1) { // not a ligature
586: Element glyphElement = glyphElements[i];
587: SVGGlyphElementBridge glyphBridge = (SVGGlyphElementBridge) ctx
588: .getBridge(glyphElement);
589: TextPaintInfo tpi = null;
590: if (aci != null) {
591: tpi = (TextPaintInfo) aci
592: .getAttribute(PAINT_INFO);
593: }
594: Glyph glyph = glyphBridge.createGlyph(ctx,
595: glyphElement, textElement, i, fontSize,
596: fontFace, tpi);
597: glyphs.add(glyph);
598: foundMatchingGlyph = true;
599: break;
600: } else {
601: // glyphCodes[i] is a ligature so try and
602: // match the rest of the glyphCode chars
603: int current = ci.getIndex();
604: boolean matched = true;
605: for (int j = 1; j < glyphUnicodes[i].length(); j++) {
606: c = ci.next();
607: if (glyphUnicodes[i].charAt(j) != c) {
608: matched = false;
609: break;
610: }
611: }
612: if (matched) { // found a matching ligature!
613:
614: Element glyphElement = glyphElements[i];
615: SVGGlyphElementBridge glyphBridge = (SVGGlyphElementBridge) ctx
616: .getBridge(glyphElement);
617: TextPaintInfo tpi = null;
618: if (aci != null) {
619: aci.setIndex(ci.getIndex());
620: tpi = (TextPaintInfo) aci
621: .getAttribute(PAINT_INFO);
622: }
623: Glyph glyph = glyphBridge.createGlyph(ctx,
624: glyphElement, textElement, i,
625: fontSize, fontFace, tpi);
626: glyphs.add(glyph);
627: foundMatchingGlyph = true;
628: break;
629:
630: } else {
631: // did not match ligature, keep looking
632: // for another glyph
633: c = ci.setIndex(current);
634: }
635: }
636: }
637: }
638: if (!foundMatchingGlyph) {
639: // add the missing glyph
640: SVGGlyphElementBridge glyphBridge = (SVGGlyphElementBridge) ctx
641: .getBridge(missingGlyphElement);
642: TextPaintInfo tpi = null;
643: if (aci != null) {
644: aci.setIndex(ci.getIndex());
645: tpi = (TextPaintInfo) aci.getAttribute(PAINT_INFO);
646: }
647: Glyph glyph = glyphBridge.createGlyph(ctx,
648: missingGlyphElement, textElement, -1, fontSize,
649: fontFace, tpi);
650: glyphs.add(glyph);
651: }
652: c = ci.next();
653: }
654:
655: // turn the vector of glyphs into an array;
656: int numGlyphs = glyphs.size();
657:
658: Glyph[] glyphArray = (Glyph[]) glyphs
659: .toArray(new Glyph[numGlyphs]);
660:
661: // return a new SVGGVTGlyphVector
662: return new SVGGVTGlyphVector(this , glyphArray, frc);
663: }
664:
665: /**
666: * Returns a new GVTGlyphVector object for the glyphs in the
667: * the glyph code array.
668: *
669: * @param frc The current font render context.
670: * @param glyphCodes An array containin the ids of the glyphs that
671: * the glyph vector will represent.
672: *
673: * @return The new glyph vector.
674: */
675: public GVTGlyphVector createGlyphVector(FontRenderContext frc,
676: int[] glyphCodes, CharacterIterator ci) {
677: // construct a string from the glyphCodes
678: int nGlyphs = glyphCodes.length;
679: StringBuffer workBuff = new StringBuffer(nGlyphs);
680: for (int i = 0; i < nGlyphs; i++) {
681: workBuff.append(glyphUnicodes[glyphCodes[i]]);
682: }
683: StringCharacterIterator sci = new StringCharacterIterator(
684: workBuff.toString());
685: return createGlyphVector(frc, sci);
686: }
687:
688: /**
689: * Returns a new GVTGlyphVector object for the specified String.
690: *
691: * @param frc The current font render context.
692: * @param str The string that the glyph vector will represent.
693: *
694: * @return The new glyph vector.
695: */
696: public GVTGlyphVector createGlyphVector(FontRenderContext frc,
697: String str) {
698: StringCharacterIterator sci = new StringCharacterIterator(str);
699: return createGlyphVector(frc, sci);
700: }
701:
702: /**
703: * Creates a new GVTFont object by replicating this font object and
704: * applying a new size to it.
705: *
706: * @param size The size of the new font.
707: *
708: * @return The new font object.
709: */
710: public GVTFont deriveFont(float size) {
711: return new SVGGVTFont(size, fontFace, glyphUnicodes,
712: glyphNames, glyphLangs, glyphOrientations, glyphForms,
713: ctx, glyphElements, missingGlyphElement, hkernElements,
714: vkernElements, textElement);
715: }
716:
717: public String getFamilyName() {
718: return fontFace.getFamilyName();
719: }
720:
721: protected GVTLineMetrics getLineMetrics(int beginIndex, int limit) {
722: if (lineMetrics != null)
723: return lineMetrics;
724:
725: float fontHeight = fontFace.getUnitsPerEm();
726: float scale = fontSize / fontHeight;
727:
728: float ascent = fontFace.getAscent() * scale;
729: float descent = fontFace.getDescent() * scale;
730:
731: float[] baselineOffsets = new float[3];
732: baselineOffsets[Font.ROMAN_BASELINE] = 0;
733: baselineOffsets[Font.CENTER_BASELINE] = (ascent + descent) / 2
734: - ascent;
735: baselineOffsets[Font.HANGING_BASELINE] = -ascent;
736:
737: float stOffset = fontFace.getStrikethroughPosition() * -scale;
738: float stThickness = fontFace.getStrikethroughThickness()
739: * scale;
740: float ulOffset = fontFace.getUnderlinePosition() * scale;
741: float ulThickness = fontFace.getUnderlineThickness() * scale;
742: float olOffset = fontFace.getOverlinePosition() * -scale;
743: float olThickness = fontFace.getOverlineThickness() * scale;
744:
745: lineMetrics = new GVTLineMetrics(ascent, Font.ROMAN_BASELINE,
746: baselineOffsets, descent, fontHeight, fontHeight, limit
747: - beginIndex, stOffset, stThickness, ulOffset,
748: ulThickness, olOffset, olThickness);
749: return lineMetrics;
750: }
751:
752: /**
753: * Returns the line metrics for the specified text.
754: *
755: * @param chars The character array containing the text.
756: * @param beginIndex The index of the first character.
757: * @param limit The limit of characters.
758: * @param frc The current font render context.
759: *
760: * @return The new GVTLineMetrics object.
761: */
762: public GVTLineMetrics getLineMetrics(char[] chars, int beginIndex,
763: int limit, FontRenderContext frc) {
764: return getLineMetrics(beginIndex, limit);
765: }
766:
767: /**
768: * Returns the line metrics for the specified text.
769: *
770: * @param ci The character iterator containing the text.
771: * @param beginIndex The index of the first character.
772: * @param limit The limit of characters.
773: * @param frc The current font render context.
774: *
775: * @return The new GVTLineMetrics object.
776: */
777: public GVTLineMetrics getLineMetrics(CharacterIterator ci,
778: int beginIndex, int limit, FontRenderContext frc) {
779: return getLineMetrics(beginIndex, limit);
780: }
781:
782: /**
783: * Returns the line metrics for the specified text.
784: *
785: * @param str The string containing the text.
786: * @param frc The current font render context.
787: *
788: * @return The new GVTLineMetrics object.
789: */
790: public GVTLineMetrics getLineMetrics(String str,
791: FontRenderContext frc) {
792: StringCharacterIterator sci = new StringCharacterIterator(str);
793: return getLineMetrics(sci, 0, str.length(), frc);
794: }
795:
796: /**
797: * Returns the line metrics for the specified text.
798: *
799: * @param str The string containing the text.
800: * @param beginIndex The index of the first character.
801: * @param limit The limit of characters.
802: * @param frc The current font render context.
803: *
804: * @return The new GVTLineMetrics object.
805: */
806: public GVTLineMetrics getLineMetrics(String str, int beginIndex,
807: int limit, FontRenderContext frc) {
808: StringCharacterIterator sci = new StringCharacterIterator(str);
809: return getLineMetrics(sci, beginIndex, limit, frc);
810: }
811:
812: /**
813: * Returns the size of this font.
814: *
815: * @return The font size.
816: */
817: public float getSize() {
818: return fontSize;
819: }
820:
821: /**
822: * Returns a string representation of this font.
823: * This is for debugging purposes only.
824: *
825: * @return A string representation of this font.
826: */
827: public String toString() {
828: return fontFace.getFamilyName() + " "
829: + fontFace.getFontWeight() + " "
830: + fontFace.getFontStyle();
831: }
832: }
|