stationery 0.8.0 → 0.10.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 +24 -0
- data/README.md +251 -37
- data/lib/stationery/canvas.rb +3 -1
- data/lib/stationery/{svg → css}/selector.rb +1 -1
- data/lib/stationery/{svg → css}/stylesheet.rb +20 -6
- data/lib/stationery/css.rb +20 -0
- data/lib/stationery/document.rb +35 -7
- data/lib/stationery/elements/forms.rb +11 -6
- data/lib/stationery/elements/rich.rb +21 -6
- data/lib/stationery/elements.rb +20 -7
- data/lib/stationery/errors.rb +4 -0
- data/lib/stationery/fonts/fallback.rb +1 -1
- data/lib/stationery/fonts/font.rb +28 -18
- data/lib/stationery/forms/acro_form.rb +95 -25
- data/lib/stationery/forms/appearance.rb +92 -30
- data/lib/stationery/forms/field.rb +20 -11
- data/lib/stationery/forms/metrics.rb +3 -17
- data/lib/stationery/forms/typeface.rb +113 -0
- data/lib/stationery/html/css.rb +276 -0
- data/lib/stationery/html/document.rb +10 -2
- data/lib/stationery/html/tokenizer.rb +15 -4
- data/lib/stationery/html/tree_builder/blocks.rb +90 -40
- data/lib/stationery/html/tree_builder/collector.rb +9 -3
- data/lib/stationery/html/tree_builder.rb +44 -4
- data/lib/stationery/layout/flow.rb +29 -0
- data/lib/stationery/layout/image.rb +21 -7
- data/lib/stationery/layout/mark.rb +2 -0
- data/lib/stationery/layout/node.rb +4 -0
- data/lib/stationery/layout/table/cell.rb +14 -4
- data/lib/stationery/layout/table.rb +72 -14
- data/lib/stationery/layout/text.rb +47 -10
- data/lib/stationery/markdown/block_parser/containers.rb +21 -0
- data/lib/stationery/markdown/block_parser.rb +9 -3
- data/lib/stationery/markdown/document.rb +8 -2
- data/lib/stationery/markdown/inline_parser/emphasis.rb +17 -7
- data/lib/stationery/markdown/inline_parser/links.rb +17 -12
- data/lib/stationery/markdown/inline_parser/nodes.rb +24 -10
- data/lib/stationery/markdown/inline_parser.rb +9 -3
- data/lib/stationery/minitest.rb +4 -0
- data/lib/stationery/pdf/assembler.rb +31 -11
- data/lib/stationery/pdf/conformance.rb +7 -5
- data/lib/stationery/pdf/serializer.rb +3 -1
- data/lib/stationery/pdf/signature/cms.rb +95 -0
- data/lib/stationery/pdf/signature/timestamp.rb +120 -0
- data/lib/stationery/pdf/signature.rb +174 -0
- data/lib/stationery/pdf/types.rb +4 -0
- data/lib/stationery/pdf/writer.rb +73 -19
- data/lib/stationery/rich/nesting.rb +30 -0
- data/lib/stationery/rich/nodes.rb +37 -11
- data/lib/stationery/rich/renderer/boxes.rb +109 -0
- data/lib/stationery/rich/renderer/indents.rb +43 -0
- data/lib/stationery/rich/renderer/inlines.rb +9 -2
- data/lib/stationery/rich/renderer.rb +49 -16
- data/lib/stationery/svg/document.rb +8 -4
- data/lib/stationery/svg/parser.rb +51 -11
- data/lib/stationery/svg/style.rb +4 -6
- data/lib/stationery/svg/walker.rb +2 -0
- data/lib/stationery/tagging/element.rb +1 -1
- data/lib/stationery/testing/inspector.rb +98 -0
- data/lib/stationery/testing/matchers.rb +25 -0
- data/lib/stationery/text/breaks.rb +123 -0
- data/lib/stationery/text/paragraph.rb +8 -0
- data/lib/stationery/text/runs_builder.rb +4 -0
- data/lib/stationery/text/wrapper.rb +69 -11
- data/lib/stationery/version.rb +1 -1
- data/lib/stationery/warnings.rb +13 -0
- data/lib/stationery.rb +6 -2
- metadata +13 -3
|
@@ -3,7 +3,8 @@
|
|
|
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)
|
|
6
|
+
# dropped at the break), after hyphens, at a zero-width space (U+200B,
|
|
7
|
+
# HTML's <wbr>) and between ideographic characters (see Breaks); a word that does not fit is
|
|
7
8
|
# hyphenated when it may be (a soft hyphen U+00AD names the break points,
|
|
8
9
|
# else the style's `hyphenate` language), and a word wider than the line
|
|
9
10
|
# is broken between characters. A word that changes style midway
|
|
@@ -12,6 +13,7 @@ module Stationery
|
|
|
12
13
|
class Wrapper
|
|
13
14
|
TOKEN = /\n|[ \t]+|[^ \t\n-]*-+|[^ \t\n-]+/
|
|
14
15
|
SOFT_HYPHEN = ""
|
|
16
|
+
ZERO_WIDTH_SPACE = Breaks::ZERO_WIDTH_SPACE
|
|
15
17
|
EPSILON = 0.0001
|
|
16
18
|
|
|
17
19
|
Segment = Data.define(:text, :style)
|
|
@@ -26,6 +28,7 @@ module Stationery
|
|
|
26
28
|
@current = []
|
|
27
29
|
@pending_space = []
|
|
28
30
|
@fallback = fallback_style
|
|
31
|
+
@widths = {}.compare_by_identity
|
|
29
32
|
items(runs).each { |item| place(item) }
|
|
30
33
|
finish unless @current.empty? && @lines.any? && !@ended_with_newline
|
|
31
34
|
@lines
|
|
@@ -33,22 +36,67 @@ module Stationery
|
|
|
33
36
|
|
|
34
37
|
private
|
|
35
38
|
|
|
36
|
-
# [:word, segments] | [:space, segments] | [:newline]
|
|
39
|
+
# [:word, segments] | [:space, segments] | [:newline]. A word that runs
|
|
40
|
+
# on into the next styled run stays one word ("<b>Tot</b>al"); a
|
|
41
|
+
# zero-width space is an empty :space, a break that draws nothing; a
|
|
42
|
+
# word with CJK characters is cut into its break units (Breaks).
|
|
37
43
|
def items(runs)
|
|
38
44
|
items = []
|
|
45
|
+
@join = false # whether the next token may continue the last word
|
|
39
46
|
runs.each do |run|
|
|
40
|
-
run.text.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
if run.text.include?(ZERO_WIDTH_SPACE)
|
|
48
|
+
run.text.split(ZERO_WIDTH_SPACE, -1).each_with_index do |part, index|
|
|
49
|
+
if index.positive?
|
|
50
|
+
items << [:space, []]
|
|
51
|
+
@join = false
|
|
52
|
+
end
|
|
53
|
+
tokens(items, part, run.style)
|
|
46
54
|
end
|
|
55
|
+
else
|
|
56
|
+
tokens(items, run.text, run.style)
|
|
47
57
|
end
|
|
48
58
|
end
|
|
49
59
|
items
|
|
50
60
|
end
|
|
51
61
|
|
|
62
|
+
def tokens(items, text, style)
|
|
63
|
+
text.scan(TOKEN) do |token|
|
|
64
|
+
if token == "\n"
|
|
65
|
+
items << [:newline]
|
|
66
|
+
@join = false
|
|
67
|
+
elsif token.match?(/\A[ \t]/)
|
|
68
|
+
items << [:space, [Segment.new(token, style)]]
|
|
69
|
+
@join = false
|
|
70
|
+
else
|
|
71
|
+
word_units(items, token, style)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# A Latin word is one unit and may run on into the next styled run
|
|
77
|
+
# unless it ends in a hyphen; a word with CJK characters is cut into
|
|
78
|
+
# its break units, none of which the next token continues. Latin words
|
|
79
|
+
# skip the unit split, so they cost no allocation of their own.
|
|
80
|
+
def word_units(items, token, style)
|
|
81
|
+
if Breaks.cjk?(token)
|
|
82
|
+
Breaks.units(token).each do |text, glued|
|
|
83
|
+
add_unit(items, text, style, glued)
|
|
84
|
+
@join = false
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
add_unit(items, token, style, false)
|
|
88
|
+
@join = !token.end_with?("-")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def add_unit(items, text, style, glued)
|
|
93
|
+
if (@join || glued) && items.last&.first == :word
|
|
94
|
+
items.last.last << Segment.new(text, style)
|
|
95
|
+
else
|
|
96
|
+
items << [:word, [Segment.new(text, style)]]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
52
100
|
def place((kind, segments))
|
|
53
101
|
@ended_with_newline = kind == :newline
|
|
54
102
|
case kind
|
|
@@ -97,6 +145,8 @@ module Stationery
|
|
|
97
145
|
# its hyphen) fits `available`; nil when the word offers no break that
|
|
98
146
|
# fits. Soft hyphens name the breaks and suppress the patterns.
|
|
99
147
|
def hyphenated(word, available)
|
|
148
|
+
return unless @explicit || word.first&.style&.hyphenate
|
|
149
|
+
|
|
100
150
|
chars = word.flat_map { |segment| segment.text.chars.map { |char| Segment.new(char, segment.style) } }
|
|
101
151
|
break_points(chars).reverse_each do |index|
|
|
102
152
|
head = chars.first(index).reject { |segment| segment.text == SOFT_HYPHEN }
|
|
@@ -112,6 +162,8 @@ module Stationery
|
|
|
112
162
|
return (1...chars.length).select { |index| chars[index - 1].text == SOFT_HYPHEN } if @explicit
|
|
113
163
|
|
|
114
164
|
text = chars.map(&:text).join
|
|
165
|
+
return [] if Breaks.cjk?(text)
|
|
166
|
+
|
|
115
167
|
language = chars.first.style.hyphenate or return []
|
|
116
168
|
core = text[/\p{L}+/] or return []
|
|
117
169
|
offset = text.index(core)
|
|
@@ -164,7 +216,7 @@ module Stationery
|
|
|
164
216
|
def fragments(segments)
|
|
165
217
|
x = 0
|
|
166
218
|
segments.chunk_while { |a, b| a.style == b.style }.filter_map do |group|
|
|
167
|
-
text = group.map(&:text).join
|
|
219
|
+
text = plain(group.map(&:text).join)
|
|
168
220
|
next if text.empty?
|
|
169
221
|
|
|
170
222
|
font, face = @book.resolve(group.first.style)
|
|
@@ -178,12 +230,18 @@ module Stationery
|
|
|
178
230
|
end
|
|
179
231
|
|
|
180
232
|
def line_width = width(@current)
|
|
181
|
-
def characters(segments) = segments.sum { |segment| segment.text
|
|
233
|
+
def characters(segments) = segments.sum { |segment| plain(segment.text).length }
|
|
182
234
|
|
|
235
|
+
# A line is summed again for every word that joins it; its segments
|
|
236
|
+
# are the same objects each time, so each is measured once per wrap.
|
|
183
237
|
def width(segments)
|
|
184
|
-
segments.sum { |segment| measure(segment.text
|
|
238
|
+
segments.sum { |segment| @widths[segment] ||= measure(plain(segment.text), segment.style) }
|
|
185
239
|
end
|
|
186
240
|
|
|
241
|
+
# The text without its soft hyphens; the same String when it has none,
|
|
242
|
+
# which is nearly always, so measuring a word allocates nothing.
|
|
243
|
+
def plain(text) = text.include?(SOFT_HYPHEN) ? text.delete(SOFT_HYPHEN) : text
|
|
244
|
+
|
|
187
245
|
def measure(text, style)
|
|
188
246
|
font = @book.resolve(style).first
|
|
189
247
|
font.width_of(text, style.render_size, letter_spacing: style.letter_spacing, kerning: style.kerning,
|
data/lib/stationery/version.rb
CHANGED
data/lib/stationery/warnings.rb
CHANGED
|
@@ -43,6 +43,19 @@ module Stationery
|
|
|
43
43
|
def message = %(link "#{href}" dropped: scheme not allowed)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
NestingLimit = Data.define(:depth, :limit) do
|
|
47
|
+
def message = "content nested #{depth} levels deep was flattened below level #{limit}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
UnsupportedCss = Data.define(:properties, :selectors) do
|
|
51
|
+
def message
|
|
52
|
+
parts = []
|
|
53
|
+
parts << "properties #{properties.join(", ")}" if properties.any?
|
|
54
|
+
parts << "selectors #{selectors.join(", ")}" if selectors.any?
|
|
55
|
+
"html styles not read: #{parts.join("; ")}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
46
59
|
DuplicateAnchor = Data.define(:name, :page) do
|
|
47
60
|
def message = %(anchor "#{name}" on page #{page} is already defined)
|
|
48
61
|
end
|
data/lib/stationery.rb
CHANGED
|
@@ -66,7 +66,11 @@ require_relative "stationery/pdf/attachments"
|
|
|
66
66
|
require_relative "stationery/pdf/xmp"
|
|
67
67
|
require_relative "stationery/pdf/conformance"
|
|
68
68
|
require_relative "stationery/pdf/factur_x"
|
|
69
|
+
require_relative "stationery/pdf/signature"
|
|
70
|
+
require_relative "stationery/pdf/signature/cms"
|
|
71
|
+
require_relative "stationery/pdf/signature/timestamp"
|
|
69
72
|
require_relative "stationery/forms/metrics"
|
|
73
|
+
require_relative "stationery/forms/typeface"
|
|
70
74
|
require_relative "stationery/forms/field"
|
|
71
75
|
require_relative "stationery/forms/appearance"
|
|
72
76
|
require_relative "stationery/forms/acro_form"
|
|
@@ -79,6 +83,7 @@ require_relative "stationery/text/entities"
|
|
|
79
83
|
require_relative "stationery/text/markup"
|
|
80
84
|
require_relative "stationery/text/runs_builder"
|
|
81
85
|
require_relative "stationery/text/line"
|
|
86
|
+
require_relative "stationery/text/breaks"
|
|
82
87
|
require_relative "stationery/text/wrapper"
|
|
83
88
|
require_relative "stationery/text/paragraph"
|
|
84
89
|
require_relative "stationery/layout/node"
|
|
@@ -103,8 +108,7 @@ require_relative "stationery/svg/path_data"
|
|
|
103
108
|
require_relative "stationery/svg/arc"
|
|
104
109
|
require_relative "stationery/svg/parser"
|
|
105
110
|
require_relative "stationery/svg/transform"
|
|
106
|
-
require_relative "stationery/
|
|
107
|
-
require_relative "stationery/svg/stylesheet"
|
|
111
|
+
require_relative "stationery/css"
|
|
108
112
|
require_relative "stationery/svg/style"
|
|
109
113
|
require_relative "stationery/svg/shapes"
|
|
110
114
|
require_relative "stationery/svg/bounds"
|
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.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -40,6 +40,9 @@ files:
|
|
|
40
40
|
- lib/stationery/cli/render.rb
|
|
41
41
|
- lib/stationery/color.rb
|
|
42
42
|
- lib/stationery/component.rb
|
|
43
|
+
- lib/stationery/css.rb
|
|
44
|
+
- lib/stationery/css/selector.rb
|
|
45
|
+
- lib/stationery/css/stylesheet.rb
|
|
43
46
|
- lib/stationery/document.rb
|
|
44
47
|
- lib/stationery/elements.rb
|
|
45
48
|
- lib/stationery/elements/forms.rb
|
|
@@ -88,7 +91,9 @@ files:
|
|
|
88
91
|
- lib/stationery/forms/appearance.rb
|
|
89
92
|
- lib/stationery/forms/field.rb
|
|
90
93
|
- lib/stationery/forms/metrics.rb
|
|
94
|
+
- lib/stationery/forms/typeface.rb
|
|
91
95
|
- lib/stationery/geometry.rb
|
|
96
|
+
- lib/stationery/html/css.rb
|
|
92
97
|
- lib/stationery/html/document.rb
|
|
93
98
|
- lib/stationery/html/tokenizer.rb
|
|
94
99
|
- lib/stationery/html/tree_builder.rb
|
|
@@ -158,6 +163,9 @@ files:
|
|
|
158
163
|
- lib/stationery/pdf/outline_writer.rb
|
|
159
164
|
- lib/stationery/pdf/page_labels.rb
|
|
160
165
|
- lib/stationery/pdf/serializer.rb
|
|
166
|
+
- lib/stationery/pdf/signature.rb
|
|
167
|
+
- lib/stationery/pdf/signature/cms.rb
|
|
168
|
+
- lib/stationery/pdf/signature/timestamp.rb
|
|
161
169
|
- lib/stationery/pdf/stream.rb
|
|
162
170
|
- lib/stationery/pdf/types.rb
|
|
163
171
|
- lib/stationery/pdf/writer.rb
|
|
@@ -168,8 +176,11 @@ files:
|
|
|
168
176
|
- lib/stationery/railtie.rb
|
|
169
177
|
- lib/stationery/regions.rb
|
|
170
178
|
- lib/stationery/resources.rb
|
|
179
|
+
- lib/stationery/rich/nesting.rb
|
|
171
180
|
- lib/stationery/rich/nodes.rb
|
|
172
181
|
- lib/stationery/rich/renderer.rb
|
|
182
|
+
- lib/stationery/rich/renderer/boxes.rb
|
|
183
|
+
- lib/stationery/rich/renderer/indents.rb
|
|
173
184
|
- lib/stationery/rich/renderer/inlines.rb
|
|
174
185
|
- lib/stationery/rich/renderer/links.rb
|
|
175
186
|
- lib/stationery/rich/styles.rb
|
|
@@ -184,11 +195,9 @@ files:
|
|
|
184
195
|
- lib/stationery/svg/parser.rb
|
|
185
196
|
- lib/stationery/svg/path_data.rb
|
|
186
197
|
- lib/stationery/svg/region.rb
|
|
187
|
-
- lib/stationery/svg/selector.rb
|
|
188
198
|
- lib/stationery/svg/shading.rb
|
|
189
199
|
- lib/stationery/svg/shapes.rb
|
|
190
200
|
- lib/stationery/svg/style.rb
|
|
191
|
-
- lib/stationery/svg/stylesheet.rb
|
|
192
201
|
- lib/stationery/svg/text.rb
|
|
193
202
|
- lib/stationery/svg/transform.rb
|
|
194
203
|
- lib/stationery/svg/viewport.rb
|
|
@@ -200,6 +209,7 @@ files:
|
|
|
200
209
|
- lib/stationery/testing/marked_text.rb
|
|
201
210
|
- lib/stationery/testing/matchers.rb
|
|
202
211
|
- lib/stationery/testing/structure_reader.rb
|
|
212
|
+
- lib/stationery/text/breaks.rb
|
|
203
213
|
- lib/stationery/text/entities.rb
|
|
204
214
|
- lib/stationery/text/entities/html4.rb
|
|
205
215
|
- lib/stationery/text/line.rb
|