@@ -51,28 +51,92 @@ def insert_multiple_lines(text_array)
5151
5252 # Get text run immediately prior to bookmark node
5353 def get_run_before
54- # at_xpath returns the first match found and preceding-sibling returns siblings in the
55- # order they appear in the document not the order as they appear when moving out from
56- # the starting node
57- if not ( r_nodes = @node . xpath ( "./preceding-sibling::w:r" ) ) . empty?
58- r_node = r_nodes . last
59- Containers ::TextRun . new ( r_node )
60- else
54+ if enclosing_paragraph
55+ # at_xpath returns the first match found and preceding-sibling returns siblings in the
56+ # order they appear in the document not the order as they appear when moving out from
57+ # the starting node
58+ unless ( r_nodes = @node . xpath ( "./preceding-sibling::w:r" ) ) . empty?
59+ return Containers ::TextRun . new ( r_nodes . last )
60+ end
61+
6162 new_r = Containers ::TextRun . create_with ( self )
6263 new_r . insert_before ( self )
63- new_r
64+ return new_r
6465 end
66+
67+ # Block-level bookmark (e.g. Google Docs places bookmarkStart/End directly
68+ # under w:body). Add a run to the preceding paragraph, or to a new
69+ # paragraph before the bookmark, so the run lives inside a w:p.
70+ run_at_paragraph_end ( @node . xpath ( "./preceding-sibling::w:p" ) . last ) ||
71+ run_in_new_paragraph { |paragraph | @node . add_previous_sibling ( paragraph ) }
6572 end
6673
6774 # Get text run immediately after bookmark node
6875 def get_run_after
69- if ( r_node = @node . at_xpath ( "./following-sibling::w:r" ) )
70- Containers ::TextRun . new ( r_node )
71- else
76+ if enclosing_paragraph
77+ if ( r_node = @node . at_xpath ( "./following-sibling::w:r" ) )
78+ return Containers ::TextRun . new ( r_node )
79+ end
80+
7281 new_r = Containers ::TextRun . create_with ( self )
7382 new_r . insert_after ( self )
74- new_r
83+ return new_r
84+ end
85+
86+ # Block-level bookmark: add a run to the following paragraph, or to a new
87+ # paragraph after the bookmark.
88+ run_at_paragraph_start ( @node . at_xpath ( "./following-sibling::w:p" ) ) ||
89+ run_in_new_paragraph { |paragraph | @node . add_next_sibling ( paragraph ) }
90+ end
91+
92+ # Override Element#parent_paragraph so insert_multiple_lines also works for
93+ # block-level bookmarks. For those we fill a fresh paragraph inserted at the
94+ # bookmark, rather than an adjacent existing paragraph, so we neither crash
95+ # (no enclosing paragraph) nor overwrite unrelated content.
96+ def parent_paragraph
97+ return Containers ::Paragraph . new ( enclosing_paragraph ) if enclosing_paragraph
98+
99+ paragraph = Nokogiri ::XML ::Node . new ( "w:p" , @node . document )
100+ @node . add_next_sibling ( paragraph )
101+ Containers ::Paragraph . new ( paragraph )
102+ end
103+
104+ private
105+
106+ # The w:p the bookmark sits inside, or nil when it is block-level.
107+ def enclosing_paragraph
108+ @node . at_xpath ( "./parent::w:p" )
109+ end
110+
111+ # A new run inserted at the start of paragraph_node (after w:pPr if present).
112+ # Returns nil when paragraph_node is nil.
113+ def run_at_paragraph_start ( paragraph_node )
114+ return nil unless paragraph_node
115+
116+ new_r = Containers ::TextRun . create_with ( self )
117+ if ( props = paragraph_node . at_xpath ( "w:pPr" ) )
118+ props . add_next_sibling ( new_r . node )
119+ else
120+ paragraph_node . prepend_child ( new_r . node )
75121 end
122+ new_r
123+ end
124+
125+ # A new run appended to the end of paragraph_node. nil when it is nil.
126+ def run_at_paragraph_end ( paragraph_node )
127+ return nil unless paragraph_node
128+
129+ Containers ::TextRun . create_within ( Containers ::Paragraph . new ( paragraph_node ) )
130+ end
131+
132+ # Create a run wrapped in a fresh w:p; the block positions that paragraph
133+ # relative to the bookmark.
134+ def run_in_new_paragraph
135+ new_r = Containers ::TextRun . create_with ( self )
136+ paragraph = Nokogiri ::XML ::Node . new ( "w:p" , @node . document )
137+ paragraph . add_child ( new_r . node )
138+ yield paragraph
139+ new_r
76140 end
77141 end
78142 end
0 commit comments