stationery 0.2.0 → 0.3.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 +21 -1
- data/README.md +47 -10
- data/lib/stationery/builder.rb +2 -1
- data/lib/stationery/canvas/text.rb +5 -4
- data/lib/stationery/canvas.rb +13 -0
- data/lib/stationery/document.rb +9 -2
- data/lib/stationery/elements.rb +1 -1
- data/lib/stationery/fonts/font.rb +36 -20
- data/lib/stationery/fonts/font_book.rb +3 -0
- data/lib/stationery/fonts/glyph_run.rb +4 -3
- data/lib/stationery/fonts/gpos.rb +11 -9
- data/lib/stationery/fonts/gsub/ligature_subst.rb +44 -0
- data/lib/stationery/fonts/gsub.rb +65 -0
- data/lib/stationery/fonts/ligatures.rb +16 -0
- data/lib/stationery/fonts/registry.rb +18 -9
- data/lib/stationery/fonts/to_unicode.rb +1 -1
- data/lib/stationery/fonts/true_type.rb +36 -12
- data/lib/stationery/fonts/woff.rb +52 -0
- data/lib/stationery/layout/box.rb +23 -6
- data/lib/stationery/layout/row.rb +1 -1
- data/lib/stationery/layout/svg.rb +5 -2
- data/lib/stationery/layout/text.rb +2 -1
- data/lib/stationery/pdf/assembler.rb +3 -2
- data/lib/stationery/pdf/encryption/aes.rb +37 -0
- data/lib/stationery/pdf/encryption/rc4.rb +33 -0
- data/lib/stationery/pdf/encryption/revision4.rb +48 -0
- data/lib/stationery/pdf/encryption/revision6.rb +56 -0
- data/lib/stationery/pdf/encryption/standard_security.rb +78 -0
- data/lib/stationery/pdf/serializer.rb +17 -9
- data/lib/stationery/pdf/writer.rb +24 -5
- data/lib/stationery/resources.rb +8 -2
- data/lib/stationery/svg/bounds.rb +76 -0
- data/lib/stationery/svg/document.rb +48 -18
- data/lib/stationery/svg/gradient.rb +115 -0
- data/lib/stationery/svg/painter.rb +70 -0
- data/lib/stationery/svg/parser.rb +24 -4
- data/lib/stationery/svg/selector.rb +37 -0
- data/lib/stationery/svg/shading.rb +34 -0
- data/lib/stationery/svg/style.rb +37 -20
- data/lib/stationery/svg/stylesheet.rb +47 -0
- data/lib/stationery/svg/text.rb +122 -0
- data/lib/stationery/svg/transform.rb +11 -0
- data/lib/stationery/text/paragraph.rb +1 -0
- data/lib/stationery/text/style.rb +4 -2
- data/lib/stationery/text/wrapper.rb +2 -1
- data/lib/stationery/version.rb +1 -1
- data/lib/stationery.rb +16 -0
- metadata +17 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stationery
|
|
4
|
+
module SVG
|
|
5
|
+
# Lays out a <text> element: its strings and tspans become pieces along
|
|
6
|
+
# the baseline, a new chunk starting wherever x or y is set, and each
|
|
7
|
+
# chunk is shifted by its text-anchor. Glyphs are drawn upright at the
|
|
8
|
+
# transformed origin, sized by the transform's uniform scale, so rotated,
|
|
9
|
+
# skewed or unevenly scaled text is approximated.
|
|
10
|
+
class Text
|
|
11
|
+
ANCHORS = { "middle" => 0.5, "end" => 1.0 }.freeze
|
|
12
|
+
DEFAULT_SIZE = 16
|
|
13
|
+
|
|
14
|
+
Piece = Data.define(:runs, :style, :x, :y, :chunk, :advance)
|
|
15
|
+
|
|
16
|
+
def initialize(canvas, painter, book, family)
|
|
17
|
+
@canvas = canvas
|
|
18
|
+
@painter = painter
|
|
19
|
+
@book = book
|
|
20
|
+
@family = family
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def draw(element, style)
|
|
24
|
+
@pieces = []
|
|
25
|
+
@x = @y = @chunk = 0
|
|
26
|
+
@space = true
|
|
27
|
+
place(element, style)
|
|
28
|
+
trim
|
|
29
|
+
@pieces.group_by(&:chunk).each_value { |chunk| draw_chunk(chunk) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def place(element, style)
|
|
35
|
+
move(element.attributes)
|
|
36
|
+
element.children.each do |child|
|
|
37
|
+
if child.is_a?(String) then add(child, style)
|
|
38
|
+
elsif child.name == "tspan" then place(child, style.child(child))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def move(attributes)
|
|
44
|
+
x, y, dx, dy = attributes.values_at("x", "y", "dx", "dy").map { |value| first(value) }
|
|
45
|
+
@chunk += 1 if x || y
|
|
46
|
+
@x = (x || @x) + (dx || 0)
|
|
47
|
+
@y = (y || @y) + (dy || 0)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def first(value) = value.to_s.split(/[\s,]+/).find { |part| !part.empty? }&.to_f
|
|
51
|
+
|
|
52
|
+
# Collapses the space between pieces and at the start of the text.
|
|
53
|
+
def add(string, style)
|
|
54
|
+
string = string.lstrip if @space
|
|
55
|
+
return if string.empty?
|
|
56
|
+
|
|
57
|
+
@space = string.end_with?(" ")
|
|
58
|
+
@pieces << piece(string, style, @x, @y)
|
|
59
|
+
@x += @pieces.last.advance
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def piece(string, style, x, y, chunk = @chunk)
|
|
63
|
+
italic = style.values["font-style"].to_s.match?(/italic|oblique/)
|
|
64
|
+
text_style = Stationery::Text::Style.new(family: family(style), size: size(style), weight: weight(style),
|
|
65
|
+
style: italic ? :italic : :normal)
|
|
66
|
+
runs = @book.fallback([Stationery::Text::Run.new(string, text_style)])
|
|
67
|
+
advance = runs.sum { |run| font(run).width_of(run.text, text_style.size) }
|
|
68
|
+
Piece.new(runs, style, x, y, chunk, advance)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def trim
|
|
72
|
+
last = @pieces.last or return
|
|
73
|
+
text = last.runs.map(&:text).join
|
|
74
|
+
return unless text.end_with?(" ")
|
|
75
|
+
|
|
76
|
+
@pieces[-1] = piece(text.rstrip, last.style, last.x, last.y, last.chunk)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def draw_chunk(pieces)
|
|
80
|
+
width = pieces.last.x + pieces.last.advance - pieces.first.x
|
|
81
|
+
shift = -width * ANCHORS.fetch(pieces.first.style.values["text-anchor"].to_s, 0)
|
|
82
|
+
pieces.each { |piece| draw_piece(piece, shift) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def draw_piece(piece, shift)
|
|
86
|
+
color = @painter.color(piece.style.fill, piece.style)
|
|
87
|
+
return unless color
|
|
88
|
+
|
|
89
|
+
x = piece.x + shift
|
|
90
|
+
scale = Transform.scale(piece.style.matrix)
|
|
91
|
+
piece.runs.each do |run|
|
|
92
|
+
font, face = @book.resolve(run.style)
|
|
93
|
+
page_x, page_y = Transform.apply(piece.style.matrix, x, piece.y)
|
|
94
|
+
@canvas.text(run.text, x: page_x, y: page_y, font:, size: run.style.size * scale, color:,
|
|
95
|
+
opacity: Painter.translucent(piece.style.opacity),
|
|
96
|
+
synthetic_bold: face.synthetic_bold, synthetic_oblique: face.synthetic_oblique)
|
|
97
|
+
x += font.width_of(run.text, run.style.size)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def font(run) = @book.resolve(run.style).first
|
|
102
|
+
|
|
103
|
+
# The first family of the list the book knows, else the document's.
|
|
104
|
+
def family(style)
|
|
105
|
+
names = style.values["font-family"].to_s.split(",").map { |name| name.strip.delete("'\"") }
|
|
106
|
+
names.find { |name| !name.empty? && @book.known?(name) } || @family
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def size(style)
|
|
110
|
+
size = style.values["font-size"].to_f
|
|
111
|
+
size.positive? ? size : DEFAULT_SIZE
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def weight(style)
|
|
115
|
+
value = style.values["font-weight"].to_s.strip
|
|
116
|
+
return value.to_i if value.match?(/\A\d+\z/)
|
|
117
|
+
|
|
118
|
+
%w[bold bolder].include?(value) ? :bold : :regular
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -31,6 +31,17 @@ module Stationery
|
|
|
31
31
|
multiply(multiply([1, 0, 0, 1, cx, cy], rotation), [1, 0, 0, 1, -cx, -cy])
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
def apply(matrix, x, y)
|
|
35
|
+
a, b, c, d, e, f = matrix
|
|
36
|
+
[(a * x) + (c * y) + e, (b * x) + (d * y) + f]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The uniform scale a matrix applies to areas, as a length factor.
|
|
40
|
+
def scale(matrix)
|
|
41
|
+
a, b, c, d, = matrix
|
|
42
|
+
Math.sqrt(((a * d) - (b * c)).abs)
|
|
43
|
+
end
|
|
44
|
+
|
|
34
45
|
# The transform that applies `inner` first, then `outer`.
|
|
35
46
|
def multiply(outer, inner)
|
|
36
47
|
a, b, c, d, e, f = outer
|
|
@@ -102,6 +102,7 @@ module Stationery
|
|
|
102
102
|
color: style.color, letter_spacing: style.letter_spacing, rise: style.rise,
|
|
103
103
|
opacity: style.opacity, underline: style.underline,
|
|
104
104
|
strikethrough: style.strikethrough, kerning: style.kerning,
|
|
105
|
+
ligatures: style.ligatures,
|
|
105
106
|
synthetic_bold: fragment.face.synthetic_bold,
|
|
106
107
|
synthetic_oblique: fragment.face.synthetic_oblique, word_spacing:)
|
|
107
108
|
canvas.link(x, top, width, line.height, style.link) if style.link
|
|
@@ -6,9 +6,11 @@ module Stationery
|
|
|
6
6
|
|
|
7
7
|
# How a run of text looks. Immutable; derive variants with #with.
|
|
8
8
|
Style = Data.define(:family, :size, :weight, :style, :color, :letter_spacing,
|
|
9
|
-
:underline, :strikethrough, :script, :link, :opacity, :kerning
|
|
9
|
+
:underline, :strikethrough, :script, :link, :opacity, :kerning,
|
|
10
|
+
:ligatures) do
|
|
10
11
|
def initialize(family:, size: 10, weight: :regular, style: :normal, color: "#000000", letter_spacing: 0,
|
|
11
|
-
underline: false, strikethrough: false, script: nil, link: nil, opacity: nil, kerning: true
|
|
12
|
+
underline: false, strikethrough: false, script: nil, link: nil, opacity: nil, kerning: true,
|
|
13
|
+
ligatures: true)
|
|
12
14
|
super
|
|
13
15
|
end
|
|
14
16
|
|
|
@@ -125,7 +125,8 @@ module Stationery
|
|
|
125
125
|
|
|
126
126
|
def measure(text, style)
|
|
127
127
|
font = @book.resolve(style).first
|
|
128
|
-
font.width_of(text, style.render_size, letter_spacing: style.letter_spacing, kerning: style.kerning
|
|
128
|
+
font.width_of(text, style.render_size, letter_spacing: style.letter_spacing, kerning: style.kerning,
|
|
129
|
+
ligatures: style.ligatures)
|
|
129
130
|
end
|
|
130
131
|
end
|
|
131
132
|
end
|
data/lib/stationery/version.rb
CHANGED
data/lib/stationery.rb
CHANGED
|
@@ -5,8 +5,14 @@ require_relative "stationery/errors"
|
|
|
5
5
|
require_relative "stationery/pdf/types"
|
|
6
6
|
require_relative "stationery/pdf/serializer"
|
|
7
7
|
require_relative "stationery/pdf/stream"
|
|
8
|
+
require_relative "stationery/pdf/encryption/rc4"
|
|
9
|
+
require_relative "stationery/pdf/encryption/aes"
|
|
10
|
+
require_relative "stationery/pdf/encryption/revision4"
|
|
11
|
+
require_relative "stationery/pdf/encryption/revision6"
|
|
12
|
+
require_relative "stationery/pdf/encryption/standard_security"
|
|
8
13
|
require_relative "stationery/pdf/writer"
|
|
9
14
|
require_relative "stationery/fonts/sfnt_metrics"
|
|
15
|
+
require_relative "stationery/fonts/woff"
|
|
10
16
|
require_relative "stationery/fonts/true_type"
|
|
11
17
|
require_relative "stationery/fonts/cff"
|
|
12
18
|
require_relative "stationery/fonts/cff_subset"
|
|
@@ -18,6 +24,9 @@ require_relative "stationery/fonts/gpos/coverage"
|
|
|
18
24
|
require_relative "stationery/fonts/gpos/class_def"
|
|
19
25
|
require_relative "stationery/fonts/gpos/pair_pos"
|
|
20
26
|
require_relative "stationery/fonts/kerning"
|
|
27
|
+
require_relative "stationery/fonts/gsub"
|
|
28
|
+
require_relative "stationery/fonts/gsub/ligature_subst"
|
|
29
|
+
require_relative "stationery/fonts/ligatures"
|
|
21
30
|
require_relative "stationery/fonts/subset"
|
|
22
31
|
require_relative "stationery/fonts/registry"
|
|
23
32
|
require_relative "stationery/fonts/glyph_run"
|
|
@@ -77,8 +86,15 @@ require_relative "stationery/svg/path_data"
|
|
|
77
86
|
require_relative "stationery/svg/arc"
|
|
78
87
|
require_relative "stationery/svg/parser"
|
|
79
88
|
require_relative "stationery/svg/transform"
|
|
89
|
+
require_relative "stationery/svg/selector"
|
|
90
|
+
require_relative "stationery/svg/stylesheet"
|
|
80
91
|
require_relative "stationery/svg/style"
|
|
81
92
|
require_relative "stationery/svg/shapes"
|
|
93
|
+
require_relative "stationery/svg/bounds"
|
|
94
|
+
require_relative "stationery/svg/gradient"
|
|
95
|
+
require_relative "stationery/svg/shading"
|
|
96
|
+
require_relative "stationery/svg/painter"
|
|
97
|
+
require_relative "stationery/svg/text"
|
|
82
98
|
require_relative "stationery/svg/document"
|
|
83
99
|
require_relative "stationery/layout/svg"
|
|
84
100
|
require_relative "stationery/layout/wrap"
|
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.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -67,9 +67,12 @@ files:
|
|
|
67
67
|
- lib/stationery/fonts/gpos/class_def.rb
|
|
68
68
|
- lib/stationery/fonts/gpos/coverage.rb
|
|
69
69
|
- lib/stationery/fonts/gpos/pair_pos.rb
|
|
70
|
+
- lib/stationery/fonts/gsub.rb
|
|
71
|
+
- lib/stationery/fonts/gsub/ligature_subst.rb
|
|
70
72
|
- lib/stationery/fonts/installer.rb
|
|
71
73
|
- lib/stationery/fonts/kern_table.rb
|
|
72
74
|
- lib/stationery/fonts/kerning.rb
|
|
75
|
+
- lib/stationery/fonts/ligatures.rb
|
|
73
76
|
- lib/stationery/fonts/name_table.rb
|
|
74
77
|
- lib/stationery/fonts/packs.rb
|
|
75
78
|
- lib/stationery/fonts/registry.rb
|
|
@@ -77,6 +80,7 @@ files:
|
|
|
77
80
|
- lib/stationery/fonts/subset.rb
|
|
78
81
|
- lib/stationery/fonts/to_unicode.rb
|
|
79
82
|
- lib/stationery/fonts/true_type.rb
|
|
83
|
+
- lib/stationery/fonts/woff.rb
|
|
80
84
|
- lib/stationery/geometry.rb
|
|
81
85
|
- lib/stationery/html/document.rb
|
|
82
86
|
- lib/stationery/html/tokenizer.rb
|
|
@@ -124,6 +128,11 @@ files:
|
|
|
124
128
|
- lib/stationery/page.rb
|
|
125
129
|
- lib/stationery/page_templates.rb
|
|
126
130
|
- lib/stationery/pdf/assembler.rb
|
|
131
|
+
- lib/stationery/pdf/encryption/aes.rb
|
|
132
|
+
- lib/stationery/pdf/encryption/rc4.rb
|
|
133
|
+
- lib/stationery/pdf/encryption/revision4.rb
|
|
134
|
+
- lib/stationery/pdf/encryption/revision6.rb
|
|
135
|
+
- lib/stationery/pdf/encryption/standard_security.rb
|
|
127
136
|
- lib/stationery/pdf/outline_writer.rb
|
|
128
137
|
- lib/stationery/pdf/serializer.rb
|
|
129
138
|
- lib/stationery/pdf/stream.rb
|
|
@@ -142,11 +151,18 @@ files:
|
|
|
142
151
|
- lib/stationery/rspec.rb
|
|
143
152
|
- lib/stationery/structure.rb
|
|
144
153
|
- lib/stationery/svg/arc.rb
|
|
154
|
+
- lib/stationery/svg/bounds.rb
|
|
145
155
|
- lib/stationery/svg/document.rb
|
|
156
|
+
- lib/stationery/svg/gradient.rb
|
|
157
|
+
- lib/stationery/svg/painter.rb
|
|
146
158
|
- lib/stationery/svg/parser.rb
|
|
147
159
|
- lib/stationery/svg/path_data.rb
|
|
160
|
+
- lib/stationery/svg/selector.rb
|
|
161
|
+
- lib/stationery/svg/shading.rb
|
|
148
162
|
- lib/stationery/svg/shapes.rb
|
|
149
163
|
- lib/stationery/svg/style.rb
|
|
164
|
+
- lib/stationery/svg/stylesheet.rb
|
|
165
|
+
- lib/stationery/svg/text.rb
|
|
150
166
|
- lib/stationery/svg/transform.rb
|
|
151
167
|
- lib/stationery/testing/inspector.rb
|
|
152
168
|
- lib/stationery/testing/matchers.rb
|