1
24 package net.sf.jasperreports.engine.fill;
25
26 import java.awt.Color;
27 import java.awt.font.TextAttribute;
28 import java.text.AttributedCharacterIterator.Attribute;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import net.sf.jasperreports.annotations.properties.Property;
34 import net.sf.jasperreports.annotations.properties.PropertyScope;
35 import net.sf.jasperreports.engine.JRCommonText;
36 import net.sf.jasperreports.engine.JRException;
37 import net.sf.jasperreports.engine.JRFont;
38 import net.sf.jasperreports.engine.JRLineBox;
39 import net.sf.jasperreports.engine.JRParagraph;
40 import net.sf.jasperreports.engine.JRPrintText;
41 import net.sf.jasperreports.engine.JRPropertiesUtil;
42 import net.sf.jasperreports.engine.JRRuntimeException;
43 import net.sf.jasperreports.engine.JRStyle;
44 import net.sf.jasperreports.engine.JRTextElement;
45 import net.sf.jasperreports.engine.base.JRBaseStyle;
46 import net.sf.jasperreports.engine.fonts.FontUtil;
47 import net.sf.jasperreports.engine.type.HorizontalTextAlignEnum;
48 import net.sf.jasperreports.engine.type.ModeEnum;
49 import net.sf.jasperreports.engine.type.RotationEnum;
50 import net.sf.jasperreports.engine.type.RunDirectionEnum;
51 import net.sf.jasperreports.engine.type.VerticalTextAlignEnum;
52 import net.sf.jasperreports.engine.util.JRSingletonCache;
53 import net.sf.jasperreports.engine.util.JRStringUtil;
54 import net.sf.jasperreports.engine.util.JRStyledText;
55 import net.sf.jasperreports.engine.util.JRStyledText.Run;
56 import net.sf.jasperreports.engine.util.JRTextAttribute;
57 import net.sf.jasperreports.engine.util.JRTextMeasurerUtil;
58 import net.sf.jasperreports.engine.util.MarkupProcessor;
59 import net.sf.jasperreports.engine.util.MarkupProcessorFactory;
60 import net.sf.jasperreports.engine.util.StyleUtil;
61 import net.sf.jasperreports.properties.PropertyConstants;
62
63
64
67 public abstract class JRFillTextElement extends JRFillElement implements JRTextElement
68 {
69
70 public static final String EXCEPTION_MESSAGE_KEY_MISSING_MARKUP_PROCESSOR_FACTORY = "fill.text.element.missing.markup.processor.factory";
71 public static final String EXCEPTION_MESSAGE_KEY_INVALID_START_INDEX = "fill.text.element.invalid.start.index";
72
73 @Property(
74 category = PropertyConstants.CATEGORY_FILL,
75 defaultValue = PropertyConstants.BOOLEAN_TRUE,
76 scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.TEXT_ELEMENT},
77 sinceVersion = PropertyConstants.VERSION_6_3_1,
78 valueType = Boolean.class
79 )
80 public static final String PROPERTY_CONSUME_SPACE_ON_OVERFLOW =
81 JRPropertiesUtil.PROPERTY_PREFIX + "consume.space.on.overflow";
82
83 @Property(
84 category = PropertyConstants.CATEGORY_FILL,
85 defaultValue = "0.5",
86 scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.TEXT_ELEMENT},
87 sinceVersion = PropertyConstants.VERSION_6_11_0,
88 valueType = Float.class
89 )
90 public static final String PROPERTY_SCALE_FONT_STEP_LIMIT =
91 JRPropertiesUtil.PROPERTY_PREFIX + "scale.font.step.limit";
92
93
96 private static final JRSingletonCache<MarkupProcessorFactory> markupProcessorFactoryCache =
97 new JRSingletonCache<MarkupProcessorFactory>(MarkupProcessorFactory.class);
98 private static final Map<String,MarkupProcessor> markupProcessors = new HashMap<String,MarkupProcessor>();
99
100
103 private boolean isLeftToRight = true;
104 private JRTextMeasurer textMeasurer;
105 private float lineSpacingFactor;
106 private float leadingOffset;
107 private float textWidth;
108 private float textHeight;
109
110 private int textStart;
111 private int textEnd;
112 private boolean isCutParagraphOverflow;
113 private boolean isCutParagraphToContinueInOverflow;
114 private short[] lineBreakOffsets;
115 private String textTruncateSuffix;
116 private String oldRawText;
117 private String rawText;
118 private JRStyledText styledText;
119 private JRStyledText processedStyledText;
120 private Map<JRStyle,Map<Attribute,Object>> styledTextAttributesMap = new HashMap<JRStyle,Map<Attribute,Object>>();
121
122 protected final JRLineBox initLineBox;
123 protected final JRParagraph initParagraph;
124 protected JRLineBox lineBox;
125 protected JRParagraph paragraph;
126
127 private JRStyle currentFillStyle;
128 private FillStyleObjects fillStyleObjects;
129 private Map<JRStyle, FillStyleObjects> fillStyleObjectsMap;
130
131 private Boolean defaultConsumeSpaceOnOverflow;
132 private boolean dynamicConsumeSpaceOnOverflow;
133 private Boolean defaultKeepFullText;
134 private boolean dynamicKeepFullText;
135 private Float defaultScaleFontStepLimit;
136 private boolean dynamicScaleFontStepLimit;
137
138
141 protected JRFillTextElement(
142 JRBaseFiller filler,
143 JRTextElement textElement,
144 JRFillObjectFactory factory
145 )
146 {
147 super(filler, textElement, factory);
148
149 initLineBox = textElement.getLineBox().clone(this);
150 initParagraph = textElement.getParagraph().clone(this);
151
152 this.dynamicConsumeSpaceOnOverflow = hasDynamicProperty(PROPERTY_CONSUME_SPACE_ON_OVERFLOW);
153 this.dynamicKeepFullText = hasDynamicProperty(JRTextElement.PROPERTY_PRINT_KEEP_FULL_TEXT);
154 this.dynamicScaleFontStepLimit = hasDynamicProperty(PROPERTY_SCALE_FONT_STEP_LIMIT);
155
156 this.fillStyleObjectsMap = new HashMap<JRStyle, JRFillTextElement.FillStyleObjects>();
157 }
158
159
160 protected JRFillTextElement(JRFillTextElement textElement, JRFillCloneFactory factory)
161 {
162 super(textElement, factory);
163
164 initLineBox = textElement.getLineBox().clone(this);
165 initParagraph = textElement.getParagraph().clone(this);
166
167 this.defaultConsumeSpaceOnOverflow = textElement.defaultConsumeSpaceOnOverflow;
168 this.dynamicConsumeSpaceOnOverflow = textElement.dynamicConsumeSpaceOnOverflow;
169 this.defaultKeepFullText = textElement.defaultKeepFullText;
170 this.dynamicKeepFullText = textElement.dynamicKeepFullText;
171 this.defaultScaleFontStepLimit = textElement.defaultScaleFontStepLimit;
172 this.dynamicScaleFontStepLimit = textElement.dynamicScaleFontStepLimit;
173
174 this.fillStyleObjectsMap = textElement.fillStyleObjectsMap;
175 }
176
177
178 private void createTextMeasurer()
179 {
180 textMeasurer = JRTextMeasurerUtil.getInstance(filler.getJasperReportsContext()).createTextMeasurer(this);
181 }
182
183 protected void ensureTextMeasurer()
184 {
185 if (textMeasurer == null)
186 {
187 createTextMeasurer();
188 }
189 }
190
191
192 @Override
193 protected void evaluateStyle(
194 byte evaluation
195 ) throws JRException
196 {
197 super.evaluateStyle(evaluation);
198
199 if (providerStyle == null)
200 {
201 lineBox = null;
202 paragraph = null;
203
204 setFillStyleObjects();
205 }
206 else
207 {
208 lineBox = initLineBox.clone(this);
209 paragraph = initParagraph.clone(this);
210 StyleUtil.appendBox(lineBox, providerStyle.getLineBox());
211 StyleUtil.appendParagraph(paragraph, providerStyle.getParagraph());
212
213 fillStyleObjects = null;
214 }
215 }
216
217 private void setFillStyleObjects()
218 {
219 JRStyle evaluatedStyle = getStyle();
220
221
222 if (fillStyleObjects != null && currentFillStyle == evaluatedStyle)
223 {
224 return;
225 }
226
227
228 currentFillStyle = evaluatedStyle;
229
230
231 fillStyleObjects = fillStyleObjectsMap.get(evaluatedStyle);
232 if (fillStyleObjects == null)
233 {
234
235 CachingLineBox cachedLineBox = new CachingLineBox(initLineBox);
236 CachingParagraph cachedParagraph = new CachingParagraph(initParagraph);
237 fillStyleObjects = new FillStyleObjects(cachedLineBox, cachedParagraph);
238
239 fillStyleObjectsMap.put(evaluatedStyle, fillStyleObjects);
240 }
241 }
242
243
244 @Override
245 public ModeEnum getModeValue()
246 {
247 return getStyleResolver().getMode(this, ModeEnum.TRANSPARENT);
248 }
249
250 @Override
251 public HorizontalTextAlignEnum getHorizontalTextAlign()
252 {
253 return getStyleResolver().getHorizontalTextAlign(this);
254 }
255
256 @Override
257 public HorizontalTextAlignEnum getOwnHorizontalTextAlign()
258 {
259 return providerStyle == null || providerStyle.getOwnHorizontalTextAlign() == null ? ((JRTextElement)this.parent).getOwnHorizontalTextAlign() : providerStyle.getOwnHorizontalTextAlign();
260 }
261
262 @Override
263 public void setHorizontalTextAlign(HorizontalTextAlignEnum horizontalAlignment)
264 {
265 throw new UnsupportedOperationException();
266 }
267
268 @Override
269 public VerticalTextAlignEnum getVerticalTextAlign()
270 {
271 return getStyleResolver().getVerticalTextAlign(this);
272 }
273
274 @Override
275 public VerticalTextAlignEnum getOwnVerticalTextAlign()
276 {
277 return providerStyle == null || providerStyle.getOwnVerticalTextAlign() == null ? ((JRTextElement)this.parent).getOwnVerticalTextAlign() : providerStyle.getOwnVerticalTextAlign();
278 }
279
280 @Override
281 public void setVerticalTextAlign(VerticalTextAlignEnum verticalAlignment)
282 {
283 throw new UnsupportedOperationException();
284 }
285
286 @Override
287 public RotationEnum getRotationValue()
288 {
289 return getStyleResolver().getRotationValue(this);
290 }
291
292 @Override
293 public RotationEnum getOwnRotationValue()
294 {
295 return providerStyle == null || providerStyle.getOwnRotationValue() == null ? ((JRTextElement)this.parent).getOwnRotationValue() : providerStyle.getOwnRotationValue();
296 }
297
298 @Override
299 public void setRotation(RotationEnum rotation)
300 {
301 throw new UnsupportedOperationException();
302 }
303
304 @Override
305 public String getMarkup()
306 {
307 return getStyleResolver().getMarkup(this);
308 }
309
310 @Override
311 public String getOwnMarkup()
312 {
313 return providerStyle == null || providerStyle.getOwnMarkup() == null ? ((JRTextElement)parent).getOwnMarkup() : providerStyle.getOwnMarkup();
314 }
315
316 @Override
317 public void setMarkup(String markup)
318 {
319 throw new UnsupportedOperationException();
320 }
321
322 protected JRLineBox getPrintLineBox()
323 {
324 return lineBox == null ? initLineBox : lineBox;
325 }
326
327 @Override
328 public JRLineBox getLineBox()
329 {
330 return lineBox == null
331 ? (fillStyleObjects == null ? initLineBox : fillStyleObjects.lineBox)
332 : lineBox;
333 }
334
335 protected JRParagraph getPrintParagraph()
336 {
337 return paragraph == null ? initParagraph : paragraph;
338 }
339
340 @Override
341 public JRParagraph getParagraph()
342 {
343 return paragraph == null
344 ? (fillStyleObjects == null ? initParagraph : fillStyleObjects.paragraph)
345 : paragraph;
346 }
347
348
351 public JRFont getFont()
352 {
353 return this;
354 }
355
356
357
360 protected Map<Attribute,Object> getStyledTextAttributes()
361 {
362 JRStyle style = getStyle();
363 Map<Attribute,Object> styledTextAttributes = styledTextAttributesMap.get(style);
364 if (styledTextAttributes == null)
365 {
366 styledTextAttributes = new HashMap<Attribute,Object>();
367
368 FontUtil.getInstance(filler.getJasperReportsContext()).getAttributesWithoutAwtFont(styledTextAttributes, this);
369 styledTextAttributes.put(TextAttribute.FOREGROUND, getForecolor());
370 if (getModeValue() == ModeEnum.OPAQUE)
371 {
372 styledTextAttributes.put(TextAttribute.BACKGROUND, getBackcolor());
373 }
374 styledTextAttributesMap.put(style, styledTextAttributes);
375 }
376
377 return styledTextAttributes;
378 }
379
380
383 protected float getLineSpacingFactor()
384 {
385 return lineSpacingFactor;
386 }
387
388
391 protected void setLineSpacingFactor(float lineSpacingFactor)
392 {
393 this.lineSpacingFactor = lineSpacingFactor;
394 }
395
396
399 protected float getLeadingOffset()
400 {
401 return leadingOffset;
402 }
403
404
407 protected void setLeadingOffset(float leadingOffset)
408 {
409 this.leadingOffset = leadingOffset;
410 }
411
412
415 public RunDirectionEnum getRunDirectionValue()
416 {
417 return isLeftToRight ? RunDirectionEnum.LTR : RunDirectionEnum.RTL;
418 }
419
420
423 public float getTextWidth()
424 {
425 return textWidth;
426 }
427
428
431 protected void setTextWidth(float textWidth)
432 {
433 this.textWidth = textWidth;
434 }
435
436
439 protected float getTextHeight()
440 {
441 return textHeight;
442 }
443
444
447 protected void setTextHeight(float textHeight)
448 {
449 this.textHeight = textHeight;
450 }
451
452
455 protected int getTextStart()
456 {
457 return textStart;
458 }
459
460
463 protected void setTextStart(int textStart)
464 {
465 this.textStart = textStart;
466 }
467
468
471 protected int getTextEnd()
472 {
473 return textEnd;
474 }
475
476
479 protected void setTextEnd(int textEnd)
480 {
481 this.textEnd = textEnd;
482 }
483
484
487 protected boolean isCutParagraphToContinueInOverflow()
488 {
489 return isCutParagraphToContinueInOverflow;
490 }
491
492
495 protected void setCutParagraphToContinueInOverflow(boolean isCutParagraphToContinueInOverflow)
496 {
497 this.isCutParagraphToContinueInOverflow = isCutParagraphToContinueInOverflow;
498 }
499
500 protected short[] getLineBreakOffsets()
501 {
502 return lineBreakOffsets;
503 }
504
505 protected void setLineBreakOffsets(short[] lineBreakOffsets)
506 {
507 this.lineBreakOffsets = lineBreakOffsets;
508 }
509
510 protected void resetTextChunk()
511 {
512 textStart = 0;
513 textEnd = 0;
514 textTruncateSuffix = null;
515 lineBreakOffsets = null;
516
517 }
518
519
522 protected String getRawText()
523 {
524 return rawText;
525 }
526
527
530 protected void setRawText(String rawText)
531 {
532 this.oldRawText = this.rawText;
533 this.rawText = rawText;
534 styledText = null;
535 processedStyledText = null;
536 }
537
538
539 @Override
540 public void reset()
541 {
542 super.reset();
543
544 isLeftToRight = true;
545 lineSpacingFactor = 0;
546 leadingOffset = 0;
547 textHeight = 0;
548
549 }
550
551
552 @Override
553 public void rewind()
554 {
555 @SuppressWarnings("deprecation")
556 boolean isLegacyBandEvaluationEnabled = filler.getFillContext().isLegacyBandEvaluationEnabled();
557 if (!isLegacyBandEvaluationEnabled)
558 {
559 this.rawText = this.oldRawText;
560 }
561 resetTextChunk();
562 }
563
564
565
568 protected JRStyledText getStyledText()
569 {
570 if (styledText == null)
571 {
572 String text = getRawText();
573 if (text != null)
574 {
575 styledText =
576 filler.getStyledTextParser().getStyledText(
577 getStyledTextAttributes(),
578 text,
579 !JRCommonText.MARKUP_NONE.equals(getMarkup()),
580 filler.getLocale()
581 );
582 }
583 processedStyledText = null;
584 }
585
586 return styledText;
587 }
588
589 protected JRStyledText getProcessedStyledText()
590 {
591 if (processedStyledText == null)
592 {
593 JRStyledText text = getStyledText();
594 if (text != null)
595 {
596 processedStyledText = filler.getStyledTextUtil().resolveFonts(text, filler.getLocale());
597 }
598 }
599 return processedStyledText;
600 }
601
602
605 public String getTextString()
606 {
607 JRStyledText tmpStyledText = getStyledText();
608
609 if (tmpStyledText == null)
610 {
611 return null;
612 }
613
614 return tmpStyledText.getText();
615 }
616
617
618 @Override
619 protected boolean prepare(
620 int availableHeight,
621 boolean isOverflow
622 ) throws JRException
623 {
624 isCutParagraphOverflow = isCutParagraphToContinueInOverflow;
625
626 return super.prepare(availableHeight, isOverflow);
627 }
628
629
630
633 protected void chopTextElement(
634 int availableStretchHeight
635 )
636 {
637 ensureTextMeasurer();
638
639 JRStyledText tmpStyledText = getStyledText();
640
641 if (tmpStyledText == null)
642 {
643 return;
644 }
645
646 int fullTextLength = tmpStyledText.getText().length();
647 if (getTextEnd() == fullTextLength)
648 {
649 return;
650 }
651
652 boolean canOverflow = canOverflow();
653 JRStyledText processedText = getProcessedStyledText();
654 JRMeasuredText measuredText = textMeasurer.measure(
655 processedText,
656 getTextEnd(),
657 availableStretchHeight,
658 !isCutParagraphOverflow,
659 canOverflow
660 );
661
662 if (
663 scaleFontToFit()
664 && measuredText.getTextOffset() < fullTextLength
665 )
666 {
667
668
669
670 JRStyledText tmpProcessedText = processedText.cloneText();
671
672 JRMeasuredText tmpMeasuredText = measuredText;
673 JRMeasuredText lastGoodMeasuredText = measuredText;
674 JRMeasuredText lastBadMeasuredText = measuredText;
675
676 float factor = 1f;
677 float lastGoodFactor = 1f;
678 float lastBadFactor = 1f;
679 float delta = 1f;
680 int deltaSign = -1;
681 int oldDeltaSign = -1;
682 float oldFontSizeMaxDiff = 0;
683 float scaleFontStepLimit = scaleFontStepLimit();
684
685 boolean keepMeasuring = true;
686
687 while (keepMeasuring)
688 {
689 delta = 0.5f * delta;
690
691 if (tmpMeasuredText.getTextOffset() < fullTextLength)
692 {
693 lastBadMeasuredText = tmpMeasuredText;
694 lastBadFactor = factor;
695
696 deltaSign = -1;
697 factor = factor - delta;
698 }
699 else
700 {
701 lastGoodMeasuredText = tmpMeasuredText;
702 lastGoodFactor = factor;
703
704 deltaSign = 1;
705 factor = factor + delta;
706 }
707
708 float newFontSizeMaxDiff = alterFontSizes(tmpProcessedText, factor, scaleFontStepLimit);
709 keepMeasuring =
710 newFontSizeMaxDiff != 0
711 && (newFontSizeMaxDiff != scaleFontStepLimit || deltaSign * newFontSizeMaxDiff != - oldDeltaSign * oldFontSizeMaxDiff);
712 if (keepMeasuring)
713 {
714 tmpMeasuredText = textMeasurer.measure(
715 tmpProcessedText,
716 getTextEnd(),
717 availableStretchHeight,
718 !isCutParagraphOverflow,
719 canOverflow
720 );
721 }
722
723 oldDeltaSign = deltaSign;
724 oldFontSizeMaxDiff = newFontSizeMaxDiff;
725 }
726
727 if (lastGoodFactor == 1f)
728 {
729 lastGoodFactor = lastBadFactor;
730 measuredText = lastBadMeasuredText;
731 }
732 else
733 {
734 measuredText = lastGoodMeasuredText;
735 }
736
737 if (!JRCommonText.MARKUP_NONE.equals(getMarkup()))
738 {
739
740
741
742 styledText = styledText.cloneText();
743 alterFontSizes(styledText, lastGoodFactor, scaleFontStepLimit);
744 }
745
746 if (providerStyle == null)
747 {
748 providerStyle = new JRBaseStyle();
749 }
750 providerStyle.setFontSize(scaleFontSize(getFontsize(), lastGoodFactor, scaleFontStepLimit));
751 }
752
753 isLeftToRight = measuredText.isLeftToRight();
754 setTextWidth(measuredText.getTextWidth());
755 setTextHeight(measuredText.getTextHeight());
756
757
758 if (getRotationValue().equals(RotationEnum.NONE))
759 {
760
761
762 int elementTextHeight = (int) getTextHeight() + getLineBox().getTopPadding() + getLineBox().getBottomPadding();
763 if (
764 measuredText.getTextOffset() >= fullTextLength
765 || !canOverflow
766 || !isConsumeSpaceOnOverflow()
767 )
768 {
769 setPrepareHeight(elementTextHeight);
770 }
771 else
772 {
773
774 setPrepareHeight(getHeight() + availableStretchHeight);
775
776
777
778
779
780
781
782
783 }
784 }
785 else
786 {
787 setPrepareHeight(getHeight());
788 }
789
790 setTextStart(getTextEnd());
791 setTextEnd(measuredText.getTextOffset());
792 setCutParagraphToContinueInOverflow(canOverflow && measuredText.isParagraphCut());
793 setLineBreakOffsets(measuredText.getLineBreakOffsets());
794 setTextTruncateSuffix(measuredText.getTextSuffix());
795 setLineSpacingFactor(measuredText.getLineSpacingFactor());
796 setLeadingOffset(measuredText.getLeadingOffset());
797 }
798
799 private static float scaleFontSize(float fontSize, float factor, float scaleFontStepLimit)
800 {
801 float newFontSize = (int)(100 * (Math.round(factor * fontSize / scaleFontStepLimit) * scaleFontStepLimit)) / 100f;
802 newFontSize = newFontSize < scaleFontStepLimit ? scaleFontStepLimit : newFontSize;
803 return newFontSize;
804 }
805
806 private static float alterFontSizes(JRStyledText styledText, float factor, float scaleFontStepLimit)
807 {
808 float fontSizeMaxDiff = 0;
809 if (styledText != null && styledText.length() != 0)
810 {
811 fontSizeMaxDiff = alterFontSize(styledText.getGlobalAttributes(), factor, scaleFontStepLimit, fontSizeMaxDiff);
812
813 List<Run> runs = styledText.getRuns();
814 for (Run run : runs)
815 {
816 fontSizeMaxDiff = alterFontSize(run.attributes, factor, scaleFontStepLimit, fontSizeMaxDiff);
817 }
818 styledText.append("");
819 }
820 return fontSizeMaxDiff;
821 }
822
823 private static float alterFontSize(Map<Attribute, Object> attributes, float factor, float scaleFontStepLimit, float fontSizeMaxDiff)
824 {
825 Float originalFontSize = (Float)attributes.get(JRTextAttribute.FONT_SIZE);
826 if (originalFontSize == null)
827 {
828 originalFontSize = (Float)attributes.get(TextAttribute.SIZE);
829 if (originalFontSize != null)
830 {
831
832 attributes.put(JRTextAttribute.FONT_SIZE, originalFontSize);
833 }
834 }
835 if (originalFontSize != null)
836 {
837 Float newFontSize = scaleFontSize(originalFontSize, factor, scaleFontStepLimit);
838 Float oldFontSize = (Float)attributes.get(TextAttribute.SIZE);
839 fontSizeMaxDiff = Math.max(Math.abs(newFontSize - oldFontSize), fontSizeMaxDiff);
840 attributes.put(TextAttribute.SIZE, newFontSize);
841 }
842 return fontSizeMaxDiff;
843 }
844
845 protected boolean isConsumeSpaceOnOverflow()
846 {
847 if (defaultConsumeSpaceOnOverflow == null)
848 {
849 defaultConsumeSpaceOnOverflow = filler.getPropertiesUtil().getBooleanProperty(
850 PROPERTY_CONSUME_SPACE_ON_OVERFLOW, true,
851
852 parent, filler.getMainDataset());
853 }
854
855 boolean consumeSpaceOnOverflow = defaultConsumeSpaceOnOverflow;
856 if (dynamicConsumeSpaceOnOverflow)
857 {
858 String consumeSpaceOnOverflowProp = getDynamicProperties().getProperty(PROPERTY_CONSUME_SPACE_ON_OVERFLOW);
859 if (consumeSpaceOnOverflowProp != null)
860 {
861 consumeSpaceOnOverflow = JRPropertiesUtil.asBoolean(consumeSpaceOnOverflowProp);
862 }
863 }
864 return consumeSpaceOnOverflow;
865 }
866
867
868
869
870
871
872 protected abstract boolean canOverflow();
873
874 protected abstract boolean scaleFontToFit();
875
876
877 @Override
878 public String getFontName()
879 {
880 return getStyleResolver().getFontName(this);
881 }
882
883 @Override
884 public String getOwnFontName()
885 {
886 return providerStyle == null || providerStyle.getOwnFontName() == null ? ((JRFont)parent).getOwnFontName() : providerStyle.getOwnFontName();
887 }
888
889 @Override
890 public void setFontName(String fontName)
891 {
892 throw new UnsupportedOperationException();
893 }
894
895
896 @Override
897 public boolean isBold()
898 {
899 return getStyleResolver().isBold(this);
900 }
901
902 @Override
903 public Boolean isOwnBold()
904 {
905 return providerStyle == null || providerStyle.isOwnBold() == null ? ((JRFont)parent).isOwnBold() : providerStyle.isOwnBold();
906 }
907
908
911 @Override
912 public void setBold(boolean isBold)
913 {
914 throw new UnsupportedOperationException();
915 }
916
917
921 @Override
922 public void setBold(Boolean isBold)
923 {
924 throw new UnsupportedOperationException();
925 }
926
927
928 @Override
929 public boolean isItalic()
930 {
931 return getStyleResolver().isItalic(this);
932 }
933
934 @Override
935 public Boolean isOwnItalic()
936 {
937 return providerStyle == null || providerStyle.isOwnItalic() == null ? ((JRFont)parent).isOwnItalic() : providerStyle.isOwnItalic();
938 }
939
940
943 @Override
944 public void setItalic(boolean isItalic)
945 {
946 throw new UnsupportedOperationException();
947 }
948
949
953 @Override
954 public void setItalic(Boolean isItalic)
955 {
956 throw new UnsupportedOperationException();
957 }
958
959 @Override
960 public boolean isUnderline()
961 {
962 return getStyleResolver().isUnderline(this);
963 }
964
965 @Override
966 public Boolean isOwnUnderline()
967 {
968 return providerStyle == null || providerStyle.isOwnUnderline() == null ? ((JRFont)parent).isOwnUnderline() : providerStyle.isOwnUnderline();
969 }
970
971
974 @Override
975 public void setUnderline(boolean isUnderline)
976 {
977 throw new UnsupportedOperationException();
978 }
979
980
984 @Override
985 public void setUnderline(Boolean isUnderline)
986 {
987 throw new UnsupportedOperationException();
988 }
989
990 @Override
991 public boolean isStrikeThrough()
992 {
993 return getStyleResolver().isStrikeThrough(this);
994 }
995
996 @Override
997 public Boolean isOwnStrikeThrough()
998 {
999 return providerStyle == null || providerStyle.isOwnStrikeThrough() == null ? ((JRFont)parent).isOwnStrikeThrough() : providerStyle.isOwnStrikeThrough();
1000 }
1001
1002
1005 @Override
1006 public void setStrikeThrough(boolean isStrikeThrough)
1007 {
1008 throw new UnsupportedOperationException();
1009 }
1010
1011
1015 @Override
1016 public void setStrikeThrough(Boolean isStrikeThrough)
1017 {
1018 throw new UnsupportedOperationException();
1019 }
1020
1021 @Override
1022 public float getFontsize()
1023 {
1024 return getStyleResolver().getFontsize(this);
1025 }
1026
1027 @Override
1028 public Float getOwnFontsize()
1029 {
1030 return providerStyle == null || providerStyle.getOwnFontsize() == null ? ((JRFont)parent).getOwnFontsize() : providerStyle.getOwnFontsize();
1031 }
1032
1033 @Override
1034 public void setFontSize(Float size)
1035 {
1036 throw new UnsupportedOperationException();
1037 }
1038
1039 @Override
1040 public String getPdfFontName()
1041 {
1042 return getStyleResolver().getPdfFontName(this);
1043 }
1044
1045 @Override
1046 public String getOwnPdfFontName()
1047 {
1048 return providerStyle == null || providerStyle.getOwnPdfFontName() == null ? ((JRFont)parent).getOwnPdfFontName() : providerStyle.getOwnPdfFontName();
1049 }
1050
1051 @Override
1052 public void setPdfFontName(String pdfFontName)
1053 {
1054 throw new UnsupportedOperationException();
1055 }
1056
1057
1058 @Override
1059 public String getPdfEncoding()
1060 {
1061 return getStyleResolver().getPdfEncoding(this);
1062 }
1063
1064 @Override
1065 public String getOwnPdfEncoding()
1066 {
1067 return providerStyle == null || providerStyle.getOwnPdfEncoding() == null ? ((JRFont)parent).getOwnPdfEncoding() : providerStyle.getOwnPdfEncoding();
1068 }
1069
1070 @Override
1071 public void setPdfEncoding(String pdfEncoding)
1072 {
1073 throw new UnsupportedOperationException();
1074 }
1075
1076
1077 @Override
1078 public boolean isPdfEmbedded()
1079 {
1080 return getStyleResolver().isPdfEmbedded(this);
1081 }
1082
1083 @Override
1084 public Boolean isOwnPdfEmbedded()
1085 {
1086 return providerStyle == null || providerStyle.isOwnPdfEmbedded() == null ? ((JRFont)parent).isOwnPdfEmbedded() : providerStyle.isOwnPdfEmbedded();
1087 }
1088
1089
1092 @Override
1093 public void setPdfEmbedded(boolean isPdfEmbedded)
1094 {
1095 throw new UnsupportedOperationException();
1096 }
1097
1098
1102 @Override
1103 public void setPdfEmbedded(Boolean isPdfEmbedded)
1104 {
1105 throw new UnsupportedOperationException();
1106 }
1107
1108
1109 @Override
1110 public Color getDefaultLineColor()
1111 {
1112 return getForecolor();
1113 }
1114
1115
1116 @Override
1117 public void setHeight(int height)
1118 {
1119 super.setHeight(height);
1120
1121 createTextMeasurer();
1122 }
1123
1124
1125 @Override
1126 public void setWidth(int width)
1127 {
1128 super.setWidth(width);
1129
1130 createTextMeasurer();
1131 }
1132
1133 protected String processMarkupText(String text)
1134 {
1135 text = JRStringUtil.replaceCRwithLF(text);
1136
1137 if (text != null)
1138 {
1139 String markup = getMarkup();
1140 if (
1141 !JRCommonText.MARKUP_NONE.equals(markup)
1142 && !JRCommonText.MARKUP_STYLED_TEXT.equals(markup)
1143 )
1144 {
1145 text = getMarkupProcessor(markup).convert(text);
1146 }
1147 }
1148
1149 return text;
1150 }
1151
1152 protected MarkupProcessor getMarkupProcessor(String markup)
1153 {
1154 MarkupProcessor markupProcessor = markupProcessors.get(markup);
1155
1156 if (markupProcessor == null)
1157 {
1158 String factoryClass = filler.getPropertiesUtil().getProperty(MarkupProcessorFactory.PROPERTY_MARKUP_PROCESSOR_FACTORY_PREFIX + markup);
1159 if (factoryClass == null)
1160 {
1161 throw
1162 new JRRuntimeException(
1163 EXCEPTION_MESSAGE_KEY_MISSING_MARKUP_PROCESSOR_FACTORY,
1164 new Object[]{markup}
1165 );
1166 }
1167
1168 MarkupProcessorFactory factory = null;
1169 try
1170 {
1171 factory = markupProcessorFactoryCache.getCachedInstance(factoryClass);
1172 }
1173 catch (JRException e)
1174 {
1175 throw new JRRuntimeException(e);
1176 }
1177
1178 markupProcessor = factory.createMarkupProcessor();
1179 markupProcessors.put(markup, markupProcessor);
1180 }
1181
1182 return markupProcessor;
1183 }
1184
1185 protected void setPrintText(JRPrintText printText)
1186 {
1187 int startIndex = getTextStart();
1188 int endIndex = getTextEnd();
1189 JRStyledText fullStyledText = getStyledText();
1190 String fullText = fullStyledText.getText();
1191
1192 boolean keepAllText = !canOverflow() && keepFullText();
1193 if (keepAllText)
1194 {
1195
1196 if (startIndex != 0)
1197 {
1198 throw
1199 new JRRuntimeException(
1200 EXCEPTION_MESSAGE_KEY_INVALID_START_INDEX,
1201 (Object[])null
1202 );
1203 }
1204
1205 if (!JRCommonText.MARKUP_NONE.equals(getMarkup()))
1206 {
1207
1208 String styledText = filler.getStyledTextParser().write(
1209 fullStyledText);
1210 setPrintText(printText, styledText);
1211 }
1212 else
1213 {
1214 setPrintText(printText, fullText);
1215 }
1216
1217 if (endIndex < fullText.length())
1218 {
1219 printText.setTextTruncateIndex(endIndex);
1220 }
1221 }
1222 else
1223 {
1224 String printedText;
1225 if (!JRCommonText.MARKUP_NONE.equals(getMarkup()))
1226 {
1227 printedText = filler.getStyledTextParser().write(
1228 fullStyledText,
1229 startIndex, endIndex);
1230 }
1231 else
1232 {
1233
1234 printedText = fullText.substring(startIndex, endIndex);
1235 }
1236
1237 setPrintText(printText, printedText);
1238 }
1239
1240 printText.setTextTruncateSuffix(getTextTruncateSuffix());
1241 printText.setLineBreakOffsets(getLineBreakOffsets());
1242
1243 if (
1244 isCutParagraphOverflow
1245 && getParagraph().getFirstLineIndent() != 0
1246 )
1247 {
1248 printText.getPropertiesMap().setProperty(JRPrintText.PROPERTY_AWT_INDENT_FIRST_LINE, Boolean.FALSE.toString());
1249 }
1250
1251 if (
1252 fullText != null
1253 && endIndex < fullText.length()
1254 && HorizontalTextAlignEnum.JUSTIFIED == getHorizontalTextAlign()
1255 )
1256 {
1257 printText.getPropertiesMap().setProperty(JRPrintText.PROPERTY_AWT_JUSTIFY_LAST_LINE, Boolean.TRUE.toString());
1258 }
1259 }
1260
1261 protected boolean keepFullText()
1262 {
1263 if (defaultKeepFullText == null)
1264 {
1265 defaultKeepFullText = filler.getPropertiesUtil().getBooleanProperty(
1266 JRTextElement.PROPERTY_PRINT_KEEP_FULL_TEXT, false,
1267
1268 parent, filler.getMainDataset());
1269 }
1270
1271 boolean keepFullText = defaultKeepFullText;
1272 if (dynamicKeepFullText)
1273 {
1274 String keepFullTextProp = getDynamicProperties().getProperty(
1275 JRTextElement.PROPERTY_PRINT_KEEP_FULL_TEXT);
1276 if (keepFullTextProp != null)
1277 {
1278 keepFullText = JRPropertiesUtil.asBoolean(keepFullTextProp);
1279 }
1280 }
1281 return keepFullText;
1282 }
1283
1284 protected float scaleFontStepLimit()
1285 {
1286 if (defaultScaleFontStepLimit == null)
1287 {
1288 defaultScaleFontStepLimit = filler.getPropertiesUtil().getFloatProperty(
1289 PROPERTY_SCALE_FONT_STEP_LIMIT, 0.5f,
1290
1291 parent, filler.getMainDataset());
1292 }
1293
1294 float scaleFontStepLimit = defaultScaleFontStepLimit;
1295 if (dynamicScaleFontStepLimit)
1296 {
1297 String scaleFontStepLimitProp = getDynamicProperties().getProperty(
1298 PROPERTY_SCALE_FONT_STEP_LIMIT);
1299 if (scaleFontStepLimitProp != null)
1300 {
1301 scaleFontStepLimit = JRPropertiesUtil.asFloat(scaleFontStepLimitProp);
1302 }
1303 }
1304 return scaleFontStepLimit;
1305 }
1306
1307 protected void setPrintText(JRPrintText printText, String text)
1308 {
1309 printText.setText(text);
1310 }
1311
1312 protected String getTextTruncateSuffix()
1313 {
1314 return textTruncateSuffix;
1315 }
1316
1317 protected void setTextTruncateSuffix(String textTruncateSuffix)
1318 {
1319 this.textTruncateSuffix = textTruncateSuffix;
1320 }
1321
1322 private static class FillStyleObjects
1323 {
1324 private final JRLineBox lineBox;
1325 private final JRParagraph paragraph;
1326
1327 public FillStyleObjects(JRLineBox lineBox, JRParagraph paragraph)
1328 {
1329 this.lineBox = lineBox;
1330 this.paragraph = paragraph;
1331 }
1332 }
1333 }
1334