From d4a65b2b19c38010c4a304c98124d21395cd3c06 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sat, 30 May 2020 19:58:19 +0200 Subject: [PATCH 01/13] Added support for omitting first sliced cells --- Shared/Examples/ExperimentFactory.swift | 17 +---------------- Source/Internal/Table/PDFTableObject.swift | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Shared/Examples/ExperimentFactory.swift b/Shared/Examples/ExperimentFactory.swift index 82799a05..d32df08a 100644 --- a/Shared/Examples/ExperimentFactory.swift +++ b/Shared/Examples/ExperimentFactory.swift @@ -19,6 +19,7 @@ class ExperimentFactory: ExampleFactory { table.margin = 10 table.padding = 10 table.showHeadersOnEveryPage = false + table.shouldSplitCellsOnPageBeak = false table.style.columnHeaderCount = 3 for row in 0.. FilteredCells { + internal func filterCellsOnPage(for generator: PDFGenerator, items: [PDFTableCalculatedCell], minOffset: CGFloat, maxOffset: CGFloat, shouldSplitCellsOnPageBeak: Bool) -> FilteredCells { let contentHeight = maxOffset - minOffset var cells: [PDFTableCalculatedCell] = [] var rest: [PDFTableCalculatedCell] = [] - for item in items { + for item in items { let cellFrame = item.frames.cell + if cellFrame.maxY < maxOffset { cells.append(item) } else { - if cellFrame.minY < maxOffset { + // If cells should be split and cell is partially on current page, add it to the cells, the cell will be sliced afterwards + if shouldSplitCellsOnPageBeak && cellFrame.minY < maxOffset { cells.append(item) } + var offsetFix: CGFloat = 0 + if !shouldSplitCellsOnPageBeak { + offsetFix = cellFrame.maxY - maxOffset + } var nextPageCell = item nextPageCell.frames.cell.origin.y -= contentHeight nextPageCell.frames.content.origin.y -= contentHeight From 568bb3beee1eb267dc12bb5d4d2960a47dfe3dcc Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 10:57:56 +0200 Subject: [PATCH 02/13] Added more documentation and tweaks --- .../Table/PDFTableCalculatedCell.swift | 15 ++++ Source/Internal/Table/PDFTableObject.swift | 81 ++++++++++--------- 2 files changed, 59 insertions(+), 37 deletions(-) create mode 100644 Source/Internal/Table/PDFTableCalculatedCell.swift diff --git a/Source/Internal/Table/PDFTableCalculatedCell.swift b/Source/Internal/Table/PDFTableCalculatedCell.swift new file mode 100644 index 00000000..3129735c --- /dev/null +++ b/Source/Internal/Table/PDFTableCalculatedCell.swift @@ -0,0 +1,15 @@ +// +// PDFTableCalculatedCell.swift +// TPPDF +// +// Created by Philip Niedertscheider on 19.07.20. +// + +import CoreGraphics + +internal struct PDFTableCalculatedCell { + var cell: PDFTableCell + var type: PDFTableObject.CellType + var style: PDFTableCellStyle + var frames: (cell: CGRect, content: CGRect) +} diff --git a/Source/Internal/Table/PDFTableObject.swift b/Source/Internal/Table/PDFTableObject.swift index 8664a4dd..964a1b81 100644 --- a/Source/Internal/Table/PDFTableObject.swift +++ b/Source/Internal/Table/PDFTableObject.swift @@ -13,11 +13,6 @@ import AppKit // swiftlint:disable function_parameter_count -internal typealias PDFTableCalculatedCell = (cell: PDFTableCell, - type: PDFTableObject.CellType, - style: PDFTableCellStyle, - frames: (cell: CGRect, content: CGRect)) - /** Internal object, used for calculating a `PDFTable` */ @@ -144,7 +139,7 @@ internal class PDFTableObject: PDFRenderObject { type: CellType, origin: CGPoint, width: CGFloat) -> PDFTableCalculatedCell { - var frame: PDFTableCalculatedCell = ( + var frame = PDFTableCalculatedCell( cell: cell, type: type, style: style, @@ -335,12 +330,14 @@ internal class PDFTableObject: PDFRenderObject { minOffset += headerHeight } - var onPageCells: [PDFTableCalculatedCell] - (onPageCells, nextPageCells) = filterCellsOnPage(for: generator, + let filterResult = filterCellsOnPage(for: generator, items: nextPageCells, minOffset: minOffset, maxOffset: maxOffset, shouldSplitCellsOnPageBeak: table.shouldSplitCellsOnPageBeak) + let onPageCells = filterResult.cells + nextPageCells = filterResult.remainder + for (idx, item) in onPageCells.enumerated() { let cellFrame = item.frames.cell @@ -396,23 +393,38 @@ internal class PDFTableObject: PDFRenderObject { return (objects: result, offset: pageEnd.y) } - internal typealias FilteredCells = (cells: [PDFTableCalculatedCell], rest: [PDFTableCalculatedCell]) + /// Holds two lists of cells, used during table calculations + internal struct FilteredCells { + /// List of calculated cells on the active page + var cells: [PDFTableCalculatedCell] + /// List of remaining cells on further pages + var remainder: [PDFTableCalculatedCell] + } + + /// Filters the given list of cells into the ones that fit on the current page, and all remainding cells, repositioned for the next page. + /// + /// - Parameters: + /// - generator: Active instance of `PDFGenerator` + /// - items: List of cells to filter + /// - minOffset: Minimum `y`-position on the page + /// - maxOffset: Maximum `y`-position on the page + /// - shouldSplitCellsOnPageBeak: If `true`, cells won't be sliced and shown on both pages, instead moved entirely to the next page + /// - Returns: Two lists of cells, see `FilteredCells` internal func filterCellsOnPage(for generator: PDFGenerator, items: [PDFTableCalculatedCell], minOffset: CGFloat, maxOffset: CGFloat, shouldSplitCellsOnPageBeak: Bool) -> FilteredCells { let contentHeight = maxOffset - minOffset - var cells: [PDFTableCalculatedCell] = [] - var rest: [PDFTableCalculatedCell] = [] + var result = FilteredCells(cells: [], remainder: []) for item in items { let cellFrame = item.frames.cell if cellFrame.maxY < maxOffset { - cells.append(item) + result.cells.append(item) } else { // If cells should be split and cell is partially on current page, add it to the cells, the cell will be sliced afterwards if shouldSplitCellsOnPageBeak && cellFrame.minY < maxOffset { - cells.append(item) + result.cells.append(item) } var offsetFix: CGFloat = 0 if !shouldSplitCellsOnPageBeak { @@ -421,10 +433,10 @@ internal class PDFTableObject: PDFRenderObject { var nextPageCell = item nextPageCell.frames.cell.origin.y -= contentHeight nextPageCell.frames.content.origin.y -= contentHeight - rest.append(nextPageCell) + result.remainder.append(nextPageCell) } } - return (cells: cells, rest: rest) + return result } internal func createSliceObject(frame: CGRect, elements: [PDFRenderObject], minOffset: CGFloat, maxOffset: CGFloat) -> PDFSlicedObject { @@ -440,9 +452,12 @@ internal class PDFTableObject: PDFRenderObject { return sliceObject } - /** - Creates a render object for the cell background - */ + /// Creates a render object for the cell background + /// + /// - Parameters: + /// - style: Style of table cell + /// - frame: Frame of cell + /// - Returns: Calculated `PDFRectangleObject` internal func createCellBackgroundObject(style: PDFTableCellStyle, frame: CGRect) -> PDFRenderObject { let object = PDFRectangleObject(lineStyle: .none, size: frame.size, fillColor: style.colors.fill) object.frame = frame @@ -510,14 +525,11 @@ internal class PDFTableObject: PDFRenderObject { return .content } - /** - Returns the style of a given cell, depending on the type. - - - parameters tableStyle: Style configuration of table - - parameters type: Type of cell - - - returns: Style of cell - */ + /// Returns the style of a given cell, depending on the type. + /// - Parameters: + /// - tableStyle: Style configuration of table + /// - type: Type of cell + /// - Returns: Style of cell internal func getStyle(tableStyle: PDFTableStyle, type: CellType) -> PDFTableCellStyle { switch type { case .header: @@ -535,14 +547,11 @@ internal class PDFTableObject: PDFRenderObject { } } - /** - Creates four outline line objects around a given frame using the given style. - - - parameter borders: Style of each border direction - - parameter frame: Frame of rectangle - - - returns: Array of `PDFLineObject` - */ + /// Creates four outline line objects around a given frame using the given style. + /// - Parameters: + /// - borders: Style of each border edge + /// - frame: Frame of rectangle + /// - Returns: Array of `PDFLineObject` internal func createCellOutlineObjects(borders: PDFTableCellBorders, frame: CGRect) -> [PDFLineObject] { [ PDFLineObject(style: borders.top, @@ -560,9 +569,7 @@ internal class PDFTableObject: PDFRenderObject { ] } - /** - Creates a new `PDFTableObject` with the same properties - */ + /// Creates a new `PDFTableObject` with the same properties override internal var copy: PDFRenderObject { PDFTableObject(table: table.copy) } From 838e2464683b05cde407c45d269135b62f0f35d7 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 11:38:04 +0200 Subject: [PATCH 03/13] Added page breaking without table cell slicing --- Shared/Examples/ExperimentFactory.swift | 10 ++--- Source/Internal/Table/PDFTableObject.swift | 39 +++++++++++++------ .../TPPDFTests/Table/PDFTableObjectSpec.swift | 11 ++++++ 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 Tests/TPPDFTests/Table/PDFTableObjectSpec.swift diff --git a/Shared/Examples/ExperimentFactory.swift b/Shared/Examples/ExperimentFactory.swift index d32df08a..7b94cff4 100644 --- a/Shared/Examples/ExperimentFactory.swift +++ b/Shared/Examples/ExperimentFactory.swift @@ -31,11 +31,11 @@ class ExperimentFactory: ExampleFactory { document.add(table: table) - let singleCellTable = PDFTable(rows: 1, columns: 1) - singleCellTable[0,0].content = (0...100).map(String.init) - .joined(separator: "\n") - .asTableContent - document.add(table: singleCellTable) +// let singleCellTable = PDFTable(rows: 1, columns: 1) +// singleCellTable[0,0].content = (0...100).map(String.init) +// .joined(separator: "\n") +// .asTableContent +// document.add(table: singleCellTable) return [document] } diff --git a/Source/Internal/Table/PDFTableObject.swift b/Source/Internal/Table/PDFTableObject.swift index 964a1b81..74555e42 100644 --- a/Source/Internal/Table/PDFTableObject.swift +++ b/Source/Internal/Table/PDFTableObject.swift @@ -331,12 +331,17 @@ internal class PDFTableObject: PDFRenderObject { } let filterResult = filterCellsOnPage(for: generator, - items: nextPageCells, - minOffset: minOffset, - maxOffset: maxOffset, - shouldSplitCellsOnPageBeak: table.shouldSplitCellsOnPageBeak) + items: nextPageCells, + minOffset: minOffset, + maxOffset: maxOffset, + shouldSplitCellsOnPageBeak: table.shouldSplitCellsOnPageBeak) let onPageCells = filterResult.cells nextPageCells = filterResult.remainder + // If none of the cells fit on the current page, the algorithm will try again on the next page and if it occurs again, an error should be thrown + if onPageCells.isEmpty && !firstPage, let firstInvalidCell = nextPageCells.first { + break +// throw PDFError.tableCellTooBig(cell: firstInvalidCell.cell) + } for (idx, item) in onPageCells.enumerated() { @@ -412,27 +417,37 @@ internal class PDFTableObject: PDFRenderObject { /// - shouldSplitCellsOnPageBeak: If `true`, cells won't be sliced and shown on both pages, instead moved entirely to the next page /// - Returns: Two lists of cells, see `FilteredCells` internal func filterCellsOnPage(for generator: PDFGenerator, items: [PDFTableCalculatedCell], minOffset: CGFloat, maxOffset: CGFloat, shouldSplitCellsOnPageBeak: Bool) -> FilteredCells { + // Maximum height available let contentHeight = maxOffset - minOffset - var result = FilteredCells(cells: [], remainder: []) + var offsetFix: CGFloat! + + // Iterate each cell and decide if it fits on current page or if it needs to be moved to the further pages for item in items { let cellFrame = item.frames.cell - if cellFrame.maxY < maxOffset { + // Cells needs to fit the current available space entirely + if cellFrame.maxY < maxOffset { // TODO: is the row padding relevant here? result.cells.append(item) } else { // If cells should be split and cell is partially on current page, add it to the cells, the cell will be sliced afterwards if shouldSplitCellsOnPageBeak && cellFrame.minY < maxOffset { result.cells.append(item) } - var offsetFix: CGFloat = 0 - if !shouldSplitCellsOnPageBeak { - offsetFix = cellFrame.maxY - maxOffset - } + // In any case, if the cell does not fit on the active page entirely, it must be repositioned for further pages var nextPageCell = item - nextPageCell.frames.cell.origin.y -= contentHeight - nextPageCell.frames.content.origin.y -= contentHeight + if shouldSplitCellsOnPageBeak { + nextPageCell.frames.cell.origin.y -= contentHeight + nextPageCell.frames.content.origin.y -= contentHeight + } else { + let cellContentOffset = nextPageCell.frames.content.minY - nextPageCell.frames.cell.minY + if offsetFix == nil { + offsetFix = nextPageCell.frames.cell.minY - minOffset + } + nextPageCell.frames.cell.origin.y -= offsetFix + nextPageCell.frames.content.origin.y = nextPageCell.frames.cell.minY + cellContentOffset + } result.remainder.append(nextPageCell) } } diff --git a/Tests/TPPDFTests/Table/PDFTableObjectSpec.swift b/Tests/TPPDFTests/Table/PDFTableObjectSpec.swift new file mode 100644 index 00000000..ae9a7a66 --- /dev/null +++ b/Tests/TPPDFTests/Table/PDFTableObjectSpec.swift @@ -0,0 +1,11 @@ +import Quick +import Nimble +@testable import TPPDF + +class PDFTableObjectSpec: QuickSpec { + + override func spec() { + + } +} + From e7cce51b6d9813f1d378d780c03e621f2d8c9c06 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 12:33:55 +0200 Subject: [PATCH 04/13] Added macOS support to tests --- Source/Internal/Table/PDFTableObject.swift | 3 +- .../Internal/Table/PDFTableObjectSpec.swift | 42 +++++++++++++++++++ .../TPPDFTests/Table/PDFTableObjectSpec.swift | 11 ----- 3 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift delete mode 100644 Tests/TPPDFTests/Table/PDFTableObjectSpec.swift diff --git a/Source/Internal/Table/PDFTableObject.swift b/Source/Internal/Table/PDFTableObject.swift index 74555e42..c05092bf 100644 --- a/Source/Internal/Table/PDFTableObject.swift +++ b/Source/Internal/Table/PDFTableObject.swift @@ -339,8 +339,7 @@ internal class PDFTableObject: PDFRenderObject { nextPageCells = filterResult.remainder // If none of the cells fit on the current page, the algorithm will try again on the next page and if it occurs again, an error should be thrown if onPageCells.isEmpty && !firstPage, let firstInvalidCell = nextPageCells.first { - break -// throw PDFError.tableCellTooBig(cell: firstInvalidCell.cell) + throw PDFError.tableCellTooBig(cell: firstInvalidCell.cell) } diff --git a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift new file mode 100644 index 00000000..08ed293d --- /dev/null +++ b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift @@ -0,0 +1,42 @@ +import Quick +import Nimble +@testable import TPPDF + +class PDFTableObjectSpec: QuickSpec { + + override func spec() { + describe("PDFTableObject") { + describe("calculations") { + context("unmerged cells on multiple pages without splicing and no table headers on every page") { + let table = PDFTable(rows: 50, columns: 4) + table.widths = [0.1, 0.3, 0.3, 0.3] + table.margin = 10 + table.padding = 10 + table.showHeadersOnEveryPage = false + table.shouldSplitCellsOnPageBeak = false + table.style.columnHeaderCount = 3 + + for row in 0.. Date: Sun, 19 Jul 2020 12:53:21 +0200 Subject: [PATCH 05/13] Added macOS tests --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0322d53b..15aa4c4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ jobs: - bundle exec pod install --project-directory="${ROOT_FOLDER}" - cd "${ROOT_FOLDER}" script: - - set -o pipefail + - set -o pipefail - xcodebuild -workspace "$WORKSPACE" -scheme "$EXAMPLE_SCHEME" -sdk iphonesimulator @@ -122,7 +122,7 @@ jobs: -clonedSourcePackagesDirPath . -derivedDataPath ${TRAVIS_BUILD_DIR}/derived_data -configuration Debug | xcpretty - - set -o pipefail + - set -o pipefail - xcodebuild -project ${PROJECT} -scheme ${EXAMPLE_SCHEME} -clonedSourcePackagesDirPath . @@ -146,7 +146,7 @@ jobs: -clonedSourcePackagesDirPath . -derivedDataPath ${TRAVIS_BUILD_DIR}/derived_data -configuration Debug | xcpretty - - set -o pipefail + - set -o pipefail - xcodebuild -project ${PROJECT} -scheme ${EXAMPLE_SCHEME} -clonedSourcePackagesDirPath . From 477ec0381ef7c15d02bf5976aa67006651ec81ce Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 13:11:04 +0200 Subject: [PATCH 06/13] iOS test fixes and macOS travis config fix --- .../Internal/Table/PDFTableObjectSpec.swift | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift index 08ed293d..033a5675 100644 --- a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift +++ b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift @@ -8,7 +8,12 @@ class PDFTableObjectSpec: QuickSpec { describe("PDFTableObject") { describe("calculations") { context("unmerged cells on multiple pages without splicing and no table headers on every page") { - let table = PDFTable(rows: 50, columns: 4) + let container = PDFContainer.contentLeft + + let rows = 50 + let columns = 4 + let count = rows * columns + let table = PDFTable(rows: rows, columns: columns) table.widths = [0.1, 0.3, 0.3, 0.3] table.margin = 10 table.padding = 10 @@ -23,16 +28,29 @@ class PDFTableObjectSpec: QuickSpec { } } - it("should return correct frames") { + var result: [PDFLocatedRenderObject]! + + beforeEach { let generator = PDFGenerator(document: .init(format: .a4)) let tableObject = PDFTableObject(table: table) - var result: [PDFLocatedRenderObject]! expect { - result = try tableObject.calculate(generator: generator, container: .contentLeft) + result = try tableObject.calculate(generator: generator, container: container) }.toNot(throwError()) + } - //expect(result).to(haveCount(1)) + it("should return all cells and necessary page breaks") { + expect(result).to(haveCount(count)) + + for (i, cell) in result.enumerated() { + let row = i / columns + let column = i % columns + expect(cell.0) == container + guard let renderObject = cell.1 as? PDFSlicedObject else { + fail() + return + } + } } } } From 98424fe74d599feac435858273b5f36fb8409d7b Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 14:12:48 +0200 Subject: [PATCH 07/13] Added frame test for table --- .../Internal/Table/PDFTableObjectSpec.swift | 156 +++++++++++++++--- 1 file changed, 137 insertions(+), 19 deletions(-) diff --git a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift index 033a5675..c77f9aab 100644 --- a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift +++ b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift @@ -1,3 +1,4 @@ +import CoreGraphics import Quick import Nimble @testable import TPPDF @@ -6,11 +7,11 @@ class PDFTableObjectSpec: QuickSpec { override func spec() { describe("PDFTableObject") { - describe("calculations") { + describe("calculation result frames") { context("unmerged cells on multiple pages without splicing and no table headers on every page") { let container = PDFContainer.contentLeft - let rows = 50 + let rows = 40 let columns = 4 let count = rows * columns let table = PDFTable(rows: rows, columns: columns) @@ -28,27 +29,145 @@ class PDFTableObjectSpec: QuickSpec { } } - var result: [PDFLocatedRenderObject]! + let generator = PDFGenerator(document: .init(format: .a4)) + let tableObject = PDFTableObject(table: table) + let result = try! tableObject.calculate(generator: generator, container: container) - beforeEach { - let generator = PDFGenerator(document: .init(format: .a4)) - let tableObject = PDFTableObject(table: table) + it("should return correct cell count including necessary page breaks") { + expect(result).to(haveCount(count + 3)) + } + + // Page breaks should be at 52, 101, 150 + + it("should have a page break between first and second page") { + let breakObject = PDFPageBreakObject() + breakObject.stayOnSamePage = true + expect(result[52].1 as? PDFPageBreakObject) == breakObject + } + + it("should have a page break between second and third page") { + let breakObject = PDFPageBreakObject() + breakObject.stayOnSamePage = true + expect(result[101].1 as? PDFPageBreakObject) == breakObject + } + + it("should have a page break between third and fourth page") { + let breakObject = PDFPageBreakObject() + breakObject.stayOnSamePage = true + expect(result[150].1 as? PDFPageBreakObject) == breakObject + } + + let frames_0_10: [[CGRect]] = (0..<11) + .map ({ row -> [CGRect] in + (0..<4).map { col -> CGRect in + CGRect(x: [70, 117.5, 260, 402.5][col], + y: 85 + CGFloat(row) * 47, + width: [27.5, 122.5, 122.5, 122.5][col], + height: row >= 10 ? 50 : 37) + } + }) + let frames_11_13: [[CGRect]] = (11..<13) + .map ({ row -> [CGRect] in + (0..<4).map { col -> CGRect in + CGRect(x: [70, 117.5, 260, 402.5][col], + y: 615 + CGFloat(row - 11) * 60, + width: [27.5, 122.5, 122.5, 122.5][col], + height: row >= 10 ? 50 : 37) + } + }) + let frames_13_25: [[CGRect]] = (13..<25) + .map ({ row -> [CGRect] in + (0..<4).map { col -> CGRect in + CGRect(x: [70, 117.5, 260, 402.5][col], + y: 60 + CGFloat(row - 13) * 60, + width: [27.5, 122.5, 122.5, 122.5][col], + height: row >= 10 ? 50 : 37) + } + }) + let frames_25_37: [[CGRect]] = (25..<37) + .map ({ row -> [CGRect] in + (0..<4).map { col -> CGRect in + CGRect(x: [70, 117.5, 260, 402.5][col], + y: 60 + CGFloat(row - 25) * 60, + width: [27.5, 122.5, 122.5, 122.5][col], + height: row >= 10 ? 50 : 37) + } + }) + let frames_37_40: [[CGRect]] = (37..<40) + .map ({ row -> [CGRect] in + (0..<4).map { col -> CGRect in + CGRect(x: [70, 117.5, 260, 402.5][col], + y: 60 + CGFloat(row - 37) * 60, + width: [27.5, 122.5, 122.5, 122.5][col], + height: row >= 10 ? 50 : 37) + } + }) + + // test cells on first page + for row in 0..<13 { + for column in 0..<4 { + context("cell \(row) \(column)") { + let locatedCell = result[row * columns + column] + + it("should be in the correct container") { + expect(locatedCell.0) == container + } - expect { - result = try tableObject.calculate(generator: generator, container: container) - }.toNot(throwError()) + it("should have correct frame") { + let expectedFrames = frames_0_10 + frames_11_13 + expect(locatedCell.1.frame) == expectedFrames[row][column] + } + } + } + } + + // test cells on second page + for row in 13..<25 { + for column in 0..<4 { + context("cell \(row) \(column)") { + let locatedCell = result[1 + (row * columns + column)] + + it("should be in the correct container") { + expect(locatedCell.0) == container + } + + it("should have correct frame") { + expect(locatedCell.1.frame) == frames_13_25[row - 13][column] + } + } + } } - it("should return all cells and necessary page breaks") { - expect(result).to(haveCount(count)) + // test cells on third page + for row in 25..<37 { + for column in 0..<4 { + context("cell \(row) \(column)") { + let locatedCell = result[2 + (row * columns + column)] - for (i, cell) in result.enumerated() { - let row = i / columns - let column = i % columns - expect(cell.0) == container - guard let renderObject = cell.1 as? PDFSlicedObject else { - fail() - return + it("should be in the correct container") { + expect(locatedCell.0) == container + } + + it("should have correct frame") { + expect(locatedCell.1.frame) == frames_25_37[row - 25][column] + } + } + } + } + + // test cells on fourth page + for row in 37..<40 { + for column in 0..<4 { + context("cell \(row) \(column)") { + let locatedCell = result[3 + row * columns + column] + + it("should be in the correct container") { + expect(locatedCell.0) == container + } + + it("should have correct frame") { + expect(locatedCell.1.frame) == frames_37_40[row - 37][column] + } } } } @@ -57,4 +176,3 @@ class PDFTableObjectSpec: QuickSpec { } } } - From 2f246b39ff65333bd14c27bff65965d8eb866ea4 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 14:46:32 +0200 Subject: [PATCH 08/13] Fixed test cases per OS --- Source/Internal/Utils/PDFConstants.swift | 10 ++++ .../Internal/Table/PDFTableObjectSpec.swift | 49 ++++++++++--------- 2 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 Source/Internal/Utils/PDFConstants.swift diff --git a/Source/Internal/Utils/PDFConstants.swift b/Source/Internal/Utils/PDFConstants.swift new file mode 100644 index 00000000..729a2f0a --- /dev/null +++ b/Source/Internal/Utils/PDFConstants.swift @@ -0,0 +1,10 @@ +import CoreGraphics + +/// Constants used throught the framework +public class PDFConstants { + + /// Default font size for objects + /// + /// In earlier versions the default `UIFont.systemFontSize` was used, but during implementation of macOS support, findings showed that it `NSFont.systemFontSize` differs. Therefore the new default fontSize is declared here + public static let defaultFontSize: CGFloat = 17 +} diff --git a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift index c77f9aab..32fb5a7b 100644 --- a/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift +++ b/Tests/TPPDFTests/Internal/Table/PDFTableObjectSpec.swift @@ -6,6 +6,8 @@ import Nimble class PDFTableObjectSpec: QuickSpec { override func spec() { + // Let's not test on macOS, as small changes in the font messes up all values + #if os(iOS) describe("PDFTableObject") { describe("calculation result frames") { context("unmerged cells on multiple pages without splicing and no table headers on every page") { @@ -42,19 +44,19 @@ class PDFTableObjectSpec: QuickSpec { it("should have a page break between first and second page") { let breakObject = PDFPageBreakObject() breakObject.stayOnSamePage = true - expect(result[52].1 as? PDFPageBreakObject) == breakObject + expect(result[56].1 as? PDFPageBreakObject) == breakObject } it("should have a page break between second and third page") { let breakObject = PDFPageBreakObject() breakObject.stayOnSamePage = true - expect(result[101].1 as? PDFPageBreakObject) == breakObject + expect(result[105].1 as? PDFPageBreakObject) == breakObject } it("should have a page break between third and fourth page") { let breakObject = PDFPageBreakObject() breakObject.stayOnSamePage = true - expect(result[150].1 as? PDFPageBreakObject) == breakObject + expect(result[154].1 as? PDFPageBreakObject) == breakObject } let frames_0_10: [[CGRect]] = (0..<11) @@ -63,48 +65,48 @@ class PDFTableObjectSpec: QuickSpec { CGRect(x: [70, 117.5, 260, 402.5][col], y: 85 + CGFloat(row) * 47, width: [27.5, 122.5, 122.5, 122.5][col], - height: row >= 10 ? 50 : 37) + height: row >= 10 ? 48 : 37) } }) - let frames_11_13: [[CGRect]] = (11..<13) + let frames_11_13: [[CGRect]] = (11..<14) .map ({ row -> [CGRect] in (0..<4).map { col -> CGRect in CGRect(x: [70, 117.5, 260, 402.5][col], - y: 615 + CGFloat(row - 11) * 60, + y: 613 + CGFloat(row - 11) * 58, width: [27.5, 122.5, 122.5, 122.5][col], - height: row >= 10 ? 50 : 37) + height: 48) } }) - let frames_13_25: [[CGRect]] = (13..<25) + let frames_13_25: [[CGRect]] = (14..<26) .map ({ row -> [CGRect] in (0..<4).map { col -> CGRect in CGRect(x: [70, 117.5, 260, 402.5][col], - y: 60 + CGFloat(row - 13) * 60, + y: 60 + CGFloat(row - 14) * 58, width: [27.5, 122.5, 122.5, 122.5][col], - height: row >= 10 ? 50 : 37) + height: 48) } }) - let frames_25_37: [[CGRect]] = (25..<37) + let frames_25_37: [[CGRect]] = (26..<39) .map ({ row -> [CGRect] in (0..<4).map { col -> CGRect in CGRect(x: [70, 117.5, 260, 402.5][col], - y: 60 + CGFloat(row - 25) * 60, + y: 60 + CGFloat(row - 26) * 58, width: [27.5, 122.5, 122.5, 122.5][col], - height: row >= 10 ? 50 : 37) + height: 48) } }) - let frames_37_40: [[CGRect]] = (37..<40) + let frames_37_40: [[CGRect]] = (38..<40) .map ({ row -> [CGRect] in (0..<4).map { col -> CGRect in CGRect(x: [70, 117.5, 260, 402.5][col], - y: 60 + CGFloat(row - 37) * 60, + y: 60 + CGFloat(row - 38) * 58, width: [27.5, 122.5, 122.5, 122.5][col], - height: row >= 10 ? 50 : 37) + height: 48) } }) // test cells on first page - for row in 0..<13 { + for row in 0..<14 { for column in 0..<4 { context("cell \(row) \(column)") { let locatedCell = result[row * columns + column] @@ -122,7 +124,7 @@ class PDFTableObjectSpec: QuickSpec { } // test cells on second page - for row in 13..<25 { + for row in 14..<26 { for column in 0..<4 { context("cell \(row) \(column)") { let locatedCell = result[1 + (row * columns + column)] @@ -132,14 +134,14 @@ class PDFTableObjectSpec: QuickSpec { } it("should have correct frame") { - expect(locatedCell.1.frame) == frames_13_25[row - 13][column] + expect(locatedCell.1.frame) == frames_13_25[row - 14][column] } } } } // test cells on third page - for row in 25..<37 { + for row in 26..<38 { for column in 0..<4 { context("cell \(row) \(column)") { let locatedCell = result[2 + (row * columns + column)] @@ -149,14 +151,14 @@ class PDFTableObjectSpec: QuickSpec { } it("should have correct frame") { - expect(locatedCell.1.frame) == frames_25_37[row - 25][column] + expect(locatedCell.1.frame) == frames_25_37[row - 26][column] } } } } // test cells on fourth page - for row in 37..<40 { + for row in 38..<40 { for column in 0..<4 { context("cell \(row) \(column)") { let locatedCell = result[3 + row * columns + column] @@ -166,7 +168,7 @@ class PDFTableObjectSpec: QuickSpec { } it("should have correct frame") { - expect(locatedCell.1.frame) == frames_37_40[row - 37][column] + expect(locatedCell.1.frame) == frames_37_40[row - 38][column] } } } @@ -174,5 +176,6 @@ class PDFTableObjectSpec: QuickSpec { } } } + #endif } } From 3aad7d8c3406ca6ee3be7e8f5d11897f39835384 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 19 Jul 2020 14:51:55 +0200 Subject: [PATCH 09/13] Fixed class PDFConstants not found --- Source/Internal/Utils/PDFConstants.swift | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 Source/Internal/Utils/PDFConstants.swift diff --git a/Source/Internal/Utils/PDFConstants.swift b/Source/Internal/Utils/PDFConstants.swift deleted file mode 100644 index 729a2f0a..00000000 --- a/Source/Internal/Utils/PDFConstants.swift +++ /dev/null @@ -1,10 +0,0 @@ -import CoreGraphics - -/// Constants used throught the framework -public class PDFConstants { - - /// Default font size for objects - /// - /// In earlier versions the default `UIFont.systemFontSize` was used, but during implementation of macOS support, findings showed that it `NSFont.systemFontSize` differs. Therefore the new default fontSize is declared here - public static let defaultFontSize: CGFloat = 17 -} From efef807107f33d91da49a1b8a0335a56ad6b6977 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Wed, 5 Aug 2020 22:43:20 +0200 Subject: [PATCH 10/13] Fixed typo of should split cells property --- Source/API/Table/PDFTable.swift | 2 +- Source/Internal/Table/PDFTableObject.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/API/Table/PDFTable.swift b/Source/API/Table/PDFTable.swift index e7531ea1..597c484d 100644 --- a/Source/API/Table/PDFTable.swift +++ b/Source/API/Table/PDFTable.swift @@ -50,7 +50,7 @@ public class PDFTable: PDFDocumentObject { /** Cells should split when overlapping page */ - public var shouldSplitCellsOnPageBeak = false + public var shouldSplitCellsOnPageBreak = false /** Count of rows and columns in this table diff --git a/Source/Internal/Table/PDFTableObject.swift b/Source/Internal/Table/PDFTableObject.swift index c05092bf..62ab88af 100644 --- a/Source/Internal/Table/PDFTableObject.swift +++ b/Source/Internal/Table/PDFTableObject.swift @@ -334,7 +334,7 @@ internal class PDFTableObject: PDFRenderObject { items: nextPageCells, minOffset: minOffset, maxOffset: maxOffset, - shouldSplitCellsOnPageBeak: table.shouldSplitCellsOnPageBeak) + shouldSplitCellsOnPageBeak: table.shouldSplitCellsOnPageBreak) let onPageCells = filterResult.cells nextPageCells = filterResult.remainder // If none of the cells fit on the current page, the algorithm will try again on the next page and if it occurs again, an error should be thrown @@ -413,7 +413,7 @@ internal class PDFTableObject: PDFRenderObject { /// - items: List of cells to filter /// - minOffset: Minimum `y`-position on the page /// - maxOffset: Maximum `y`-position on the page - /// - shouldSplitCellsOnPageBeak: If `true`, cells won't be sliced and shown on both pages, instead moved entirely to the next page + /// - shouldSplitCellsOnPageBreak: If `true`, cells won't be sliced and shown on both pages, instead moved entirely to the next page /// - Returns: Two lists of cells, see `FilteredCells` internal func filterCellsOnPage(for generator: PDFGenerator, items: [PDFTableCalculatedCell], minOffset: CGFloat, maxOffset: CGFloat, shouldSplitCellsOnPageBeak: Bool) -> FilteredCells { // Maximum height available From 286ca2e33d0fa31b39c9ac430afcb34a2cc2ffcb Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Wed, 14 Oct 2020 12:13:05 +0200 Subject: [PATCH 11/13] Added missing file to project --- TPPDF.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TPPDF.xcodeproj/project.pbxproj b/TPPDF.xcodeproj/project.pbxproj index f3369aa7..ab96d17f 100644 --- a/TPPDF.xcodeproj/project.pbxproj +++ b/TPPDF.xcodeproj/project.pbxproj @@ -161,6 +161,7 @@ D4803D3A24703E5300DDA039 /* PDFAttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4803C9F24703E5300DDA039 /* PDFAttributedText.swift */; }; D4803D3B24703E5300DDA039 /* PDFText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4803CA024703E5300DDA039 /* PDFText.swift */; }; D4803D3C24703E5300DDA039 /* PDFTextStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4803CA124703E5300DDA039 /* PDFTextStyle.swift */; }; + D4839C39253706950005BB87 /* PDFTableCalculatedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4839C38253706950005BB87 /* PDFTableCalculatedCell.swift */; }; D4EE2F9724A34C990004E3B9 /* PDFContextGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EE2F9624A34C990004E3B9 /* PDFContextGraphics.swift */; }; D4EE2F9924A34CBF0004E3B9 /* CrossPlattformGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EE2F9824A34CBF0004E3B9 /* CrossPlattformGraphics.swift */; }; OBJ_419 /* AdapterProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_331 /* AdapterProtocols.swift */; }; @@ -621,6 +622,7 @@ D4803C9F24703E5300DDA039 /* PDFAttributedText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFAttributedText.swift; sourceTree = ""; }; D4803CA024703E5300DDA039 /* PDFText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFText.swift; sourceTree = ""; }; D4803CA124703E5300DDA039 /* PDFTextStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFTextStyle.swift; sourceTree = ""; }; + D4839C38253706950005BB87 /* PDFTableCalculatedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFTableCalculatedCell.swift; sourceTree = ""; }; D4EE2F9624A34C990004E3B9 /* PDFContextGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFContextGraphics.swift; sourceTree = ""; }; D4EE2F9824A34CBF0004E3B9 /* CrossPlattformGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossPlattformGraphics.swift; sourceTree = ""; }; "Nimble::Nimble::Product" /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1054,6 +1056,7 @@ D4803C3724703E5300DDA039 /* Table */ = { isa = PBXGroup; children = ( + D4839C38253706950005BB87 /* PDFTableCalculatedCell.swift */, D4803C3824703E5300DDA039 /* PDFTableMergeUtil.swift */, D4803C3924703E5300DDA039 /* PDFTableNode.swift */, D4803C3A24703E5300DDA039 /* PDFTableObject.swift */, @@ -2265,6 +2268,7 @@ D4803D1124703E5300DDA039 /* PDFBezierPath.swift in Sources */, D4803D3A24703E5300DDA039 /* PDFAttributedText.swift in Sources */, D4803D2224703E5300DDA039 /* PDFTable+RowSubscripts.swift in Sources */, + D4839C39253706950005BB87 /* PDFTableCalculatedCell.swift in Sources */, D4803CF924703E5300DDA039 /* PDFSection.swift in Sources */, D4803CB924703E5300DDA039 /* PDFPaginationStyle+Equatable.swift in Sources */, D4803CD124703E5300DDA039 /* PDFRectangleObject.swift in Sources */, From 84e7ef0ec9be0faddc5ddfcda9add0e4b14261c9 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Fri, 4 Dec 2020 20:43:32 +0100 Subject: [PATCH 12/13] Fixed typo after rebase --- Shared/Examples/ExperimentFactory.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/Examples/ExperimentFactory.swift b/Shared/Examples/ExperimentFactory.swift index 7b94cff4..58234932 100644 --- a/Shared/Examples/ExperimentFactory.swift +++ b/Shared/Examples/ExperimentFactory.swift @@ -19,7 +19,7 @@ class ExperimentFactory: ExampleFactory { table.margin = 10 table.padding = 10 table.showHeadersOnEveryPage = false - table.shouldSplitCellsOnPageBeak = false + table.shouldSplitCellsOnPageBreak = false table.style.columnHeaderCount = 3 for row in 0.. Date: Sat, 5 Dec 2020 13:58:13 +0100 Subject: [PATCH 13/13] updated Changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b74dfc..d3be1fcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,21 @@ **Implemented enhancements:** +- Added optional table cell splicing disabling + **Fixed bugs:** **Closed issues:** +- Issue #205 +- Issue #222 +- Issue #243 +- Issue #249 + **Merged pull requests:** +- PR #223 + ## [2.3.1](https://github.com/techprimate/TPPDF/tree/2.3.1) (2020-09-23) [Full Changelog](https://github.com/techprimate/TPPDF/compare/2.3.0...2.3.1)