diff --git a/src/paint/builder.rs b/src/paint/builder.rs index 6bfebcb6a..0fccaaa1d 100644 --- a/src/paint/builder.rs +++ b/src/paint/builder.rs @@ -57,55 +57,39 @@ impl LayerBuilder { } let own_ops = match &node.node_type { - RenderNodeType::PageBackground(background) => Some(vec![PaintOp::PageBackground { - bbox: node.bbox, - background: background.clone(), - }]), + RenderNodeType::PageBackground(background) => Some(vec![PaintOp::page_background( + node.bbox, + background.clone(), + )]), RenderNodeType::TextRun(run) => { Some(text_run_ops(node.bbox, run.clone(), self.output_options)) } - RenderNodeType::FootnoteMarker(marker) => Some(vec![PaintOp::FootnoteMarker { - bbox: node.bbox, - marker: marker.clone(), - }]), - RenderNodeType::Line(line) => Some(vec![PaintOp::Line { - bbox: node.bbox, - line: line.clone(), - }]), - RenderNodeType::Rectangle(rect) => Some(vec![PaintOp::Rectangle { - bbox: node.bbox, - rect: rect.clone(), - }]), - RenderNodeType::Ellipse(ellipse) => Some(vec![PaintOp::Ellipse { - bbox: node.bbox, - ellipse: ellipse.clone(), - }]), - RenderNodeType::Path(path) => Some(vec![PaintOp::Path { - bbox: node.bbox, - path: path.clone(), - }]), - RenderNodeType::Image(image) => Some(vec![PaintOp::Image { - bbox: node.bbox, - image: image.clone(), - resolved: crate::renderer::image_resolver::resolve_image_payload(image) - .map(Box::new), - }]), - RenderNodeType::Equation(equation) => Some(vec![PaintOp::Equation { - bbox: node.bbox, - equation: equation.clone(), - }]), - RenderNodeType::FormObject(form) => Some(vec![PaintOp::FormObject { - bbox: node.bbox, - form: form.clone(), - }]), - RenderNodeType::Placeholder(placeholder) => Some(vec![PaintOp::Placeholder { - bbox: node.bbox, - placeholder: placeholder.clone(), - }]), - RenderNodeType::RawSvg(raw) => Some(vec![PaintOp::RawSvg { - bbox: node.bbox, - raw: raw.clone(), - }]), + RenderNodeType::FootnoteMarker(marker) => { + Some(vec![PaintOp::footnote_marker(node.bbox, marker.clone())]) + } + RenderNodeType::Line(line) => Some(vec![PaintOp::line(node.bbox, line.clone())]), + RenderNodeType::Rectangle(rect) => { + Some(vec![PaintOp::rectangle(node.bbox, rect.clone())]) + } + RenderNodeType::Ellipse(ellipse) => { + Some(vec![PaintOp::ellipse(node.bbox, ellipse.clone())]) + } + RenderNodeType::Path(path) => Some(vec![PaintOp::path(node.bbox, path.clone())]), + RenderNodeType::Image(image) => Some(vec![PaintOp::image( + node.bbox, + image.clone(), + crate::renderer::image_resolver::resolve_image_payload(image), + )]), + RenderNodeType::Equation(equation) => { + Some(vec![PaintOp::equation(node.bbox, equation.clone())]) + } + RenderNodeType::FormObject(form) => { + Some(vec![PaintOp::form_object(node.bbox, form.clone())]) + } + RenderNodeType::Placeholder(placeholder) => { + Some(vec![PaintOp::placeholder(node.bbox, placeholder.clone())]) + } + RenderNodeType::RawSvg(raw) => Some(vec![PaintOp::raw_svg(node.bbox, raw.clone())]), _ => None, }; @@ -251,48 +235,36 @@ fn text_run_ops( + has_strikethrough as usize + has_emphasis_dot as usize, ); - ops.push(PaintOp::TextRun { - bbox, - run: run.clone(), - }); + ops.push(PaintOp::text_run(bbox, run.clone())); if has_char_overlap { - ops.push(PaintOp::CharOverlap { - bbox, - run: run.clone(), - }); + ops.push(PaintOp::char_overlap(bbox, run.clone())); } if has_control_mark { - ops.push(PaintOp::TextControlMark { - bbox, - run: run.clone(), - }); + ops.push(PaintOp::text_control_mark(bbox, run.clone())); } if has_tab_leader { - ops.push(PaintOp::TabLeader { - bbox, - run: run.clone(), - }); + ops.push(PaintOp::tab_leader(bbox, run.clone())); } if has_underline { - ops.push(PaintOp::TextDecoration { + ops.push(PaintOp::text_decoration( bbox, - run: run.clone(), - kind: TextDecorationKind::Underline, - }); + run.clone(), + TextDecorationKind::Underline, + )); } if has_strikethrough { - ops.push(PaintOp::TextDecoration { + ops.push(PaintOp::text_decoration( bbox, - run: run.clone(), - kind: TextDecorationKind::Strikethrough, - }); + run.clone(), + TextDecorationKind::Strikethrough, + )); } if has_emphasis_dot { - ops.push(PaintOp::TextDecoration { + ops.push(PaintOp::text_decoration( bbox, run, - kind: TextDecorationKind::EmphasisDot, - }); + TextDecorationKind::EmphasisDot, + )); } ops } diff --git a/src/paint/json.rs b/src/paint/json.rs index a02761952..10dc2201f 100644 --- a/src/paint/json.rs +++ b/src/paint/json.rs @@ -2582,9 +2582,9 @@ mod tests { #[test] fn serializes_text_and_shape_ops_for_browser_replay() { - let text = PaintOp::TextRun { - bbox: BoundingBox::new(10.0, 20.0, 80.0, 18.0), - run: TextRunNode { + let text = PaintOp::text_run( + BoundingBox::new(10.0, 20.0, 80.0, 18.0), + TextRunNode { text: "가A".to_string(), style: TextStyle { font_family: "Noto Sans KR".to_string(), @@ -2615,10 +2615,10 @@ mod tests { baseline: 13.0, field_marker: FieldMarkerType::FieldBegin, }, - }; - let rect = PaintOp::Rectangle { - bbox: BoundingBox::new(8.0, 18.0, 84.0, 22.0), - rect: crate::renderer::render_tree::RectangleNode::new( + ); + let rect = PaintOp::rectangle( + BoundingBox::new(8.0, 18.0, 84.0, 22.0), + crate::renderer::render_tree::RectangleNode::new( 4.0, ShapeStyle { fill_color: Some(0x00F0F1F2), @@ -2628,7 +2628,7 @@ mod tests { }, None, ), - }; + ); let tree = PageLayerTree::new( 120.0, @@ -2725,9 +2725,9 @@ mod tests { let display_text = "(인)(Signature)"; let source_positions = compute_char_positions(text, &style); let display_positions = compute_char_positions(display_text, &style); - let text_run = PaintOp::TextRun { - bbox: BoundingBox::new(10.0, 20.0, 80.0, 18.0), - run: TextRunNode { + let text_run = PaintOp::text_run( + BoundingBox::new(10.0, 20.0, 80.0, 18.0), + TextRunNode { text: text.to_string(), style: style.clone(), char_shape_id: None, @@ -2745,7 +2745,7 @@ mod tests { baseline: 13.0, field_marker: FieldMarkerType::None, }, - }; + ); let tree = PageLayerTree::new( 120.0, 80.0, @@ -2783,9 +2783,9 @@ mod tests { #[test] fn serializes_empty_display_positions_for_hidden_pua_filler() { - let text_run = PaintOp::TextRun { - bbox: BoundingBox::new(10.0, 20.0, 80.0, 18.0), - run: TextRunNode { + let text_run = PaintOp::text_run( + BoundingBox::new(10.0, 20.0, 80.0, 18.0), + TextRunNode { text: "\u{F081C}".to_string(), style: TextStyle { font_family: "Noto Sans KR".to_string(), @@ -2807,7 +2807,7 @@ mod tests { baseline: 13.0, field_marker: FieldMarkerType::None, }, - }; + ); let tree = PageLayerTree::new( 120.0, 80.0, @@ -2868,32 +2868,12 @@ mod tests { BoundingBox::new(0.0, 0.0, 120.0, 80.0), None, vec![ - PaintOp::TextRun { - bbox, - run: run.clone(), - }, - PaintOp::CharOverlap { - bbox, - run: run.clone(), - }, - PaintOp::TextControlMark { - bbox, - run: run.clone(), - }, - PaintOp::TabLeader { - bbox, - run: run.clone(), - }, - PaintOp::TextDecoration { - bbox, - run: run.clone(), - kind: TextDecorationKind::Underline, - }, - PaintOp::TextDecoration { - bbox, - run, - kind: TextDecorationKind::EmphasisDot, - }, + PaintOp::text_run(bbox, run.clone()), + PaintOp::char_overlap(bbox, run.clone()), + PaintOp::text_control_mark(bbox, run.clone()), + PaintOp::tab_leader(bbox, run.clone()), + PaintOp::text_decoration(bbox, run.clone(), TextDecorationKind::Underline), + PaintOp::text_decoration(bbox, run, TextDecorationKind::EmphasisDot), ], ), ); @@ -2940,9 +2920,9 @@ mod tests { shaping_engine: ShapingEngineId("test".to_string()), fallback_policy: FontFallbackPolicyId("none".to_string()), }; - let text_run = PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: TextRunNode { + let text_run = PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + TextRunNode { text: "A".to_string(), style: TextStyle { font_family: "Test".to_string(), @@ -2965,7 +2945,7 @@ mod tests { baseline: 12.0, field_marker: FieldMarkerType::None, }, - }; + ); let glyph_run = PaintOp::GlyphRun { bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), run: Box::new(LayerGlyphRunPaint { @@ -3119,9 +3099,9 @@ mod tests { utf16_range: TextSourceRange::new(0, 1), stable_source_key: None, }; - let text_run = PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: TextRunNode { + let text_run = PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + TextRunNode { text: "A".to_string(), style: TextStyle { font_family: "Test".to_string(), @@ -3144,7 +3124,7 @@ mod tests { baseline: 12.0, field_marker: FieldMarkerType::None, }, - }; + ); let outline = PaintOp::GlyphOutline { bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), outline: Box::new(LayerGlyphOutlinePaint { @@ -3605,18 +3585,11 @@ mod tests { BoundingBox::new(0.0, 0.0, 120.0, 80.0), None, vec![ - PaintOp::Path { - bbox: BoundingBox::new(1.0, 2.0, 30.0, 20.0), - path, - }, - PaintOp::Image { - bbox: BoundingBox::new(3.0, 4.0, 30.0, 20.0), - image, - resolved: None, - }, - PaintOp::Equation { - bbox: BoundingBox::new(5.0, 6.0, 30.0, 20.0), - equation: EquationNode { + PaintOp::path(BoundingBox::new(1.0, 2.0, 30.0, 20.0), path), + PaintOp::image(BoundingBox::new(3.0, 4.0, 30.0, 20.0), image, None), + PaintOp::equation( + BoundingBox::new(5.0, 6.0, 30.0, 20.0), + EquationNode { svg_content: "x".to_string(), layout_box: LayoutBox { x: 0.0, @@ -3636,21 +3609,21 @@ mod tests { cell_para_index: None, note_ref: None, }, - }, - PaintOp::Placeholder { - bbox: BoundingBox::new(7.0, 8.0, 30.0, 20.0), - placeholder: PlaceholderNode { + ), + PaintOp::placeholder( + BoundingBox::new(7.0, 8.0, 30.0, 20.0), + PlaceholderNode { fill_color: 0x00F0F0F0, stroke_color: 0x00000000, label: "OLE".to_string(), }, - }, - PaintOp::RawSvg { - bbox: BoundingBox::new(9.0, 10.0, 30.0, 20.0), - raw: RawSvgNode { + ), + PaintOp::raw_svg( + BoundingBox::new(9.0, 10.0, 30.0, 20.0), + RawSvgNode { svg: "".to_string(), }, - }, + ), ], ), ); diff --git a/src/paint/paint_op.rs b/src/paint/paint_op.rs index 33788af42..91c62b800 100644 --- a/src/paint/paint_op.rs +++ b/src/paint/paint_op.rs @@ -49,11 +49,11 @@ pub struct ResolvedImagePayload { pub enum PaintOp { PageBackground { bbox: BoundingBox, - background: PageBackgroundNode, + background: Box, }, TextRun { bbox: BoundingBox, - run: TextRunNode, + run: Box, }, GlyphRun { bbox: BoundingBox, @@ -69,68 +69,200 @@ pub enum PaintOp { /// 새 backend는 이 op를 선택하고 TextRun mirror를 건너뛸 수 있다. CharOverlap { bbox: BoundingBox, - run: TextRunNode, + run: Box, }, /// 문단 끝/줄 바꿈/필드 마커처럼 source text와 visual projection이 다른 표식. TextControlMark { bbox: BoundingBox, - run: TextRunNode, + run: Box, }, /// 탭 리더 visual geometry. TabLeader { bbox: BoundingBox, - run: TextRunNode, + run: Box, }, /// 밑줄/취소선/강조점 visual geometry. TextDecoration { bbox: BoundingBox, - run: TextRunNode, + run: Box, kind: TextDecorationKind, }, FootnoteMarker { bbox: BoundingBox, - marker: FootnoteMarkerNode, + marker: Box, }, Line { bbox: BoundingBox, - line: LineNode, + line: Box, }, Rectangle { bbox: BoundingBox, - rect: RectangleNode, + rect: Box, }, Ellipse { bbox: BoundingBox, - ellipse: EllipseNode, + ellipse: Box, }, Path { bbox: BoundingBox, - path: PathNode, + path: Box, }, Image { bbox: BoundingBox, - image: ImageNode, + image: Box, resolved: Option>, }, Equation { bbox: BoundingBox, - equation: EquationNode, + equation: Box, }, FormObject { bbox: BoundingBox, - form: FormObjectNode, + form: Box, }, Placeholder { bbox: BoundingBox, - placeholder: PlaceholderNode, + placeholder: Box, }, RawSvg { bbox: BoundingBox, - raw: RawSvgNode, + raw: Box, }, } impl PaintOp { + pub fn page_background(bbox: BoundingBox, background: PageBackgroundNode) -> Self { + Self::PageBackground { + bbox, + background: Box::new(background), + } + } + + pub fn text_run(bbox: BoundingBox, run: TextRunNode) -> Self { + Self::TextRun { + bbox, + run: Box::new(run), + } + } + + pub fn glyph_run(bbox: BoundingBox, run: LayerGlyphRunPaint) -> Self { + Self::GlyphRun { + bbox, + run: Box::new(run), + } + } + + pub fn glyph_outline(bbox: BoundingBox, outline: LayerGlyphOutlinePaint) -> Self { + Self::GlyphOutline { + bbox, + outline: Box::new(outline), + } + } + + pub fn char_overlap(bbox: BoundingBox, run: TextRunNode) -> Self { + Self::CharOverlap { + bbox, + run: Box::new(run), + } + } + + pub fn text_control_mark(bbox: BoundingBox, run: TextRunNode) -> Self { + Self::TextControlMark { + bbox, + run: Box::new(run), + } + } + + pub fn tab_leader(bbox: BoundingBox, run: TextRunNode) -> Self { + Self::TabLeader { + bbox, + run: Box::new(run), + } + } + + pub fn text_decoration(bbox: BoundingBox, run: TextRunNode, kind: TextDecorationKind) -> Self { + Self::TextDecoration { + bbox, + run: Box::new(run), + kind, + } + } + + pub fn footnote_marker(bbox: BoundingBox, marker: FootnoteMarkerNode) -> Self { + Self::FootnoteMarker { + bbox, + marker: Box::new(marker), + } + } + + pub fn line(bbox: BoundingBox, line: LineNode) -> Self { + Self::Line { + bbox, + line: Box::new(line), + } + } + + pub fn rectangle(bbox: BoundingBox, rect: RectangleNode) -> Self { + Self::Rectangle { + bbox, + rect: Box::new(rect), + } + } + + pub fn ellipse(bbox: BoundingBox, ellipse: EllipseNode) -> Self { + Self::Ellipse { + bbox, + ellipse: Box::new(ellipse), + } + } + + pub fn path(bbox: BoundingBox, path: PathNode) -> Self { + Self::Path { + bbox, + path: Box::new(path), + } + } + + pub fn image( + bbox: BoundingBox, + image: ImageNode, + resolved: Option, + ) -> Self { + Self::Image { + bbox, + image: Box::new(image), + resolved: resolved.map(Box::new), + } + } + + pub fn equation(bbox: BoundingBox, equation: EquationNode) -> Self { + Self::Equation { + bbox, + equation: Box::new(equation), + } + } + + pub fn form_object(bbox: BoundingBox, form: FormObjectNode) -> Self { + Self::FormObject { + bbox, + form: Box::new(form), + } + } + + pub fn placeholder(bbox: BoundingBox, placeholder: PlaceholderNode) -> Self { + Self::Placeholder { + bbox, + placeholder: Box::new(placeholder), + } + } + + pub fn raw_svg(bbox: BoundingBox, raw: RawSvgNode) -> Self { + Self::RawSvg { + bbox, + raw: Box::new(raw), + } + } + pub fn bounds(&self) -> BoundingBox { match self { PaintOp::PageBackground { bbox, .. } diff --git a/src/paint/replay_order.rs b/src/paint/replay_order.rs index 9cb83358d..249461571 100644 --- a/src/paint/replay_order.rs +++ b/src/paint/replay_order.rs @@ -105,11 +105,7 @@ mod tests { fn image_with_wrap(wrap: Option) -> PaintOp { let mut image = ImageNode::new(1, Some(vec![1, 2, 3])); image.text_wrap = wrap; - PaintOp::Image { - bbox: bbox(), - image, - resolved: None, - } + PaintOp::image(bbox(), image, None) } #[test] @@ -122,16 +118,16 @@ mod tests { #[test] fn page_background_replays_on_background_plane() { - let op = PaintOp::PageBackground { - bbox: bbox(), - background: PageBackgroundNode { + let op = PaintOp::page_background( + bbox(), + PageBackgroundNode { background_color: None, border_color: None, border_width: 0.0, gradient: None, image: None, }, - }; + ); assert_eq!(paint_op_replay_plane(&op), PaintReplayPlane::Background); } @@ -154,10 +150,8 @@ mod tests { fn non_layered_ops_replay_on_flow_plane() { let plain_image = image_with_wrap(None); let top_and_bottom_image = image_with_wrap(Some(TextWrap::TopAndBottom)); - let vector = PaintOp::Rectangle { - bbox: bbox(), - rect: RectangleNode::new(0.0, ShapeStyle::default(), None), - }; + let vector = + PaintOp::rectangle(bbox(), RectangleNode::new(0.0, ShapeStyle::default(), None)); assert_eq!(paint_op_replay_plane(&plain_image), PaintReplayPlane::Flow); assert_eq!( @@ -169,10 +163,8 @@ mod tests { #[test] fn render_layer_metadata_overrides_non_image_paint_ops() { - let vector = PaintOp::Rectangle { - bbox: bbox(), - rect: RectangleNode::new(0.0, ShapeStyle::default(), None), - }; + let vector = + PaintOp::rectangle(bbox(), RectangleNode::new(0.0, ShapeStyle::default(), None)); let behind_layer = RenderLayerInfo::new(Some(TextWrap::BehindText), 1, 1); let front_layer = RenderLayerInfo::new(Some(TextWrap::InFrontOfText), 2, 2); @@ -216,10 +208,10 @@ mod tests { let child = LayerNode::leaf( bbox(), None, - vec![PaintOp::Rectangle { - bbox: bbox(), - rect: RectangleNode::new(0.0, ShapeStyle::default(), None), - }], + vec![PaintOp::rectangle( + bbox(), + RectangleNode::new(0.0, ShapeStyle::default(), None), + )], ); let group = LayerNode::group( bbox(), diff --git a/src/paint/text_shape.rs b/src/paint/text_shape.rs index c837c4201..5c23aa88c 100644 --- a/src/paint/text_shape.rs +++ b/src/paint/text_shape.rs @@ -513,10 +513,10 @@ mod tests { let root = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: text_run("A"), - }], + vec![PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + text_run("A"), + )], ); let lowerer = TextShapeLowerer::diagnostics_only(&PortableResolver); let report = lowerer.analyze_root(&root); @@ -539,10 +539,10 @@ mod tests { let mut root = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: text_run("A"), - }], + vec![PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + text_run("A"), + )], ); let lowerer = TextShapeLowerer::new(&PortableResolver); let report = lowerer.lower_root(&mut root); @@ -574,10 +574,10 @@ mod tests { let mut root = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: text_run("A"), - }], + vec![PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + text_run("A"), + )], ); let lowerer = TextShapeLowerer::new(&EmittingResolver); let report = lowerer.lower_root(&mut root); @@ -603,10 +603,10 @@ mod tests { let mut root = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: text_run("A"), - }], + vec![PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + text_run("A"), + )], ); let lowerer = TextShapeLowerer::new(&EmittingResolver); let first_report = lowerer.lower_root(&mut root); @@ -632,10 +632,10 @@ mod tests { let mut root = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), + vec![PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), run, - }], + )], ); let lowerer = TextShapeLowerer::new(&EmittingResolver); let report = lowerer.lower_root(&mut root); diff --git a/src/paint/text_v2.rs b/src/paint/text_v2.rs index 08e9c102d..c6883dc0f 100644 --- a/src/paint/text_v2.rs +++ b/src/paint/text_v2.rs @@ -830,10 +830,7 @@ mod tests { } fn text_op(text: &str) -> PaintOp { - PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run: text_run(text), - } + PaintOp::text_run(BoundingBox::new(0.0, 0.0, 20.0, 20.0), text_run(text)) } fn add_portable_font_resources(resources: &mut ResourceArena) { @@ -1458,10 +1455,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), + vec![PaintOp::text_run( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), run, - }], + )], ), ); let diagnostics = TextV2Diagnostics::from_layer_tree(&tree); @@ -1486,10 +1483,7 @@ mod tests { BoundingBox::new(0.0, 0.0, 100.0, 100.0), None, vec![ - PaintOp::TextRun { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - run, - }, + PaintOp::text_run(BoundingBox::new(0.0, 0.0, 20.0, 20.0), run), glyph_op_for_text("A\tB", None, 0), ], ), diff --git a/src/paint/text_variants.rs b/src/paint/text_variants.rs index 722adac6e..a5e78464e 100644 --- a/src/paint/text_variants.rs +++ b/src/paint/text_variants.rs @@ -325,9 +325,9 @@ mod tests { } fn text_op() -> PaintOp { - PaintOp::TextRun { - bbox: bbox(), - run: TextRunNode { + PaintOp::text_run( + bbox(), + TextRunNode { text: "A".to_string(), style: TextStyle::default(), char_shape_id: None, @@ -345,7 +345,7 @@ mod tests { baseline: 10.0, field_marker: FieldMarkerType::None, }, - } + ) } fn glyph_op(variant: PaintVariantMeta) -> PaintOp { diff --git a/src/renderer/canvaskit_policy.rs b/src/renderer/canvaskit_policy.rs index 6980d7b0a..3a9fa6dd7 100644 --- a/src/renderer/canvaskit_policy.rs +++ b/src/renderer/canvaskit_policy.rs @@ -825,11 +825,11 @@ mod tests { #[test] fn default_mode_reports_simple_image_as_direct() { - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), - image: ImageNode::new(1, Some(vec![1, 2, 3])), - resolved: None, - }]); + let tree = tree_with_ops(vec![PaintOp::image( + bbox(), + ImageNode::new(1, Some(vec![1, 2, 3])), + None, + )]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -847,11 +847,11 @@ mod tests { #[test] fn compat_mode_reports_simple_image_as_direct() { - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), - image: ImageNode::new(1, Some(vec![1, 2, 3])), - resolved: None, - }]); + let tree = tree_with_ops(vec![PaintOp::image( + bbox(), + ImageNode::new(1, Some(vec![1, 2, 3])), + None, + )]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Compat); @@ -876,11 +876,7 @@ mod tests { image.crop = Some((10, 20, 90, 80)); image.transform.rotation = 15.0; - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), - image, - resolved: None, - }]); + let tree = tree_with_ops(vec![PaintOp::image(bbox(), image, None)]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -898,11 +894,7 @@ mod tests { image.brightness = 10; image.contrast = -20; - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), - image, - resolved: None, - }]); + let tree = tree_with_ops(vec![PaintOp::image(bbox(), image, None)]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -920,16 +912,16 @@ mod tests { image.brightness = 70; image.contrast = -50; - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), + let tree = tree_with_ops(vec![PaintOp::image( + bbox(), image, - resolved: Some(Box::new(ResolvedImagePayload { + Some(ResolvedImagePayload { data: vec![4, 5, 6], mime: "image/png", kind: ResolvedImageKind::BakedWatermark, suppress_effects: true, - })), - }]); + }), + )]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -948,24 +940,10 @@ mod tests { front.text_wrap = Some(TextWrap::InFrontOfText); let tree = tree_with_ops(vec![ - PaintOp::PageBackground { - bbox: bbox(), - background: page_background(None, None), - }, - PaintOp::Image { - bbox: bbox(), - image: behind, - resolved: None, - }, - PaintOp::TextRun { - bbox: bbox(), - run: text_run("A"), - }, - PaintOp::Image { - bbox: bbox(), - image: front, - resolved: None, - }, + PaintOp::page_background(bbox(), page_background(None, None)), + PaintOp::image(bbox(), behind, None), + PaintOp::text_run(bbox(), text_run("A")), + PaintOp::image(bbox(), front, None), ]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -989,20 +967,14 @@ mod tests { let layered_rect = LayerNode::leaf( bbox(), None, - vec![PaintOp::Rectangle { - bbox: bbox(), - rect: RectangleNode::new(0.0, ShapeStyle::default(), None), - }], + vec![PaintOp::rectangle( + bbox(), + RectangleNode::new(0.0, ShapeStyle::default(), None), + )], ) .with_layer(Some(RenderLayerInfo::new(Some(TextWrap::BehindText), 1, 1))); - let flow_text = LayerNode::leaf( - bbox(), - None, - vec![PaintOp::TextRun { - bbox: bbox(), - run: text_run("A"), - }], - ); + let flow_text = + LayerNode::leaf(bbox(), None, vec![PaintOp::text_run(bbox(), text_run("A"))]); let tree = PageLayerTree::new( 100.0, 100.0, @@ -1034,11 +1006,7 @@ mod tests { let mut image = ImageNode::new(1, Some(vec![1, 2, 3])); image.external_path = Some("linked-image.png".to_string()); - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), - image, - resolved: None, - }]); + let tree = tree_with_ops(vec![PaintOp::image(bbox(), image, None)]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -1054,11 +1022,7 @@ mod tests { let mut image = ImageNode::new(1, None); image.external_path = Some("linked-image.png".to_string()); - let tree = tree_with_ops(vec![PaintOp::Image { - bbox: bbox(), - image, - resolved: None, - }]); + let tree = tree_with_ops(vec![PaintOp::image(bbox(), image, None)]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -1093,14 +1057,8 @@ mod tests { })), ); let tree = tree_with_ops(vec![ - PaintOp::PageBackground { - bbox: bbox(), - background: image_background, - }, - PaintOp::PageBackground { - bbox: bbox(), - background: gradient_background, - }, + PaintOp::page_background(bbox(), image_background), + PaintOp::page_background(bbox(), gradient_background), ]); let default_plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -1131,14 +1089,8 @@ mod tests { let mut vertical = text_run("A"); vertical.is_vertical = true; let tree = tree_with_ops(vec![ - PaintOp::TextRun { - bbox: bbox(), - run: text_run("A"), - }, - PaintOp::TextRun { - bbox: bbox(), - run: vertical, - }, + PaintOp::text_run(bbox(), text_run("A")), + PaintOp::text_run(bbox(), vertical), ]); let default_plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -1158,10 +1110,7 @@ mod tests { #[test] fn text_run_op_type_matches_layer_tree_schema_name() { - let tree = tree_with_ops(vec![PaintOp::TextRun { - bbox: bbox(), - run: text_run("A"), - }]); + let tree = tree_with_ops(vec![PaintOp::text_run(bbox(), text_run("A"))]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); @@ -1170,9 +1119,9 @@ mod tests { #[test] fn footnote_marker_is_reported_as_text_special_visual() { - let tree = tree_with_ops(vec![PaintOp::FootnoteMarker { - bbox: bbox(), - marker: FootnoteMarkerNode { + let tree = tree_with_ops(vec![PaintOp::footnote_marker( + bbox(), + FootnoteMarkerNode { number: 1, text: "1)".to_string(), base_font_size: 12.0, @@ -1182,7 +1131,7 @@ mod tests { para_index: 0, control_index: 0, }, - }]); + )]); let default_plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); assert_eq!( @@ -1220,10 +1169,7 @@ mod tests { #[test] fn replay_plan_serializes_mode_and_summary() { - let tree = tree_with_ops(vec![PaintOp::TextRun { - bbox: bbox(), - run: text_run("A"), - }]); + let tree = tree_with_ops(vec![PaintOp::text_run(bbox(), text_run("A"))]); let plan = analyze_canvaskit_replay_plan(&tree, CanvasKitReplayMode::Default); let json = serde_json::to_string(&plan).expect("serialize CanvasKit replay plan"); diff --git a/src/renderer/layer_renderer.rs b/src/renderer/layer_renderer.rs index 836dfa728..00d7248d4 100644 --- a/src/renderer/layer_renderer.rs +++ b/src/renderer/layer_renderer.rs @@ -784,9 +784,9 @@ mod tests { } fn text_op() -> PaintOp { - PaintOp::TextRun { - bbox: bbox(), - run: TextRunNode { + PaintOp::text_run( + bbox(), + TextRunNode { text: "A".to_string(), style: TextStyle { font_family: "Test".to_string(), @@ -809,7 +809,7 @@ mod tests { baseline: 12.0, field_marker: FieldMarkerType::None, }, - } + ) } fn variant(kind: TextVariantKind, id: &str) -> PaintVariantMeta { diff --git a/src/renderer/skia/equation_conv.rs b/src/renderer/skia/equation_conv.rs index 18ff62200..468e7f329 100644 --- a/src/renderer/skia/equation_conv.rs +++ b/src/renderer/skia/equation_conv.rs @@ -1,6 +1,8 @@ use skia_safe::{font, paint, Canvas, Color, Font, FontMgr, FontStyle, Paint, PathBuilder}; -use super::font_lookup::{match_system_family_style, SystemFontFamilies}; +use super::font_lookup::{ + legacy_typeface_for_style, match_system_family_style, SystemFontFamilies, +}; use crate::renderer::equation::ast::MatrixStyle; use crate::renderer::equation::layout::{ @@ -699,7 +701,7 @@ fn draw_text( .map(str::trim) .filter(|family| !family.is_empty()) .find_map(|family| match_system_family_style(font_mgr, system_families, family, font_style)) - .or_else(|| font_mgr.legacy_make_typeface(None::<&str>, font_style)); + .or_else(|| legacy_typeface_for_style(font_mgr, font_style)); let mut font = if let Some(typeface) = typeface { Font::new(typeface, font_size as f32) } else { diff --git a/src/renderer/skia/font_lookup.rs b/src/renderer/skia/font_lookup.rs index 15c89c18f..fca21c536 100644 --- a/src/renderer/skia/font_lookup.rs +++ b/src/renderer/skia/font_lookup.rs @@ -1,9 +1,40 @@ -use std::collections::HashSet; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use skia_safe::{FontMgr, FontStyle, Typeface}; pub(super) type SystemFontFamilies = HashSet; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct FontStyleCacheKey { + weight: i32, + width: i32, + slant: i32, +} + +impl FontStyleCacheKey { + fn new(style: FontStyle) -> Self { + Self { + weight: *style.weight(), + width: *style.width(), + slant: style.slant() as i32, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct FontLookupKey { + family: String, + style: FontStyleCacheKey, +} + +thread_local! { + static SYSTEM_TYPEFACE_CACHE: RefCell>> = + RefCell::new(HashMap::new()); + static LEGACY_TYPEFACE_CACHE: RefCell>> = + RefCell::new(HashMap::new()); +} + pub(super) fn collect_system_families(font_mgr: &FontMgr) -> SystemFontFamilies { font_mgr.family_names().collect() } @@ -18,11 +49,36 @@ pub(super) fn match_system_family_style( family: &str, style: FontStyle, ) -> Option { - if has_system_family(system_families, family) { - font_mgr.match_family_style(family, style) - } else { - None + if !has_system_family(system_families, family) { + return None; } + + let key = FontLookupKey { + family: family.to_string(), + style: FontStyleCacheKey::new(style), + }; + SYSTEM_TYPEFACE_CACHE.with(|cache| { + if let Some(cached) = { cache.borrow().get(&key).cloned() } { + return cached; + } + + let matched = font_mgr.match_family_style(family, style); + cache.borrow_mut().insert(key, matched.clone()); + matched + }) +} + +pub(super) fn legacy_typeface_for_style(font_mgr: &FontMgr, style: FontStyle) -> Option { + let key = FontStyleCacheKey::new(style); + LEGACY_TYPEFACE_CACHE.with(|cache| { + if let Some(cached) = { cache.borrow().get(&key).cloned() } { + return cached; + } + + let matched = font_mgr.legacy_make_typeface(None::<&str>, style); + cache.borrow_mut().insert(key, matched.clone()); + matched + }) } #[cfg(test)] diff --git a/src/renderer/skia/image_conv.rs b/src/renderer/skia/image_conv.rs index b4dba6bfe..0212c2aee 100644 --- a/src/renderer/skia/image_conv.rs +++ b/src/renderer/skia/image_conv.rs @@ -3,6 +3,7 @@ use skia_safe::{ canvas::SrcRectConstraint, color_filters, image::RequiredProperties, Color, Data, FilterMode, IRect, Image, Matrix, MipmapMode, Paint, Rect, SamplingOptions, TileMode, }; +use std::sync::{Arc, OnceLock}; use crate::model::image::ImageEffect; use crate::model::style::ImageFillMode; @@ -361,10 +362,21 @@ fn svg_parse_options() -> usvg::Options<'static> { resolve_data: usvg::ImageHrefResolver::default_data_resolver(), resolve_string: Box::new(|_, _| None), }; - let fontdb = options.fontdb_mut(); - fontdb.load_system_fonts(); - fontdb.set_sans_serif_family("Noto Sans CJK KR"); - fontdb.set_serif_family("Noto Serif CJK KR"); - fontdb.set_monospace_family("D2Coding"); + options.fontdb = svg_fontdb(); options } + +fn svg_fontdb() -> Arc { + static SVG_FONTDB: OnceLock> = OnceLock::new(); + + SVG_FONTDB + .get_or_init(|| { + let mut fontdb = usvg::fontdb::Database::new(); + fontdb.load_system_fonts(); + fontdb.set_sans_serif_family("Noto Sans CJK KR"); + fontdb.set_serif_family("Noto Serif CJK KR"); + fontdb.set_monospace_family("D2Coding"); + Arc::new(fontdb) + }) + .clone() +} diff --git a/src/renderer/skia/renderer.rs b/src/renderer/skia/renderer.rs index 0d6095b3a..71badc5e9 100644 --- a/src/renderer/skia/renderer.rs +++ b/src/renderer/skia/renderer.rs @@ -1,6 +1,6 @@ use skia_safe::{ - paint, surfaces, Canvas, Color, EncodedImageFormat, Font, FontMgr, FontStyle, Paint, - PathBuilder, PathEffect, RRect, Rect, Typeface, + paint, png_encoder, surfaces, Canvas, Color, Font, FontMgr, FontStyle, Paint, PathBuilder, + PathEffect, RRect, Rect, Typeface, }; use std::borrow::Cow; use std::collections::{BTreeSet, HashMap, HashSet}; @@ -23,7 +23,10 @@ use crate::renderer::render_tree::RenderLayerInfo; use crate::renderer::{svg_arc_to_beziers, LineStyle, PathCommand, ShapeStyle, StrokeDash}; use super::equation_conv::render_equation; -use super::font_lookup::{collect_system_families, match_system_family_style, SystemFontFamilies}; +use super::font_lookup::{ + collect_system_families, legacy_typeface_for_style, match_system_family_style, + SystemFontFamilies, +}; use super::image_conv::{draw_image_bytes, draw_svg_fragment, ImageSampling}; use super::text_replay::SkiaTextReplay; @@ -401,6 +404,7 @@ impl SkiaLayerRenderer { if options.scale != 1.0 { canvas.scale((options.scale as f32, options.scale as f32)); } + let mut next_text_source_id = 0_u32; for replay_plane in PaintReplayPlane::ORDERED { if !layer_node_has_replay_plane(&tree.root, replay_plane) { @@ -418,9 +422,16 @@ impl SkiaLayerRenderer { } let image = surface.image_snapshot(); - let data = image - .encode(None, EncodedImageFormat::PNG, None) - .ok_or_else(|| HwpError::RenderError("Skia PNG 인코딩 실패".to_string()))?; + let mut png_options = png_encoder::Options::default(); + // PNG is lossless at every zlib level. Level 1 avoids spending most of + // native export time on compression while preserving decoded pixels. + png_options.z_lib_level = 1; + let data = png_encoder::encode_image( + None::<&mut skia_safe::gpu::DirectContext>, + &image, + &png_options, + ) + .ok_or_else(|| HwpError::RenderError("Skia PNG 인코딩 실패".to_string()))?; Ok(RasterRenderOutput { bytes: data.as_bytes().to_vec(), format: RasterOutputFormat::Png, @@ -1137,7 +1148,7 @@ impl SkiaLayerRenderer { return Font::new(tf, size); } } - if let Some(tf) = self.font_mgr.legacy_make_typeface(None::<&str>, style) { + if let Some(tf) = legacy_typeface_for_style(&self.font_mgr, style) { return Font::new(tf, size); } let mut f = Font::default(); @@ -1836,18 +1847,18 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, page_width, page_height), None, - vec![PaintOp::Rectangle { + vec![PaintOp::rectangle( bbox, - rect: RectangleNode::new(0.0, style, None), - }], + RectangleNode::new(0.0, style, None), + )], ), ) } fn solid_rect_op(bbox: BoundingBox, fill_color: ColorRef) -> PaintOp { - PaintOp::Rectangle { + PaintOp::rectangle( bbox, - rect: RectangleNode::new( + RectangleNode::new( 0.0, ShapeStyle { fill_color: Some(fill_color), @@ -1855,17 +1866,13 @@ mod tests { }, None, ), - } + ) } fn solid_image_op(bbox: BoundingBox, color: [u8; 4], wrap: TextWrap) -> PaintOp { let mut image = ImageNode::new(1, Some(solid_png(color))); image.text_wrap = Some(wrap); - PaintOp::Image { - bbox, - image, - resolved: None, - } + PaintOp::image(bbox, image, None) } #[test] @@ -1964,10 +1971,10 @@ mod tests { let child = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 20.0, 20.0), None, - vec![PaintOp::Rectangle { - bbox: BoundingBox::new(0.0, 0.0, 20.0, 20.0), - rect: RectangleNode::new(0.0, style, None), - }], + vec![PaintOp::rectangle( + BoundingBox::new(0.0, 0.0, 20.0, 20.0), + RectangleNode::new(0.0, style, None), + )], ); let clipped = PageLayerTree::new( 20.0, @@ -2114,16 +2121,16 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 8.0, 8.0), None, - vec![PaintOp::PageBackground { - bbox: BoundingBox::new(0.0, 0.0, 8.0, 8.0), - background: PageBackgroundNode { + vec![PaintOp::page_background( + BoundingBox::new(0.0, 0.0, 8.0, 8.0), + PageBackgroundNode { background_color: Some(0x0000ff00), border_color: Some(0x00ff0000), border_width: 2.0, gradient: None, image: None, }, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2148,9 +2155,9 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 8.0, 8.0), None, - vec![PaintOp::PageBackground { - bbox: BoundingBox::new(0.0, 0.0, 8.0, 8.0), - background: PageBackgroundNode { + vec![PaintOp::page_background( + BoundingBox::new(0.0, 0.0, 8.0, 8.0), + PageBackgroundNode { background_color: None, border_color: None, border_width: 0.0, @@ -2163,7 +2170,7 @@ mod tests { effect: crate::model::image::ImageEffect::RealPic, }), }, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2251,26 +2258,11 @@ mod tests { BoundingBox::new(0.0, 0.0, 40.0, 40.0), None, vec![ - PaintOp::Rectangle { - bbox: BoundingBox::new(2.0, 2.0, 10.0, 8.0), - rect: gradient_rect, - }, - PaintOp::Rectangle { - bbox: BoundingBox::new(16.0, 2.0, 10.0, 8.0), - rect: pattern_rect, - }, - PaintOp::Ellipse { - bbox: BoundingBox::new(2.0, 12.0, 10.0, 10.0), - ellipse, - }, - PaintOp::Path { - bbox: BoundingBox::new(2.0, 24.0, 10.0, 10.0), - path, - }, - PaintOp::Line { - bbox: BoundingBox::new(18.0, 28.0, 16.0, 4.0), - line, - }, + PaintOp::rectangle(BoundingBox::new(2.0, 2.0, 10.0, 8.0), gradient_rect), + PaintOp::rectangle(BoundingBox::new(16.0, 2.0, 10.0, 8.0), pattern_rect), + PaintOp::ellipse(BoundingBox::new(2.0, 12.0, 10.0, 10.0), ellipse), + PaintOp::path(BoundingBox::new(2.0, 24.0, 10.0, 10.0), path), + PaintOp::line(BoundingBox::new(18.0, 28.0, 16.0, 4.0), line), ], ), ); @@ -2311,10 +2303,7 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 24.0, 18.0), None, - vec![PaintOp::Path { - bbox: BoundingBox::new(4.0, 4.0, 16.0, 12.0), - path, - }], + vec![PaintOp::path(BoundingBox::new(4.0, 4.0, 16.0, 12.0), path)], ), ); let output = SkiaLayerRenderer::new() @@ -2334,16 +2323,16 @@ mod tests { BoundingBox::new(0.0, 0.0, 20.0, 10.0), None, vec![ - PaintOp::Image { - bbox: BoundingBox::new(0.0, 0.0, 8.0, 8.0), - image: ImageNode::new(1, Some(solid_png([0, 0, 255, 255]))), - resolved: None, - }, - PaintOp::Image { - bbox: BoundingBox::new(10.0, 0.0, 8.0, 8.0), - image: ImageNode::new(2, Some(vec![1, 2, 3, 4])), - resolved: None, - }, + PaintOp::image( + BoundingBox::new(0.0, 0.0, 8.0, 8.0), + ImageNode::new(1, Some(solid_png([0, 0, 255, 255]))), + None, + ), + PaintOp::image( + BoundingBox::new(10.0, 0.0, 8.0, 8.0), + ImageNode::new(2, Some(vec![1, 2, 3, 4])), + None, + ), ], ), ); @@ -2462,11 +2451,11 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 8.0, 8.0), None, - vec![PaintOp::Image { - bbox: BoundingBox::new(0.0, 0.0, 8.0, 8.0), - image: node, - resolved: None, - }], + vec![PaintOp::image( + BoundingBox::new(0.0, 0.0, 8.0, 8.0), + node, + None, + )], ), ); let output = SkiaLayerRenderer::new() @@ -2493,11 +2482,11 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 16.0, 4.0), None, - vec![PaintOp::Image { - bbox: BoundingBox::new(0.0, 0.0, 16.0, 4.0), - image: node, - resolved: None, - }], + vec![PaintOp::image( + BoundingBox::new(0.0, 0.0, 16.0, 4.0), + node, + None, + )], ), ); let output = SkiaLayerRenderer::new() @@ -2525,11 +2514,11 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 8.0, 8.0), None, - vec![PaintOp::Image { - bbox: BoundingBox::new(0.0, 0.0, 8.0, 8.0), - image: node, - resolved: None, - }], + vec![PaintOp::image( + BoundingBox::new(0.0, 0.0, 8.0, 8.0), + node, + None, + )], ), ); let output = SkiaLayerRenderer::new() @@ -2552,11 +2541,11 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 8.0, 8.0), None, - vec![PaintOp::Image { - bbox: BoundingBox::new(f64::NAN, 0.0, 8.0, 8.0), - image: ImageNode::new(1, Some(solid_png([255, 0, 0, 255]))), - resolved: None, - }], + vec![PaintOp::image( + BoundingBox::new(f64::NAN, 0.0, 8.0, 8.0), + ImageNode::new(1, Some(solid_png([255, 0, 0, 255]))), + None, + )], ), ); let output = SkiaLayerRenderer::new() @@ -2608,14 +2597,8 @@ mod tests { BoundingBox::new(0.0, 0.0, 64.0, 32.0), None, vec![ - PaintOp::TextRun { - bbox: BoundingBox::new(4.0, 4.0, 24.0, 24.0), - run, - }, - PaintOp::FootnoteMarker { - bbox: BoundingBox::new(32.0, 4.0, 24.0, 24.0), - marker, - }, + PaintOp::text_run(BoundingBox::new(4.0, 4.0, 24.0, 24.0), run), + PaintOp::footnote_marker(BoundingBox::new(32.0, 4.0, 24.0, 24.0), marker), ], ), ); @@ -2660,10 +2643,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 40.0, 40.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(8.0, 8.0, 24.0, 24.0), + vec![PaintOp::text_run( + BoundingBox::new(8.0, 8.0, 24.0, 24.0), run, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2709,10 +2692,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 88.0, 36.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(4.0, 4.0, 80.0, 28.0), + vec![PaintOp::text_run( + BoundingBox::new(4.0, 4.0, 80.0, 28.0), run, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2753,10 +2736,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 72.0, 36.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(4.0, 4.0, 60.0, 28.0), + vec![PaintOp::text_run( + BoundingBox::new(4.0, 4.0, 60.0, 28.0), run, - }], + )], ), ) .with_output_options(LayerOutputOptions { @@ -2805,10 +2788,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 48.0, 40.0), None, - vec![PaintOp::TextRun { - bbox: BoundingBox::new(8.0, 8.0, 32.0, 28.0), + vec![PaintOp::text_run( + BoundingBox::new(8.0, 8.0, 32.0, 28.0), run, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2845,10 +2828,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 64.0, 48.0), None, - vec![PaintOp::Equation { - bbox: BoundingBox::new(6.0, 6.0, 44.0, 32.0), + vec![PaintOp::equation( + BoundingBox::new(6.0, 6.0, 44.0, 32.0), equation, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2892,10 +2875,10 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 64.0, 48.0), None, - vec![PaintOp::Equation { - bbox: BoundingBox::new(6.0, 6.0, 44.0, 32.0), + vec![PaintOp::equation( + BoundingBox::new(6.0, 6.0, 44.0, 32.0), equation, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -2936,24 +2919,21 @@ mod tests { BoundingBox::new(0.0, 0.0, 48.0, 16.0), None, vec![ - PaintOp::Placeholder { - bbox: BoundingBox::new(0.0, 0.0, 14.0, 14.0), - placeholder: PlaceholderNode { + PaintOp::placeholder( + BoundingBox::new(0.0, 0.0, 14.0, 14.0), + PlaceholderNode { fill_color: 0, stroke_color: 0, label: "ph".to_string(), }, - }, - PaintOp::RawSvg { - bbox: BoundingBox::new(16.0, 0.0, 14.0, 14.0), - raw: RawSvgNode { + ), + PaintOp::raw_svg( + BoundingBox::new(16.0, 0.0, 14.0, 14.0), + RawSvgNode { svg: "" .to_string(), }, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -3011,15 +2991,15 @@ mod tests { LayerNode::leaf( BoundingBox::new(0.0, 0.0, 32.0, 24.0), None, - vec![PaintOp::RawSvg { - bbox: BoundingBox::new(4.0, 4.0, 20.0, 16.0), - raw: RawSvgNode { + vec![PaintOp::raw_svg( + BoundingBox::new(4.0, 4.0, 20.0, 16.0), + RawSvgNode { svg: format!( "", external_href ), }, - }], + )], ), ); let output = SkiaLayerRenderer::new() @@ -3040,9 +3020,9 @@ mod tests { let red = LayerNode::leaf( BoundingBox::new(0.0, 0.0, 12.0, 12.0), None, - vec![PaintOp::Rectangle { - bbox: BoundingBox::new(0.0, 0.0, 12.0, 12.0), - rect: RectangleNode::new( + vec![PaintOp::rectangle( + BoundingBox::new(0.0, 0.0, 12.0, 12.0), + RectangleNode::new( 0.0, ShapeStyle { fill_color: Some(0x000000ff), @@ -3050,14 +3030,14 @@ mod tests { }, None, ), - }], + )], ); let blue = LayerNode::leaf( BoundingBox::new(3.0, 3.0, 6.0, 6.0), None, - vec![PaintOp::Rectangle { - bbox: BoundingBox::new(3.0, 3.0, 6.0, 6.0), - rect: RectangleNode::new( + vec![PaintOp::rectangle( + BoundingBox::new(3.0, 3.0, 6.0, 6.0), + RectangleNode::new( 0.0, ShapeStyle { fill_color: Some(0x00ff0000), @@ -3065,7 +3045,7 @@ mod tests { }, None, ), - }], + )], ); let tree = PageLayerTree::new( 12.0, diff --git a/src/renderer/skia/text_replay.rs b/src/renderer/skia/text_replay.rs index bd4d28ee6..6eac33a90 100644 --- a/src/renderer/skia/text_replay.rs +++ b/src/renderer/skia/text_replay.rs @@ -13,7 +13,9 @@ use crate::renderer::layout::{compute_char_positions, split_into_clusters}; use crate::renderer::render_tree::BoundingBox; use crate::renderer::{clamp_tab_leader_end_x, TextStyle}; -use super::font_lookup::{match_system_family_style, SystemFontFamilies}; +use super::font_lookup::{ + legacy_typeface_for_style, match_system_family_style, SystemFontFamilies, +}; use super::renderer::colorref_to_skia; pub(super) struct SkiaTextReplay<'a> { @@ -115,7 +117,7 @@ impl SkiaTextReplay<'_> { push(&mut chain, &mut seen, tf); } } - if let Some(tf) = self.font_mgr.legacy_make_typeface(None::<&str>, font_style) { + if let Some(tf) = legacy_typeface_for_style(self.font_mgr, font_style) { push(&mut chain, &mut seen, tf); } chain @@ -693,10 +695,7 @@ impl SkiaTextReplay<'_> { "DejaVu Sans", FontStyle::normal(), ) - .or_else(|| { - self.font_mgr - .legacy_make_typeface(None::<&str>, FontStyle::normal()) - }) + .or_else(|| legacy_typeface_for_style(self.font_mgr, FontStyle::normal())) .map(|tf| Font::new(tf, size)) .unwrap_or_else(|| { let mut font = Font::default(); diff --git a/src/renderer/svg_layer.rs b/src/renderer/svg_layer.rs index 375c1090a..415e8e3a7 100644 --- a/src/renderer/svg_layer.rs +++ b/src/renderer/svg_layer.rs @@ -124,37 +124,37 @@ impl SvgLayerRenderer { let node = match op { PaintOp::PageBackground { bbox, background } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::PageBackground(background.clone()), + RenderNodeType::PageBackground(background.as_ref().clone()), *bbox, ), PaintOp::TextRun { bbox, run } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::TextRun(run.clone()), + RenderNodeType::TextRun(run.as_ref().clone()), *bbox, ), PaintOp::FootnoteMarker { bbox, marker } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::FootnoteMarker(marker.clone()), + RenderNodeType::FootnoteMarker(marker.as_ref().clone()), *bbox, ), PaintOp::Line { bbox, line } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::Line(line.clone()), + RenderNodeType::Line(line.as_ref().clone()), *bbox, ), PaintOp::Rectangle { bbox, rect } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::Rectangle(rect.clone()), + RenderNodeType::Rectangle(rect.as_ref().clone()), *bbox, ), PaintOp::Ellipse { bbox, ellipse } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::Ellipse(ellipse.clone()), + RenderNodeType::Ellipse(ellipse.as_ref().clone()), *bbox, ), PaintOp::Path { bbox, path } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::Path(path.clone()), + RenderNodeType::Path(path.as_ref().clone()), *bbox, ), PaintOp::Image { @@ -173,22 +173,22 @@ impl SvgLayerRenderer { ), PaintOp::Equation { bbox, equation } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::Equation(equation.clone()), + RenderNodeType::Equation(equation.as_ref().clone()), *bbox, ), PaintOp::FormObject { bbox, form } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::FormObject(form.clone()), + RenderNodeType::FormObject(form.as_ref().clone()), *bbox, ), PaintOp::Placeholder { bbox, placeholder } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::Placeholder(placeholder.clone()), + RenderNodeType::Placeholder(placeholder.as_ref().clone()), *bbox, ), PaintOp::RawSvg { bbox, raw } => RenderNode::new( self.take_node_id(source_node_id), - RenderNodeType::RawSvg(raw.clone()), + RenderNodeType::RawSvg(raw.as_ref().clone()), *bbox, ), PaintOp::GlyphRun { .. }