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
value = props.getLastChainProperty("width");
if (value != null) {
cell.setOriginalStyleWidth(value);
cell.setStyleWidth(CSSUtils.parseFloat(value));
}
//解析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() {
if (rows.isEmpty())
return new PdfPTable(1);
int ncol = 0;
ArrayList c0 = (ArrayList) rows.get(0);
for (int k = 0; k < c0.size(); ++k) {
ncol += ((PdfPCell) c0.get(k)).getColspan();
}
List<String> firstLineColWidths = getFirstLineColWidths();
int ncol = getMaxColCount();
processColWidth(ncol);
PdfPTable table = new PdfPTable(ncol);
try {
TableProperties tableProperties = parseTableProperties();
table.setTableProperties(tableProperties);
table.setFirstLineColWidths(firstLineColWidths);
//相对宽度
float[] floats = new float[relativeColWidths.size()];
@ -149,18 +148,69 @@ public class IncTable {
}
for (int row = 0; row < rows.size(); ++row) {
ArrayList col = (ArrayList) rows.get(row);
int colCount=0;
for (int k = 0; k < col.size(); ++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);
table.adjustFixedLayoutColumnWidth();
}catch (Exception e){
e.printStackTrace();
}
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){
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;
}
/**

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

@ -141,7 +141,7 @@ public class PdfPRow {
* @param widths
* @return true if everything went right
*/
public boolean setWidths(float widths[]) {
public boolean setWidths(float widths[], TableProperties tablePropertie) {
if (widths.length != cells.length)
return false;
System.arraycopy(widths, 0, this.widths, 0, cells.length);
@ -160,6 +160,19 @@ public class PdfPRow {
for (; k < last; ++k)
total += widths[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.setTop(0);
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;
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 List<String> firstLineColWidths;
public TableProperties getTableProperties() {
return tableProperties;
}
@ -113,6 +118,10 @@ public class PdfPTable implements LargeElement {
this.tableProperties = tableProperties;
}
public void setFirstLineColWidths(List<String> firstLineColWidths) {
this.firstLineColWidths = firstLineColWidths;
}
/**
* Holds value of property headerRows.
*/
@ -369,12 +378,244 @@ 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);
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);
if (totalWidth > 0) {
row.setWidths(absoluteWidths);
row.setWidths(absoluteWidths, tableProperties);
// totalHeight += row.getMaxHeights();
}
rows.add(row);
@ -876,7 +1117,7 @@ public class PdfPTable implements LargeElement {
if (row == null)
return 0;
if (firsttime)
row.setWidths(absoluteWidths);
row.setWidths(absoluteWidths, tableProperties);
float height = row.getMaxHeights();
PdfPCell cell;
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 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