From d9019364170813c89c174a39f05c85f3442fabb8 Mon Sep 17 00:00:00 2001 From: "Hugh.C" Date: Fri, 19 Jun 2020 16:05:16 +0800 Subject: [PATCH] =?UTF-8?q?REPORT-34112=20HTML=E8=A7=A3=E6=9E=90=EF=BC=9At?= =?UTF-8?q?able=E6=A0=87=E7=AD=BE=E6=94=AF=E6=8C=81table-layout=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84fixed=20=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/third/v2/lowagie/text/html/CSS.java | 2 + .../text/html/simpleparser/IncCell.java | 1 + .../text/html/simpleparser/IncTable.java | 48 +++- .../third/v2/lowagie/text/pdf/PdfPCell.java | 20 ++ .../third/v2/lowagie/text/pdf/PdfPTable.java | 240 ++++++++++++++++++ .../v2/lowagie/text/pdf/TableProperties.java | 20 ++ 6 files changed, 325 insertions(+), 6 deletions(-) diff --git a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/CSS.java b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/CSS.java index 7b6f60330..3c408f3fd 100644 --- a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/CSS.java +++ b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/CSS.java @@ -195,6 +195,8 @@ public class CSS{ public static final String BOTTOM = "bottom"; public static final String FLOAT = "float"; public static final String DIRECTION = "direction"; + + public static final String TABLE_LAYOUT = "table_layout"; } } \ No newline at end of file diff --git a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncCell.java b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncCell.java index eaf6ec573..8c8a8c2a3 100644 --- a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncCell.java +++ b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncCell.java @@ -107,6 +107,7 @@ public class IncCell implements TextElementArray { //解析td上声明的width value = props.getLastChainProperty("width"); if (value != null) { + cell.setOriginalStyleWidth(value); cell.setStyleWidth(CSSUtils.parseFloat(value)); } //解析td上声明的height diff --git a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java index 3fca955a7..3b6fa35da 100644 --- a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java +++ b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java @@ -110,19 +110,21 @@ public class IncTable { public PdfPTable buildTable() { if (rows.isEmpty()) return new PdfPTable(1); - int ncol = 0; ArrayList c0 = (ArrayList) rows.get(0); + String[] firstColWidths = new String[c0.size()]; for (int k = 0; k < c0.size(); ++k) { - ncol += ((PdfPCell) c0.get(k)).getColspan(); + firstColWidths[k] = ((PdfPCell) c0.get(k)).getOriginalStyleWidth(); } + int ncol = getMaxColCount(); processColWidth(ncol); PdfPTable table = new PdfPTable(ncol); try { TableProperties tableProperties = parseTableProperties(); table.setTableProperties(tableProperties); + table.setFirstColWidths(firstColWidths); //相对宽度 float[] floats = new float[relativeColWidths.size()]; @@ -148,19 +150,44 @@ public class IncTable { } } for (int row = 0; row < rows.size(); ++row) { - ArrayList col = (ArrayList)rows.get(row); + ArrayList col = (ArrayList) rows.get(row); for (int k = 0; k < col.size(); ++k) { - table.addCell((PdfPCell)col.get(k)); + table.addCell((PdfPCell) col.get(k)); + } + //补充空白列 + for (int i = 0; i < ncol - c0.size(); i++) { + PdfPCell pdfPCell = new PdfPCell(); + pdfPCell.setInvalid(true); + table.addCell(pdfPCell); } } - processRowHeight(table); + table.adjustFixedLayoutColumnWidth(); }catch (Exception e){ e.printStackTrace(); } return table; } + /** + * 获取最大列数 + * + * @return + */ + public int getMaxColCount() { + int max = 0; + for (int row = 0; row < rows.size(); ++row) { + ArrayList col = (ArrayList) rows.get(row); + int ncol = 0; + for (int k = 0; k < col.size(); ++k) { + ncol += ((PdfPCell) col.get(k)).getColspan(); + } + max = Math.max(ncol, max); + } + return max; + } + + /** * 调整列宽,每列取最大值 * @@ -177,7 +204,7 @@ public class IncTable { for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) { ArrayList cols = (ArrayList) rows.get(rowIndex); int colSpan = 1; - for (int colIndex = 0; colIndex < cols.size(); colIndex+=colSpan) { + for (int colIndex = 0; colIndex < cols.size(); colIndex += colSpan) { PdfPCell pCell = ((PdfPCell) cols.get(colIndex)); colSpan = pCell.getColspan(); float avgWidth = pCell.getStyleWidth() / colSpan; @@ -290,6 +317,15 @@ public class IncTable { if(value != null){ tableProperties.setCellpadding(CSSUtils.parseFloat(value)); } + value = (String) props.get("width"); + if (value != null) { + tableProperties.setStyleWidth(value); + } + value = (String) props.get("table-layout"); + if (value != null) { + tableProperties.setLayout(value); + } + ChainedProperties properties = new ChainedProperties(); properties.addToChain(HtmlTags.TABLE, props); //解析background相关属性 diff --git a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPCell.java b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPCell.java index 102e4e9d7..edb23a080 100644 --- a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPCell.java +++ b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPCell.java @@ -71,6 +71,8 @@ public class PdfPCell extends Rectangle { private float styleWidth = 0.0f; private float styleHeight = 0.0f; + private String originalStyleWidth; + public float getStyleHeight() { return styleHeight; } @@ -85,6 +87,8 @@ public class PdfPCell extends Rectangle { public Map background ; + private boolean invalid = false; + public Map getBackground() { return background; } @@ -97,6 +101,21 @@ public class PdfPCell extends Rectangle { this.styleWidth = styleWidth; } + public String getOriginalStyleWidth() { + return originalStyleWidth; + } + + public void setOriginalStyleWidth(String originalStyleWidth) { + this.originalStyleWidth = originalStyleWidth; + } + + public boolean isInvalid() { + return invalid; + } + + public void setInvalid(boolean invalid) { + this.invalid = invalid; + } private ColumnText column = new ColumnText(null); @@ -291,6 +310,7 @@ public class PdfPCell extends Rectangle { background = cell.background; styleWidth = cell.styleWidth; styleHeight = cell.styleHeight; + invalid=cell.invalid; } /** diff --git a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPTable.java b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPTable.java index 38d9a250f..76ee0b451 100644 --- a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPTable.java +++ b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPTable.java @@ -49,6 +49,8 @@ package com.fr.third.v2.lowagie.text.pdf; +import com.fr.third.v2.lowagie.text.html.CSS; +import com.fr.third.v2.lowagie.text.html.CSSUtils; import java.util.ArrayList; import com.fr.third.v2.lowagie.text.DocumentException; @@ -59,6 +61,7 @@ import com.fr.third.v2.lowagie.text.Image; import com.fr.third.v2.lowagie.text.Phrase; import com.fr.third.v2.lowagie.text.Rectangle; import com.fr.third.v2.lowagie.text.pdf.events.PdfPTableEventForwarder; +import java.util.List; /** * This is a table that can be put at an absolute position but can also @@ -105,6 +108,8 @@ public class PdfPTable implements LargeElement { protected PdfPTableEvent tableEvent; private TableProperties tableProperties; + private String[] firstColWidths; + public TableProperties getTableProperties() { return tableProperties; } @@ -113,6 +118,10 @@ public class PdfPTable implements LargeElement { this.tableProperties = tableProperties; } + public void setFirstColWidths(String[] firstColWidths) { + this.firstColWidths = firstColWidths; + } + /** * Holds value of property headerRows. */ @@ -369,14 +378,245 @@ public class PdfPTable implements LargeElement { return; this.totalWidth = totalWidth; totalHeight = 0; + if (adjustFixedLayoutColumnWidth()) { + return; + } calculateWidths(); // calculateHeights(true); + adjustRowWidth(); + } + + private void adjustRowWidth() { for (int k = 0; k < rows.size(); ++k) { PdfPRow row = (PdfPRow) rows.get(k); row.setWidths(absoluteWidths); } } + /** + * 计算非内容区域的宽度(边框+cellpadding+cellspacing) + * + * @return + */ + private float calOutOfContentWidth() { + int numCols = getNumberOfColumns(); + float borderWidth = tableProperties.getBorderWidth(); + return tableProperties.getCellspacing() * (numCols + 1) + numCols * 2 * (borderWidth + tableProperties.getCellpadding()) + 2 * borderWidth; + } + + /** + * 调整固定布局(table-layout:fixed )的列宽 + * + * @return false : 不符合table-layout:fixed 的定义 + */ + public boolean adjustFixedLayoutColumnWidth() { + if (!needFixedLayout()) { + return false; + } + //是fixed 布局 且设置了width(仅设置 table-layout:fixed 而不设置width 时 前台依旧使用的是auto 布局) + + //内容区域外的宽度 + float outOfContentWidth = calOutOfContentWidth(); + //实际内容宽度 + float contentWidth = totalWidth - outOfContentWidth; + + //重新初始化一下 + absoluteWidths = new float[relativeWidths.length]; + + if (dealFirstLineHasNoWidth(contentWidth)) { + adjustRowWidth(); + return true; + } + + //第一行列宽 + float[] colWidths = new float[firstColWidths.length]; + //绝对宽度和 + float absWidthSum = 0; + //相对宽度和 + float relWidthSum = 0; + //绝对宽度 (如 10px) 的索引 + ArrayList absIndex = new ArrayList(); + //相对宽度 (如 10%) 的索引 + ArrayList relIndex = new ArrayList(); + + //初始化一下上述字段 + for (int i = 0; i < firstColWidths.length; i++) { + float w = CSSUtils.parseFloat(firstColWidths[i]); + if (null != firstColWidths[i]) { + if (firstColWidths[i].endsWith("%")) { + w = w / 100 * contentWidth; + relWidthSum += w; + relIndex.add(i); + } else { + absWidthSum += w; + absIndex.add(i); + } + } + colWidths[i] = w; + } + + //第一行td中指定的绝对宽度 >= table标签上指定的宽度,以td的宽度为准,将table撑开 + if (absWidthSum >= contentWidth) { + for (int i = 0; i < absIndex.size(); i++) { + absoluteWidths[absIndex.get(i)] = colWidths[absIndex.get(i)]; + } + totalWidth = outOfContentWidth + absWidthSum; + dealInvalidCell(absIndex); + adjustRowWidth(); + return true; + } + + //table 总的列数 + int colNum = getNumberOfColumns(); + //相对宽度与绝对宽度的列数和 + int absAndRelColNum = absIndex.size() + relIndex.size(); + //相对宽度与绝对宽度的列宽和 + float absAndRelColWidthSum = absWidthSum + relWidthSum; + + //第一行td中指定的绝对宽度+相对宽度小于table内容宽度 + if (absAndRelColWidthSum < contentWidth) { + //剩余宽度 + float remaindWidth = contentWidth - absAndRelColWidthSum; + //如果总列数大于第一行绝对宽度和相对宽度列数,则多余的宽度由剩余列平分 + if (colNum > absAndRelColNum) { + fillAbsoluteWidths(colWidths, absIndex); + fillAbsoluteWidths(colWidths, relIndex); + float remaindAvgWidth = remaindWidth / (colNum - absAndRelColNum); + for (int i = 0; i < absoluteWidths.length; i++) { + if (absIndex.contains(i) || relIndex.contains(i)) { + continue; + } + absoluteWidths[i] = remaindAvgWidth; + } + } else { + //按比例分摊剩余宽度 + for (int i = 0; i < absIndex.size(); i++) { + float width = colWidths[absIndex.get(i)]; + absoluteWidths[absIndex.get(i)] = width + remaindWidth * width / absAndRelColWidthSum; + } + for (int i = 0; i < relIndex.size(); i++) { + float width = colWidths[relIndex.get(i)]; + absoluteWidths[relIndex.get(i)] = width + remaindWidth * width / absAndRelColWidthSum; + } + + dealInvalidCell(absIndex, relIndex); + } + + } else { + //第一行td中指定的绝对宽度+相对宽度大于table标签上指定的宽度,剩余宽度由相对宽度平分 + fillAbsoluteWidths(colWidths, absIndex); + //剩余宽度 + float remaindWidth = contentWidth - absWidthSum; + for (int i = 0; i < relIndex.size(); i++) { + float width = colWidths[relIndex.get(i)]; + absoluteWidths[relIndex.get(i)] = remaindWidth * width / relWidthSum; + } + dealInvalidCell(absIndex, relIndex); + } + adjustRowWidth(); + return true; + } + + private boolean needFixedLayout() { + return totalWidth > 0 && rows.size() > 0 && CSS.Value.FIXED.equals(tableProperties.getLayout()) && null != tableProperties.getStyleWidth(); + } + + /** + * 获取列索引List + */ + public List getColIndexList() { + List list = new ArrayList<>(absoluteWidths.length); + for (int i = 0; i < absoluteWidths.length; i++) { + list.add(i); + } + return list; + } + + /** + * 填充最终宽度 + * + * @param colWidths 列宽 + * @param indexList 要设置的列索引List + */ + private void fillAbsoluteWidths(float[] colWidths, ArrayList indexList) { + for (int i = 0; i < indexList.size() && i < absoluteWidths.length; i++) { + Integer index = indexList.get(i); + absoluteWidths[index] = colWidths[index]; + } + } + + /** + * validIndexList 之外的列对应的单元格设置为无效的 + * + * @param validIndexList + */ + private void dealInvalidCell(List... validIndexList) { + if (null == validIndexList) { + return; + } + //计算无效列索引 + List invalidIndexList = getColIndexList(); + + List list = new ArrayList(); + for (List item : validIndexList) { + list.addAll(item); + } + invalidIndexList.removeAll(list); + for (int row = 0; row < rows.size(); row++) { + PdfPRow pdfRow = (PdfPRow) rows.get(row); + int index = 0; + PdfPCell[] cells = pdfRow.getCells(); + for (PdfPCell cell : cells) { + if (cell == null) { + index += 1; + continue; + } + if (invalidIndexList.contains(index)) { + cell.setInvalid(true); + } + index += cell.getColspan(); + } + } + //将无效列的多余宽度平分给其他列 + dealInvalidColWidth(invalidIndexList.size(), list); + } + + /** + * 处理无效列的内容区域外的宽度(平分给其他列) + * + * @param invalidColNum + * @param validIndexList + */ + private void dealInvalidColWidth(int invalidColNum, List validIndexList) { + if (0 == invalidColNum || null == validIndexList || 0 == validIndexList.size()) { + return; + } + float invalidColWidth = invalidColNum * tableProperties.getCellspacing() + 2 * invalidColNum * (tableProperties.getCellpadding() + tableProperties.getBorderWidth()); + float avgWidth = invalidColWidth / validIndexList.size(); + for (int i = 0; i < validIndexList.size() && i < absoluteWidths.length; i++) { + Integer index = validIndexList.get(i); + absoluteWidths[index] += avgWidth; + } + } + + /** + * 处理第一行没有设置width属性情况 + * + * @param contentWidth 内容宽度 + * @return + */ + private boolean dealFirstLineHasNoWidth(float contentWidth) { + if (null != firstColWidths && 0 != firstColWidths.length) { + return false; + } + //第一行的td都没有设置width属性,则所有列平分table宽度 + int length = absoluteWidths.length; + for (int i = 0; i < length; i++) { + absoluteWidths[i] = contentWidth / length; + } + return true; + } + /** * Sets the full width of the table from the absolute column width. * diff --git a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/TableProperties.java b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/TableProperties.java index a886f136e..6e46128f7 100644 --- a/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/TableProperties.java +++ b/fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/TableProperties.java @@ -11,7 +11,11 @@ public class TableProperties { private float cellspacing = 2.0f; private float cellpadding = 1.0f; private boolean collapse = false; + public Map background; + private String styleWidth = null; + + private String layout = null; public BorderStyle getBorderStyle() { return borderStyle; @@ -57,6 +61,22 @@ public class TableProperties { this.background = background; } + public String getStyleWidth() { + return styleWidth; + } + + public void setStyleWidth(String styleWidth) { + this.styleWidth = styleWidth; + } + + public String getLayout() { + return layout; + } + + public void setLayout(String layout) { + this.layout = layout; + } + public String toHtmlString(){ StringBuffer sb = new StringBuffer(); if(!isCollapse()){