use harfrust::{Direction, Script, Tag}; use icu_properties::{CodePointMapData, props::Script as IcuScript}; use unicode_bidi::BidiInfo; use crate::layout::WritingMode; use crate::types::{RenderBlock, TextDirection}; pub fn writing_mode_for_block(block: &RenderBlock) -> WritingMode { if block.text.is_empty() { return WritingMode::Horizontal; } // Non-CJK text always lays out horizontally regardless of bubble shape — // an English translation in a tall manga bubble still reads left-to-right. if !is_cjk_text(&block.text) { return WritingMode::Horizontal; } // CJK content: prefer the OCR/detector's source direction when available, // so bubble aspect ratio doesn't override a trusted signal. The bbox // heuristic is kept only as a fallback for user-added blocks that have // no recorded source direction. match block.source_direction { Some(TextDirection::Vertical) => WritingMode::VerticalRl, Some(TextDirection::Horizontal) => WritingMode::Horizontal, None => { if block.height > block.width { WritingMode::VerticalRl } else { WritingMode::Horizontal } } } } pub(crate) struct ScriptFlags { pub has_cjk: bool, pub rtl_script: Option, pub has_thai: bool, } pub(crate) fn detect_scripts(text: &str) -> ScriptFlags { let script_map = CodePointMapData::::new(); let (mut has_cjk, mut rtl_script, mut has_thai) = (false, None, false); for c in text.chars() { match script_map.get(c) { IcuScript::Han | IcuScript::Hiragana | IcuScript::Katakana | IcuScript::Hangul | IcuScript::Bopomofo => has_cjk = true, rtl @ (IcuScript::Arabic | IcuScript::Hebrew | IcuScript::Syriac | IcuScript::Thaana | IcuScript::Nko | IcuScript::Adlam) if rtl_script.is_none() => { rtl_script = Some(rtl); } IcuScript::Thai | IcuScript::Lao | IcuScript::Khmer | IcuScript::Myanmar => { has_thai = true } _ => {} } if has_cjk && rtl_script.is_some() && has_thai { break; } } ScriptFlags { has_cjk, rtl_script, has_thai, } } pub fn shaping_direction_for_text( text: &str, writing_mode: WritingMode, ) -> (Direction, Option