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 +4 -4
- data/README.md +140 -2
- data/lib/notare/builder.rb +53 -13
- data/lib/notare/document.rb +26 -3
- data/lib/notare/nodes/break.rb +18 -0
- data/lib/notare/nodes/hyperlink.rb +20 -0
- data/lib/notare/nodes/list_item.rb +3 -2
- data/lib/notare/nodes/run.rb +6 -2
- data/lib/notare/package.rb +8 -2
- data/lib/notare/style.rb +20 -5
- data/lib/notare/version.rb +1 -1
- data/lib/notare/xml/document_xml.rb +35 -2
- data/lib/notare/xml/numbering.rb +24 -11
- data/lib/notare/xml/relationships.rb +13 -1
- data/lib/notare/xml/styles_xml.rb +2 -0
- data/lib/notare.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 97f9b8f11f3cbda0479fa7a05f428505475c572d46c8d0dfbad65af11740c3d5
|
|
4
|
+
data.tar.gz: 81a6c3ce45d3f3e8dece12d1c48f8bb3345760fdb6b8488ef5b9481860190367
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 |
|
data/lib/notare/builder.rb
CHANGED
|
@@ -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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
data/lib/notare/document.rb
CHANGED
|
@@ -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 =
|
|
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,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)
|
data/lib/notare/nodes/run.rb
CHANGED
|
@@ -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,
|
|
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
|
data/lib/notare/package.rb
CHANGED
|
@@ -29,13 +29,17 @@ module Notare
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
31
|
def lists?
|
|
32
|
-
@document.
|
|
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(
|
|
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,
|
|
11
|
-
|
|
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
|
data/lib/notare/version.rb
CHANGED
|
@@ -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" =>
|
|
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")
|
data/lib/notare/xml/numbering.rb
CHANGED
|
@@ -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
|
-
|
|
32
|
-
xml["w"].
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
xml["w"].
|
|
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
|
|
50
|
-
type == :bullet
|
|
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
|
|
54
|
-
type == :bullet
|
|
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
|
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.
|
|
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-
|
|
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
|