1
24 package net.sf.jasperreports.engine.util;
25
26 import java.awt.Color;
27 import java.awt.GraphicsEnvironment;
28 import java.awt.font.TextAttribute;
29 import java.io.IOException;
30 import java.io.StringReader;
31 import java.lang.ref.SoftReference;
32 import java.text.AttributedCharacterIterator;
33 import java.text.AttributedCharacterIterator.Attribute;
34 import java.text.AttributedString;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.StringTokenizer;
44
45 import javax.xml.parsers.DocumentBuilder;
46 import javax.xml.parsers.DocumentBuilderFactory;
47 import javax.xml.parsers.ParserConfigurationException;
48
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51 import org.w3c.dom.Document;
52 import org.w3c.dom.NamedNodeMap;
53 import org.w3c.dom.Node;
54 import org.w3c.dom.NodeList;
55 import org.xml.sax.ErrorHandler;
56 import org.xml.sax.InputSource;
57 import org.xml.sax.SAXException;
58 import org.xml.sax.SAXParseException;
59
60 import net.sf.jasperreports.engine.JRPrintHyperlink;
61 import net.sf.jasperreports.engine.JRPrintHyperlinkParameter;
62 import net.sf.jasperreports.engine.JRPrintHyperlinkParameters;
63 import net.sf.jasperreports.engine.JRRuntimeException;
64 import net.sf.jasperreports.engine.base.JRBasePrintHyperlink;
65 import net.sf.jasperreports.engine.fonts.FontFamily;
66 import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
67 import net.sf.jasperreports.engine.util.JRStyledText.Run;
68 import net.sf.jasperreports.extensions.ExtensionsEnvironment;
69
70
71
74 public class JRStyledTextParser implements ErrorHandler
75 {
76 private static final Log log = LogFactory.getLog(JRStyledTextParser.class);
77
78 private static final Set<String> AVAILABLE_FONT_FACE_NAMES = new HashSet<String>();
79 static
80 {
81
82 try
83 {
84
85
86 List<FontFamily> families = ExtensionsEnvironment.getExtensionsRegistry().getExtensions(FontFamily.class);
87 for (Iterator<FontFamily> itf = families.iterator(); itf.hasNext();)
88 {
89 FontFamily family =itf.next();
90 AVAILABLE_FONT_FACE_NAMES.add(family.getName());
91 }
92
93
94 AVAILABLE_FONT_FACE_NAMES.addAll(
95 Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())
96 );
97 }
98 catch (Exception e)
99 {
100 log.error("Error while loading available fonts", e);
101 throw e;
102 }
103 }
104
105
108 private static final String ROOT_START = "<st>";
109 private static final String ROOT_END = "</st>";
110 private static final String NODE_style = "style";
111 private static final String NODE_bold = "b";
112 private static final String NODE_italic = "i";
113 private static final String NODE_underline = "u";
114 private static final String NODE_sup = "sup";
115 private static final String NODE_sub = "sub";
116 private static final String NODE_font = "font";
117 private static final String NODE_br = "br";
118 private static final String NODE_li = "li";
119 private static final String NODE_a = "a";
120 private static final String NODE_param = "param";
121 private static final String ATTRIBUTE_fontName = "fontName";
122 private static final String ATTRIBUTE_fontFace = "face";
123 private static final String ATTRIBUTE_color = "color";
124 private static final String ATTRIBUTE_size = "size";
125 private static final String ATTRIBUTE_isBold = "isBold";
126 private static final String ATTRIBUTE_isItalic = "isItalic";
127 private static final String ATTRIBUTE_isUnderline = "isUnderline";
128 private static final String ATTRIBUTE_isStrikeThrough = "isStrikeThrough";
129 private static final String ATTRIBUTE_forecolor = "forecolor";
130 private static final String ATTRIBUTE_backcolor = "backcolor";
131 private static final String ATTRIBUTE_pdfFontName = "pdfFontName";
132 private static final String ATTRIBUTE_pdfEncoding = "pdfEncoding";
133 private static final String ATTRIBUTE_isPdfEmbedded = "isPdfEmbedded";
134 private static final String ATTRIBUTE_type = "type";
135 private static final String ATTRIBUTE_href = "href";
136 private static final String ATTRIBUTE_target = "target";
137 private static final String ATTRIBUTE_name = "name";
138 private static final String ATTRIBUTE_valueClass = "valueClass";
139
140 private static final String SPACE = " ";
141 private static final String EQUAL_QUOTE = "=\"";
142 private static final String QUOTE = "\"";
143 private static final String LESS = "<";
144 private static final String LESS_SLASH = "</";
145 private static final String GREATER = ">";
146
147
150 private static final ThreadLocal<SoftReference<JRStyledTextParser>> threadInstances = new ThreadLocal<SoftReference<JRStyledTextParser>>();
151
152
155 private static final ThreadLocal<Locale> threadLocale = new ThreadLocal<Locale>();
156
157
162 public static JRStyledTextParser getInstance()
163 {
164 JRStyledTextParser instance = null;
165 SoftReference<JRStyledTextParser> instanceRef = threadInstances.get();
166 if (instanceRef != null)
167 {
168 instance = instanceRef.get();
169 }
170 if (instance == null)
171 {
172 instance = new JRStyledTextParser();
173 threadInstances.set(new SoftReference<JRStyledTextParser>(instance));
174 }
175 return instance;
176 }
177
178
179
182 public static void setLocale(Locale locale)
183 {
184 threadLocale.set(locale);
185 }
186
187
190 public static Locale getLocale()
191 {
192 return threadLocale.get();
193 }
194
195
198 private DocumentBuilder documentBuilder;
199
200
203 private JRBasePrintHyperlink hyperlink;
204
205
206
209 private JRStyledTextParser()
210 {
211 try
212 {
213 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
214 factory.setFeature(JRXmlUtils.FEATURE_DISALLOW_DOCTYPE, true);
215
216 documentBuilder = factory.newDocumentBuilder();
217 documentBuilder.setErrorHandler(this);
218 }
219 catch (ParserConfigurationException e)
220 {
221 throw new JRRuntimeException(e);
222 }
223 }
224
225
226
229 public JRStyledText parse(Map<Attribute,Object> attributes, String text, Locale locale) throws SAXException
230 {
231 JRStyledText styledText = new JRStyledText(locale);
232
233 Document document = null;
234
235 try
236 {
237 document = documentBuilder.parse(new InputSource(new StringReader(ROOT_START + text + ROOT_END)));
238 }
239 catch (IOException e)
240 {
241 throw new JRRuntimeException(e);
242 }
243
244 hyperlink = null;
245
246 parseStyle(styledText, document.getDocumentElement());
247
248 styledText.setGlobalAttributes(attributes);
249
250 return styledText;
251 }
252
253
263 public JRStyledText getStyledText(Map<Attribute,Object> parentAttributes, String text, boolean isStyledText, Locale locale)
264 {
265 JRStyledText styledText = null;
266 if (isStyledText)
267 {
268 try
269 {
270 styledText = parse(parentAttributes, text, locale);
271 }
272 catch (SAXException e)
273 {
274
275 }
276 }
277
278 if (styledText == null)
279 {
280
281 styledText = new JRStyledText(locale, text, parentAttributes);
282 }
283
284 return styledText;
285 }
286
287
293 public String write(JRStyledText styledText)
294 {
295 return write(styledText.getGlobalAttributes(),
296 styledText.getAttributedString().getIterator(),
297 styledText.getText());
298 }
299
300
310 public String write(Map<Attribute,Object> parentAttrs, AttributedCharacterIterator iterator, String text)
311 {
312 StringBuilder sb = new StringBuilder();
313
314 int runLimit = 0;
315
316 while(runLimit < iterator.getEndIndex() && (runLimit = iterator.getRunLimit()) <= iterator.getEndIndex())
317 {
318 String chunk = text.substring(iterator.getIndex(), runLimit);
319 Map<Attribute,Object> attrs = iterator.getAttributes();
320
321 StringBuilder styleBuilder = writeStyleAttributes(parentAttrs, attrs);
322 if (styleBuilder.length() > 0)
323 {
324 sb.append(LESS);
325 sb.append(NODE_style);
326 sb.append(styleBuilder.toString());
327 sb.append(GREATER);
328 writeChunk(sb, parentAttrs, attrs, chunk);
329 sb.append(LESS_SLASH);
330 sb.append(NODE_style);
331 sb.append(GREATER);
332 }
333 else
334 {
335 writeChunk(sb, parentAttrs, attrs, chunk);
336 }
337
338 iterator.setIndex(runLimit);
339 }
340
341 return sb.toString();
342 }
343
344
354 public String write(JRStyledText styledText,
355 int startIndex, int endIndex)
356 {
357 AttributedCharacterIterator subIterator = new AttributedString(
358 styledText.getAttributedString().getIterator(),
359 startIndex, endIndex).getIterator();
360 String subText = styledText.getText().substring(startIndex, endIndex);
361 return write(styledText.getGlobalAttributes(), subIterator, subText);
362 }
363
364
367 public void writeChunk(StringBuilder sb, Map<Attribute,Object> parentAttrs, Map<Attribute,Object> attrs, String chunk)
368 {
369 Object value = attrs.get(TextAttribute.SUPERSCRIPT);
370 Object oldValue = parentAttrs.get(TextAttribute.SUPERSCRIPT);
371
372 boolean isSuper = false;
373 boolean isSub = false;
374
375 if (value != null && !value.equals(oldValue))
376 {
377 isSuper=TextAttribute.SUPERSCRIPT_SUPER.equals(value);
378 isSub=TextAttribute.SUPERSCRIPT_SUB.equals(value);
379 }
380
381 String scriptNode = isSuper?NODE_sup:NODE_sub;
382
383 if (isSuper || isSub)
384 {
385 sb.append(LESS);
386 sb.append(scriptNode);
387 sb.append(GREATER);
388 }
389
390 JRPrintHyperlink hlink = (JRPrintHyperlink)attrs.get(JRTextAttribute.HYPERLINK);
391 if (hlink != null)
392 {
393 sb.append(LESS);
394 sb.append(NODE_a);
395
396 String href = hlink.getHyperlinkReference();
397 if (href != null && href.trim().length() > 0)
398 {
399 sb.append(SPACE);
400 sb.append(ATTRIBUTE_href);
401 sb.append(EQUAL_QUOTE);
402 sb.append(JRStringUtil.htmlEncode(href));
403 sb.append(QUOTE);
404 }
405
406 String type = hlink.getLinkType();
407 if (type != null && type.trim().length() > 0)
408 {
409 sb.append(SPACE);
410 sb.append(ATTRIBUTE_type);
411 sb.append(EQUAL_QUOTE);
412 sb.append(type);
413 sb.append(QUOTE);
414 }
415
416 String target = hlink.getLinkTarget();
417 if (target != null && target.trim().length() > 0)
418 {
419 sb.append(SPACE);
420 sb.append(ATTRIBUTE_target);
421 sb.append(EQUAL_QUOTE);
422 sb.append(target);
423 sb.append(QUOTE);
424 }
425
426 sb.append(GREATER);
427
428 JRPrintHyperlinkParameters parameters = hlink.getHyperlinkParameters();
429 if (parameters != null && parameters.getParameters() != null)
430 {
431 for (JRPrintHyperlinkParameter parameter : parameters.getParameters())
432 {
433 sb.append(LESS);
434 sb.append(NODE_param);
435 sb.append(SPACE);
436 sb.append(ATTRIBUTE_name);
437 sb.append(EQUAL_QUOTE);
438 sb.append(parameter.getName());
439 sb.append(QUOTE);
440 sb.append(GREATER);
441
442 if (parameter.getValue() != null)
443 {
444 String strValue = JRValueStringUtils.serialize(parameter.getValueClass(), parameter.getValue());
445 sb.append(JRStringUtil.xmlEncode(strValue));
446 }
447
448 sb.append(LESS_SLASH);
449 sb.append(NODE_param);
450 sb.append(GREATER);
451 }
452 }
453 }
454
455 sb.append(JRStringUtil.xmlEncode(chunk));
456
457 if (hlink != null)
458 {
459 sb.append(LESS_SLASH);
460 sb.append(NODE_a);
461 sb.append(GREATER);
462 }
463
464 if (isSuper || isSub)
465 {
466 sb.append(LESS_SLASH);
467 sb.append(scriptNode);
468 sb.append(GREATER);
469 }
470 }
471
472
475 private void parseStyle(JRStyledText styledText, Node parentNode) throws SAXException
476 {
477 NodeList nodeList = parentNode.getChildNodes();
478 for(int i = 0; i < nodeList.getLength(); i++)
479 {
480 Node node = nodeList.item(i);
481 if (node.getNodeType() == Node.TEXT_NODE)
482 {
483 styledText.append(node.getNodeValue());
484 }
485 else if (
486 node.getNodeType() == Node.ELEMENT_NODE
487 && NODE_style.equals(node.getNodeName())
488 )
489 {
490 NamedNodeMap nodeAttrs = node.getAttributes();
491
492 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
493
494 if (nodeAttrs.getNamedItem(ATTRIBUTE_fontName) != null)
495 {
496 styleAttrs.put(
497 TextAttribute.FAMILY,
498 nodeAttrs.getNamedItem(ATTRIBUTE_fontName).getNodeValue()
499 );
500 }
501
502 if (nodeAttrs.getNamedItem(ATTRIBUTE_isBold) != null)
503 {
504 styleAttrs.put(
505 TextAttribute.WEIGHT,
506 Boolean.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_isBold).getNodeValue())
507 ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR
508 );
509 }
510
511 if (nodeAttrs.getNamedItem(ATTRIBUTE_isItalic) != null)
512 {
513 styleAttrs.put(
514 TextAttribute.POSTURE,
515 Boolean.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_isItalic).getNodeValue())
516 ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR
517 );
518 }
519
520 if (nodeAttrs.getNamedItem(ATTRIBUTE_isUnderline) != null)
521 {
522 styleAttrs.put(
523 TextAttribute.UNDERLINE,
524 Boolean.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_isUnderline).getNodeValue())
525 ? TextAttribute.UNDERLINE_ON : null
526 );
527 }
528
529 if (nodeAttrs.getNamedItem(ATTRIBUTE_isStrikeThrough) != null)
530 {
531 styleAttrs.put(
532 TextAttribute.STRIKETHROUGH,
533 Boolean.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_isStrikeThrough).getNodeValue())
534 ? TextAttribute.STRIKETHROUGH_ON : null
535 );
536 }
537
538 if (nodeAttrs.getNamedItem(ATTRIBUTE_size) != null)
539 {
540 styleAttrs.put(
541 TextAttribute.SIZE,
542 Float.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_size).getNodeValue())
543 );
544 }
545
546 if (nodeAttrs.getNamedItem(ATTRIBUTE_pdfFontName) != null)
547 {
548 styleAttrs.put(
549 JRTextAttribute.PDF_FONT_NAME,
550 nodeAttrs.getNamedItem(ATTRIBUTE_pdfFontName).getNodeValue()
551 );
552 }
553
554 if (nodeAttrs.getNamedItem(ATTRIBUTE_pdfEncoding) != null)
555 {
556 styleAttrs.put(
557 JRTextAttribute.PDF_ENCODING,
558 nodeAttrs.getNamedItem(ATTRIBUTE_pdfEncoding).getNodeValue()
559 );
560 }
561
562 if (nodeAttrs.getNamedItem(ATTRIBUTE_isPdfEmbedded) != null)
563 {
564 styleAttrs.put(
565 JRTextAttribute.IS_PDF_EMBEDDED,
566 Boolean.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_isPdfEmbedded).getNodeValue())
567 );
568 }
569
570 if (nodeAttrs.getNamedItem(ATTRIBUTE_forecolor) != null)
571 {
572 Color color =
573 JRColorUtil.getColor(
574 nodeAttrs.getNamedItem(ATTRIBUTE_forecolor).getNodeValue(),
575 Color.black
576 );
577 styleAttrs.put(
578 TextAttribute.FOREGROUND,
579 color
580 );
581 }
582
583 if (nodeAttrs.getNamedItem(ATTRIBUTE_backcolor) != null)
584 {
585 Color color =
586 JRColorUtil.getColor(
587 nodeAttrs.getNamedItem(ATTRIBUTE_backcolor).getNodeValue(),
588 Color.black
589 );
590 styleAttrs.put(
591 TextAttribute.BACKGROUND,
592 color
593 );
594 }
595
596 int startIndex = styledText.length();
597
598 parseStyle(styledText, node);
599
600 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
601 }
602 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_bold.equalsIgnoreCase(node.getNodeName()))
603 {
604 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
605 styleAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
606
607 int startIndex = styledText.length();
608
609 parseStyle(styledText, node);
610
611 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
612 }
613 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_italic.equalsIgnoreCase(node.getNodeName()))
614 {
615 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
616 styleAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
617
618 int startIndex = styledText.length();
619
620 parseStyle(styledText, node);
621
622 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
623 }
624 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_underline.equalsIgnoreCase(node.getNodeName()))
625 {
626 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
627 styleAttrs.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
628
629 int startIndex = styledText.length();
630
631 parseStyle(styledText, node);
632
633 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
634 }
635 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_sup.equalsIgnoreCase(node.getNodeName()))
636 {
637 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
638 styleAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
639
640 int startIndex = styledText.length();
641
642 parseStyle(styledText, node);
643
644 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
645 }
646 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_sub.equalsIgnoreCase(node.getNodeName()))
647 {
648 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
649 styleAttrs.put(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
650
651 int startIndex = styledText.length();
652
653 parseStyle(styledText, node);
654
655 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
656 }
657 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_font.equalsIgnoreCase(node.getNodeName()))
658 {
659 NamedNodeMap nodeAttrs = node.getAttributes();
660
661 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
662
663 if (nodeAttrs.getNamedItem(ATTRIBUTE_size) != null)
664 {
665 styleAttrs.put(
666 TextAttribute.SIZE,
667 Float.valueOf(nodeAttrs.getNamedItem(ATTRIBUTE_size).getNodeValue())
668 );
669 }
670
671 if (nodeAttrs.getNamedItem(ATTRIBUTE_color) != null)
672 {
673 Color color =
674 JRColorUtil.getColor(
675 nodeAttrs.getNamedItem(ATTRIBUTE_color).getNodeValue(),
676 Color.black
677 );
678 styleAttrs.put(
679 TextAttribute.FOREGROUND,
680 color
681 );
682 }
683
684 if (nodeAttrs.getNamedItem(ATTRIBUTE_fontFace) != null)
685 {
686 String fontFaces = nodeAttrs.getNamedItem(ATTRIBUTE_fontFace).getNodeValue();
687
688 StringTokenizer t = new StringTokenizer(fontFaces, ",");
689 while (t.hasMoreTokens())
690 {
691 String face = t.nextToken().trim();
692 if (AVAILABLE_FONT_FACE_NAMES.contains(face))
693 {
694 styleAttrs.put(TextAttribute.FAMILY, face);
695 break;
696 }
697 }
698 }
699
700 int startIndex = styledText.length();
701
702 parseStyle(styledText, node);
703
704 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
705
706 }
707 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_br.equalsIgnoreCase(node.getNodeName()))
708 {
709 styledText.append("\n");
710
711 int startIndex = styledText.length();
712 resizeRuns(styledText.getRuns(), startIndex, 1);
713
714 parseStyle(styledText, node);
715 styledText.addRun(new JRStyledText.Run(new HashMap<Attribute,Object>(), startIndex, styledText.length()));
716
717 if (startIndex < styledText.length()) {
718 styledText.append("\n");
719 resizeRuns(styledText.getRuns(), startIndex, 1);
720 }
721 }
722 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_li.equalsIgnoreCase(node.getNodeName()))
723 {
724 String tmpText = styledText.getText();
725 if(tmpText.length() > 0 && !tmpText.endsWith("\n"))
726 {
727 styledText.append("\n");
728 }
729 styledText.append(" \u2022 ");
730
731 int startIndex = styledText.length();
732 resizeRuns(styledText.getRuns(), startIndex, 1);
733 parseStyle(styledText, node);
734 styledText.addRun(new JRStyledText.Run(new HashMap<Attribute,Object>(), startIndex, styledText.length()));
735
736
737
738 Node nextNode = node.getNextSibling();
739 String textContent = getFirstTextOccurence(nextNode);
740 if(nextNode != null &&
741 !((nextNode.getNodeType() == Node.ELEMENT_NODE &&
742 NODE_li.equalsIgnoreCase(nextNode.getNodeName()) ||
743 (textContent != null && textContent.startsWith("\n")))
744 ))
745 {
746 styledText.append("\n");
747 resizeRuns(styledText.getRuns(), startIndex, 1);
748 }
749 }
750 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_a.equalsIgnoreCase(node.getNodeName()))
751 {
752 if (hyperlink == null)
753 {
754 NamedNodeMap nodeAttrs = node.getAttributes();
755
756 Map<Attribute,Object> styleAttrs = new HashMap<Attribute,Object>();
757
758 hyperlink = new JRBasePrintHyperlink();
759 hyperlink.setHyperlinkType(HyperlinkTypeEnum.REFERENCE);
760 styleAttrs.put(JRTextAttribute.HYPERLINK, hyperlink);
761
762 if (nodeAttrs.getNamedItem(ATTRIBUTE_href) != null)
763 {
764 hyperlink.setHyperlinkReference( nodeAttrs.getNamedItem(ATTRIBUTE_href).getNodeValue());
765 }
766
767 if (nodeAttrs.getNamedItem(ATTRIBUTE_type) != null)
768 {
769 hyperlink.setLinkType(nodeAttrs.getNamedItem(ATTRIBUTE_type).getNodeValue());
770 }
771
772 if (nodeAttrs.getNamedItem(ATTRIBUTE_target) != null)
773 {
774 hyperlink.setLinkTarget(nodeAttrs.getNamedItem(ATTRIBUTE_target).getNodeValue());
775 }
776
777 int startIndex = styledText.length();
778
779 parseStyle(styledText, node);
780
781 styledText.addRun(new JRStyledText.Run(styleAttrs, startIndex, styledText.length()));
782
783 hyperlink = null;
784 }
785 else
786 {
787 throw new SAXException("Hyperlink <a> tags cannot be nested.");
788 }
789 }
790 else if (node.getNodeType() == Node.ELEMENT_NODE && NODE_param.equalsIgnoreCase(node.getNodeName()))
791 {
792 if (hyperlink == null)
793 {
794 throw new SAXException("Hyperlink <param> tags must appear inside an <a> tag only.");
795 }
796 else
797 {
798 NamedNodeMap nodeAttrs = node.getAttributes();
799
800 JRPrintHyperlinkParameter parameter = new JRPrintHyperlinkParameter();
801
802 if (nodeAttrs.getNamedItem(ATTRIBUTE_name) != null)
803 {
804 parameter.setName(nodeAttrs.getNamedItem(ATTRIBUTE_name).getNodeValue());
805 }
806
807 if (nodeAttrs.getNamedItem(ATTRIBUTE_valueClass) != null)
808 {
809 parameter.setValueClass(nodeAttrs.getNamedItem(ATTRIBUTE_valueClass).getNodeValue());
810 }
811
812 String strValue = node.getTextContent();
813 if (strValue != null)
814 {
815 Object value = JRValueStringUtils.deserialize(parameter.getValueClass(), strValue);
816 parameter.setValue(value);
817 }
818
819 hyperlink.addHyperlinkParameter(parameter);
820 }
821 }
822 else if (node.getNodeType() == Node.ELEMENT_NODE)
823 {
824 String nodeName = "<" + node.getNodeName() + ">";
825 throw new SAXException("Tag " + nodeName + " is not a valid styled text tag.");
826 }
827 }
828 }
829
830
833 private void resizeRuns(List<Run> runs, int startIndex, int count)
834 {
835 for (int j = 0; j < runs.size(); j++)
836 {
837 JRStyledText.Run run = runs.get(j);
838 if (run.startIndex <= startIndex && run.endIndex > startIndex - count)
839 {
840 run.endIndex += count;
841 }
842 }
843 }
844
845
846
849 private StringBuilder writeStyleAttributes(Map<Attribute,Object> parentAttrs, Map<Attribute,Object> attrs)
850 {
851 StringBuilder sb = new StringBuilder();
852
853 Object value = attrs.get(TextAttribute.FAMILY);
854 Object oldValue = parentAttrs.get(TextAttribute.FAMILY);
855
856 if (value != null && !value.equals(oldValue))
857 {
858 sb.append(SPACE);
859 sb.append(ATTRIBUTE_fontName);
860 sb.append(EQUAL_QUOTE);
861 sb.append(value);
862 sb.append(QUOTE);
863 }
864
865 value = attrs.get(TextAttribute.WEIGHT);
866 oldValue = parentAttrs.get(TextAttribute.WEIGHT);
867
868 if (value != null && !value.equals(oldValue))
869 {
870 sb.append(SPACE);
871 sb.append(ATTRIBUTE_isBold);
872 sb.append(EQUAL_QUOTE);
873 sb.append(value.equals(TextAttribute.WEIGHT_BOLD));
874 sb.append(QUOTE);
875 }
876
877 value = attrs.get(TextAttribute.POSTURE);
878 oldValue = parentAttrs.get(TextAttribute.POSTURE);
879
880 if (value != null && !value.equals(oldValue))
881 {
882 sb.append(SPACE);
883 sb.append(ATTRIBUTE_isItalic);
884 sb.append(EQUAL_QUOTE);
885 sb.append(value.equals(TextAttribute.POSTURE_OBLIQUE));
886 sb.append(QUOTE);
887 }
888
889 value = attrs.get(TextAttribute.UNDERLINE);
890 oldValue = parentAttrs.get(TextAttribute.UNDERLINE);
891
892 if (
893 (value == null && oldValue != null)
894 || (value != null && !value.equals(oldValue))
895 )
896 {
897 sb.append(SPACE);
898 sb.append(ATTRIBUTE_isUnderline);
899 sb.append(EQUAL_QUOTE);
900 sb.append(value != null);
901 sb.append(QUOTE);
902 }
903
904 value = attrs.get(TextAttribute.STRIKETHROUGH);
905 oldValue = parentAttrs.get(TextAttribute.STRIKETHROUGH);
906
907 if (
908 (value == null && oldValue != null)
909 || (value != null && !value.equals(oldValue))
910 )
911 {
912 sb.append(SPACE);
913 sb.append(ATTRIBUTE_isStrikeThrough);
914 sb.append(EQUAL_QUOTE);
915 sb.append(value != null);
916 sb.append(QUOTE);
917 }
918
919 value = attrs.get(TextAttribute.SIZE);
920 oldValue = parentAttrs.get(TextAttribute.SIZE);
921
922 if (value != null && !value.equals(oldValue))
923 {
924 sb.append(SPACE);
925 sb.append(ATTRIBUTE_size);
926 sb.append(EQUAL_QUOTE);
927 sb.append(value);
928 sb.append(QUOTE);
929 }
930
931 value = attrs.get(JRTextAttribute.PDF_FONT_NAME);
932 oldValue = parentAttrs.get(JRTextAttribute.PDF_FONT_NAME);
933
934 if (value != null && !value.equals(oldValue))
935 {
936 sb.append(SPACE);
937 sb.append(ATTRIBUTE_pdfFontName);
938 sb.append(EQUAL_QUOTE);
939 sb.append(value);
940 sb.append(QUOTE);
941 }
942
943 value = attrs.get(JRTextAttribute.PDF_ENCODING);
944 oldValue = parentAttrs.get(JRTextAttribute.PDF_ENCODING);
945
946 if (value != null && !value.equals(oldValue))
947 {
948 sb.append(SPACE);
949 sb.append(ATTRIBUTE_pdfEncoding);
950 sb.append(EQUAL_QUOTE);
951 sb.append(value);
952 sb.append(QUOTE);
953 }
954
955 value = attrs.get(JRTextAttribute.IS_PDF_EMBEDDED);
956 oldValue = parentAttrs.get(JRTextAttribute.IS_PDF_EMBEDDED);
957
958 if (value != null && !value.equals(oldValue))
959 {
960 sb.append(SPACE);
961 sb.append(ATTRIBUTE_isPdfEmbedded);
962 sb.append(EQUAL_QUOTE);
963 sb.append(value);
964 sb.append(QUOTE);
965 }
966
967 value = attrs.get(TextAttribute.FOREGROUND);
968 oldValue = parentAttrs.get(TextAttribute.FOREGROUND);
969
970 if (value != null && !value.equals(oldValue))
971 {
972 sb.append(SPACE);
973 sb.append(ATTRIBUTE_forecolor);
974 sb.append(EQUAL_QUOTE);
975 sb.append(JRColorUtil.getCssColor((Color)value));
976 sb.append(QUOTE);
977 }
978
979 value = attrs.get(TextAttribute.BACKGROUND);
980 oldValue = parentAttrs.get(TextAttribute.BACKGROUND);
981
982 if (value != null && !value.equals(oldValue))
983 {
984 sb.append(SPACE);
985 sb.append(ATTRIBUTE_backcolor);
986 sb.append(EQUAL_QUOTE);
987 sb.append(JRColorUtil.getCssColor((Color)value));
988 sb.append(QUOTE);
989 }
990
991 return sb;
992 }
993
994
999 private String getFirstTextOccurence(Node node){
1000 if(node != null)
1001 {
1002 if(node.getNodeValue() != null)
1003 {
1004 return node.getNodeValue();
1005 }
1006 NodeList nodeList = node.getChildNodes();
1007 for (int i=0; i< nodeList.getLength(); i++)
1008 {
1009 String firstOccurence = getFirstTextOccurence(nodeList.item(i));
1010 if(firstOccurence != null)
1011 {
1012 return firstOccurence;
1013 }
1014 }
1015 }
1016 return null;
1017 }
1018
1019 @Override
1020 public void error(SAXParseException e) {
1021 if(log.isErrorEnabled())
1022 {
1023 log.error("Error parsing styled text.", e);
1024 }
1025 }
1026
1027 @Override
1028 public void fatalError(SAXParseException e) {
1029 if(log.isFatalEnabled())
1030 {
1031 log.fatal("Error parsing styled text.", e);
1032 }
1033 }
1034
1035 @Override
1036 public void warning(SAXParseException e) {
1037 if(log.isWarnEnabled())
1038 {
1039 log.warn("Error parsing styled text.", e);
1040 }
1041 }
1042
1043 }
1044