Browse Source

REPORT-34112 HTML解析:table标签支持table-layout 中的fixed 布局

research/11.0
Hugh.C 5 years ago
parent
commit
d901936417
  1. 2
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/CSS.java
  2. 1
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncCell.java
  3. 48
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java
  4. 20
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPCell.java
  5. 240
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPTable.java
  6. 20
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/TableProperties.java

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

1
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

48
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相关属性

20
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<String, String> background ;
private boolean invalid = false;
public Map<String, String> 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;
}
/**

240
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<Integer> absIndex = new ArrayList<Integer>();
//相对宽度 (如 10%) 的索引
ArrayList<Integer> relIndex = new ArrayList<Integer>();
//初始化一下上述字段
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<Integer> getColIndexList() {
List<Integer> 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<Integer> 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<Integer>... validIndexList) {
if (null == validIndexList) {
return;
}
//计算无效列索引
List<Integer> invalidIndexList = getColIndexList();
List<Integer> list = new ArrayList<Integer>();
for (List<Integer> 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<Integer> 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.
*

20
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<String, String> 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()){

Loading…
Cancel
Save