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. 42
      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 BOTTOM = "bottom";
public static final String FLOAT = "float"; public static final String FLOAT = "float";
public static final String DIRECTION = "direction"; 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 //解析td上声明的width
value = props.getLastChainProperty("width"); value = props.getLastChainProperty("width");
if (value != null) { if (value != null) {
cell.setOriginalStyleWidth(value);
cell.setStyleWidth(CSSUtils.parseFloat(value)); cell.setStyleWidth(CSSUtils.parseFloat(value));
} }
//解析td上声明的height //解析td上声明的height

42
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() { public PdfPTable buildTable() {
if (rows.isEmpty()) if (rows.isEmpty())
return new PdfPTable(1); return new PdfPTable(1);
int ncol = 0;
ArrayList c0 = (ArrayList) rows.get(0); ArrayList c0 = (ArrayList) rows.get(0);
String[] firstColWidths = new String[c0.size()];
for (int k = 0; k < c0.size(); ++k) { 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); processColWidth(ncol);
PdfPTable table = new PdfPTable(ncol); PdfPTable table = new PdfPTable(ncol);
try { try {
TableProperties tableProperties = parseTableProperties(); TableProperties tableProperties = parseTableProperties();
table.setTableProperties(tableProperties); table.setTableProperties(tableProperties);
table.setFirstColWidths(firstColWidths);
//相对宽度 //相对宽度
float[] floats = new float[relativeColWidths.size()]; float[] floats = new float[relativeColWidths.size()];
@ -152,15 +154,40 @@ public class IncTable {
for (int k = 0; k < col.size(); ++k) { 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); processRowHeight(table);
table.adjustFixedLayoutColumnWidth();
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.printStackTrace();
} }
return table; 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;
}
/** /**
* 调整列宽每列取最大值 * 调整列宽每列取最大值
* *
@ -290,6 +317,15 @@ public class IncTable {
if(value != null){ if(value != null){
tableProperties.setCellpadding(CSSUtils.parseFloat(value)); 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(); ChainedProperties properties = new ChainedProperties();
properties.addToChain(HtmlTags.TABLE, props); properties.addToChain(HtmlTags.TABLE, props);
//解析background相关属性 //解析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 styleWidth = 0.0f;
private float styleHeight = 0.0f; private float styleHeight = 0.0f;
private String originalStyleWidth;
public float getStyleHeight() { public float getStyleHeight() {
return styleHeight; return styleHeight;
} }
@ -85,6 +87,8 @@ public class PdfPCell extends Rectangle {
public Map<String, String> background ; public Map<String, String> background ;
private boolean invalid = false;
public Map<String, String> getBackground() { public Map<String, String> getBackground() {
return background; return background;
} }
@ -97,6 +101,21 @@ public class PdfPCell extends Rectangle {
this.styleWidth = styleWidth; 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); private ColumnText column = new ColumnText(null);
@ -291,6 +310,7 @@ public class PdfPCell extends Rectangle {
background = cell.background; background = cell.background;
styleWidth = cell.styleWidth; styleWidth = cell.styleWidth;
styleHeight = cell.styleHeight; 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; 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 java.util.ArrayList;
import com.fr.third.v2.lowagie.text.DocumentException; 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.Phrase;
import com.fr.third.v2.lowagie.text.Rectangle; import com.fr.third.v2.lowagie.text.Rectangle;
import com.fr.third.v2.lowagie.text.pdf.events.PdfPTableEventForwarder; 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 * 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; protected PdfPTableEvent tableEvent;
private TableProperties tableProperties; private TableProperties tableProperties;
private String[] firstColWidths;
public TableProperties getTableProperties() { public TableProperties getTableProperties() {
return tableProperties; return tableProperties;
} }
@ -113,6 +118,10 @@ public class PdfPTable implements LargeElement {
this.tableProperties = tableProperties; this.tableProperties = tableProperties;
} }
public void setFirstColWidths(String[] firstColWidths) {
this.firstColWidths = firstColWidths;
}
/** /**
* Holds value of property headerRows. * Holds value of property headerRows.
*/ */
@ -369,14 +378,245 @@ public class PdfPTable implements LargeElement {
return; return;
this.totalWidth = totalWidth; this.totalWidth = totalWidth;
totalHeight = 0; totalHeight = 0;
if (adjustFixedLayoutColumnWidth()) {
return;
}
calculateWidths(); calculateWidths();
// calculateHeights(true); // calculateHeights(true);
adjustRowWidth();
}
private void adjustRowWidth() {
for (int k = 0; k < rows.size(); ++k) { for (int k = 0; k < rows.size(); ++k) {
PdfPRow row = (PdfPRow) rows.get(k); PdfPRow row = (PdfPRow) rows.get(k);
row.setWidths(absoluteWidths); 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. * 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 cellspacing = 2.0f;
private float cellpadding = 1.0f; private float cellpadding = 1.0f;
private boolean collapse = false; private boolean collapse = false;
public Map<String, String> background; public Map<String, String> background;
private String styleWidth = null;
private String layout = null;
public BorderStyle getBorderStyle() { public BorderStyle getBorderStyle() {
return borderStyle; return borderStyle;
@ -57,6 +61,22 @@ public class TableProperties {
this.background = background; 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(){ public String toHtmlString(){
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
if(!isCollapse()){ if(!isCollapse()){

Loading…
Cancel
Save