public void SetUp() { body = new Tag("body", new Dictionary<String, String>()); table = new Tag("table", new Dictionary<String, String>()); row = new Tag("tr", new Dictionary<String, String>()); cell = new Tag("td", new Dictionary<String, String>()); config = new HtmlPipelineContext(null); calc = new WidthCalculator(); LoggerFactory.GetInstance().SetLogger(new SysoLogger(3)); body.AddChild(table); table.Parent = body; table.AddChild(row); row.Parent = table; row.AddChild(cell); cell.Parent = row; }
/* * (non-Javadoc) * * @see * com.itextpdf.tool.xml.ITagProcessor#endElement(com.itextpdf.tool.xml.Tag, * java.util.List, com.itextpdf.text.Document) */ public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent) { try { bool percentage = false; String widthValue = null; tag.CSS.TryGetValue(HTML.Attribute.WIDTH, out widthValue); if (widthValue == null) { tag.Attributes.TryGetValue(HTML.Attribute.WIDTH, out widthValue); } if(widthValue != null && widthValue.Trim().EndsWith("%")) { percentage = true; } int numberOfColumns = 0; List<TableRowElement> tableRows = new List<TableRowElement>(currentContent.Count); IList<IElement> invalidRowElements = new List<IElement>(1); String repeatHeader; tag.CSS.TryGetValue(CSS.Property.REPEAT_HEADER, out repeatHeader); String repeatFooter; tag.CSS.TryGetValue(CSS.Property.REPEAT_FOOTER, out repeatFooter); int headerRows = 0; int footerRows = 0; foreach (IElement e in currentContent) { int localNumCols = 0; if (e is TableRowElement) { TableRowElement tableRowElement = (TableRowElement) e; foreach (HtmlCell cell in tableRowElement.Content) { localNumCols += cell.Colspan; } if (localNumCols > numberOfColumns) { numberOfColumns = localNumCols; } tableRows.Add(tableRowElement); if (repeatHeader != null && Util.EqualsIgnoreCase(repeatHeader, "yes") && tableRowElement.RowPlace.Equals(TableRowElement.Place.HEADER)) { headerRows++; } if (repeatFooter != null && Util.EqualsIgnoreCase(repeatFooter, "yes") && tableRowElement.RowPlace.Equals(TableRowElement.Place.FOOTER)){ footerRows++; } } else { invalidRowElements.Add(e); } } if(repeatFooter == null || !Util.EqualsIgnoreCase(repeatFooter, "yes")) { SortUtil.MergeSort<TableRowElement>(tableRows, delegate(TableRowElement o1, TableRowElement o2) { return o1.RowPlace.Normal.CompareTo(o2.RowPlace.Normal); }); } else { SortUtil.MergeSort<TableRowElement>(tableRows, delegate(TableRowElement o1, TableRowElement o2) { return o1.RowPlace.Repeated.CompareTo(o2.RowPlace.Repeated); }); } PdfPTable table = IntPdfPTable(numberOfColumns); table.HeaderRows = headerRows + footerRows; table.FooterRows = footerRows; if (tag.Attributes.ContainsKey(HTML.Attribute.ALIGN)) { String value = tag.Attributes[HTML.Attribute.ALIGN]; table.HorizontalAlignment = CSS.GetElementAlignment(value); } int direction = GetRunDirection(tag); if (direction != PdfWriter.RUN_DIRECTION_DEFAULT) { table.RunDirection = direction; } foreach (KeyValuePair<String, String> entry in tag.CSS) { if (Util.EqualsIgnoreCase(entry.Key,CSS.Property.PAGE_BREAK_INSIDE)) { if (Util.EqualsIgnoreCase(entry.Value,CSS.Value.AVOID.ToLower())) { table.KeepTogether = true; } } } TableStyleValues styleValues = SetStyleValues(tag); table.TableEvent = new TableBorderEvent(styleValues); SetVerticalMargin(table, tag, styleValues, ctx); WidenLastCell(tableRows, styleValues.HorBorderSpacing); float[] columnWidths = new float[numberOfColumns]; float[] widestWords = new float[numberOfColumns]; float[] fixedWidths = new float[numberOfColumns]; float[] colspanWidestWords = new float[numberOfColumns]; int[] rowspanValue = new int[numberOfColumns]; float largestColumn = 0; float largestColspanColumn = 0; int indexOfLargestColumn = -1; int indexOfLargestColspanColumn = -1; // Initial fill of the widths arrays foreach (TableRowElement row in tableRows) { int column = 0; foreach (HtmlCell cell in row.Content) { // check whether the current column should be skipped due to a // rowspan value of higher cell in this column. // Contribution made by Arnost Havelka (Asseco): added while condition while ((column < numberOfColumns) && (rowspanValue[column] > 0)) { rowspanValue[column] = rowspanValue[column] - 1; ++column; } // sets a rowspan counter for current column (counter not // needed for last column). if (cell.Rowspan > 1 && column != numberOfColumns - 1 && column < rowspanValue.Length) { rowspanValue[column] = cell.Rowspan - 1; } int colspan = cell.Colspan; if (cell.FixedWidth != 0) { float fixedWidth = cell.FixedWidth + GetCellStartWidth(cell); float colSpanWidthSum = 0; int nonZeroColspanCols = 0; // Contribution made by Arnost Havelka (Asseco) (modified) for (int c = column; c < column + colspan && c < numberOfColumns; c++) { colSpanWidthSum += fixedWidths[c]; if (fixedWidths[c] != 0) nonZeroColspanCols++; } for (int c = column; c < column + colspan && c < numberOfColumns; c++) { if (fixedWidths[c] == 0) { fixedWidths[c] = (fixedWidth - colSpanWidthSum)/(colspan - nonZeroColspanCols); columnWidths[c] = (fixedWidth - colSpanWidthSum)/(colspan - nonZeroColspanCols); } } } if (cell.CompositeElements != null) { float[] widthValues = SetCellWidthAndWidestWord(cell); float cellWidth = widthValues[0] / colspan; float widestWordOfCell = widthValues[1] / colspan; for (int i = 0; i < colspan; i++) { int c = column + i; // Contribution made by Arnost Havelka (Asseco) if (c >= numberOfColumns) { continue; } if (fixedWidths[c] == 0 && cellWidth > columnWidths[c]) { columnWidths[c] = cellWidth; if (colspan == 1) { if (cellWidth > largestColumn) { largestColumn = cellWidth; indexOfLargestColumn = c; } } else { if (cellWidth > largestColspanColumn) { largestColspanColumn = cellWidth; indexOfLargestColspanColumn = c; } } } if (colspan == 1) { if (widestWordOfCell > widestWords[c]) { widestWords[c] = widestWordOfCell; } } else { if (widestWordOfCell > colspanWidestWords[c]) { colspanWidestWords[c] = widestWordOfCell; } } } } if (colspan > 1) { if (LOG.IsLogging(Level.TRACE)) { LOG.Trace(String.Format(LocaleMessages.GetInstance().GetMessage(LocaleMessages.COLSPAN), colspan)); } column += colspan - 1; } column++; } } if (indexOfLargestColumn == -1) { indexOfLargestColumn = indexOfLargestColspanColumn; if (indexOfLargestColumn == -1) { indexOfLargestColumn = 0; } for (int column = 0; column < numberOfColumns; column++) { widestWords[column] = colspanWidestWords[column]; } } float outerWidth = GetTableOuterWidth(tag, styleValues.HorBorderSpacing, ctx); float initialTotalWidth = GetTableWidth(columnWidths, 0); // float targetWidth = calculateTargetWidth(tag, columnWidths, outerWidth, ctx); float targetWidth = 0; HtmlPipelineContext htmlPipelineContext = GetHtmlPipelineContext(ctx); float max = htmlPipelineContext.PageSize.Width - outerWidth; bool tableWidthFixed = false; if (tag.Attributes.ContainsKey(CSS.Property.WIDTH) || tag.CSS.ContainsKey(CSS.Property.WIDTH)) { targetWidth = new WidthCalculator().GetWidth(tag, htmlPipelineContext.GetRootTags(), htmlPipelineContext.PageSize.Width, initialTotalWidth); if (targetWidth > max) { targetWidth = max; } tableWidthFixed = true; } else if (initialTotalWidth <= max) { targetWidth = initialTotalWidth; } else if (null == tag.Parent || (null != tag.Parent && htmlPipelineContext.GetRootTags().Contains(tag.Parent.Name))) { targetWidth = max; } else /* this table is an inner table and width adjustment is done in outer table */{ targetWidth = GetTableWidth(columnWidths, outerWidth); } float totalFixedColumnWidth = GetTableWidth(fixedWidths, 0); float targetPercentage = 0; if (totalFixedColumnWidth == initialTotalWidth) { // all column widths are fixed targetPercentage = targetWidth / initialTotalWidth; if (initialTotalWidth > targetWidth) { for (int column = 0; column < columnWidths.Length; column++) { columnWidths[column] *= targetPercentage; } } else if(tableWidthFixed && targetPercentage != 1){ for (int column = 0; column < columnWidths.Length; column++) { columnWidths[column] *= targetPercentage; } } } else { targetPercentage = (targetWidth - totalFixedColumnWidth) / (initialTotalWidth - totalFixedColumnWidth); // Reduce width of columns if the columnWidth array + borders + // paddings // is too large for the given targetWidth. if (initialTotalWidth > targetWidth) { float leftToReduce = 0; for (int column = 0; column < columnWidths.Length; column++) { if (fixedWidths[column] == 0) { // Reduce width of the column to its targetWidth, if // widestWord of column still fits in the targetWidth of // the // column. if (widestWords[column] <= columnWidths[column] * targetPercentage) { columnWidths[column] *= targetPercentage; // else take the widest word and calculate space // left to // reduce. } else { columnWidths[column] = widestWords[column]; leftToReduce += widestWords[column] - columnWidths[column] * targetPercentage; } // if widestWord of a column does not fit in the // fixedWidth, // set the column width to the widestWord. } else if (fixedWidths[column] < widestWords[column]) { columnWidths[column] = widestWords[column]; leftToReduce += widestWords[column] - fixedWidths[column]; } } if (leftToReduce != 0) { // Reduce width of the column with the most text, if its // widestWord still fits in the reduced column. if (widestWords[indexOfLargestColumn] <= columnWidths[indexOfLargestColumn] - leftToReduce) { columnWidths[indexOfLargestColumn] -= leftToReduce; } else { // set all columns to their minimum, with the // widestWord array. for (int column = 0; leftToReduce != 0 && column < columnWidths.Length; column++) { if (fixedWidths[column] == 0 && columnWidths[column] > widestWords[column]) { float difference = columnWidths[column] - widestWords[column]; if (difference <= leftToReduce) { leftToReduce -= difference; columnWidths[column] = widestWords[column]; } else { columnWidths[column] -= leftToReduce; leftToReduce = 0; } } } if (leftToReduce != 0) { // If the table has an insufficient fixed width // by // an // attribute or style, try to enlarge the table // to // its // minimum width (= widestWords array). float pageWidth = GetHtmlPipelineContext(ctx).PageSize.Width; if (GetTableWidth(widestWords, outerWidth) < pageWidth) { targetWidth = GetTableWidth(widestWords, outerWidth); leftToReduce = 0; } else { // If all columnWidths are set to the // widestWordWidths and the table is still // to // wide // content will fall off the edge of a page, // which // is similar to HTML. targetWidth = pageWidth - outerWidth; leftToReduce = 0; } } } } // Enlarge width of columns to fit the targetWidth. } else if (initialTotalWidth < targetWidth) { for (int column = 0; column < columnWidths.Length; column++) { if (fixedWidths[column] == 0) { columnWidths[column] *= targetPercentage; } } } } try { table.SetTotalWidth(columnWidths); table.LockedWidth = true; table.DefaultCell.Border = Rectangle.NO_BORDER; } catch (DocumentException e) { throw new RuntimeWorkerException(LocaleMessages.GetInstance().GetMessage(LocaleMessages.NO_CUSTOM_CONTEXT), e); } float? tableHeight = new HeightCalculator().GetHeight(tag, GetHtmlPipelineContext(ctx).PageSize.Height); float? tableRowHeight = null; if (tableHeight != null && tableHeight > 0) tableRowHeight = tableHeight / tableRows.Count; int rowNumber = 0; foreach (TableRowElement row in tableRows) { int columnNumber = -1; float? computedRowHeight = null; /*if ( tableHeight != null && tableRows.IndexOf(row) == tableRows.Count - 1) { float computedTableHeigt = table.CalculateHeights(); computedRowHeight = tableHeight - computedTableHeigt; }*/ IList<HtmlCell> rowContent = row.Content; if(rowContent.Count < 1) continue; foreach (HtmlCell cell in rowContent) { IList<IElement> compositeElements = cell.CompositeElements; if (compositeElements != null) { foreach (IElement baseLevel in compositeElements) { if (baseLevel is PdfPTable) { TableStyleValues cellValues = cell.CellValues; float totalBordersWidth = cellValues.IsLastInRow ? styleValues.HorBorderSpacing * 2 : styleValues.HorBorderSpacing; totalBordersWidth += cellValues.BorderWidthLeft + cellValues.BorderWidthRight; float columnWidth = 0; for (int currentColumnNumber = columnNumber + 1; currentColumnNumber <= columnNumber + cell.Colspan; currentColumnNumber++){ columnWidth += columnWidths[currentColumnNumber]; } IPdfPTableEvent tableEvent = ((PdfPTable) baseLevel).TableEvent; TableStyleValues innerStyleValues = ((TableBorderEvent) tableEvent).TableStyleValues; totalBordersWidth += innerStyleValues.BorderWidthLeft; totalBordersWidth += innerStyleValues.BorderWidthRight; ((PdfPTable) baseLevel).TotalWidth = columnWidth - totalBordersWidth; } } } columnNumber += cell.Colspan; table.AddCell(cell); } table.CompleteRow(); if ((computedRowHeight == null || computedRowHeight <= 0) && tableRowHeight != null) computedRowHeight = tableRowHeight; if (computedRowHeight != null && computedRowHeight > 0) { float rowHeight = table.GetRow(rowNumber).MaxHeights; if (rowHeight < computedRowHeight) { table.GetRow(rowNumber).MaxHeights = computedRowHeight.Value; } else if (tableRowHeight != null && tableRowHeight < rowHeight) { tableRowHeight = (tableHeight - rowHeight - rowNumber * tableRowHeight) / (tableRows.Count - rowNumber - 1); } } rowNumber++; } if (percentage) { table.WidthPercentage = utils.ParsePxInCmMmPcToPt(widthValue); table.LockedWidth = false; } List<IElement> elems = new List<IElement>(); if (invalidRowElements.Count > 0) { // all invalid row elements taken as caption int i = 0; Tag captionTag = tag.Children[i++]; while (!Util.EqualsIgnoreCase(captionTag.Name, HTML.Tag.CAPTION) && i < tag.Children.Count) { captionTag = tag.Children[i]; i++; } String captionSideValue; captionTag.CSS.TryGetValue(CSS.Property.CAPTION_SIDE, out captionSideValue); if (captionSideValue != null && Util.EqualsIgnoreCase(captionSideValue, CSS.Value.BOTTOM)) { elems.Add(table); elems.AddRange(invalidRowElements); } else { elems.AddRange(invalidRowElements); elems.Add(table); } } else { elems.Add(table); } return elems; } catch (NoCustomContextException e) { throw new RuntimeWorkerException(LocaleMessages.GetInstance().GetMessage(LocaleMessages.NO_CUSTOM_CONTEXT), e); } }
/** * Calculates the target width. First checks: * <ol> * <li>if the attribute or style "width" is found in the given tag and it is not wider than pageWidth - outerWidth, then the * targetWidth = width value</li> * <li>if the columnWidths array in total is not wider than pageWidth - outerWidth, then the * targetWidth = the total of the columnWidths array</li> * <li>if table's parent is a root tag or table has no parent, then the * targetWidth = width of the page - outerWidth * {@link Table#getTableOuterWidth(Tag, float)}.</li> * </ol> * If none of the above is true, the width of the table is set to its * default with the columnWidths array. * * @param tag containing attributes and css. * @param columnWidths float[] containing the widest lines of text found in * the columns. * @param outerWidth width needed for margins and borders. * @param ctx * @return float the target width of a table. * @throws NoCustomContextException */ private float CalculateTargetWidth(Tag tag, float[] columnWidths, float outerWidth, IWorkerContext ctx) { float targetWidth = 0; HtmlPipelineContext htmlPipelineContext = GetHtmlPipelineContext(ctx); float max = htmlPipelineContext.PageSize.Width - outerWidth; float start = GetTableWidth(columnWidths, 0); if (tag.Attributes.ContainsKey(CSS.Property.WIDTH) || tag.CSS.ContainsKey(CSS.Property.WIDTH)) { targetWidth = new WidthCalculator().GetWidth(tag, htmlPipelineContext.GetRootTags(), htmlPipelineContext .PageSize.Width); if (targetWidth > max) { targetWidth = max; } } else if (start <= max) { targetWidth = start; } else if (null == tag.Parent || (null != tag.Parent && htmlPipelineContext.GetRootTags().Contains(tag.Parent.Name))) { targetWidth = max; } else /* * this table is an inner table and width adjustment is done in * outer table */{ targetWidth = GetTableWidth(columnWidths, outerWidth); } return targetWidth; }