blitz-pdf 0.1.1 → 0.1.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.
- package/README.md +266 -12
- package/THIRD_PARTY_NOTICES.md +4 -2
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/document-options.d.ts +36 -0
- package/dist/document-options.d.ts.map +1 -0
- package/dist/document-options.js +236 -0
- package/dist/document-options.js.map +1 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/options.d.ts +35 -1
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +21 -14
- package/dist/options.js.map +1 -1
- package/dist/types.d.ts +71 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +2 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +7 -0
- package/dist/validation.js.map +1 -0
- package/examples/zugferd.mjs +41 -0
- package/licenses/RUST_DEPENDENCIES.txt +27 -0
- package/package.json +3 -2
- package/src/browser.ts +1 -1
- package/src/document-options.ts +218 -0
- package/src/node.ts +1 -1
- package/src/options.ts +18 -13
- package/src/types.ts +76 -1
- package/src/validation.ts +6 -0
- package/vendor/README.md +27 -2
- package/vendor/krilla/.cargo-ok +1 -0
- package/vendor/krilla/.cargo_vcs_info.json +6 -0
- package/vendor/krilla/Cargo.lock +1357 -0
- package/vendor/krilla/Cargo.toml +165 -0
- package/vendor/krilla/LICENSE_APACHE +202 -0
- package/vendor/krilla/LICENSE_MIT +21 -0
- package/vendor/krilla/README.md +156 -0
- package/vendor/krilla/examples/basic.rs +117 -0
- package/vendor/krilla/examples/empty_document.rs +25 -0
- package/vendor/krilla/examples/parley.rs +174 -0
- package/vendor/krilla/examples/simple_text.rs +118 -0
- package/vendor/krilla/examples/stream_builder.rs +93 -0
- package/vendor/krilla/icc/LICENSE +116 -0
- package/vendor/krilla/icc/README.md +1 -0
- package/vendor/krilla/icc/sGrey-v2-magic.icc +0 -0
- package/vendor/krilla/icc/sGrey-v4.icc +0 -0
- package/vendor/krilla/icc/sRGB-v2-magic.icc +0 -0
- package/vendor/krilla/icc/sRGB-v4.icc +0 -0
- package/vendor/krilla/src/chunk_container.rs +503 -0
- package/vendor/krilla/src/configure/PDF_A1.md +227 -0
- package/vendor/krilla/src/configure/PDF_A2.md +269 -0
- package/vendor/krilla/src/configure/PDF_A3.md +31 -0
- package/vendor/krilla/src/configure/PDF_A4.md +211 -0
- package/vendor/krilla/src/configure/PDF_UA1.md +202 -0
- package/vendor/krilla/src/configure/README.md +23 -0
- package/vendor/krilla/src/configure/mod.rs +197 -0
- package/vendor/krilla/src/configure/validate.rs +1244 -0
- package/vendor/krilla/src/configure/version.rs +134 -0
- package/vendor/krilla/src/content.rs +1352 -0
- package/vendor/krilla/src/data.rs +49 -0
- package/vendor/krilla/src/document.rs +156 -0
- package/vendor/krilla/src/error.rs +170 -0
- package/vendor/krilla/src/geom.rs +341 -0
- package/vendor/krilla/src/graphics/blend.rs +46 -0
- package/vendor/krilla/src/graphics/color.rs +552 -0
- package/vendor/krilla/src/graphics/graphic.rs +30 -0
- package/vendor/krilla/src/graphics/graphics_state.rs +251 -0
- package/vendor/krilla/src/graphics/icc.rs +218 -0
- package/vendor/krilla/src/graphics/image.rs +835 -0
- package/vendor/krilla/src/graphics/mask.rs +128 -0
- package/vendor/krilla/src/graphics/mod.rs +14 -0
- package/vendor/krilla/src/graphics/paint.rs +434 -0
- package/vendor/krilla/src/graphics/separation.rs +86 -0
- package/vendor/krilla/src/graphics/shading_function.rs +865 -0
- package/vendor/krilla/src/graphics/shading_pattern.rs +60 -0
- package/vendor/krilla/src/graphics/tiling_pattern.rs +119 -0
- package/vendor/krilla/src/graphics/xobject.rs +150 -0
- package/vendor/krilla/src/interactive/action.rs +68 -0
- package/vendor/krilla/src/interactive/annotation.rs +283 -0
- package/vendor/krilla/src/interactive/destination.rs +289 -0
- package/vendor/krilla/src/interactive/mod.rs +3 -0
- package/vendor/krilla/src/interchange/embed.rs +240 -0
- package/vendor/krilla/src/interchange/metadata.rs +564 -0
- package/vendor/krilla/src/interchange/mod.rs +4 -0
- package/vendor/krilla/src/interchange/outline.rs +263 -0
- package/vendor/krilla/src/interchange/tagging/fmt.rs +733 -0
- package/vendor/krilla/src/interchange/tagging/generate.toml +474 -0
- package/vendor/krilla/src/interchange/tagging/generated.rs +4754 -0
- package/vendor/krilla/src/interchange/tagging/mod.rs +1324 -0
- package/vendor/krilla/src/interchange/tagging/tag.rs +686 -0
- package/vendor/krilla/src/lib.rs +179 -0
- package/vendor/krilla/src/num.rs +26 -0
- package/vendor/krilla/src/page.rs +612 -0
- package/vendor/krilla/src/pdf.rs +313 -0
- package/vendor/krilla/src/resource.rs +436 -0
- package/vendor/krilla/src/serialize.rs +1101 -0
- package/vendor/krilla/src/stream.rs +368 -0
- package/vendor/krilla/src/surface.rs +664 -0
- package/vendor/krilla/src/text/cid.rs +488 -0
- package/vendor/krilla/src/text/font.rs +351 -0
- package/vendor/krilla/src/text/glyph/bitmap.rs +81 -0
- package/vendor/krilla/src/text/glyph/colr.rs +581 -0
- package/vendor/krilla/src/text/glyph/mod.rs +205 -0
- package/vendor/krilla/src/text/glyph/outline.rs +72 -0
- package/vendor/krilla/src/text/glyph/svg.rs +34 -0
- package/vendor/krilla/src/text/group.rs +463 -0
- package/vendor/krilla/src/text/mod.rs +232 -0
- package/vendor/krilla/src/text/shape.rs +102 -0
- package/vendor/krilla/src/text/type3.rs +514 -0
- package/vendor/krilla/src/util.rs +286 -0
- package/vendor/stylo/properties/longhands.toml +0 -1
- package/vendor/stylo/stylesheets/rule_parser.rs +1 -1
- package/wasm/blitz_pdf.js +10 -5
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
//! Format the tag tree in a human readable way, in form of a YAML document.
|
|
2
|
+
//! YAML is a good fit to represent the hierarchy of the tag tree, and many
|
|
3
|
+
//! editors allow folding the sub-trees.
|
|
4
|
+
|
|
5
|
+
use std::fmt::Display;
|
|
6
|
+
|
|
7
|
+
use crate::tagging::{
|
|
8
|
+
Attr, BBox, BlockAlign, BorderStyle, ColumnDimensions, GlyphOrientationVertical, Identifier,
|
|
9
|
+
IdentifierInner, IdentifierType, InlineAlign, LayoutAttr, LineHeight, ListAttr, ListNumbering,
|
|
10
|
+
NaiveRgbColor, Node, Placement, Sides, StructAttr, TableAttr, TableHeaderScope, TagGroup,
|
|
11
|
+
TagId, TagKind, TagTree, TextAlign, TextDecorationType, WritingMode,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/// Helper trait for indented output.
|
|
15
|
+
pub trait Output {
|
|
16
|
+
/// Wrapper around [`Output::output`] with a zero indent;
|
|
17
|
+
fn output(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
|
18
|
+
self.output_indent(f, Indent(0))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Output data with an indent.
|
|
22
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result;
|
|
23
|
+
|
|
24
|
+
/// Create a [`Display`]able wrapper.
|
|
25
|
+
fn display<'a>(&'a self) -> Wrapper<'a, Self> {
|
|
26
|
+
Wrapper {
|
|
27
|
+
inner: self,
|
|
28
|
+
indent: Indent(0),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Create a [`Display`]able wrapper with a specific indent.
|
|
33
|
+
fn display_indent<'a>(&'a self, indent: Indent) -> Wrapper<'a, Self> {
|
|
34
|
+
Wrapper {
|
|
35
|
+
inner: self,
|
|
36
|
+
indent,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// A [`Display`]able wrapper struct around an [`Output`].
|
|
42
|
+
pub struct Wrapper<'a, T: Output + ?Sized> {
|
|
43
|
+
inner: &'a T,
|
|
44
|
+
indent: Indent,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
impl<T: Output> Display for Wrapper<'_, T> {
|
|
48
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
49
|
+
self.inner.output_indent(f, self.indent)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// A [`Display`]able indentation.
|
|
54
|
+
#[derive(Clone, Copy)]
|
|
55
|
+
pub struct Indent(pub usize);
|
|
56
|
+
|
|
57
|
+
impl Indent {
|
|
58
|
+
fn inc(self) -> Self {
|
|
59
|
+
Self(self.0 + 1)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
impl Display for Indent {
|
|
64
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
65
|
+
write!(f, "{:indent$}", "", indent = 2 * self.0)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
impl Output for TagTree {
|
|
70
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
71
|
+
for child in self.children.iter() {
|
|
72
|
+
child.output_indent(f, indent)?;
|
|
73
|
+
}
|
|
74
|
+
Ok(())
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl Output for Node {
|
|
79
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
80
|
+
match self {
|
|
81
|
+
Node::Group(group) => group.output_indent(f, indent),
|
|
82
|
+
Node::Leaf(Identifier(IdentifierInner::Real(IdentifierType::PageIdentifier(pi)))) => {
|
|
83
|
+
writeln!(
|
|
84
|
+
f,
|
|
85
|
+
"{indent}- Content: page={} mcid={}",
|
|
86
|
+
pi.page_index, pi.mcid
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
Node::Leaf(Identifier(IdentifierInner::Real(
|
|
90
|
+
IdentifierType::AnnotationIdentifier(ai),
|
|
91
|
+
))) => {
|
|
92
|
+
writeln!(
|
|
93
|
+
f,
|
|
94
|
+
"{indent}- Annotation: page={} index={}",
|
|
95
|
+
ai.page_index, ai.annot_index
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
Node::Leaf(Identifier(IdentifierInner::Dummy)) => writeln!(f, "{indent}- Artifact"),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
impl Output for TagGroup {
|
|
104
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
105
|
+
self.tag.output_indent(f, indent)?;
|
|
106
|
+
let indent = indent.inc();
|
|
107
|
+
if !self.children.is_empty() {
|
|
108
|
+
writeln!(f, "{indent}/K:")?;
|
|
109
|
+
let indent = indent.inc();
|
|
110
|
+
for node in self.children.iter() {
|
|
111
|
+
node.output_indent(f, indent)?;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
Ok(())
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
impl Output for TagKind {
|
|
119
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
120
|
+
write!(f, "{indent}- Tag: ")?;
|
|
121
|
+
match self {
|
|
122
|
+
TagKind::Part(_) => write!(f, "Part"),
|
|
123
|
+
TagKind::Article(_) => write!(f, "Article"),
|
|
124
|
+
TagKind::Section(_) => write!(f, "Section"),
|
|
125
|
+
TagKind::Div(_) => write!(f, "Div"),
|
|
126
|
+
TagKind::BlockQuote(_) => write!(f, "BlockQuote"),
|
|
127
|
+
TagKind::Caption(_) => write!(f, "Caption"),
|
|
128
|
+
TagKind::TOC(_) => write!(f, "TOC"),
|
|
129
|
+
TagKind::TOCI(_) => write!(f, "TOCI"),
|
|
130
|
+
TagKind::Index(_) => write!(f, "Index"),
|
|
131
|
+
TagKind::P(_) => write!(f, "P"),
|
|
132
|
+
TagKind::Hn(tag) => write!(f, "H{}", tag.level().get()),
|
|
133
|
+
TagKind::L(_) => write!(f, "L"),
|
|
134
|
+
TagKind::LI(_) => write!(f, "LI"),
|
|
135
|
+
TagKind::Lbl(_) => write!(f, "Lbl"),
|
|
136
|
+
TagKind::LBody(_) => write!(f, "LBody"),
|
|
137
|
+
TagKind::Table(_) => write!(f, "Table"),
|
|
138
|
+
TagKind::TR(_) => write!(f, "TR"),
|
|
139
|
+
TagKind::TH(_) => write!(f, "TH"),
|
|
140
|
+
TagKind::TD(_) => write!(f, "TD"),
|
|
141
|
+
TagKind::THead(_) => write!(f, "THead"),
|
|
142
|
+
TagKind::TBody(_) => write!(f, "TBody"),
|
|
143
|
+
TagKind::TFoot(_) => write!(f, "TFoot"),
|
|
144
|
+
TagKind::Span(_) => write!(f, "Span"),
|
|
145
|
+
TagKind::InlineQuote(_) => write!(f, "InlineQuote"),
|
|
146
|
+
TagKind::Note(_) => write!(f, "Note"),
|
|
147
|
+
TagKind::Reference(_) => write!(f, "Reference"),
|
|
148
|
+
TagKind::BibEntry(_) => write!(f, "BibEntry"),
|
|
149
|
+
TagKind::Code(_) => write!(f, "Code"),
|
|
150
|
+
TagKind::Link(_) => write!(f, "Link"),
|
|
151
|
+
TagKind::Annot(_) => write!(f, "Annot"),
|
|
152
|
+
TagKind::Figure(_) => write!(f, "Figure"),
|
|
153
|
+
TagKind::Formula(_) => write!(f, "Formula"),
|
|
154
|
+
TagKind::Form(_) => write!(f, "Form"),
|
|
155
|
+
TagKind::NonStruct(_) => write!(f, "NonStruct"),
|
|
156
|
+
TagKind::Datetime(_) => write!(f, "Datetime"),
|
|
157
|
+
TagKind::Terms(_) => write!(f, "Terms"),
|
|
158
|
+
TagKind::Title(_) => write!(f, "Title"),
|
|
159
|
+
TagKind::Strong(_) => write!(f, "Strong"),
|
|
160
|
+
TagKind::Em(_) => write!(f, "Em"),
|
|
161
|
+
}?;
|
|
162
|
+
writeln!(f)?;
|
|
163
|
+
|
|
164
|
+
let indent = indent.inc();
|
|
165
|
+
for attr in self.as_any().attrs.iter() {
|
|
166
|
+
attr.output_indent(f, indent)?;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Ok(())
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
impl Output for Attr {
|
|
174
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
175
|
+
use LayoutAttr::*;
|
|
176
|
+
use ListAttr::*;
|
|
177
|
+
use StructAttr::*;
|
|
178
|
+
use TableAttr::*;
|
|
179
|
+
|
|
180
|
+
if let Attr::Struct(StructAttr::HeadingLevel(_)) = self {
|
|
181
|
+
return Ok(());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
write!(f, "{indent}")?;
|
|
185
|
+
match self {
|
|
186
|
+
Attr::Struct(struct_attr) => match struct_attr {
|
|
187
|
+
Id(id) => writeln!(f, "/Id: {}", id.display()),
|
|
188
|
+
Lang(lang) => writeln!(f, "/Lang: {lang:?}"),
|
|
189
|
+
AltText(alt) => writeln!(f, "/Alt: {alt:?}"),
|
|
190
|
+
Expanded(e) => writeln!(f, "/E: {e:?}"),
|
|
191
|
+
ActualText(actual) => writeln!(f, "/ActualText: {actual:?}"),
|
|
192
|
+
Title(title) => writeln!(f, "/T: {title:?}"),
|
|
193
|
+
|
|
194
|
+
// Not a real attribute, is already displayed in tag kind.
|
|
195
|
+
HeadingLevel(_) => Ok(()),
|
|
196
|
+
},
|
|
197
|
+
Attr::List(list_attr) => match list_attr {
|
|
198
|
+
Numbering(n) => writeln!(f, "/Numbering: {}", n.display()),
|
|
199
|
+
},
|
|
200
|
+
Attr::Table(table_attr) => match table_attr {
|
|
201
|
+
Summary(summary) => writeln!(f, "/Summary: {summary:?}"),
|
|
202
|
+
HeaderScope(scope) => writeln!(f, "/Scope: {}", scope.display()),
|
|
203
|
+
CellHeaders(headers) => {
|
|
204
|
+
write!(f, "/Headers: [")?;
|
|
205
|
+
if let Some((first, remainder)) = headers.split_first() {
|
|
206
|
+
first.output(f)?;
|
|
207
|
+
for id in remainder.iter() {
|
|
208
|
+
write!(f, ", {}", id.display())?;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
writeln!(f, "]")
|
|
212
|
+
}
|
|
213
|
+
RowSpan(rowspan) => writeln!(f, "/RowSpan: {}", rowspan.get()),
|
|
214
|
+
ColSpan(colspan) => writeln!(f, "/ColSpan: {}", colspan.get()),
|
|
215
|
+
},
|
|
216
|
+
Attr::Layout(layout_attr) => match layout_attr {
|
|
217
|
+
Placement(placement) => writeln!(f, "/Placement: {}", placement.display()),
|
|
218
|
+
WritingMode(mode) => writeln!(f, "/WritingMode: {}", mode.display()),
|
|
219
|
+
BBox(bbox) => writeln!(f, "/BBox:{}", bbox.display_indent(indent.inc())),
|
|
220
|
+
Width(width) => writeln!(f, "/Width: {}", width.display()),
|
|
221
|
+
Height(height) => writeln!(f, "/Height: {}", height.display()),
|
|
222
|
+
BackgroundColor(color) => writeln!(f, "/BackgroundColor: {}", color.display()),
|
|
223
|
+
BorderColor(sides) => {
|
|
224
|
+
let space = omit_if(" ", sides.is_multiline());
|
|
225
|
+
let sides = sides.display_indent(indent.inc());
|
|
226
|
+
writeln!(f, "/BorderColor:{space}{sides}")
|
|
227
|
+
}
|
|
228
|
+
BorderStyle(sides) => {
|
|
229
|
+
let space = omit_if(" ", sides.is_multiline());
|
|
230
|
+
let sides = sides.display_indent(indent.inc());
|
|
231
|
+
writeln!(f, "/BorderStyle:{space}{sides}")
|
|
232
|
+
}
|
|
233
|
+
BorderThickness(sides) => {
|
|
234
|
+
let space = omit_if(" ", sides.is_multiline());
|
|
235
|
+
let sides = sides.display_indent(indent.inc());
|
|
236
|
+
writeln!(f, "/BorderThickness:{space}{sides}")
|
|
237
|
+
}
|
|
238
|
+
Padding(sides) => {
|
|
239
|
+
writeln!(f, "/Padding: {}", sides.display_indent(indent.inc()))
|
|
240
|
+
}
|
|
241
|
+
Color(color) => writeln!(f, "/Color: {}", color.display()),
|
|
242
|
+
SpaceBefore(space) => writeln!(f, "/SpaceBefore: {}", space.display()),
|
|
243
|
+
SpaceAfter(space) => writeln!(f, "/SpaceAfter: {}", space.display()),
|
|
244
|
+
StartIndent(indent) => writeln!(f, "/StartIndent: {}", indent.display()),
|
|
245
|
+
EndIndent(indent) => writeln!(f, "/EndIndent: {}", indent.display()),
|
|
246
|
+
TextIndent(indent) => writeln!(f, "/TextIndent: {}", indent.display()),
|
|
247
|
+
TextAlign(align) => writeln!(f, "/TextAlign: {}", align.display()),
|
|
248
|
+
BlockAlign(align) => writeln!(f, "/BlockAlign: {}", align.display()),
|
|
249
|
+
InlineAlign(align) => writeln!(f, "/InlineAlign: {}", align.display()),
|
|
250
|
+
TableBorderStyle(sides) => {
|
|
251
|
+
let space = omit_if(" ", sides.is_multiline());
|
|
252
|
+
let style = sides.display_indent(indent.inc());
|
|
253
|
+
writeln!(f, "/TableBorderStyle:{space}{style}")
|
|
254
|
+
}
|
|
255
|
+
TablePadding(sides) => {
|
|
256
|
+
let space = omit_if(" ", sides.is_multiline());
|
|
257
|
+
let sides = sides.display_indent(indent.inc());
|
|
258
|
+
writeln!(f, "/TablePadding:{space}{sides}")
|
|
259
|
+
}
|
|
260
|
+
BaselineShift(shift) => writeln!(f, "/BaselineShift: {}", shift.display()),
|
|
261
|
+
LineHeight(line_height) => {
|
|
262
|
+
writeln!(f, "/LineHeight: {}", line_height.display())
|
|
263
|
+
}
|
|
264
|
+
TextDecorationColor(color) => {
|
|
265
|
+
writeln!(f, "/TextDecorationColor: {}", color.display())
|
|
266
|
+
}
|
|
267
|
+
TextDecorationThickness(thickness) => {
|
|
268
|
+
writeln!(f, "/TextDecorationThickness: {}", thickness.display())
|
|
269
|
+
}
|
|
270
|
+
TextDecorationType(deco_type) => {
|
|
271
|
+
writeln!(f, "/TextDecorationType: {}", deco_type.display())
|
|
272
|
+
}
|
|
273
|
+
GlyphOrientationVertical(orientation) => {
|
|
274
|
+
writeln!(f, "/GlyphOrientationVertical: {}", orientation.display())
|
|
275
|
+
}
|
|
276
|
+
ColumnCount(column_count) => writeln!(f, "/ColumnCount: {}", column_count.get()),
|
|
277
|
+
ColumnGap(column_gap) => {
|
|
278
|
+
let space = omit_if(" ", column_gap.is_multiline());
|
|
279
|
+
let column_gap = column_gap.display_indent(indent.inc());
|
|
280
|
+
writeln!(f, "/ColumnGap:{space}{column_gap}")
|
|
281
|
+
}
|
|
282
|
+
ColumnWidths(column_width) => {
|
|
283
|
+
let space = omit_if(" ", column_width.is_multiline());
|
|
284
|
+
let column_width = column_width.display_indent(indent.inc());
|
|
285
|
+
writeln!(f, "/ColumnWidths:{space}{column_width}")
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
trait ValueOutput: Output {
|
|
293
|
+
fn is_multiline(&self) -> bool {
|
|
294
|
+
false
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#[derive(Clone, Copy)]
|
|
299
|
+
struct OmitText {
|
|
300
|
+
text: &'static str,
|
|
301
|
+
omit: bool,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
fn omit_if(text: &'static str, omit: bool) -> OmitText {
|
|
305
|
+
OmitText { text, omit }
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
impl Display for OmitText {
|
|
309
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
310
|
+
if !self.omit {
|
|
311
|
+
f.write_str(self.text)?;
|
|
312
|
+
}
|
|
313
|
+
Ok(())
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
impl ValueOutput for TagId {}
|
|
318
|
+
impl Output for TagId {
|
|
319
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
320
|
+
if self
|
|
321
|
+
.as_bytes()
|
|
322
|
+
.iter()
|
|
323
|
+
.all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
|
|
324
|
+
{
|
|
325
|
+
let str = std::str::from_utf8(self.as_bytes()).unwrap();
|
|
326
|
+
write!(f, "\"{str}\"")?;
|
|
327
|
+
} else {
|
|
328
|
+
for b in self.as_bytes() {
|
|
329
|
+
write!(f, "0x{b:02x}")?;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
Ok(())
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
impl ValueOutput for ListNumbering {}
|
|
337
|
+
impl Output for ListNumbering {
|
|
338
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
339
|
+
match self {
|
|
340
|
+
ListNumbering::None => write!(f, "None"),
|
|
341
|
+
ListNumbering::Disc => write!(f, "Disc"),
|
|
342
|
+
ListNumbering::Circle => write!(f, "Circle"),
|
|
343
|
+
ListNumbering::Square => write!(f, "Square"),
|
|
344
|
+
ListNumbering::Decimal => write!(f, "Decimal"),
|
|
345
|
+
ListNumbering::LowerRoman => write!(f, "LowerRoman"),
|
|
346
|
+
ListNumbering::UpperRoman => write!(f, "UpperRoman"),
|
|
347
|
+
ListNumbering::LowerAlpha => write!(f, "LowerAlpha"),
|
|
348
|
+
ListNumbering::UpperAlpha => write!(f, "UpperAlpha"),
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
impl ValueOutput for TableHeaderScope {}
|
|
354
|
+
impl Output for TableHeaderScope {
|
|
355
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
356
|
+
match self {
|
|
357
|
+
TableHeaderScope::Row => write!(f, "Row"),
|
|
358
|
+
TableHeaderScope::Column => write!(f, "Column"),
|
|
359
|
+
TableHeaderScope::Both => write!(f, "Both"),
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
impl ValueOutput for Placement {}
|
|
365
|
+
impl Output for Placement {
|
|
366
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
367
|
+
match self {
|
|
368
|
+
Placement::Block => write!(f, "Block"),
|
|
369
|
+
Placement::Inline => write!(f, "Inline"),
|
|
370
|
+
Placement::Before => write!(f, "Before"),
|
|
371
|
+
Placement::Start => write!(f, "Start"),
|
|
372
|
+
Placement::End => write!(f, "End"),
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
impl ValueOutput for WritingMode {}
|
|
378
|
+
impl Output for WritingMode {
|
|
379
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
380
|
+
match self {
|
|
381
|
+
WritingMode::LrTb => write!(f, "LrTb"),
|
|
382
|
+
WritingMode::RlTb => write!(f, "RlTb"),
|
|
383
|
+
WritingMode::TbRl => write!(f, "TbRl"),
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
impl ValueOutput for BBox {
|
|
389
|
+
fn is_multiline(&self) -> bool {
|
|
390
|
+
true
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
impl Output for BBox {
|
|
394
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
395
|
+
writeln!(f)?;
|
|
396
|
+
writeln!(f, "{indent}page: {}", self.page_idx)?;
|
|
397
|
+
writeln!(
|
|
398
|
+
f,
|
|
399
|
+
"{indent}left: {}",
|
|
400
|
+
self.rect.left().display_indent(indent)
|
|
401
|
+
)?;
|
|
402
|
+
writeln!(
|
|
403
|
+
f,
|
|
404
|
+
"{indent}top: {}",
|
|
405
|
+
self.rect.top().display_indent(indent)
|
|
406
|
+
)?;
|
|
407
|
+
writeln!(
|
|
408
|
+
f,
|
|
409
|
+
"{indent}right: {}",
|
|
410
|
+
self.rect.right().display_indent(indent)
|
|
411
|
+
)?;
|
|
412
|
+
write!(
|
|
413
|
+
f,
|
|
414
|
+
"{indent}bottom: {}",
|
|
415
|
+
self.rect.bottom().display_indent(indent)
|
|
416
|
+
)?;
|
|
417
|
+
Ok(())
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
impl<T: PartialEq + ValueOutput> ValueOutput for Sides<T> {
|
|
422
|
+
fn is_multiline(&self) -> bool {
|
|
423
|
+
!self.is_uniform() || self.before.is_multiline()
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
impl<T: PartialEq + ValueOutput> Output for Sides<T> {
|
|
427
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
428
|
+
if self.is_uniform() {
|
|
429
|
+
self.before.output_indent(f, indent)?;
|
|
430
|
+
} else {
|
|
431
|
+
writeln!(f)?;
|
|
432
|
+
|
|
433
|
+
let space = omit_if(" ", self.before.is_multiline());
|
|
434
|
+
let before = self.before.display_indent(indent.inc());
|
|
435
|
+
writeln!(f, "{indent}before:{space}{before}")?;
|
|
436
|
+
|
|
437
|
+
let space = omit_if(" ", self.after.is_multiline());
|
|
438
|
+
let after = self.after.display_indent(indent.inc());
|
|
439
|
+
writeln!(f, "{indent}after:{space}{after}")?;
|
|
440
|
+
|
|
441
|
+
let space = omit_if(" ", self.start.is_multiline());
|
|
442
|
+
let start = self.start.display_indent(indent.inc());
|
|
443
|
+
writeln!(f, "{indent}start:{space}{start}")?;
|
|
444
|
+
|
|
445
|
+
let space = omit_if(" ", self.end.is_multiline());
|
|
446
|
+
let end = self.end.display_indent(indent.inc());
|
|
447
|
+
write!(f, "{indent}end:{space}{end}")?;
|
|
448
|
+
}
|
|
449
|
+
Ok(())
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
impl ValueOutput for NaiveRgbColor {}
|
|
454
|
+
impl Output for NaiveRgbColor {
|
|
455
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
456
|
+
let r = self.red;
|
|
457
|
+
let g = self.green;
|
|
458
|
+
let b = self.blue;
|
|
459
|
+
write!(f, "#{r:02x}{g:02x}{b:02x}")
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
impl ValueOutput for BorderStyle {}
|
|
464
|
+
impl Output for BorderStyle {
|
|
465
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
466
|
+
match self {
|
|
467
|
+
BorderStyle::None => write!(f, "None"),
|
|
468
|
+
BorderStyle::Hidden => write!(f, "Hidden"),
|
|
469
|
+
BorderStyle::Solid => write!(f, "Solid"),
|
|
470
|
+
BorderStyle::Dashed => write!(f, "Dashed"),
|
|
471
|
+
BorderStyle::Dotted => write!(f, "Dotted"),
|
|
472
|
+
BorderStyle::Double => write!(f, "Double"),
|
|
473
|
+
BorderStyle::Groove => write!(f, "Groove"),
|
|
474
|
+
BorderStyle::Ridge => write!(f, "Ridge"),
|
|
475
|
+
BorderStyle::Inset => write!(f, "Inset"),
|
|
476
|
+
BorderStyle::Outset => write!(f, "Outset"),
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
impl ValueOutput for TextAlign {}
|
|
482
|
+
impl Output for TextAlign {
|
|
483
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
484
|
+
match self {
|
|
485
|
+
TextAlign::Start => write!(f, "Start"),
|
|
486
|
+
TextAlign::Center => write!(f, "Center"),
|
|
487
|
+
TextAlign::End => write!(f, "End"),
|
|
488
|
+
TextAlign::Justify => write!(f, "Justify"),
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
impl ValueOutput for BlockAlign {}
|
|
494
|
+
impl Output for BlockAlign {
|
|
495
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
496
|
+
match self {
|
|
497
|
+
BlockAlign::Begin => write!(f, "Begin"),
|
|
498
|
+
BlockAlign::Middle => write!(f, "Middle"),
|
|
499
|
+
BlockAlign::After => write!(f, "After"),
|
|
500
|
+
BlockAlign::Justify => write!(f, "Justify"),
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
impl ValueOutput for InlineAlign {}
|
|
506
|
+
impl Output for InlineAlign {
|
|
507
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
508
|
+
match self {
|
|
509
|
+
InlineAlign::Start => write!(f, "Start"),
|
|
510
|
+
InlineAlign::Center => write!(f, "Center"),
|
|
511
|
+
InlineAlign::End => write!(f, "End"),
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
impl ValueOutput for LineHeight {}
|
|
517
|
+
impl Output for LineHeight {
|
|
518
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
519
|
+
match self {
|
|
520
|
+
LineHeight::Normal => write!(f, "Normal"),
|
|
521
|
+
LineHeight::Auto => write!(f, "Auto"),
|
|
522
|
+
LineHeight::Custom(custom) => custom.output(f),
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
impl ValueOutput for TextDecorationType {}
|
|
528
|
+
impl Output for TextDecorationType {
|
|
529
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
530
|
+
match self {
|
|
531
|
+
TextDecorationType::None => write!(f, "None"),
|
|
532
|
+
TextDecorationType::Underline => write!(f, "Underline"),
|
|
533
|
+
TextDecorationType::Overline => write!(f, "Overline"),
|
|
534
|
+
TextDecorationType::LineThrough => write!(f, "LineThrough"),
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
impl ValueOutput for GlyphOrientationVertical {}
|
|
540
|
+
impl Output for GlyphOrientationVertical {
|
|
541
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
542
|
+
match self {
|
|
543
|
+
GlyphOrientationVertical::Auto => write!(f, "Auto"),
|
|
544
|
+
GlyphOrientationVertical::None => write!(f, "0"),
|
|
545
|
+
GlyphOrientationVertical::Clockwise90 => write!(f, "90"),
|
|
546
|
+
GlyphOrientationVertical::CounterClockwise90 => write!(f, "-90"),
|
|
547
|
+
GlyphOrientationVertical::Clockwise180 => write!(f, "180"),
|
|
548
|
+
GlyphOrientationVertical::CounterClockwise180 => write!(f, "-180"),
|
|
549
|
+
GlyphOrientationVertical::Clockwise270 => write!(f, "270"),
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
impl ValueOutput for ColumnDimensions {
|
|
555
|
+
fn is_multiline(&self) -> bool {
|
|
556
|
+
matches!(self, Self::Specific(_))
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
impl Output for ColumnDimensions {
|
|
560
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, indent: Indent) -> std::fmt::Result {
|
|
561
|
+
match self {
|
|
562
|
+
ColumnDimensions::All(all) => all.output(f),
|
|
563
|
+
ColumnDimensions::Specific(list) => {
|
|
564
|
+
let Some((last, remainder)) = list.split_last() else {
|
|
565
|
+
return Ok(());
|
|
566
|
+
};
|
|
567
|
+
writeln!(f)?;
|
|
568
|
+
for dim in remainder.iter() {
|
|
569
|
+
writeln!(f, "{indent}- {}", dim.display())?;
|
|
570
|
+
}
|
|
571
|
+
write!(f, "{indent}- {}", last.display())
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
impl ValueOutput for f32 {}
|
|
578
|
+
impl Output for f32 {
|
|
579
|
+
fn output_indent(&self, f: &mut impl std::fmt::Write, _: Indent) -> std::fmt::Result {
|
|
580
|
+
write!(f, "{self:7.3}")
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
#[cfg(test)]
|
|
585
|
+
mod tests {
|
|
586
|
+
use std::num::NonZeroU16;
|
|
587
|
+
|
|
588
|
+
use pretty_assertions::assert_eq;
|
|
589
|
+
|
|
590
|
+
use crate::action::{Action, LinkAction};
|
|
591
|
+
use crate::annotation::{LinkAnnotation, Target};
|
|
592
|
+
use crate::geom::Rect;
|
|
593
|
+
use crate::tagging::fmt::{Indent, Output};
|
|
594
|
+
use crate::tagging::{
|
|
595
|
+
BBox, ColumnDimensions, ContentTag, LineHeight, NaiveRgbColor, Sides, Tag, TagGroup,
|
|
596
|
+
TagTree,
|
|
597
|
+
};
|
|
598
|
+
use crate::Document;
|
|
599
|
+
|
|
600
|
+
#[test]
|
|
601
|
+
fn display_empty_tag_tree() {
|
|
602
|
+
assert_eq!("", TagTree::new().display().to_string());
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
#[test]
|
|
606
|
+
fn display_tag_tree() {
|
|
607
|
+
let mut document = Document::new();
|
|
608
|
+
let mut page = document.start_page();
|
|
609
|
+
let mut tree = TagTree::new();
|
|
610
|
+
|
|
611
|
+
let sec = Tag::Section
|
|
612
|
+
.with_lang(Some("de".into()))
|
|
613
|
+
.with_column_widths(Some(ColumnDimensions::Specific(vec![17.0, 23.0, 34.0])))
|
|
614
|
+
.with_column_gap(Some(ColumnDimensions::Specific(vec![3.0, 4.0])));
|
|
615
|
+
let mut sec = TagGroup::new(sec);
|
|
616
|
+
|
|
617
|
+
let figure_rect = Rect::from_ltrb(12.1, 12.342, 24.789877, 32.0).unwrap();
|
|
618
|
+
let figure = Tag::Figure(Some("figure alt text".into()))
|
|
619
|
+
.with_actual_text(Some("THE ACTUAL TEXT".into()))
|
|
620
|
+
.with_bbox(Some(BBox::new(0, figure_rect)))
|
|
621
|
+
.with_line_height(Some(LineHeight::Normal));
|
|
622
|
+
let mut figure = TagGroup::new(figure);
|
|
623
|
+
|
|
624
|
+
let link_rect = Rect::from_ltrb(12.0, 12.0, 24.0, 32.32).unwrap();
|
|
625
|
+
let link_target =
|
|
626
|
+
Target::Action(Action::Link(LinkAction::new("https://github.com".into())));
|
|
627
|
+
let link_id =
|
|
628
|
+
page.add_tagged_annotation(LinkAnnotation::new(link_rect, link_target).into());
|
|
629
|
+
figure.push(link_id);
|
|
630
|
+
sec.push(figure);
|
|
631
|
+
|
|
632
|
+
let border_color = Sides::new(
|
|
633
|
+
NaiveRgbColor::new(0x1A, 0x66, 0xFF),
|
|
634
|
+
NaiveRgbColor::new(0x4D, 0x80, 0x33),
|
|
635
|
+
NaiveRgbColor::new(0x4D, 0x66, 0x4D),
|
|
636
|
+
NaiveRgbColor::new(0x00, 0xB3, 0x33),
|
|
637
|
+
);
|
|
638
|
+
let table = Tag::Table
|
|
639
|
+
.with_border_color(Some(border_color))
|
|
640
|
+
.with_line_height(Some(LineHeight::Custom(23.0)));
|
|
641
|
+
let table = TagGroup::new(table);
|
|
642
|
+
sec.push(table);
|
|
643
|
+
|
|
644
|
+
tree.push(sec);
|
|
645
|
+
|
|
646
|
+
let yaml = tree.display().to_string();
|
|
647
|
+
let expected = "\
|
|
648
|
+
- Tag: Section
|
|
649
|
+
/Lang: \"de\"
|
|
650
|
+
/ColumnGap:
|
|
651
|
+
- 3.000
|
|
652
|
+
- 4.000
|
|
653
|
+
/ColumnWidths:
|
|
654
|
+
- 17.000
|
|
655
|
+
- 23.000
|
|
656
|
+
- 34.000
|
|
657
|
+
/K:
|
|
658
|
+
- Tag: Figure
|
|
659
|
+
/Alt: \"figure alt text\"
|
|
660
|
+
/ActualText: \"THE ACTUAL TEXT\"
|
|
661
|
+
/BBox:
|
|
662
|
+
page: 0
|
|
663
|
+
left: 12.100
|
|
664
|
+
top: 12.342
|
|
665
|
+
right: 24.790
|
|
666
|
+
bottom: 32.000
|
|
667
|
+
/LineHeight: Normal
|
|
668
|
+
/K:
|
|
669
|
+
- Annotation: page=0 index=0
|
|
670
|
+
- Tag: Table
|
|
671
|
+
/BorderColor:
|
|
672
|
+
before: #1a66ff
|
|
673
|
+
after: #4d8033
|
|
674
|
+
start: #4d664d
|
|
675
|
+
end: #00b333
|
|
676
|
+
/LineHeight: 23.000
|
|
677
|
+
";
|
|
678
|
+
assert_eq!(expected, yaml)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
#[test]
|
|
682
|
+
fn display_sides_mutliline() {
|
|
683
|
+
let sides = Sides::new(
|
|
684
|
+
ColumnDimensions::specific(vec![1.0, 5.0]),
|
|
685
|
+
ColumnDimensions::specific(vec![2.0, 6.0]),
|
|
686
|
+
ColumnDimensions::specific(vec![3.0, 7.0]),
|
|
687
|
+
ColumnDimensions::specific(vec![4.0, 8.0]),
|
|
688
|
+
);
|
|
689
|
+
let yaml = format!("val:{}\n", sides.display_indent(Indent(1)));
|
|
690
|
+
let expected = "\
|
|
691
|
+
val:
|
|
692
|
+
before:
|
|
693
|
+
- 1.000
|
|
694
|
+
- 5.000
|
|
695
|
+
after:
|
|
696
|
+
- 2.000
|
|
697
|
+
- 6.000
|
|
698
|
+
start:
|
|
699
|
+
- 3.000
|
|
700
|
+
- 7.000
|
|
701
|
+
end:
|
|
702
|
+
- 4.000
|
|
703
|
+
- 8.000
|
|
704
|
+
";
|
|
705
|
+
assert_eq!(expected, yaml);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
#[test]
|
|
709
|
+
fn display_heading_with_children() {
|
|
710
|
+
let mut document = Document::new();
|
|
711
|
+
let mut page = document.start_page();
|
|
712
|
+
let mut tree = TagTree::new();
|
|
713
|
+
|
|
714
|
+
let heading = Tag::Hn(NonZeroU16::new(1).unwrap(), None);
|
|
715
|
+
let mut heading = TagGroup::new(heading);
|
|
716
|
+
|
|
717
|
+
let mut surface = page.surface();
|
|
718
|
+
|
|
719
|
+
let id1 = surface.start_tagged(ContentTag::Other);
|
|
720
|
+
surface.end_tagged();
|
|
721
|
+
heading.push(id1);
|
|
722
|
+
|
|
723
|
+
tree.push(heading);
|
|
724
|
+
|
|
725
|
+
let yaml = tree.display().to_string();
|
|
726
|
+
let expected = "\
|
|
727
|
+
- Tag: H1
|
|
728
|
+
/K:
|
|
729
|
+
- Content: page=0 mcid=0
|
|
730
|
+
";
|
|
731
|
+
assert_eq!(expected, yaml);
|
|
732
|
+
}
|
|
733
|
+
}
|