private void drawCellContent()

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;
    }