in extensions/vw/tabular/pdf/src/main/java/org/apache/causeway/extensions/tabular/pdf/factory/internal/Table.java [316:683]
private void drawCellContent(final Row row, final float rowHeight) throws IOException {
// position into first cell (horizontal)
float cursorX = margin;
float cursorY;
for (var cell : row.getCells()) {
// remember horizontal cursor position, so we can advance to the
// next cell easily later
float cellStartX = cursorX;
if (cell instanceof ImageCell imageCell) {
cursorY = yStart - cell.getTopPadding()
- (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth());
// image cell vertical alignment
switch (cell.getValign()) {
case TOP:
break;
case MIDDLE:
cursorY -= cell.getVerticalFreeSpace() / 2;
break;
case BOTTOM:
cursorY -= cell.getVerticalFreeSpace();
break;
}
cursorX += cell.getLeftPadding() + (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth());
// image cell horizontal alignment
switch (cell.getAlign()) {
case CENTER:
cursorX += cell.getHorizontalFreeSpace() / 2;
break;
case LEFT:
break;
case RIGHT:
cursorX += cell.getHorizontalFreeSpace();
break;
}
imageCell.getImage().draw(document, tableContentStream, cursorX, cursorY);
if (imageCell.getUrl() != null) {
List<PDAnnotation> annotations = currentPage.getAnnotations();
PDBorderStyleDictionary borderULine = new PDBorderStyleDictionary();
borderULine.setStyle(PDBorderStyleDictionary.STYLE_UNDERLINE);
borderULine.setWidth(1); // 1 point
PDAnnotationLink txtLink = new PDAnnotationLink();
txtLink.setBorderStyle(borderULine);
// Set the rectangle containing the link
// PDRectangle sets a the x,y and the width and height extend upwards from that!
PDRectangle position = new PDRectangle(cursorX, cursorY, (imageCell.getImage().getWidth()), -(imageCell.getImage().getHeight()));
txtLink.setRectangle(position);
// add an action
PDActionURI action = new PDActionURI();
action.setURI(imageCell.getUrl().toString());
txtLink.setAction(action);
annotations.add(txtLink);
}
} else if (cell instanceof TableCell tableCell) {
cursorY = yStart - cell.getTopPadding()
- (cell.getTopBorderStyle() != null ? cell.getTopBorderStyle().getWidth() : 0);
// table cell vertical alignment
switch (cell.getValign()) {
case TOP:
break;
case MIDDLE:
cursorY -= cell.getVerticalFreeSpace() / 2;
break;
case BOTTOM:
cursorY -= cell.getVerticalFreeSpace();
break;
}
cursorX += cell.getLeftPadding() + (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth());
tableCell.setXPosition(cursorX);
tableCell.setYPosition(cursorY);
this.tableContentStream.endText();
tableCell.draw(currentPage);
} else {
// no text without font
if (cell.getFont() == null) {
throw new IllegalArgumentException("Font is null on Cell=" + cell.getText());
}
if (cell.isTextRotated()) {
// debugging mode - drawing (default!) padding of rotated
// cells
// left
// PDStreamUtils.rect(tableContentStream, cursorX, yStart,
// 5, cell.getHeight(), Color.GREEN);
// top
// PDStreamUtils.rect(tableContentStream, cursorX, yStart,
// cell.getWidth(), 5 , Color.GREEN);
// bottom
// PDStreamUtils.rect(tableContentStream, cursorX, yStart -
// cell.getHeight(), cell.getWidth(), -5 , Color.GREEN);
// right
// PDStreamUtils.rect(tableContentStream, cursorX +
// cell.getWidth() - 5, yStart, 5, cell.getHeight(),
// Color.GREEN);
cursorY = yStart - cell.getInnerHeight() - cell.getTopPadding()
- (cell.getTopBorderStyle() != null ? cell.getTopBorderStyle().getWidth() : 0);
switch (cell.getAlign()) {
case CENTER:
cursorY += cell.getVerticalFreeSpace() / 2;
break;
case LEFT:
break;
case RIGHT:
cursorY += cell.getVerticalFreeSpace();
break;
}
// respect left padding and descend by font height to get
// position of the base line
cursorX += cell.getLeftPadding()
+ (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth())
+ FontUtils.getHeight(cell.getFont(), cell.getFontSize())
+ FontUtils.getDescent(cell.getFont(), cell.getFontSize());
switch (cell.getValign()) {
case TOP:
break;
case MIDDLE:
cursorX += cell.getHorizontalFreeSpace() / 2;
break;
case BOTTOM:
cursorX += cell.getHorizontalFreeSpace();
break;
}
// make tokenize method just in case
cell.getParagraph().getLines();
} else {
// debugging mode - drawing (default!) padding of rotated
// cells
// left
// PDStreamUtils.rect(tableContentStream, cursorX, yStart,
// 5, cell.getHeight(), Color.RED);
// top
// PDStreamUtils.rect(tableContentStream, cursorX, yStart,
// cell.getWidth(), 5 , Color.RED);
// bottom
// PDStreamUtils.rect(tableContentStream, cursorX, yStart -
// cell.getHeight(), cell.getWidth(), -5 , Color.RED);
// right
// PDStreamUtils.rect(tableContentStream, cursorX +
// cell.getWidth() - 5, yStart, 5, cell.getHeight(),
// Color.RED);
// position at top of current cell descending by font height
// - font descent, because we are
// positioning the base line here
cursorY = yStart - cell.getTopPadding() - FontUtils.getHeight(cell.getFont(), cell.getFontSize())
- FontUtils.getDescent(cell.getFont(), cell.getFontSize())
- (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth());
if (drawDebug) {
// @formatter:off
// top padding
PDStreamUtils.rect(tableContentStream, cursorX + (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth()), yStart - (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth()), cell.getWidth() - (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth()) - (cell.getRightBorderStyle() == null ? 0 : cell.getRightBorderStyle().getWidth()), cell.getTopPadding(), Color.RED);
// bottom padding
PDStreamUtils.rect(tableContentStream, cursorX + (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth()), yStart - cell.getHeight() + (cell.getBottomBorderStyle() == null ? 0 : cell.getBottomBorderStyle().getWidth()) + cell.getBottomPadding(), cell.getWidth() - (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth()) - (cell.getRightBorderStyle() == null ? 0 : cell.getRightBorderStyle().getWidth()), cell.getBottomPadding(), Color.RED);
// left padding
PDStreamUtils.rect(tableContentStream, cursorX + (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth()), yStart - (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth()), cell.getLeftPadding(), cell.getHeight() - (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth()) - (cell.getBottomBorderStyle() == null ? 0 : cell.getBottomBorderStyle().getWidth()), Color.RED);
// right padding
PDStreamUtils.rect(tableContentStream, cursorX + cell.getWidth() - (cell.getRightBorderStyle() == null ? 0 : cell.getRightBorderStyle().getWidth()), yStart - (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth()), -cell.getRightPadding(), cell.getHeight() - (cell.getTopBorderStyle() == null ? 0 : cell.getTopBorderStyle().getWidth()) - (cell.getBottomBorderStyle() == null ? 0 : cell.getBottomBorderStyle().getWidth()), Color.RED);
// @formatter:on
}
// respect left padding
cursorX += cell.getLeftPadding()
+ (cell.getLeftBorderStyle() == null ? 0 : cell.getLeftBorderStyle().getWidth());
// the widest text does not fill the inner width of the
// cell? no
// problem, just add it ;)
switch (cell.getAlign()) {
case CENTER:
cursorX += cell.getHorizontalFreeSpace() / 2;
break;
case LEFT:
break;
case RIGHT:
cursorX += cell.getHorizontalFreeSpace();
break;
}
switch (cell.getValign()) {
case TOP:
break;
case MIDDLE:
cursorY -= cell.getVerticalFreeSpace() / 2;
break;
case BOTTOM:
cursorY -= cell.getVerticalFreeSpace();
break;
}
if (cell.getUrl() != null) {
List<PDAnnotation> annotations = currentPage.getAnnotations();
PDAnnotationLink txtLink = new PDAnnotationLink();
// Set the rectangle containing the link
// PDRectangle sets a the x,y and the width and height extend upwards from that!
PDRectangle position = new PDRectangle(cursorX - 5, cursorY + 10, (cell.getWidth()), -(cell.getHeight()));
txtLink.setRectangle(position);
// add an action
PDActionURI action = new PDActionURI();
action.setURI(cell.getUrl().toString());
txtLink.setAction(action);
annotations.add(txtLink);
}
}
// remember this horizontal position, as it is the anchor for
// each
// new line
float lineStartX = cursorX;
float lineStartY = cursorY;
this.tableContentStream.setNonStrokingColor(cell.getTextColor());
int italicCounter = 0;
int boldCounter = 0;
this.tableContentStream.setRotated(cell.isTextRotated());
// print all lines of the cell
for (Map.Entry<Integer, List<Token>> entry : cell.getParagraph().getMapLineTokens().entrySet()) {
// calculate the width of this line
float freeSpaceWithinLine = cell.getParagraph().getMaxLineWidth()
- cell.getParagraph().getLineWidth(entry.getKey());
// TODO: need to implemented rotated text yo!
if (cell.isTextRotated()) {
cursorY = lineStartY;
switch (cell.getAlign()) {
case CENTER:
cursorY += freeSpaceWithinLine / 2;
break;
case LEFT:
break;
case RIGHT:
cursorY += freeSpaceWithinLine;
break;
}
} else {
cursorX = lineStartX;
switch (cell.getAlign()) {
case CENTER:
cursorX += freeSpaceWithinLine / 2;
break;
case LEFT:
// it doesn't matter because X position is always
// the same
// as row above
break;
case RIGHT:
cursorX += freeSpaceWithinLine;
break;
}
}
// iterate through tokens in current line
PDFont currentFont = cell.getParagraph().getFont(false, false);
for (Token token : entry.getValue()) {
switch (token.type()) {
case OPEN_TAG:
if ("b".equals(token.text())) {
boldCounter++;
} else if ("i".equals(token.text())) {
italicCounter++;
}
break;
case CLOSE_TAG:
if ("b".equals(token.text())) {
boldCounter = Math.max(boldCounter - 1, 0);
} else if ("i".equals(token.text())) {
italicCounter = Math.max(italicCounter - 1, 0);
}
break;
case PADDING:
cursorX += Float.parseFloat(token.text());
break;
case ORDERING:
currentFont = cell.getParagraph().getFont(boldCounter > 0, italicCounter > 0);
this.tableContentStream.setFont(currentFont, cell.getFontSize());
if (cell.isTextRotated()) {
tableContentStream.newLineAt(cursorX, cursorY);
this.tableContentStream.showText(token.text());
cursorY += token.getWidth(currentFont) / 1000 * cell.getFontSize();
} else {
this.tableContentStream.newLineAt(cursorX, cursorY);
this.tableContentStream.showText(token.text());
cursorX += token.getWidth(currentFont) / 1000 * cell.getFontSize();
}
break;
case BULLET:
float widthOfSpace = currentFont.getSpaceWidth();
float halfHeight = FontUtils.getHeight(currentFont, cell.getFontSize()) / 2;
if (cell.isTextRotated()) {
PDStreamUtils.rect(tableContentStream, cursorX + halfHeight, cursorY,
token.getWidth(currentFont) / 1000 * cell.getFontSize(),
widthOfSpace / 1000 * cell.getFontSize(),
cell.getTextColor());
// move cursorY for two characters (one for
// bullet, one for space after bullet)
cursorY += 2 * widthOfSpace / 1000 * cell.getFontSize();
} else {
PDStreamUtils.rect(tableContentStream, cursorX, cursorY + halfHeight,
token.getWidth(currentFont) / 1000 * cell.getFontSize(),
widthOfSpace / 1000 * cell.getFontSize(),
cell.getTextColor());
// move cursorX for two characters (one for
// bullet, one for space after bullet)
cursorX += 2 * widthOfSpace / 1000 * cell.getFontSize();
}
break;
case TEXT:
currentFont = cell.getParagraph().getFont(boldCounter > 0, italicCounter > 0);
this.tableContentStream.setFont(currentFont, cell.getFontSize());
if (cell.isTextRotated()) {
tableContentStream.newLineAt(cursorX, cursorY);
this.tableContentStream.showText(token.text());
cursorY += token.getWidth(currentFont) / 1000 * cell.getFontSize();
} else {
try {
this.tableContentStream.newLineAt(cursorX, cursorY);
this.tableContentStream.showText(token.text());
cursorX += token.getWidth(currentFont) / 1000 * cell.getFontSize();
} catch (IOException e) {
e.printStackTrace();
}
}
break;
case POSSIBLE_WRAP_POINT, WRAP_POINT:
break;
}
}
if (cell.isTextRotated()) {
cursorX = cursorX + cell.getParagraph().getFontHeight() * cell.getLineSpacing();
} else {
cursorY = cursorY - cell.getParagraph().getFontHeight() * cell.getLineSpacing();
}
}
}
PDRectangle rectangle = new PDRectangle(cellStartX, yStart - cell.getHeight(), cell.getWidth(), cell.getHeight());
cell.notifyContentDrawnListeners(getDocument(), getCurrentPage(), rectangle);
// set cursor to the start of this cell plus its width to advance to
// the next cell
cursorX = cellStartX + cell.getWidth();
}
// Set Y position for next row
yStart = yStart - rowHeight;
}