Skip to content

Commit 60e2470

Browse files
committed
added support for hyperlinks (implemented #70 again)
1 parent 4edc599 commit 60e2470

6 files changed

Lines changed: 50 additions & 3 deletions

File tree

lib/docx/containers/paragraph.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def to_html
5555

5656
# Array of text runs contained within paragraph
5757
def text_runs
58-
@node.xpath('w:r|w:hyperlink/w:r').map { |r_node| Containers::TextRun.new(r_node, @document_properties) }
58+
@node.xpath('w:r|w:hyperlink').map { |r_node| Containers::TextRun.new(r_node, @document_properties) }
5959
end
6060

6161
# Iterate over each text run within a paragraph

lib/docx/containers/text_run.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def self.tag
2323
def initialize(node, document_properties = {})
2424
@node = node
2525
@text_nodes = @node.xpath('w:t').map {|t_node| Elements::Text.new(t_node) }
26+
@text_nodes = @node.xpath('w:t|w:r/w:t').map {|t_node| Elements::Text.new(t_node) }
27+
2628
@properties_tag = 'rPr'
2729
@text = parse_text || ''
2830
@formatting = parse_formatting || DEFAULT_FORMATTING
@@ -74,6 +76,7 @@ def to_html
7476
# No need to be granular with font size down to the span level if it doesn't vary.
7577
styles['font-size'] = "#{font_size}pt" if font_size != @font_size
7678
html = html_tag(:span, content: html, styles: styles) unless styles.empty?
79+
html = html_tag(:a, content: html, attributes: {href: href, target: "_blank"}) if hyperlink?
7780
return html
7881
end
7982

@@ -89,6 +92,18 @@ def underlined?
8992
@formatting[:underline]
9093
end
9194

95+
def hyperlink?
96+
@node.name == 'hyperlink'
97+
end
98+
99+
def href
100+
@document_properties[:hyperlinks][hyperlink_id]
101+
end
102+
103+
def hyperlink_id
104+
@node.attributes['id'].value
105+
end
106+
92107
def font_size
93108
size_tag = @node.xpath('w:rPr//w:sz').first
94109
size_tag ? size_tag.attributes['val'].value.to_i / 2 : @font_size

lib/docx/document.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def initialize(path_or_io, options = {})
4545
# This stores the current global document properties, for now
4646
def document_properties
4747
{
48-
font_size: font_size
48+
font_size: font_size,
49+
hyperlinks: hyperlinks
4950
}
5051
end
5152

@@ -83,6 +84,17 @@ def font_size
8384
size_tag ? size_tag.attributes['val'].value.to_i / 2 : nil
8485
end
8586

87+
# Hyperlink targets are extracted from the document.xml.rels file
88+
def hyperlinks
89+
hyperlink_relationships.each_with_object({}) do |rel, hash|
90+
hash[rel.attributes['Id'].value] = rel.attributes['Target'].value
91+
end
92+
end
93+
94+
def hyperlink_relationships
95+
@rels.xpath("//xmlns:Relationship[contains(@Type,'hyperlink')]")
96+
end
97+
8698
##
8799
# *Deprecated*
88100
#
@@ -157,6 +169,8 @@ def replace_entry(entry_path, file_contents)
157169
def load_styles
158170
@styles_xml = @zip.read('word/styles.xml')
159171
@styles = Nokogiri::XML(@styles_xml)
172+
@rels_xml = @zip.read('word/_rels/document.xml.rels')
173+
@rels = Nokogiri::XML(@rels_xml)
160174
rescue Errno::ENOENT => e
161175
warn e.message
162176
nil

lib/docx/elements/element.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,24 @@ def copy
6565
def html_tag(name, options = {})
6666
content = options[:content]
6767
styles = options[:styles]
68+
attributes = options[:attributes]
6869

6970
html = "<#{name.to_s}"
71+
7072
unless styles.nil? || styles.empty?
7173
styles_array = []
7274
styles.each do |property, value|
7375
styles_array << "#{property.to_s}:#{value};"
7476
end
7577
html << " style=\"#{styles_array.join('')}\""
7678
end
79+
80+
unless attributes.nil? || attributes.empty?
81+
attributes.each do |attr_name, attr_value|
82+
html << " #{attr_name}=\"#{attr_value}\""
83+
end
84+
end
85+
7786
html << ">"
7887
html << content if content
7988
html << "</#{name.to_s}>"

spec/docx/document_spec.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
describe Docx::Document do
88
before(:all) do
99
@fixtures_path = 'spec/fixtures'
10-
@formatting_line_count = 12 # number of lines the formatting.docx file has
10+
@formatting_line_count = 13 # number of lines the formatting.docx file has
1111
end
1212

1313
describe '#open' do
@@ -205,6 +205,8 @@
205205
expect(@doc.paragraphs[8].text).to eq('This paragraph is aligned right.')
206206
expect(@doc.paragraphs[9].text).to eq('This paragraph is 14 points.')
207207
expect(@doc.paragraphs[10].text).to eq('This paragraph has a word at 16 points.')
208+
expect(@doc.paragraphs[11].text).to eq('This sentence has different formatting in different places.')
209+
expect(@doc.paragraphs[12].text).to eq('This sentence has a hyperlink.')
208210
end
209211

210212
it 'should contain a paragraph with multiple text runs' do
@@ -354,6 +356,7 @@
354356
@span_regex = /(\<span).+((?<=\>)\w+)(<\/span>)/
355357
@em_regex = /(\<em).+((?<=\>)\w+)(\<\/em\>)/
356358
@strong_regex = /(\<strong).+((?<=\>)\w+)(\<\/strong\>)/
359+
@anchor_tag_regex = /\<a href="(.+)" target="_blank"\>(.+)\<\/a>/
357360
end
358361

359362
it 'should wrap pragraphs in a p tag' do
@@ -434,6 +437,12 @@
434437
it 'should join paragraphs with newlines' do
435438
expect(@doc.to_html.scan(%(<p style="font-size:11pt;">Normal</p>\n<p style="font-size:11pt;"><em>Italic</em></p>\n<p style="font-size:11pt;"><strong>Bold</strong></p>)).size).to eq 1
436439
end
440+
441+
it 'should convert hyperlinks to anchor tags' do
442+
scan = @doc.to_html.scan(@anchor_tag_regex).flatten
443+
expect(scan[0]).to eq "http://www.google.com/"
444+
expect(scan[1]).to eq "hyperlink"
445+
end
437446
end
438447

439448
describe 'replacing contents' do

spec/fixtures/formatting.docx

-815 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)