Skip to content

Commit 3eb911d

Browse files
ars18wrwiText-CI
authored andcommitted
Add some tests on TextRenderer extensions. Add appropriate documentation.
DEVSIX-5277
1 parent 5c6acf9 commit 3eb911d

File tree

5 files changed

+164
-2
lines changed

5 files changed

+164
-2
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ public final class LogMessageConstant {
8282
public static final String COMB_FLAG_MAY_BE_SET_ONLY_IF_MAXLEN_IS_PRESENT = "The Comb flag may be set only if the MaxLen entry is present in the text field dictionary and if the Multiline, Password, and FileSelect flags are clear.";
8383
public static final String COULD_NOT_FIND_GLYPH_WITH_CODE = "Could not find glyph with the following code: {0}";
8484
public static final String CREATED_ROOT_TAG_HAS_MAPPING = "Created root tag has role mapping: \"/Document\" role{0} is mapped{1}. Resulting tag structure might have invalid root tag.";
85+
public static final String CREATE_COPY_SHOULD_BE_OVERRIDDEN = "While processing an instance of TextRenderer, "
86+
+ "iText uses createCopy() to create glyph lines of specific fonts, which represent its parts. "
87+
+ "So if one extends TextRenderer, one should override createCopy, otherwise if FontSelector "
88+
+ "related logic is triggered, copies of this TextRenderer will have the default behavior "
89+
+ "rather than the custom one.";
8590
public static final String DESTINATION_NOT_PERMITTED_WHEN_ACTION_IS_SET = "Destinations are not permitted for link annotations that already have actions. The old action will be removed.";
8691
public static final String DIRECTONLY_OBJECT_CANNOT_BE_INDIRECT = "DirectOnly object cannot be indirect";
8792
public static final String DOCFONT_HAS_ILLEGAL_DIFFERENCES = "Document Font has illegal differences array. Entry {0} references a glyph ID over 255 and will be ignored.";

layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,10 +1209,12 @@ public float getTabAnchorCharacterPosition() {
12091209
* {@link #layout(LayoutContext)} is called more than once.
12101210
*
12111211
* <p>
1212-
* If a renderer overflows to the next area, iText uses this method to create a renderer
1212+
* If {@link TextRenderer} overflows to the next line, iText uses this method to create a renderer
12131213
* for the overflow part. So if one wants to extend {@link TextRenderer}, one should override
12141214
* this method: otherwise the default method will be used and thus the default rather than the custom
1215-
* renderer will be created.
1215+
* renderer will be created. Another method that should be overridden in case of
1216+
* {@TextRenderer}'s extension is {@link #createCopy(GlyphLine, PdfFont)}. This method is responsible
1217+
* for creation of {@TextRenderer}'s copies, which represent its parts of specific font.
12161218
* @return new renderer instance
12171219
*/
12181220
@Override
@@ -1591,7 +1593,24 @@ protected void setProcessedGlyphLineAndFont(GlyphLine gl, PdfFont font) {
15911593
setProperty(Property.FONT, font);
15921594
}
15931595

1596+
/**
1597+
* Creates a copy of this {@link TextRenderer}, which corresponds to the passed {@link GlyphLine}
1598+
* with {@link PdfFont}.
1599+
* <p>
1600+
* While processing {@link TextRenderer}, iText uses this method to create {@link GlyphLine glyph lines}
1601+
* of specific {@link PdfFont fonts}, which represent the {@link TextRenderer}'s parts. If one extends
1602+
* {@link TextRenderer}, one should override this method, otherwise if {@link com.itextpdf.layout.font.FontSelector}
1603+
* related logic is triggered, copies of this {@link TextRenderer} will have the default behavior rather than
1604+
* the custom one.
1605+
* @param gl a {@link GlyphLine} which represents some of this {@link TextRenderer}'s content
1606+
* @param font a {@link PdfFont} for this part of the {@link TextRenderer}'s content
1607+
* @return copy of this {@link TextRenderer}, which correspond to the passed {@link GlyphLine} with {@link PdfFont}
1608+
*/
15941609
protected TextRenderer createCopy(GlyphLine gl, PdfFont font) {
1610+
if (TextRenderer.class != this.getClass()) {
1611+
Logger logger = LoggerFactory.getLogger(TextRenderer.class);
1612+
logger.error(MessageFormatUtil.format(LogMessageConstant.CREATE_COPY_SHOULD_BE_OVERRIDDEN));
1613+
}
15951614
TextRenderer copy = new TextRenderer(this);
15961615
copy.setProcessedGlyphLineAndFont(gl, font);
15971616
return copy;

layout/src/test/java/com/itextpdf/layout/renderer/TextRendererIntegrationTest.java

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import com.itextpdf.io.LogMessageConstant;
2626
import com.itextpdf.io.font.PdfEncodings;
27+
import com.itextpdf.io.font.otf.GlyphLine;
2728
import com.itextpdf.io.image.ImageDataFactory;
2829
import com.itextpdf.kernel.colors.ColorConstants;
2930
import com.itextpdf.kernel.font.PdfFont;
@@ -42,6 +43,7 @@ This file is part of the iText (R) project.
4243
import com.itextpdf.layout.element.Paragraph;
4344
import com.itextpdf.layout.element.Table;
4445
import com.itextpdf.layout.element.Text;
46+
import com.itextpdf.layout.font.FontProvider;
4547
import com.itextpdf.layout.property.FloatPropertyValue;
4648
import com.itextpdf.layout.property.OverflowPropertyValue;
4749
import com.itextpdf.layout.property.OverflowWrapPropertyValue;
@@ -54,6 +56,7 @@ This file is part of the iText (R) project.
5456
import com.itextpdf.test.annotations.LogMessages;
5557
import com.itextpdf.test.annotations.type.IntegrationTest;
5658

59+
import java.io.FileNotFoundException;
5760
import java.io.IOException;
5861
import org.junit.Assert;
5962
import org.junit.BeforeClass;
@@ -718,4 +721,139 @@ public void overflowWrapBreakWordWithOverflowXTest() throws IOException, Interru
718721
doc.close();
719722
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
720723
}
724+
725+
@Test
726+
@LogMessages(messages = {
727+
@LogMessage(messageTemplate = LogMessageConstant.GET_NEXT_RENDERER_SHOULD_BE_OVERRIDDEN, count = 3)
728+
})
729+
public void customTextRendererShouldOverrideGetNextRendererTest() throws IOException, InterruptedException {
730+
String outFileName = destinationFolder + "customTextRendererShouldOverrideGetNextRendererTest.pdf";
731+
String cmpFileName = sourceFolder + "cmp_customTextRendererShouldOverrideGetNextRendererTest.pdf";
732+
733+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
734+
Document doc = new Document(pdfDoc);
735+
736+
Text text = new Text("If getNextRenderer() is not overridden and text overflows to the next line,"
737+
+ " then customizations are not applied. ");
738+
text.setNextRenderer(new TextRenderer(text) {
739+
@Override
740+
public void draw(DrawContext drawContext) {
741+
drawContext.getCanvas()
742+
.saveState()
743+
.setFillColor(ColorConstants.RED)
744+
.rectangle(occupiedArea.getBBox())
745+
.fill()
746+
.restoreState();
747+
super.draw(drawContext);
748+
}
749+
});
750+
doc.add(new Paragraph(text));
751+
752+
text = new Text("If getNextRenderer() is overridden and text overflows to the next line, "
753+
+ "then customizations are applied. ");
754+
text.setNextRenderer(new TextRenderer(text) {
755+
@Override
756+
public void draw(DrawContext drawContext) {
757+
drawContext.getCanvas()
758+
.saveState()
759+
.setFillColor(ColorConstants.RED)
760+
.rectangle(occupiedArea.getBBox())
761+
.fill()
762+
.restoreState();
763+
super.draw(drawContext);
764+
}
765+
766+
@Override
767+
public IRenderer getNextRenderer() {
768+
return new TextRendererWithOverriddenGetNextRenderer((Text) modelElement);
769+
}
770+
});
771+
doc.add(new Paragraph(text));
772+
773+
doc.close();
774+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
775+
}
776+
777+
@Test
778+
@LogMessages(messages = {
779+
@LogMessage(messageTemplate = LogMessageConstant.CREATE_COPY_SHOULD_BE_OVERRIDDEN, count = 8)
780+
})
781+
public void customTextRendererShouldOverrideCreateCopyTest() throws IOException, InterruptedException {
782+
String outFileName = destinationFolder + "customTextRendererShouldOverrideCreateCopyTest.pdf";
783+
String cmpFileName = sourceFolder + "cmp_customTextRendererShouldOverrideCreateCopyTest.pdf";
784+
785+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
786+
Document doc = new Document(pdfDoc);
787+
788+
FontProvider fontProvider = new FontProvider();
789+
Assert.assertTrue(fontProvider.addFont(fontsFolder + "NotoSans-Regular.ttf"));
790+
791+
doc.setFontProvider(fontProvider);
792+
// To trigger font selector related logic one need to apply some font on a document
793+
doc.setProperty(Property.FONT, new String[] {"SomeFont"});
794+
795+
StringBuilder longTextBuilder = new StringBuilder();
796+
for (int i = 0; i < 4; i++) {
797+
longTextBuilder.append("Дзень добры, свет! Hallo Welt! ");
798+
}
799+
800+
Text text = new Text(longTextBuilder.toString());
801+
text.setNextRenderer(new TextRenderer(text) {
802+
@Override
803+
public void draw(DrawContext drawContext) {
804+
drawContext.getCanvas()
805+
.saveState()
806+
.setFillColor(ColorConstants.RED)
807+
.rectangle(occupiedArea.getBBox())
808+
.fill()
809+
.restoreState();
810+
super.draw(drawContext);
811+
}
812+
813+
@Override
814+
public IRenderer getNextRenderer() {
815+
return new TextRendererWithOverriddenGetNextRenderer((Text) modelElement);
816+
}
817+
});
818+
doc.add(new Paragraph(text));
819+
820+
text.setNextRenderer(new TextRendererWithOverriddenGetNextRenderer(text));
821+
doc.add(new Paragraph(text));
822+
823+
doc.close();
824+
Assert.assertNull(new CompareTool().compareByContent(outFileName, cmpFileName, destinationFolder));
825+
}
826+
827+
private static class TextRendererWithOverriddenGetNextRenderer extends TextRenderer {
828+
public TextRendererWithOverriddenGetNextRenderer(Text textElement) {
829+
super(textElement);
830+
}
831+
832+
protected TextRendererWithOverriddenGetNextRenderer(TextRenderer other) {
833+
super(other);
834+
}
835+
836+
@Override
837+
public void draw(DrawContext drawContext) {
838+
drawContext.getCanvas()
839+
.saveState()
840+
.setFillColor(ColorConstants.RED)
841+
.rectangle(occupiedArea.getBBox())
842+
.fill()
843+
.restoreState();
844+
super.draw(drawContext);
845+
}
846+
847+
@Override
848+
public IRenderer getNextRenderer() {
849+
return new TextRendererWithOverriddenGetNextRenderer((Text) modelElement);
850+
}
851+
852+
@Override
853+
protected TextRenderer createCopy(GlyphLine gl, PdfFont font) {
854+
TextRenderer copy = new TextRendererWithOverriddenGetNextRenderer(this);
855+
copy.setProcessedGlyphLineAndFont(gl, font);
856+
return copy;
857+
}
858+
}
721859
}

0 commit comments

Comments
 (0)