Skip to content

Commit

Permalink
Merge pull request #64 from ASML-Labs/fix-multiple-hlinks
Browse files Browse the repository at this point in the history
Fix multiple hyperlinks per slide
  • Loading branch information
matthijscox-asml authored Oct 22, 2024
2 parents 5f25d91 + f4116e9 commit 9d43091
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "PPTX"
uuid = "14a86994-10a4-4a7d-b9ad-ef6f3b1fac6a"
authors = ["Xander de Vries", "Matthijs Cox"]
version = "0.9.0"
version = "0.9.1"

[deps]
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Expand Down
5 changes: 4 additions & 1 deletion src/AbstractShape.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ function _show_string(shape::AbstractShape, compact::Bool)
return "$(typeof(shape))"
end

hlink_xml(hlink) = Dict("a:hlinkClick" => Dict("rId" => "rId$(rid(hlink))", "action" => "ppaction://hlinksldjump"))
function hlink_xml(hlink, relationship_map::Dict)
rel_id = relationship_map[hlink]
Dict("a:hlinkClick" => Dict("r:id" => "rId$rel_id", "action" => "ppaction://hlinksldjump"))
end
has_hyperlink(s::AbstractShape) = hasfield(typeof(s), :hlink) && !isnothing(s.hlink)
9 changes: 5 additions & 4 deletions src/Picture.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,14 @@ function _show_string(p::Picture, compact::Bool)
return show_string
end

function make_xml(shape::Picture, id::Int)
function make_xml(shape::Picture, id::Int, relationship_map::Dict)
cNvPr = Dict("p:cNvPr" => [Dict("id" => "$id"), Dict("name" => "Picture")])
cNvPicPr = Dict("p:cNvPicPr" => Dict("a:picLocks" => Dict("noChangeAspect" => "1")))
nvPr = Dict("p:nvPr" => missing)
nvPicPr = Dict("p:nvPicPr" => [cNvPr, cNvPicPr, nvPr])

blip = Dict("a:blip" => Dict("r:embed" => "rId$(rid(shape))"))
rel_id = relationship_map[shape]
blip = Dict("a:blip" => Dict("r:embed" => "rId$rel_id"))
stretch = Dict("a:stretch" => Dict("a:fillRect" => missing))
blipFill = Dict("p:blipFill" => [blip, stretch])

Expand Down Expand Up @@ -137,10 +138,10 @@ function filename(p::Picture)
return "$(source_filename)_$(p._uuid)$extension"
end

function relationship_xml(p::Picture)
function relationship_xml(p::Picture, r_id::Integer)
return Dict(
"Relationship" => [
Dict("Id" => "rId$(rid(p))"),
Dict("Id" => "rId$r_id"),
Dict("Type" => type_schema(p)),
Dict("Target" => "../media/$(filename(p))"),
],
Expand Down
37 changes: 30 additions & 7 deletions src/Slide.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ function Base.push!(slide::Slide, shape::AbstractShape)
end
end

function make_slide(s::Slide)::AbstractDict
function make_slide(s::Slide, relationship_map::Dict = slide_relationship_map(s))::AbstractDict
xml_slide = OrderedDict("p:sld" => main_attributes())

spTree = init_sptree()
initial_max_id = 1
for (index, shape) in enumerate(shapes(s))
id = index + initial_max_id
push!(spTree["p:spTree"], make_xml(shape, id))
push!(spTree["p:spTree"], make_xml(shape, id, relationship_map))
end

push!(xml_slide["p:sld"], OrderedDict("p:cSld" => [spTree]))
Expand Down Expand Up @@ -148,17 +148,32 @@ function type_schema(s::Slide)
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide"
end

function relationship_xml(s::Slide)
function relationship_xml(s::Slide, r_id::Integer)
return Dict(
"Relationship" => [
Dict("Id" => "rId$(rid(s))"),
Dict("Id" => "rId$r_id"),
Dict("Type" => type_schema(s)),
Dict("Target" => slide_fname(s)),
],
)
end

function make_slide_relationships(s::Slide)::AbstractDict
function slide_relationship_map(s::Slide)
d = Dict{Union{Slide, AbstractShape}, Int}()
r_id = 1 # first rid is reserved by slideLayout
for shape in shapes(s)
if has_rid(shape) && !haskey(d, shape)
r_id += 1
d[shape] = r_id
elseif has_hyperlink(shape) && !haskey(d, shape.hlink)
r_id += 1
d[shape.hlink] = r_id
end
end
return d
end

function make_slide_relationships(s::Slide, relationship_map::Dict = slide_relationship_map(s))::AbstractDict
xml_slide_rels = OrderedDict("Relationships" => Dict[])
push!(
xml_slide_rels["Relationships"],
Expand All @@ -176,12 +191,20 @@ function make_slide_relationships(s::Slide)::AbstractDict
),
),
)
used_r_ids = [1]
for shape in shapes(s)
r_id = 1
if has_rid(shape)
push!(xml_slide_rels["Relationships"], relationship_xml(shape))
r_id = relationship_map[shape]
r_shape = shape
end
if has_hyperlink(shape)
push!(xml_slide_rels["Relationships"], relationship_xml(shape.hlink))
r_id = relationship_map[shape.hlink]
r_shape = shape.hlink
end
if r_id used_r_ids
push!(xml_slide_rels["Relationships"], relationship_xml(r_shape, r_id))
push!(used_r_ids, r_id)
end
end
return xml_slide_rels
Expand Down
2 changes: 1 addition & 1 deletion src/Tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function _show_string(t::Table, compact::Bool)
return show_string
end

function make_xml(t::Table, id::Integer)
function make_xml(t::Table, id::Integer, relationship_map::Dict = Dict())
nvGraphicFramePr = make_nvGraphicFramePr(t, id)
xfrm = make_xfrm(t)
tbl = make_xml_table(t)
Expand Down
4 changes: 2 additions & 2 deletions src/TextBox.jl
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ function text_style_xml(t::TextStyle)
return style
end

function make_xml(t::TextBox, id::Int=1)
function make_xml(t::TextBox, id::Integer, relationship_map::Dict)
cNvPr = Dict("p:cNvPr" => Dict[Dict("id" => "$id"), Dict("name" => "TextBox")])
if has_hyperlink(t)
push!(cNvPr["p:cNvPr"], hlink_xml(t.hlink))
push!(cNvPr["p:cNvPr"], hlink_xml(t.hlink, relationship_map))
end

cNvSpPr = Dict("p:cNvSpPr" => Dict("txBox" => "1"))
Expand Down
10 changes: 8 additions & 2 deletions src/write.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function update_table_style!(w::ZipWriter, template::ZipBufferReader)
nothing
end

function add_contenttypes!(w::ZipWriter, template::ZipBufferReader)
function add_contenttypes!(w::ZipWriter, template::ZipBufferReader, pres::Presentation)
path = "[Content_Types].xml"
doc = EzXML.parsexml(zip_readentry(template, path))
r = root(doc)
Expand All @@ -93,6 +93,12 @@ function add_contenttypes!(w::ZipWriter, template::ZipBufferReader)
isnothing(findfirst(x -> (x.name == "Default" && x["Extension"] == ext), elements(r))) || continue
addelement!(r, "Default Extension=\"$ext\" ContentType=\"$ct\"")
end
for slide in pres.slides
slide_path = "/ppt/slides/slide$(slide.slide_nr).xml"
isnothing(findfirst(x -> (x.name == "Override" && x["PartName"] == slide_path), elements(r))) || continue
slide_ct = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"
addelement!(r, "Override PartName=\"$slide_path\" ContentType=\"$slide_ct\"")
end
zip_newfile(w, path; compress=true)
prettyprint(w, doc)
zip_commitfile(w)
Expand Down Expand Up @@ -178,7 +184,7 @@ function Base.write(
write_slides!(w, p, template_reader)
write_shapes!(w, p)
update_table_style!(w, template_reader)
add_contenttypes!(w, template_reader)
add_contenttypes!(w, template_reader, p)
# copy over any files from the template
# but don't overwrite any files in w
for i in zip_nentries(template_reader):-1:1
Expand Down
43 changes: 22 additions & 21 deletions test/testHyperlinks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
pres = Presentation()
s2 = Slide()
s3 = Slide()
s4 = Slide()

box = TextBox(content = "Hyperlinked text", hlink = s3)
push!(s2, box)

@testset "has hyperlink" begin
@test PPTX.has_hyperlink(box)
Expand All @@ -13,32 +15,31 @@
end

@testset "hyperlink xml" begin
xml = PPTX.hlink_xml(box.hlink)
@test xml["a:hlinkClick"]["rId"] == "rId$(PPTX.rid(s3))"
relationship_map = PPTX.slide_relationship_map(s2)
@test relationship_map[box.hlink] == 2
xml = PPTX.hlink_xml(box.hlink, relationship_map)
@test xml["a:hlinkClick"]["r:id"] == "rId2"
end

push!(s2, box)
push!(pres, s2)
push!(pres, s3)
# push the same link once more
box2 = TextBox(content = "Hyperlinked text", hlink = s3)
push!(s2, box2)

@testset "hyperlink xml after rid update" begin
# After updating the rId of s2 the hyperlink xml should also be updated
xml = PPTX.hlink_xml(box.hlink)
@test xml["a:hlinkClick"]["rId"] == "rId$(PPTX.rid(s3))"
@testset "duplicate hyperlink relations" begin
relationship_map = PPTX.slide_relationship_map(s2)
@test length(relationship_map) == 1
@test relationship_map[box2.hlink] == 2
end

# I malificently swap slides which should not affect hyperlinking
pres.slides[2] = s3
pres.slides[3] = s2

@testset "update slide nrs" begin
PPTX.update_slide_nrs!(pres)
@test pres.slides[2].slide_nr == 2
@test pres.slides[3].slide_nr == 3
xml_rels = PPTX.relationship_xml(box.hlink)
# Chech that rId is still the same
@test xml_rels["Relationship"][1]["Id"] == "rId$(PPTX.rid(s3))"
@test xml_rels["Relationship"][3]["Target"] == "slide$(PPTX.slide_nr(s3)).xml"
box3 = TextBox(content = "Hyperlinked text", hlink = s4)
push!(s2, box3)

@testset "multiple hyperlink relations" begin
relationship_map = PPTX.slide_relationship_map(s2)
@test length(relationship_map) == 2
@test relationship_map[box.hlink] == 2
@test relationship_map[box2.hlink] == 2
@test relationship_map[box3.hlink] == 3
end

end

2 comments on commit 9d43091

@matthijscox-asml
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

Fixed multiple hyperlinks per slide

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/117808

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.9.1 -m "<description of version>" 9d43091f938c48c86db056f65114cb793beee136
git push origin v0.9.1

Please sign in to comment.