Browse Source

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

Merge in CORE/base-third from ~HUGH.C/base-third:feature/10.0 to feature/10.0

* commit '71ad6c59cf5b9756de8aa09354d04d0d15f297cd':
  REPORT-34112 review 改进
  REPORT-34112 改进
  REPORT-34112 去除无用字段
  REPORT-34112 改进
  REPORT-34112 HTML解析:table标签支持table-layout 中的fixed 布局
research/11.0
Hugh.C 4 years ago
parent
commit
5e7eb4cdac
  1. 1
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncCell.java
  2. 69
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java
  3. 20
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPCell.java
  4. 15
      fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPRow.java
  5. 247
      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

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

69
fine-itext/src/main/java/com/fr/third/v2/lowagie/text/html/simpleparser/IncTable.java

@ -110,19 +110,18 @@ 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);
for (int k = 0; k < c0.size(); ++k) { List<String> firstLineColWidths = getFirstLineColWidths();
ncol += ((PdfPCell) c0.get(k)).getColspan();
}
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.setFirstLineColWidths(firstLineColWidths);
//相对宽度 //相对宽度
float[] floats = new float[relativeColWidths.size()]; float[] floats = new float[relativeColWidths.size()];
@ -149,18 +148,69 @@ public class IncTable {
} }
for (int row = 0; row < rows.size(); ++row) { for (int row = 0; row < rows.size(); ++row) {
ArrayList col = (ArrayList) rows.get(row); ArrayList col = (ArrayList) rows.get(row);
int colCount=0;
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));
colCount += ((PdfPCell) col.get(k)).getColspan();
}
//补充空白列
for (int i = 0; i < ncol - colCount; 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
*/
private List<String> getFirstLineColWidths() {
ArrayList c0 = (ArrayList) rows.get(0);
List<String> firstLineColWidths = new ArrayList<String>();
for (int i = 0; i < c0.size(); i++) {
PdfPCell pdfPCell = (PdfPCell) c0.get(i);
String width = pdfPCell.getOriginalStyleWidth();
if (null != width && 1 < pdfPCell.getColspan()) {
float w = CSSUtils.parseFloat(width);
//平分且需要保持原始的样式(有百分号的需要加上百分百)
w /= pdfPCell.getColspan();
width = width.endsWith("%") ? String.valueOf(w) + "%" : String.valueOf(w);
}
for (int j = 0; j < pdfPCell.getColspan(); j++) {
firstLineColWidths.add(width);
}
}
return firstLineColWidths;
}
/**
* 获取最大列数
*
* @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 +340,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;
} }
/** /**

15
fine-itext/src/main/java/com/fr/third/v2/lowagie/text/pdf/PdfPRow.java

@ -141,7 +141,7 @@ public class PdfPRow {
* @param widths * @param widths
* @return true if everything went right * @return true if everything went right
*/ */
public boolean setWidths(float widths[]) { public boolean setWidths(float widths[], TableProperties tablePropertie) {
if (widths.length != cells.length) if (widths.length != cells.length)
return false; return false;
System.arraycopy(widths, 0, this.widths, 0, cells.length); System.arraycopy(widths, 0, this.widths, 0, cells.length);
@ -160,6 +160,19 @@ public class PdfPRow {
for (; k < last; ++k) for (; k < last; ++k)
total += widths[k]; total += widths[k];
--k; --k;
float borderWidth=0;
float cellspacing=0;
float cellpadding=0;
if (null != tablePropertie) {
borderWidth = tablePropertie.getBorderWidth();
cellspacing = tablePropertie.getCellspacing();
cellpadding = tablePropertie.getCellpadding();
}
if (cell.getColspan() > 1) {
total += ((cell.getColspan() - 1) * 2 * borderWidth);
total += ((cell.getColspan() - 1) * cellspacing);
total += ((cell.getColspan() - 1) * 2 * cellpadding);
}
cell.setRight(total); cell.setRight(total);
cell.setTop(0); cell.setTop(0);
processColWidth(cell.getColumn().getCompositeElements(), cell.getWidth()); processColWidth(cell.getColumn().getCompositeElements(), cell.getWidth());

247
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 List<String> firstLineColWidths;
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 setFirstLineColWidths(List<String> firstLineColWidths) {
this.firstLineColWidths = firstLineColWidths;
}
/** /**
* Holds value of property headerRows. * Holds value of property headerRows.
*/ */
@ -369,12 +378,244 @@ 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, tableProperties);
}
}
/**
* 计算非内容区域的宽度边框+cellpadding+cellspacing
*
* @return
*/
private float calOutOfContentWidth() {
int numCols = getNumberOfColumns();
float allCellspacingWidth = tableProperties.getCellspacing() * (numCols + 1);
float allCellpaddingWidth = numCols * 2 * tableProperties.getCellpadding();
float allCellBorderWidth = (numCols + 1) * 2 * tableProperties.getBorderWidth();
return allCellspacingWidth + allCellpaddingWidth + allCellBorderWidth;
}
/**
* 调整固定布局(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[firstLineColWidths.size()];
//绝对宽度和
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 < firstLineColWidths.size(); i++) {
String widthStr = firstLineColWidths.get(i);
float w = CSSUtils.parseFloat(widthStr);
if (null != widthStr) {
if (widthStr.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;
}
}
} 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() && indexList.get(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() && validIndexList.get(i) < absoluteWidths.length; i++) {
Integer index = validIndexList.get(i);
absoluteWidths[index] += avgWidth;
}
}
/**
* 处理第一行没有设置width属性情况
*
* @param contentWidth 内容宽度
* @return
*/
private boolean dealFirstLineHasNoWidth(float contentWidth) {
if (null != firstLineColWidths && 0 != firstLineColWidths.size()) {
return false;
}
//第一行的td都没有设置width属性,则所有列平分table宽度
int length = absoluteWidths.length;
for (int i = 0; i < length; i++) {
absoluteWidths[i] = contentWidth / length;
}
return true;
} }
/** /**
@ -509,7 +750,7 @@ public class PdfPTable implements LargeElement {
} }
PdfPRow row = new PdfPRow(currentRow); PdfPRow row = new PdfPRow(currentRow);
if (totalWidth > 0) { if (totalWidth > 0) {
row.setWidths(absoluteWidths); row.setWidths(absoluteWidths, tableProperties);
// totalHeight += row.getMaxHeights(); // totalHeight += row.getMaxHeights();
} }
rows.add(row); rows.add(row);
@ -876,7 +1117,7 @@ public class PdfPTable implements LargeElement {
if (row == null) if (row == null)
return 0; return 0;
if (firsttime) if (firsttime)
row.setWidths(absoluteWidths); row.setWidths(absoluteWidths, tableProperties);
float height = row.getMaxHeights(); float height = row.getMaxHeights();
PdfPCell cell; PdfPCell cell;
PdfPRow tmprow; PdfPRow tmprow;

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