docxi 0.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +356 -134
- data/lib/docxi/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: adb4b3e9c80abf2e5842f1f504505b0f5eeb8e35dee0abbfc0d91c898ac54e34
|
|
4
|
+
data.tar.gz: 886b8c529421621a4cbe0d136bd5aa53851d7aa388d8acf282c6d76553c7afe3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7dd88edac3c0d0e768c458c3225f1016a4abfbc1ac52b5f79829c425a494f04a9a156bb74424baa5b361d07e7cb64df5e52cc3fdf2e2a07a13ceaef4a68787fd
|
|
7
|
+
data.tar.gz: a2069394d5976cd29bfe9665b62be84cf29b3d4cd8a02ed117cc192b37215c6c33f0553d9ed08e269786340acb67215622e9abb0546d83fcc50ddd320a346000
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
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
|
+
|
|
3
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).
|
|
@@ -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")`
|
|
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
|
-
|
|
1
|
+
# docxi: create Word (.docx) documents with Ruby
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/docxi)
|
|
4
|
+
[](https://github.com/hammad417/docxi/actions/workflows/ci.yml)
|
|
5
|
+
[](https://rubygems.org/gems/docxi)
|
|
6
|
+
[](https://rubygems.org/gems/docxi)
|
|
7
|
+
[](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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
```sh
|
|
104
|
+
gem install docxi
|
|
105
|
+
```
|
|
33
106
|
|
|
34
|
-
|
|
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
|
-
##
|
|
109
|
+
## Quick start
|
|
37
110
|
|
|
38
111
|
```ruby
|
|
39
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
75
|
-
p.text "
|
|
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
|
-
|
|
79
|
-
document.
|
|
80
|
-
|
|
145
|
+
document.p(ul: true) { |p| p.text "First bullet" }
|
|
146
|
+
document.p(ul: true) { |p| p.text "Second bullet" }
|
|
81
147
|
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
tr.tc
|
|
115
|
-
|
|
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
|
|
123
|
-
tr.tc(borders: { top:
|
|
124
|
-
|
|
125
|
-
tr.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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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
|
-
|
|
172
|
-
document.
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
|
187
|
-
|
|
388
|
+
bundle exec rake test
|
|
389
|
+
```
|
|
188
390
|
|
|
189
|
-
|
|
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
|
|
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
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.
|
|
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:
|
|
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:
|
|
176
|
+
summary: Generate Microsoft Word (.docx) documents with Ruby
|
|
174
177
|
test_files: []
|