docpdf 0.1.4 → 0.1.6
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 +15 -0
- data/README.md +48 -4
- data/lib/docpdf/adapters/converters/hexapdf.rb +9 -0
- data/lib/docpdf/adapters/converters/prawn.rb +24 -2
- data/lib/docpdf/adapters/stampers/combine_pdf.rb +12 -1
- data/lib/docpdf/adapters/stampers/hexapdf.rb +11 -1
- data/lib/docpdf/configuration.rb +4 -0
- data/lib/docpdf/converter_resolver.rb +34 -4
- data/lib/docpdf/font_files.rb +35 -0
- data/lib/docpdf/prawn_errors.rb +14 -0
- data/lib/docpdf/version.rb +1 -1
- data/lib/docpdf/watermarker.rb +3 -1
- data/lib/docpdf.rb +1 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b68eda5d7355d1d3a227ef71f44d41f95dc7ccbfe7b08af17739e629c9ab26dc
|
|
4
|
+
data.tar.gz: 23e0ab911eb89df49eeac3634b50ef6d8bf8f7b74a8861067e2a0e44f3946e82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 14aff5944d6ac087f241b078c557e480974f322ca2cbcf3c9d08136e6b37f6598ca1bbd17e14267b060632e83e54491dbb5dc6a42b15768eb64883bbe3fd7838
|
|
7
|
+
data.tar.gz: cb43492ce6e3b7257e3c6cc01281904c509dcb473f77e932361ae986f0f39cda9af3bc0a4080b2e8c7d24e2c13adf6d4a8b93230ed455eef6b6ed74ede98e301
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.6] - 2026-09-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `font_file` registers a TrueType font for text conversion (`config.text_options`) and text watermarks (`config.watermark_options`, or per stamp). PDF's built-in fonts are limited to Windows-1252, so Cyrillic, Greek and other non-Latin text previously raised `ConversionError` with no way around it. Takes a path for the regular weight or a hash of `normal`/`bold`/`italic`/`bold_italic`, and is supported by both text converters and both stampers.
|
|
7
|
+
|
|
8
|
+
## [0.1.5] - 2026-09-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `config.converter` selects a preferred converter adapter, mirroring `config.stamper`. It only claims the mime types that adapter registered for, so everything else still resolves in registration order and `config.converter = :hexapdf` does not disturb Word or image conversion.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Watermarking a structurally valid PDF with an empty page tree raised `NoMethodError` instead of a `DocPDF::ConversionError`. Both stampers and the per-page watermark path now raise `ConversionError` with a clear message.
|
|
15
|
+
- Plain text containing U+2028 LINE SEPARATOR or U+2029 PARAGRAPH SEPARATOR crashed the Prawn converter, since Windows-1252 has no room for them. Both are now translated to a newline, which is what they mean, so text pasted out of word processors and web pages converts cleanly.
|
|
16
|
+
- Adapters no longer leak third-party exceptions to callers rescuing `DocPDF::Error`. The Prawn converter wraps every `Prawn::Errors` class plus `Encoding::UndefinedConversionError`, and both stampers wrap a missing watermark image file.
|
|
17
|
+
|
|
3
18
|
## [0.1.4] - 2026-04-08
|
|
4
19
|
|
|
5
20
|
### Fixed
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
Convert documents from any common format to PDF, with optional watermarking. Zero hard dependencies; bring your own PDF library.
|
|
10
10
|
|
|
11
11
|
[](https://github.com/velocity-labs/docpdf/actions/workflows/ci.yml)
|
|
12
|
-
[](https://rubygems.org/gems/docpdf)
|
|
13
13
|
|
|
14
14
|
<br clear="left">
|
|
15
15
|
|
|
@@ -197,12 +197,18 @@ DocPDF.configure do |config|
|
|
|
197
197
|
# Stamper adapter for watermarking (default: nil, auto-detects hexapdf then combine_pdf)
|
|
198
198
|
config.stamper = :hexapdf # or :combine_pdf
|
|
199
199
|
|
|
200
|
+
# Preferred converter adapter (default: nil, uses registration order)
|
|
201
|
+
# Only applies to the MIME types that adapter registered for; everything else
|
|
202
|
+
# still resolves normally, so this will not affect Word or image conversion.
|
|
203
|
+
config.converter = :hexapdf # or :prawn, :rmagick, :mini_magick, ...
|
|
204
|
+
|
|
200
205
|
# Page size (default: "LETTER")
|
|
201
206
|
config.page_size = "A4"
|
|
202
207
|
|
|
203
208
|
# Plain text file conversion defaults
|
|
204
209
|
config.text_options = {
|
|
205
210
|
font: "Helvetica", # default: "Courier"
|
|
211
|
+
font_file: nil, # default: nil (see "Non-Latin text" below)
|
|
206
212
|
font_size: 12, # default: 10
|
|
207
213
|
margins: [72, 72, 72, 72], # default: [50, 50, 50, 50] (points: top, right, bottom, left)
|
|
208
214
|
color: "000000", # default: "333333" (hex)
|
|
@@ -211,6 +217,7 @@ DocPDF.configure do |config|
|
|
|
211
217
|
# Text watermark defaults (per-stamp options override these)
|
|
212
218
|
config.watermark_options = {
|
|
213
219
|
font: "Times", # default: "Helvetica"
|
|
220
|
+
font_file: nil, # default: nil (see "Non-Latin text" below)
|
|
214
221
|
font_size: 96, # default: 72
|
|
215
222
|
color: "FF0000", # default: "AAAAAA" (hex)
|
|
216
223
|
rotation: 30, # default: 45 (degrees counter-clockwise)
|
|
@@ -220,12 +227,49 @@ end
|
|
|
220
227
|
|
|
221
228
|
Converter adapters (for format-to-PDF conversion) are auto-detected based on MIME type and gem availability. The first available adapter wins, in registration order:
|
|
222
229
|
|
|
223
|
-
- **text/plain**: Prawn, then HexaPDF
|
|
230
|
+
- **text/plain**: Prawn, then HexaPDF (set `config.converter` to reverse this)
|
|
224
231
|
- **image/\***: RMagick, then MiniMagick
|
|
225
232
|
- **Office formats**: LibreOffice (always available if installed)
|
|
226
233
|
- **application/pdf**: Passthrough (returned unchanged)
|
|
227
234
|
- **Unknown formats**: Fallback (tries LibreOffice, then returns raw data)
|
|
228
235
|
|
|
236
|
+
### Non-Latin text
|
|
237
|
+
|
|
238
|
+
PDF's built-in fonts only cover the Windows-1252 character set, so text outside it
|
|
239
|
+
(Cyrillic, Greek, CJK, emoji, and many accented forms) cannot be rendered and raises
|
|
240
|
+
`DocPDF::ConversionError`. Point `font_file` at a TrueType font to lift that limit:
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
DocPDF.configure do |config|
|
|
244
|
+
config.text_options = config.text_options.merge(
|
|
245
|
+
font: "DejaVuSans",
|
|
246
|
+
font_file: "/path/to/DejaVuSans.ttf"
|
|
247
|
+
)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
DocPDF.convert("resume.txt") # renders Cyrillic, Greek, and more
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`font_file` takes a path for the regular weight, or a hash to register several styles:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
font_file: {
|
|
257
|
+
normal: "/fonts/DejaVuSans.ttf",
|
|
258
|
+
bold: "/fonts/DejaVuSans-Bold.ttf",
|
|
259
|
+
italic: "/fonts/DejaVuSans-Oblique.ttf",
|
|
260
|
+
bold_italic: "/fonts/DejaVuSans-BoldOblique.ttf"
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The same option works for text watermarks, via `config.watermark_options` or per stamp:
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
DocPDF.watermark("doc.pdf", { text: "ЧЕРНОВИК", font: "DejaVuSans", font_file: "/fonts/DejaVuSans.ttf" })
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
A font only renders the characters it actually contains. DejaVu covers Latin, Cyrillic
|
|
271
|
+
and Greek but not CJK, so pick a font that covers the scripts you expect.
|
|
272
|
+
|
|
229
273
|
## Adapters
|
|
230
274
|
|
|
231
275
|
DocPDF has two types of adapters:
|
|
@@ -235,8 +279,8 @@ DocPDF has two types of adapters:
|
|
|
235
279
|
| Adapter | Gem | Formats |
|
|
236
280
|
|---------|-----|---------|
|
|
237
281
|
| Soffice | None (system) | Word, Excel, PowerPoint, ODF, CSV, HTML, RTF |
|
|
238
|
-
| Prawn | `prawn` | Plain text |
|
|
239
|
-
| HexaPDF | `hexapdf` | Plain text |
|
|
282
|
+
| Prawn | `prawn` | Plain text (wraps long lines; built-in fonts are Windows-1252 only) |
|
|
283
|
+
| HexaPDF | `hexapdf` | Plain text (wider character support; does not wrap long lines) |
|
|
240
284
|
| RMagick | `rmagick` | JPEG, PNG, HEIC, WebP |
|
|
241
285
|
| MiniMagick | `mini_magick` | JPEG, PNG, HEIC, WebP |
|
|
242
286
|
| Passthrough | None | PDF (returned unchanged) |
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "hexapdf"
|
|
2
2
|
require_relative "base"
|
|
3
|
+
require_relative "../../font_files"
|
|
3
4
|
|
|
4
5
|
module DocPDF
|
|
5
6
|
module Adapters
|
|
@@ -22,6 +23,7 @@ module DocPDF
|
|
|
22
23
|
page_size = normalize_page_size(config.page_size)
|
|
23
24
|
|
|
24
25
|
doc = HexaPDF::Document.new
|
|
26
|
+
register_font(doc, opts[:font], opts[:font_file])
|
|
25
27
|
page = doc.pages.add(page_size)
|
|
26
28
|
canvas = page.canvas
|
|
27
29
|
|
|
@@ -51,6 +53,13 @@ module DocPDF
|
|
|
51
53
|
|
|
52
54
|
private
|
|
53
55
|
|
|
56
|
+
def register_font(doc, name, font_file)
|
|
57
|
+
map = FontFiles.for_hexapdf(font_file)
|
|
58
|
+
return unless map
|
|
59
|
+
|
|
60
|
+
doc.config["font.map"] = (doc.config["font.map"] || {}).merge(name => map)
|
|
61
|
+
end
|
|
62
|
+
|
|
54
63
|
def normalize_page_size(size)
|
|
55
64
|
return size if size.is_a?(Symbol)
|
|
56
65
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
require "prawn"
|
|
2
2
|
require_relative "base"
|
|
3
|
+
require_relative "../../prawn_errors"
|
|
4
|
+
require_relative "../../font_files"
|
|
3
5
|
|
|
4
6
|
module DocPDF
|
|
5
7
|
module Adapters
|
|
@@ -7,18 +9,38 @@ module DocPDF
|
|
|
7
9
|
class Prawn < Base
|
|
8
10
|
MIME_TYPES = %w[text/plain].freeze
|
|
9
11
|
|
|
12
|
+
# U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR mean "break the
|
|
13
|
+
# line", but Windows-1252 has no room for them, so Prawn's built-in fonts
|
|
14
|
+
# reject them. Text pasted out of word processors and web pages carries
|
|
15
|
+
# them routinely, so translate to a newline instead of failing on it.
|
|
16
|
+
LINE_SEPARATORS = /[
]/
|
|
17
|
+
|
|
10
18
|
class << self
|
|
11
19
|
def convert(data, _source_filename)
|
|
12
20
|
config = DocPDF.configuration
|
|
13
21
|
opts = config.text_options
|
|
14
|
-
content = data
|
|
22
|
+
content = normalize(data)
|
|
15
23
|
pdf = ::Prawn::Document.new(page_size: config.page_size, margin: opts[:margins])
|
|
24
|
+
register_font(pdf, opts[:font], opts[:font_file])
|
|
16
25
|
pdf.font(opts[:font], size: opts[:font_size])
|
|
17
26
|
pdf.text content, color: opts[:color]
|
|
18
27
|
pdf.render
|
|
19
|
-
rescue
|
|
28
|
+
rescue *PRAWN_ERRORS, ::Encoding::UndefinedConversionError => e
|
|
20
29
|
raise ConversionError, "Prawn failed to render text to PDF: #{e.message}"
|
|
21
30
|
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def register_font(pdf, name, font_file)
|
|
35
|
+
families = FontFiles.for_prawn(font_file)
|
|
36
|
+
return unless families
|
|
37
|
+
|
|
38
|
+
pdf.font_families.update(name => families)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def normalize(data)
|
|
42
|
+
data.dup.force_encoding("UTF-8").gsub(LINE_SEPARATORS, "\n")
|
|
43
|
+
end
|
|
22
44
|
end
|
|
23
45
|
end
|
|
24
46
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require "combine_pdf"
|
|
2
2
|
require "prawn"
|
|
3
3
|
require_relative "base"
|
|
4
|
+
require_relative "../../prawn_errors"
|
|
5
|
+
require_relative "../../font_files"
|
|
4
6
|
|
|
5
7
|
module DocPDF
|
|
6
8
|
module Adapters
|
|
@@ -10,6 +12,7 @@ module DocPDF
|
|
|
10
12
|
def stamp(data, stamps, page_indices: nil)
|
|
11
13
|
source = ::CombinePDF.parse(data)
|
|
12
14
|
source_page = source.pages.first
|
|
15
|
+
raise ConversionError, "Cannot stamp a PDF with no pages" unless source_page
|
|
13
16
|
page_w = source_page[:MediaBox][2].to_f
|
|
14
17
|
page_h = source_page[:MediaBox][3].to_f
|
|
15
18
|
|
|
@@ -20,7 +23,7 @@ module DocPDF
|
|
|
20
23
|
page << stamp_page if page_indices.nil? || page_indices.include?(idx)
|
|
21
24
|
end
|
|
22
25
|
source.to_pdf
|
|
23
|
-
rescue ::CombinePDF::ParsingError => e
|
|
26
|
+
rescue ::CombinePDF::ParsingError, *PRAWN_ERRORS, Errno::ENOENT => e
|
|
24
27
|
raise ConversionError, "CombinePDF failed to stamp PDF: #{e.message}"
|
|
25
28
|
end
|
|
26
29
|
|
|
@@ -60,6 +63,13 @@ module DocPDF
|
|
|
60
63
|
[img_w, img_h]
|
|
61
64
|
end
|
|
62
65
|
|
|
66
|
+
def register_font(pdf, name, font_file)
|
|
67
|
+
families = FontFiles.for_prawn(font_file)
|
|
68
|
+
return unless families
|
|
69
|
+
|
|
70
|
+
pdf.font_families.update(name => families)
|
|
71
|
+
end
|
|
72
|
+
|
|
63
73
|
def render_image_stamp(pdf, stamp)
|
|
64
74
|
page_w = pdf.bounds.width
|
|
65
75
|
page_h = pdf.bounds.height
|
|
@@ -86,6 +96,7 @@ module DocPDF
|
|
|
86
96
|
page_w = pdf.bounds.width
|
|
87
97
|
page_h = pdf.bounds.height
|
|
88
98
|
|
|
99
|
+
register_font(pdf, stamp[:font], stamp[:font_file])
|
|
89
100
|
pdf.font(stamp[:font])
|
|
90
101
|
font_size = fit_font_size(stamp[:font_size], stamp[:text], pdf, page_w, page_h)
|
|
91
102
|
text_w = pdf.width_of(stamp[:text], size: font_size)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "hexapdf"
|
|
2
2
|
require_relative "base"
|
|
3
|
+
require_relative "../../font_files"
|
|
3
4
|
|
|
4
5
|
module DocPDF
|
|
5
6
|
module Adapters
|
|
@@ -9,6 +10,7 @@ module DocPDF
|
|
|
9
10
|
def stamp(data, stamps, page_indices: nil)
|
|
10
11
|
doc = HexaPDF::Document.new(io: StringIO.new(data))
|
|
11
12
|
source_page = doc.pages[0]
|
|
13
|
+
raise ConversionError, "Cannot stamp a PDF with no pages" unless source_page
|
|
12
14
|
page_w = source_page.box.width
|
|
13
15
|
page_h = source_page.box.height
|
|
14
16
|
|
|
@@ -21,7 +23,7 @@ module DocPDF
|
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
write_to_string(doc)
|
|
24
|
-
rescue HexaPDF::Error => e
|
|
26
|
+
rescue HexaPDF::Error, Errno::ENOENT => e
|
|
25
27
|
raise ConversionError, "HexaPDF failed to stamp PDF: #{e.message}"
|
|
26
28
|
end
|
|
27
29
|
|
|
@@ -87,6 +89,7 @@ module DocPDF
|
|
|
87
89
|
page_w = page.box.width
|
|
88
90
|
page_h = page.box.height
|
|
89
91
|
|
|
92
|
+
register_font(doc, stamp[:font], stamp[:font_file])
|
|
90
93
|
font = doc.fonts.add(stamp[:font])
|
|
91
94
|
glyph_units = font.decode_utf8(stamp[:text]).sum { |g| g.width }
|
|
92
95
|
font_size = fit_font_size(stamp[:font_size], glyph_units, page_w, page_h)
|
|
@@ -115,6 +118,13 @@ module DocPDF
|
|
|
115
118
|
end
|
|
116
119
|
end
|
|
117
120
|
|
|
121
|
+
def register_font(doc, name, font_file)
|
|
122
|
+
map = FontFiles.for_hexapdf(font_file)
|
|
123
|
+
return unless map
|
|
124
|
+
|
|
125
|
+
doc.config["font.map"] = (doc.config["font.map"] || {}).merge(name => map)
|
|
126
|
+
end
|
|
127
|
+
|
|
118
128
|
def write_to_string(doc)
|
|
119
129
|
io = StringIO.new
|
|
120
130
|
doc.write(io)
|
data/lib/docpdf/configuration.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module DocPDF
|
|
2
2
|
class Configuration
|
|
3
3
|
attr_accessor :soffice_path,
|
|
4
|
+
:converter,
|
|
4
5
|
:stamper,
|
|
5
6
|
:page_size,
|
|
6
7
|
:text_options,
|
|
@@ -8,6 +9,7 @@ module DocPDF
|
|
|
8
9
|
|
|
9
10
|
TEXT_DEFAULTS = {
|
|
10
11
|
font: "Courier",
|
|
12
|
+
font_file: nil,
|
|
11
13
|
font_size: 10,
|
|
12
14
|
margins: [50, 50, 50, 50],
|
|
13
15
|
color: "333333",
|
|
@@ -15,6 +17,7 @@ module DocPDF
|
|
|
15
17
|
|
|
16
18
|
WATERMARK_DEFAULTS = {
|
|
17
19
|
font: "Helvetica",
|
|
20
|
+
font_file: nil,
|
|
18
21
|
font_size: 72,
|
|
19
22
|
color: "AAAAAA",
|
|
20
23
|
rotation: 45,
|
|
@@ -22,6 +25,7 @@ module DocPDF
|
|
|
22
25
|
|
|
23
26
|
def initialize
|
|
24
27
|
@soffice_path = "soffice"
|
|
28
|
+
@converter = nil
|
|
25
29
|
@stamper = nil
|
|
26
30
|
@page_size = "LETTER"
|
|
27
31
|
@text_options = TEXT_DEFAULTS.dup
|
|
@@ -8,12 +8,44 @@ module DocPDF
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def resolve(mime_type)
|
|
11
|
+
configured = DocPDF.configuration.converter
|
|
12
|
+
return resolve_configured(configured, mime_type) if configured
|
|
13
|
+
|
|
14
|
+
resolve_registered(mime_type)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def load_adapter(entry)
|
|
20
|
+
require entry[:require_name] if entry[:require_name]
|
|
21
|
+
entry[:loader].call
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# A configured converter only claims the mime types it registered for, so
|
|
25
|
+
# anything else still falls through to the normal registration order. That
|
|
26
|
+
# keeps config.converter = :hexapdf from breaking Word conversion.
|
|
27
|
+
def resolve_configured(name, mime_type)
|
|
28
|
+
entry = @adapters.find { |e| e[:name] == name }
|
|
29
|
+
unless entry
|
|
30
|
+
valid_names = @adapters.map { |e| e[:name].inspect }.join(", ")
|
|
31
|
+
raise AdapterNotFoundError, "Unknown converter: #{name.inspect}. Valid converters are: #{valid_names}."
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return resolve_registered(mime_type) unless entry[:mime_types].include?(mime_type)
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
load_adapter(entry)
|
|
38
|
+
rescue LoadError => e
|
|
39
|
+
raise AdapterNotFoundError, "Converter #{name.inspect} requires gems that are not installed: #{e.message}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def resolve_registered(mime_type)
|
|
11
44
|
missing_gems = []
|
|
12
45
|
|
|
13
46
|
@adapters.each do |entry|
|
|
14
47
|
next unless entry[:mime_types].include?(mime_type)
|
|
15
|
-
|
|
16
|
-
return entry[:loader].call
|
|
48
|
+
return load_adapter(entry)
|
|
17
49
|
rescue LoadError
|
|
18
50
|
missing_gems << entry[:require_name]
|
|
19
51
|
next
|
|
@@ -27,8 +59,6 @@ module DocPDF
|
|
|
27
59
|
resolve_fallback
|
|
28
60
|
end
|
|
29
61
|
|
|
30
|
-
private
|
|
31
|
-
|
|
32
62
|
def resolve_fallback
|
|
33
63
|
entry = @adapters.find { |e| e[:name] == :fallback }
|
|
34
64
|
raise AdapterNotFoundError, "No converter found and no fallback registered." unless entry
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module DocPDF
|
|
2
|
+
# Prawn's built-in AFM fonts only cover Windows-1252, so any text outside it
|
|
3
|
+
# fails to render. Pointing an adapter at a TrueType file lifts that limit.
|
|
4
|
+
# Each backend wants the styles keyed differently, so normalize once here.
|
|
5
|
+
module FontFiles
|
|
6
|
+
STYLES = %i[normal bold italic bold_italic].freeze
|
|
7
|
+
|
|
8
|
+
# HexaPDF calls the unstyled variant :none where Prawn calls it :normal.
|
|
9
|
+
HEXAPDF_STYLES = { normal: :none, bold: :bold, italic: :italic, bold_italic: :bold_italic }.freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Accepts a path for the regular weight, or a hash of styles:
|
|
13
|
+
# "DejaVuSans.ttf"
|
|
14
|
+
# { normal: "...", bold: "...", italic: "...", bold_italic: "..." }
|
|
15
|
+
def normalize(font_file)
|
|
16
|
+
return if font_file.nil?
|
|
17
|
+
return { normal: font_file.to_s } if font_file.is_a?(String) || font_file.is_a?(Pathname)
|
|
18
|
+
|
|
19
|
+
styles = font_file.to_h.transform_keys(&:to_sym).slice(*STYLES)
|
|
20
|
+
styles.empty? ? nil : styles.transform_values(&:to_s)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def for_hexapdf(font_file)
|
|
24
|
+
styles = normalize(font_file)
|
|
25
|
+
return unless styles
|
|
26
|
+
|
|
27
|
+
styles.each_with_object({}) { |(style, path), map| map[HEXAPDF_STYLES.fetch(style)] = path }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def for_prawn(font_file)
|
|
31
|
+
normalize(font_file)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "prawn"
|
|
2
|
+
|
|
3
|
+
module DocPDF
|
|
4
|
+
# Prawn defines every error class directly under StandardError with no shared
|
|
5
|
+
# ancestor, so there is no namespace to rescue. Collecting them lets adapters
|
|
6
|
+
# wrap the whole family in DocPDF::ConversionError rather than leaking Prawn's
|
|
7
|
+
# exceptions to callers who only rescue DocPDF::Error.
|
|
8
|
+
#
|
|
9
|
+
# Only require this from files that already require "prawn"; prawn is optional.
|
|
10
|
+
PRAWN_ERRORS = ::Prawn::Errors.constants
|
|
11
|
+
.map { |name| ::Prawn::Errors.const_get(name) }
|
|
12
|
+
.select { |const| const.is_a?(Class) && const <= StandardError }
|
|
13
|
+
.freeze
|
|
14
|
+
end
|
data/lib/docpdf/version.rb
CHANGED
data/lib/docpdf/watermarker.rb
CHANGED
|
@@ -46,7 +46,7 @@ module DocPDF
|
|
|
46
46
|
merged = STAMP_DEFAULTS.merge(stamp)
|
|
47
47
|
if merged[:text]
|
|
48
48
|
wm = DocPDF.configuration.watermark_options
|
|
49
|
-
{ font: wm[:font], font_size: wm[:font_size], color: wm[:color], rotation: wm[:rotation] }.merge(merged)
|
|
49
|
+
{ font: wm[:font], font_file: wm[:font_file], font_size: wm[:font_size], color: wm[:color], rotation: wm[:rotation] }.merge(merged)
|
|
50
50
|
else
|
|
51
51
|
merged
|
|
52
52
|
end
|
|
@@ -138,6 +138,8 @@ module DocPDF
|
|
|
138
138
|
|
|
139
139
|
def stamp_per_page(stamper)
|
|
140
140
|
page_count = pdf_page_count
|
|
141
|
+
raise ConversionError, "Cannot stamp a PDF with no pages" if page_count.zero?
|
|
142
|
+
|
|
141
143
|
result = @pdf_bytes
|
|
142
144
|
|
|
143
145
|
build_page_stamp_map(page_count).each do |page_indices, stamps_for_pages|
|
data/lib/docpdf.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: docpdf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Velocity Labs, LLC
|
|
@@ -38,8 +38,10 @@ files:
|
|
|
38
38
|
- lib/docpdf/converter.rb
|
|
39
39
|
- lib/docpdf/converter_resolver.rb
|
|
40
40
|
- lib/docpdf/errors.rb
|
|
41
|
+
- lib/docpdf/font_files.rb
|
|
41
42
|
- lib/docpdf/input_normalizer.rb
|
|
42
43
|
- lib/docpdf/mime_detector.rb
|
|
44
|
+
- lib/docpdf/prawn_errors.rb
|
|
43
45
|
- lib/docpdf/result.rb
|
|
44
46
|
- lib/docpdf/stamper_resolver.rb
|
|
45
47
|
- lib/docpdf/version.rb
|