Table extends AbstractTagProcessor { @Override public List<Element> end(final WorkerContext ctx, final Tag tag, final List<Element> currentContent) { try { boolean percentage = false; String widthValue = tag.getCSS().get(HTML.Attribute.WIDTH); if(widthValue == null) { widthValue = tag.getAttributes().get(HTML.Attribute.WIDTH); } if(widthValue != null && widthValue.trim().endsWith("%")) { percentage = true; } int numberOfColumns = 0; List<TableRowElement> tableRows = new ArrayList<TableRowElement>(currentContent.size()); List<Element> invalidRowElements = new ArrayList<Element>(1); String repeatHeader = tag.getCSS().get(CSS.Property.REPEAT_HEADER); String repeatFooter = tag.getCSS().get(CSS.Property.REPEAT_FOOTER); int headerRows = 0; int footerRows = 0; for (Element e : currentContent) { int localNumCols = 0; if (e instanceof TableRowElement) { TableRowElement tableRowElement = (TableRowElement) e; for (HtmlCell cell : tableRowElement.getContent()) { localNumCols += cell.getColspan(); } if (localNumCols > numberOfColumns) { numberOfColumns = localNumCols; } tableRows.add(tableRowElement); if (repeatHeader != null && repeatHeader.equalsIgnoreCase("yes") && tableRowElement.getPlace().equals(TableRowElement.Place.HEADER)) { headerRows++; } if (repeatFooter != null && repeatFooter.equalsIgnoreCase("yes") && tableRowElement.getPlace().equals(TableRowElement.Place.FOOTER)) { footerRows++; } } else { invalidRowElements.add(e); } } if (repeatFooter == null || !repeatFooter.equalsIgnoreCase("yes")) { Collections.sort(tableRows, new NormalRowComparator()); } else { Collections.sort(tableRows, new RepeatedRowComparator()); } PdfPTable table = intPdfPTable(numberOfColumns); table.setHeaderRows(headerRows + footerRows); table.setFooterRows(footerRows); if ( tag.getAttributes().containsKey(HTML.Attribute.ALIGN)) { String value = tag.getAttributes().get(HTML.Attribute.ALIGN); table.setHorizontalAlignment(CSS.getElementAlignment(value)); } int direction = getRunDirection(tag); table.setRunDirection(direction); for (Entry<String, String> entry : tag.getCSS().entrySet()) { if (entry.getKey().equalsIgnoreCase(CSS.Property.PAGE_BREAK_INSIDE)) { if (entry.getValue().equalsIgnoreCase(CSS.Value.AVOID)) { table.setKeepTogether(true); } } } TableStyleValues styleValues = setStyleValues(tag); table.setTableEvent(new TableBorderEvent(styleValues)); setVerticalMargin(table, tag, styleValues, ctx); widenLastCell(tableRows, styleValues.getHorBorderSpacing()); float[] columnWidths = new float[numberOfColumns]; float[] widestWords = new float[numberOfColumns]; float[] fixedWidths = new float[numberOfColumns]; float[] colspanWidestWords = new float[numberOfColumns]; int[] rowspanValue = new int[numberOfColumns]; float largestColumn = 0; float largestColspanColumn = 0; int indexOfLargestColumn = -1; int indexOfLargestColspanColumn = -1; for (TableRowElement row : tableRows) { int column = 0; for (HtmlCell cell : row.getContent()) { while ((column < numberOfColumns) && (rowspanValue[column] > 0)) { rowspanValue[column] = rowspanValue[column] - 1; ++column; } if (cell.getRowspan() > 1 && column != numberOfColumns - 1 && column < rowspanValue.length) { rowspanValue[column] = cell.getRowspan() - 1; } int colspan = cell.getColspan(); if (cell.getFixedWidth() != 0) { float fixedWidth = cell.getFixedWidth() + getCellStartWidth(cell); float colSpanWidthSum = 0; int nonZeroColspanCols = 0; for (int c = column; c < column + colspan && c < numberOfColumns; c++) { colSpanWidthSum += fixedWidths[c]; if (fixedWidths[c] != 0) nonZeroColspanCols++; } for (int c = column; c < column + colspan && c < numberOfColumns; c++) { if (fixedWidths[c] == 0) { fixedWidths[c] = (fixedWidth - colSpanWidthSum) / (colspan - nonZeroColspanCols); columnWidths[c] = (fixedWidth - colSpanWidthSum) / (colspan - nonZeroColspanCols); } } } if (cell.getCompositeElements() != null) { float[] widthValues = setCellWidthAndWidestWord(cell); float cellWidth = widthValues[0] / colspan; float widestWordOfCell = widthValues[1] / colspan; for (int i = 0; i < colspan; i++) { int c = column + i; if (c >= numberOfColumns) { continue; } if (fixedWidths[c] == 0 && cellWidth > columnWidths[c]) { columnWidths[c] = cellWidth; if (colspan == 1) { if (cellWidth > largestColumn) { largestColumn = cellWidth; indexOfLargestColumn = c; } } else { if (cellWidth > largestColspanColumn) { largestColspanColumn = cellWidth; indexOfLargestColspanColumn = c; } } } if (colspan == 1) { if (widestWordOfCell > widestWords[c]) { widestWords[c] = widestWordOfCell; } } else { if (widestWordOfCell > colspanWidestWords[c]) { colspanWidestWords[c] = widestWordOfCell; } } } } if (colspan > 1) { if (LOG.isLogging(Level.TRACE)) { LOG.trace(String.format(LocaleMessages.getInstance().getMessage(LocaleMessages.COLSPAN), colspan)); } column += colspan - 1; } column++; } } if (indexOfLargestColumn == -1) { indexOfLargestColumn = indexOfLargestColspanColumn; if (indexOfLargestColumn == -1) { indexOfLargestColumn = 0; } for (int column = 0; column < numberOfColumns; column++) { widestWords[column] = colspanWidestWords[column]; } } float outerWidth = getTableOuterWidth(tag, styleValues.getHorBorderSpacing(), ctx); float initialTotalWidth = getTableWidth(columnWidths, 0); float targetWidth = 0; HtmlPipelineContext htmlPipelineContext = getHtmlPipelineContext(ctx); float max = htmlPipelineContext.getPageSize().getWidth() - outerWidth; boolean tableWidthFixed = false; if (tag.getAttributes().get(CSS.Property.WIDTH) != null || tag.getCSS().get(CSS.Property.WIDTH) != null) { targetWidth = new WidthCalculator().getWidth(tag, htmlPipelineContext.getRootTags(), htmlPipelineContext.getPageSize().getWidth(), initialTotalWidth); if (targetWidth > max) { targetWidth = max; } tableWidthFixed = true; } else if (initialTotalWidth <= max) { targetWidth = initialTotalWidth; } else if (null == tag.getParent() || (null != tag.getParent() && htmlPipelineContext.getRootTags().contains(tag.getParent().getName()))) { targetWidth = max; } else { targetWidth = getTableWidth(columnWidths, outerWidth); } float totalFixedColumnWidth = getTableWidth(fixedWidths, 0); float targetPercentage = 0; if (totalFixedColumnWidth == initialTotalWidth) { targetPercentage = targetWidth / initialTotalWidth; if (initialTotalWidth > targetWidth) { for (int column = 0; column < columnWidths.length; column++) { columnWidths[column] *= targetPercentage; } } else if(tableWidthFixed && targetPercentage != 1){ for (int column = 0; column < columnWidths.length; column++) { columnWidths[column] *= targetPercentage; } } } else { targetPercentage = (targetWidth - totalFixedColumnWidth) / (initialTotalWidth - totalFixedColumnWidth); if (initialTotalWidth > targetWidth) { float leftToReduce = 0; for (int column = 0; column < columnWidths.length; column++) { if (fixedWidths[column] == 0) { if (widestWords[column] <= columnWidths[column] * targetPercentage) { columnWidths[column] *= targetPercentage; } else { columnWidths[column] = widestWords[column]; leftToReduce += widestWords[column] - columnWidths[column] * targetPercentage; } } else if (fixedWidths[column] < widestWords[column]) { columnWidths[column] = widestWords[column]; leftToReduce += widestWords[column] - fixedWidths[column]; } } if (leftToReduce != 0) { if (widestWords[indexOfLargestColumn] <= columnWidths[indexOfLargestColumn] - leftToReduce) { columnWidths[indexOfLargestColumn] -= leftToReduce; } else { for (int column = 0; leftToReduce != 0 && column < columnWidths.length; column++) { if (fixedWidths[column] == 0 && columnWidths[column] > widestWords[column]) { float difference = columnWidths[column] - widestWords[column]; if (difference <= leftToReduce) { leftToReduce -= difference; columnWidths[column] = widestWords[column]; } else { columnWidths[column] -= leftToReduce; leftToReduce = 0; } } } if (leftToReduce != 0) { float pageWidth = getHtmlPipelineContext(ctx).getPageSize().getWidth(); if (getTableWidth(widestWords, outerWidth) < pageWidth) { targetWidth = getTableWidth(widestWords, outerWidth); leftToReduce = 0; } else { targetWidth = pageWidth - outerWidth; leftToReduce = 0; } } } } } else if (initialTotalWidth < targetWidth) { for (int column = 0; column < columnWidths.length; column++) { if (fixedWidths[column] == 0) { columnWidths[column] *= targetPercentage; } } } } try { table.setTotalWidth(columnWidths); table.setLockedWidth(true); table.getDefaultCell().setBorder(Rectangle.NO_BORDER); } catch (DocumentException e) { throw new RuntimeWorkerException(LocaleMessages.getInstance().getMessage( LocaleMessages.NO_CUSTOM_CONTEXT), e); } Float tableHeight = new HeightCalculator().getHeight(tag, getHtmlPipelineContext(ctx).getPageSize().getHeight()); Float tableRowHeight = null; if (tableHeight != null && tableHeight > 0) tableRowHeight = tableHeight/tableRows.size(); int rowNumber = 0; for (TableRowElement row : tableRows) { int columnNumber = -1; Float computedRowHeight = null; List<HtmlCell> rowContent = row.getContent(); if (rowContent.size() < 1) continue; for (HtmlCell cell : rowContent) { List<Element> compositeElements = cell.getCompositeElements(); if (compositeElements != null) { for (Element baseLevel : compositeElements) { if (baseLevel instanceof PdfPTable) { TableStyleValues cellValues = cell.getCellValues(); float totalBordersWidth = cellValues.isLastInRow() ? styleValues .getHorBorderSpacing() * 2 : styleValues.getHorBorderSpacing(); totalBordersWidth += cellValues.getBorderWidthLeft() + cellValues.getBorderWidthRight(); float columnWidth = 0; for (int currentColumnNumber = columnNumber + 1 ;currentColumnNumber <= columnNumber + cell.getColspan(); currentColumnNumber++ ) { columnWidth += columnWidths[currentColumnNumber]; } PdfPTableEvent tableEvent = ((PdfPTable) baseLevel).getTableEvent(); TableStyleValues innerStyleValues = ((TableBorderEvent) tableEvent) .getTableStyleValues(); totalBordersWidth += innerStyleValues.getBorderWidthLeft(); totalBordersWidth += innerStyleValues.getBorderWidthRight(); ((PdfPTable) baseLevel).setTotalWidth(columnWidth - totalBordersWidth); } } } columnNumber += cell.getColspan(); table.addCell(cell); } table.completeRow(); if ((computedRowHeight == null || computedRowHeight <= 0) && tableRowHeight != null) computedRowHeight = tableRowHeight; if (computedRowHeight != null && computedRowHeight > 0) { float rowHeight = table.getRow(rowNumber).getMaxHeights(); if (rowHeight < computedRowHeight) { table.getRow(rowNumber).setMaxHeights(computedRowHeight); } else if (tableRowHeight != null && tableRowHeight < rowHeight){ tableRowHeight = (tableHeight - rowHeight - rowNumber*tableRowHeight) /(tableRows.size() - rowNumber - 1); } } rowNumber++; } if (percentage) { table.setWidthPercentage(utils.parsePxInCmMmPcToPt(widthValue)); table.setLockedWidth(false); } List<Element> elems = new ArrayList<Element>(); if (invalidRowElements.size() > 0) { int i = 0; Tag captionTag = tag.getChildren().get(i++); while (!captionTag.getName().equalsIgnoreCase(HTML.Tag.CAPTION) && i < tag.getChildren().size()) { captionTag = tag.getChildren().get(i); i++; } String captionSideValue = captionTag.getCSS().get(CSS.Property.CAPTION_SIDE); if (captionSideValue != null && captionSideValue.equalsIgnoreCase(CSS.Value.BOTTOM)) { elems.add(table); elems.addAll(invalidRowElements); } else { elems.addAll(invalidRowElements); elems.add(table); } } else { elems.add(table); } return elems; } catch (NoCustomContextException e) { throw new RuntimeWorkerException(LocaleMessages.getInstance().getMessage(LocaleMessages.NO_CUSTOM_CONTEXT), e); } } Table(); @Override List<Element> end(final WorkerContext ctx, final Tag tag, final List<Element> currentContent); static TableStyleValues setStyleValues(final Tag tag); static TableStyleValues setBorderAttributeForCell(final Tag tag); static float getBorderOrCellSpacing(final boolean getHor, final Map<String, String> css,
			final Map<String, String> attributes); @Override boolean isStackOwner(); static final float DEFAULT_CELL_BORDER_WIDTH; }
@Test public void resolveBuild() { AbstractTagProcessor table2 = new Table(); table2.setCssAppliers(new CssAppliersImpl()); WorkerContextImpl context = new WorkerContextImpl(); context.put(HtmlPipeline.class.getName(), new HtmlPipelineContext(null)); PdfPTable table = (PdfPTable) (table2.end(context, tag, rows).get(0)); assertEquals(4, table.getRow(0).getCells().length); assertEquals(4, table.getNumberOfColumns()); }