prawnx 0.1.1
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 +7 -0
- data/COPYING +2 -0
- data/GPLv2 +339 -0
- data/GPLv3 +674 -0
- data/LICENSE +65 -0
- data/LICENSE-prawn-svg +21 -0
- data/README.md +107 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/lib/pdf/core.rb +4 -0
- data/lib/prawn/measurement_extensions.rb +4 -0
- data/lib/prawn/svg/attributes/clip_path.rb +46 -0
- data/lib/prawn/svg/attributes/mask.rb +16 -0
- data/lib/prawn/svg/attributes/opacity.rb +30 -0
- data/lib/prawn/svg/attributes/space.rb +10 -0
- data/lib/prawn/svg/attributes/stroke.rb +50 -0
- data/lib/prawn/svg/attributes/transform.rb +10 -0
- data/lib/prawn/svg/attributes.rb +6 -0
- data/lib/prawn/svg/calculators/arc_to_bezier_curve.rb +57 -0
- data/lib/prawn/svg/calculators/aspect_ratio.rb +60 -0
- data/lib/prawn/svg/calculators/document_sizing.rb +103 -0
- data/lib/prawn/svg/calculators/path_length.rb +168 -0
- data/lib/prawn/svg/calculators/pixels.rb +57 -0
- data/lib/prawn/svg/color.rb +240 -0
- data/lib/prawn/svg/css/font_face_parser.rb +72 -0
- data/lib/prawn/svg/css/font_family_parser.rb +39 -0
- data/lib/prawn/svg/css/font_parser.rb +46 -0
- data/lib/prawn/svg/css/selector_parser.rb +178 -0
- data/lib/prawn/svg/css/stylesheets.rb +233 -0
- data/lib/prawn/svg/css/values_parser.rb +68 -0
- data/lib/prawn/svg/document.rb +170 -0
- data/lib/prawn/svg/elements/anchor.rb +10 -0
- data/lib/prawn/svg/elements/base.rb +312 -0
- data/lib/prawn/svg/elements/bbox_scaling.rb +78 -0
- data/lib/prawn/svg/elements/call_duplicator.rb +37 -0
- data/lib/prawn/svg/elements/circle.rb +25 -0
- data/lib/prawn/svg/elements/clip_path.rb +26 -0
- data/lib/prawn/svg/elements/container.rb +10 -0
- data/lib/prawn/svg/elements/direct_render_base.rb +27 -0
- data/lib/prawn/svg/elements/ellipse.rb +22 -0
- data/lib/prawn/svg/elements/gradient.rb +184 -0
- data/lib/prawn/svg/elements/ignored.rb +5 -0
- data/lib/prawn/svg/elements/image.rb +177 -0
- data/lib/prawn/svg/elements/line.rb +27 -0
- data/lib/prawn/svg/elements/marker.rb +72 -0
- data/lib/prawn/svg/elements/mask.rb +65 -0
- data/lib/prawn/svg/elements/path.rb +294 -0
- data/lib/prawn/svg/elements/pattern.rb +150 -0
- data/lib/prawn/svg/elements/polygon.rb +25 -0
- data/lib/prawn/svg/elements/polyline.rb +23 -0
- data/lib/prawn/svg/elements/rect.rb +64 -0
- data/lib/prawn/svg/elements/root.rb +22 -0
- data/lib/prawn/svg/elements/switch.rb +92 -0
- data/lib/prawn/svg/elements/text.rb +99 -0
- data/lib/prawn/svg/elements/text_component.rb +346 -0
- data/lib/prawn/svg/elements/text_node.rb +308 -0
- data/lib/prawn/svg/elements/text_path.rb +153 -0
- data/lib/prawn/svg/elements/use.rb +142 -0
- data/lib/prawn/svg/elements/viewport.rb +29 -0
- data/lib/prawn/svg/elements.rb +46 -0
- data/lib/prawn/svg/extension.rb +25 -0
- data/lib/prawn/svg/font.rb +20 -0
- data/lib/prawn/svg/font_metrics.rb +144 -0
- data/lib/prawn/svg/font_registry.rb +315 -0
- data/lib/prawn/svg/funciri.rb +14 -0
- data/lib/prawn/svg/gradient_renderer.rb +332 -0
- data/lib/prawn/svg/gradients.rb +53 -0
- data/lib/prawn/svg/interface.rb +72 -0
- data/lib/prawn/svg/length.rb +43 -0
- data/lib/prawn/svg/link_renderer.rb +40 -0
- data/lib/prawn/svg/loaders/data.rb +17 -0
- data/lib/prawn/svg/loaders/file.rb +111 -0
- data/lib/prawn/svg/loaders/web.rb +24 -0
- data/lib/prawn/svg/paint.rb +67 -0
- data/lib/prawn/svg/pathable.rb +153 -0
- data/lib/prawn/svg/pattern_renderer.rb +112 -0
- data/lib/prawn/svg/pdfmatrix.rb +37 -0
- data/lib/prawn/svg/percentage.rb +24 -0
- data/lib/prawn/svg/properties.rb +329 -0
- data/lib/prawn/svg/renderer.rb +249 -0
- data/lib/prawn/svg/state.rb +24 -0
- data/lib/prawn/svg/transform_parser.rb +78 -0
- data/lib/prawn/svg/ttc.rb +35 -0
- data/lib/prawn/svg/ttf.rb +95 -0
- data/lib/prawn/svg/url_loader.rb +46 -0
- data/lib/prawn/svg/version.rb +5 -0
- data/lib/prawn/table.rb +4 -0
- data/lib/prawn-svg.rb +53 -0
- data/lib/prawn.rb +10 -0
- data/lib/prawnx/annotations.rb +21 -0
- data/lib/prawnx/document/bounding_box.rb +193 -0
- data/lib/prawnx/document/column_box.rb +91 -0
- data/lib/prawnx/document/internals.rb +144 -0
- data/lib/prawnx/document/span.rb +25 -0
- data/lib/prawnx/document.rb +253 -0
- data/lib/prawnx/encoding.rb +29 -0
- data/lib/prawnx/errors.rb +37 -0
- data/lib/prawnx/font.rb +214 -0
- data/lib/prawnx/fonts/afm.rb +171 -0
- data/lib/prawnx/fonts/otf.rb +50 -0
- data/lib/prawnx/fonts/ttf.rb +410 -0
- data/lib/prawnx/graphic_state.rb +41 -0
- data/lib/prawnx/graphics/color.rb +148 -0
- data/lib/prawnx/graphics/patterns.rb +100 -0
- data/lib/prawnx/graphics/style.rb +171 -0
- data/lib/prawnx/graphics.rb +208 -0
- data/lib/prawnx/grid.rb +216 -0
- data/lib/prawnx/images/image.rb +244 -0
- data/lib/prawnx/images/png_decoder.rb +65 -0
- data/lib/prawnx/images.rb +115 -0
- data/lib/prawnx/measurement_extensions.rb +40 -0
- data/lib/prawnx/measurements.rb +62 -0
- data/lib/prawnx/outline.rb +146 -0
- data/lib/prawnx/page.rb +125 -0
- data/lib/prawnx/page_geometry.rb +27 -0
- data/lib/prawnx/pdf/outline_node.rb +53 -0
- data/lib/prawnx/pdf/reference.rb +31 -0
- data/lib/prawnx/pdf/writer.rb +105 -0
- data/lib/prawnx/pdf.rb +66 -0
- data/lib/prawnx/pdf_a/srgb_profile.rb +61 -0
- data/lib/prawnx/pdf_a.rb +130 -0
- data/lib/prawnx/pdf_core.rb +25 -0
- data/lib/prawnx/repeater.rb +79 -0
- data/lib/prawnx/security.rb +108 -0
- data/lib/prawnx/stamp.rb +74 -0
- data/lib/prawnx/table/cell/kinds.rb +246 -0
- data/lib/prawnx/table/cell.rb +331 -0
- data/lib/prawnx/table/cells.rb +208 -0
- data/lib/prawnx/table.rb +296 -0
- data/lib/prawnx/text/box.rb +16 -0
- data/lib/prawnx/text/formatted/arranger.rb +217 -0
- data/lib/prawnx/text/formatted/box.rb +312 -0
- data/lib/prawnx/text/formatted/fragment.rb +205 -0
- data/lib/prawnx/text/formatted/line_wrap.rb +204 -0
- data/lib/prawnx/text/formatted/parser.rb +149 -0
- data/lib/prawnx/text/formatted/wrap.rb +103 -0
- data/lib/prawnx/text.rb +281 -0
- data/lib/prawnx/transformation_stack.rb +43 -0
- data/lib/prawnx/true_type/cff_subset.rb +157 -0
- data/lib/prawnx/true_type/file.rb +371 -0
- data/lib/prawnx/true_type/open_type_layout.rb +189 -0
- data/lib/prawnx/true_type/simple_subsets.rb +94 -0
- data/lib/prawnx/true_type/subset.rb +187 -0
- data/lib/prawnx/version.rb +7 -0
- data/lib/prawnx/view.rb +28 -0
- data/lib/prawnx.rb +102 -0
- metadata +251 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Prawn::SVG
|
|
2
|
+
Paint = Struct.new(:color, :url) do
|
|
3
|
+
class << self
|
|
4
|
+
def none
|
|
5
|
+
new(:none, nil)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def black
|
|
9
|
+
new(Color.black, nil)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def parse(value)
|
|
13
|
+
case CSS::ValuesParser.parse(value)
|
|
14
|
+
in [['url', [url]]]
|
|
15
|
+
# If there is no fallback color, and the URL is unresolvable, the spec says that the document is in error.
|
|
16
|
+
# Chrome appears to treat this the same as an explicit 'none', so we do the same.
|
|
17
|
+
new(:none, url)
|
|
18
|
+
in [['url', [url]], keyword_or_color]
|
|
19
|
+
parse_keyword_or_color(keyword_or_color, url)
|
|
20
|
+
in [['url', [url]], keyword_or_color, ['icc-color', _args]] # rubocop:disable Lint/DuplicateBranch
|
|
21
|
+
parse_keyword_or_color(keyword_or_color, url)
|
|
22
|
+
in [keyword_or_color, ['icc-color', _args]]
|
|
23
|
+
parse_keyword_or_color(keyword_or_color, nil)
|
|
24
|
+
in [keyword_or_color] # rubocop:disable Lint/DuplicateBranch
|
|
25
|
+
parse_keyword_or_color(keyword_or_color, nil)
|
|
26
|
+
else
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def parse_keyword_or_color(value, url)
|
|
34
|
+
if value.is_a?(String)
|
|
35
|
+
keyword = value.downcase
|
|
36
|
+
return new(keyword.to_sym, url) if ['none', 'currentcolor'].include?(keyword)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
color = Color.parse(value)
|
|
40
|
+
new(color, url) if color
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def none?
|
|
45
|
+
color == :none && (url.nil? || !!@unresolved_url)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def resolve(gradients, current_color, color_mode)
|
|
49
|
+
if url
|
|
50
|
+
if url[0] == '#' && gradients && (gradient = gradients[url[1..]])
|
|
51
|
+
return gradient
|
|
52
|
+
else
|
|
53
|
+
@unresolved_url = true
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
case color
|
|
58
|
+
when :currentcolor
|
|
59
|
+
current_color
|
|
60
|
+
when :none
|
|
61
|
+
nil
|
|
62
|
+
else
|
|
63
|
+
color_mode == :cmyk ? color.to_cmyk : color
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
module Prawn::SVG::Pathable
|
|
2
|
+
Move = Struct.new(:destination)
|
|
3
|
+
Close = Struct.new(:destination)
|
|
4
|
+
Line = Struct.new(:destination)
|
|
5
|
+
Curve = Struct.new(:destination, :point1, :point2)
|
|
6
|
+
|
|
7
|
+
def bounding_box
|
|
8
|
+
points = commands.map { |command| translate(command.destination) }
|
|
9
|
+
x1, x2 = points.map(&:first).minmax
|
|
10
|
+
y2, y1 = points.map(&:last).minmax
|
|
11
|
+
|
|
12
|
+
[x1, y1, x2, y2]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
protected
|
|
16
|
+
|
|
17
|
+
def apply_commands
|
|
18
|
+
commands.each do |command|
|
|
19
|
+
case command
|
|
20
|
+
when Move
|
|
21
|
+
add_call 'move_to', translate(command.destination)
|
|
22
|
+
when Close
|
|
23
|
+
add_call 'close_path'
|
|
24
|
+
when Line
|
|
25
|
+
add_call 'line_to', translate(command.destination)
|
|
26
|
+
when Curve
|
|
27
|
+
add_call 'curve_to', translate(command.destination),
|
|
28
|
+
bounds: [translate(command.point1), translate(command.point2)]
|
|
29
|
+
else
|
|
30
|
+
raise NotImplementedError, 'Unknown path command type'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def apply_markers
|
|
36
|
+
if (marker = extract_element_from_url_id_reference(properties.marker_start, 'marker'))
|
|
37
|
+
marker.apply_marker(self, point: commands.first.destination, angle: angles.first)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if (marker = extract_element_from_url_id_reference(properties.marker_mid, 'marker'))
|
|
41
|
+
(1..commands.length - 2).each do |index|
|
|
42
|
+
marker.apply_marker(self, point: commands[index].destination, angle: angles[index])
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if (marker = extract_element_from_url_id_reference(properties.marker_end, 'marker'))
|
|
47
|
+
marker.apply_marker(self, point: commands.last.destination, angle: angles.last)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def angles
|
|
52
|
+
return @angles if @angles
|
|
53
|
+
|
|
54
|
+
last_point = nil
|
|
55
|
+
|
|
56
|
+
destination_angles = commands.map do |command|
|
|
57
|
+
angles =
|
|
58
|
+
case command
|
|
59
|
+
when Move
|
|
60
|
+
[nil, nil]
|
|
61
|
+
when Close, Line
|
|
62
|
+
angle = Math.atan2(command.destination[1] - last_point[1],
|
|
63
|
+
command.destination[0] - last_point[0]) * 180.0 / Math::PI
|
|
64
|
+
[angle, angle]
|
|
65
|
+
when Curve
|
|
66
|
+
point = select_non_equal_point(last_point, command.point1, command.point2, command.destination)
|
|
67
|
+
start = Math.atan2(point[1] - last_point[1], point[0] - last_point[0]) * 180.0 / Math::PI
|
|
68
|
+
|
|
69
|
+
point = select_non_equal_point(command.destination, command.point2, command.point1, last_point)
|
|
70
|
+
stop = Math.atan2(command.destination[1] - point[1],
|
|
71
|
+
command.destination[0] - point[0]) * 180.0 / Math::PI
|
|
72
|
+
|
|
73
|
+
[start, stop]
|
|
74
|
+
else
|
|
75
|
+
raise NotImplementedError, 'Unknown path command type'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
last_point = command.destination
|
|
79
|
+
angles
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
angles = destination_angles.each_cons(2).map do |first_angles, second_angles|
|
|
83
|
+
if first_angles.first.nil?
|
|
84
|
+
second_angles.first || 0
|
|
85
|
+
elsif second_angles.first.nil?
|
|
86
|
+
first_angles.last
|
|
87
|
+
else
|
|
88
|
+
first = first_angles.last
|
|
89
|
+
second = second_angles.last
|
|
90
|
+
bisect = (first + second) / 2.0
|
|
91
|
+
|
|
92
|
+
if (first - second).abs > 180
|
|
93
|
+
bisect >= 0 ? bisect - 180 : bisect + 180
|
|
94
|
+
else
|
|
95
|
+
bisect
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if commands.last.is_a?(Close)
|
|
101
|
+
first = destination_angles.last.last || 0
|
|
102
|
+
second = angles.first
|
|
103
|
+
bisect = (first + second) / 2.0
|
|
104
|
+
|
|
105
|
+
closing_angle = if (first - second).abs > 180
|
|
106
|
+
bisect >= 0 ? bisect - 180 : bisect + 180
|
|
107
|
+
else
|
|
108
|
+
bisect
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
angles[0] = closing_angle
|
|
112
|
+
angles << closing_angle
|
|
113
|
+
else
|
|
114
|
+
angles << destination_angles.last.last
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@angles = angles
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def parse_points(points_string)
|
|
121
|
+
values = points_string
|
|
122
|
+
.to_s
|
|
123
|
+
.strip
|
|
124
|
+
.gsub(/(\d)-(\d)/, '\1 -\2')
|
|
125
|
+
.split(Prawn::SVG::Elements::COMMA_WSP_REGEXP)
|
|
126
|
+
.map(&:to_f)
|
|
127
|
+
|
|
128
|
+
if values.length.odd?
|
|
129
|
+
document.warnings << 'points attribute has an odd number of points; ignoring the last one'
|
|
130
|
+
values.pop
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
raise Prawn::SVG::Elements::Base::SkipElementQuietly if values.empty?
|
|
134
|
+
|
|
135
|
+
values.each_slice(2).to_a
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def translate(point)
|
|
139
|
+
[point[0].to_f, document.sizing.output_height - point[1].to_f]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def select_non_equal_point(base, point_a, point_b, point_c)
|
|
145
|
+
if point_a != base
|
|
146
|
+
point_a
|
|
147
|
+
elsif point_b != base
|
|
148
|
+
point_b
|
|
149
|
+
else
|
|
150
|
+
point_c
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
class Prawn::SVG::PatternRenderer
|
|
2
|
+
include Prawn::SVG::PDFMatrix
|
|
3
|
+
|
|
4
|
+
def initialize(prawn, draw_type, renderer, tile_x:, tile_y:, tile_width:, tile_height:, transform:, calls:)
|
|
5
|
+
@prawn = prawn
|
|
6
|
+
@draw_type = draw_type
|
|
7
|
+
@renderer = renderer
|
|
8
|
+
@tile_x = tile_x
|
|
9
|
+
@tile_y = tile_y
|
|
10
|
+
@tile_width = tile_width
|
|
11
|
+
@tile_height = tile_height
|
|
12
|
+
@transform = transform
|
|
13
|
+
@calls = calls
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def draw
|
|
17
|
+
key = Prawn::SVG::GradientRenderer.next_key
|
|
18
|
+
|
|
19
|
+
pattern_ref = create_tiling_pattern(key)
|
|
20
|
+
|
|
21
|
+
prawn.page.resources[:Pattern] ||= {}
|
|
22
|
+
prawn.page.resources[:Pattern]["PSVG-Pattern-#{key}"] = pattern_ref
|
|
23
|
+
|
|
24
|
+
prawn.send(:set_color_space, draw_type, :Pattern)
|
|
25
|
+
draw_operator = draw_type == :fill ? 'scn' : 'SCN'
|
|
26
|
+
prawn.renderer.add_content("/PSVG-Pattern-#{key} #{draw_operator}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_reader :prawn, :draw_type, :renderer, :tile_x, :tile_y, :tile_width, :tile_height, :transform, :calls
|
|
32
|
+
|
|
33
|
+
def create_tiling_pattern(key)
|
|
34
|
+
stamp_name = "PSVG-PatternContent-#{key}"
|
|
35
|
+
|
|
36
|
+
prawn.create_stamp(stamp_name) do
|
|
37
|
+
renderer.render_calls(prawn, calls)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
registry = prawn.instance_variable_get(:@stamp_dictionary_registry)
|
|
41
|
+
stamp_entry = registry[stamp_name]
|
|
42
|
+
stamp_dict = stamp_entry[:stamp_dictionary]
|
|
43
|
+
|
|
44
|
+
# The content shift uses the base matrix (without patternTransform) so that
|
|
45
|
+
# content fills the tile correctly. The pattern Matrix includes the transform
|
|
46
|
+
# so the tiling grid is rotated/scaled, but each tile's content is not.
|
|
47
|
+
base = base_pattern_matrix
|
|
48
|
+
inverse = base.inverse
|
|
49
|
+
|
|
50
|
+
pattern_ref = prawn.ref!(
|
|
51
|
+
PatternType: 1,
|
|
52
|
+
PaintType: 1,
|
|
53
|
+
TilingType: 1,
|
|
54
|
+
BBox: [0, 0, tile_width, tile_height],
|
|
55
|
+
XStep: tile_width,
|
|
56
|
+
YStep: tile_height,
|
|
57
|
+
Matrix: matrix_for_pdf(full_pattern_matrix),
|
|
58
|
+
Resources: stamp_dict.data[:Resources] || {}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
inv_pdf = matrix_for_pdf(inverse)
|
|
62
|
+
cm_str = inv_pdf.map { |v| v.is_a?(Float) ? format('%.6f', v) : v.to_s }.join(' ')
|
|
63
|
+
pattern_ref.stream << "#{cm_str} cm\n"
|
|
64
|
+
pattern_ref.stream << stamp_dict.stream
|
|
65
|
+
|
|
66
|
+
registry.delete(stamp_name)
|
|
67
|
+
|
|
68
|
+
pattern_ref
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def svg_to_page_matrix
|
|
72
|
+
bounds_x, bounds_y = prawn.bounds.anchor
|
|
73
|
+
output_height = prawn.bounds.height
|
|
74
|
+
|
|
75
|
+
Matrix[
|
|
76
|
+
[1.0, 0.0, bounds_x.to_f],
|
|
77
|
+
[0.0, -1.0, bounds_y.to_f + output_height],
|
|
78
|
+
[0.0, 0.0, 1.0]
|
|
79
|
+
]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def pattern_to_svg_matrix
|
|
83
|
+
output_height = prawn.bounds.height
|
|
84
|
+
svg_x = tile_x.to_f
|
|
85
|
+
svg_y = (output_height - tile_y - tile_height).to_f
|
|
86
|
+
|
|
87
|
+
Matrix[
|
|
88
|
+
[1.0, 0.0, svg_x],
|
|
89
|
+
[0.0, -1.0, svg_y + tile_height],
|
|
90
|
+
[0.0, 0.0, 1.0]
|
|
91
|
+
]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Maps pattern space to page space WITHOUT patternTransform.
|
|
95
|
+
# Used to shift stamp content (in page coords) to pattern-local coords.
|
|
96
|
+
def base_pattern_matrix
|
|
97
|
+
svg_to_page_matrix * pattern_to_svg_matrix
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Maps pattern space to page space WITH patternTransform and viewport scaling.
|
|
101
|
+
# Used as the pattern's Matrix field so the tiling grid is transformed.
|
|
102
|
+
# Viewport scaling is needed because stamp content is in SVG user units,
|
|
103
|
+
# but shapes on the page are scaled by the document's viewport transform.
|
|
104
|
+
def full_pattern_matrix
|
|
105
|
+
viewport_scale = scale_matrix(sizing.x_scale, sizing.y_scale)
|
|
106
|
+
svg_to_page_matrix * viewport_scale * transform * pattern_to_svg_matrix
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def sizing
|
|
110
|
+
renderer.document.sizing
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Prawn::SVG::PDFMatrix
|
|
2
|
+
def load_matrix(matrix)
|
|
3
|
+
if matrix.is_a?(Matrix) && matrix.row_count == 3 && matrix.column_count == 3
|
|
4
|
+
matrix
|
|
5
|
+
elsif matrix.is_a?(Array) && matrix.length == 6
|
|
6
|
+
Matrix[
|
|
7
|
+
[matrix[0], matrix[2], matrix[4]],
|
|
8
|
+
[matrix[1], matrix[3], matrix[5]],
|
|
9
|
+
[0.0, 0.0, 1.0]
|
|
10
|
+
]
|
|
11
|
+
else
|
|
12
|
+
raise ArgumentError, 'unexpected matrix format'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def matrix_for_pdf(matrix)
|
|
17
|
+
matrix.to_a[0..1].transpose.flatten
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def translation_matrix(x = 0, y = 0)
|
|
21
|
+
Matrix[[1.0, 0.0, x.to_f], [0.0, 1.0, y.to_f], [0.0, 0.0, 1.0]]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def scale_matrix(x = 1, y = x)
|
|
25
|
+
Matrix[[x.to_f, 0.0, 0.0], [0.0, y.to_f, 0.0], [0.0, 0.0, 1.0]]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def rotation_matrix(angle, space: :pdf)
|
|
29
|
+
dir = space == :svg ? 1.0 : -1.0
|
|
30
|
+
|
|
31
|
+
Matrix[
|
|
32
|
+
[Math.cos(angle), -dir * Math.sin(angle), 0],
|
|
33
|
+
[dir * Math.sin(angle), Math.cos(angle), 0],
|
|
34
|
+
[0, 0, 1]
|
|
35
|
+
]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Prawn::SVG::Percentage = Struct.new(:value)
|
|
2
|
+
|
|
3
|
+
class Prawn::SVG::Percentage
|
|
4
|
+
REGEXP = /\A([+-]?\d*(?:\.\d+)?)%\z/i.freeze
|
|
5
|
+
|
|
6
|
+
def self.parse(value, positive_only: false)
|
|
7
|
+
if (matches = value.match(REGEXP))
|
|
8
|
+
number = Float(matches[1], exception: false)
|
|
9
|
+
new(number) if number && (!positive_only || number >= 0)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_f
|
|
14
|
+
value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_factor
|
|
18
|
+
value / 100.0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_pixels(axis_length, _font_size)
|
|
22
|
+
value * axis_length / 100.0
|
|
23
|
+
end
|
|
24
|
+
end
|