diff --git a/src/WriteDocx.jl b/src/WriteDocx.jl
index abfa7d6..8995541 100644
--- a/src/WriteDocx.jl
+++ b/src/WriteDocx.jl
@@ -1037,6 +1037,86 @@ Base.@kwdef struct Footers
even::Maybe{Footer} = nothing
end
+"""
+ Column(; [width, space])
+
+Describes a single column. `width` sets the column width, and `space` sets the whitespace after the column (before the next column).
+
+See also: [`Columns`](@ref)
+"""
+Base.@kwdef struct Column
+ width::Maybe{Twip} = nothing
+ space::Maybe{Twip} = nothing
+end
+
+"""
+ Columns(; kwargs...)
+
+Sets the columns for a [`Section`](@ref).
+
+## Keyword arguments
+
+| Keyword | Description |
+| :-- | :-- |
+| `equal::Bool = true` | Sets all columns to be equal-width with `space` between every column |
+| `num::Int` | The number of columns to layout. Ignored if `equal==false`. |
+| `space::`[`Twip`](@ref) | The space between columns. Ignored if `equal==false`. |
+| `sep::Bool = false` | Sets whether a vertical separator line is drawn between columns |
+| `cols::Vector{`[`Column`](@ref)`}` | A vector of custom columns. May not be specified with `equal==true`. |
+"""
+struct Columns
+ num::Int
+ space::Maybe{Twip}
+ sep::Bool
+ cols::Vector{Column}
+ equal::Bool
+
+ function Columns(;kwargs...)
+ sd = setdiff(keys(kwargs), (:num, :space, :sep, :cols, :equal))
+ isempty(sd) || throw(ArgumentError("got unsupported keyword argument(s): $(sd)"))
+ if haskey(kwargs, :cols) && haskey(kwargs, :equal) && kwargs[:equal]
+ throw(ArgumentError("keyword arguments \"cols\" and \"equal\" (or equal=true) are mutually incompatible. If \"cols\" is given, \"equal\" must be false or not defined"))
+ elseif !haskey(kwargs, :cols) && haskey(kwargs, :equal) && !kwargs[:equal]
+ throw(ArgumentError("\"cols\" must be defined/non-empty when \"equal=false\""))
+ end
+ haskey(kwargs, :cols) && isempty(kwargs[:cols]) && throw(ArgumentError("\"cols\" must not be empty"))
+
+ return new(
+ get(kwargs, :num, 1),
+ get(kwargs, :space, nothing),
+ get(kwargs, :sep, false),
+ get(kwargs, :cols, Column[]),
+ get(kwargs, :equal, !haskey(kwargs, :cols)))
+ end
+end
+
+"""
+ PageMargins(; top, right, bottom, left, kwargs...)
+
+Describes page margins in a [`Section`](@ref).
+
+## Keyword arguments
+
+| Keyword | Description |
+| :-- | :-- |
+| `top::`[`Twip`](@ref) | The top margin. |
+| `right::`[`Twip`](@ref) | The right margin. |
+| `bottom::`[`Twip`](@ref) | The bottom margin. |
+| `left::`[`Twip`](@ref) | The left margin. |
+| `header::`[`Twip`](@ref)`=Twip(0)` | The header margin. |
+| `footer::`[`Twip`](@ref)`=Twip(0)` | The footer margin. |
+| `gutter::`[`Twip`](@ref)`=Twip(0)` | The gutter margin. |
+"""
+Base.@kwdef struct PageMargins
+ top::Twip
+ right::Twip
+ bottom::Twip
+ left::Twip
+ header::Twip = Twip(0)
+ footer::Twip = Twip(0)
+ gutter::Twip = Twip(0)
+end
+
"""
SectionProperties(; kwargs...)
@@ -1047,15 +1127,19 @@ Holds properties for a [`Section`](@ref).
| Keyword | Description |
| :-- | :-- |
| `pagesize::PageSize` | The size of each page in the section. |
+| `margins::PageMargins` | The margins for each page in the section |
| `valign::PageVerticalAlign.T` | The vertical alignment of content on each page of the section. |
| `headers::`[`Headers`](@ref) | Defines the header content shown at the top of each page of the section. |
| `footers::`[`Footers`](@ref) | Defines the footer content shown at the bottom of each page of the section. |
+| `columns::`[`Columns`](@ref) | Configures the columns in the section |
"""
Base.@kwdef struct SectionProperties
pagesize::Maybe{PageSize} = nothing
+ margins::Maybe{PageMargins} = nothing
valign::Maybe{PageVerticalAlign.T} = nothing
headers::Maybe{Headers} = nothing
footers::Maybe{Footers} = nothing
+ columns::Maybe{Columns} = nothing
end
"""
@@ -1590,9 +1674,15 @@ function to_xml(body::Body, rels)
if props.pagesize !== nothing
E.link!(section_params_node, to_xml(props.pagesize, rels))
end
+ if props.margins !== nothing
+ E.link!(section_params_node, to_xml(props.margins, rels))
+ end
if props.valign !== nothing
E.link!(section_params_node, to_xml(props.valign, rels))
end
+ if props.columns !== nothing
+ E.link!(section_params_node, to_xml(props.columns, rels))
+ end
if props.headers !== nothing
for type in (:default, :first, :even)
x = getproperty(props.headers, type)
@@ -1827,6 +1917,14 @@ function children(p::ParagraphProperties)
return c
end
+function children(p::Columns)
+ c = []
+ if !p.equal
+ append!(c, p.cols)
+ end
+ return c
+end
+
function children(p::TableCellProperties)
c = []
p.borders === nothing || push!(c, p.borders)
@@ -1899,6 +1997,7 @@ function attributes(t::Union{TableCellBorder, ParagraphBorder})
return attrs
end
attributes(p::PageSize) = (("w:h", p.height), ("w:w", p.width), ("w:orient", p.orientation))
+attributes(p::PageMargins) = (("w:top", p.top), ("w:right", p.right), ("w:bottom", p.bottom), ("w:left", p.left), ("w:header", p.header), ("w:footer", p.footer), ("w:gutter", p.gutter))
attributes(p::PageVerticalAlign.T) = (("w:val", p),)
attributes(p::Justification.T) = (("w:val", p),)
function attributes(s::Style)
@@ -1915,6 +2014,26 @@ end
attributes(p::ParagraphStyle) = (("w:val", p.name),)
attributes(p::RunStyle) = (("w:val", p.name),)
attributes(p::VerticalAlignment.T) = (("w:val", p),)
+function attributes(p::Column)
+ attrs = Tuple{String, Any}[]
+ p.width === nothing || push!(attrs, ("w:w", p.width))
+ p.space === nothing || push!(attrs, ("w:space", p.space))
+ return attrs
+end
+function attributes(p::Columns)
+ attrs = Tuple{String, Any}[]
+ if p.num > 1 && p.equal
+ push!(attrs, ("w:equalWidth", p.equal))
+ push!(attrs, ("w:num", p.num))
+ p.sep === false || push!(attrs, ("w:sep", p.sep))
+ p.space === nothing || push!(attrs, ("w:space", p.space))
+ elseif !p.equal
+ push!(attrs, ("w:equalWidth", p.equal))
+ push!(attrs, ("w:num", length(p.cols)))
+ p.sep === false || push!(attrs, ("w:sep", p.sep))
+ end
+ return attrs
+end
function attributes(f::Fonts)
attrs = Tuple{String, Any}[]
f.ascii === nothing || push!(attrs, ("w:ascii", f.ascii))
@@ -2011,7 +2130,10 @@ function xmltag(t::Tuple{ParagraphBorder, Symbol})
throw(ArgumentError("Invalid symbol $(repr(sym)), options are :top, :left, :right, :bottom, :between."))
end
xmltag(::PageSize) = "w:pgSz"
+xmltag(::PageMargins) = "w:pgMar"
xmltag(::PageVerticalAlign.T) = "w:vAlign"
+xmltag(::Columns) = "w:cols"
+xmltag(::Column) = "w:col"
xmltag(::Style) = "w:style"
xmltag(::Justification.T) = "w:jc"
xmltag(::Fonts) = "w:rFonts"
diff --git a/test/references/breaks/word/document.xml b/test/references/breaks/word/document.xml
index 4ae1d18..f4270f6 100644
--- a/test/references/breaks/word/document.xml
+++ b/test/references/breaks/word/document.xml
@@ -12,6 +12,22 @@
after page break
-
+
+
+
+
+
+
+
+
+
+ column 1
+
+ column 2
+
+
+
+
+
diff --git a/test/references/columns/[Content_Types].xml b/test/references/columns/[Content_Types].xml
new file mode 100644
index 0000000..95c03c6
--- /dev/null
+++ b/test/references/columns/[Content_Types].xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/test/references/columns/_rels/.rels b/test/references/columns/_rels/.rels
new file mode 100644
index 0000000..27e1e49
--- /dev/null
+++ b/test/references/columns/_rels/.rels
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/test/references/columns/word/_rels/document.xml.rels b/test/references/columns/word/_rels/document.xml.rels
new file mode 100644
index 0000000..66cfbbf
--- /dev/null
+++ b/test/references/columns/word/_rels/document.xml.rels
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/test/references/columns/word/document.xml b/test/references/columns/word/document.xml
new file mode 100644
index 0000000..34cb0c6
--- /dev/null
+++ b/test/references/columns/word/document.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+ No columns No columns No columns No columns No columns No columns No columns No columns No columns No columns
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns 2 columns
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ different width columns different width columns different width columns different width columns different width columns different width columns different width columns different width columns different width columns different width columns different width columns different width columns
+
+
+
+
+
+
+
+
+
+
diff --git a/test/references/columns/word/styles.xml b/test/references/columns/word/styles.xml
new file mode 100644
index 0000000..7b8142b
--- /dev/null
+++ b/test/references/columns/word/styles.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/references/page_margins/[Content_Types].xml b/test/references/page_margins/[Content_Types].xml
new file mode 100644
index 0000000..95c03c6
--- /dev/null
+++ b/test/references/page_margins/[Content_Types].xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/test/references/page_margins/_rels/.rels b/test/references/page_margins/_rels/.rels
new file mode 100644
index 0000000..27e1e49
--- /dev/null
+++ b/test/references/page_margins/_rels/.rels
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/test/references/page_margins/word/_rels/document.xml.rels b/test/references/page_margins/word/_rels/document.xml.rels
new file mode 100644
index 0000000..66cfbbf
--- /dev/null
+++ b/test/references/page_margins/word/_rels/document.xml.rels
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/test/references/page_margins/word/document.xml b/test/references/page_margins/word/document.xml
new file mode 100644
index 0000000..06ab4f1
--- /dev/null
+++ b/test/references/page_margins/word/document.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ no margins
+
+
+
+
+
+
+
+
+
+
+
+ thiccc margins
+
+
+
+
+
+
+
diff --git a/test/references/page_margins/word/styles.xml b/test/references/page_margins/word/styles.xml
new file mode 100644
index 0000000..7b8142b
--- /dev/null
+++ b/test/references/page_margins/word/styles.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/runtests.jl b/test/runtests.jl
index aefa5ca..60d6a33 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -708,6 +708,62 @@ Base.show(io::IO, ::MIME"image/png", p::PNG) = write(io, p.bytes)
reftest_docx(doc, "multiple_sections")
end
+ @testset "Columns" begin
+ doc = W.Document(
+ W.Body([
+ W.Section([
+ W.Paragraph([
+ W.Run(
+ [W.Text("No columns " ^ 10)],
+ W.RunProperties(size = 40W.pt)
+ )
+ ])
+ ])
+ W.Section([
+ W.Paragraph([
+ W.Run(
+ [W.Text("2 columns " ^ 20)],
+ W.RunProperties(size = 40W.pt)
+ )
+ ])
+ ]; columns=W.Columns(;num=2))
+ W.Section([
+ W.Paragraph([
+ W.Run(
+ [W.Text("different width columns " ^ 12)],
+ W.RunProperties(size = 40W.pt)
+ )
+ ])
+ ]; columns=W.Columns(;cols=[W.Column(;width=4W.inch, space=0.5W.inch), W.Column(;width=2W.inch)]))
+ ])
+ )
+
+ reftest_docx(doc, "columns")
+ end
+
+ @testset "Page margins" begin
+ doc = W.Document(
+ W.Body([
+ W.Section([
+ W.Paragraph([
+ W.Run([
+ W.Text("no margins")
+ ])
+ ])
+ ])
+ W.Section([
+ W.Paragraph([
+ W.Run([
+ W.Text("thiccc margins")
+ ])
+ ])
+ ]; margins=W.PageMargins(;top=2.5W.inch, right=2.5W.inch, bottom=2.5W.inch, left=2.5W.inch))
+ ])
+ )
+
+ reftest_docx(doc, "page_margins")
+ end
+
@testset "Page sizes" begin
doc = W.Document(
W.Body(
@@ -892,6 +948,17 @@ Base.show(io::IO, ::MIME"image/png", p::PNG) = write(io, p.bytes)
)
])
])
+ W.Section([
+ W.Paragraph([
+ W.Run(
+ [
+ W.Text("column 1"),
+ W.Break(W.BreakType.column),
+ W.Text("column 2"),
+ ]
+ )
+ ])
+ ]; columns=W.Columns(;num=2))
]
)
)