Skip to content

Commit

Permalink
Final html itext8 improvements (#734)
Browse files Browse the repository at this point in the history
* Let iText handle text positioning and style while rendering HTML

* Support justification for text rendering and fit as much HTML as possible
Issue:103050

* Set text alignment for textblocks

* Undo unnecessary calculations and the presence of magic numbers
  • Loading branch information
tomas-sexenian authored Jun 27, 2023
1 parent 78eba03 commit 3d6f7a5
Showing 1 changed file with 61 additions and 44 deletions.
105 changes: 61 additions & 44 deletions java/src/main/java/com/genexus/reports/PDFReportItext8.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class PDFReportItext8 extends GXReportPDFCommons {
public boolean barcode128AsImage = true;
ConcurrentHashMap<String, Image> documentImages;

float DEFAULT_LEADING_FACTOR = 1.2f;

static {
log = org.apache.logging.log4j.LogManager.getLogger(PDFReportItext8.class);
}
Expand Down Expand Up @@ -570,7 +572,8 @@ public void GxDrawText(String sTxt, int left, int top, int right, int bottom, in
cb.setFillColor(new DeviceRgb(foreColor));
float captionHeight = baseFont.getAscent(sTxt,fontSize) / 1000;
float rectangleWidth = baseFont.getWidth(sTxt, fontSize);
float lineHeight = (1 / 1000) * (baseFont.getAscent(sTxt,fontSize) - baseFont.getDescent(sTxt,fontSize)) + (fontSize * 1.2f);
float lineHeight = (1 / 1000) * (baseFont.getAscent(sTxt,fontSize) - baseFont.getDescent(sTxt,fontSize)) // Multiply by (1/1000) to convert from thousands of text spaces per unit to just text spaces per unit
+ (fontSize * DEFAULT_LEADING_FACTOR);
float textBlockHeight = (float)convertScale(bottom-top);
int linesCount = (int)(textBlockHeight/lineHeight);
int bottomOri = bottom;
Expand All @@ -591,7 +594,7 @@ else if (valign == VerticalAlign.BOTTOM.value())
boolean autoResize = (align & 256) == 256;

if (htmlformat == 1) {
log.info("As of now, you might experience unexpected behaviour since not all possible HTML code is supported");
log.debug("As of now, you might experience unexpected behaviour since not all possible HTML code is supported");
try {
bottomAux = (float)convertScale(bottom);
topAux = (float)convertScale(top);
Expand All @@ -604,16 +607,14 @@ else if (valign == VerticalAlign.BOTTOM.value())

// Define the rectangle where the content will be displayed
Rectangle htmlRectangle = new Rectangle(llx, lly, urx - llx, ury - lly);
Canvas canvas = new Canvas(pdfDocument.getPage(page), htmlRectangle);
YPosition yPosition = new YPosition(htmlRectangle.getTop());
TextAlignment txtAlignment = getTextAlignment(alignment);

ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setFontProvider(document.getFontProvider());
//Iterate over the elements (a.k.a the parsed HTML string) and handle each case accordingly
List<IElement> elements = HtmlConverter.convertToElements(sTxt, converterProperties);
for (IElement element : elements)
processHTMLElement(htmlRectangle, yPosition, txtAlignment, (IBlockElement) element);
processHTMLElement(htmlRectangle, yPosition, (IBlockElement) element);
} catch (Exception e) {
log.error("GxDrawText failed to print HTML text : ", e);
}
Expand Down Expand Up @@ -701,12 +702,8 @@ else if (valign == VerticalAlign.BOTTOM.value())
float urx = rightAux + leftMargin;
float ury = this.pageSize.getTop() - topAux - topMargin - bottomMargin;

try{
DrawTextColumn(llx, lly, urx, ury, sTxt, leading, valign, alignment, style, wrap);
}
catch (Exception ex) {
log.error("Text wrap in GxDrawText failed: ", ex);
}
DrawTextColumn(llx, lly, urx, ury, leading, sTxt, valign, alignment, style, wrap);

} else {
try {
if (!autoResize) {
Expand Down Expand Up @@ -740,31 +737,36 @@ else if (valign == VerticalAlign.BOTTOM.value())
}
}

void processHTMLElement(Rectangle htmlRectangle, YPosition currentYPosition, TextAlignment txtAlignment, IBlockElement blockElement){
void processHTMLElement(Rectangle htmlRectangle, YPosition currentYPosition, IBlockElement blockElement){
if (blockElement instanceof Div) {
Div div = (Div) blockElement;
// Iterate through the children of the Div and process each child element recursively
for (IElement child : div.getChildren())
if (child instanceof IBlockElement)
processHTMLElement(htmlRectangle, currentYPosition, (IBlockElement) child);
}

float blockElementHeight = getBlockElementHeight(blockElement, htmlRectangle);
float availableSpace = currentYPosition.getCurrentYPosition() - htmlRectangle.getBottom();
if (blockElementHeight > availableSpace){
log.error("You are trying to render an element of height " + blockElementHeight + " in a space of height " + availableSpace);
return;
}

if (blockElement instanceof Paragraph){
Paragraph p = (Paragraph) blockElement;
float paragraphHeight = getBlockElementHeight(blockElement, htmlRectangle);
document.showTextAligned(p, htmlRectangle.getLeft(), currentYPosition.getCurrentYPosition(), txtAlignment);
currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - paragraphHeight);
p.setFixedPosition(page, htmlRectangle.getX(), currentYPosition.getCurrentYPosition() - blockElementHeight, htmlRectangle.getWidth());
document.add(p);
} else if (blockElement instanceof Table){
Table table = (Table) blockElement;
float tableHeight = getBlockElementHeight(blockElement, htmlRectangle);
table.setFixedPosition(page, htmlRectangle.getX(), currentYPosition.getCurrentYPosition() - tableHeight, htmlRectangle.getWidth());
currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - tableHeight);
table.setFixedPosition(page, htmlRectangle.getX(), currentYPosition.getCurrentYPosition() - blockElementHeight, htmlRectangle.getWidth());
document.add(table);
} else if (blockElement instanceof com.itextpdf.layout.element.List){
com.itextpdf.layout.element.List list = (com.itextpdf.layout.element.List) blockElement;
float listHeight = getBlockElementHeight(blockElement, htmlRectangle);
list.setFixedPosition(page, htmlRectangle.getX(),currentYPosition.getCurrentYPosition() - listHeight, htmlRectangle.getWidth());
currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - listHeight);
list.setFixedPosition(page, htmlRectangle.getX(),currentYPosition.getCurrentYPosition() - blockElementHeight, htmlRectangle.getWidth());
document.add(list);
} else if (blockElement instanceof Div) {
Div div = (Div) blockElement;
// Iterate through the children of the Div and process each child element recursively
for (IElement child : div.getChildren())
if (child instanceof IBlockElement)
processHTMLElement(htmlRectangle, currentYPosition, txtAlignment, (IBlockElement) child);
}
currentYPosition.setCurrentYPosition(currentYPosition.getCurrentYPosition() - blockElementHeight);
}

private float getBlockElementHeight(IBlockElement blockElement, Rectangle htmlRectangle) throws RuntimeException{
Expand All @@ -777,8 +779,11 @@ private float getBlockElementHeight(IBlockElement blockElement, Rectangle htmlRe
} else if (blockElement instanceof com.itextpdf.layout.element.List){
com.itextpdf.layout.element.List list = (com.itextpdf.layout.element.List) blockElement;
return list.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight();
} else if (blockElement instanceof Div){
Div div = (Div) blockElement;
return div.createRendererSubTree().setParent(document.getRenderer()).layout(new LayoutContext(new LayoutArea(page, htmlRectangle))).getOccupiedArea().getBBox().getHeight();
}
throw new RuntimeException("getBlockElementHeight failed to calculate the height of the block element");
throw new RuntimeException("getBlockElementHeight failed, you might be trying to render something that is not a <p>, <table> or a <li>");
}

public class YPosition {
Expand Down Expand Up @@ -815,33 +820,45 @@ private TextAlignment getTextAlignment(int alignment){
}
}

void DrawTextColumn(float llx, float lly, float urx, float ury, String text, float leading, int valign, int alignment, Style style, boolean wrap){
float y = lly;
if (valign == VerticalAlign.MIDDLE.value())
ury = ury - ((y - lly) / 2) + leading;
else if (valign == VerticalAlign.BOTTOM.value())
ury = ury - (y - lly- leading);
else if (valign == VerticalAlign.TOP.value())
void DrawTextColumn(float llx, float lly, float urx, float ury, float leading, String text, int valign, int alignment, Style style, boolean wrap){
Paragraph p = new Paragraph(text);

if (valign == VerticalAlign.MIDDLE.value()){
ury = ury + leading;
p.setVerticalAlignment(VerticalAlignment.MIDDLE);
}
else if (valign == VerticalAlign.BOTTOM.value()){
ury = ury + leading;
p.setVerticalAlignment(VerticalAlignment.BOTTOM);
}
else if (valign == VerticalAlign.TOP.value()){
ury = ury + leading/2;
p.setVerticalAlignment(VerticalAlignment.TOP);
}
Rectangle rect = new Rectangle(llx, lly, urx - llx, ury - lly);
p.setTextAlignment(getTextAlignment(alignment));

Paragraph p = new Paragraph(text);
p.addStyle(style);
TextAlignment txtAlignment = getTextAlignment(alignment);
p.setTextAlignment(txtAlignment);
if (wrap) {
p.setProperty(Property.SPLIT_CHARACTERS, new CustomSplitCharacters());
Table table = new Table(1);
table.setFixedPosition(page,llx, lly, urx - llx);
table.setFixedPosition(page,rect.getX(), rect.getY(), rect.getWidth());
Cell cell = new Cell();
cell.setWidth(urx - llx);
cell.setHeight(ury - lly);
cell.setWidth(rect.getWidth());
cell.setHeight(rect.getHeight());
cell.setBorder(Border.NO_BORDER);
cell.setVerticalAlignment(VerticalAlignment.MIDDLE);
cell.add(p);
table.addCell(cell);
document.add(table);
} else
document.showTextAligned(p, llx, lly, this.page, txtAlignment, VerticalAlignment.MIDDLE,0);
} else{
try{
PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
Canvas canvas = new Canvas(pdfCanvas, rect);
canvas.add(p);
canvas.close();
} catch (Exception e) { log.error("GxDrawText failed to justify text column: ", e); }
}
}

private static class CustomSplitCharacters extends DefaultSplitCharacters {
Expand Down Expand Up @@ -877,7 +894,7 @@ public boolean GxPrintInit(String output, int gxXPage[], int gxYPage[], String i
else
return true;
} catch (Exception e){
log.error("GxPrintInit faile" , e);
log.error("GxPrintInit failed" , e);
return false;
}
}
Expand Down

0 comments on commit 3d6f7a5

Please sign in to comment.