diff --git a/crates/typlite/src/common.rs b/crates/typlite/src/common.rs
index ff02fe77..4f34a7dd 100644
--- a/crates/typlite/src/common.rs
+++ b/crates/typlite/src/common.rs
@@ -208,11 +208,11 @@ impl InlineNode {
}
}
-/// Verbatim node for raw text output
+/// Inline verbatim node for raw text output
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = false, html_impl = true)]
pub struct VerbatimNode {
- /// The content to directly output
+ /// The content to directly output inline
pub content: EcoString,
}
@@ -226,6 +226,25 @@ impl VerbatimNode {
}
}
+/// Block verbatim node for raw text output without paragraph wrapping
+#[derive(Debug, PartialEq, Clone)]
+#[custom_node(block = true, html_impl = true)]
+pub struct BlockVerbatimNode {
+ /// The content to directly output as a block
+ pub content: EcoString,
+}
+
+impl BlockVerbatimNode {
+ fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
+ writer.write_str(&self.content)?;
+ Ok(())
+ }
+
+ fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
+ writer.write_trusted_html(&self.content)
+ }
+}
+
/// Alert node for alert messages
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = false)]
diff --git a/crates/typlite/src/fixtures/integration/snaps/convert@issue-1844.typ.snap b/crates/typlite/src/fixtures/integration/snaps/convert@issue-1844.typ.snap
index 58bea71f..28ba5000 100644
--- a/crates/typlite/src/fixtures/integration/snaps/convert@issue-1844.typ.snap
+++ b/crates/typlite/src/fixtures/integration/snaps/convert@issue-1844.typ.snap
@@ -64,7 +64,7 @@ separately rendered SVG
- ````
+````
== Under the Greenwood Tree
by Shakespeare.
@@ -74,8 +74,7 @@ Under the greenwood tree #emoji.tree
Who loves to lie with me,
...
```)
-````
-
+````
|
diff --git a/crates/typlite/src/fixtures/integration/snaps/docx_generation_hash@issue-1844.typ.snap b/crates/typlite/src/fixtures/integration/snaps/docx_generation_hash@issue-1844.typ.snap
index a8c37f80..1da63d1a 100644
--- a/crates/typlite/src/fixtures/integration/snaps/docx_generation_hash@issue-1844.typ.snap
+++ b/crates/typlite/src/fixtures/integration/snaps/docx_generation_hash@issue-1844.typ.snap
@@ -3,4 +3,4 @@ source: crates/typlite/src/tests.rs
expression: hash
input_file: crates/typlite/src/fixtures/integration/issue-1844.typ
---
-siphash128_13:5680ee2c48f93da5b40c162e391ef15
+siphash128_13:b700ae0ff0daf675a40354416190674e
diff --git a/crates/typlite/src/parser/core.rs b/crates/typlite/src/parser/core.rs
index d8a3c686..c91eb4da 100644
--- a/crates/typlite/src/parser/core.rs
+++ b/crates/typlite/src/parser/core.rs
@@ -14,10 +14,8 @@ use typst_html::{HtmlElement, HtmlNode, tag};
use crate::Result;
use crate::TypliteFeat;
-use crate::attributes::{
- AlertsAttr, HeadingAttr, RawAttr, TypliteAttrsParser, VerbatimAttr, md_attr,
-};
-use crate::common::{AlertNode, CenterNode, VerbatimNode};
+use crate::attributes::{AlertsAttr, HeadingAttr, RawAttr, TypliteAttrsParser, VerbatimAttr};
+use crate::common::{AlertNode, BlockVerbatimNode, CenterNode, VerbatimNode};
use crate::diagnostics::WarningCollector;
use crate::tags::md_tag;
@@ -196,9 +194,9 @@ impl HtmlToAstParser {
let attrs = VerbatimAttr::parse(&element.attrs)?;
if attrs.block {
self.flush_inline_buffer();
- self.blocks.push(Node::Paragraph(vec![Node::Custom(Box::new(
- VerbatimNode { content: attrs.src },
- ))]));
+ self.blocks.push(Node::Custom(Box::new(BlockVerbatimNode {
+ content: attrs.src,
+ })));
} else {
self.inline_buffer
.push(Node::Custom(Box::new(VerbatimNode { content: attrs.src })));
@@ -405,12 +403,8 @@ impl HtmlToAstParser {
}
fn is_verbatim_block(element: &HtmlElement) -> bool {
- element
- .attrs
- .0
- .iter()
- .find(|(name, _)| *name == md_attr::block)
- .and_then(|(_, value)| value.parse::().ok())
+ VerbatimAttr::parse(&element.attrs)
+ .map(|attrs| attrs.block)
.unwrap_or(false)
}
diff --git a/crates/typlite/src/writer/docx/writer.rs b/crates/typlite/src/writer/docx/writer.rs
index f37c7c9a..42c58ddc 100644
--- a/crates/typlite/src/writer/docx/writer.rs
+++ b/crates/typlite/src/writer/docx/writer.rs
@@ -4,13 +4,14 @@ use base64::Engine;
use cmark_writer::ast::{ListItem, Node};
use docx_rs::*;
use ecow::EcoString;
-use log::{debug, warn};
+use log::debug;
use std::fs;
use std::io::Cursor;
use crate::Result;
use crate::common::{
- CenterNode, FigureNode, FormatWriter, HighlightNode, InlineNode, VerbatimNode,
+ BlockVerbatimNode, CenterNode, FigureNode, FormatWriter, HighlightNode, InlineNode,
+ VerbatimNode,
};
use super::image_processor::DocxImageProcessor;
@@ -253,10 +254,7 @@ impl DocxWriter {
}
node if node.is_custom_type::() => {
let node = node.as_custom_type::().unwrap();
- warn!(
- "ignoring `m1verbatim` content in DOCX export: {:?}",
- node.content
- );
+ run = run.style("CodeInline").add_text(&node.content);
}
// Other inline element types
_ => {
@@ -504,6 +502,15 @@ impl DocxWriter {
docx = docx.add_paragraph(para);
}
}
+ node if node.is_custom_type::() => {
+ let block_node = node.as_custom_type::().unwrap();
+ for line in block_node.content.split('\n') {
+ let para = Paragraph::new()
+ .style("CodeBlock")
+ .add_run(Run::new().add_text(line));
+ docx = docx.add_paragraph(para);
+ }
+ }
node if node.is_custom_type::() => {
let inline_node = node.as_custom_type::().unwrap();
// Handle InlineNode at block level (convert to paragraph)
diff --git a/crates/typlite/src/writer/latex.rs b/crates/typlite/src/writer/latex.rs
index b3ef3698..27617164 100644
--- a/crates/typlite/src/writer/latex.rs
+++ b/crates/typlite/src/writer/latex.rs
@@ -8,8 +8,8 @@ use tinymist_std::path::unix_slash;
use crate::Result;
use crate::common::{
- CenterNode, ExternalFrameNode, FigureNode, FormatWriter, HighlightNode, InlineNode, ListState,
- VerbatimNode,
+ BlockVerbatimNode, CenterNode, ExternalFrameNode, FigureNode, FormatWriter, HighlightNode,
+ InlineNode, ListState, VerbatimNode,
};
/// LaTeX writer implementation
@@ -289,6 +289,11 @@ impl LaTeXWriter {
self.write_node(child, output)?;
}
}
+ node if node.is_custom_type::() => {
+ let block_node = node.as_custom_type::().unwrap();
+ output.push_str(&block_node.content);
+ output.push_str("\n\n");
+ }
node if node.is_custom_type::() => {
let inline_node = node.as_custom_type::().unwrap();
output.push_str(&inline_node.content);
diff --git a/crates/typlite/src/writer/text.rs b/crates/typlite/src/writer/text.rs
index 76f45cef..d8153c55 100644
--- a/crates/typlite/src/writer/text.rs
+++ b/crates/typlite/src/writer/text.rs
@@ -4,7 +4,7 @@ use cmark_writer::ast::Node;
use ecow::EcoString;
use crate::Result;
-use crate::common::{ExternalFrameNode, FigureNode, FormatWriter};
+use crate::common::{BlockVerbatimNode, ExternalFrameNode, FigureNode, FormatWriter, VerbatimNode};
/// Text writer implementation
#[derive(Default)]
@@ -161,6 +161,12 @@ impl TextWriter {
output.push_str(&external_frame.alt_text);
}
}
+ node if node.is_custom_type::() => {
+ if let Some(block_node) = node.as_custom_type::() {
+ output.push_str(&block_node.content);
+ output.push_str("\n\n");
+ }
+ }
node if node.is_custom_type::() => {
if let Some(highlight) = node.as_custom_type::() {
for child in &highlight.content {
@@ -168,6 +174,11 @@ impl TextWriter {
}
}
}
+ node if node.is_custom_type::() => {
+ if let Some(inline_node) = node.as_custom_type::() {
+ output.push_str(&inline_node.content);
+ }
+ }
_ => {}
}
Ok(())