docpdf 0.1.5 → 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 +5 -0
- data/README.md +40 -1
- data/lib/docpdf/adapters/converters/hexapdf.rb +9 -0
- data/lib/docpdf/adapters/converters/prawn.rb +9 -0
- data/lib/docpdf/adapters/stampers/combine_pdf.rb +9 -0
- data/lib/docpdf/adapters/stampers/hexapdf.rb +9 -0
- data/lib/docpdf/configuration.rb +2 -0
- data/lib/docpdf/font_files.rb +35 -0
- data/lib/docpdf/version.rb +1 -1
- data/lib/docpdf/watermarker.rb +1 -1
- data/lib/docpdf.rb +1 -0
- metadata +2 -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,10 @@
|
|
|
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
|
+
|
|
3
8
|
## [0.1.5] - 2026-09-18
|
|
4
9
|
|
|
5
10
|
### Added
|
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
|
|
|
@@ -208,6 +208,7 @@ DocPDF.configure do |config|
|
|
|
208
208
|
# Plain text file conversion defaults
|
|
209
209
|
config.text_options = {
|
|
210
210
|
font: "Helvetica", # default: "Courier"
|
|
211
|
+
font_file: nil, # default: nil (see "Non-Latin text" below)
|
|
211
212
|
font_size: 12, # default: 10
|
|
212
213
|
margins: [72, 72, 72, 72], # default: [50, 50, 50, 50] (points: top, right, bottom, left)
|
|
213
214
|
color: "000000", # default: "333333" (hex)
|
|
@@ -216,6 +217,7 @@ DocPDF.configure do |config|
|
|
|
216
217
|
# Text watermark defaults (per-stamp options override these)
|
|
217
218
|
config.watermark_options = {
|
|
218
219
|
font: "Times", # default: "Helvetica"
|
|
220
|
+
font_file: nil, # default: nil (see "Non-Latin text" below)
|
|
219
221
|
font_size: 96, # default: 72
|
|
220
222
|
color: "FF0000", # default: "AAAAAA" (hex)
|
|
221
223
|
rotation: 30, # default: 45 (degrees counter-clockwise)
|
|
@@ -231,6 +233,43 @@ Converter adapters (for format-to-PDF conversion) are auto-detected based on MIM
|
|
|
231
233
|
- **application/pdf**: Passthrough (returned unchanged)
|
|
232
234
|
- **Unknown formats**: Fallback (tries LibreOffice, then returns raw data)
|
|
233
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
|
+
|
|
234
273
|
## Adapters
|
|
235
274
|
|
|
236
275
|
DocPDF has two types of adapters:
|
|
@@ -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,6 +1,7 @@
|
|
|
1
1
|
require "prawn"
|
|
2
2
|
require_relative "base"
|
|
3
3
|
require_relative "../../prawn_errors"
|
|
4
|
+
require_relative "../../font_files"
|
|
4
5
|
|
|
5
6
|
module DocPDF
|
|
6
7
|
module Adapters
|
|
@@ -20,6 +21,7 @@ module DocPDF
|
|
|
20
21
|
opts = config.text_options
|
|
21
22
|
content = normalize(data)
|
|
22
23
|
pdf = ::Prawn::Document.new(page_size: config.page_size, margin: opts[:margins])
|
|
24
|
+
register_font(pdf, opts[:font], opts[:font_file])
|
|
23
25
|
pdf.font(opts[:font], size: opts[:font_size])
|
|
24
26
|
pdf.text content, color: opts[:color]
|
|
25
27
|
pdf.render
|
|
@@ -29,6 +31,13 @@ module DocPDF
|
|
|
29
31
|
|
|
30
32
|
private
|
|
31
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
|
+
|
|
32
41
|
def normalize(data)
|
|
33
42
|
data.dup.force_encoding("UTF-8").gsub(LINE_SEPARATORS, "\n")
|
|
34
43
|
end
|
|
@@ -2,6 +2,7 @@ require "combine_pdf"
|
|
|
2
2
|
require "prawn"
|
|
3
3
|
require_relative "base"
|
|
4
4
|
require_relative "../../prawn_errors"
|
|
5
|
+
require_relative "../../font_files"
|
|
5
6
|
|
|
6
7
|
module DocPDF
|
|
7
8
|
module Adapters
|
|
@@ -62,6 +63,13 @@ module DocPDF
|
|
|
62
63
|
[img_w, img_h]
|
|
63
64
|
end
|
|
64
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
|
+
|
|
65
73
|
def render_image_stamp(pdf, stamp)
|
|
66
74
|
page_w = pdf.bounds.width
|
|
67
75
|
page_h = pdf.bounds.height
|
|
@@ -88,6 +96,7 @@ module DocPDF
|
|
|
88
96
|
page_w = pdf.bounds.width
|
|
89
97
|
page_h = pdf.bounds.height
|
|
90
98
|
|
|
99
|
+
register_font(pdf, stamp[:font], stamp[:font_file])
|
|
91
100
|
pdf.font(stamp[:font])
|
|
92
101
|
font_size = fit_font_size(stamp[:font_size], stamp[:text], pdf, page_w, page_h)
|
|
93
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
|
|
@@ -88,6 +89,7 @@ module DocPDF
|
|
|
88
89
|
page_w = page.box.width
|
|
89
90
|
page_h = page.box.height
|
|
90
91
|
|
|
92
|
+
register_font(doc, stamp[:font], stamp[:font_file])
|
|
91
93
|
font = doc.fonts.add(stamp[:font])
|
|
92
94
|
glyph_units = font.decode_utf8(stamp[:text]).sum { |g| g.width }
|
|
93
95
|
font_size = fit_font_size(stamp[:font_size], glyph_units, page_w, page_h)
|
|
@@ -116,6 +118,13 @@ module DocPDF
|
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
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
|
+
|
|
119
128
|
def write_to_string(doc)
|
|
120
129
|
io = StringIO.new
|
|
121
130
|
doc.write(io)
|
data/lib/docpdf/configuration.rb
CHANGED
|
@@ -9,6 +9,7 @@ module DocPDF
|
|
|
9
9
|
|
|
10
10
|
TEXT_DEFAULTS = {
|
|
11
11
|
font: "Courier",
|
|
12
|
+
font_file: nil,
|
|
12
13
|
font_size: 10,
|
|
13
14
|
margins: [50, 50, 50, 50],
|
|
14
15
|
color: "333333",
|
|
@@ -16,6 +17,7 @@ module DocPDF
|
|
|
16
17
|
|
|
17
18
|
WATERMARK_DEFAULTS = {
|
|
18
19
|
font: "Helvetica",
|
|
20
|
+
font_file: nil,
|
|
19
21
|
font_size: 72,
|
|
20
22
|
color: "AAAAAA",
|
|
21
23
|
rotation: 45,
|
|
@@ -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
|
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
|
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,6 +38,7 @@ 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
|
|
43
44
|
- lib/docpdf/prawn_errors.rb
|