Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final html itext8 improvements #734

Merged
merged 7 commits into from
Jun 27, 2023
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