stationery 0.3.0 → 0.4.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 +79 -0
- data/README.md +98 -4
- data/lib/stationery/canvas/debug.rb +2 -1
- data/lib/stationery/canvas/marking.rb +124 -0
- data/lib/stationery/canvas.rb +24 -8
- data/lib/stationery/document.rb +28 -10
- data/lib/stationery/elements/forms.rb +55 -0
- data/lib/stationery/elements/lists.rb +15 -5
- data/lib/stationery/elements.rb +10 -7
- data/lib/stationery/forms/acro_form.rb +115 -0
- data/lib/stationery/forms/appearance.rb +119 -0
- data/lib/stationery/forms/field.rb +122 -0
- data/lib/stationery/forms/metrics.rb +49 -0
- data/lib/stationery/layout/box.rb +11 -5
- data/lib/stationery/layout/field.rb +51 -0
- data/lib/stationery/layout/flow.rb +17 -9
- data/lib/stationery/layout/image.rb +4 -2
- data/lib/stationery/layout/list_item.rb +15 -6
- data/lib/stationery/layout/node.rb +2 -0
- data/lib/stationery/layout/paginator.rb +3 -2
- data/lib/stationery/layout/svg.rb +7 -3
- data/lib/stationery/layout/table/cell.rb +10 -2
- data/lib/stationery/layout/table.rb +42 -10
- data/lib/stationery/layout/table_of_contents/entry.rb +17 -9
- data/lib/stationery/layout/table_of_contents.rb +1 -1
- data/lib/stationery/layout/text.rb +4 -3
- data/lib/stationery/minitest.rb +2 -0
- data/lib/stationery/page.rb +5 -2
- data/lib/stationery/page_templates.rb +7 -4
- data/lib/stationery/pdf/assembler.rb +36 -4
- data/lib/stationery/rich/renderer.rb +3 -3
- data/lib/stationery/structure.rb +15 -9
- data/lib/stationery/tagging/element.rb +74 -0
- data/lib/stationery/tagging/tree.rb +43 -0
- data/lib/stationery/tagging/writer.rb +81 -0
- data/lib/stationery/testing/inspector.rb +15 -0
- data/lib/stationery/testing/marked_text.rb +60 -0
- data/lib/stationery/testing/matchers.rb +25 -0
- data/lib/stationery/testing/structure_reader.rb +71 -0
- data/lib/stationery/text/paragraph.rb +27 -12
- data/lib/stationery/version.rb +1 -1
- data/lib/stationery/warnings.rb +8 -0
- data/lib/stationery.rb +10 -0
- metadata +13 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stationery
|
|
4
|
+
module Tagging
|
|
5
|
+
# Writes a Tree as the catalog's /StructTreeRoot: one StructElem per
|
|
6
|
+
# element that holds content, and the /ParentTree mapping each page's
|
|
7
|
+
# /StructParents key to its elements by MCID.
|
|
8
|
+
class Writer
|
|
9
|
+
def initialize(tree, pages:, refs:)
|
|
10
|
+
@tree = tree
|
|
11
|
+
@page_refs = pages.zip(refs).to_h.compare_by_identity
|
|
12
|
+
@keys = pages.select { |page| tree.marked(page).any? }.each_with_index.to_h.compare_by_identity
|
|
13
|
+
@annotations = {}.compare_by_identity
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# An annotation's entries: the /StructParent key of its element, when it
|
|
17
|
+
# belongs to one. `ref` is the annotation's reference, for the /OBJR.
|
|
18
|
+
def annotation(link, ref)
|
|
19
|
+
return {} unless link[:tag]
|
|
20
|
+
|
|
21
|
+
@annotations[link] = [ref, @keys.size + @annotations.size]
|
|
22
|
+
{ StructParent: @annotations[link][1] }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The page dictionary's entries: its parent tree key, when it has one.
|
|
26
|
+
def page_entries(page)
|
|
27
|
+
key = @keys[page]
|
|
28
|
+
key ? { StructParents: key } : {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Writes the tree and returns the catalog entries pointing at it.
|
|
32
|
+
def write(writer)
|
|
33
|
+
root = writer.reserve
|
|
34
|
+
@refs = {}.compare_by_identity
|
|
35
|
+
reserve(writer, @tree.root)
|
|
36
|
+
@refs.each_key { |element| writer.set(@refs[element], dictionary(element, root)) }
|
|
37
|
+
writer.set(root, { Type: :StructTreeRoot, K: [@refs[@tree.root]], ParentTree: { Nums: parent_tree },
|
|
38
|
+
ParentTreeNextKey: @keys.size + @annotations.size })
|
|
39
|
+
{ MarkInfo: { Marked: true }, StructTreeRoot: root }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def reserve(writer, element)
|
|
45
|
+
@refs[element] = writer.reserve
|
|
46
|
+
element.elements.each { |kid| reserve(writer, kid) unless kid.empty? }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dictionary(element, root)
|
|
50
|
+
page = element.marked_content.first&.page
|
|
51
|
+
{
|
|
52
|
+
Type: :StructElem, S: element.type, P: element.parent ? @refs[element.parent] : root,
|
|
53
|
+
Pg: page && @page_refs[page], K: element.kids.filter_map { |kid| kid_entry(kid, page) },
|
|
54
|
+
Alt: element.alt && PDF::TextString.new(element.alt),
|
|
55
|
+
A: attributes(element)
|
|
56
|
+
}.compact
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def kid_entry(kid, page)
|
|
60
|
+
case kid
|
|
61
|
+
when Element then @refs[kid]
|
|
62
|
+
when MarkedContent
|
|
63
|
+
kid.page.equal?(page) ? kid.mcid : { Type: :MCR, Pg: @page_refs[kid.page], MCID: kid.mcid }
|
|
64
|
+
when ObjectRef
|
|
65
|
+
ref, = @annotations[kid.annotation]
|
|
66
|
+
ref && { Type: :OBJR, Obj: ref, Pg: @page_refs[kid.page] }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def attributes(element)
|
|
71
|
+
list = element.attributes.map { |owner, values| { O: owner, **values } }
|
|
72
|
+
list.size > 1 ? list : list.first
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parent_tree
|
|
76
|
+
@keys.flat_map { |page, key| [key, @tree.marked(page).map { |element| @refs[element] }] } +
|
|
77
|
+
@annotations.flat_map { |link, (_, key)| [key, @refs[link[:tag]]] }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "pathname"
|
|
4
4
|
require "stringio"
|
|
5
5
|
require "stationery"
|
|
6
|
+
require_relative "structure_reader"
|
|
6
7
|
|
|
7
8
|
module Stationery
|
|
8
9
|
module Testing
|
|
@@ -63,6 +64,20 @@ module Stationery
|
|
|
63
64
|
outlines ? titles(outlines[:First]) : []
|
|
64
65
|
end
|
|
65
66
|
|
|
67
|
+
# The structure tree of a tagged PDF as nested arrays; see StructureReader.
|
|
68
|
+
def structure = @structure ||= StructureReader.new(reader).tree
|
|
69
|
+
|
|
70
|
+
# Whether the catalog marks the PDF as tagged and holds a structure tree.
|
|
71
|
+
def tagged?
|
|
72
|
+
catalog = objects.deref!(objects.trailer[:Root])
|
|
73
|
+
catalog.dig(:MarkInfo, :Marked) == true && !catalog[:StructTreeRoot].nil?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Text shown outside any marked content (neither tagged nor an artifact).
|
|
77
|
+
def untagged_text
|
|
78
|
+
@untagged_text ||= reader.pages.flat_map { |page| MarkedText.read(page).unmarked }
|
|
79
|
+
end
|
|
80
|
+
|
|
66
81
|
private
|
|
67
82
|
|
|
68
83
|
def objects = reader.objects
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module Stationery
|
|
6
|
+
module Testing
|
|
7
|
+
# A pdf-reader receiver that collects a page's text by MCID, and the text
|
|
8
|
+
# shown outside any marked content. Text on a new baseline starts after
|
|
9
|
+
# a space, so a paragraph's lines read as one string.
|
|
10
|
+
class MarkedText
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
13
|
+
STATE = %i[save_graphics_state restore_graphics_state concatenate_matrix begin_text_object end_text_object
|
|
14
|
+
set_text_font_and_size move_text_position move_text_position_and_set_leading
|
|
15
|
+
set_text_matrix_and_text_line_matrix move_to_start_of_next_line set_text_leading
|
|
16
|
+
set_character_spacing set_word_spacing set_text_rise set_horizontal_text_scaling
|
|
17
|
+
set_text_rendering_mode].freeze
|
|
18
|
+
|
|
19
|
+
def_delegators :@state, *STATE
|
|
20
|
+
|
|
21
|
+
# { MCID => text } and the strings shown outside marked content.
|
|
22
|
+
attr_reader :texts, :unmarked
|
|
23
|
+
|
|
24
|
+
def self.read(page) = new(page).tap { |receiver| page.walk(receiver) }
|
|
25
|
+
|
|
26
|
+
def initialize(page)
|
|
27
|
+
@state = ::PDF::Reader::PageState.new(page)
|
|
28
|
+
@open = []
|
|
29
|
+
@texts = {}
|
|
30
|
+
@baselines = {}
|
|
31
|
+
@unmarked = []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def begin_marked_content_with_pl(_tag, properties)
|
|
35
|
+
@open << (properties[:MCID] if properties.is_a?(Hash))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def begin_marked_content(_tag) = @open << nil
|
|
39
|
+
def end_marked_content = @open.pop
|
|
40
|
+
def show_text(string) = append(string)
|
|
41
|
+
def show_text_with_positioning(params) = params.grep(String).each { |string| append(string) }
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def append(string)
|
|
46
|
+
font = @state.current_font
|
|
47
|
+
text = font.unpack(string).map { |code| font.to_utf8(code) }.join
|
|
48
|
+
return @unmarked << text if @open.empty?
|
|
49
|
+
|
|
50
|
+
mcid = @open.compact.last
|
|
51
|
+
return unless mcid
|
|
52
|
+
|
|
53
|
+
baseline = @state.trm_transform_point(0, 0).y.round(2)
|
|
54
|
+
separator = @texts.key?(mcid) && @baselines[mcid] != baseline ? " " : ""
|
|
55
|
+
@texts[mcid] = "#{@texts[mcid]}#{separator}#{text}"
|
|
56
|
+
@baselines[mcid] = baseline
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -111,6 +111,29 @@ module Stationery
|
|
|
111
111
|
def actual = messages.empty? ? "got none" : "got:\n#{messages.map { |m| " - #{m}" }.join("\n")}"
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
class HaveStructure < Base
|
|
115
|
+
def description = "have structure #{@expected.inspect}"
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def match?(pdf) = pdf.structure == @expected
|
|
120
|
+
def actual = "got #{@inspector.structure.inspect}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class HaveTaggedContent < Base
|
|
124
|
+
def description = "be tagged with every text in marked content"
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def match?(pdf) = pdf.tagged? && pdf.untagged_text.empty?
|
|
129
|
+
|
|
130
|
+
def actual
|
|
131
|
+
return "but it is not tagged" unless @inspector.tagged?
|
|
132
|
+
|
|
133
|
+
"got untagged text #{@inspector.untagged_text.inspect}"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
114
137
|
def have_pdf_text(expected) = HaveText.new(expected)
|
|
115
138
|
def have_pdf_text_on_page(page, expected) = HaveTextOnPage.new(page, expected)
|
|
116
139
|
def have_page_count(expected) = HavePageCount.new(expected)
|
|
@@ -118,6 +141,8 @@ module Stationery
|
|
|
118
141
|
def have_image_count(expected) = HaveImageCount.new(expected)
|
|
119
142
|
def have_bookmark(title) = HaveBookmark.new(title)
|
|
120
143
|
def have_no_warnings = HaveNoWarnings.new
|
|
144
|
+
def have_structure(expected) = HaveStructure.new(expected)
|
|
145
|
+
def have_tagged_content = HaveTaggedContent.new
|
|
121
146
|
end
|
|
122
147
|
end
|
|
123
148
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "marked_text"
|
|
4
|
+
|
|
5
|
+
module Stationery
|
|
6
|
+
module Testing
|
|
7
|
+
# Reads a tagged PDF's structure tree as nested arrays, each element's
|
|
8
|
+
# text taken from the marked content it owns:
|
|
9
|
+
#
|
|
10
|
+
# [[:Document, [[:H1, "Intro"], [:P, ["Read", [:Link, "the docs"], "now"]], [:Figure, "Logo"]]]]
|
|
11
|
+
#
|
|
12
|
+
# An element is [type, text] when it holds only text, [type, [kids]]
|
|
13
|
+
# when it holds elements (with its own text between them), [type] when
|
|
14
|
+
# empty. A Figure reads as its alt text. Untagged PDFs read as [].
|
|
15
|
+
class StructureReader
|
|
16
|
+
def initialize(reader)
|
|
17
|
+
@reader = reader
|
|
18
|
+
@objects = reader.objects
|
|
19
|
+
@texts = {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def tree
|
|
23
|
+
root = @objects.deref(@objects.deref(@objects.trailer[:Root])[:StructTreeRoot])
|
|
24
|
+
root ? kids_of(root).grep(Array) : []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def element(dictionary)
|
|
30
|
+
return [dictionary[:S], dictionary[:Alt]].compact if dictionary[:Alt]
|
|
31
|
+
|
|
32
|
+
kids = merge(kids_of(dictionary, dictionary[:Pg]))
|
|
33
|
+
return [dictionary[:S]] if kids.empty?
|
|
34
|
+
|
|
35
|
+
[dictionary[:S], kids.size == 1 && kids.first.is_a?(String) ? kids.first : kids]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def kids_of(dictionary, page = nil)
|
|
39
|
+
Array(@objects.deref(dictionary[:K])).filter_map do |kid|
|
|
40
|
+
kid = @objects.deref(kid)
|
|
41
|
+
case kid
|
|
42
|
+
when Integer then text(page, kid)
|
|
43
|
+
when Hash then kid[:Type] == :MCR ? text(kid[:Pg], kid[:MCID]) : kid_element(kid)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def kid_element(kid) = kid[:Type] == :OBJR ? nil : element(kid)
|
|
49
|
+
|
|
50
|
+
# Adjacent strings (one element's marked content on two lines or pages) join with a space.
|
|
51
|
+
def merge(kids)
|
|
52
|
+
kids.each_with_object([]) do |kid, merged|
|
|
53
|
+
next if kid.is_a?(String) && kid.strip.empty?
|
|
54
|
+
|
|
55
|
+
if kid.is_a?(String) && merged.last.is_a?(String)
|
|
56
|
+
merged[-1] = "#{merged.last} #{kid.strip}"
|
|
57
|
+
else
|
|
58
|
+
merged << (kid.is_a?(String) ? kid.strip : kid)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def text(page, mcid) = page_texts(page)[mcid]
|
|
64
|
+
|
|
65
|
+
def page_texts(page)
|
|
66
|
+
index = @objects.page_references.index(page)
|
|
67
|
+
@texts[index] ||= MarkedText.read(@reader.page(index + 1)).texts
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -43,17 +43,32 @@ module Stationery
|
|
|
43
43
|
shrink(max_height)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
# `tag` is the structure element the text belongs to in a tagged PDF;
|
|
47
|
+
# linked runs become Link elements inside it.
|
|
48
|
+
def draw(canvas, x, y, tag: nil)
|
|
49
|
+
links = Links.new
|
|
50
|
+
canvas.tag_runs(tag) do |mark|
|
|
51
|
+
top = y
|
|
52
|
+
@lines.each do |line|
|
|
53
|
+
offset = x + Geometry.align_offset(@align, @width, line.width)
|
|
54
|
+
extra = word_spacing(line)
|
|
55
|
+
spaces = 0
|
|
56
|
+
line.fragments.each do |fragment|
|
|
57
|
+
link = links.for(fragment.style.link)
|
|
58
|
+
mark.call(link) { draw_fragment(canvas, fragment, offset + (extra * spaces), top, line, extra, link:) }
|
|
59
|
+
spaces += fragment.text.count(" ")
|
|
60
|
+
end
|
|
61
|
+
top += line.height + @leading
|
|
55
62
|
end
|
|
56
|
-
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# One Link element per run of fragments with the same link target.
|
|
67
|
+
class Links
|
|
68
|
+
def for(target)
|
|
69
|
+
@element = nil unless target == @target
|
|
70
|
+
@target = target
|
|
71
|
+
@element = target && (@element || Tagging::Element.new(:Link))
|
|
57
72
|
end
|
|
58
73
|
end
|
|
59
74
|
|
|
@@ -94,7 +109,7 @@ module Stationery
|
|
|
94
109
|
[(@width - line.width) / line.space_count, 0].max
|
|
95
110
|
end
|
|
96
111
|
|
|
97
|
-
def draw_fragment(canvas, fragment, offset, top, line, word_spacing)
|
|
112
|
+
def draw_fragment(canvas, fragment, offset, top, line, word_spacing, link: nil)
|
|
98
113
|
style = fragment.style
|
|
99
114
|
count_missing(fragment)
|
|
100
115
|
x = offset + fragment.x
|
|
@@ -105,7 +120,7 @@ module Stationery
|
|
|
105
120
|
ligatures: style.ligatures,
|
|
106
121
|
synthetic_bold: fragment.face.synthetic_bold,
|
|
107
122
|
synthetic_oblique: fragment.face.synthetic_oblique, word_spacing:)
|
|
108
|
-
canvas.link(x, top, width, line.height, style.link) if style.link
|
|
123
|
+
canvas.link(x, top, width, line.height, style.link, tag: link) if style.link
|
|
109
124
|
end
|
|
110
125
|
|
|
111
126
|
def count_missing(fragment)
|
data/lib/stationery/version.rb
CHANGED
data/lib/stationery/warnings.rb
CHANGED
|
@@ -37,6 +37,14 @@ module Stationery
|
|
|
37
37
|
def message = %(anchor "#{name}" on page #{page} is already defined)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
MissingAlt = Data.define(:kind, :page) do
|
|
41
|
+
def message = "#{kind} on page #{page} has no alt: text"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
MissingLanguage = Data.define do
|
|
45
|
+
def message = "tagged PDF has no language: set metadata lang:"
|
|
46
|
+
end
|
|
47
|
+
|
|
40
48
|
def initialize
|
|
41
49
|
@items = []
|
|
42
50
|
@glyphs = Hash.new(0)
|
data/lib/stationery.rb
CHANGED
|
@@ -49,11 +49,19 @@ require_relative "stationery/color"
|
|
|
49
49
|
require_relative "stationery/geometry"
|
|
50
50
|
require_relative "stationery/page"
|
|
51
51
|
require_relative "stationery/resources"
|
|
52
|
+
require_relative "stationery/tagging/element"
|
|
53
|
+
require_relative "stationery/tagging/tree"
|
|
54
|
+
require_relative "stationery/tagging/writer"
|
|
52
55
|
require_relative "stationery/canvas/path"
|
|
53
56
|
require_relative "stationery/canvas/text"
|
|
54
57
|
require_relative "stationery/canvas/debug"
|
|
58
|
+
require_relative "stationery/canvas/marking"
|
|
55
59
|
require_relative "stationery/canvas"
|
|
56
60
|
require_relative "stationery/pdf/outline_writer"
|
|
61
|
+
require_relative "stationery/forms/metrics"
|
|
62
|
+
require_relative "stationery/forms/field"
|
|
63
|
+
require_relative "stationery/forms/appearance"
|
|
64
|
+
require_relative "stationery/forms/acro_form"
|
|
57
65
|
require_relative "stationery/pdf/assembler"
|
|
58
66
|
require_relative "stationery/text/style"
|
|
59
67
|
require_relative "stationery/fonts/fallback"
|
|
@@ -99,6 +107,7 @@ require_relative "stationery/svg/document"
|
|
|
99
107
|
require_relative "stationery/layout/svg"
|
|
100
108
|
require_relative "stationery/layout/wrap"
|
|
101
109
|
require_relative "stationery/layout/positioned"
|
|
110
|
+
require_relative "stationery/layout/field"
|
|
102
111
|
require_relative "stationery/layout/list_item"
|
|
103
112
|
require_relative "stationery/list_markers"
|
|
104
113
|
require_relative "stationery/outline"
|
|
@@ -106,6 +115,7 @@ require_relative "stationery/builder"
|
|
|
106
115
|
require_relative "stationery/elements"
|
|
107
116
|
require_relative "stationery/elements/lists"
|
|
108
117
|
require_relative "stationery/elements/rich"
|
|
118
|
+
require_relative "stationery/elements/forms"
|
|
109
119
|
require_relative "stationery/component"
|
|
110
120
|
require_relative "stationery/regions"
|
|
111
121
|
require_relative "stationery/page_templates"
|
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.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -32,6 +32,7 @@ files:
|
|
|
32
32
|
- lib/stationery/builder.rb
|
|
33
33
|
- lib/stationery/canvas.rb
|
|
34
34
|
- lib/stationery/canvas/debug.rb
|
|
35
|
+
- lib/stationery/canvas/marking.rb
|
|
35
36
|
- lib/stationery/canvas/path.rb
|
|
36
37
|
- lib/stationery/canvas/text.rb
|
|
37
38
|
- lib/stationery/cli.rb
|
|
@@ -41,6 +42,7 @@ files:
|
|
|
41
42
|
- lib/stationery/component.rb
|
|
42
43
|
- lib/stationery/document.rb
|
|
43
44
|
- lib/stationery/elements.rb
|
|
45
|
+
- lib/stationery/elements/forms.rb
|
|
44
46
|
- lib/stationery/elements/lists.rb
|
|
45
47
|
- lib/stationery/elements/rich.rb
|
|
46
48
|
- lib/stationery/errors.rb
|
|
@@ -81,6 +83,10 @@ files:
|
|
|
81
83
|
- lib/stationery/fonts/to_unicode.rb
|
|
82
84
|
- lib/stationery/fonts/true_type.rb
|
|
83
85
|
- lib/stationery/fonts/woff.rb
|
|
86
|
+
- lib/stationery/forms/acro_form.rb
|
|
87
|
+
- lib/stationery/forms/appearance.rb
|
|
88
|
+
- lib/stationery/forms/field.rb
|
|
89
|
+
- lib/stationery/forms/metrics.rb
|
|
84
90
|
- lib/stationery/geometry.rb
|
|
85
91
|
- lib/stationery/html/document.rb
|
|
86
92
|
- lib/stationery/html/tokenizer.rb
|
|
@@ -94,6 +100,7 @@ files:
|
|
|
94
100
|
- lib/stationery/images/png.rb
|
|
95
101
|
- lib/stationery/images/scanlines.rb
|
|
96
102
|
- lib/stationery/layout/box.rb
|
|
103
|
+
- lib/stationery/layout/field.rb
|
|
97
104
|
- lib/stationery/layout/flow.rb
|
|
98
105
|
- lib/stationery/layout/image.rb
|
|
99
106
|
- lib/stationery/layout/leaves.rb
|
|
@@ -164,8 +171,13 @@ files:
|
|
|
164
171
|
- lib/stationery/svg/stylesheet.rb
|
|
165
172
|
- lib/stationery/svg/text.rb
|
|
166
173
|
- lib/stationery/svg/transform.rb
|
|
174
|
+
- lib/stationery/tagging/element.rb
|
|
175
|
+
- lib/stationery/tagging/tree.rb
|
|
176
|
+
- lib/stationery/tagging/writer.rb
|
|
167
177
|
- lib/stationery/testing/inspector.rb
|
|
178
|
+
- lib/stationery/testing/marked_text.rb
|
|
168
179
|
- lib/stationery/testing/matchers.rb
|
|
180
|
+
- lib/stationery/testing/structure_reader.rb
|
|
169
181
|
- lib/stationery/text/entities.rb
|
|
170
182
|
- lib/stationery/text/entities/html4.rb
|
|
171
183
|
- lib/stationery/text/line.rb
|