html-to-markdown 3.4.0 → 3.6.0.pre.rc.23
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/LICENSE +21 -0
- data/README.md +347 -0
- data/Steepfile +10 -2
- data/ext/html_to_markdown_rb/Cargo.toml +3 -2
- data/ext/html_to_markdown_rb/extconf.rb +5 -5
- data/ext/html_to_markdown_rb/native/Cargo.lock +962 -0
- data/ext/html_to_markdown_rb/native/Cargo.toml +6 -11
- data/ext/html_to_markdown_rb/native/extconf.rb +14 -0
- data/ext/html_to_markdown_rb/src/lib.rs +1715 -646
- data/lib/html_to_markdown/native.rb +913 -37
- data/lib/html_to_markdown/version.rb +3 -3
- data/lib/html_to_markdown.rb +9 -4
- data/lib/html_to_markdown_rb.so +0 -0
- data/sig/types.rbs +59 -292
- metadata +32 -179
- data/ext/html_to_markdown_rb/Makefile +0 -592
- data/lib/bin/html-to-markdown +0 -0
- data/vendor/Cargo.toml +0 -33
- data/vendor/html-to-markdown-rs/Cargo.toml +0 -54
- data/vendor/html-to-markdown-rs/README.md +0 -278
- data/vendor/html-to-markdown-rs/examples/basic.rs +0 -24
- data/vendor/html-to-markdown-rs/examples/table.rs +0 -25
- data/vendor/html-to-markdown-rs/examples/test_deser.rs +0 -12
- data/vendor/html-to-markdown-rs/examples/test_escape.rs +0 -58
- data/vendor/html-to-markdown-rs/examples/test_inline_formatting.rs +0 -113
- data/vendor/html-to-markdown-rs/examples/test_lists.rs +0 -39
- data/vendor/html-to-markdown-rs/examples/test_semantic_tags.rs +0 -89
- data/vendor/html-to-markdown-rs/examples/test_tables.rs +0 -100
- data/vendor/html-to-markdown-rs/examples/test_task_lists.rs +0 -61
- data/vendor/html-to-markdown-rs/examples/test_whitespace.rs +0 -34
- data/vendor/html-to-markdown-rs/src/convert_api.rs +0 -349
- data/vendor/html-to-markdown-rs/src/converter/block/blockquote.rs +0 -178
- data/vendor/html-to-markdown-rs/src/converter/block/container.rs +0 -114
- data/vendor/html-to-markdown-rs/src/converter/block/div.rs +0 -149
- data/vendor/html-to-markdown-rs/src/converter/block/heading.rs +0 -428
- data/vendor/html-to-markdown-rs/src/converter/block/horizontal_rule.rs +0 -103
- data/vendor/html-to-markdown-rs/src/converter/block/line_break.rs +0 -89
- data/vendor/html-to-markdown-rs/src/converter/block/mod.rs +0 -10
- data/vendor/html-to-markdown-rs/src/converter/block/paragraph.rs +0 -140
- data/vendor/html-to-markdown-rs/src/converter/block/preformatted.rs +0 -298
- data/vendor/html-to-markdown-rs/src/converter/block/table/builder.rs +0 -453
- data/vendor/html-to-markdown-rs/src/converter/block/table/caption.rs +0 -44
- data/vendor/html-to-markdown-rs/src/converter/block/table/cell.rs +0 -276
- data/vendor/html-to-markdown-rs/src/converter/block/table/cells.rs +0 -336
- data/vendor/html-to-markdown-rs/src/converter/block/table/layout.rs +0 -58
- data/vendor/html-to-markdown-rs/src/converter/block/table/mod.rs +0 -266
- data/vendor/html-to-markdown-rs/src/converter/block/table/scanner.rs +0 -146
- data/vendor/html-to-markdown-rs/src/converter/block/table/utils.rs +0 -34
- data/vendor/html-to-markdown-rs/src/converter/block/unknown.rs +0 -138
- data/vendor/html-to-markdown-rs/src/converter/context.rs +0 -208
- data/vendor/html-to-markdown-rs/src/converter/dom_context.rs +0 -337
- data/vendor/html-to-markdown-rs/src/converter/form/elements.rs +0 -770
- data/vendor/html-to-markdown-rs/src/converter/form/mod.rs +0 -82
- data/vendor/html-to-markdown-rs/src/converter/format/djot.rs +0 -64
- data/vendor/html-to-markdown-rs/src/converter/format/markdown.rs +0 -59
- data/vendor/html-to-markdown-rs/src/converter/format/mod.rs +0 -43
- data/vendor/html-to-markdown-rs/src/converter/handlers/blockquote.rs +0 -173
- data/vendor/html-to-markdown-rs/src/converter/handlers/code_block.rs +0 -434
- data/vendor/html-to-markdown-rs/src/converter/handlers/graphic.rs +0 -234
- data/vendor/html-to-markdown-rs/src/converter/handlers/image.rs +0 -282
- data/vendor/html-to-markdown-rs/src/converter/handlers/link.rs +0 -316
- data/vendor/html-to-markdown-rs/src/converter/handlers/mod.rs +0 -26
- data/vendor/html-to-markdown-rs/src/converter/inline/code.rs +0 -306
- data/vendor/html-to-markdown-rs/src/converter/inline/emphasis.rs +0 -345
- data/vendor/html-to-markdown-rs/src/converter/inline/link.rs +0 -428
- data/vendor/html-to-markdown-rs/src/converter/inline/mod.rs +0 -237
- data/vendor/html-to-markdown-rs/src/converter/inline/ruby.rs +0 -337
- data/vendor/html-to-markdown-rs/src/converter/inline/semantic/marks.rs +0 -566
- data/vendor/html-to-markdown-rs/src/converter/inline/semantic/mod.rs +0 -86
- data/vendor/html-to-markdown-rs/src/converter/inline/semantic/typography.rs +0 -558
- data/vendor/html-to-markdown-rs/src/converter/list/definition.rs +0 -232
- data/vendor/html-to-markdown-rs/src/converter/list/item.rs +0 -332
- data/vendor/html-to-markdown-rs/src/converter/list/mod.rs +0 -70
- data/vendor/html-to-markdown-rs/src/converter/list/ordered.rs +0 -201
- data/vendor/html-to-markdown-rs/src/converter/list/unordered.rs +0 -195
- data/vendor/html-to-markdown-rs/src/converter/list/utils.rs +0 -314
- data/vendor/html-to-markdown-rs/src/converter/main.rs +0 -710
- data/vendor/html-to-markdown-rs/src/converter/main_helpers.rs +0 -452
- data/vendor/html-to-markdown-rs/src/converter/media/embedded.rs +0 -393
- data/vendor/html-to-markdown-rs/src/converter/media/graphic.rs +0 -4
- data/vendor/html-to-markdown-rs/src/converter/media/image.rs +0 -183
- data/vendor/html-to-markdown-rs/src/converter/media/mod.rs +0 -87
- data/vendor/html-to-markdown-rs/src/converter/media/svg.rs +0 -280
- data/vendor/html-to-markdown-rs/src/converter/metadata.rs +0 -220
- data/vendor/html-to-markdown-rs/src/converter/mod.rs +0 -156
- data/vendor/html-to-markdown-rs/src/converter/plain_text.rs +0 -516
- data/vendor/html-to-markdown-rs/src/converter/preprocessing_helpers.rs +0 -201
- data/vendor/html-to-markdown-rs/src/converter/reference_collector.rs +0 -69
- data/vendor/html-to-markdown-rs/src/converter/semantic/attributes.rs +0 -269
- data/vendor/html-to-markdown-rs/src/converter/semantic/definition_list.rs +0 -266
- data/vendor/html-to-markdown-rs/src/converter/semantic/figure.rs +0 -391
- data/vendor/html-to-markdown-rs/src/converter/semantic/mod.rs +0 -112
- data/vendor/html-to-markdown-rs/src/converter/semantic/sectioning.rs +0 -85
- data/vendor/html-to-markdown-rs/src/converter/semantic/summary.rs +0 -324
- data/vendor/html-to-markdown-rs/src/converter/text/mod.rs +0 -8
- data/vendor/html-to-markdown-rs/src/converter/text/processing.rs +0 -56
- data/vendor/html-to-markdown-rs/src/converter/text_node.rs +0 -269
- data/vendor/html-to-markdown-rs/src/converter/utility/attributes.rs +0 -151
- data/vendor/html-to-markdown-rs/src/converter/utility/caching.rs +0 -74
- data/vendor/html-to-markdown-rs/src/converter/utility/content.rs +0 -271
- data/vendor/html-to-markdown-rs/src/converter/utility/mod.rs +0 -17
- data/vendor/html-to-markdown-rs/src/converter/utility/preprocessing.rs +0 -1002
- data/vendor/html-to-markdown-rs/src/converter/utility/serialization.rs +0 -126
- data/vendor/html-to-markdown-rs/src/converter/utility/siblings.rs +0 -97
- data/vendor/html-to-markdown-rs/src/converter/visitor_hooks.rs +0 -189
- data/vendor/html-to-markdown-rs/src/error.rs +0 -43
- data/vendor/html-to-markdown-rs/src/exports.rs +0 -24
- data/vendor/html-to-markdown-rs/src/inline_images.rs +0 -336
- data/vendor/html-to-markdown-rs/src/lib.rs +0 -139
- data/vendor/html-to-markdown-rs/src/metadata/collector.rs +0 -457
- data/vendor/html-to-markdown-rs/src/metadata/config.rs +0 -394
- data/vendor/html-to-markdown-rs/src/metadata/extraction.rs +0 -398
- data/vendor/html-to-markdown-rs/src/metadata/mod.rs +0 -288
- data/vendor/html-to-markdown-rs/src/metadata/types.rs +0 -477
- data/vendor/html-to-markdown-rs/src/options/conversion.rs +0 -559
- data/vendor/html-to-markdown-rs/src/options/inline_image.rs +0 -111
- data/vendor/html-to-markdown-rs/src/options/mod.rs +0 -20
- data/vendor/html-to-markdown-rs/src/options/preprocessing.rs +0 -201
- data/vendor/html-to-markdown-rs/src/options/validation.rs +0 -416
- data/vendor/html-to-markdown-rs/src/prelude.rs +0 -1
- data/vendor/html-to-markdown-rs/src/rcdom.rs +0 -487
- data/vendor/html-to-markdown-rs/src/text.rs +0 -358
- data/vendor/html-to-markdown-rs/src/types/document.rs +0 -191
- data/vendor/html-to-markdown-rs/src/types/mod.rs +0 -17
- data/vendor/html-to-markdown-rs/src/types/result.rs +0 -54
- data/vendor/html-to-markdown-rs/src/types/structure_builder.rs +0 -791
- data/vendor/html-to-markdown-rs/src/types/structure_collector.rs +0 -483
- data/vendor/html-to-markdown-rs/src/types/tables.rs +0 -52
- data/vendor/html-to-markdown-rs/src/types/warnings.rs +0 -33
- data/vendor/html-to-markdown-rs/src/validation.rs +0 -158
- data/vendor/html-to-markdown-rs/src/visitor/default_impl.rs +0 -63
- data/vendor/html-to-markdown-rs/src/visitor/mod.rs +0 -41
- data/vendor/html-to-markdown-rs/src/visitor/traits.rs +0 -370
- data/vendor/html-to-markdown-rs/src/visitor/types.rs +0 -319
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/callbacks/mod.rs +0 -1
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/content.rs +0 -126
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/mod.rs +0 -27
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/state.rs +0 -110
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/traversal.rs +0 -250
- data/vendor/html-to-markdown-rs/src/visitor_helpers.rs +0 -597
- data/vendor/html-to-markdown-rs/src/wrapper/sync.rs +0 -413
- data/vendor/html-to-markdown-rs/src/wrapper/utils.rs +0 -290
- data/vendor/html-to-markdown-rs/src/wrapper.rs +0 -9
- data/vendor/html-to-markdown-rs/tests/br_in_inline_test.rs +0 -87
- data/vendor/html-to-markdown-rs/tests/commonmark_compliance_test.rs +0 -297
- data/vendor/html-to-markdown-rs/tests/djot_output_test.rs +0 -153
- data/vendor/html-to-markdown-rs/tests/exclude_selectors_test.rs +0 -132
- data/vendor/html-to-markdown-rs/tests/integration_test.rs +0 -631
- data/vendor/html-to-markdown-rs/tests/issue_121_regressions.rs +0 -49
- data/vendor/html-to-markdown-rs/tests/issue_127_regressions.rs +0 -58
- data/vendor/html-to-markdown-rs/tests/issue_128_regressions.rs +0 -17
- data/vendor/html-to-markdown-rs/tests/issue_131_regressions.rs +0 -41
- data/vendor/html-to-markdown-rs/tests/issue_134_regressions.rs +0 -40
- data/vendor/html-to-markdown-rs/tests/issue_139_regressions.rs +0 -26
- data/vendor/html-to-markdown-rs/tests/issue_140_regressions.rs +0 -185
- data/vendor/html-to-markdown-rs/tests/issue_143_regressions.rs +0 -100
- data/vendor/html-to-markdown-rs/tests/issue_145_regressions.rs +0 -133
- data/vendor/html-to-markdown-rs/tests/issue_146_regressions.rs +0 -144
- data/vendor/html-to-markdown-rs/tests/issue_176_regressions.rs +0 -62
- data/vendor/html-to-markdown-rs/tests/issue_190_regressions.rs +0 -128
- data/vendor/html-to-markdown-rs/tests/issue_199_regressions.rs +0 -20
- data/vendor/html-to-markdown-rs/tests/issue_200_regressions.rs +0 -62
- data/vendor/html-to-markdown-rs/tests/issue_212_regressions.rs +0 -68
- data/vendor/html-to-markdown-rs/tests/issue_216_217_regressions.rs +0 -87
- data/vendor/html-to-markdown-rs/tests/issue_336_regressions.rs +0 -74
- data/vendor/html-to-markdown-rs/tests/issue_339_regressions.rs +0 -92
- data/vendor/html-to-markdown-rs/tests/issue_347_regressions.rs +0 -154
- data/vendor/html-to-markdown-rs/tests/issue_348_visitor_plain.rs +0 -93
- data/vendor/html-to-markdown-rs/tests/json_ld_script_extraction.rs +0 -44
- data/vendor/html-to-markdown-rs/tests/lists_test.rs +0 -199
- data/vendor/html-to-markdown-rs/tests/plain_output_test.rs +0 -273
- data/vendor/html-to-markdown-rs/tests/preprocessing_tests.rs +0 -61
- data/vendor/html-to-markdown-rs/tests/reference_links_test.rs +0 -169
- data/vendor/html-to-markdown-rs/tests/sectioning_elements_test.rs +0 -137
- data/vendor/html-to-markdown-rs/tests/skip_images_test.rs +0 -522
- data/vendor/html-to-markdown-rs/tests/tables_test.rs +0 -743
- data/vendor/html-to-markdown-rs/tests/test_custom_elements.rs +0 -41
- data/vendor/html-to-markdown-rs/tests/test_issue_187.rs +0 -204
- data/vendor/html-to-markdown-rs/tests/test_issue_218.rs +0 -68
- data/vendor/html-to-markdown-rs/tests/test_issue_277.rs +0 -77
- data/vendor/html-to-markdown-rs/tests/test_max_depth.rs +0 -82
- data/vendor/html-to-markdown-rs/tests/test_nested_simple.rs +0 -45
- data/vendor/html-to-markdown-rs/tests/test_script_style_stripping.rs +0 -396
- data/vendor/html-to-markdown-rs/tests/test_spa_bisect.rs +0 -34
- data/vendor/html-to-markdown-rs/tests/visitor_code_integration_test.rs +0 -121
- data/vendor/html-to-markdown-rs/tests/visitor_integration_test.rs +0 -1190
- data/vendor/html-to-markdown-rs/tests/xml_tables_test.rs +0 -372
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
#![allow(clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::unused_self)]
|
|
2
|
-
use std::collections::BTreeMap;
|
|
3
|
-
|
|
4
|
-
use crate::error::ConversionError;
|
|
5
|
-
|
|
6
|
-
/// Configuration for capturing inline images during conversion.
|
|
7
|
-
#[derive(Debug, Clone)]
|
|
8
|
-
pub struct InlineImageConfig {
|
|
9
|
-
/// Maximum allowed decoded size in bytes; larger payloads are rejected.
|
|
10
|
-
pub max_decoded_size_bytes: u64,
|
|
11
|
-
/// Optional prefix for generated filenames (defaults to "`embedded_image`").
|
|
12
|
-
pub filename_prefix: Option<String>,
|
|
13
|
-
/// Whether to capture inline SVG elements (defaults to true).
|
|
14
|
-
pub capture_svg: bool,
|
|
15
|
-
/// Whether to decode raster images to infer dimensions (defaults to false).
|
|
16
|
-
pub infer_dimensions: bool,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/// Default maximum size for inline image extraction (5 MB).
|
|
20
|
-
pub const DEFAULT_INLINE_IMAGE_LIMIT: u64 = 5 * 1024 * 1024;
|
|
21
|
-
|
|
22
|
-
/// Partial update for `InlineImageConfig`.
|
|
23
|
-
///
|
|
24
|
-
/// This struct uses `Option<T>` to represent optional fields that can be selectively updated.
|
|
25
|
-
/// Only specified fields (Some values) will override existing options; None values leave the
|
|
26
|
-
/// corresponding fields unchanged when applied via [`InlineImageConfig::apply_update`].
|
|
27
|
-
#[derive(Debug, Clone, Default)]
|
|
28
|
-
#[cfg_attr(any(feature = "serde", feature = "metadata"), derive(serde::Deserialize))]
|
|
29
|
-
#[cfg_attr(any(feature = "serde", feature = "metadata"), serde(deny_unknown_fields))]
|
|
30
|
-
pub struct InlineImageConfigUpdate {
|
|
31
|
-
/// Optional maximum decoded size override in bytes.
|
|
32
|
-
pub max_decoded_size_bytes: Option<u64>,
|
|
33
|
-
/// Optional filename prefix override for generated filenames.
|
|
34
|
-
pub filename_prefix: Option<String>,
|
|
35
|
-
/// Optional inline SVG capture enablement override.
|
|
36
|
-
pub capture_svg: Option<bool>,
|
|
37
|
-
/// Optional dimension inference override for raster images.
|
|
38
|
-
pub infer_dimensions: Option<bool>,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
impl InlineImageConfig {
|
|
42
|
-
/// Create a new configuration with required maximum decoded size.
|
|
43
|
-
#[must_use]
|
|
44
|
-
pub const fn new(max_decoded_size_bytes: u64) -> Self {
|
|
45
|
-
Self {
|
|
46
|
-
max_decoded_size_bytes,
|
|
47
|
-
filename_prefix: None,
|
|
48
|
-
capture_svg: true,
|
|
49
|
-
infer_dimensions: false,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/// Apply a partial update to this inline image configuration.
|
|
54
|
-
///
|
|
55
|
-
/// Any specified fields in the update will override the current values.
|
|
56
|
-
/// Unspecified fields (None) are left unchanged.
|
|
57
|
-
///
|
|
58
|
-
/// # Arguments
|
|
59
|
-
///
|
|
60
|
-
/// * `update` - Partial inline image options update with fields to override
|
|
61
|
-
pub fn apply_update(&mut self, update: InlineImageConfigUpdate) {
|
|
62
|
-
if let Some(max_decoded_size_bytes) = update.max_decoded_size_bytes {
|
|
63
|
-
self.max_decoded_size_bytes = max_decoded_size_bytes;
|
|
64
|
-
}
|
|
65
|
-
if let Some(filename_prefix) = update.filename_prefix {
|
|
66
|
-
self.filename_prefix = Some(filename_prefix);
|
|
67
|
-
}
|
|
68
|
-
if let Some(capture_svg) = update.capture_svg {
|
|
69
|
-
self.capture_svg = capture_svg;
|
|
70
|
-
}
|
|
71
|
-
if let Some(infer_dimensions) = update.infer_dimensions {
|
|
72
|
-
self.infer_dimensions = infer_dimensions;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/// Create new inline image configuration from a partial update.
|
|
77
|
-
///
|
|
78
|
-
/// Creates a new `InlineImageConfig` struct with defaults, then applies the update.
|
|
79
|
-
/// Fields not specified in the update keep their default values.
|
|
80
|
-
///
|
|
81
|
-
/// # Arguments
|
|
82
|
-
///
|
|
83
|
-
/// * `update` - Partial inline image options update with fields to set
|
|
84
|
-
///
|
|
85
|
-
/// # Returns
|
|
86
|
-
///
|
|
87
|
-
/// New `InlineImageConfig` with specified updates applied to defaults
|
|
88
|
-
#[must_use]
|
|
89
|
-
pub fn from_update(update: InlineImageConfigUpdate) -> Self {
|
|
90
|
-
let mut config = Self::new(DEFAULT_INLINE_IMAGE_LIMIT);
|
|
91
|
-
config.apply_update(update);
|
|
92
|
-
config
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/// Supported inline image formats derived from the MIME subtype.
|
|
97
|
-
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
98
|
-
pub enum InlineImageFormat {
|
|
99
|
-
/// PNG (Portable Network Graphics) raster image format.
|
|
100
|
-
Png,
|
|
101
|
-
/// JPEG (Joint Photographic Experts Group) raster image format.
|
|
102
|
-
Jpeg,
|
|
103
|
-
/// GIF (Graphics Interchange Format) raster image format.
|
|
104
|
-
Gif,
|
|
105
|
-
/// BMP (Bitmap) raster image format.
|
|
106
|
-
Bmp,
|
|
107
|
-
/// WebP modern raster image format.
|
|
108
|
-
Webp,
|
|
109
|
-
/// SVG (Scalable Vector Graphics) vector format.
|
|
110
|
-
Svg,
|
|
111
|
-
/// Custom or unrecognized image format; contains the MIME subtype.
|
|
112
|
-
Other(String),
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
impl std::fmt::Display for InlineImageFormat {
|
|
116
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
117
|
-
match self {
|
|
118
|
-
Self::Png => write!(f, "png"),
|
|
119
|
-
Self::Jpeg => write!(f, "jpeg"),
|
|
120
|
-
Self::Gif => write!(f, "gif"),
|
|
121
|
-
Self::Bmp => write!(f, "bmp"),
|
|
122
|
-
Self::Webp => write!(f, "webp"),
|
|
123
|
-
Self::Svg => write!(f, "svg"),
|
|
124
|
-
Self::Other(custom) => write!(f, "{custom}"),
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/// Source of the inline image.
|
|
130
|
-
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
131
|
-
pub enum InlineImageSource {
|
|
132
|
-
/// Image sourced from an `<img>` tag's `data:` URI.
|
|
133
|
-
ImgDataUri,
|
|
134
|
-
/// Image sourced from an inline `<svg>` element.
|
|
135
|
-
SvgElement,
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
impl std::fmt::Display for InlineImageSource {
|
|
139
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
140
|
-
match self {
|
|
141
|
-
Self::ImgDataUri => write!(f, "img_data_uri"),
|
|
142
|
-
Self::SvgElement => write!(f, "svg_element"),
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/// Information about an extracted inline image.
|
|
148
|
-
#[derive(Debug, Clone)]
|
|
149
|
-
pub struct InlineImage {
|
|
150
|
-
/// Raw image data as bytes (encoded in its original format).
|
|
151
|
-
pub data: Vec<u8>,
|
|
152
|
-
/// Detected or inferred image format.
|
|
153
|
-
pub format: InlineImageFormat,
|
|
154
|
-
/// Generated or extracted filename for the image.
|
|
155
|
-
pub filename: Option<String>,
|
|
156
|
-
/// Alt text or other descriptive metadata from the source HTML.
|
|
157
|
-
pub description: Option<String>,
|
|
158
|
-
/// Image dimensions in pixels (width, height); only present if inferred.
|
|
159
|
-
pub dimensions: Option<(u32, u32)>,
|
|
160
|
-
/// Where the image originated (data URI or SVG element).
|
|
161
|
-
pub source: InlineImageSource,
|
|
162
|
-
/// Additional HTML attributes from the source element.
|
|
163
|
-
pub attributes: BTreeMap<String, String>,
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
impl std::fmt::Display for InlineImage {
|
|
167
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
168
|
-
write!(f, "{self:?}")
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/// Human-friendly warning emitted during inline image extraction.
|
|
173
|
-
#[derive(Debug, Clone)]
|
|
174
|
-
pub struct InlineImageWarning {
|
|
175
|
-
/// The index of the image that triggered the warning.
|
|
176
|
-
pub index: usize,
|
|
177
|
-
/// Descriptive message explaining the warning or issue.
|
|
178
|
-
pub message: String,
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/// Output containing extracted inline images from `convert()` when `extract_images` is enabled.
|
|
182
|
-
#[derive(Debug, Clone)]
|
|
183
|
-
pub struct HtmlExtraction {
|
|
184
|
-
/// Converted markdown output.
|
|
185
|
-
pub markdown: String,
|
|
186
|
-
/// Extracted inline images found in the HTML.
|
|
187
|
-
pub inline_images: Vec<InlineImage>,
|
|
188
|
-
/// Non-fatal warnings encountered during extraction.
|
|
189
|
-
pub warnings: Vec<InlineImageWarning>,
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/// Internal collector that maintains inline image state during traversal.
|
|
193
|
-
#[derive(Debug)]
|
|
194
|
-
pub struct InlineImageCollector {
|
|
195
|
-
config: InlineImageConfig,
|
|
196
|
-
prefix: String,
|
|
197
|
-
next_index: usize,
|
|
198
|
-
images: Vec<InlineImage>,
|
|
199
|
-
warnings: Vec<InlineImageWarning>,
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
impl InlineImageCollector {
|
|
203
|
-
pub(crate) fn new(config: InlineImageConfig) -> Result<Self, ConversionError> {
|
|
204
|
-
if config.max_decoded_size_bytes == 0 {
|
|
205
|
-
return Err(ConversionError::ConfigError(
|
|
206
|
-
"inline image max_decoded_size_bytes must be greater than zero".to_string(),
|
|
207
|
-
));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
let prefix = config
|
|
211
|
-
.filename_prefix
|
|
212
|
-
.as_deref()
|
|
213
|
-
.filter(|value| !value.trim().is_empty())
|
|
214
|
-
.unwrap_or("embedded_image")
|
|
215
|
-
.to_string();
|
|
216
|
-
|
|
217
|
-
Ok(Self {
|
|
218
|
-
config,
|
|
219
|
-
prefix,
|
|
220
|
-
next_index: 0,
|
|
221
|
-
images: Vec::new(),
|
|
222
|
-
warnings: Vec::new(),
|
|
223
|
-
})
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
pub(crate) const fn capture_svg(&self) -> bool {
|
|
227
|
-
self.config.capture_svg
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
pub(crate) const fn should_infer_dimensions(&self) -> bool {
|
|
231
|
-
self.config.infer_dimensions
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
pub(crate) const fn max_decoded_size(&self) -> u64 {
|
|
235
|
-
self.config.max_decoded_size_bytes
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
pub(crate) const fn next_index(&mut self) -> usize {
|
|
239
|
-
self.next_index += 1;
|
|
240
|
-
self.next_index
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
pub(crate) fn finalize_filename(&self, provided: Option<&str>, index: usize, format: &InlineImageFormat) -> String {
|
|
244
|
-
if let Some(name) = provided {
|
|
245
|
-
return name.to_string();
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
let extension = match format {
|
|
249
|
-
InlineImageFormat::Png => "png",
|
|
250
|
-
InlineImageFormat::Jpeg => "jpeg",
|
|
251
|
-
InlineImageFormat::Gif => "gif",
|
|
252
|
-
InlineImageFormat::Bmp => "bmp",
|
|
253
|
-
InlineImageFormat::Webp => "webp",
|
|
254
|
-
InlineImageFormat::Svg => "svg",
|
|
255
|
-
// ~keep: Split on MIME type delimiters (+, ., ;) to extract base subtype
|
|
256
|
-
InlineImageFormat::Other(custom) => custom
|
|
257
|
-
.split(['+', '.', ';'])
|
|
258
|
-
.next()
|
|
259
|
-
.filter(|s| !s.is_empty())
|
|
260
|
-
.unwrap_or("bin"),
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
format!("{}_{}.{}", self.prefix, index, extension)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
pub(crate) fn warn_skip(&mut self, index: usize, reason: impl Into<String>) {
|
|
267
|
-
let message = format!("Skipped inline image {}: {}", index, reason.into());
|
|
268
|
-
self.warnings.push(InlineImageWarning { index, message });
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
pub(crate) fn warn_info(&mut self, index: usize, reason: impl Into<String>) {
|
|
272
|
-
let message = format!("Inline image {}: {}", index, reason.into());
|
|
273
|
-
self.warnings.push(InlineImageWarning { index, message });
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
pub(crate) fn push_image(&mut self, index: usize, mut image: InlineImage) {
|
|
277
|
-
if image.filename.is_none() {
|
|
278
|
-
let derived = self.finalize_filename(None, index, &image.format);
|
|
279
|
-
image.filename = Some(derived);
|
|
280
|
-
}
|
|
281
|
-
self.images.push(image);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
#[allow(clippy::too_many_arguments)]
|
|
285
|
-
pub(crate) const fn build_image(
|
|
286
|
-
&self,
|
|
287
|
-
data: Vec<u8>,
|
|
288
|
-
format: InlineImageFormat,
|
|
289
|
-
filename: Option<String>,
|
|
290
|
-
description: Option<String>,
|
|
291
|
-
dimensions: Option<(u32, u32)>,
|
|
292
|
-
source: InlineImageSource,
|
|
293
|
-
attributes: BTreeMap<String, String>,
|
|
294
|
-
) -> InlineImage {
|
|
295
|
-
InlineImage {
|
|
296
|
-
data,
|
|
297
|
-
format,
|
|
298
|
-
filename,
|
|
299
|
-
description,
|
|
300
|
-
dimensions,
|
|
301
|
-
source,
|
|
302
|
-
attributes,
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
pub(crate) fn infer_dimensions(
|
|
307
|
-
&mut self,
|
|
308
|
-
index: usize,
|
|
309
|
-
data: &[u8],
|
|
310
|
-
format: &InlineImageFormat,
|
|
311
|
-
) -> Option<(u32, u32)> {
|
|
312
|
-
if !self.should_infer_dimensions() {
|
|
313
|
-
return None;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
match format {
|
|
317
|
-
InlineImageFormat::Svg | InlineImageFormat::Other(_) => return None,
|
|
318
|
-
_ => {}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
match image::load_from_memory(data) {
|
|
322
|
-
Ok(img) => Some((img.width(), img.height())),
|
|
323
|
-
Err(err) => {
|
|
324
|
-
self.warn_info(
|
|
325
|
-
index,
|
|
326
|
-
format!("unable to decode raster data for dimension inference ({err})"),
|
|
327
|
-
);
|
|
328
|
-
None
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
pub(crate) fn finish(self) -> (Vec<InlineImage>, Vec<InlineImageWarning>) {
|
|
334
|
-
(self.images, self.warnings)
|
|
335
|
-
}
|
|
336
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#![allow(
|
|
2
|
-
clippy::too_many_lines,
|
|
3
|
-
clippy::option_if_let_else,
|
|
4
|
-
clippy::match_wildcard_for_single_variants,
|
|
5
|
-
clippy::needless_pass_by_value,
|
|
6
|
-
clippy::struct_excessive_bools,
|
|
7
|
-
clippy::fn_params_excessive_bools,
|
|
8
|
-
clippy::branches_sharing_code,
|
|
9
|
-
clippy::match_same_arms,
|
|
10
|
-
clippy::missing_errors_doc,
|
|
11
|
-
clippy::items_after_statements,
|
|
12
|
-
clippy::doc_markdown,
|
|
13
|
-
clippy::cast_sign_loss,
|
|
14
|
-
clippy::default_trait_access,
|
|
15
|
-
clippy::unused_self,
|
|
16
|
-
clippy::cast_precision_loss,
|
|
17
|
-
clippy::collapsible_if,
|
|
18
|
-
clippy::too_many_arguments,
|
|
19
|
-
clippy::collapsible_else_if,
|
|
20
|
-
clippy::extra_unused_lifetimes,
|
|
21
|
-
clippy::unnecessary_lazy_evaluations,
|
|
22
|
-
clippy::must_use_candidate,
|
|
23
|
-
clippy::trivially_copy_pass_by_ref,
|
|
24
|
-
clippy::explicit_iter_loop,
|
|
25
|
-
clippy::missing_const_for_fn,
|
|
26
|
-
clippy::manual_assert,
|
|
27
|
-
clippy::return_self_not_must_use,
|
|
28
|
-
clippy::collapsible_match,
|
|
29
|
-
clippy::cast_possible_truncation,
|
|
30
|
-
clippy::map_unwrap_or,
|
|
31
|
-
clippy::manual_let_else,
|
|
32
|
-
clippy::used_underscore_binding,
|
|
33
|
-
clippy::assigning_clones,
|
|
34
|
-
clippy::uninlined_format_args
|
|
35
|
-
)]
|
|
36
|
-
|
|
37
|
-
//! High-performance HTML to Markdown converter.
|
|
38
|
-
//!
|
|
39
|
-
//! Built with html5ever for fast, memory-efficient HTML parsing.
|
|
40
|
-
//!
|
|
41
|
-
//! ## Optional inline image extraction
|
|
42
|
-
//!
|
|
43
|
-
//! Enable the `inline-images` Cargo feature to collect embedded data URI images and inline SVG
|
|
44
|
-
//! assets alongside the produced Markdown.
|
|
45
|
-
|
|
46
|
-
// ============================================================================
|
|
47
|
-
// Module Declarations
|
|
48
|
-
// ============================================================================
|
|
49
|
-
|
|
50
|
-
pub mod error;
|
|
51
|
-
#[cfg(feature = "metadata")]
|
|
52
|
-
pub mod metadata;
|
|
53
|
-
pub mod options;
|
|
54
|
-
pub mod types;
|
|
55
|
-
#[cfg(feature = "visitor")]
|
|
56
|
-
pub mod visitor;
|
|
57
|
-
|
|
58
|
-
// Internal modules (not part of public API)
|
|
59
|
-
mod convert_api;
|
|
60
|
-
#[allow(dead_code)]
|
|
61
|
-
pub(crate) mod converter;
|
|
62
|
-
mod exports;
|
|
63
|
-
#[cfg(feature = "inline-images")]
|
|
64
|
-
mod inline_images;
|
|
65
|
-
pub(crate) mod prelude;
|
|
66
|
-
mod rcdom;
|
|
67
|
-
pub(crate) mod text;
|
|
68
|
-
mod validation;
|
|
69
|
-
#[cfg(feature = "visitor")]
|
|
70
|
-
#[allow(clippy::ref_option)]
|
|
71
|
-
pub(crate) mod visitor_helpers;
|
|
72
|
-
pub(crate) mod wrapper;
|
|
73
|
-
|
|
74
|
-
// ============================================================================
|
|
75
|
-
// Public Re-exports (from exports module)
|
|
76
|
-
// ============================================================================
|
|
77
|
-
|
|
78
|
-
pub use exports::*;
|
|
79
|
-
pub use types::{
|
|
80
|
-
AnnotationKind, ConversionResult, DocumentNode, DocumentStructure, GridCell, NodeContent, ProcessingWarning,
|
|
81
|
-
TableData, TableGrid, TextAnnotation, WarningKind,
|
|
82
|
-
};
|
|
83
|
-
#[cfg(feature = "visitor")]
|
|
84
|
-
pub use visitor::{NodeContext, NodeType, VisitResult};
|
|
85
|
-
|
|
86
|
-
// ============================================================================
|
|
87
|
-
// Main Public API Functions
|
|
88
|
-
// ============================================================================
|
|
89
|
-
|
|
90
|
-
pub use convert_api::convert;
|
|
91
|
-
|
|
92
|
-
// Tests
|
|
93
|
-
// ============================================================================
|
|
94
|
-
|
|
95
|
-
#[cfg(test)]
|
|
96
|
-
mod basic_tests {
|
|
97
|
-
use super::*;
|
|
98
|
-
|
|
99
|
-
#[test]
|
|
100
|
-
fn test_binary_input_rejected() {
|
|
101
|
-
let html = format!("abc{}def", "\0".repeat(20));
|
|
102
|
-
let result = convert(&html, None);
|
|
103
|
-
assert!(matches!(result, Err(ConversionError::InvalidInput(_))));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
#[test]
|
|
107
|
-
fn test_binary_magic_rejected() {
|
|
108
|
-
let html = "%PDF-1.7";
|
|
109
|
-
let result = convert(html, None);
|
|
110
|
-
assert!(matches!(result, Err(ConversionError::InvalidInput(_))));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
#[test]
|
|
114
|
-
fn test_utf16_hint_recovered() {
|
|
115
|
-
let html = String::from_utf8_lossy(b"\xFF\xFE<\0h\0t\0m\0l\0>\0").to_string();
|
|
116
|
-
let result = convert(&html, None);
|
|
117
|
-
assert!(result.is_ok(), "UTF-16 input should be recovered instead of rejected");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
#[test]
|
|
121
|
-
fn test_plain_text_allowed() {
|
|
122
|
-
let result = convert("Just text", None).unwrap();
|
|
123
|
-
let content = result.content.unwrap_or_default();
|
|
124
|
-
assert!(content.contains("Just text"));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
#[test]
|
|
128
|
-
fn test_plain_text_escaped_when_enabled() {
|
|
129
|
-
let options = ConversionOptions {
|
|
130
|
-
escape_asterisks: true,
|
|
131
|
-
escape_underscores: true,
|
|
132
|
-
..ConversionOptions::default()
|
|
133
|
-
};
|
|
134
|
-
let result = convert("Text *asterisks* _underscores_", Some(options)).unwrap();
|
|
135
|
-
let content = result.content.unwrap_or_default();
|
|
136
|
-
assert!(content.contains(r"\*asterisks\*"));
|
|
137
|
-
assert!(content.contains(r"\_underscores\_"));
|
|
138
|
-
}
|
|
139
|
-
}
|