Skip to content

Commit 5675b60

Browse files
committed
Finished bookmark insertions
1 parent 5e22f9e commit 5675b60

24 files changed

Lines changed: 136 additions & 18 deletions

lib/docx/containers/container.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ module Docx
44
module Elements
55
module Containers
66
module Container
7-
include Elements::Element
87
# Relation methods
98
# TODO: Create a properties object, include Element
109
def properties

lib/docx/containers/paragraph.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ module Elements
66
module Containers
77
class Paragraph
88
include Container
9-
attr_accessor :text_runs
10-
9+
include Elements::Element
10+
1111
TAG = 'p'
1212

1313
# Child elements: pPr, r, fldSimple, hlink, subDoc
@@ -20,12 +20,14 @@ def initialize(node)
2020
# Handle direct text insertion into paragraph on some conditions
2121
def text=(content)
2222
if text_runs.size == 1
23-
@text_runs.first.text = content
23+
text_runs.first.text = content
2424
elsif text_runs.size == 0
2525
new_r = TextRun.create_within(self)
2626
new_r.text = content
2727
else
28-
nil
28+
text_runs.each {|r| r.node.remove }
29+
new_r = TextRun.create_within(self)
30+
new_r.text = content
2931
end
3032
end
3133

lib/docx/containers/text_run.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module Elements
55
module Containers
66
class TextRun
77
include Container
8+
include Elements::Element
9+
810
DEFAULT_FORMATTING = {
911
italic: false,
1012
bold: false,
@@ -18,18 +20,23 @@ class TextRun
1820

1921
def initialize(node)
2022
@node = node
23+
@text_nodes = @node.xpath('w:t').map {|t_node| Elements::Text.new(t_node) }
2124
@properties_tag = 'rPr'
2225
@text = parse_text || ''
2326
@formatting = parse_formatting || DEFAULT_FORMATTING
2427
end
2528

2629
def text=(content)
27-
@node.content = content
28-
self
30+
if @text_nodes.size == 1
31+
@text_nodes.first.content = content
32+
elsif @text_nodes.empty?
33+
new_t = Elements::Text.create_within(self)
34+
new_t.content = content
35+
end
2936
end
3037

3138
def parse_text
32-
@node.xpath('w:t').map(&:text).join('')
39+
@text_nodes.map(&:content).join('')
3340
end
3441

3542
def parse_formatting

lib/docx/elements.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
require 'docx/elements/bookmark'
2-
require 'docx/elements/element'
2+
require 'docx/elements/element'
3+
require 'docx/elements/text'

lib/docx/elements/bookmark.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,46 @@ class Bookmark
66
include Element
77
attr_accessor :name
88

9+
TAG = 'bookmarkStart'
10+
911
def initialize(node)
1012
@node = node
1113
@name = @node['w:name']
1214
end
15+
16+
def insert_text_before(text)
17+
text_run = get_run_after
18+
text_run.text = "#{text}#{text_run.text}"
19+
end
20+
21+
def insert_text_after(text)
22+
text_run = get_run_before
23+
text_run.text = "#{text_run.text}#{text}"
24+
end
25+
26+
def get_run_before
27+
# at_xpath returns the first match found and preceding-sibling returns siblings in the
28+
# order they appear in the document not the order as they appear when moving out from
29+
# the starting node
30+
if (r_nodes = @node.xpath("./preceding-sibling::w:r"))
31+
r_node = r_nodes.last
32+
Containers::TextRun.new(r_node)
33+
else
34+
new_r = Containers::TextRun.create_with(self)
35+
new_r.insert_before(self)
36+
new_r
37+
end
38+
end
39+
40+
def get_run_after
41+
if (r_node = @node.at_xpath("./following-sibling::w:r"))
42+
Containers::TextRun.new(r_node)
43+
else
44+
new_r = Containers::TextRun.create_with(self)
45+
new_r.insert_after(self)
46+
new_r
47+
end
48+
end
1349
end
1450
end
1551
end

lib/docx/elements/element.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
module Docx
44
module Elements
55
module Element
6+
DEFAULT_TAG = ''
7+
68
def self.included(base)
79
base.extend(ClassMethods)
10+
base.const_set(:TAG, Element::DEFAULT_TAG) unless base.const_defined?(:TAG)
811
end
912

1013
attr_accessor :node
@@ -24,20 +27,24 @@ def paragraph
2427
# Insert node as last child
2528
def append_to(element)
2629
@node = element.node.add_child(@node)
30+
self
2731
end
2832

2933
# Insert node as first child (after properties)
3034
def prepend_to(element)
3135
@node = element.node.properties.add_next_sibling(@node)
36+
self
3237
end
3338

3439
def insert_after(element)
3540
# Returns newly re-parented node
3641
@node = element.node.add_next_sibling(@node)
42+
self
3743
end
3844

3945
def insert_before(element)
4046
@node = element.node.add_previous_sibling(@node)
47+
self
4148
end
4249

4350
# Creation/edit methods
@@ -48,12 +55,13 @@ def copy
4855
module ClassMethods
4956
def create_with(element)
5057
# Need to somehow get the xml document accessible here by default, but this is alright in the interim
51-
self.new(Nokogiri::XML::Node.new("w:#{TAG}"), element.node)
58+
self.new(Nokogiri::XML::Node.new("w:#{self.const_get(:TAG)}", element.node))
5259
end
5360

5461
def create_within(element)
5562
new_element = create_with(element)
5663
new_element.append_to(element)
64+
new_element
5765
end
5866
end
5967
end

lib/docx/elements/text.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Docx
2+
module Elements
3+
class Text
4+
include Element
5+
delegate :content, :content=, :to => :@node
6+
TAG = 't'
7+
8+
def initialize(node)
9+
@node = node
10+
end
11+
end
12+
end
13+
end

lib/docx/parser.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ def paragraphs
2121
end
2222

2323
def bookmarks
24-
@doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node }
24+
bkmrks_hsh = Hash.new
25+
bkmrks_ary = @doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node }
26+
# auto-generated by office 2010
27+
bkmrks_ary.reject! {|b| b.name == "_GoBack" }
28+
bkmrks_ary.each {|b| bkmrks_hsh[b.name] = b }
29+
bkmrks_hsh
2530
end
2631

2732
private

test/docx/test_document.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def test_basic_functionality
1414
end
1515

1616
def test_bookmarks
17-
assert_equal 2, @doc.bookmarks.size
18-
assert_equal 'test_bookmark', @doc.bookmarks.first.name
17+
assert_equal 1, @doc.bookmarks.size
18+
assert @doc.bookmarks['test_bookmark']
1919
end
2020

2121
def test_each_paragraph

test/docx/test_editing.rb

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
require 'docx'
22
require 'test/unit'
33

4-
class SaveTest < Test::Unit::TestCase
4+
class EditTest < Test::Unit::TestCase
55
def setup
6-
@doc = Docx::Document.open('test/fixtures/saving.docx')
6+
@doc = Docx::Document.open('test/fixtures/editing.docx')
77
end
88

9-
def test_copy
9+
def test_copy_paragraph
1010
old_p = @doc.paragraphs.first
1111
new_p = old_p.copy
1212
assert_kind_of Docx::Elements::Containers::Paragraph, new_p
@@ -15,11 +15,34 @@ def test_copy
1515
end
1616

1717
def test_insertion
18-
assert_equal 2, @doc.paragraphs.size
18+
assert_equal 3, @doc.paragraphs.size
1919
first_p = @doc.paragraphs.first
2020
new_p = first_p.copy
2121
new_p.insert_after first_p
22-
assert_equal 3, @doc.paragraphs.size
22+
assert_equal 4, @doc.paragraphs.size
23+
end
24+
25+
def test_paragraph_text
26+
assert_equal 'test text', @doc.paragraphs.first.text
27+
@doc.paragraphs.first.text = 'the real test'
28+
assert_equal 'the real test', @doc.paragraphs.first.text
29+
end
30+
31+
def test_inserting_text_before_bookmark
32+
assert_equal 'test text', @doc.paragraphs.first.text
33+
@doc.bookmarks['beginning_bookmark'].insert_text_before('foo')
34+
assert_equal 'footest text', @doc.paragraphs.first.text
35+
end
36+
37+
def test_inserting_text_after_bookmark
38+
assert_equal 'test text', @doc.paragraphs.first.text
39+
@doc.bookmarks['end_bookmark'].insert_text_after('bar')
40+
assert_equal 'test textbar', @doc.paragraphs.first.text
41+
end
42+
43+
# Insert text intelligently around bookmark
44+
def test_inserting_text_around_bookmark
45+
2346
end
2447

2548
def test_blank

0 commit comments

Comments
 (0)