notare 0.0.2 → 0.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 913db4066f434c9f4fa4dda05b007642c08a08e44aecaaf105d12e77c7ab724e
4
- data.tar.gz: 013f0cb3ce077237d040583a8ea39ce1718b0db7ff5b9b8ff89954e4a6386c3f
3
+ metadata.gz: 97f9b8f11f3cbda0479fa7a05f428505475c572d46c8d0dfbad65af11740c3d5
4
+ data.tar.gz: 81a6c3ce45d3f3e8dece12d1c48f8bb3345760fdb6b8488ef5b9481860190367
5
5
  SHA512:
6
- metadata.gz: b078513721fac08090157247172d5e4ce06cbb0d10770d07169e630a147a9cef688180544c550ec0e441e6cbadbcc810dffcb51029de2c61b0a4c577af80442f
7
- data.tar.gz: 80af9a1ea5e9d585b41d516b3c49418d28f7fe8314549958d420109b24d402f0e7fdd57e9536fa350665e1d5da310a6ed8497425f0b8b291d95fd606f87abdd2
6
+ metadata.gz: 470684cd1e7a5ed4ee7169f3e01aa0547feee8ba13fe89c68bc7b556c50235d2c43b27e32dd0f66d97f16460a53a2b088e31d56e1c083a309ead256d14fd6d0d
7
+ data.tar.gz: bb58e3d474b6cea285fd548c6c87e9e3bfad55bce889ac4b2edb8cbdcdb9fb5557ff6d280992f29045338350522800b846ba6c99ede4143b14fb7ba45b4bfffd
data/README.md CHANGED
@@ -58,6 +58,8 @@ Notare::Document.create("output.docx") do |doc|
58
58
  doc.i { doc.text "italic" }
59
59
  doc.text " and "
60
60
  doc.u { doc.text "underlined" }
61
+ doc.text " and "
62
+ doc.s { doc.text "strikethrough" }
61
63
  end
62
64
 
63
65
  # Nested formatting (bold + italic)
@@ -66,6 +68,13 @@ Notare::Document.create("output.docx") do |doc|
66
68
  doc.i { doc.text "bold and italic" }
67
69
  end
68
70
  end
71
+
72
+ # Show edits (strikethrough old, bold new)
73
+ doc.p do
74
+ doc.s { doc.text "old text" }
75
+ doc.text " "
76
+ doc.b { doc.text "new text" }
77
+ end
69
78
  end
70
79
  ```
71
80
 
@@ -146,10 +155,14 @@ end
146
155
  - `bold: true/false`
147
156
  - `italic: true/false`
148
157
  - `underline: true/false`
158
+ - `strike: true/false` - strikethrough
159
+ - `highlight: "yellow"` - text highlight (see colors below)
149
160
  - `color: "FF0000"` (hex RGB)
150
161
  - `size: 14` (points)
151
162
  - `font: "Arial"` (font family)
152
163
 
164
+ **Highlight colors:** `black`, `blue`, `cyan`, `darkBlue`, `darkCyan`, `darkGray`, `darkGreen`, `darkMagenta`, `darkRed`, `darkYellow`, `green`, `lightGray`, `magenta`, `red`, `white`, `yellow`
165
+
153
166
  **Paragraph properties:**
154
167
  - `align: :left / :center / :right / :justify`
155
168
  - `indent: 720` (twips, 1 inch = 1440 twips)
@@ -182,6 +195,40 @@ Notare::Document.create("output.docx") do |doc|
182
195
  end
183
196
  ```
184
197
 
198
+ #### Nested Lists
199
+
200
+ Lists can be nested inside list items. Mixed types (bullets inside numbered or vice versa) are supported.
201
+
202
+ ```ruby
203
+ Notare::Document.create("output.docx") do |doc|
204
+ doc.ol do
205
+ doc.li "First item"
206
+ doc.li "Second item" do
207
+ doc.ul do
208
+ doc.li "Nested bullet A"
209
+ doc.li "Nested bullet B"
210
+ end
211
+ end
212
+ doc.li "Third item"
213
+ end
214
+
215
+ # Deeply nested
216
+ doc.ul do
217
+ doc.li "Level 0"
218
+ doc.li "Has children" do
219
+ doc.ul do
220
+ doc.li "Level 1"
221
+ doc.li "Goes deeper" do
222
+ doc.ul do
223
+ doc.li "Level 2"
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+ ```
231
+
185
232
  ### Tables
186
233
 
187
234
  ```ruby
@@ -246,6 +293,91 @@ Notare::Document.create("output.docx") do |doc|
246
293
  end
247
294
  ```
248
295
 
296
+ ### Line Breaks
297
+
298
+ Use `br` for soft line breaks within a paragraph (text continues in the same paragraph but on a new line):
299
+
300
+ ```ruby
301
+ Notare::Document.create("output.docx") do |doc|
302
+ doc.p do
303
+ doc.text "Line one"
304
+ doc.br
305
+ doc.text "Line two (same paragraph)"
306
+ doc.br
307
+ doc.text "Line three"
308
+ end
309
+
310
+ # Useful for addresses
311
+ doc.p do
312
+ doc.b { doc.text "Address:" }
313
+ doc.br
314
+ doc.text "123 Main Street"
315
+ doc.br
316
+ doc.text "Anytown, ST 12345"
317
+ end
318
+ end
319
+ ```
320
+
321
+ ### Page Breaks
322
+
323
+ Use `page_break` to force content to start on a new page:
324
+
325
+ ```ruby
326
+ Notare::Document.create("output.docx") do |doc|
327
+ doc.h1 "Chapter 1"
328
+ doc.p "Content of chapter 1..."
329
+
330
+ doc.page_break
331
+
332
+ doc.h1 "Chapter 2"
333
+ doc.p "This starts on a new page."
334
+ end
335
+ ```
336
+
337
+ ### Hyperlinks
338
+
339
+ Add clickable links with `link`:
340
+
341
+ ```ruby
342
+ Notare::Document.create("output.docx") do |doc|
343
+ # Link with custom text
344
+ doc.p do
345
+ doc.text "Visit "
346
+ doc.link "https://example.com", "our website"
347
+ doc.text " for more info."
348
+ end
349
+
350
+ # Link showing the URL as text
351
+ doc.p do
352
+ doc.text "URL: "
353
+ doc.link "https://example.com"
354
+ end
355
+
356
+ # Link with formatted content
357
+ doc.p do
358
+ doc.link "https://github.com" do
359
+ doc.b { doc.text "GitHub" }
360
+ end
361
+ end
362
+
363
+ # Links in lists
364
+ doc.ul do
365
+ doc.li do
366
+ doc.link "https://ruby-lang.org", "Ruby"
367
+ end
368
+ doc.li do
369
+ doc.link "https://rubyonrails.org", "Rails"
370
+ end
371
+ end
372
+
373
+ # Email links
374
+ doc.p do
375
+ doc.text "Contact: "
376
+ doc.link "mailto:hello@example.com", "hello@example.com"
377
+ end
378
+ end
379
+ ```
380
+
249
381
  ### Complete Example
250
382
 
251
383
  ```ruby
@@ -296,11 +428,17 @@ end
296
428
  | `b { }` | Bold formatting |
297
429
  | `i { }` | Italic formatting |
298
430
  | `u { }` | Underline formatting |
431
+ | `s { }` | Strikethrough formatting |
432
+ | `br` | Line break (soft break within paragraph) |
433
+ | `page_break` | Page break (force new page) |
434
+ | `link(url, text)` | Hyperlink with custom text |
435
+ | `link(url) { }` | Hyperlink with block content |
299
436
  | `define_style(name, **props)` | Define a custom style |
300
- | `ul { }` | Bullet list |
301
- | `ol { }` | Numbered list |
437
+ | `ul { }` | Bullet list (can be nested) |
438
+ | `ol { }` | Numbered list (can be nested) |
302
439
  | `li(text)` | List item with text |
303
440
  | `li { }` | List item with block content |
441
+ | `li(text) { }` | List item with text and nested content |
304
442
  | `table { }` | Table |
305
443
  | `tr { }` | Table row |
306
444
  | `td(text)` | Table cell with text |
@@ -60,6 +60,30 @@ module Notare
60
60
  with_format(:underline, &block)
61
61
  end
62
62
 
63
+ def s(&block)
64
+ with_format(:strike, &block)
65
+ end
66
+
67
+ def br
68
+ @current_target.add_run(Nodes::Break.new(type: :line))
69
+ end
70
+
71
+ def page_break
72
+ @nodes << Nodes::Break.new(type: :page)
73
+ end
74
+
75
+ def link(url, text = nil, &block)
76
+ hyperlink = register_hyperlink(url)
77
+ if block
78
+ with_target(hyperlink, &block)
79
+ elsif text
80
+ hyperlink.add_run(Nodes::Run.new(text, underline: true, color: "0000FF"))
81
+ else
82
+ hyperlink.add_run(Nodes::Run.new(url, underline: true, color: "0000FF"))
83
+ end
84
+ @current_target.add_run(hyperlink)
85
+ end
86
+
63
87
  def ul(&block)
64
88
  list(:bullet, &block)
65
89
  end
@@ -69,12 +93,10 @@ module Notare
69
93
  end
70
94
 
71
95
  def li(text = nil, &block)
72
- item = Nodes::ListItem.new([], list_type: @current_list.type, num_id: @current_list.num_id)
73
- if block
74
- with_target(item, &block)
75
- elsif text
76
- item.add_run(Nodes::Run.new(text, **current_formatting))
77
- end
96
+ current_type = @list_type_stack.last
97
+ item = Nodes::ListItem.new([], list_type: current_type, num_id: @current_list.num_id, level: @list_level)
98
+ item.add_run(Nodes::Run.new(text, **current_formatting)) if text
99
+ with_target(item, &block) if block
78
100
  @current_list.add_item(item)
79
101
  end
80
102
 
@@ -110,14 +132,31 @@ module Notare
110
132
 
111
133
  def list(type, &block)
112
134
  @num_id_counter ||= 0
113
- @num_id_counter += 1
135
+ @list_level ||= 0
136
+ @list_type_stack ||= []
114
137
 
115
- list_node = Nodes::List.new(type: type, num_id: @num_id_counter)
116
138
  previous_list = @current_list
117
- @current_list = list_node
118
- block.call
119
- @current_list = previous_list
120
- @nodes << list_node
139
+ nested = !previous_list.nil?
140
+
141
+ if nested
142
+ # Nested list: reuse parent list, push new type, increment level
143
+ @list_level += 1
144
+ @list_type_stack.push(type)
145
+ block.call
146
+ @list_type_stack.pop
147
+ @list_level -= 1
148
+ else
149
+ # Top-level list: new List node
150
+ @num_id_counter += 1
151
+ mark_has_lists!
152
+ list_node = Nodes::List.new(type: type, num_id: @num_id_counter)
153
+ @list_type_stack.push(type)
154
+ @current_list = list_node
155
+ block.call
156
+ @current_list = previous_list
157
+ @list_type_stack.pop
158
+ @nodes << list_node
159
+ end
121
160
  end
122
161
 
123
162
  def with_format(format, &block)
@@ -139,7 +178,8 @@ module Notare
139
178
  {
140
179
  bold: @format_stack.include?(:bold),
141
180
  italic: @format_stack.include?(:italic),
142
- underline: @format_stack.include?(:underline)
181
+ underline: @format_stack.include?(:underline),
182
+ strike: @format_stack.include?(:strike)
143
183
  }
144
184
  end
145
185
 
@@ -4,7 +4,7 @@ module Notare
4
4
  class Document
5
5
  include Builder
6
6
 
7
- attr_reader :nodes, :styles
7
+ attr_reader :nodes, :styles, :hyperlinks
8
8
 
9
9
  def self.create(path, &block)
10
10
  doc = new
@@ -21,7 +21,9 @@ module Notare
21
21
  @current_table = nil
22
22
  @current_row = nil
23
23
  @num_id_counter = 0
24
+ @has_lists = false
24
25
  @images = {}
26
+ @hyperlinks = []
25
27
  @styles = {}
26
28
  register_built_in_styles
27
29
  end
@@ -42,10 +44,25 @@ module Notare
42
44
  @nodes.select { |n| n.is_a?(Nodes::List) }
43
45
  end
44
46
 
47
+ def uses_lists?
48
+ @has_lists
49
+ end
50
+
51
+ def mark_has_lists!
52
+ @has_lists = true
53
+ end
54
+
45
55
  def images
46
56
  @images.values
47
57
  end
48
58
 
59
+ def register_hyperlink(url)
60
+ rid = next_hyperlink_rid
61
+ hyperlink = Nodes::Hyperlink.new(url: url, rid: rid)
62
+ @hyperlinks << hyperlink
63
+ hyperlink
64
+ end
65
+
49
66
  def register_image(path, width: nil, height: nil)
50
67
  return @images[path] if @images[path]
51
68
 
@@ -61,11 +78,17 @@ module Notare
61
78
  def next_image_rid
62
79
  # rId1 = styles.xml (always present)
63
80
  # rId2 = numbering.xml (if lists present)
64
- # rId3+ = images
65
- base = lists.any? ? 3 : 2
81
+ # rId3+ = images, then hyperlinks
82
+ base = @has_lists ? 3 : 2
66
83
  "rId#{base + @images.size}"
67
84
  end
68
85
 
86
+ def next_hyperlink_rid
87
+ # Hyperlinks come after images
88
+ base = @has_lists ? 3 : 2
89
+ "rId#{base + @images.size + @hyperlinks.size}"
90
+ end
91
+
69
92
  def register_built_in_styles
70
93
  # Headings (spacing_before ensures they're rendered as paragraph styles)
71
94
  define_style :heading1, size: 24, bold: true, spacing_before: 240, spacing_after: 120
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notare
4
+ module Nodes
5
+ class Break < Base
6
+ attr_reader :type
7
+
8
+ def initialize(type: :line)
9
+ super()
10
+ @type = type
11
+ end
12
+
13
+ def page?
14
+ type == :page
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notare
4
+ module Nodes
5
+ class Hyperlink < Base
6
+ attr_reader :url, :rid, :runs
7
+
8
+ def initialize(url:, rid:)
9
+ super()
10
+ @url = url
11
+ @rid = rid
12
+ @runs = []
13
+ end
14
+
15
+ def add_run(run)
16
+ @runs << run
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,13 +3,14 @@
3
3
  module Notare
4
4
  module Nodes
5
5
  class ListItem < Base
6
- attr_reader :runs, :list_type, :num_id
6
+ attr_reader :runs, :list_type, :num_id, :level
7
7
 
8
- def initialize(runs = [], list_type:, num_id:)
8
+ def initialize(runs = [], list_type:, num_id:, level: 0)
9
9
  super()
10
10
  @runs = runs
11
11
  @list_type = list_type
12
12
  @num_id = num_id
13
+ @level = level
13
14
  end
14
15
 
15
16
  def add_run(run)
@@ -3,14 +3,18 @@
3
3
  module Notare
4
4
  module Nodes
5
5
  class Run < Base
6
- attr_reader :text, :bold, :italic, :underline, :style
6
+ attr_reader :text, :bold, :italic, :underline, :strike, :highlight, :color, :style
7
7
 
8
- def initialize(text, bold: false, italic: false, underline: false, style: nil)
8
+ def initialize(text, bold: false, italic: false, underline: false,
9
+ strike: false, highlight: nil, color: nil, style: nil)
9
10
  super()
10
11
  @text = text
11
12
  @bold = bold
12
13
  @italic = italic
13
14
  @underline = underline
15
+ @strike = strike
16
+ @highlight = highlight
17
+ @color = color
14
18
  @style = style
15
19
  end
16
20
  end
@@ -29,13 +29,17 @@ module Notare
29
29
  private
30
30
 
31
31
  def lists?
32
- @document.lists.any?
32
+ @document.uses_lists?
33
33
  end
34
34
 
35
35
  def images
36
36
  @document.images
37
37
  end
38
38
 
39
+ def hyperlinks
40
+ @document.hyperlinks
41
+ end
42
+
39
43
  def content_types_xml
40
44
  Xml::ContentTypes.new(has_numbering: lists?, images: images, has_styles: true).to_xml
41
45
  end
@@ -45,7 +49,9 @@ module Notare
45
49
  end
46
50
 
47
51
  def document_relationships_xml
48
- Xml::DocumentRelationships.new(has_numbering: lists?, images: images, has_styles: true).to_xml
52
+ Xml::DocumentRelationships.new(
53
+ has_numbering: lists?, images: images, hyperlinks: hyperlinks, has_styles: true
54
+ ).to_xml
49
55
  end
50
56
 
51
57
  def document_xml
data/lib/notare/style.rb CHANGED
@@ -2,18 +2,24 @@
2
2
 
3
3
  module Notare
4
4
  class Style
5
- attr_reader :name, :bold, :italic, :underline, :color, :size, :font,
5
+ attr_reader :name, :bold, :italic, :underline, :strike, :highlight, :color, :size, :font,
6
6
  :align, :indent, :spacing_before, :spacing_after
7
7
 
8
8
  ALIGNMENTS = %i[left center right justify].freeze
9
+ HIGHLIGHT_COLORS = %w[
10
+ black blue cyan darkBlue darkCyan darkGray darkGreen darkMagenta
11
+ darkRed darkYellow green lightGray magenta red white yellow
12
+ ].freeze
9
13
 
10
- def initialize(name, bold: nil, italic: nil, underline: nil, color: nil,
11
- size: nil, font: nil, align: nil, indent: nil,
12
- spacing_before: nil, spacing_after: nil)
14
+ def initialize(name, bold: nil, italic: nil, underline: nil, strike: nil,
15
+ highlight: nil, color: nil, size: nil, font: nil, align: nil,
16
+ indent: nil, spacing_before: nil, spacing_after: nil)
13
17
  @name = name
14
18
  @bold = bold
15
19
  @italic = italic
16
20
  @underline = underline
21
+ @strike = strike
22
+ @highlight = validate_highlight(highlight)
17
23
  @color = normalize_color(color)
18
24
  @size = size
19
25
  @font = font
@@ -36,7 +42,7 @@ module Notare
36
42
  end
37
43
 
38
44
  def text_properties?
39
- !!(bold || italic || underline || color || size || font)
45
+ !!(bold || italic || underline || strike || highlight || color || size || font)
40
46
  end
41
47
 
42
48
  # Size in half-points for OOXML (14pt = 28 half-points)
@@ -61,5 +67,14 @@ module Notare
61
67
 
62
68
  raise ArgumentError, "Invalid alignment: #{align}. Use #{ALIGNMENTS.join(", ")}"
63
69
  end
70
+
71
+ def validate_highlight(highlight)
72
+ return nil if highlight.nil?
73
+
74
+ color = highlight.to_s
75
+ return color if HIGHLIGHT_COLORS.include?(color)
76
+
77
+ raise ArgumentError, "Invalid highlight color: #{highlight}. Use one of: #{HIGHLIGHT_COLORS.join(", ")}"
78
+ end
64
79
  end
65
80
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Notare
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
  end
@@ -37,6 +37,16 @@ module Notare
37
37
  render_list(xml, node)
38
38
  when Nodes::Table
39
39
  render_table(xml, node)
40
+ when Nodes::Break
41
+ render_page_break(xml, node)
42
+ end
43
+ end
44
+
45
+ def render_page_break(xml, _node)
46
+ xml["w"].p do
47
+ xml["w"].r do
48
+ xml["w"].br("w:type" => "page")
49
+ end
40
50
  end
41
51
  end
42
52
 
@@ -59,7 +69,7 @@ module Notare
59
69
  xml["w"].p do
60
70
  xml["w"].pPr do
61
71
  xml["w"].numPr do
62
- xml["w"].ilvl("w:val" => "0")
72
+ xml["w"].ilvl("w:val" => item.level.to_s)
63
73
  xml["w"].numId("w:val" => item.num_id.to_s)
64
74
  end
65
75
  end
@@ -71,19 +81,42 @@ module Notare
71
81
  case run
72
82
  when Nodes::Image
73
83
  render_image(xml, run)
84
+ when Nodes::Break
85
+ render_break(xml, run)
86
+ when Nodes::Hyperlink
87
+ render_hyperlink(xml, run)
74
88
  when Nodes::Run
75
89
  render_text_run(xml, run)
76
90
  end
77
91
  end
78
92
 
93
+ def render_hyperlink(xml, hyperlink)
94
+ xml["w"].hyperlink("r:id" => hyperlink.rid) do
95
+ hyperlink.runs.each { |run| render_run(xml, run) }
96
+ end
97
+ end
98
+
99
+ def render_break(xml, break_node)
100
+ xml["w"].r do
101
+ if break_node.page?
102
+ xml["w"].br("w:type" => "page")
103
+ else
104
+ xml["w"].br
105
+ end
106
+ end
107
+ end
108
+
79
109
  def render_text_run(xml, run)
80
110
  xml["w"].r do
81
- if run.bold || run.italic || run.underline || run.style
111
+ if run.bold || run.italic || run.underline || run.strike || run.highlight || run.color || run.style
82
112
  xml["w"].rPr do
83
113
  xml["w"].rStyle("w:val" => run.style.style_id) if run.style
84
114
  xml["w"].b if run.bold
85
115
  xml["w"].i if run.italic
86
116
  xml["w"].u("w:val" => "single") if run.underline
117
+ xml["w"].strike if run.strike
118
+ xml["w"].highlight("w:val" => run.highlight) if run.highlight
119
+ xml["w"].color("w:val" => run.color) if run.color
87
120
  end
88
121
  end
89
122
  xml["w"].t(run.text, "xml:space" => "preserve")
@@ -4,6 +4,8 @@ module Notare
4
4
  module Xml
5
5
  class Numbering
6
6
  NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
7
+ BULLET_CHARS = ["•", "○", "■"].freeze
8
+ NUMBER_FORMATS = %w[decimal lowerLetter lowerRoman].freeze
7
9
 
8
10
  def initialize(lists)
9
11
  @lists = lists
@@ -28,13 +30,16 @@ module Notare
28
30
 
29
31
  def render_abstract_num(xml, list)
30
32
  xml["w"].abstractNum("w:abstractNumId" => list.num_id.to_s) do
31
- xml["w"].lvl("w:ilvl" => "0") do
32
- xml["w"].start("w:val" => "1")
33
- xml["w"].numFmt("w:val" => num_format(list.type))
34
- xml["w"].lvlText("w:val" => lvl_text(list.type))
35
- xml["w"].lvlJc("w:val" => "left")
36
- xml["w"].pPr do
37
- xml["w"].ind("w:left" => "720", "w:hanging" => "360")
33
+ 9.times do |level|
34
+ xml["w"].lvl("w:ilvl" => level.to_s) do
35
+ xml["w"].start("w:val" => "1")
36
+ xml["w"].numFmt("w:val" => num_format_for_level(list.type, level))
37
+ xml["w"].lvlText("w:val" => lvl_text_for_level(list.type, level))
38
+ xml["w"].lvlJc("w:val" => "left")
39
+ xml["w"].pPr do
40
+ left = 720 * (level + 1)
41
+ xml["w"].ind("w:left" => left.to_s, "w:hanging" => "360")
42
+ end
38
43
  end
39
44
  end
40
45
  end
@@ -46,12 +51,20 @@ module Notare
46
51
  end
47
52
  end
48
53
 
49
- def num_format(type)
50
- type == :bullet ? "bullet" : "decimal"
54
+ def num_format_for_level(type, level)
55
+ if type == :bullet
56
+ "bullet"
57
+ else
58
+ NUMBER_FORMATS[level % NUMBER_FORMATS.length]
59
+ end
51
60
  end
52
61
 
53
- def lvl_text(type)
54
- type == :bullet ? "•" : "%1."
62
+ def lvl_text_for_level(type, level)
63
+ if type == :bullet
64
+ BULLET_CHARS[level % BULLET_CHARS.length]
65
+ else
66
+ "%#{level + 1}."
67
+ end
55
68
  end
56
69
  end
57
70
  end
@@ -24,10 +24,12 @@ module Notare
24
24
  STYLES_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
25
25
  NUMBERING_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering"
26
26
  IMAGE_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
27
+ HYPERLINK_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
27
28
 
28
- def initialize(has_numbering: false, images: [], has_styles: false)
29
+ def initialize(has_numbering: false, images: [], hyperlinks: [], has_styles: false)
29
30
  @has_numbering = has_numbering
30
31
  @images = images
32
+ @hyperlinks = hyperlinks
31
33
  @has_styles = has_styles
32
34
  end
33
35
 
@@ -60,6 +62,16 @@ module Notare
60
62
  Target: "media/#{image.filename}"
61
63
  )
62
64
  end
65
+
66
+ # Hyperlinks come after images
67
+ @hyperlinks.each do |hyperlink|
68
+ xml.Relationship(
69
+ Id: hyperlink.rid,
70
+ Type: HYPERLINK_TYPE,
71
+ Target: hyperlink.url,
72
+ TargetMode: "External"
73
+ )
74
+ end
63
75
  end
64
76
  end
65
77
  builder.to_xml
@@ -59,6 +59,8 @@ module Notare
59
59
  xml["w"].b if style.bold
60
60
  xml["w"].i if style.italic
61
61
  xml["w"].u("w:val" => "single") if style.underline
62
+ xml["w"].strike if style.strike
63
+ xml["w"].highlight("w:val" => style.highlight) if style.highlight
62
64
  end
63
65
  end
64
66
  end
data/lib/notare.rb CHANGED
@@ -4,6 +4,8 @@ require "nokogiri"
4
4
 
5
5
  require_relative "notare/version"
6
6
  require_relative "notare/nodes/base"
7
+ require_relative "notare/nodes/break"
8
+ require_relative "notare/nodes/hyperlink"
7
9
  require_relative "notare/nodes/run"
8
10
  require_relative "notare/nodes/image"
9
11
  require_relative "notare/nodes/paragraph"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: notare
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mathias
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-12-02 00:00:00.000000000 Z
11
+ date: 2025-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fastimage
@@ -121,6 +121,8 @@ files:
121
121
  - lib/notare/document.rb
122
122
  - lib/notare/image_dimensions.rb
123
123
  - lib/notare/nodes/base.rb
124
+ - lib/notare/nodes/break.rb
125
+ - lib/notare/nodes/hyperlink.rb
124
126
  - lib/notare/nodes/image.rb
125
127
  - lib/notare/nodes/list.rb
126
128
  - lib/notare/nodes/list_item.rb