Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/SwiftDocX/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ public class Document {
/// Document properties (metadata and accessibility)
public var properties: DocumentProperties

/// Page size and margins
public var pageAttributes: PageAttributes

/// Document header (appears at top of pages)
public var header: Header?

Expand All @@ -84,6 +87,7 @@ public class Document {
self.tables = []
self.elements = []
self.properties = DocumentProperties()
self.pageAttributes = PageAttributes()
self.header = nil
self.footer = nil
}
Expand Down
13 changes: 8 additions & 5 deletions Sources/SwiftDocX/Document/DocumentWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public class DocumentWriter {

// Build document XML with header/footer references
let documentXML = buildDocumentXMLWithHeaderFooter(
elements: document.elements,
document: document,
headerRelId: headerRelId,
footerRelId: footerRelId
)
Expand Down Expand Up @@ -210,7 +210,7 @@ public class DocumentWriter {
// MARK: - Private Helpers

private func buildDocumentXMLWithHeaderFooter(
elements: [DocumentElement],
document: Document,
headerRelId: String?,
footerRelId: String?
) -> String {
Expand All @@ -220,7 +220,7 @@ public class DocumentWriter {
<w:body>
"""

for element in elements {
for element in document.elements {
switch element {
case .paragraph(let paragraph):
xml += buildParagraphXML(paragraph)
Expand All @@ -237,8 +237,11 @@ public class DocumentWriter {
if let footerRelId = footerRelId {
xml += "<w:footerReference w:type=\"default\" r:id=\"\(footerRelId)\"/>"
}
xml += "<w:pgSz w:w=\"12240\" w:h=\"15840\"/>"
xml += "<w:pgMar w:top=\"1440\" w:right=\"1440\" w:bottom=\"1440\" w:left=\"1440\" w:header=\"720\" w:footer=\"720\" w:gutter=\"0\"/>"

// Page size and margins
let margins = document.pageAttributes.margins
xml += "<w:pgSz w:w=\"\(Int(document.pageAttributes.width * 20.0))\" w:h=\"\(Int(document.pageAttributes.height * 20.0))\"/>"
xml += "<w:pgMar w:top=\"\(Int(margins.top * 20.0))\" w:right=\"\(Int(margins.right * 20.0))\" w:bottom=\"\(Int(margins.bottom * 20.0))\" w:left=\"\(Int(margins.left * 20.0))\" w:header=\"\(Int(document.pageAttributes.headerMargin * 20.0))\" w:footer=\"\(Int(document.pageAttributes.footerMargin * 20.0))\" w:gutter=\"0\"/>"
xml += "</w:sectPr>"

xml += """
Expand Down
56 changes: 56 additions & 0 deletions Sources/SwiftDocX/Model/PageAttributes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import Foundation


/// Represents attributes of a page, including paper size and margins.
public struct PageAttributes: Equatable, Sendable {
/// The page width, in points. (72 pts per inch). Defaults to 8.5" (612 pts).
public var width: Double = 612.0

/// The page height, in points (72 pts per inch). Defaults to 11" (792 pts).
public var height: Double = 792.0

/// The margins of the page (top, bottom, left, right).
public var margins: Margins

/// The margin for the page header, in points (1 inch = 72 points). Defaults to 36.
public var headerMargin: Double = 36.0

/// The margin for the page footer, in points (1 inch = 72 points). Defaults to 36.
public var footerMargin: Double = 36.0

/// Initializes a `PageAttributes` with the default 8.5 x 11" page size, 1" margins, and 0.5" margins for the header/footer.
public init(width: Double = 612.0, height: Double = 792.0, margins: Margins = .init(top: 72.0, bottom: 72.0, left: 72.0, right: 72.0), headerMargin: Double = 36.0, footerMargin: Double = 36.0) {
self.width = width
self.height = height
self.margins = margins
self.headerMargin = headerMargin
self.footerMargin = footerMargin
}
}


/// Represents the margins of an element such as a page or table cell.
public struct Margins: Equatable, Sendable {
/// The element's top margin, in points (1 inch = 72 points). Defaults to 0.
public var top: Double

/// The element's bottom margin, in points (1 inch = 72 points). Defaults to 0.
public var bottom: Double

/// The element's left margin, in points (1 inch = 72 points). Defaults to 0.
public var left: Double

/// The element's right margin, in points (1 inch = 72 points). Defaults to 0.
public var right: Double

/// Initializes a `Margins`. By default, all edges are set to 0 points.
public init(top: Double = 0, bottom: Double = 0, left: Double = 0, right: Double = 0)
{
self.top = top
self.bottom = bottom
self.left = left
self.right = right
}
}


8 changes: 8 additions & 0 deletions Sources/SwiftDocX/Model/Table.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public class TableCell {
/// Cell width in points (nil for auto)
public var width: Double?

/// The inset from the cell edges to the content of the cell. This value overrides the value of `Table.cellMargins` if both are set.
public var margins: Margins?

/// Background/shading color
public var backgroundColor: Color?

Expand All @@ -99,6 +102,7 @@ public class TableCell {
public init() {
self.paragraphs = []
self.width = nil
self.margins = nil
self.backgroundColor = nil
self.verticalAlignment = nil
self.columnSpan = 1
Expand Down Expand Up @@ -178,6 +182,9 @@ public class Table {
/// Table width in points (nil for auto)
public var width: Double?

/// The margins (insets) to use for each cell in this `Table`. Can be overriden for a particular cell by setting `TableCell.margins`.
public var cellMargins: Margins?

/// Table alignment
public var alignment: ParagraphAlignment?

Expand All @@ -192,6 +199,7 @@ public class Table {
self.borders = .single
self.columnWidths = []
self.width = nil
self.cellMargins = nil
self.alignment = nil
self.accessibilityCaption = nil
self.accessibilitySummary = nil
Expand Down
19 changes: 19 additions & 0 deletions Sources/SwiftDocX/XML/DocumentXMLBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,16 @@ public class DocumentXMLBuilder {
xml += "<w:jc w:val=\"\(alignment.rawValue)\"/>"
}

// Table cell margins
if let margins = table.cellMargins {
xml += "<w:tblCellMar>"
xml += "<w:top w:w=\"\(Int(margins.top * 20.0))\" w:type=\"dxa\"/>"
xml += "<w:start w:w=\"\(Int(margins.left * 20.0))\" w:type=\"dxa\"/>"
xml += "<w:bottom w:w=\"\(Int(margins.bottom * 20.0))\" w:type=\"dxa\"/>"
xml += "<w:end w:w=\"\(Int(margins.right * 20.0))\" w:type=\"dxa\"/>"
xml += "</w:tblCellMar>"
}

// Accessibility: Caption (visible title) and Description (for screen readers)
if let caption = table.accessibilityCaption {
xml += "<w:tblCaption w:val=\"\(escapeXMLAttribute(caption))\"/>"
Expand Down Expand Up @@ -413,6 +423,15 @@ public class DocumentXMLBuilder {
xml += "<w:tcW w:w=\"0\" w:type=\"auto\"/>"
}

if let margins = cell.margins {
xml += "<w:tcMar>"
xml += "<w:top w:w=\"\(Int(margins.top * 20.0))\" w:type=\"dxa\"/>"
xml += "<w:start w:w=\"\(Int(margins.left * 20.0))\" w:type=\"dxa\"/>"
xml += "<w:bottom w:w=\"\(Int(margins.bottom * 20.0))\" w:type=\"dxa\"/>"
xml += "<w:end w:w=\"\(Int(margins.right * 20.0))\" w:type=\"dxa\"/>"
xml += "</w:tcMar>"
}

if cell.columnSpan > 1 {
xml += "<w:gridSpan w:val=\"\(cell.columnSpan)\"/>"
}
Expand Down