diff --git a/Source/API/Utils/PDFRenderObject.swift b/Source/API/Utils/PDFRenderObject.swift index 42d1346e..a931ee0c 100644 --- a/Source/API/Utils/PDFRenderObject.swift +++ b/Source/API/Utils/PDFRenderObject.swift @@ -22,6 +22,11 @@ public class PDFRenderObject: CustomStringConvertible { /// Attributes set for this object, and their calculated frame var attributes: [(attribute: PDFObjectAttribute, frame: CGRect)] = [] + + init(frame: CGRect = .null, attributes: [(attribute: PDFObjectAttribute, frame: CGRect)] = []) { + self.frame = frame + self.attributes = attributes + } /** * Calculates the object and returns all calculated objects which are created by this calculated. diff --git a/Source/Internal/Graphics/PDFRectangleObject.swift b/Source/Internal/Graphics/PDFRectangleObject.swift index 532d7b57..cc1ef59a 100644 --- a/Source/Internal/Graphics/PDFRectangleObject.swift +++ b/Source/Internal/Graphics/PDFRectangleObject.swift @@ -22,11 +22,6 @@ class PDFRectangleObject: PDFRenderObject { */ var lineStyle: PDFLineStyle - /** - Defines the size of the rectangle - */ - var size: CGSize - /** Defines the fill color the rectangle */ @@ -39,10 +34,10 @@ class PDFRectangleObject: PDFRenderObject { - Parameter size: Size of rectangle, defaults to `CGSize.zero` - Parameter fillColor: Fill color, defaults to `Color.clear` */ - init(lineStyle: PDFLineStyle = PDFLineStyle(), size: CGSize = CGSize.zero, fillColor: Color = Color.clear) { + init(lineStyle: PDFLineStyle = PDFLineStyle(), frame: CGRect = CGRect.zero, fillColor: Color = Color.clear) { self.lineStyle = lineStyle - self.size = size self.fillColor = fillColor + super.init(frame: frame) } /** @@ -56,9 +51,9 @@ class PDFRectangleObject: PDFRenderObject { - Returns: Self */ override func calculate(generator: PDFGenerator, container: PDFContainer) throws -> [PDFLocatedRenderObject] { - let position = PDFCalculations.calculateElementPosition(for: generator, in: container, with: size) + let position = PDFCalculations.calculateElementPosition(for: generator, in: container, with: frame.size) - frame = CGRect(origin: position, size: size) + frame = CGRect(origin: position, size: frame.size) return [(container, self)] } @@ -80,6 +75,6 @@ class PDFRectangleObject: PDFRenderObject { Creates new `PDFRectangleObject` with the same properties */ override var copy: PDFRenderObject { - PDFRectangleObject(lineStyle: lineStyle, size: size, fillColor: fillColor) + PDFRectangleObject(lineStyle: lineStyle, frame: frame, fillColor: fillColor) } } diff --git a/Source/Internal/Section/PDFSectionObject.swift b/Source/Internal/Section/PDFSectionObject.swift index 95d4fa92..35c08117 100644 --- a/Source/Internal/Section/PDFSectionObject.swift +++ b/Source/Internal/Section/PDFSectionObject.swift @@ -167,8 +167,7 @@ class PDFSectionObject: PDFRenderObject { continue } let frame = CGRect(x: met.minX, y: sectionMinY, width: met.width, height: sectionMaxY - sectionMinY) - let rect = PDFRectangleObject(lineStyle: .none, size: frame.size, fillColor: backgroundColor) - rect.frame = frame + let rect = PDFRectangleObject(lineStyle: .none, frame: frame, fillColor: backgroundColor) result += [(container, rect)] + columnObjects } } diff --git a/Source/Internal/Table/PDFTableObject.swift b/Source/Internal/Table/PDFTableObject.swift index c9628862..75c67514 100644 --- a/Source/Internal/Table/PDFTableObject.swift +++ b/Source/Internal/Table/PDFTableObject.swift @@ -289,6 +289,7 @@ class PDFTableObject: PDFRenderObject { repeat { var pageStart = CGPoint.null + pageEnd = CGPoint() // Calculate top page inset var minOffset = PDFCalculations.calculateTopMinimum(for: generator) @@ -303,9 +304,12 @@ class PDFTableObject: PDFRenderObject { cellFrame.origin.y += table.margin contentFrame.origin.y -= startPosition.y - minOffset contentFrame.origin.y += table.margin - - pageStart = pageStart == .null ? cellFrame.origin : pageStart - pageEnd = CGPoint(x: cellFrame.maxX, y: cellFrame.maxY) + CGPoint(x: table.margin, y: table.margin) + + // Maintain pageStart and pageEnd + pageStart.x = min(cellFrame.minX, pageStart.x) + pageStart.y = min(cellFrame.minY, pageStart.y) + pageEnd.x = max(cellFrame.maxX, pageEnd.x) + pageEnd.y = max(cellFrame.maxY, pageEnd.y) var cellElements = [PDFRenderObject]() @@ -362,13 +366,14 @@ class PDFTableObject: PDFRenderObject { throw PDFError.tableCellTooBig(cell: firstInvalidCell.cell) } - for (idx, item) in onPageCells.enumerated() { + for item in onPageCells { let cellFrame = item.frames.cell - if pageStart == CGPoint.null { - pageStart = cellFrame.origin - CGPoint(x: table.margin, y: table.margin) - } - pageEnd = CGPoint(x: cellFrame.maxX, y: cellFrame.maxY) + CGPoint(x: table.margin, y: table.margin) + // Maintain pageStart and pageEnd + pageStart.x = min(cellFrame.minX, pageStart.x) + pageStart.y = min(cellFrame.minY, pageStart.y) + pageEnd.x = max(cellFrame.maxX, pageEnd.x) + pageEnd.y = max(cellFrame.maxY, pageEnd.y) var cellElements = [PDFRenderObject]() @@ -395,25 +400,24 @@ class PDFTableObject: PDFRenderObject { minOffset: minOffset, maxOffset: maxOffset) result += try sliceObject.calculate(generator: generator, container: container) - - if nextPageCells.isEmpty && idx == cells.count - 1 { - let tableOutlineObject = PDFRectangleObject(lineStyle: table.style.outline, size: CGSize.zero) - tableOutlineObject.frame = CGRect( - x: pageStart.x, - y: pageStart.y, - width: pageEnd.x - pageStart.x, - height: pageEnd.y - pageStart.y - ) - result += try tableOutlineObject.calculate(generator: generator, container: container) - } } + + // Draw the table outline for the page + let tableOutlineObject = PDFRectangleObject(lineStyle: table.style.outline, frame: CGRect( + x: pageStart.x - table.margin, + y: pageStart.y - table.margin, + width: pageEnd.x - pageStart.x + table.margin*2, + height: pageEnd.y - pageStart.y + table.margin*2 + )) + result += [(container, tableOutlineObject)] + if !nextPageCells.isEmpty { result += try PDFPageBreakObject().calculate(generator: generator, container: container) firstPage = false - pageEnd = .null } } while !nextPageCells.isEmpty - return (objects: result, offset: pageEnd.y) + + return (objects: result, offset: pageEnd.y + table.margin) } /// Holds two lists of cells, used during table calculations @@ -511,9 +515,7 @@ class PDFTableObject: PDFRenderObject { /// - frame: Frame of cell /// - Returns: Calculated `PDFRectangleObject` func createCellBackgroundObject(style: PDFTableCellStyle, frame: CGRect) -> PDFRenderObject { - let object = PDFRectangleObject(lineStyle: .none, size: frame.size, fillColor: style.colors.fill) - object.frame = frame - return object + return PDFRectangleObject(lineStyle: .none, frame: frame, fillColor: style.colors.fill) } /** diff --git a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift index d7905e9e..02fc29d7 100644 --- a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift +++ b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift @@ -6,235 +6,246 @@ import Quick import XCTest #if os(iOS) || os(visionOS) - class PDFTableObjectSpec: XCTestCase { - /// Preconditions: - /// - Multiple pages - /// - No cell splicing - /// - No column headers on every page - func testCells_unmergedCellsMultiplePagesNoSplicingTableNoHeadersEveryPage_shouldNotAddHeadersToEveryPage() throws { - let rows = 20 - let columns = 4 - let count = rows * columns - - let table = generateTable(rows: rows, columns: columns) - table.showHeadersOnEveryPage = false - let result = try calculate(table: table) - - // should return correct cell count including necessary page breaks - XCTAssertEqual(result.count, count + 1) - XCTAssertTrue(result[56].1 is PDFPageBreakObject) - - // check cell frames - - let columnXPositions: [CGFloat] = [70, 117.5, 260, 402.5] - let columnWidths: [CGFloat] = [27.5, 122.5, 122.5, 122.5] - - let rowYPositions: [CGFloat] = [ - // Page 1 - // Headers - 70, // 70, // Header Row 0 - 117, // 70 + 37 + 10, // Header Row 1 - 164, // 70 + (37 + 10) * 2, // Header Row 2 - // First smaller rows - 211, // 70 + (37 + 10) * 3, // Row 3 - 258, // 70 + (37 + 10) * 4, // Row 4 - 305, // 70 + (37 + 10) * 5, // Row 5 - 352, // 70 + (37 + 10) * 6, // Row 6 - 399, // 70 + (37 + 10) * 7, // Row 7 - 446, // 70 + (37 + 10) * 8, // Row 8 - 493, // 70 + (37 + 10) * 9, // Row 9 - // Larger rows - 540, // 70 + (37 + 10) * 10, // Row 10 - 598, // 70 + (37 + 10) * 10 + (48 + 10) * 1, // Row 11 - 656, // 70 + (37 + 10) * 10 + (48 + 10) * 2, // Row 12 - 714, // 70 + (37 + 10) * 10 + (48 + 10) * 3, // Row 13 - // Page 2 - 70, // 70 + (48 + 10), // Row 14 - 128, // 70 + (48 + 10) * 1, // Row 15 - 186, // 70 + (48 + 10) * 2, // Row 16 - 244, // 70 + (48 + 10) * 3, // Row 17 - 302, // 70 + (48 + 10) * 4, // Row 18 - 360, // 70 + (48 + 10) * 5, // Row 19 - ] - let rowHeights: [CGFloat] = [ - // Page 1 - // Headers - 37, // Header Row 0 - 37, // Header Row 1 - 37, // Header Row 2 - // First smaller rows - 37, // Row 3 - 37, // Row 4 - 37, // Row 5 - 37, // Row 6 - 37, // Row 7 - 37, // Row 8 - 37, // Row 9 - // Larger rows - 48, // Row 10 - 48, // Row 11 - 48, // Row 12 - 48, // Row 13 - // Page 2 - 48, // Row 14 - 48, // Row 15 - 48, // Row 16 - 48, // Row 17 - 48, // Row 18 - 48, // Row 19 - ] - - for rowIdx in 0..<14 { - for colIdx in 0.. PDFTable { - let table = PDFTable(rows: rows, columns: columns) - table.widths = [0.1, 0.3, 0.3, 0.3] - table.margin = 10 - table.padding = 10 - table.shouldSplitCellsOnPageBreak = false - table.style.columnHeaderCount = 3 - - for row in 0.. PDFTable { + let table = PDFTable(rows: rows, columns: columns) + table.widths = [0.1, 0.3, 0.3, 0.3] + table.margin = 10 + table.padding = 10 + table.shouldSplitCellsOnPageBreak = false + table.style.columnHeaderCount = columnHeaderCount + + for row in 0.. [PDFLocatedRenderObject] { - let container = PDFContainer.contentLeft - let generator = PDFGenerator(document: .init(format: .a4)) - let tableObject = PDFTableObject(table: table) - return try tableObject.calculate(generator: generator, container: container) - } + private func calculate(table: PDFTable) throws -> [PDFLocatedRenderObject] { + let container = PDFContainer.contentLeft + let generator = PDFGenerator(document: .init(format: .a4)) + let tableObject = PDFTableObject(table: table) + return try tableObject.calculate(generator: generator, container: container) } +} #endif