docxi 0.1.0.rc1 → 0.1.1

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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -2
  3. data/README.md +356 -134
  4. data/lib/docxi/version.rb +1 -1
  5. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92d0e49fe9e1a189dc7d1cb7dccb3c7e7a3f4877f4b4c56f5cfb2cc779a944de
4
- data.tar.gz: 8a0192d020e0471216c6224de46478a7b2ac4a71a04c4a05569f6774ae3c0b76
3
+ metadata.gz: adb4b3e9c80abf2e5842f1f504505b0f5eeb8e35dee0abbfc0d91c898ac54e34
4
+ data.tar.gz: 886b8c529421621a4cbe0d136bd5aa53851d7aa388d8acf282c6d76553c7afe3
5
5
  SHA512:
6
- metadata.gz: 1060898ce0f6fe2a1122a98cce52b2220460c96969ea78ebeb576e32f20fadefed3b8b6bd741c623cdc9ae0d4721189fa649ef7e0d04fbf5767852001ac0392f
7
- data.tar.gz: 266f7794a16fbad300b520f5d49bf70e6fd15407143e8a3bc54f6b8fff91a32ed7358a498a76ddaca98be92df6a87310c55612da45c5ece198130ad64e9fe63f
6
+ metadata.gz: 7dd88edac3c0d0e768c458c3225f1016a4abfbc1ac52b5f79829c425a494f04a9a156bb74424baa5b361d07e7cb64df5e52cc3fdf2e2a07a13ceaef4a68787fd
7
+ data.tar.gz: a2069394d5976cd29bfe9665b62be84cf29b3d4cd8a02ed117cc192b37215c6c33f0553d9ed08e269786340acb67215622e9abb0546d83fcc50ddd320a346000
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0 (unreleased)
3
+ ## 0.1.1 (2026-10-29)
4
+
5
+ No code changes, and the generated documents are the same as 0.1.0's.
6
+
7
+ - The gem's summary and description now say what docxi does. They used to call it a template generator.
8
+ - RubyGems links to the wiki as the gem's documentation.
9
+
10
+ ## 0.1.0 (2026-10-28)
4
11
 
5
12
  docxi installs and runs again in current apps, with or without Rails. No code changes are needed, and the generated documents are the same as 0.0.6's (the tests compare them against 0.0.6's output).
6
13
 
@@ -18,7 +25,7 @@ docxi installs and runs again in current apps, with or without Rails. No code ch
18
25
  - `add_media` with an image URL works on Ruby 3 again.
19
26
  - `add_media` no longer runs a string that starts with `|` as a shell command.
20
27
  - `tab` and `frame` no longer raise `NameError`.
21
- - `document.frame("text")` no longer raises `TypeError`.
28
+ - `document.frame("text")` builds a text box containing the text.
22
29
  - `page_numbers` in a header no longer raises `NameError`.
23
30
 
24
31
  ### Gem
data/README.md CHANGED
@@ -1,194 +1,416 @@
1
- Docxi gem provides ability to create docx documents using Ruby.
1
+ # docxi: create Word (.docx) documents with Ruby
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/docxi)](https://rubygems.org/gems/docxi)
4
+ [![CI](https://github.com/hammad417/docxi/actions/workflows/ci.yml/badge.svg)](https://github.com/hammad417/docxi/actions/workflows/ci.yml)
5
+ [![Downloads](https://img.shields.io/gem/dt/docxi)](https://rubygems.org/gems/docxi)
6
+ [![Ruby >= 2.5](https://img.shields.io/badge/ruby-%3E%3D%202.5-CC342D)](https://rubygems.org/gems/docxi)
7
+ [![License: MIT](https://img.shields.io/github/license/hammad417/docxi)](LICENSE)
8
+
9
+ [English](README.md) · [简体中文](docs/i18n/README.zh-CN.md) · [繁體中文](docs/i18n/README.zh-TW.md) · [Español](docs/i18n/README.es.md) · [العربية](docs/i18n/README.ar.md) · [Français](docs/i18n/README.fr.md) · [Português (Brasil)](docs/i18n/README.pt-BR.md) · [Deutsch](docs/i18n/README.de.md) · [हिन्दी](docs/i18n/README.hi.md) · [日本語](docs/i18n/README.ja.md) · [한국어](docs/i18n/README.ko.md) · [Русский](docs/i18n/README.ru.md) · [Bahasa Indonesia](docs/i18n/README.id.md) · [Türkçe](docs/i18n/README.tr.md) · [Italiano](docs/i18n/README.it.md) · [Tiếng Việt](docs/i18n/README.vi.md)
10
+
11
+ docxi is a Ruby gem that builds Microsoft Word `.docx` files from code. You describe the document with a small block-based DSL (paragraphs, formatted text, tables, images, headers and footers, page numbers, hyperlinks) and docxi writes the Office Open XML package. It doesn't need Microsoft Word, LibreOffice or a template file, and it works in plain Ruby scripts as well as in Ruby on Rails apps.
12
+
13
+ ```ruby
14
+ require "docxi"
15
+
16
+ word = Docxi::Document.new
17
+ word.document.p { |p| p.text "Hello from docxi", bold: true, size: 16 }
18
+ File.binwrite("hello.docx", word.render.string)
19
+ ```
20
+
21
+ ## Contents
22
+
23
+ - [Overview](#overview)
24
+ - [Features](#features)
25
+ - [Technology stack](#technology-stack)
26
+ - [Installation](#installation)
27
+ - [Quick start](#quick-start)
28
+ - [Usage examples](#usage-examples)
29
+ - [Configuration](#configuration)
30
+ - [How it works](#how-it-works)
31
+ - [API at a glance](#api-at-a-glance)
32
+ - [Supported platforms](#supported-platforms)
33
+ - [Common use cases](#common-use-cases)
34
+ - [Troubleshooting](#troubleshooting)
35
+ - [FAQ](#faq)
36
+ - [Documentation](#documentation)
37
+ - [Contributing](#contributing)
38
+ - [Security](#security)
39
+ - [License](#license)
40
+ - [Maintainers](#maintainers)
41
+
42
+ ## Overview
43
+
44
+ Generating a Word document from an application usually means filling in a template, automating an office suite, or converting HTML and hoping the layout survives. docxi takes a different route: you build the document in Ruby, element by element, and get back a `StringIO` holding a complete `.docx` file that you can write to disk, attach to an email or send from a Rails controller.
45
+
46
+ It is aimed at Ruby and Rails developers who need to export reports, letters or other documents in Word format from their own data. The only runtime dependencies are [nokogiri](https://nokogiri.org/), which builds the XML, and [rubyzip](https://github.com/rubyzip/rubyzip), which packages it.
2
47
 
3
48
  ## Features
4
49
 
5
- 1. Paragraphs
6
- 2. Text formatting
7
- 3. Header and Footer (limited)
8
- 4. Tables (limited)
9
- 5. Images
10
- 6. Table of Content
11
- 7. Page Numbering
50
+ - **Paragraphs**: alignment, background shading, left and right indentation, a bottom border, and bullet list items
51
+ - **Text**: bold, italic, underline, color, size, font, background shading, preserved spaces and line breaks
52
+ - **Breaks and tabs**: line breaks, page breaks and tab characters
53
+ - **Tables**: column widths, row height, table indentation, per-cell borders, shading and vertical alignment, and merged cells with `colspan` and `rowspan`
54
+ - **Images**: from an IO such as a `File`, a file path or an `http(s)` URL, placed inline with a CSS-style size such as `120pt`
55
+ - **Headers and footers**: text, tables, images in headers, page numbers, and a separate header and footer for the first page
56
+ - **Table of contents**: rows with dotted (or other) leaders and right-aligned page numbers that you supply
57
+ - **Hyperlinks** to external URLs
58
+ - **Text frames** (text boxes)
59
+ - **Page margins**
60
+ - **Zip64-free output**: archives are written without Zip64 records even with rubyzip 3, which Google Docs and older LibreOffice versions refuse to open
12
61
 
13
- At the moment, the gem supports a limited number of functions, but the development of the gem is still in progress, and perhaps functionality will be expanded in the future. First of all, try to add documentation for the code and tests.
14
- ## NOTE
15
- Use Latest version of Gem. Old Version has bugs.
62
+ ### Limitations and known issues
63
+
64
+ docxi creates new documents only. It can't open or edit an existing `.docx`, fill in a template or run a mail merge. There are no numbered lists, heading styles or automatically generated tables of contents, and the page size is fixed to US Letter in portrait.
65
+
66
+ These issues are known and planned for 0.2.0:
67
+
68
+ - Images placed with `pH`/`pV`, images in headers and images in table cells get their size from `width * height`, so they come out much smaller than expected. Inline images sized with `style:` are not affected.
69
+ - Table-level `borders:` are applied to every cell rather than to the outside of the table. Per-cell `borders:` work.
70
+ - The page-number field code is missing the backslash in `\* MERGEFORMAT`.
71
+ - Page numbers inside table cells are prefixed with the text "GlobalOptions".
72
+ - Images are always stored as PNG parts, and `[Content_Types].xml` only declares the first two headers and footers.
73
+
74
+ ## Technology stack
75
+
76
+ | Part | What it uses |
77
+ |---|---|
78
+ | Language | Ruby 2.5 or newer |
79
+ | XML | [nokogiri](https://nokogiri.org/) `>= 1.8, < 2` |
80
+ | Packaging | [rubyzip](https://github.com/rubyzip/rubyzip) `>= 1.3, < 4` |
81
+ | Output format | Office Open XML WordprocessingML (`.docx`) |
82
+ | Tests | Minitest, with output compared against files generated by the previous release |
83
+ | CI | GitHub Actions on Ubuntu 24.04, plus a LibreOffice conversion check |
16
84
 
17
85
  ## Installation
18
86
 
87
+ ### Prerequisites
88
+
89
+ - Ruby 2.5 or newer
90
+ - Bundler, if you install through a Gemfile
91
+ - A platform where nokogiri installs. It ships precompiled builds for common Linux, macOS and Windows platforms.
92
+
93
+ ### Install
94
+
95
+ Add docxi to your Gemfile:
96
+
19
97
  ```ruby
20
98
  gem "docxi", "~> 0.1"
21
99
  ```
22
100
 
23
- ## Supported versions
24
-
25
- | Dependency | Supported versions |
26
- |---|---|
27
- | Ruby | 2.5 or newer |
28
- | Rails | Not required. Tested alongside Rails 5.2, 6.0, 6.1, 7.0, 7.1, 7.2, 8.0 and 8.1 |
29
- | nokogiri | 1.8 or newer |
30
- | rubyzip | 1.3 or newer, including 3.x |
101
+ and run `bundle install`. Or install it directly:
31
102
 
32
- ### Upgrading from 0.0.x
103
+ ```sh
104
+ gem install docxi
105
+ ```
33
106
 
34
- Run `bundle update docxi`. The API is unchanged and 0.1.0 generates the same documents as 0.0.6. Apps on a Ruby older than 2.5 stay on 0.0.6 automatically.
107
+ Upgrading from 0.0.x needs no code changes: run `bundle update docxi`. 0.1.0 generates the same documents as 0.0.6. Apps on a Ruby older than 2.5 stay on 0.0.6 automatically.
35
108
 
36
- ## Usage
109
+ ## Quick start
37
110
 
38
111
  ```ruby
39
- # default attributes (top: 720,right: 1440,left: 1440,bottom: 720,footer: 200, header: 190)
40
- word = Docxi::Document.new
41
- # or change margins
42
- # word = Docxi::Document.new(top: 0,right: 0,left: 0,bottom: 0)
112
+ require "docxi"
43
113
 
114
+ word = Docxi::Document.new
44
115
  document = word.document
45
116
 
46
- # Creates Header
47
- header = Docxi::Word::Headers::Header.new do |h|
48
- # Inserts image into header
49
- header_logo = document.add_media File.open('logo.png')
50
- h.image header_logo, align: 'right', width: 100, height: 100
117
+ document.p(align: "center") do |p|
118
+ p.text "Monthly report", size: 20, bold: true
51
119
  end
52
120
 
53
- # Adds header
54
- document.add_header(header)
55
-
56
- # Creates Footer
57
- footer = Docxi::Word::Footers::Footer.new do |f|
58
- # Adds footer text
59
- f.text "Footer Text", size: 8, align: 'center'
60
- # Adds page numbers
61
- f.page_numbers
121
+ document.p do |p|
122
+ p.text "Generated with docxi on ", space: true
123
+ p.text Time.now.strftime("%Y-%m-%d"), bold: true
62
124
  end
63
- # Adds footer
64
- document.add_footer(footer)
65
125
 
66
- # Creates paragraph
67
- document.p(size: 12, bold: true, align: "center", color: "000000", fill: "AAAAAA") do |p|
68
- p.text "Title", size: 12, bold: true, align: "center", color: "000000"
69
- # Adds 3 break lines
70
- p.br times: 3
71
- end
126
+ File.binwrite("report.docx", word.render.string)
127
+ ```
128
+
129
+ `word.render` returns a `StringIO`, and its `.string` is the complete `.docx` file.
72
130
 
131
+ ## Usage examples
132
+
133
+ The examples continue from the quick start, using the same `word` and `document`.
134
+
135
+ ### Formatted text and bullet lists
136
+
137
+ ```ruby
73
138
  document.p do |p|
74
- # Adds text with specific styles
75
- p.text "Subtitle", size: 14, color: '568AB2', bold: true
139
+ p.text "Bold", bold: true
140
+ p.text " italic", italic: true, space: true
141
+ p.text " underlined", underline: true, space: true, br: true
142
+ p.text "Dark red Georgia, 14 pt, on yellow", color: "8B0000", font: "Georgia", size: 14, fill: "FFFF00"
76
143
  end
77
144
 
78
- # Adds break page
79
- document.br(page: true)
80
-
145
+ document.p(ul: true) { |p| p.text "First bullet" }
146
+ document.p(ul: true) { |p| p.text "Second bullet" }
81
147
 
82
- # Adds TOC
83
- document.text "Table of Content", size: 18, color: 'D36C8A', bold: true
84
- document.table_of_content(size: 14, bold: true) do |table|
85
- # Adds TOC element
86
- table.add_row("Table", 1)
148
+ document.p(align: "center", fill: "EEEEEE", bottom: true) do |p|
149
+ p.text "Shaded, centered, with a bottom border"
87
150
  end
88
151
 
89
- # Adds break page
90
152
  document.br(page: true)
153
+ ```
91
154
 
92
- # Adds table
93
- document.table( columns_width: [ 316, 160, 135 ] ) do |table|
94
- # Adds row with specific styles
95
- table.tr(fill: "FBD4B4", bold: true, align: 'center' ) do |tr|
96
- # Adds cells
97
- tr.tc(borders: { top: 1, right: nil, bottom: 1, left: 1 }) do |tc|
98
- tc.text "Header 1"
99
- end
100
- tr.tc(borders: { top: 1, right: nil, bottom: 1, left: nil }) do |tc|
101
- tc.text "Header 2"
102
- end
103
- tr.tc(borders: { top: 1, right: 1, bottom: 1, left: nil }) do |tc|
104
- tc.text "Header 3"
105
- end
106
- end
155
+ Use `space: true` on text that starts or ends with a space, otherwise Word drops the space.
107
156
 
108
- # Adds another row
109
- table.tr(align: 'center') do |tr|
110
- # Adds rowspan
111
- tr.tc(rowspan: true, borders: { top: 1, right: 1, bottom: 1, left: 1 }, valign: 'center') do |tc|
112
- tc.text "Rowspan", align: 'left'
113
- end
114
- tr.tc(borders: { top: 1, right: 1, bottom: 1, left: 1 }) do |tc|
115
- tc.text "Cell Value 2"
116
- end
117
- tr.tc(borders: { top: 1, right: 1, bottom: 1, left: 1 }) do |tc|
118
- tc.text "Cell Value 3"
119
- end
157
+ ### Tables
158
+
159
+ ```ruby
160
+ document.table(columns_width: [240, 100, 100]) do |table|
161
+ table.tr(fill: "D9D9D9", bold: true) do |tr|
162
+ tr.tc { |tc| tc.text "Item" }
163
+ tr.tc { |tc| tc.text "Qty" }
164
+ tr.tc { |tc| tc.text "Price" }
120
165
  end
121
166
 
122
- table.tr(fill: "D9D9D9", bold: true, align: 'center' ) do |tr|
123
- tr.tc(borders: { top: 1, right: 1, bottom: 1, left: 1 }, merged: true) # Use with rowspan to mark cell as merged
124
- # Adds colspan
125
- tr.tc(colspan: 2, valign: 'bottom', borders: { top: 1, right: 1, bottom: 1, left: 1 }) do |tc|
126
- tc.text "Colspan"
127
- end
167
+ table.tr do |tr|
168
+ tr.tc(borders: { top: 4, right: 4, bottom: 4, left: 4 }) { |tc| tc.text "Widget" }
169
+ tr.tc(valign: "center") { |tc| tc.text "2" }
170
+ tr.tc { |tc| tc.text "$20.00", align: "right" }
128
171
  end
129
- end
130
172
 
131
- # Adds table with row height and column indentation
132
- document.table( columns_width: [ 316, 160, 135 ], height: 268, iwidth: 108) do |table|
133
- # Adds row with specific styles
134
- table.tr(fill: "FBD4B4", bold: true, align: 'center' ) do |tr|
135
- # Adds cells
136
- tr.tc(borders: { top: 1, right: nil, bottom: 1, left: 1 }) do |tc|
137
- tc.text "Header 1"
138
- end
139
- tr.tc(borders: { top: 1, right: nil, bottom: 1, left: nil }) do |tc|
140
- tc.text "Header 2"
141
- end
142
- tr.tc(borders: { top: 1, right: 1, bottom: 1, left: nil }) do |tc|
143
- tc.text "Header 3"
144
- end
173
+ table.tr do |tr|
174
+ tr.tc(colspan: 2) { |tc| tc.text "Total", bold: true }
175
+ tr.tc { |tc| tc.text "$20.00", align: "right", bold: true }
145
176
  end
146
177
  end
178
+ ```
147
179
 
180
+ Options on `tr` apply to every cell in the row. To merge cells vertically, pass `rowspan: true` to the top cell and `merged: true` to the cell below it.
148
181
 
149
- # Adds image
150
- document.p do |p|
151
- p.text "Image Example", size: 16, bold: true, italic: true, underline: true, br: true
182
+ ### Images
183
+
184
+ ```ruby
185
+ logo = document.add_media(File.open("logo.png", "rb"))
186
+ # add_media also takes a file path or an http(s) URL:
187
+ # logo = document.add_media("https://example.com/logo.png")
152
188
 
153
- # Adds image to container
154
- media = document.add_media File.open('image.png')
155
- # Or add image URL
156
- # media = document.add_media image_url
157
- # Insert media into paragraph
158
- p.image media, style: { width: '200pt', height: '200pt' }
189
+ document.p(align: "center") do |p|
190
+ p.image logo, style: { width: "120pt", height: "60pt" }
191
+ end
192
+ ```
159
193
 
160
- # Add Hyperlink
161
- hyper = document.add_hyperlink("http://google.com")
162
- p.hyperlink "Go to Google", "http://google.com", hyper
194
+ ### Headers, footers and page numbers
163
195
 
196
+ ```ruby
197
+ header = Docxi::Word::Headers::Header.new do |h|
198
+ h.text "ACME Corp. quarterly report", size: 9, align: "right"
164
199
  end
165
200
 
166
- # Add Underline
167
- document.p(bottom: true) do |p|
168
- p.text "Example text"
201
+ footer = Docxi::Word::Footers::Footer.new do |f|
202
+ f.page_numbers name: "Page", align: "center"
169
203
  end
170
204
 
171
- # Set position of Paragraph
172
- document.p(fill: "d7d7d7",align: "center", right: -1000, left: -1000) do |p|
173
- p.text "Example Text 2"
174
- p.br
205
+ document.add_header(header)
206
+ document.add_footer(footer)
207
+
208
+ # The first page always has its own header and footer:
209
+ document.add_first_header(Docxi::Word::Headers::Header.new { |h| h.text "Cover page" })
210
+ document.add_first_footer(Docxi::Word::Footers::Footer.new { |f| f.text "Confidential" })
211
+ ```
212
+
213
+ ### Hyperlinks
214
+
215
+ ```ruby
216
+ url = "https://github.com/hammad417/docxi"
217
+ link = document.add_hyperlink(url)
218
+
219
+ document.p { |p| p.hyperlink "docxi on GitHub", url, link }
220
+ ```
221
+
222
+ ### Table of contents
223
+
224
+ ```ruby
225
+ document.text "Contents", size: 16, bold: true
226
+
227
+ document.table_of_content do |toc|
228
+ toc.add_row "Introduction", 1
229
+ toc.add_row "Results", 3
230
+ toc.add_row "Appendix", 7, leader: "hyphen"
175
231
  end
232
+ ```
233
+
234
+ docxi doesn't calculate page numbers. The rows show the numbers you pass in.
235
+
236
+ ### Sending a document from Rails
237
+
238
+ ```ruby
239
+ class ReportsController < ApplicationController
240
+ def show
241
+ word = Docxi::Document.new
242
+ word.document.p { |p| p.text "Report for #{Date.today}" }
243
+
244
+ send_data word.render.string,
245
+ filename: "report.docx",
246
+ type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
247
+ end
248
+ end
249
+ ```
250
+
251
+ ### More examples
252
+
253
+ [`examples/full_document.rb`](examples/full_document.rb) uses every feature in one document, and the wiki's [Usage Examples](https://github.com/hammad417/docxi/wiki/Usage-Examples) page has complete recipes such as a report, an invoice and a letter. In a clone of this repository, `bundle exec rake samples` writes the sample documents from the test suite to `tmp/samples/`.
254
+
255
+ ## Configuration
256
+
257
+ docxi has no configuration files or environment variables. Page margins are set when you create the document:
176
258
 
177
- # Creates Word Document
178
- io = word.render
179
- File.open('doc_1.docx', 'wb'){|f| f.write(io.string) }
259
+ ```ruby
260
+ # Values are in twentieths of a point (1440 = 1 inch). Pass all four sides.
261
+ word = Docxi::Document.new(top: 1440, right: 1080, bottom: 1440, left: 1080, header: 720, footer: 720)
262
+ ```
263
+
264
+ The defaults are `top: 720, right: 1440, bottom: 720, left: 1440, header: 190, footer: 200`. If you set any margin, set `top`, `right`, `bottom` and `left` together, because the ones you leave out are written empty.
265
+
266
+ | Option | Unit |
267
+ |---|---|
268
+ | Text `size:` | Points |
269
+ | Margins, `left:` and `right:` paragraph indents, table `iwidth:` | Twentieths of a point (1440 per inch) |
270
+ | Cell `borders:` sizes | Eighths of a point |
271
+ | `columns_width:` and cell `width:` | Multiplied by 14.92 to get twentieths of a point, which is close to pixels |
272
+ | Image `style:` | CSS lengths such as `"120pt"` |
273
+
274
+ The page is US Letter in portrait, and the default font is Arial 12 pt.
275
+
276
+ ## How it works
277
+
278
+ ```mermaid
279
+ flowchart LR
280
+ A["Your Ruby code"] --> B["Docxi::Document"]
281
+ B --> C["Word::Document<br/>paragraphs, tables, images,<br/>headers, footers"]
282
+ C --> D["nokogiri<br/>WordprocessingML XML"]
283
+ D --> E["rubyzip<br/>.docx package"]
284
+ E --> F["StringIO"]
180
285
  ```
181
286
 
182
- ## Development
287
+ `Docxi::Document` holds the package-level parts: relationships, content types and document properties. `word.document` is a `Docxi::Word::Document`, which collects the elements you add (`Docxi::Word::Contents::Paragraph`, `Table`, `Image`, `TableOfContent` and so on) along with headers, footers, images, hyperlinks, styles, numbering and the theme. When you call `render`, each part builds its XML with nokogiri and is written into a zip archive with rubyzip. `Docxi.write_entry` sets each entry's size up front so rubyzip doesn't add Zip64 records.
288
+
289
+ ## API at a glance
290
+
291
+ | Call | Adds |
292
+ |---|---|
293
+ | `Docxi::Document.new(margins)` | A new document. `#render` returns the `.docx` as a `StringIO` |
294
+ | `document.p(options)` with a block | A paragraph. Inside the block: `text`, `br`, `tab`, `image`, `hyperlink`, `frame` |
295
+ | `document.text(string, options)` | A paragraph with one run of text |
296
+ | `document.br(page: true)` | A page break |
297
+ | `document.table(options)` with a block | A table. Inside: `tr`, then `tc`, then `text`, `p`, `image` or `page_numbers` |
298
+ | `document.table_of_content(options)` with a block | Table of contents rows added with `add_row(text, page)` |
299
+ | `document.add_media(file_path_or_url)` | An image to place with `image` |
300
+ | `document.add_hyperlink(url)` | A link target to use with `hyperlink` |
301
+ | `document.add_header(header)` and `add_first_header` | Headers built with `Docxi::Word::Headers::Header` |
302
+ | `document.add_footer(footer)` and `add_first_footer` | Footers built with `Docxi::Word::Footers::Footer` |
303
+ | `document.frame(text, options)` | A text box |
304
+
305
+ ## Supported platforms
306
+
307
+ | Area | Support |
308
+ |---|---|
309
+ | Ruby | 2.5 or newer. CI runs Ruby 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4 and 4.0 |
310
+ | Rails | Not required. CI loads Rails 5.2, 6.0, 6.1, 7.0, 7.1, 7.2, 8.0 and 8.1 next to docxi |
311
+ | Dependencies | CI covers rubyzip 1.3, 2.4 and 3.x, and nokogiri 1.11 through 1.19 |
312
+ | Operating systems | CI runs on Ubuntu 24.04, and the tests have also been run on macOS. Windows isn't tested. |
313
+ | Output | `.docx` for Microsoft Word. CI checks that LibreOffice converts the sample documents |
314
+
315
+ ## Common use cases
316
+
317
+ - Exporting reports from a Rails app as Word files
318
+ - Generating letters, certificates or invoices from database records
319
+ - Producing documents in background jobs or command-line scripts, where no office suite is installed
320
+
321
+ ## Troubleshooting
322
+
323
+ **Bundler can't find compatible versions for `nokogiri` when you add docxi to a Rails 6+ app.**
324
+ docxi 0.0.6 pins `nokogiri < 1.7`, and every Rails since 6.0 needs nokogiri 1.8.5 or newer. Use `gem "docxi", "~> 0.1"`.
325
+
326
+ **Bundler can't find compatible versions for `rubyzip`.**
327
+ docxi 0.0.6 pins `rubyzip ~> 0.9.9`, which conflicts with any gem that needs a newer rubyzip. docxi 0.1.0 works with rubyzip 1.3 to 3.x.
328
+
329
+ **`NoMethodError: undefined method 'present?'` outside Rails.**
330
+ docxi 0.0.6 relied on ActiveSupport being loaded. 0.1.0 doesn't.
331
+
332
+ **Google Docs or LibreOffice won't open a file generated with rubyzip 3.**
333
+ docxi 0.0.6 with rubyzip 3 writes Zip64 records into every entry. 0.1.0 writes archives without them.
334
+
335
+ **Image URLs raise `Errno::ENOENT` on Ruby 3.**
336
+ docxi 0.0.6 opened URLs with `Kernel#open`, which no longer fetches URLs on Ruby 3. 0.1.0 downloads them with `URI#open`.
337
+
338
+ **`NameError` when calling `tab`, `frame` or a header's `page_numbers`.**
339
+ These were broken in 0.0.6 and work in 0.1.0.
340
+
341
+ **The first page doesn't show your header or footer.**
342
+ docxi always gives the first page its own header and footer. Set them with `add_first_header` and `add_first_footer`.
343
+
344
+ **A page margin is missing.**
345
+ Pass `top`, `right`, `bottom` and `left` together when you set margins. See [Configuration](#configuration).
346
+
347
+ ## FAQ
348
+
349
+ **Do I need Microsoft Word or LibreOffice installed?**
350
+ No. docxi writes the XML and the zip archive itself.
351
+
352
+ **Does docxi require Rails?**
353
+ No. It works in any Ruby program. CI also runs the tests with Rails 5.2 through 8.1 loaded.
354
+
355
+ **How do I generate a Word document in Ruby on Rails?**
356
+ Build it with docxi in a controller or a background job and send `word.render.string` with `send_data`. See [Sending a document from Rails](#sending-a-document-from-rails).
357
+
358
+ **Can docxi edit an existing .docx or fill in a Word template?**
359
+ No. docxi only creates new documents. For templates with merge fields, use a template-based gem such as sablon.
360
+
361
+ **Is the table of contents generated from headings?**
362
+ No. You add each row and its page number yourself.
363
+
364
+ **Can I change the page size or orientation?**
365
+ Not yet. Pages are US Letter in portrait, and you can only change the margins.
366
+
367
+ **Which image formats work?**
368
+ Images are stored as PNG parts, so PNG files are the safe choice.
369
+
370
+ **Which Ruby and Rails versions are supported?**
371
+ Ruby 2.5 and newer. Rails is optional, and CI tests Rails 5.2 through 8.1.
372
+
373
+ ## Documentation
374
+
375
+ - The [docxi wiki](https://github.com/hammad417/docxi/wiki) covers every option, with more examples and troubleshooting
376
+ - [CHANGELOG.md](CHANGELOG.md) lists the changes in each release
377
+ - [examples/full_document.rb](examples/full_document.rb) is a complete example
378
+ - [docs/i18n](docs/i18n/README.md) tracks the translations of this README
379
+
380
+ ## Contributing
381
+
382
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/hammad417/docxi/issues). To work on docxi:
183
383
 
184
384
  ```sh
385
+ git clone https://github.com/hammad417/docxi.git
386
+ cd docxi
185
387
  bundle install
186
- bundle exec rake test # run the tests
187
- bundle exec rake samples # write tmp/samples/*.docx to open in Word, LibreOffice or Google Docs
388
+ bundle exec rake test
389
+ ```
188
390
 
189
- # run the tests with a specific Rails version loaded
391
+ To run the tests with a specific Rails version loaded, use one of the Gemfiles in `gemfiles/`:
392
+
393
+ ```sh
190
394
  BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile bundle install
191
395
  BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile DOCXI_WITH_RAILS=1 bundle exec rake test
192
396
  ```
193
397
 
194
- `test/golden` holds the XML that 0.0.6 generated for the sample documents in `test/support/sample_documents.rb`, and the tests check every release against it. After an intentional change to the generated XML, update it with `bundle exec rake golden` and review the diff.
398
+ `test/golden` holds the XML that 0.0.6 generated for the documents in `test/support/sample_documents.rb`, and the tests compare every change against it. If you change the generated XML on purpose, update it with `bundle exec rake golden` and explain the change in your pull request.
399
+
400
+ ## Security
401
+
402
+ Please don't report security problems in public issues. Email the maintainer instead. The addresses are in [docxi.gemspec](docxi.gemspec).
403
+
404
+ If you still use docxi 0.0.x, upgrade to 0.1.0. Older versions pass image path strings to `Kernel#open`, which runs strings that start with `|` as shell commands.
405
+
406
+ ## License
407
+
408
+ docxi is released under the [MIT License](LICENSE). The license file keeps the original copyright notice from docxer, the gem docxi was built from.
409
+
410
+ ## Maintainers
411
+
412
+ docxi is maintained by [Hammad Maqbool](https://github.com/hammad417) at [Phaedra Solutions](https://www.phaedrasolutions.com/). It started in 2016 from [docxer](https://github.com/gtimti/docxer) by Timur Gabdrakipov.
413
+
414
+ ### About Phaedra Solutions
415
+
416
+ Phaedra Solutions is an AI-first software engineering company that has been building custom software for businesses since 2013.
data/lib/docxi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Docxi
2
- VERSION = "0.1.0.rc1"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docxi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.rc1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hammad Maqbool
@@ -89,7 +89,9 @@ dependencies:
89
89
  - - "<"
90
90
  - !ruby/object:Gem::Version
91
91
  version: '14'
92
- description: Create docx template using Ruby
92
+ description: 'docxi builds .docx files from Ruby code: paragraphs, formatted text,
93
+ tables, images, headers and footers, page numbers, hyperlinks and a table of contents.
94
+ No Word, LibreOffice or template file needed, and it works with or without Rails.'
93
95
  email:
94
96
  - hammad.maqbool@phaedrasolutions.com
95
97
  - hammad417@gmail.com
@@ -153,6 +155,7 @@ metadata:
153
155
  source_code_uri: https://github.com/hammad417/docxi
154
156
  changelog_uri: https://github.com/hammad417/docxi/blob/master/CHANGELOG.md
155
157
  bug_tracker_uri: https://github.com/hammad417/docxi/issues
158
+ documentation_uri: https://github.com/hammad417/docxi/wiki
156
159
  rubygems_mfa_required: 'true'
157
160
  rdoc_options: []
158
161
  require_paths:
@@ -170,5 +173,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
173
  requirements: []
171
174
  rubygems_version: 4.0.20
172
175
  specification_version: 4
173
- summary: Docx Template Generator
176
+ summary: Generate Microsoft Word (.docx) documents with Ruby
174
177
  test_files: []