prawn-accessibility 1.0.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 +7 -0
- data/CHANGELOG.md +27 -0
- data/COPYING +5 -0
- data/GPLv2 +339 -0
- data/GPLv3 +674 -0
- data/LICENSE +58 -0
- data/README.md +89 -0
- data/lib/prawn/accessibility/document.rb +153 -0
- data/lib/prawn/accessibility/marked_content.rb +60 -0
- data/lib/prawn/accessibility/pdf_core.rb +79 -0
- data/lib/prawn/accessibility/structure_tree.rb +275 -0
- data/lib/prawn/accessibility/table.rb +144 -0
- data/lib/prawn/accessibility/version.rb +8 -0
- data/lib/prawn/accessibility.rb +14 -0
- data/lib/prawn-accessibility.rb +4 -0
- metadata +97 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PDF
|
|
4
|
+
module Core
|
|
5
|
+
# Manages the PDF structure tree for tagged/accessible PDFs.
|
|
6
|
+
#
|
|
7
|
+
# The structure tree provides the logical structure of a document,
|
|
8
|
+
# mapping marked content sequences in page content streams to
|
|
9
|
+
# structure elements (headings, paragraphs, tables, etc.).
|
|
10
|
+
#
|
|
11
|
+
# PDF spec references: Section 14.7 (Logical Structure)
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class StructureTree
|
|
15
|
+
# @return [PDF::Core::Renderer] owning renderer
|
|
16
|
+
attr_reader :renderer
|
|
17
|
+
|
|
18
|
+
# @return [PDF::Core::Reference] StructTreeRoot indirect object
|
|
19
|
+
attr_reader :root_ref
|
|
20
|
+
|
|
21
|
+
# @return [PDF::Core::Reference] Document-level structure element
|
|
22
|
+
attr_reader :document_elem_ref
|
|
23
|
+
|
|
24
|
+
# @return [Array<Hash>] all structure elements created
|
|
25
|
+
attr_reader :elements
|
|
26
|
+
|
|
27
|
+
# @return [Hash{Integer => Array}] page StructParents index => array of
|
|
28
|
+
# structure element refs for marked content on that page
|
|
29
|
+
attr_reader :parent_tree_map
|
|
30
|
+
|
|
31
|
+
# @return [Integer] next available MCID for the current page
|
|
32
|
+
attr_reader :next_mcid
|
|
33
|
+
|
|
34
|
+
# @return [Array<PDF::Core::Reference>] stack of open structure elements
|
|
35
|
+
attr_reader :element_stack
|
|
36
|
+
|
|
37
|
+
# @param renderer [PDF::Core::Renderer]
|
|
38
|
+
def initialize(renderer)
|
|
39
|
+
@renderer = renderer
|
|
40
|
+
@elements = []
|
|
41
|
+
@parent_tree_map = {}
|
|
42
|
+
@next_mcid = 0
|
|
43
|
+
@element_stack = []
|
|
44
|
+
@page_mcid_map = {} # page_ref_id => next mcid for that page
|
|
45
|
+
@root_ref = nil
|
|
46
|
+
@document_elem_ref = nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Allocate the next MCID for the current page and track it.
|
|
50
|
+
#
|
|
51
|
+
# @return [Integer] the allocated MCID
|
|
52
|
+
def allocate_mcid
|
|
53
|
+
page = renderer.state.page
|
|
54
|
+
page_id = page.dictionary.identifier
|
|
55
|
+
|
|
56
|
+
@page_mcid_map[page_id] ||= 0
|
|
57
|
+
mcid = @page_mcid_map[page_id]
|
|
58
|
+
@page_mcid_map[page_id] += 1
|
|
59
|
+
|
|
60
|
+
mcid
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Add a structure element as a child of the current open element
|
|
64
|
+
# (or the document element if none is open).
|
|
65
|
+
#
|
|
66
|
+
# @param tag [Symbol] structure type (e.g., :P, :H1, :Table, :TD)
|
|
67
|
+
# @param attributes [Hash] additional attributes for the structure element
|
|
68
|
+
# @option attributes [String] :Alt alternative text (for Figure, Formula)
|
|
69
|
+
# @option attributes [String] :ActualText replacement text for screen
|
|
70
|
+
# readers (e.g., "required" instead of reading "*")
|
|
71
|
+
# @option attributes [String] :Lang language tag
|
|
72
|
+
# @option attributes [Symbol] :Scope TH scope (:Column, :Row, :Both)
|
|
73
|
+
# @return [PDF::Core::Reference] the structure element reference
|
|
74
|
+
def add_element(tag, attributes = {})
|
|
75
|
+
parent_ref = current_element || document_element
|
|
76
|
+
|
|
77
|
+
elem_data = {
|
|
78
|
+
Type: :StructElem,
|
|
79
|
+
S: tag,
|
|
80
|
+
P: parent_ref,
|
|
81
|
+
K: [],
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
elem_data[:Alt] = attributes[:Alt] if attributes[:Alt]
|
|
85
|
+
elem_data[:ActualText] = attributes[:ActualText] if attributes[:ActualText]
|
|
86
|
+
elem_data[:Lang] = attributes[:Lang] if attributes[:Lang]
|
|
87
|
+
|
|
88
|
+
if attributes[:Scope]
|
|
89
|
+
elem_data[:A] = {
|
|
90
|
+
O: :Table,
|
|
91
|
+
Scope: attributes[:Scope],
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
elem_ref = renderer.ref!(elem_data)
|
|
96
|
+
@elements << elem_ref
|
|
97
|
+
|
|
98
|
+
# Add as child of parent
|
|
99
|
+
parent_data = renderer.deref(parent_ref)
|
|
100
|
+
parent_data[:K] << elem_ref
|
|
101
|
+
|
|
102
|
+
elem_ref
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Begin a structure element scope. Content rendered inside will be
|
|
106
|
+
# children of this element.
|
|
107
|
+
#
|
|
108
|
+
# @param tag [Symbol] structure type
|
|
109
|
+
# @param attributes [Hash] additional attributes
|
|
110
|
+
# @return [PDF::Core::Reference] the opened structure element
|
|
111
|
+
def begin_element(tag, attributes = {})
|
|
112
|
+
elem_ref = add_element(tag, attributes)
|
|
113
|
+
@element_stack.push(elem_ref)
|
|
114
|
+
elem_ref
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# End the current structure element scope.
|
|
118
|
+
#
|
|
119
|
+
# @return [PDF::Core::Reference] the closed structure element
|
|
120
|
+
def end_element
|
|
121
|
+
@element_stack.pop
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Add marked content to the current structure element.
|
|
125
|
+
# This allocates an MCID, records the mapping, and emits BDC/EMC
|
|
126
|
+
# operators around the yielded block.
|
|
127
|
+
#
|
|
128
|
+
# @param tag [Symbol] marked content tag (e.g., :P, :Span)
|
|
129
|
+
# @param struct_elem_ref [PDF::Core::Reference, nil] the structure element
|
|
130
|
+
# this content belongs to. If nil, uses current_element.
|
|
131
|
+
# @yield content to render inside the marked content sequence
|
|
132
|
+
# @return [void]
|
|
133
|
+
def mark_content(tag, struct_elem_ref: nil)
|
|
134
|
+
elem_ref = struct_elem_ref || current_element || document_element
|
|
135
|
+
mcid = allocate_mcid
|
|
136
|
+
page = renderer.state.page
|
|
137
|
+
page_ref = page.dictionary
|
|
138
|
+
|
|
139
|
+
# Record in parent tree map
|
|
140
|
+
page_struct_parents = page_struct_parents_index(page_ref)
|
|
141
|
+
@parent_tree_map[page_struct_parents] ||= []
|
|
142
|
+
|
|
143
|
+
# Add marked content reference to the structure element's K array
|
|
144
|
+
mcr = { Type: :MCR, MCID: mcid, Pg: page_ref }
|
|
145
|
+
elem_data = renderer.deref(elem_ref)
|
|
146
|
+
elem_data[:K] << mcr
|
|
147
|
+
|
|
148
|
+
# Track which struct element owns this MCID on this page
|
|
149
|
+
@parent_tree_map[page_struct_parents][mcid] = elem_ref
|
|
150
|
+
|
|
151
|
+
# Emit BDC/EMC in content stream
|
|
152
|
+
renderer.begin_marked_content_with_properties(tag, { MCID: mcid })
|
|
153
|
+
yield if block_given?
|
|
154
|
+
renderer.end_marked_content
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Mark content as an artifact (decorative, not read by screen readers).
|
|
158
|
+
#
|
|
159
|
+
# @param artifact_type [Symbol, nil] optional artifact type
|
|
160
|
+
# (:Pagination, :Layout, :Page, :Background)
|
|
161
|
+
# @yield content to render as artifact
|
|
162
|
+
# @return [void]
|
|
163
|
+
def mark_artifact(artifact_type: nil)
|
|
164
|
+
if artifact_type
|
|
165
|
+
renderer.begin_marked_content_with_properties(
|
|
166
|
+
:Artifact, { Type: artifact_type },
|
|
167
|
+
)
|
|
168
|
+
else
|
|
169
|
+
renderer.begin_marked_content(:Artifact)
|
|
170
|
+
end
|
|
171
|
+
yield if block_given?
|
|
172
|
+
renderer.end_marked_content
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Finalize the structure tree before rendering. Called via
|
|
176
|
+
# before_render callback.
|
|
177
|
+
#
|
|
178
|
+
# Builds the StructTreeRoot, ParentTree, and wires everything
|
|
179
|
+
# into the Catalog.
|
|
180
|
+
#
|
|
181
|
+
# @return [void]
|
|
182
|
+
def finalize!
|
|
183
|
+
return if @elements.empty? && @parent_tree_map.empty?
|
|
184
|
+
|
|
185
|
+
build_root
|
|
186
|
+
build_parent_tree
|
|
187
|
+
assign_struct_parents_to_pages
|
|
188
|
+
attach_to_catalog
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
# The current open structure element, or nil if none.
|
|
194
|
+
#
|
|
195
|
+
# @return [PDF::Core::Reference, nil]
|
|
196
|
+
def current_element
|
|
197
|
+
@element_stack.last
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Get or create the Document-level structure element.
|
|
201
|
+
#
|
|
202
|
+
# @return [PDF::Core::Reference]
|
|
203
|
+
def document_element
|
|
204
|
+
return @document_elem_ref if @document_elem_ref
|
|
205
|
+
|
|
206
|
+
@document_elem_ref = renderer.ref!(
|
|
207
|
+
Type: :StructElem,
|
|
208
|
+
S: :Document,
|
|
209
|
+
P: nil, # will be set to StructTreeRoot in finalize!
|
|
210
|
+
K: [],
|
|
211
|
+
)
|
|
212
|
+
@elements << @document_elem_ref
|
|
213
|
+
@document_elem_ref
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Get or assign a StructParents index for a page.
|
|
217
|
+
#
|
|
218
|
+
# @param page_ref [PDF::Core::Reference] page dictionary reference
|
|
219
|
+
# @return [Integer] the StructParents index
|
|
220
|
+
def page_struct_parents_index(page_ref)
|
|
221
|
+
page_ref.data[:StructParents] ||= @parent_tree_map.size
|
|
222
|
+
page_ref.data[:StructParents]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Build the StructTreeRoot object.
|
|
226
|
+
#
|
|
227
|
+
# @return [void]
|
|
228
|
+
def build_root
|
|
229
|
+
@root_ref = renderer.ref!(
|
|
230
|
+
Type: :StructTreeRoot,
|
|
231
|
+
K: document_element,
|
|
232
|
+
ParentTree: nil, # set in build_parent_tree
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Point Document element's parent to the root
|
|
236
|
+
doc_data = renderer.deref(@document_elem_ref)
|
|
237
|
+
doc_data[:P] = @root_ref
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Build the ParentTree (a number tree mapping StructParents indices
|
|
241
|
+
# to arrays of structure element references).
|
|
242
|
+
#
|
|
243
|
+
# @return [void]
|
|
244
|
+
def build_parent_tree
|
|
245
|
+
# ParentTree is a number tree. For simplicity, use a flat Nums array
|
|
246
|
+
# since most documents won't have enough pages to need a balanced tree.
|
|
247
|
+
nums = []
|
|
248
|
+
@parent_tree_map.sort_by { |k, _| k }.each do |index, elem_array|
|
|
249
|
+
nums << index
|
|
250
|
+
nums << renderer.ref!(elem_array)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
parent_tree_ref = renderer.ref!(Type: :ParentTree, Nums: nums)
|
|
254
|
+
root_data = renderer.deref(@root_ref)
|
|
255
|
+
root_data[:ParentTree] = parent_tree_ref
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Ensure each page that has marked content has a StructParents entry.
|
|
259
|
+
# (Already handled lazily in page_struct_parents_index, but verify.)
|
|
260
|
+
#
|
|
261
|
+
# @return [void]
|
|
262
|
+
def assign_struct_parents_to_pages
|
|
263
|
+
# Already assigned lazily when mark_content is called.
|
|
264
|
+
# This method exists as a hook for any additional finalization.
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Wire the StructTreeRoot into the document Catalog.
|
|
268
|
+
#
|
|
269
|
+
# @return [void]
|
|
270
|
+
def attach_to_catalog
|
|
271
|
+
renderer.state.store.root.data[:StructTreeRoot] = @root_ref
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Table accessibility patches. This file is only loaded when Prawn::Table is
|
|
4
|
+
# already defined (see prawn/accessibility.rb), so requiring prawn-accessibility
|
|
5
|
+
# without prawn-table installed is safe.
|
|
6
|
+
|
|
7
|
+
unless defined?(Prawn::Table)
|
|
8
|
+
raise 'prawn/accessibility/table requires prawn-table to be loaded first'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Prawn
|
|
12
|
+
module Accessibility
|
|
13
|
+
# Patches applied to the published +prawn-table+ gem so that tables emit
|
|
14
|
+
# +<Table>+/+<TR>+/+<TH>+/+<TD>+ structure elements when the owning document
|
|
15
|
+
# is tagged. Untagged tables render exactly as upstream (via +super+).
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
module TablePatch
|
|
19
|
+
# Prepended onto {Prawn::Table#initialize} to flag header cells after
|
|
20
|
+
# the table has been built and positioned.
|
|
21
|
+
#
|
|
22
|
+
# @api private
|
|
23
|
+
module TableInstancePatch
|
|
24
|
+
def initialize(*args, &block)
|
|
25
|
+
super
|
|
26
|
+
mark_header_cells
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Draws the table, wrapping it in a +<Table>+ structure element when
|
|
30
|
+
# the document is tagged.
|
|
31
|
+
def draw
|
|
32
|
+
if @pdf.respond_to?(:tagged?) && @pdf.tagged?
|
|
33
|
+
@pdf.structure_container(:Table) { super() }
|
|
34
|
+
else
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Marks cells in header rows as header cells for accessibility.
|
|
40
|
+
#
|
|
41
|
+
# @return [void]
|
|
42
|
+
def mark_header_cells
|
|
43
|
+
n = number_of_header_rows
|
|
44
|
+
return if n.zero?
|
|
45
|
+
|
|
46
|
+
@cells.each do |cell|
|
|
47
|
+
cell.is_header_cell = true if cell.row < n
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Prepended onto the {Prawn::Table::Cell} singleton so
|
|
53
|
+
# <tt>Cell.draw_cells</tt> emits row/cell structure tags in tagged mode.
|
|
54
|
+
#
|
|
55
|
+
# @api private
|
|
56
|
+
module CellClassPatch
|
|
57
|
+
def draw_cells(cells)
|
|
58
|
+
return super if cells.empty?
|
|
59
|
+
|
|
60
|
+
first_entry = cells.first
|
|
61
|
+
first_cell = first_entry.is_a?(Array) ? first_entry[0] : first_entry
|
|
62
|
+
pdf = first_cell.instance_variable_get(:@pdf)
|
|
63
|
+
tagged = pdf.respond_to?(:tagged?) && pdf.tagged?
|
|
64
|
+
|
|
65
|
+
return super unless tagged
|
|
66
|
+
|
|
67
|
+
# Phase 1: backgrounds (decorative — artifact in tagged mode)
|
|
68
|
+
pdf.artifact(type: :Layout) do
|
|
69
|
+
cells.each do |cell, pt|
|
|
70
|
+
cell.set_width_constraints
|
|
71
|
+
cell.draw_background(pt)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Phase 2: borders and content, wrapped in TR/TH/TD structure
|
|
76
|
+
draw_cells_tagged(cells, pdf)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Draw cells with accessibility structure tags (TR, TH, TD).
|
|
80
|
+
#
|
|
81
|
+
# @api private
|
|
82
|
+
def draw_cells_tagged(cells, pdf)
|
|
83
|
+
# Group cells by row for TR wrapping
|
|
84
|
+
rows = cells.group_by { |cell, _pt| cell.row }
|
|
85
|
+
|
|
86
|
+
rows.sort_by { |row_num, _| row_num }.each do |_row_num, row_cells|
|
|
87
|
+
pdf.structure_container(:TR) do
|
|
88
|
+
row_cells.each do |cell, pt|
|
|
89
|
+
# Skip span dummy cells — the master cell handles drawing
|
|
90
|
+
next if cell.is_a?(Prawn::Table::Cell::SpanDummy)
|
|
91
|
+
|
|
92
|
+
# Borders are decorative
|
|
93
|
+
pdf.artifact(type: :Layout) { cell.draw_borders(pt) }
|
|
94
|
+
|
|
95
|
+
# Content gets TH or TD tag
|
|
96
|
+
tag = cell.header? ? :TH : :TD
|
|
97
|
+
attrs = {}
|
|
98
|
+
attrs[:Scope] = :Column if tag == :TH
|
|
99
|
+
|
|
100
|
+
pdf.structure(tag, attrs) do
|
|
101
|
+
cell.draw_content_only(pt)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# --- Additive re-open of Prawn::Table::Cell ---------------------------------
|
|
113
|
+
|
|
114
|
+
module Prawn
|
|
115
|
+
class Table
|
|
116
|
+
class Cell
|
|
117
|
+
# Whether this cell is a header cell (TH) for accessibility. Set
|
|
118
|
+
# automatically for cells in header rows when the table has
|
|
119
|
+
# <tt>header: true</tt>.
|
|
120
|
+
attr_accessor :is_header_cell
|
|
121
|
+
|
|
122
|
+
# Whether this cell is a header cell.
|
|
123
|
+
#
|
|
124
|
+
# @return [Boolean]
|
|
125
|
+
def header?
|
|
126
|
+
!!@is_header_cell
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Draws only the cell content (no borders or background). Used by the
|
|
130
|
+
# tagged PDF rendering path where borders are drawn separately as
|
|
131
|
+
# artifacts.
|
|
132
|
+
#
|
|
133
|
+
# @api private
|
|
134
|
+
def draw_content_only(pt)
|
|
135
|
+
draw_bounded_content(pt)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
Prawn::Table.prepend(Prawn::Accessibility::TablePatch::TableInstancePatch)
|
|
142
|
+
Prawn::Table::Cell.singleton_class.prepend(
|
|
143
|
+
Prawn::Accessibility::TablePatch::CellClassPatch,
|
|
144
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'accessibility/version'
|
|
4
|
+
require_relative 'accessibility/document'
|
|
5
|
+
|
|
6
|
+
# prawn-table support is optional. If the gem is installed, load it and apply
|
|
7
|
+
# the table tagging patches; otherwise carry on without table support.
|
|
8
|
+
begin
|
|
9
|
+
require 'prawn/table'
|
|
10
|
+
rescue LoadError
|
|
11
|
+
# prawn-table is not available; table tagging is disabled.
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require_relative 'accessibility/table' if defined?(Prawn::Table)
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: prawn-accessibility
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mes Amis
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: pdf-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.10'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.10'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: prawn
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.5'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.5'
|
|
40
|
+
description: |
|
|
41
|
+
prawn-accessibility layers tagged-PDF support onto the published prawn,
|
|
42
|
+
pdf-core, and (optionally) prawn-table gems. It adds a high-level API for
|
|
43
|
+
marking document structure — headings, paragraphs, figures, tables, and
|
|
44
|
+
artifacts — so Prawn can produce Section 508 / WCAG accessible PDFs.
|
|
45
|
+
|
|
46
|
+
The behavior originates from the stalled upstream pull requests
|
|
47
|
+
prawnpdf/pdf-core#67, prawnpdf/prawn#1391, and prawnpdf/prawn-table#164,
|
|
48
|
+
repackaged here as a standalone gem that patches the released libraries via
|
|
49
|
+
`prepend`/additive re-opens rather than forking them.
|
|
50
|
+
email:
|
|
51
|
+
- craig@monami.io
|
|
52
|
+
executables: []
|
|
53
|
+
extensions: []
|
|
54
|
+
extra_rdoc_files: []
|
|
55
|
+
files:
|
|
56
|
+
- CHANGELOG.md
|
|
57
|
+
- COPYING
|
|
58
|
+
- GPLv2
|
|
59
|
+
- GPLv3
|
|
60
|
+
- LICENSE
|
|
61
|
+
- README.md
|
|
62
|
+
- lib/prawn-accessibility.rb
|
|
63
|
+
- lib/prawn/accessibility.rb
|
|
64
|
+
- lib/prawn/accessibility/document.rb
|
|
65
|
+
- lib/prawn/accessibility/marked_content.rb
|
|
66
|
+
- lib/prawn/accessibility/pdf_core.rb
|
|
67
|
+
- lib/prawn/accessibility/structure_tree.rb
|
|
68
|
+
- lib/prawn/accessibility/table.rb
|
|
69
|
+
- lib/prawn/accessibility/version.rb
|
|
70
|
+
homepage: https://github.com/mes-amis/prawn-accessibility
|
|
71
|
+
licenses:
|
|
72
|
+
- Nonstandard
|
|
73
|
+
- GPL-2.0-only
|
|
74
|
+
- GPL-3.0-only
|
|
75
|
+
metadata:
|
|
76
|
+
homepage_uri: https://github.com/mes-amis/prawn-accessibility
|
|
77
|
+
source_code_uri: https://github.com/mes-amis/prawn-accessibility
|
|
78
|
+
changelog_uri: https://github.com/mes-amis/prawn-accessibility/blob/main/CHANGELOG.md
|
|
79
|
+
rubygems_mfa_required: 'true'
|
|
80
|
+
rdoc_options: []
|
|
81
|
+
require_paths:
|
|
82
|
+
- lib
|
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '3.0'
|
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '0'
|
|
93
|
+
requirements: []
|
|
94
|
+
rubygems_version: 3.6.9
|
|
95
|
+
specification_version: 4
|
|
96
|
+
summary: Tagged PDF (Section 508 / WCAG) accessibility for Prawn.
|
|
97
|
+
test_files: []
|