gamba 2.0.1 → 2.0.2
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 +18 -1
- data/README.md +13 -5
- data/lib/gamba/table.rb +3 -0
- data/lib/gamba/version.rb +1 -1
- data/lib/prawn/svg/document.rb +50 -16
- data/lib/prawn/svg/elements/image.rb +19 -4
- data/lib/prawn/svg/elements/text_component.rb +29 -26
- data/lib/prawn/svg/elements/use.rb +2 -2
- data/lib/prawn/svg/url_loader.rb +2 -1
- metadata +26 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6909a10a173cb8d39f9f93432f7b47495e2bf18ca5d56559b263ac8292235ba7
|
|
4
|
+
data.tar.gz: 1a54fa9a6878a7eab8ed06e4d9b57fde344ad3c3edd850c93de2fb5b2bd099fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8eb03f65d8d6e26264a0fbea81cb38bf68968b171bb84b41aeafd3b11360024c2ad6cfa2ce171a321861a6bfc2756ec8cd445168d20d345724358eb10604b3c
|
|
7
|
+
data.tar.gz: bf668afa09665f836ed62ff0b8d2a593baabaa062353c1b46b9297439bdc6c9cd3e0c977b937a397fa3a3e03dd53dd4cf8478133d49b0aadd7602b20c2cfbc18
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.0.2] — 2026-11-07
|
|
6
|
+
|
|
7
|
+
SVGs that users send can no longer crash or stall a server through `<use>`, `<image>` or `<tref>`.
|
|
8
|
+
|
|
9
|
+
**Fixed**
|
|
10
|
+
- An SVG loaded through `<use>` or `<image>` is checked like the document, attributes included: a `data:` URL nested
|
|
11
|
+
thousands of levels deep overflowed the stack, and entities in an attribute raised `RuntimeError`.
|
|
12
|
+
- `<tref>` draws through the `<use>` path: a tref inside its own text no longer overflows the stack, one to a missing
|
|
13
|
+
id no longer raises `NoMethodError`, and trefs doubling at each level stop at the 20,000-element budget.
|
|
14
|
+
- Embedded SVG images share the budget of the SVG that embeds them and stop at 16 levels, and `data:` URLs are
|
|
15
|
+
decoded once per document.
|
|
16
|
+
- An `@font-face` that is not a font becomes a warning, and a table with no cells draws nothing instead of raising
|
|
17
|
+
`NoMethodError`.
|
|
18
|
+
- addressable 2.9.1 or later is required: before it, an `@import` path took quadratic time to resolve
|
|
19
|
+
(GHSA-qq38-m9mg-xjj5).
|
|
20
|
+
|
|
5
21
|
## [2.0.1] — 2026-11-06
|
|
6
22
|
|
|
7
23
|
Fonts, images, SVGs and PDFs that users send can no longer hang a server, exhaust its memory or crash it.
|
|
@@ -88,7 +104,8 @@ speed. Same API and same pages as Prawn, pixel for pixel, in lighter files.
|
|
|
88
104
|
place of combine_pdf.
|
|
89
105
|
- `Gamba::VERSION` is the gem's; `Prawn::VERSION` stays 2.5.0, the API reproduced.
|
|
90
106
|
|
|
91
|
-
[Unreleased]: https://github.com/levups/gamba/compare/v2.0.
|
|
107
|
+
[Unreleased]: https://github.com/levups/gamba/compare/v2.0.2...HEAD
|
|
108
|
+
[2.0.2]: https://github.com/levups/gamba/compare/v2.0.1...v2.0.2
|
|
92
109
|
[2.0.1]: https://github.com/levups/gamba/compare/v2.0.0...v2.0.1
|
|
93
110
|
[2.0.0]: https://github.com/levups/gamba/compare/v1.1.0...v2.0.0
|
|
94
111
|
[1.1.0]: https://github.com/levups/gamba/compare/v1.0.1...v1.1.0
|
data/README.md
CHANGED
|
@@ -96,11 +96,18 @@ Any of it can be built to attack the server that renders it.
|
|
|
96
96
|
- SVG images fetch nothing from the web unless you pass `enable_web_requests: true`. prawn-svg 0.40 fetched by
|
|
97
97
|
default, so a submitted SVG could make your server request any URL. prawn-svg 1.0 plans the same default as
|
|
98
98
|
Gamba.
|
|
99
|
-
- An SVG
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
- An SVG reads only what you allow. Base64 `data:` URLs of PNG, JPEG and SVG images always load; `http` and
|
|
100
|
+
`https` URLs need `enable_web_requests: true`; files need `enable_file_requests_with_root`, and only regular
|
|
101
|
+
files inside that directory load, symbolic links in it followed. Any other URL, path or scheme is skipped with a
|
|
102
|
+
warning, and an entity the SVG declares from a file or URL is never read.
|
|
103
|
+
- An SVG can't make Gamba loop or run out of stack through references (`<use>`, `<tref>`, gradients) or deep
|
|
104
|
+
nesting. An SVG it loads with `<use>` or `<image>` is checked the same way, SVG images stop 16 levels inside
|
|
105
|
+
one another, and they draw within the budget of the SVG that embeds them.
|
|
106
|
+
- Fonts an SVG declares with `@font-face` disappear once it is drawn, and one that is not a font is left out. In
|
|
107
|
+
prawn-svg, an uploaded SVG declaring a "Helvetica" font changed the font of the application's own text.
|
|
102
108
|
- A damaged or malicious PNG, JPEG, TrueType or OpenType file either renders or raises an error Gamba names
|
|
103
|
-
(`Prawn::Errors::UnsupportedImageType`, `Prawn::Images::JPG::FormatError`, `Gamba::TrueType::Error
|
|
109
|
+
(`Prawn::Errors::UnsupportedImageType`, `Prawn::Images::JPG::FormatError`, `Gamba::TrueType::Error`, or
|
|
110
|
+
`Prawn::Errors::CannotFit` when a damaged font makes a glyph wider than the page). A PNG
|
|
104
111
|
inflates no further than the size its header gives. CI corrupts sample files on every run to check this.
|
|
105
112
|
- `Gamba.combine` stops at 64 levels of nested arrays and dictionaries and at 256 MB of decompressed data, and
|
|
106
113
|
ignores references that lead back to themselves, so a crafted PDF can't send it into a loop or fill
|
|
@@ -110,7 +117,8 @@ Any of it can be built to attack the server that renders it.
|
|
|
110
117
|
without them.
|
|
111
118
|
|
|
112
119
|
Inline formatting still turns `<link href="...">` into a clickable link. Escape user text before you put it inside
|
|
113
|
-
markup.
|
|
120
|
+
markup. A word with no space to break at, thousands of characters long, takes time that grows with the square of its
|
|
121
|
+
length, as in Prawn: limit the length of what users type.
|
|
114
122
|
|
|
115
123
|
To report a vulnerability, see [SECURITY.md](SECURITY.md).
|
|
116
124
|
|
data/lib/gamba/table.rb
CHANGED
|
@@ -77,6 +77,9 @@ module Prawn
|
|
|
77
77
|
alias_method :column, :columns
|
|
78
78
|
|
|
79
79
|
def draw
|
|
80
|
+
# Gamba: rows without a single cell, such as [[]], draw nothing, where prawn-table raised NoMethodError.
|
|
81
|
+
return if @cells.empty?
|
|
82
|
+
|
|
80
83
|
with_position do
|
|
81
84
|
reference_bounds = @pdf.reference_bounds
|
|
82
85
|
started_new_page_at_row = initial_row_on_initial_page
|
data/lib/gamba/version.rb
CHANGED
data/lib/prawn/svg/document.rb
CHANGED
|
@@ -10,6 +10,8 @@ class Prawn::SVG::Document
|
|
|
10
10
|
MAXIMUM_ELEMENTS_THROUGH_USE = 20_000
|
|
11
11
|
# Elements inside one another: deeper, Ruby's stack runs out, and REXML resolves each namespace by its ancestors.
|
|
12
12
|
MAXIMUM_DEPTH = 256
|
|
13
|
+
# SVG images inside SVG images: over the web, an SVG can embed itself without end.
|
|
14
|
+
MAXIMUM_EMBEDDING_DEPTH = 16
|
|
13
15
|
|
|
14
16
|
class << self
|
|
15
17
|
attr_accessor :enable_web_requests_warned
|
|
@@ -26,19 +28,20 @@ class Prawn::SVG::Document
|
|
|
26
28
|
:elements_by_id, :gradients,
|
|
27
29
|
:element_styles,
|
|
28
30
|
:color_mode,
|
|
29
|
-
:external_svg_cache
|
|
31
|
+
:external_svg_cache,
|
|
32
|
+
:embedding_depth,
|
|
33
|
+
:raster_dimensions_by_data
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
# Gamba: embedder is the document whose <image> embeds this one, for the budgets they share.
|
|
36
|
+
def initialize(data, bounds, options, font_registry: nil, css_parser: CssParser::Parser.new, attribute_overrides: {}, embedder: nil)
|
|
32
37
|
begin
|
|
33
|
-
@root =
|
|
38
|
+
@root = self.class.parse(data) or raise_parse_error(data)
|
|
34
39
|
rescue REXML::ParseException => e
|
|
35
40
|
raise_parse_error(data, e.message)
|
|
36
|
-
rescue RuntimeError => e
|
|
37
|
-
# Gamba: REXML's entity expansion limits raise a bare RuntimeError.
|
|
38
|
-
raise InvalidSVGData, "The data supplied is not a valid SVG document: #{e.message}"
|
|
39
41
|
end
|
|
40
|
-
check_tree!(@root)
|
|
41
42
|
|
|
43
|
+
@embedder = embedder
|
|
44
|
+
@embedding_depth = embedder ? embedder.embedding_depth + 1 : 0
|
|
42
45
|
@warnings = []
|
|
43
46
|
@options = options
|
|
44
47
|
@elements_by_id = {}
|
|
@@ -48,6 +51,7 @@ class Prawn::SVG::Document
|
|
|
48
51
|
@depth = 0
|
|
49
52
|
@gradients = Prawn::SVG::Gradients.new(self)
|
|
50
53
|
@external_svg_cache = {}
|
|
54
|
+
@raster_dimensions_by_data = {}.compare_by_identity
|
|
51
55
|
@fallback_font_name = options.fetch(:fallback_font_name, DEFAULT_FALLBACK_FONT_NAME)
|
|
52
56
|
@font_registry = font_registry
|
|
53
57
|
@color_mode = load_color_mode
|
|
@@ -96,26 +100,54 @@ class Prawn::SVG::Document
|
|
|
96
100
|
def drawing_through_use(source)
|
|
97
101
|
raise Prawn::SVG::Elements::Base::SkipElementError, "use tag references an element that contains it" if @references_in_use.any? { _1.equal?(source) }
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
source.each_recursive { @elements_through_use += 1 }
|
|
101
|
-
raise Prawn::SVG::Elements::Base::SkipElementError, "use tags draw more than #{MAXIMUM_ELEMENTS_THROUGH_USE} elements" if @elements_through_use > MAXIMUM_ELEMENTS_THROUGH_USE
|
|
102
|
-
|
|
103
|
+
draw_again(self.class.element_count(source))
|
|
103
104
|
@references_in_use << source
|
|
104
105
|
yield
|
|
105
106
|
ensure
|
|
106
107
|
@references_in_use.pop if @references_in_use.last.equal?(source)
|
|
107
108
|
end
|
|
108
109
|
|
|
109
|
-
# Gamba:
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
# Gamba: elements drawn once more, counted against one budget for this document and the SVG images it embeds.
|
|
111
|
+
def draw_again(element_count)
|
|
112
|
+
return @embedder.draw_again(element_count) if @embedder
|
|
113
|
+
|
|
114
|
+
@elements_through_use += element_count
|
|
115
|
+
raise Prawn::SVG::Elements::Base::SkipElementError, "use tags and SVG images draw more than #{MAXIMUM_ELEMENTS_THROUGH_USE} elements" if @elements_through_use > MAXIMUM_ELEMENTS_THROUGH_USE
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.element_count(source)
|
|
119
|
+
count = 1
|
|
120
|
+
source.each_recursive { count += 1 }
|
|
121
|
+
count
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Gamba: the root of an SVG, checked before anything walks it: the document's own, and every SVG it loads with
|
|
125
|
+
# <use> or <image>. REXML's limits on entity expansion raise a bare RuntimeError.
|
|
126
|
+
def self.parse(data)
|
|
127
|
+
root = REXML::Document.new(data).root
|
|
128
|
+
check_tree!(root) if root
|
|
129
|
+
root
|
|
130
|
+
rescue REXML::ParseException
|
|
131
|
+
raise
|
|
132
|
+
rescue RuntimeError => e
|
|
133
|
+
raise InvalidSVGData, "The data supplied is not a valid SVG document: #{e.message}"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Gamba: walked without recursion. Texts and attributes expand their entities here, so that REXML's limits on
|
|
137
|
+
# expansion raise InvalidSVGData rather than a RuntimeError mid-render.
|
|
138
|
+
def self.check_tree!(root)
|
|
112
139
|
level = [root]
|
|
113
140
|
MAXIMUM_DEPTH.times do
|
|
114
|
-
level.each
|
|
141
|
+
level.each do |element|
|
|
142
|
+
element.texts.each(&:value)
|
|
143
|
+
element.attributes.each_attribute(&:value)
|
|
144
|
+
end
|
|
115
145
|
level = level.flat_map { _1.elements.to_a }
|
|
116
146
|
return if level.empty?
|
|
117
147
|
end
|
|
118
148
|
raise InvalidSVGData, "The SVG document nests elements deeper than #{MAXIMUM_DEPTH} levels"
|
|
149
|
+
rescue REXML::ParseException
|
|
150
|
+
raise
|
|
119
151
|
rescue RuntimeError => e
|
|
120
152
|
raise InvalidSVGData, "The data supplied is not a valid SVG document: #{e.message}"
|
|
121
153
|
end
|
|
@@ -199,10 +231,12 @@ class Prawn::SVG::Document
|
|
|
199
231
|
tempfile.write(data)
|
|
200
232
|
tempfile.close
|
|
201
233
|
@font_face_tempfiles << tempfile
|
|
234
|
+
# Gamba: read now, so that a damaged font is a warning like a missing one, not an error mid-render.
|
|
235
|
+
Gamba::TrueType::File.open(tempfile.path).glyph_by_code
|
|
202
236
|
|
|
203
237
|
font_registry.register_font_face(family, weight, style, stretch, tempfile.path)
|
|
204
238
|
true
|
|
205
|
-
rescue Prawn::SVG::UrlLoader::Error => e
|
|
239
|
+
rescue Prawn::SVG::UrlLoader::Error, Gamba::TrueType::Error => e
|
|
206
240
|
warnings << "Failed to load @font-face font from #{source[:url]}: #{e.message}"
|
|
207
241
|
false
|
|
208
242
|
end
|
|
@@ -82,10 +82,11 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
|
82
82
|
|
|
83
83
|
def process_image(data, width, height, preserve_aspect_ratio)
|
|
84
84
|
if (handler = find_image_handler(data))
|
|
85
|
-
|
|
86
|
-
ImageData.new([image.width.to_f, image.height.to_f], nil)
|
|
85
|
+
ImageData.new(raster_dimensions(handler, data), nil)
|
|
87
86
|
|
|
88
87
|
elsif potentially_svg?(data)
|
|
88
|
+
raise SkipElementError, "SVG images nested deeper than #{Prawn::SVG::Document::MAXIMUM_EMBEDDING_DEPTH} levels" if @document.embedding_depth >= Prawn::SVG::Document::MAXIMUM_EMBEDDING_DEPTH
|
|
89
|
+
|
|
89
90
|
attribute_overrides = {}
|
|
90
91
|
attribute_overrides["preserveAspectRatio"] = preserve_aspect_ratio if preserve_aspect_ratio
|
|
91
92
|
|
|
@@ -98,8 +99,10 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
|
98
99
|
document = Prawn::SVG::Document.new(
|
|
99
100
|
data, [width, height], {width:, height:, enable_web_requests: has_web_loader},
|
|
100
101
|
# Gamba: the parent's fonts, without which an embedded SVG holding text failed to render.
|
|
101
|
-
attribute_overrides:, font_registry: @document.font_registry
|
|
102
|
+
attribute_overrides:, font_registry: @document.font_registry, embedder: @document
|
|
102
103
|
)
|
|
104
|
+
# Gamba: drawn again for each <use> of it, and for each image of an image, which a few kilobytes can repeat.
|
|
105
|
+
@document.draw_again(Prawn::SVG::Document.element_count(document.root)) if state.inside_use || @document.embedding_depth.positive?
|
|
103
106
|
|
|
104
107
|
dimensions = [document.sizing.output_width, document.sizing.output_height]
|
|
105
108
|
ImageData.new(dimensions, document)
|
|
@@ -111,6 +114,18 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
|
111
114
|
raise_invalid_image_type
|
|
112
115
|
end
|
|
113
116
|
|
|
117
|
+
# Gamba: the image of a data: URL, which the loader keeps, is read once however many times <use> draws it.
|
|
118
|
+
def raster_dimensions(handler, data)
|
|
119
|
+
return read_raster_dimensions(handler, data) unless @url[0, 5].casecmp?("data:")
|
|
120
|
+
|
|
121
|
+
@document.raster_dimensions_by_data[data] ||= read_raster_dimensions(handler, data)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def read_raster_dimensions(handler, data)
|
|
125
|
+
image = handler.new(data)
|
|
126
|
+
[image.width.to_f, image.height.to_f]
|
|
127
|
+
end
|
|
128
|
+
|
|
114
129
|
def extract_fragment(url)
|
|
115
130
|
return [url, nil] if url.start_with?("data:")
|
|
116
131
|
|
|
@@ -149,7 +164,7 @@ class Prawn::SVG::Elements::Image < Prawn::SVG::Elements::Base
|
|
|
149
164
|
end
|
|
150
165
|
|
|
151
166
|
def find_view_element_attributes(data, fragment)
|
|
152
|
-
root =
|
|
167
|
+
root = Prawn::SVG::Document.parse(data)
|
|
153
168
|
return unless root
|
|
154
169
|
|
|
155
170
|
element = Prawn::SVG::Document.element_by_id(root, fragment)
|
|
@@ -27,20 +27,12 @@ module Prawn::SVG
|
|
|
27
27
|
|
|
28
28
|
@font, @fallback_fonts = select_fonts
|
|
29
29
|
|
|
30
|
-
@children =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
build_child(child)
|
|
37
|
-
when "textPath"
|
|
38
|
-
build_text_path(child)
|
|
39
|
-
else
|
|
40
|
-
warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
|
|
41
|
-
[]
|
|
42
|
-
end
|
|
43
|
-
end
|
|
30
|
+
@children = if name == "tref"
|
|
31
|
+
# Gamba: a <tref> draws its text again, as a <use> does: not inside itself, and within the same budget.
|
|
32
|
+
reference = find_referenced_element
|
|
33
|
+
reference ? document.drawing_through_use(reference.source) { build_children(reference.source.children) } : []
|
|
34
|
+
else
|
|
35
|
+
build_children(source.children)
|
|
44
36
|
end
|
|
45
37
|
end
|
|
46
38
|
|
|
@@ -158,9 +150,29 @@ module Prawn::SVG
|
|
|
158
150
|
Elements::TextNode.new(self, text, leading, trailing)
|
|
159
151
|
end
|
|
160
152
|
|
|
153
|
+
def build_children(nodes)
|
|
154
|
+
svg_text_children(nodes).flat_map do |child|
|
|
155
|
+
if child.node_type == :text
|
|
156
|
+
build_text_node(child)
|
|
157
|
+
else
|
|
158
|
+
case child.name
|
|
159
|
+
when "tspan", "tref"
|
|
160
|
+
build_child(child)
|
|
161
|
+
when "textPath"
|
|
162
|
+
build_text_path(child)
|
|
163
|
+
else
|
|
164
|
+
warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
|
|
165
|
+
[]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
161
171
|
def build_child(child)
|
|
162
172
|
component = self.class.new(document, child, [], state.dup, self)
|
|
163
173
|
component.process
|
|
174
|
+
return [] unless component.children
|
|
175
|
+
|
|
164
176
|
component
|
|
165
177
|
end
|
|
166
178
|
|
|
@@ -172,8 +184,8 @@ module Prawn::SVG
|
|
|
172
184
|
component
|
|
173
185
|
end
|
|
174
186
|
|
|
175
|
-
def svg_text_children
|
|
176
|
-
|
|
187
|
+
def svg_text_children(nodes)
|
|
188
|
+
nodes.select do |child|
|
|
177
189
|
child.node_type == :text || (
|
|
178
190
|
child.node_type == :element &&
|
|
179
191
|
[SVG_NAMESPACE, ""].include?(child.namespace)
|
|
@@ -182,21 +194,12 @@ module Prawn::SVG
|
|
|
182
194
|
end
|
|
183
195
|
end
|
|
184
196
|
|
|
185
|
-
def text_children
|
|
186
|
-
if name == "tref"
|
|
187
|
-
reference = find_referenced_element
|
|
188
|
-
reference ? reference.source.children : []
|
|
189
|
-
else
|
|
190
|
-
source.children
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
197
|
def find_referenced_element
|
|
195
198
|
href = href_attribute
|
|
196
199
|
|
|
197
200
|
if href && href[0..0] == "#"
|
|
198
201
|
element = document.elements_by_id[href[1..]] # rubocop:disable Performance/ArraySemiInfiniteRangeSlice -- String
|
|
199
|
-
element if element
|
|
202
|
+
element if element&.name == "text"
|
|
200
203
|
end
|
|
201
204
|
end
|
|
202
205
|
|
|
@@ -101,8 +101,8 @@ class Prawn::SVG::Elements::Use < Prawn::SVG::Elements::Base
|
|
|
101
101
|
data = @document.url_loader.load(url)
|
|
102
102
|
|
|
103
103
|
root = begin
|
|
104
|
-
|
|
105
|
-
rescue REXML::ParseException
|
|
104
|
+
Prawn::SVG::Document.parse(data)
|
|
105
|
+
rescue REXML::ParseException, Prawn::SVG::Document::InvalidSVGData
|
|
106
106
|
raise SkipElementError, "use tag references external SVG at '#{url}' which could not be parsed"
|
|
107
107
|
end
|
|
108
108
|
|
data/lib/prawn/svg/url_loader.rb
CHANGED
|
@@ -29,7 +29,8 @@ class Prawn::SVG::UrlLoader
|
|
|
29
29
|
|
|
30
30
|
def perform_and_cache(url)
|
|
31
31
|
data = perform(url)
|
|
32
|
-
|
|
32
|
+
# Gamba: a data: URL decodes to the same bytes every time, and <use> can make a document load it thousands of times.
|
|
33
|
+
add_to_cache(url, data) if enable_cache || url[0, 5].casecmp?("data:")
|
|
33
34
|
data
|
|
34
35
|
end
|
|
35
36
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gamba
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Level UP Solutions
|
|
@@ -9,6 +9,26 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: addressable
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 2.9.1
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '3'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: 2.9.1
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '3'
|
|
12
32
|
- !ruby/object:Gem::Dependency
|
|
13
33
|
name: css_parser
|
|
14
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -63,9 +83,9 @@ dependencies:
|
|
|
63
83
|
- - "<"
|
|
64
84
|
- !ruby/object:Gem::Version
|
|
65
85
|
version: '4'
|
|
66
|
-
description: '
|
|
67
|
-
|
|
68
|
-
lighter files.'
|
|
86
|
+
description: 'Gamba is a Ruby library that writes PDF files. It is a faster rewrite
|
|
87
|
+
of Prawn 2.5, prawn-table and prawn-svg: code written for them runs unchanged and
|
|
88
|
+
draws the same pages, in lighter files.'
|
|
69
89
|
executables: []
|
|
70
90
|
extensions: []
|
|
71
91
|
extra_rdoc_files: []
|
|
@@ -259,5 +279,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
259
279
|
requirements: []
|
|
260
280
|
rubygems_version: 4.0.20
|
|
261
281
|
specification_version: 4
|
|
262
|
-
summary:
|
|
282
|
+
summary: A faster rewrite of Prawn, prawn-table and prawn-svg that draws the same
|
|
283
|
+
pages
|
|
263
284
|
test_files: []
|