notare 0.0.3 → 0.0.4

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: 97f9b8f11f3cbda0479fa7a05f428505475c572d46c8d0dfbad65af11740c3d5
4
- data.tar.gz: 81a6c3ce45d3f3e8dece12d1c48f8bb3345760fdb6b8488ef5b9481860190367
3
+ metadata.gz: 1dd4e9ab394b198d4a9ddd59eb40fac35c865159e527fe14a90113bba1071fd0
4
+ data.tar.gz: e10d211085e797c80850ab3433b8802270df96bc01cd3d2ac348da6f17c675ec
5
5
  SHA512:
6
- metadata.gz: 470684cd1e7a5ed4ee7169f3e01aa0547feee8ba13fe89c68bc7b556c50235d2c43b27e32dd0f66d97f16460a53a2b088e31d56e1c083a309ead256d14fd6d0d
7
- data.tar.gz: bb58e3d474b6cea285fd548c6c87e9e3bfad55bce889ac4b2edb8cbdcdb9fb5557ff6d280992f29045338350522800b846ba6c99ede4143b14fb7ba45b4bfffd
6
+ metadata.gz: ba2c27ab7d6ad5a6f5b5239f54504e8352a5fee52374685f7e09d61284781ba93e9353a0c75f07a3e047571cc55d598c3764beb3c1d08663e54d11119e515fa5
7
+ data.tar.gz: b42bc57c20a639882a8912b1da7759b2c131443ce840f3d901a1a29a70a86e4f8238ce0b9d38ed688e0332fb99b77f7c5edcebffc611302273ede404c2a72197
data/README.md CHANGED
@@ -105,6 +105,21 @@ Notare includes built-in styles and supports custom style definitions.
105
105
 
106
106
  #### Built-in Styles
107
107
 
108
+ | Style | Properties |
109
+ |-------|------------|
110
+ | `:title` | 26pt, bold, centered |
111
+ | `:subtitle` | 15pt, italic, gray (#666666) |
112
+ | `:quote` | italic, gray (#666666), indented |
113
+ | `:code` | Courier New, 10pt |
114
+ | `:heading1` | 24pt, bold |
115
+ | `:heading2` | 18pt, bold |
116
+ | `:heading3` | 14pt, bold |
117
+ | `:heading4` | 12pt, bold |
118
+ | `:heading5` | 11pt, bold, italic |
119
+ | `:heading6` | 10pt, bold, italic |
120
+
121
+ Note: `h1` through `h6` methods use the corresponding heading styles automatically.
122
+
108
123
  ```ruby
109
124
  Notare::Document.create("output.docx") do |doc|
110
125
  doc.p "This is a title", style: :title
@@ -246,6 +261,77 @@ Notare::Document.create("output.docx") do |doc|
246
261
  end
247
262
  ```
248
263
 
264
+ #### Table Styles
265
+
266
+ Define reusable table styles with borders, shading, cell margins, and alignment:
267
+
268
+ ```ruby
269
+ Notare::Document.create("output.docx") do |doc|
270
+ # Define a custom table style
271
+ doc.define_table_style :fancy,
272
+ borders: { style: "double", color: "0066CC", size: 6 },
273
+ shading: "E6F2FF",
274
+ cell_margins: 100,
275
+ align: :center
276
+
277
+ # Apply the style to a table
278
+ doc.table(style: :fancy) do
279
+ doc.tr do
280
+ doc.td "Product"
281
+ doc.td "Price"
282
+ end
283
+ doc.tr do
284
+ doc.td "Widget"
285
+ doc.td "$10.00"
286
+ end
287
+ end
288
+ end
289
+ ```
290
+
291
+ #### Table Style Properties
292
+
293
+ | Property | Description | Example |
294
+ |----------|-------------|---------|
295
+ | `borders` | Border configuration | `{ style: "single", color: "000000", size: 4 }` |
296
+ | `shading` | Background color (hex) | `"EEEEEE"` |
297
+ | `cell_margins` | Cell padding (twips) | `100` or `{ top: 50, bottom: 50, left: 100, right: 100 }` |
298
+ | `align` | Table alignment | `:left`, `:center`, `:right` |
299
+
300
+ **Border styles:** `single`, `double`, `dotted`, `dashed`, `triple`, `none`
301
+
302
+ **Border configuration options:**
303
+
304
+ ```ruby
305
+ # All borders the same
306
+ borders: { style: "single", color: "000000", size: 4 }
307
+
308
+ # Per-edge borders
309
+ borders: {
310
+ top: { style: "double", color: "FF0000", size: 8 },
311
+ bottom: { style: "single", color: "000000", size: 4 },
312
+ left: { style: "none" },
313
+ right: { style: "none" },
314
+ insideH: { style: "dotted", color: "CCCCCC", size: 2 },
315
+ insideV: { style: "dotted", color: "CCCCCC", size: 2 }
316
+ }
317
+
318
+ # No borders
319
+ borders: :none
320
+ ```
321
+
322
+ #### Built-in Table Styles
323
+
324
+ | Style | Description |
325
+ |-------|-------------|
326
+ | `:grid` | Standard black single-line borders |
327
+ | `:borderless` | No borders |
328
+
329
+ ```ruby
330
+ doc.table(style: :borderless) do
331
+ doc.tr { doc.td "No borders here" }
332
+ end
333
+ ```
334
+
249
335
  ### Images
250
336
 
251
337
  Images can be added to paragraphs, table cells, and list items. Supports PNG and JPEG formats.
@@ -434,12 +520,13 @@ end
434
520
  | `link(url, text)` | Hyperlink with custom text |
435
521
  | `link(url) { }` | Hyperlink with block content |
436
522
  | `define_style(name, **props)` | Define a custom style |
523
+ | `define_table_style(name, **props)` | Define a custom table style |
437
524
  | `ul { }` | Bullet list (can be nested) |
438
525
  | `ol { }` | Numbered list (can be nested) |
439
526
  | `li(text)` | List item with text |
440
527
  | `li { }` | List item with block content |
441
528
  | `li(text) { }` | List item with text and nested content |
442
- | `table { }` | Table |
529
+ | `table(style:) { }` | Table with optional style |
443
530
  | `tr { }` | Table row |
444
531
  | `td(text)` | Table cell with text |
445
532
  | `td { }` | Table cell with block content |
@@ -100,8 +100,8 @@ module Notare
100
100
  @current_list.add_item(item)
101
101
  end
102
102
 
103
- def table(&block)
104
- tbl = Nodes::Table.new
103
+ def table(style: nil, &block)
104
+ tbl = Nodes::Table.new(style: resolve_table_style(style))
105
105
  previous_table = @current_table
106
106
  @current_table = tbl
107
107
  block.call
@@ -198,5 +198,12 @@ module Notare
198
198
 
199
199
  style(style_or_name) || raise(ArgumentError, "Unknown style: #{style_or_name}")
200
200
  end
201
+
202
+ def resolve_table_style(style_or_name)
203
+ return nil if style_or_name.nil?
204
+ return style_or_name if style_or_name.is_a?(TableStyle)
205
+
206
+ table_style(style_or_name) || raise(ArgumentError, "Unknown table style: #{style_or_name}")
207
+ end
201
208
  end
202
209
  end
@@ -4,7 +4,7 @@ module Notare
4
4
  class Document
5
5
  include Builder
6
6
 
7
- attr_reader :nodes, :styles, :hyperlinks
7
+ attr_reader :nodes, :styles, :table_styles, :hyperlinks
8
8
 
9
9
  def self.create(path, &block)
10
10
  doc = new
@@ -25,7 +25,9 @@ module Notare
25
25
  @images = {}
26
26
  @hyperlinks = []
27
27
  @styles = {}
28
+ @table_styles = {}
28
29
  register_built_in_styles
30
+ register_built_in_table_styles
29
31
  end
30
32
 
31
33
  def define_style(name, **properties)
@@ -36,6 +38,14 @@ module Notare
36
38
  @styles[name]
37
39
  end
38
40
 
41
+ def define_table_style(name, **properties)
42
+ @table_styles[name] = TableStyle.new(name, **properties)
43
+ end
44
+
45
+ def table_style(name)
46
+ @table_styles[name]
47
+ end
48
+
39
49
  def save(path)
40
50
  Package.new(self).save(path)
41
51
  end
@@ -104,5 +114,13 @@ module Notare
104
114
  define_style :quote, italic: true, color: "666666", indent: 720
105
115
  define_style :code, font: "Courier New", size: 10
106
116
  end
117
+
118
+ def register_built_in_table_styles
119
+ define_table_style :grid,
120
+ borders: { style: "single", color: "000000", size: 4 }
121
+
122
+ define_table_style :borderless,
123
+ borders: :none
124
+ end
107
125
  end
108
126
  end
@@ -3,11 +3,12 @@
3
3
  module Notare
4
4
  module Nodes
5
5
  class Table < Base
6
- attr_reader :rows
6
+ attr_reader :rows, :style
7
7
 
8
- def initialize
9
- super
8
+ def initialize(style: nil)
9
+ super()
10
10
  @rows = []
11
+ @style = style
11
12
  end
12
13
 
13
14
  def add_row(row)
@@ -59,7 +59,7 @@ module Notare
59
59
  end
60
60
 
61
61
  def styles_xml
62
- Xml::StylesXml.new(@document.styles).to_xml
62
+ Xml::StylesXml.new(@document.styles, @document.table_styles).to_xml
63
63
  end
64
64
 
65
65
  def numbering_xml
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Notare
4
+ class TableStyle
5
+ attr_reader :name, :borders, :shading, :cell_margins, :align
6
+
7
+ BORDER_STYLES = %w[single double dotted dashed triple none nil].freeze
8
+ BORDER_POSITIONS = %i[top bottom left right insideH insideV].freeze
9
+ ALIGNMENTS = %i[left center right].freeze
10
+
11
+ def initialize(name, borders: nil, shading: nil, cell_margins: nil, align: nil)
12
+ @name = name
13
+ @borders = normalize_borders(borders)
14
+ @shading = normalize_color(shading)
15
+ @cell_margins = normalize_cell_margins(cell_margins)
16
+ @align = validate_align(align)
17
+ end
18
+
19
+ def style_id
20
+ name.to_s.split("_").map(&:capitalize).join
21
+ end
22
+
23
+ def display_name
24
+ name.to_s.split("_").map(&:capitalize).join(" ")
25
+ end
26
+
27
+ private
28
+
29
+ def normalize_borders(borders)
30
+ return nil if borders.nil?
31
+ return :none if borders == :none
32
+
33
+ # Check if it's a per-edge configuration
34
+ if borders.keys.any? { |k| BORDER_POSITIONS.include?(k) }
35
+ borders.transform_values { |v| normalize_single_border(v) }
36
+ else
37
+ # Single border config applied to all edges
38
+ normalize_single_border(borders)
39
+ end
40
+ end
41
+
42
+ def normalize_single_border(border)
43
+ return :none if border == :none || border[:style] == "none"
44
+
45
+ style = border[:style] || "single"
46
+ unless BORDER_STYLES.include?(style)
47
+ raise ArgumentError, "Invalid border style: #{style}. Use #{BORDER_STYLES.join(", ")}"
48
+ end
49
+
50
+ {
51
+ style: style,
52
+ color: normalize_color(border[:color]) || "000000",
53
+ size: border[:size] || 4
54
+ }
55
+ end
56
+
57
+ def normalize_color(color)
58
+ return nil if color.nil?
59
+
60
+ hex = color.to_s.sub(/^#/, "").upcase
61
+ return hex if hex.match?(/\A[0-9A-F]{6}\z/)
62
+
63
+ raise ArgumentError, "Invalid color: #{color}. Use 6-digit hex (e.g., 'FF0000')"
64
+ end
65
+
66
+ def normalize_cell_margins(margins)
67
+ return nil if margins.nil?
68
+
69
+ if margins.is_a?(Hash)
70
+ margins.slice(:top, :bottom, :left, :right)
71
+ else
72
+ margins.to_i
73
+ end
74
+ end
75
+
76
+ def validate_align(align)
77
+ return nil if align.nil?
78
+ return align if ALIGNMENTS.include?(align)
79
+
80
+ raise ArgumentError, "Invalid alignment: #{align}. Use #{ALIGNMENTS.join(", ")}"
81
+ end
82
+ end
83
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Notare
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
  end
@@ -169,9 +169,13 @@ module Notare
169
169
  xml["w"].tbl do
170
170
  xml["w"].tblPr do
171
171
  xml["w"].tblW("w:w" => "5000", "w:type" => "pct")
172
- xml["w"].tblBorders do
173
- %w[top left bottom right insideH insideV].each do |border|
174
- xml["w"].send(border, "w:val" => "single", "w:sz" => "4", "w:space" => "0", "w:color" => "000000")
172
+ if table.style
173
+ xml["w"].tblStyle("w:val" => table.style.style_id)
174
+ else
175
+ xml["w"].tblBorders do
176
+ %w[top left bottom right insideH insideV].each do |border|
177
+ xml["w"].send(border, "w:val" => "single", "w:sz" => "4", "w:space" => "0", "w:color" => "000000")
178
+ end
175
179
  end
176
180
  end
177
181
  end
@@ -12,8 +12,15 @@ module Notare
12
12
  justify: "both"
13
13
  }.freeze
14
14
 
15
- def initialize(styles)
15
+ TABLE_ALIGNMENT_MAP = {
16
+ left: "left",
17
+ center: "center",
18
+ right: "right"
19
+ }.freeze
20
+
21
+ def initialize(styles, table_styles = {})
16
22
  @styles = styles
23
+ @table_styles = table_styles
17
24
  end
18
25
 
19
26
  def to_xml
@@ -24,6 +31,10 @@ module Notare
24
31
  @styles.each_value do |style|
25
32
  render_style(xml, style)
26
33
  end
34
+
35
+ @table_styles.each_value do |style|
36
+ render_table_style(xml, style)
37
+ end
27
38
  end
28
39
  end
29
40
  builder.to_xml
@@ -63,6 +74,59 @@ module Notare
63
74
  xml["w"].highlight("w:val" => style.highlight) if style.highlight
64
75
  end
65
76
  end
77
+
78
+ def render_table_style(xml, style)
79
+ xml["w"].style("w:type" => "table", "w:styleId" => style.style_id) do
80
+ xml["w"].name("w:val" => style.display_name)
81
+
82
+ xml["w"].tblPr do
83
+ render_table_borders(xml, style.borders) if style.borders
84
+ render_table_shading(xml, style.shading) if style.shading
85
+ render_table_cell_margins(xml, style.cell_margins) if style.cell_margins
86
+ xml["w"].jc("w:val" => TABLE_ALIGNMENT_MAP[style.align]) if style.align
87
+ end
88
+ end
89
+ end
90
+
91
+ def render_table_borders(xml, borders)
92
+ xml["w"].tblBorders do
93
+ %i[top left bottom right insideH insideV].each do |pos|
94
+ border = borders == :none ? :none : (borders[pos] || borders)
95
+ render_single_border(xml, pos, border)
96
+ end
97
+ end
98
+ end
99
+
100
+ def render_single_border(xml, position, border)
101
+ if border == :none
102
+ xml["w"].send(position, "w:val" => "nil")
103
+ else
104
+ xml["w"].send(position,
105
+ "w:val" => border[:style],
106
+ "w:sz" => border[:size].to_s,
107
+ "w:space" => "0",
108
+ "w:color" => border[:color])
109
+ end
110
+ end
111
+
112
+ def render_table_shading(xml, color)
113
+ xml["w"].shd("w:val" => "clear", "w:color" => "auto", "w:fill" => color)
114
+ end
115
+
116
+ def render_table_cell_margins(xml, margins)
117
+ xml["w"].tblCellMar do
118
+ if margins.is_a?(Hash)
119
+ xml["w"].top("w:w" => margins[:top].to_s, "w:type" => "dxa") if margins[:top]
120
+ xml["w"].left("w:w" => margins[:left].to_s, "w:type" => "dxa") if margins[:left]
121
+ xml["w"].bottom("w:w" => margins[:bottom].to_s, "w:type" => "dxa") if margins[:bottom]
122
+ xml["w"].right("w:w" => margins[:right].to_s, "w:type" => "dxa") if margins[:right]
123
+ else
124
+ %i[top left bottom right].each do |side|
125
+ xml["w"].send(side, "w:w" => margins.to_s, "w:type" => "dxa")
126
+ end
127
+ end
128
+ end
129
+ end
66
130
  end
67
131
  end
68
132
  end
data/lib/notare.rb CHANGED
@@ -16,6 +16,7 @@ require_relative "notare/nodes/table_row"
16
16
  require_relative "notare/nodes/table_cell"
17
17
  require_relative "notare/image_dimensions"
18
18
  require_relative "notare/style"
19
+ require_relative "notare/table_style"
19
20
  require_relative "notare/xml/content_types"
20
21
  require_relative "notare/xml/relationships"
21
22
  require_relative "notare/xml/document_xml"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: notare
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mathias
@@ -133,6 +133,7 @@ files:
133
133
  - lib/notare/nodes/table_row.rb
134
134
  - lib/notare/package.rb
135
135
  - lib/notare/style.rb
136
+ - lib/notare/table_style.rb
136
137
  - lib/notare/version.rb
137
138
  - lib/notare/xml/content_types.rb
138
139
  - lib/notare/xml/document_xml.rb