I need extends TextRenderer for custom underline, but in one element Text i have one letter. I have Large text and do this:
String s = "d < like to filter > RENDER_TEXT events as they are <written> to an output file. I have a <PDF that has some text in it> that I want filtered out. I've found that I can walk the document once and determine the characteristics of the render events that I want to filter. Now I'd like to copy the pages of the source document and skip over some RENDER_TEXT events so that the text does not appear in the destination document. I have an IEventFilter that will accept the correct events. I just need to know how to put this filter on the document writer.";
String[] words = s.split("");
Paragraph paragraph = new Paragraph();
Text text = null;
boolean isUnderStart = false;
boolean isUnderEnd = false;
int i = 1;
for (String word : words) {
if (word.equals("<")) {
isUnderStart = true;
}
if (word.equals(">")) {
isUnderEnd = true;
}
text = new Text(word);
text.setNextRenderer(new Word25TextRenderer(text, isUnderStart,isUnderEnd));
isUnderStart = false;
isUnderEnd = false;
paragraph.add(text);
i++;
}
doc.add(paragraph);
doc.close();
"<" и ">" i do for test (In a real program, I do to use Jsoup) With help this symbols i find out coordinate axes for first simbol and end simbol. This is needed to draw the canvas element(line, curveTo). Also i add boolean to draw two times, the coordinates for the first letter and for the second.
Everything word for one line, if I want to draw underline for two line it does not break. In this part: <PDF that has some text in it>
static class Word25TextRenderer extends TextRenderer {
private boolean isUnderStart;
private boolean isUnderEnd;
public Word25TextRenderer(Text textElement, boolean isUnderStart, boolean isUnderEnd) {
super(textElement);
this.isUnderStart = isUnderStart;
this.isUnderEnd = isUnderEnd;
}
@Override
public IRenderer getNextRenderer() {
return new Word25TextRenderer((Text) modelElement, isUnderStart, isUnderEnd);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle textRect = getOccupiedAreaBBox();
int pageNumber = getOccupiedArea().getPageNumber();
PdfCanvas canvas = drawContext.getCanvas();
if (isUnderStart) {
canvas
.saveState()
.setStrokeColor(DeviceRgb.RED)
.moveTo(textRect.getRight(), textRect.getBottom());
}
if (isUnderEnd) {
canvas.curveTo(textRect.getRight(), textRect.getBottom() + textRect.getHeight() / 2,
textRect.getRight(), textRect.getBottom(),
textRect.getRight(), textRect.getBottom())
.stroke()
.restoreState();
}
}
}
Maybe you know other way? Here's what happened in the end
p.s:I add one element to the text, because then I change the elements individually (font, fontSize etc) It's necessary!
Instead of doing the underlining at the Text
element / renderer level, it's much handier to do it at the level of the Paragraph
, where all the information about line split is known, since you have to underline parts of a same Text
that are placed on different lines.
We will create a CustomText
class to store information about whether we want to underline that piece of text still (although we could have used setProperty
on a regular Text
element):
static class CustomText extends Text {
private boolean isUnderStart;
private boolean isUnderEnd;
public CustomText(String text, boolean isUnderStart, boolean isUnderEnd) {
super(text);
this.isUnderStart = isUnderStart;
this.isUnderEnd = isUnderEnd;
}
public boolean isUnderStart() {
return isUnderStart;
}
public boolean isUnderEnd() {
return isUnderEnd;
}
}
The meaty part is our custom ParagraphRenderer
- we go over the lines, remember the last "open bracket", or the text chunk start defines the start of the text to be underlined, and then once we face the "closing bracket", or the text chunk that defines the end of the text we do the actual drawing for the whole region. Additionally, we take care of the splits by underlining the remaining part of the line if by the time we finished line inspection we still have our "open bracket". Here id the code:
static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
@Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle lastRectStart = null;
for (int i = 0; i < lines.size(); i++) {
LineRenderer lineRenderer = lines.get(i);
boolean anyEventsThisLine = false;
for (IRenderer renderer : lineRenderer.getChildRenderers()) {
if (renderer.getModelElement() instanceof CustomText &&
(((CustomText) renderer.getModelElement()).isUnderEnd()
|| ((CustomText) renderer.getModelElement()).isUnderStart())) {
anyEventsThisLine = true;
if ((((CustomText) renderer.getModelElement()).isUnderEnd())) {
Rectangle endRect = renderer.getOccupiedArea().getBBox();
PdfCanvas canvas = drawContext.getCanvas();
canvas.saveState().setStrokeColor(ColorConstants.RED)
.moveTo(lastRectStart.getRight(), lastRectStart.getBottom())
.curveTo(endRect.getRight(), endRect.getBottom() + endRect.getHeight() / 2,
endRect.getRight(), endRect.getBottom(),
endRect.getRight(), endRect.getBottom())
.stroke().restoreState();
lastRectStart = null;
} else {
lastRectStart = renderer.getOccupiedArea().getBBox();
}
}
}
if (lastRectStart != null && !anyEventsThisLine) {
// Underline the whole line
PdfCanvas canvas = drawContext.getCanvas();
Rectangle lineRect = lineRenderer.getOccupiedArea().getBBox();
canvas.saveState().setStrokeColor(ColorConstants.RED)
.moveTo(lineRect.getLeft(), lineRect.getBottom())
.curveTo(lineRect.getRight(), lineRect.getBottom() + lineRect.getHeight() / 2,
lineRect.getRight(), lineRect.getBottom(),
lineRect.getRight(), lineRect.getBottom())
.stroke().restoreState();
}
if (lastRectStart != null) {
// Draw till end of line
Rectangle endRect = lineRenderer.getChildRenderers().get(lineRenderer.getChildRenderers().size() - 1)
.getOccupiedArea().getBBox();
PdfCanvas canvas = drawContext.getCanvas();
canvas.saveState().setStrokeColor(ColorConstants.RED)
.moveTo(lastRectStart.getRight(), lastRectStart.getBottom())
.curveTo(endRect.getRight(), endRect.getBottom() + endRect.getHeight() / 2,
endRect.getRight(), endRect.getBottom(),
endRect.getRight(), endRect.getBottom())
.stroke().restoreState();
if (i + 1 < lines.size()) {
lastRectStart = lines.get(i + 1).getChildRenderers().get(0).getOccupiedArea().getBBox();
} else {
lastRectStart = null;
}
}
}
}
}
The main entry point code only has slight changes:
PdfDocument pdfDocument = new PdfDocument(new PdfWriter("C:/path/to.pdf"));
Document doc = new Document(pdfDocument);
String s = "d < like to filter > RENDER_TEXT events as they are <written> to an output file. I have a <PDF that has some text in it> that I want filtered out. I've found that I can walk the document once and determine the characteristics of the render events that I want to filter. Now I'd like to copy the pages of the source document and skip over some RENDER_TEXT events so that the text does not appear in the destination document. I have an IEventFilter that will accept the correct events. I just need to know how to put this filter on the document writer.";
String[] words = s.split("");
Paragraph paragraph = new Paragraph();
paragraph.setNextRenderer(new CustomParagraphRenderer(paragraph));
Text text = null;
boolean isUnderStart = false;
boolean isUnderEnd = false;
int i = 1;
for (String word : words) {
if (word.equals("<")) {
isUnderStart = true;
}
if (word.equals(">")) {
isUnderEnd = true;
}
text = new CustomText(word, isUnderStart, isUnderEnd);
isUnderStart = false;
isUnderEnd = false;
paragraph.add(text);
i++;
}
doc.add(paragraph);
doc.close();