techprimate / TPPDF

TPPDF is a simple-to-use PDF builder for iOS and macOS written in Swift

Home Page:https://techprimate.github.io/TPPDF/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Adding a header image and a PDFTable in the document header cuts off the top of the table

brendand opened this issue · comments

ℹ Please fill out this template when filing an issue.
All lines beginning with an ℹ symbol instruct you with what info we expect. You can delete those lines once you've filled in the info.

What did you do?

I added a banner image to the document's .headerLeft and a table in the .headerLeft. The table has an Image to the left and Text to the right of the image.

document.add(.headerLeft, image: bannerPDFImage)

document.add(.headerLeft, table: headerTable)

What did you expect to happen?

I expected to see the full image and text beneath the banner image. Instead, there's a gap between the banner image and table below, with the top of the table cut off.

What happened instead?

image

TPPDF Environment

TPPDF version: 2.5.3
Xcode version: 14.2
Swift version: 5.7

Demo Code / Project

From the TableExampleFactory.swift file:


//
//  TableExampleFactory.swift
//  TPPDF_Example
//
//  Created by Philip Niedertscheider on 16.12.19.
//  Copyright © 2022 techprimate GmbH. All rights reserved.
//

#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif

import TPPDF

class TableExampleFactory: ExampleFactory {

    func generateDocument() -> [PDFDocument] {
        let document = PDFDocument(format: .a4)

		let headerStyle = PDFTableStyleDefaults.none

		let headerTable = PDFTable(rows: 1, columns: 2)
		headerTable.widths = [0.1, 0.9]
		headerTable.style = headerStyle
		
		var tableContent : [[PDFTableContentable]] = [[PDFTableContentable]]()
		var rowContent : [PDFTableContentable] = [PDFTableContentable]()
		
		if #available(macOS 12.0, *) {

			let bannerImage = Image(named: "Business Logo Banner.jpg")
			if let bannerImage = bannerImage {
				let bannerPDFImage = PDFImage(image: bannerImage)
				document.add(.headerLeft, image: bannerPDFImage)
			}
			
			var formIcon = Image(systemSymbolName: "doc.plaintext", accessibilityDescription: "")
			formIcon?.resizingMode = .tile
			var config = NSImage.SymbolConfiguration(textStyle: .largeTitle,
													 scale: .large)
			config = config.applying(NSImage.SymbolConfiguration.preferringMulticolor())
			formIcon = formIcon?.withSymbolConfiguration(config)
			if let formIcon = formIcon {
				rowContent.append(formIcon)
			} else {
				rowContent.append("")
			}
			
			headerStyle.contentStyle = PDFTableCellStyle(
				
				borders: PDFTableCellBorders(left: PDFLineStyle(type: .none),
											 top: PDFLineStyle(type: .none),
											 right: PDFLineStyle(type: .none),
											 bottom: PDFLineStyle(type: .none)),
				
				font: Font.systemFont(ofSize: 28, weight: .light)
			)
			
			rowContent.append("Test Header Text Content")
			
			tableContent.append(rowContent)
			headerTable.content = tableContent
			headerTable.rows.allCellsAlignment = .left
			document.add(.headerLeft, table: headerTable)
			
		} else {
			// Fallback on earlier versions
		}

		
        // Create a table
        var table = PDFTable(rows: 34, columns: 4)

        // Tables can contain Strings, Numbers, Images or nil, in case you need an empty cell.
        // If you add a unknown content type, an assertion will be thrown and the rendering will stop.
        table.content = [
            [nil, "Name",      "Image",                        "Description"],
            [1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
            [2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
            [3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
            [4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
            [1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
            [2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
            [3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
            [4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
			[1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
			[2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
			[3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
			[4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
			[1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
			[2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
			[3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
			[4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
			[1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
			[2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
			[3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
			[4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
			[1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
			[2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
			[3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
			[4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
			[1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
			[2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
			[3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
			[4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],
			[1,   "Waterfall", Image(named: "Image-1.jpg")!, "Water flowing down stones."],
			[2,   "Forrest",   Image(named: "Image-2.jpg")!, "Sunlight shining through the leafs."],
			[3,   "Fireworks", Image(named: "Image-3.jpg")!, "Fireworks exploding into 100.000 stars"],
			[4,   "Fields",    Image(named: "Image-4.jpg")!, "Crops growing big and providing food."],

            [nil, nil,         nil,                            "Many beautiful places"]
        ]
        table.rows.allRowsAlignment = [.center, .left, .center, .right]

        // The widths of each column is proportional to the total width, set by a value between 0.0 and 1.0, representing percentage.

        table.widths = [
            0.1, 0.25, 0.35, 0.3
        ]

        // To speed up table styling, use a default and change it

        let style = PDFTableStyleDefaults.simple

        // Change standardized styles
        style.footerStyle = PDFTableCellStyle(
            colors: (
                fill: Color(red: 0.171875,
                              green: 0.2421875,
                              blue: 0.3125,
                              alpha: 1.0),
                text: Color.white
            ),
            borders: PDFTableCellBorders(left: PDFLineStyle(type: .full),
                                         top: PDFLineStyle(type: .full),
                                         right: PDFLineStyle(type: .full),
                                         bottom: PDFLineStyle(type: .full)),

            font: Font.systemFont(ofSize: 10)
        )

        // Simply set the amount of footer and header rows

        style.columnHeaderCount = 1
        style.footerCount = 1

        table.style = style

        // Style each cell individually
        table[1,1].style = PDFTableCellStyle(colors: (fill: Color.yellow, text: Color.black))

        // Set table padding and margin
        table.padding = 5.0
        table.margin = 10.0

        // In case of a linebreak during rendering we want to have table headers on each page.

        table.showHeadersOnEveryPage = true

        document.add(table: table)

        // Another table:

        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.style.columnHeaderCount = 3

        for row in 0..<table.size.rows {
            table[row, 0].content = "\(row)".asTableContent
            for column in 1..<table.size.columns {
                table[row, column].content = "\(row),\(column)".asTableContent
            }
        }

        for i in stride(from: 3, to: 48, by: 3) {
            table[rows: i...(i + 2), column: 1].merge(with: PDFTableCell(content: Array(repeating: "\(i),1", count: 3).joined(separator: "\n").asTableContent,
                                                           alignment: .center))
        }
        for i in stride(from: 4, to: 47, by: 3) {
            table[rows: i...(i + 2), column: 2].merge(with: PDFTableCell(content: Array(repeating: "\(i),2", count: 3).joined(separator: "\n").asTableContent,
                                                           alignment: .center))
        }
        for i in stride(from: 5, to: 48, by: 3) {
            table[rows: i...(i + 2), column: 3].merge(with: PDFTableCell(content: Array(repeating: "\(i),3", count: 3).joined(separator: "\n").asTableContent,
                                                           alignment: .center))
        }

        table[rows: 0..<2, column: 2].merge()
        table[rows: 1..<3, column: 3].merge()

        document.add(table: table)

        return [document]
    }
}

and here's the banner image I added to the project for testing purposes.

Business Logo Banner

So I discovered if I make a small change to the TPPDF framework code, the table in the header lines up. I still don't know what's causing the gap between the header banner image and the table in the header. I would think there should be no gap and the whole header should be a bit shorter.

On line 62 of PDFTableObject.swift, I added 10 to that line's y parameter:

let origin = CGPoint(x: tableOrigin.x + originX, y: verticalOrigins[node.position.row] + 10)

image

I don't know why it solves the problem at the moment, but it does. I still think the position of the table is too low. Or at least I should be able to control the gap between the table and the header image.

Well, although that fixes the header issue, the content cells all get a gap between rows too, so that's no good.

It would seem that the table top is getting cut off because I have style.columHeaderCount = 0. So it seems the code expects every PDFTable to have a header. In this case I'm using a PDFTable simply to draw an icon to the left of the text.

Is there a better way to do that inside the header? I tried with an attributed string and an NSTextAttachment, but it got stripped out when the PDF was generated.