stationery 0.6.0 → 0.7.0
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 +10 -0
- data/README.md +72 -15
- data/lib/stationery/builder.rb +5 -3
- data/lib/stationery/canvas/text.rb +4 -3
- data/lib/stationery/canvas.rb +5 -2
- data/lib/stationery/document.rb +26 -9
- data/lib/stationery/elements.rb +1 -1
- data/lib/stationery/fonts/fallback.rb +3 -3
- data/lib/stationery/fonts/font.rb +39 -21
- data/lib/stationery/fonts/gsub/single_subst.rb +37 -0
- data/lib/stationery/fonts/gsub.rb +65 -20
- data/lib/stationery/fonts/ligatures.rb +6 -4
- data/lib/stationery/hyphenation/patterns/LICENSES.md +60 -0
- data/lib/stationery/hyphenation/patterns/de-1996.pat.txt +36709 -0
- data/lib/stationery/hyphenation/patterns/en-us.hyp.txt +14 -0
- data/lib/stationery/hyphenation/patterns/en-us.pat.txt +4938 -0
- data/lib/stationery/hyphenation/patterns/sv.pat.txt +4693 -0
- data/lib/stationery/hyphenation.rb +132 -0
- data/lib/stationery/images/png.rb +66 -0
- data/lib/stationery/images/resampled.rb +80 -0
- data/lib/stationery/layout/image.rb +28 -2
- data/lib/stationery/layout/paginator.rb +1 -1
- data/lib/stationery/layout/text.rb +2 -2
- data/lib/stationery/minitest.rb +5 -0
- data/lib/stationery/page_templates.rb +2 -1
- data/lib/stationery/pdf/assembler.rb +6 -2
- data/lib/stationery/pdf/page_labels.rb +71 -0
- data/lib/stationery/testing/inspector.rb +13 -0
- data/lib/stationery/testing/matchers.rb +10 -0
- data/lib/stationery/text/entities.rb +14 -4
- data/lib/stationery/text/paragraph.rb +1 -1
- data/lib/stationery/text/style.rb +14 -3
- data/lib/stationery/text/wrapper.rb +73 -12
- data/lib/stationery/version.rb +1 -1
- data/lib/stationery/warnings.rb +6 -0
- data/lib/stationery.rb +4 -0
- metadata +10 -1
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
module Stationery
|
|
4
4
|
module Text
|
|
5
5
|
# Greedy line breaking over styled runs. Breaks at spaces (the space is
|
|
6
|
-
# dropped at the break) and after hyphens; a word
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# dropped at the break) and after hyphens; a word that does not fit is
|
|
7
|
+
# hyphenated when it may be (a soft hyphen U+00AD names the break points,
|
|
8
|
+
# else the style's `hyphenate` language), and a word wider than the line
|
|
9
|
+
# is broken between characters. A word that changes style midway
|
|
10
|
+
# ("<b>Tot</b>al") is still one word. Soft hyphens are never measured or
|
|
11
|
+
# drawn: a broken word draws "-" at the break and nothing otherwise.
|
|
9
12
|
class Wrapper
|
|
10
13
|
TOKEN = /\n|[ \t]+|[^ \t\n-]*-+|[^ \t\n-]+/
|
|
14
|
+
SOFT_HYPHEN = ""
|
|
15
|
+
EPSILON = 0.0001
|
|
11
16
|
|
|
12
17
|
Segment = Data.define(:text, :style)
|
|
13
18
|
|
|
@@ -53,16 +58,70 @@ module Stationery
|
|
|
53
58
|
end
|
|
54
59
|
end
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
# A word holding soft hyphens breaks only at them, even once the part
|
|
62
|
+
# after a break carries none (TeX's rule: a discretionary hyphen exempts
|
|
63
|
+
# the whole word from the patterns).
|
|
64
|
+
def place_word(word, explicit: word.any? { |segment| segment.text.include?(SOFT_HYPHEN) })
|
|
65
|
+
@explicit = explicit
|
|
57
66
|
needed = width(@pending_space) + width(word)
|
|
58
|
-
if @current.empty? || line_width + needed <= @max +
|
|
67
|
+
if @current.empty? || line_width + needed <= @max + EPSILON
|
|
59
68
|
@current.concat(@pending_space, word)
|
|
69
|
+
elsif (head, tail = hyphenated(word, @max - line_width - width(@pending_space)))
|
|
70
|
+
@current.concat(@pending_space, head)
|
|
71
|
+
finish(wrapped: true)
|
|
72
|
+
return place_word(tail, explicit:)
|
|
60
73
|
else
|
|
61
74
|
finish(wrapped: true)
|
|
62
75
|
@current.concat(word)
|
|
63
76
|
end
|
|
64
77
|
@pending_space = []
|
|
65
|
-
|
|
78
|
+
overflow
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# A word alone on its line and still too wide: hyphenate what fits,
|
|
82
|
+
# else move the characters that overflow onto following lines.
|
|
83
|
+
def overflow
|
|
84
|
+
while line_width > @max + EPSILON && characters(@current) > 1
|
|
85
|
+
head, tail = hyphenated(@current, @max)
|
|
86
|
+
if head
|
|
87
|
+
@current = head
|
|
88
|
+
finish(wrapped: true)
|
|
89
|
+
@current = tail
|
|
90
|
+
else
|
|
91
|
+
break_long_word
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# [head, tail] with the longest hyphenation of `word` whose head (and
|
|
97
|
+
# its hyphen) fits `available`; nil when the word offers no break that
|
|
98
|
+
# fits. Soft hyphens name the breaks and suppress the patterns.
|
|
99
|
+
def hyphenated(word, available)
|
|
100
|
+
chars = word.flat_map { |segment| segment.text.chars.map { |char| Segment.new(char, segment.style) } }
|
|
101
|
+
break_points(chars).reverse_each do |index|
|
|
102
|
+
head = chars.first(index).reject { |segment| segment.text == SOFT_HYPHEN }
|
|
103
|
+
head << Segment.new(hyphen_for(chars[index - 1].style), chars[index - 1].style)
|
|
104
|
+
return [head, chars.drop(index)] if width(head) <= available + EPSILON
|
|
105
|
+
end
|
|
106
|
+
nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Indexes into the word's characters before which it may break: after
|
|
110
|
+
# each soft hyphen, else where the language's patterns allow.
|
|
111
|
+
def break_points(chars)
|
|
112
|
+
return (1...chars.length).select { |index| chars[index - 1].text == SOFT_HYPHEN } if @explicit
|
|
113
|
+
|
|
114
|
+
text = chars.map(&:text).join
|
|
115
|
+
language = chars.first.style.hyphenate or return []
|
|
116
|
+
core = text[/\p{L}+/] or return []
|
|
117
|
+
offset = text.index(core)
|
|
118
|
+
Hyphenation.points(core, language).map { |point| point + offset }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# The font's hyphen, or the dedicated U+2010 when it lacks "-".
|
|
122
|
+
def hyphen_for(style)
|
|
123
|
+
font = @book.resolve(style).first
|
|
124
|
+
!font.glyph?("-") && font.glyph?("‐") ? "‐" : "-"
|
|
66
125
|
end
|
|
67
126
|
|
|
68
127
|
# Moves characters that overflow the line onto following lines.
|
|
@@ -71,7 +130,7 @@ module Stationery
|
|
|
71
130
|
@current.each do |segment|
|
|
72
131
|
segment.text.each_char do |char|
|
|
73
132
|
piece = Segment.new(char, segment.style)
|
|
74
|
-
return keep_overflow(fitting, piece) if fitting.any? && width(fitting + [piece]) > @max +
|
|
133
|
+
return keep_overflow(fitting, piece) if fitting.any? && width(fitting + [piece]) > @max + EPSILON
|
|
75
134
|
|
|
76
135
|
fitting << piece
|
|
77
136
|
end
|
|
@@ -104,8 +163,10 @@ module Stationery
|
|
|
104
163
|
|
|
105
164
|
def fragments(segments)
|
|
106
165
|
x = 0
|
|
107
|
-
segments.chunk_while { |a, b| a.style == b.style }.
|
|
108
|
-
text = group.map(&:text).join
|
|
166
|
+
segments.chunk_while { |a, b| a.style == b.style }.filter_map do |group|
|
|
167
|
+
text = group.map(&:text).join.delete(SOFT_HYPHEN)
|
|
168
|
+
next if text.empty?
|
|
169
|
+
|
|
109
170
|
font, face = @book.resolve(group.first.style)
|
|
110
171
|
fragment_width = measure(text, group.first.style)
|
|
111
172
|
Fragment.new(text, group.first.style, font, face, fragment_width, x).tap { x += fragment_width }
|
|
@@ -117,16 +178,16 @@ module Stationery
|
|
|
117
178
|
end
|
|
118
179
|
|
|
119
180
|
def line_width = width(@current)
|
|
120
|
-
def characters(segments) = segments.sum { |segment| segment.text.length }
|
|
181
|
+
def characters(segments) = segments.sum { |segment| segment.text.delete(SOFT_HYPHEN).length }
|
|
121
182
|
|
|
122
183
|
def width(segments)
|
|
123
|
-
segments.sum { |segment| measure(segment.text, segment.style) }
|
|
184
|
+
segments.sum { |segment| measure(segment.text.delete(SOFT_HYPHEN), segment.style) }
|
|
124
185
|
end
|
|
125
186
|
|
|
126
187
|
def measure(text, style)
|
|
127
188
|
font = @book.resolve(style).first
|
|
128
189
|
font.width_of(text, style.render_size, letter_spacing: style.letter_spacing, kerning: style.kerning,
|
|
129
|
-
ligatures: style.ligatures)
|
|
190
|
+
ligatures: style.ligatures, features: style.features)
|
|
130
191
|
end
|
|
131
192
|
end
|
|
132
193
|
end
|
data/lib/stationery/version.rb
CHANGED
data/lib/stationery/warnings.rb
CHANGED
|
@@ -29,6 +29,12 @@ module Stationery
|
|
|
29
29
|
def message = %(image "#{source}" skipped: #{reason})
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
OversizedImage = Data.define(:source, :pixels, :ppi, :limit) do
|
|
33
|
+
def message
|
|
34
|
+
%(image "#{source}" #{pixels}px wide is drawn at #{ppi} ppi (limit #{limit}); resize it before embedding)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
32
38
|
UnresolvedLink = Data.define(:name, :page) do
|
|
33
39
|
def message = %(link to "#{name}" on page #{page} has no matching anchor)
|
|
34
40
|
end
|
data/lib/stationery.rb
CHANGED
|
@@ -27,6 +27,7 @@ require_relative "stationery/fonts/gpos/pair_pos"
|
|
|
27
27
|
require_relative "stationery/fonts/kerning"
|
|
28
28
|
require_relative "stationery/fonts/gsub"
|
|
29
29
|
require_relative "stationery/fonts/gsub/ligature_subst"
|
|
30
|
+
require_relative "stationery/fonts/gsub/single_subst"
|
|
30
31
|
require_relative "stationery/fonts/ligatures"
|
|
31
32
|
require_relative "stationery/fonts/subset"
|
|
32
33
|
require_relative "stationery/fonts/registry"
|
|
@@ -46,6 +47,7 @@ require_relative "stationery/images/cache"
|
|
|
46
47
|
require_relative "stationery/images/scanlines"
|
|
47
48
|
require_relative "stationery/images/jpeg"
|
|
48
49
|
require_relative "stationery/images/png"
|
|
50
|
+
require_relative "stationery/images/resampled"
|
|
49
51
|
require_relative "stationery/color"
|
|
50
52
|
require_relative "stationery/geometry"
|
|
51
53
|
require_relative "stationery/page"
|
|
@@ -59,11 +61,13 @@ require_relative "stationery/canvas/debug"
|
|
|
59
61
|
require_relative "stationery/canvas/marking"
|
|
60
62
|
require_relative "stationery/canvas"
|
|
61
63
|
require_relative "stationery/pdf/outline_writer"
|
|
64
|
+
require_relative "stationery/pdf/page_labels"
|
|
62
65
|
require_relative "stationery/forms/metrics"
|
|
63
66
|
require_relative "stationery/forms/field"
|
|
64
67
|
require_relative "stationery/forms/appearance"
|
|
65
68
|
require_relative "stationery/forms/acro_form"
|
|
66
69
|
require_relative "stationery/pdf/assembler"
|
|
70
|
+
require_relative "stationery/hyphenation"
|
|
67
71
|
require_relative "stationery/text/style"
|
|
68
72
|
require_relative "stationery/fonts/fallback"
|
|
69
73
|
require_relative "stationery/fonts/font_book"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stationery
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -71,6 +71,7 @@ files:
|
|
|
71
71
|
- lib/stationery/fonts/gpos/pair_pos.rb
|
|
72
72
|
- lib/stationery/fonts/gsub.rb
|
|
73
73
|
- lib/stationery/fonts/gsub/ligature_subst.rb
|
|
74
|
+
- lib/stationery/fonts/gsub/single_subst.rb
|
|
74
75
|
- lib/stationery/fonts/installer.rb
|
|
75
76
|
- lib/stationery/fonts/kern_table.rb
|
|
76
77
|
- lib/stationery/fonts/kerning.rb
|
|
@@ -94,10 +95,17 @@ files:
|
|
|
94
95
|
- lib/stationery/html/tree_builder/blocks.rb
|
|
95
96
|
- lib/stationery/html/tree_builder/collector.rb
|
|
96
97
|
- lib/stationery/html/whitespace.rb
|
|
98
|
+
- lib/stationery/hyphenation.rb
|
|
99
|
+
- lib/stationery/hyphenation/patterns/LICENSES.md
|
|
100
|
+
- lib/stationery/hyphenation/patterns/de-1996.pat.txt
|
|
101
|
+
- lib/stationery/hyphenation/patterns/en-us.hyp.txt
|
|
102
|
+
- lib/stationery/hyphenation/patterns/en-us.pat.txt
|
|
103
|
+
- lib/stationery/hyphenation/patterns/sv.pat.txt
|
|
97
104
|
- lib/stationery/images/cache.rb
|
|
98
105
|
- lib/stationery/images/images.rb
|
|
99
106
|
- lib/stationery/images/jpeg.rb
|
|
100
107
|
- lib/stationery/images/png.rb
|
|
108
|
+
- lib/stationery/images/resampled.rb
|
|
101
109
|
- lib/stationery/images/scanlines.rb
|
|
102
110
|
- lib/stationery/instrumentation.rb
|
|
103
111
|
- lib/stationery/layout/box.rb
|
|
@@ -143,6 +151,7 @@ files:
|
|
|
143
151
|
- lib/stationery/pdf/encryption/revision6.rb
|
|
144
152
|
- lib/stationery/pdf/encryption/standard_security.rb
|
|
145
153
|
- lib/stationery/pdf/outline_writer.rb
|
|
154
|
+
- lib/stationery/pdf/page_labels.rb
|
|
146
155
|
- lib/stationery/pdf/serializer.rb
|
|
147
156
|
- lib/stationery/pdf/stream.rb
|
|
148
157
|
- lib/stationery/pdf/types.rb
|