From 893c8fd44da16a496ad2e12bd4eb2ca5bc2a0eaa Mon Sep 17 00:00:00 2001 From: Abimael Martell <1450169+abimaelmartell@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:12:48 -0700 Subject: [PATCH] fix(markdown): address review of font-name consumers - Monotype is a foundry prefix on proportional faces (Monotype Corsiva, Monotype Garamond); it must not satisfy is_monospace_font's generic "mono" token. Regression tests pin both directions. - Flush the pending code block before inserting a positioned table or image, so a block that falls between two code lines cannot be emitted ahead of code that precedes it in reading order; a code line after the block reopens a new fence naturally. --- src/markdown/classify.rs | 17 +++++++++++++++++ src/markdown/convert.rs | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/src/markdown/classify.rs b/src/markdown/classify.rs index 25fa20e..0a828f5 100644 --- a/src/markdown/classify.rs +++ b/src/markdown/classify.rs @@ -233,6 +233,12 @@ pub(crate) fn line_is_monospace(line: &crate::types::TextLine) -> bool { /// Check if font name indicates monospace pub(crate) fn is_monospace_font(font_name: &str) -> bool { let lower = font_name.to_lowercase(); + // "Monotype" is a foundry prefix on proportional faces (Monotype + // Corsiva, Monotype Garamond) — it must not satisfy the generic "mono" + // token below. + if lower.contains("monotype") { + return false; + } let patterns = [ "courier", "consolas", @@ -257,6 +263,17 @@ pub(crate) fn is_monospace_font(font_name: &str) -> bool { mod tests { use super::*; + #[test] + fn monotype_foundry_faces_are_not_monospace() { + // "Monotype" is a foundry prefix on proportional faces; the generic + // "mono" token must not classify them as code fonts. + assert!(!is_monospace_font("MonotypeCorsiva")); + assert!(!is_monospace_font("ABCDEF+Monotype-Garamond")); + assert!(is_monospace_font("RobotoMono-Regular")); + assert!(is_monospace_font("PTMono")); + assert!(is_monospace_font("Courier")); + } + #[test] fn format_list_item_plain_bullet() { assert_eq!(format_list_item("● Item"), "- Item"); diff --git a/src/markdown/convert.rs b/src/markdown/convert.rs index b15b7f6..822b20e 100644 --- a/src/markdown/convert.rs +++ b/src/markdown/convert.rs @@ -877,6 +877,14 @@ pub(super) fn to_markdown_from_lines_with_tables_and_images( PositionedBlockKind::Image => inserted_images.contains(&(current_page, idx)), }; if positioned_block_precedes_line(block, line) && !already_inserted { + // Code lines buffer until their block closes; flush them + // first so this block cannot jump ahead of code that + // precedes it in reading order. A code line after the + // block reopens a new fence naturally. + if in_code_block { + flush_code_block(&mut output, &mut pending_code); + in_code_block = false; + } if in_paragraph { output.push_str("\n\n"); in_paragraph = false;