docxi 0.0.2 → 0.1.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,8 @@ module Docxi
28
28
  xml['w'].tblPr do
29
29
  xml['w'].tblStyle( 'w:val' => options[:style] ) if options[:style]
30
30
  xml['w'].tblW( 'w:w' => options[:width], 'w:type' => "auto" ) if options[:width]
31
+ xml['w'].jc( 'w:val' => options[:align] ) if options[:align]
32
+ xml['w'].tblInd( 'w:w' => options[:iwidth], 'w:type' => "dxa" ) if options[:iwidth]
31
33
  xml['w'].tblLook( 'w:val' => "04A0", 'w:firstRow' => 1, 'w:lastRow' => 0, 'w:firstColumn' => 1, 'w:lastColumn' => 0, 'w:noHBand' => 0, 'w:noVBand' => 1 )
32
34
  if @options[:borders]
33
35
  xml['w'].tblBorders do
@@ -40,6 +42,15 @@ module Docxi
40
42
  end
41
43
  end
42
44
  end
45
+ if @options[:padding]
46
+ xml['w'].tblCellMar do
47
+ xml['w'].top( 'w:w' => "288", 'w:type' => "dxa" )
48
+ xml['w'].left( 'w:w' => "573", 'w:type' => "dxa" )
49
+ xml['w'].bottom( 'w:w' => "288", 'w:type' => "dxa" )
50
+ xml['w'].right( 'w:w' => "573", 'w:type' => "dxa" )
51
+ end
52
+ end
53
+
43
54
  end
44
55
  xml['w'].tblGrid do
45
56
  if options[:columns_width]
@@ -81,6 +92,12 @@ module Docxi
81
92
 
82
93
  def render(xml)
83
94
  xml['w'].tr do
95
+ if Docxi::Util.present?(options[:height])
96
+ xml['w'].trPr do
97
+ xml['w'].trHeight('w:val' => options[:height], 'w:hRule' => 'atLeast')
98
+ xml['w'].cantSplit('w:val' => 'false')
99
+ end
100
+ end
84
101
  @cells.each do |cell|
85
102
  cell.render(xml)
86
103
  end
@@ -102,6 +119,9 @@ module Docxi
102
119
  end
103
120
 
104
121
  def render(xml)
122
+ if Docxi::Util.blank?(options[:fill])
123
+ options[:fill] = 'FFFFFF'
124
+ end
105
125
  xml['w'].tc do
106
126
  xml['w'].tcPr do
107
127
  xml['w'].tcW( 'w:w' => ( options[:width].to_i * 14.92 ).to_i, 'w:type' => "dxa" ) if options[:width]
@@ -150,6 +170,18 @@ module Docxi
150
170
  img
151
171
  end
152
172
 
173
+ def page_numbers
174
+ numbers = PageNumbers.new
175
+ @content << numbers
176
+ numbers
177
+ end
178
+
179
+ def p(options={}, &block)
180
+ element = Docxi::Word::Contents::Paragraph.new(options, &block)
181
+ @content << element
182
+ element
183
+ end
184
+
153
185
  class Image
154
186
  attr_accessor :media, :options
155
187
  def initialize(media, options={})
@@ -215,6 +247,54 @@ module Docxi
215
247
  end
216
248
  end
217
249
  end
250
+
251
+ class PageNumbers
252
+
253
+ attr_accessor :options
254
+ def initialize(options={})
255
+ @options = options
256
+ end
257
+
258
+ def render(xml)
259
+ xml['w'].sdt do
260
+ xml['w'].sdtPr do
261
+ xml['w'].id( 'w:val' => "-472213903" )
262
+ xml['w'].docPartObj do
263
+ xml['w'].docPartGallery( 'w:val' => "Page Numbers (Bottom of Page)" )
264
+ xml['w'].docPartUnique
265
+ end
266
+ end
267
+ xml['w'].sdtContent do
268
+ xml['w'].p do
269
+ xml['w'].pPr do
270
+ xml['w'].jc( 'w:val' => @options[:align] || 'right' )
271
+ end
272
+ xml['w'].r do
273
+ xml['w'].rPr do
274
+ xml['w'].rFonts( 'w:cs'=> 'Arial', 'w:ascii'=> 'Arial', 'w:hAnsi' => 'Arial' )
275
+ xml['w'].color( 'w:val' => '404040')
276
+ xml['w'].sz( 'w:val' => '20' )
277
+ end
278
+ xml['w'].t 'GlobalOptions '
279
+ end
280
+ xml['w'].r do
281
+ xml['w'].fldChar( 'w:fldCharType' => "begin" )
282
+ end
283
+ xml['w'].r do
284
+ xml['w'].instrText "PAGE \* MERGEFORMAT"
285
+ end
286
+ xml['w'].r do
287
+ xml['w'].fldChar( 'w:fldCharType' => "separate" )
288
+ end
289
+ xml['w'].r do
290
+ xml['w'].fldChar( 'w:fldCharType' => "end" )
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end
297
+
218
298
  end
219
299
  end
220
300
  end
@@ -14,13 +14,19 @@ module Docxi
14
14
  if !@text.nil?
15
15
  xml['w'].r do
16
16
  xml['w'].rPr do
17
+ xml['w'].rFonts( 'w:cs'=> @options[:font], 'w:ascii'=> @options[:font], 'w:hAnsi' => @options[:font] ) if @options[:font]
17
18
  xml['w'].b if @options[:bold]
18
19
  xml['w'].i if @options[:italic]
19
20
  xml['w'].u( 'w:val' => "single" ) if options[:underline]
20
21
  xml['w'].color( 'w:val' => @options[:color] ) if @options[:color]
21
22
  xml['w'].sz( 'w:val' => @options[:size].to_i * 2 ) if @options[:size]
23
+ xml['w'].shd( 'w:val' => 'clear','w:fill' => @options[:fill] ) if @options[:fill]
24
+ end
25
+ if @options[:space]
26
+ xml['w'].t( @text, 'xml:space' => "preserve" )
27
+ else
28
+ xml['w'].t @text
22
29
  end
23
- xml['w'].t @text
24
30
  end
25
31
  if options[:br]
26
32
  br = Docxi::Word::Contents::Break.new
@@ -1,4 +1,6 @@
1
1
  require 'docxi/word/contents/break'
2
+ require 'docxi/word/contents/tab'
3
+ require 'docxi/word/contents/frame'
2
4
  require 'docxi/word/contents/text'
3
5
  require 'docxi/word/contents/table_of_content'
4
6
  require 'docxi/word/contents/image'
@@ -1,10 +1,12 @@
1
+ require_relative 'hyperlinks/hyperlink.rb'
2
+ require_relative 'hyperlinks.rb'
1
3
  module Docxi
2
4
  module Word
3
5
  class Document
4
6
 
5
7
  include Helpers
6
8
 
7
- attr_accessor :options, :fonts, :setings, :styles, :numbering, :effects, :web_settings, :themes, :relationships, :footnotes, :endnotes, :footers, :headers
9
+ attr_accessor :options, :fonts, :setings, :styles, :numbering, :effects, :web_settings, :themes, :relationships, :footnotes, :endnotes, :footers, :headers, :hyperlinks
8
10
 
9
11
  def initialize(options)
10
12
  @options = options
@@ -26,6 +28,22 @@ module Docxi
26
28
  @footer = footer
27
29
  end
28
30
 
31
+ def add_first_footer(footer, options={})
32
+ footer.id = footers.sequence
33
+ rel = relationships.add(footer.content_type, footer.target)
34
+ footer.sequence = rel.id
35
+ footers.add(footer)
36
+ @first_footer = footer
37
+ end
38
+
39
+ def add_first_header(header, options={})
40
+ header.id = headers.sequence
41
+ rel = relationships.add(header.content_type, header.target)
42
+ header.sequence = rel.id
43
+ headers.add(header)
44
+ @first_header = header
45
+ end
46
+
29
47
  def add_header(header, options={})
30
48
  header.id = headers.sequence
31
49
  rel = relationships.add(header.content_type, header.target)
@@ -34,6 +52,14 @@ module Docxi
34
52
  @header = header
35
53
  end
36
54
 
55
+ def add_hyperlink(link, options={})
56
+ hyperlink = Hyperlinks::Hyperlink.new(hyperlinks.sequence, options)
57
+ rel = relationships.add(hyperlink.content_type, link, 'External')
58
+ hyperlink.set_sequence(rel.id)
59
+ hyperlinks.add(hyperlink)
60
+ return rel.id
61
+ end
62
+
37
63
  def fonts
38
64
  @fonts ||= Fonts.new
39
65
  end
@@ -82,13 +108,24 @@ module Docxi
82
108
  @footers ||= Footers.new
83
109
  end
84
110
 
111
+ def first_footer
112
+ @first_footer ||= Footers.new
113
+ end
114
+
115
+ def first_header
116
+ @first_header ||= Headers.new
117
+ end
118
+
85
119
  def headers
86
120
  @headers ||= Headers.new
87
121
  end
88
122
 
123
+ def hyperlinks
124
+ @hyperlinks ||= Hyperlinks.new
125
+ end
126
+
89
127
  def render(zip)
90
- zip.put_next_entry('word/document.xml')
91
- zip.write(Docxi.to_xml(document))
128
+ Docxi.write_entry(zip, 'word/document.xml', Docxi.to_xml(document))
92
129
 
93
130
  fonts.render(zip)
94
131
  settings.render(zip)
@@ -108,7 +145,7 @@ module Docxi
108
145
  private
109
146
  def document
110
147
  Nokogiri::XML::Builder.with(Nokogiri::XML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')) do |xml|
111
- xml.document('xmlns:wpc' => "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", 'xmlns:mc' => "http://schemas.openxmlformats.org/markup-compatibility/2006", 'xmlns:o' => "urn:schemas-microsoft-com:office:office", 'xmlns:r' => "http://schemas.openxmlformats.org/officeDocument/2006/relationships", 'xmlns:m' => "http://schemas.openxmlformats.org/officeDocument/2006/math", 'xmlns:v' => "urn:schemas-microsoft-com:vml", 'xmlns:wp14' => "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", 'xmlns:wp' => "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", 'xmlns:w10' => "urn:schemas-microsoft-com:office:word", 'xmlns:w' => "http://schemas.openxmlformats.org/wordprocessingml/2006/main", 'xmlns:w14' => "http://schemas.microsoft.com/office/word/2010/wordml", 'xmlns:wpg' => "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", 'xmlns:wpi' => "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", 'xmlns:wne' => "http://schemas.microsoft.com/office/word/2006/wordml", 'xmlns:wps' => "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", 'mc:Ignorable' => "w14 wp14") do
148
+ xml.document('xmlns:o' => 'urn:schemas-microsoft-com:office:office', 'xmlns:r' => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' ,'xmlns:v' => 'urn:schemas-microsoft-com:vml', 'xmlns:w' => 'http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'xmlns:w10' =>'urn:schemas-microsoft-com:office:word', 'xmlns:wp' =>'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing') do
112
149
  xml.parent.namespace = xml.parent.namespace_definitions.find{|ns| ns.prefix == "w" }
113
150
 
114
151
  xml['w'].body do
@@ -122,11 +159,13 @@ module Docxi
122
159
  xml['w'].bookmarkEnd('w:id' => "0")
123
160
  end
124
161
  xml['w'].sectPr do
125
- xml['w'].footerReference( 'w:type' => "default", 'r:id' => @footer.sequence ) if @footer
126
162
  xml['w'].headerReference( 'w:type' => "default", 'r:id' => @header.sequence ) if @header
163
+ xml['w'].headerReference( 'w:type' => "first", 'r:id' => @first_header.sequence ) if @first_header
164
+ xml['w'].footerReference( 'w:type' => "default", 'r:id' => @footer.sequence ) if @footer
165
+ xml['w'].footerReference( 'w:type' => "first", 'r:id' => @first_footer.sequence ) if @first_footer
127
166
  xml['w'].titlePg
128
167
  xml['w'].pgSz( 'w:w' => 12240, 'w:h' => 15840 )
129
- xml['w'].pgMar( 'w:top' => 800, 'w:right' => 940, 'w:bottom' => 1000, 'w:left' => 940, 'w:header' => 190, 'w:footer' => 200, 'w:gutter' => 0)
168
+ xml['w'].pgMar( 'w:top' => @options[:top], 'w:right' => @options[:right], 'w:bottom' => @options[:bottom], 'w:left' => @options[:left], 'w:header' => Docxi::Util.present?(@options[:header]) ? @options[:header] : 190, 'w:footer' => Docxi::Util.present?(@options[:footer]) ? @options[:footer] : 200 , 'w:gutter' => 0)
130
169
  xml['w'].cols( 'w:space' => 720)
131
170
  xml['w'].docGrid( 'w:linePitch' => "360")
132
171
  end
@@ -8,8 +8,7 @@ module Docxi
8
8
  end
9
9
 
10
10
  def render(zip)
11
- zip.put_next_entry('word/stylesWithEffects.xml')
12
- zip.write(Docxi.to_xml(document))
11
+ Docxi.write_entry(zip, 'word/stylesWithEffects.xml', Docxi.to_xml(document))
13
12
  end
14
13
 
15
14
  private
@@ -8,8 +8,7 @@ module Docxi
8
8
  end
9
9
 
10
10
  def render(zip)
11
- zip.put_next_entry('word/endnotes.xml')
12
- zip.write(Docxi.to_xml(document))
11
+ Docxi.write_entry(zip, 'word/endnotes.xml', Docxi.to_xml(document))
13
12
  end
14
13
 
15
14
  def document
@@ -20,8 +20,7 @@ module Docxi
20
20
  end
21
21
 
22
22
  def render(zip)
23
- zip.put_next_entry('word/fontTable.xml')
24
- zip.write(Docxi.to_xml(document))
23
+ Docxi.write_entry(zip, 'word/fontTable.xml', Docxi.to_xml(document))
25
24
  end
26
25
 
27
26
  private
@@ -18,6 +18,12 @@ module Docxi
18
18
  end
19
19
  end
20
20
 
21
+ def br(options={})
22
+ br = Docxi::Word::Contents::Break.new(options)
23
+ @content << br
24
+ br
25
+ end
26
+
21
27
  def content_type
22
28
  "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
23
29
  end
@@ -27,12 +33,10 @@ module Docxi
27
33
  end
28
34
 
29
35
  def render(zip)
30
- zip.put_next_entry("word/#{target}")
31
- zip.write(Docxi.to_xml(document))
36
+ Docxi.write_entry(zip, "word/#{target}", Docxi.to_xml(document))
32
37
 
33
38
  if !@relationships.empty?
34
- zip.put_next_entry("word/_rels/#{target}.rels")
35
- zip.write(Docxi.to_xml(relationships))
39
+ Docxi.write_entry(zip, "word/_rels/#{target}.rels", Docxi.to_xml(relationships))
36
40
  end
37
41
  end
38
42
 
@@ -45,12 +49,18 @@ module Docxi
45
49
  element
46
50
  end
47
51
 
48
- def page_numbers
49
- numbers = PageNumbers.new
52
+ def page_numbers(options={})
53
+ numbers = PageNumbers.new(options)
50
54
  @content << numbers
51
55
  numbers
52
56
  end
53
57
 
58
+ def table(options={}, &block)
59
+ table = Docxi::Word::Contents::Table.new(options, &block)
60
+ @content << table
61
+ table
62
+ end
63
+
54
64
  class PageNumbers
55
65
 
56
66
  attr_accessor :options
@@ -72,6 +82,14 @@ module Docxi
72
82
  xml['w'].pPr do
73
83
  xml['w'].jc( 'w:val' => @options[:align] || 'right' )
74
84
  end
85
+ xml['w'].r do
86
+ xml['w'].rPr do
87
+ xml['w'].rFonts( 'w:cs'=> 'Arial', 'w:ascii'=> 'Arial', 'w:hAnsi' => 'Arial' )
88
+ xml['w'].color( 'w:val' => '404040')
89
+ xml['w'].sz( 'w:val' => '20' )
90
+ end
91
+ xml['w'].t @options[:name].to_s+' '
92
+ end
75
93
  xml['w'].r do
76
94
  xml['w'].fldChar( 'w:fldCharType' => "begin" )
77
95
  end
@@ -8,8 +8,7 @@ module Docxi
8
8
  end
9
9
 
10
10
  def render(zip)
11
- zip.put_next_entry('word/footnotes.xml')
12
- zip.write(Docxi.to_xml(document))
11
+ Docxi.write_entry(zip, 'word/footnotes.xml', Docxi.to_xml(document))
13
12
  end
14
13
 
15
14
  private
@@ -27,12 +27,10 @@ module Docxi
27
27
  end
28
28
 
29
29
  def render(zip)
30
- zip.put_next_entry("word/#{target}")
31
- zip.write(Docxi.to_xml(document))
30
+ Docxi.write_entry(zip, "word/#{target}", Docxi.to_xml(document))
32
31
 
33
32
  if !@relationships.empty?
34
- zip.put_next_entry("word/_rels/#{target}.rels")
35
- zip.write(Docxi.to_xml(relationships))
33
+ Docxi.write_entry(zip, "word/_rels/#{target}.rels", Docxi.to_xml(relationships))
36
34
  end
37
35
  end
38
36
 
@@ -43,11 +41,38 @@ module Docxi
43
41
  img
44
42
  end
45
43
 
44
+ def text(text, options={})
45
+ options = @options.merge(options)
46
+ element = Docxi::Word::Contents::Paragraph.new(options) do |p|
47
+ p.text(text)
48
+ end
49
+ @content << element
50
+ element
51
+ end
52
+
53
+ def page_numbers(options={})
54
+ numbers = Docxi::Word::Footers::Footer::PageNumbers.new(options)
55
+ @content << numbers
56
+ numbers
57
+ end
58
+
59
+ def br(options={})
60
+ br = Docxi::Word::Contents::Break.new(options)
61
+ @content << br
62
+ br
63
+ end
64
+
65
+ def table(options={}, &block)
66
+ table = Docxi::Word::Contents::Table.new(options, &block)
67
+ @content << table
68
+ table
69
+ end
70
+
46
71
  class Image
47
72
  attr_accessor :media, :options
48
73
  def initialize(media, options={})
49
74
  @media = media
50
- @media.file.rewind
75
+ # @media.file.rewind
51
76
  @options = options
52
77
  end
53
78
 
@@ -69,23 +94,35 @@ module Docxi
69
94
  xml['w'].noProof
70
95
  end
71
96
  xml['w'].drawing do
72
- xml['wp'].inline( 'distT' => 0, 'distB' => 0, 'distL' => 0, 'distR' => 0 ) do
73
- xml['wp'].extent( 'cx' => ( width * 9250 ).to_i, 'cy' => ( height * 9250 ).to_i )
74
- xml['wp'].effectExtent( 'l' => 0, 't' => 0, 'r' => 0, 'b' => 1905 )
97
+ xml['wp'].anchor( "behindDoc" => options[:behind] ,"distT" => 0,"distB" => 0, "distL" => 0, "distR" => 0 ,"simplePos" => 0 ,"locked" => 0, "layoutInCell" => 1, "allowOverlap" => 1, "relativeHeight" => 4) do
98
+ xml['wp'].simplePos('x'=> 0 , 'y' => 0 )
99
+ xml['wp'].positionH("relativeFrom" => "column") do
100
+ xml['wp'].posOffset options[:pH]
101
+ end
102
+ xml['wp'].positionV("relativeFrom" => "paragraph") do
103
+ xml['wp'].posOffset options[:pV]
104
+ end
105
+ xml['wp'].extent( 'cx' => ( options[:width] * options[:height] * 14.92 ).to_i, 'cy' => ( options[:width] * options[:height] * 14.92 ).to_i ) if options[:width] && options[:height]
106
+ xml['wp'].effectExtent( 'l' => 0, 't' => 0, 'r' => 0, 'b' => 0 )
107
+ if options[:wrap] == "none"
108
+ xml['wp'].wrapNone
109
+ else
110
+ xml['wp'].wrapSquare("wrapText"=> "bothSides")
111
+ end
75
112
  xml['wp'].docPr( 'id' => 1, 'name'=> "Image", 'descr' => "image")
76
113
  xml['wp'].cNvGraphicFramePr do
77
- xml.graphicFrameLocks( 'xmlns:a' => "http://schemas.openxmlformats.org/drawingml/2006/main", 'noChangeAspect' => 1 ) do
114
+ xml.graphicFrameLocks( 'xmlns:a' => "http://schemas.openxmlformats.org/drawingml/2006/main", 'noChangeAspect' => "1" ) do
78
115
  xml.parent.namespace = xml.parent.namespace_definitions.find{|ns| ns.prefix == "a" }
79
116
  end
80
117
  end
81
118
  xml.graphic( 'xmlns:a' => "http://schemas.openxmlformats.org/drawingml/2006/main" ) do
82
119
  xml.parent.namespace = xml.parent.namespace_definitions.find{|ns| ns.prefix == "a" }
83
120
  xml['a'].graphicData( 'uri' => "http://schemas.openxmlformats.org/drawingml/2006/picture") do
84
- xml.pic( 'xmlns:pic' => "http://schemas.openxmlformats.org/drawingml/2006/picture" ) do
121
+ xml.pic( 'xmlns:pic' => "http://schemas.openxmlformats.org/drawingml/2006/picture") do
85
122
  xml.parent.namespace = xml.parent.namespace_definitions.find{|ns| ns.prefix == "pic" }
86
123
  xml['pic'].nvPicPr do
87
124
  xml['pic'].cNvPr( 'id' => 0, 'name' => "Image" )
88
- xml['pic'].cNvPicPr
125
+ xml['pic'].cNvPicPr
89
126
  end
90
127
  xml['pic'].blipFill do
91
128
  xml['a'].blip( 'r:embed' => @media.sequence ) do
@@ -95,14 +132,21 @@ module Docxi
95
132
  xml['a'].fillRect
96
133
  end
97
134
  end
98
- xml['pic'].spPr do
135
+ xml['pic'].spPr( 'bwMode' => "auto" ) do
99
136
  xml['a'].xfrm do
100
137
  xml['a'].off( 'x' => 0, 'y' => 0 )
101
- xml['a'].ext( 'cx' => ( width * 50 ).to_i, 'cy' => ( height * 50 ).to_i )
138
+ xml['a'].ext( 'cx' => ( options[:width] * options[:height] * 14.92 ).to_i, 'cy' => ( options[:width] * options[:height] * 14.92).to_i ) if options[:width] && options[:height]
102
139
  end
103
140
  xml['a'].prstGeom( 'prst' => "rect" ) do
104
141
  xml['a'].avLst
105
142
  end
143
+ # xml['a'].noFill
144
+ # xml['a'].ln('w' => "9525") do
145
+ # xml['a'].noFill
146
+ # xml['a'].miter('lim'=>"800000")
147
+ # xml['a'].headEnd
148
+ # xml['a'].tailEnd
149
+ # end
106
150
  end
107
151
  end
108
152
  end
@@ -8,12 +8,24 @@ module Docxi
8
8
  end
9
9
  end
10
10
 
11
+ def frame(text, options={})
12
+ p(options) do |p|
13
+ p.frame(options) { |f| f.text text }
14
+ end
15
+ end
16
+
11
17
  def br(options={})
12
18
  element = Docxi::Word::Contents::Break.new(options)
13
19
  @content << element
14
20
  element
15
21
  end
16
22
 
23
+ def tab(options={})
24
+ element = Docxi::Word::Contents::Tab.new(options)
25
+ @content << element
26
+ element
27
+ end
28
+
17
29
  def p(options={}, &block)
18
30
  element = Docxi::Word::Contents::Paragraph.new(options, &block)
19
31
  @content << element
@@ -0,0 +1,94 @@
1
+ #encoding: utf-8
2
+
3
+ module Docxi
4
+ module Word
5
+ class Hyperlinks
6
+ class Hyperlink
7
+
8
+ attr_accessor :id, :sequence
9
+ def initialize(id,options={})
10
+ @options = options
11
+ @id = id
12
+
13
+ if block_given?
14
+ yield self
15
+ else
16
+
17
+ end
18
+ end
19
+
20
+ def set_sequence(sequence)
21
+ @sequence = sequence
22
+ self
23
+ end
24
+
25
+ def content_type
26
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
27
+ end
28
+
29
+ def target
30
+ "document.xml"
31
+ end
32
+
33
+ def render(zip)
34
+ Docxi.write_entry(zip, "word/#{target}", Docxi.to_xml(document))
35
+ if !@relationships.empty?
36
+ Docxi.write_entry(zip, "word/_rels/#{target}.rels", Docxi.to_xml(relationships))
37
+ end
38
+ end
39
+
40
+ def hyperlink(text,link)
41
+ text = Hyperlink.new(text, link, id)
42
+ @content << text
43
+ end
44
+
45
+ class Hyperlink
46
+ attr_accessor :text, :link
47
+ def initialize(text, link )
48
+ @text = text
49
+ @link = link
50
+ end
51
+
52
+ def content_type
53
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
54
+ end
55
+
56
+ def render(xml)
57
+ xml['w'].hyperlink('w:id' => '1') do
58
+ xml['w'].r do
59
+ xml['w'].pPr do
60
+ xml['w'].rStyle( 'w:val' => "InternetLink")
61
+ end
62
+ xml['w'].t @text
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+
69
+ private
70
+ def document
71
+ Nokogiri::XML::Builder.with(Nokogiri::XML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')) do |xml|
72
+ xml.hdr( 'xmlns:wpc' => "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", 'xmlns:mc' => "http://schemas.openxmlformats.org/markup-compatibility/2006", 'xmlns:o' => "urn:schemas-microsoft-com:office:office", 'xmlns:r' => "http://schemas.openxmlformats.org/officeDocument/2006/relationships", 'xmlns:m' => "http://schemas.openxmlformats.org/officeDocument/2006/math", 'xmlns:v' => "urn:schemas-microsoft-com:vml", 'xmlns:wp14' => "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", 'xmlns:wp' => "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", 'xmlns:w10' => "urn:schemas-microsoft-com:office:word", 'xmlns:w' => "http://schemas.openxmlformats.org/wordprocessingml/2006/main", 'xmlns:w14' => "http://schemas.microsoft.com/office/word/2010/wordml", 'xmlns:wpg' => "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", 'xmlns:wpi' => "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", 'xmlns:wne' => "http://schemas.microsoft.com/office/word/2006/wordml", 'xmlns:wps' => "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", 'mc:Ignorable' => "w14 wp14" ) do
73
+ xml.parent.namespace = xml.parent.namespace_definitions.find{|ns| ns.prefix == "w" }
74
+ @content.each do |element|
75
+ element.render(xml)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def relationships
82
+ Nokogiri::XML::Builder.with(Nokogiri::XML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')) do |xml|
83
+ xml.Relationships(xmlns: 'http://schemas.openxmlformats.org/package/2006/relationships') do
84
+ @relationships.each do |relationship|
85
+ xml.Relationship('Id' => relationship.sequence, 'Type' => relationship.content_type, 'Target' => relationship.target)
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,30 @@
1
+ require 'docxi/word/hyperlinks/hyperlink'
2
+
3
+ module Docxi
4
+ module Word
5
+ class Hyperlinks
6
+
7
+ attr_accessor :hyperlinks, :counter
8
+ def initialize
9
+ @hyperlinks = []
10
+ @counter = 0
11
+ end
12
+
13
+ def add(hyperlink)
14
+ @hyperlinks << hyperlink
15
+ hyperlink
16
+ end
17
+
18
+ def sequence
19
+ @counter += 1
20
+ end
21
+
22
+ def render(zip)
23
+ @hyperlinks.each do |hyperlink|
24
+ hyperlink.render(zip)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -3,6 +3,8 @@ module Docxi
3
3
  class Medias
4
4
  class Media
5
5
 
6
+ URL = %r{\A(?:https?|ftp)://}i
7
+
6
8
  attr_accessor :id, :sequence, :file
7
9
  def initialize(id, file, options)
8
10
  @id = id
@@ -10,6 +12,22 @@ module Docxi
10
12
  self
11
13
  end
12
14
 
15
+ # Strings are URLs or file paths. Kernel#open, and File.binread on
16
+ # Ruby 2.5, run strings starting with "|" as shell commands, and
17
+ # Kernel#open stopped fetching URLs in Ruby 3.
18
+ def read
19
+ if @file.is_a?(String)
20
+ if @file =~ URL
21
+ require 'open-uri'
22
+ URI.parse(@file).open { |io| io.read }
23
+ else
24
+ File.open(@file, 'rb') { |file| file.read }
25
+ end
26
+ else
27
+ @file.read
28
+ end
29
+ end
30
+
13
31
  def set_sequence(sequence)
14
32
  @sequence = sequence
15
33
  self