Add support for drawing COLR glyphs in SVG export (#6693)
This commit is contained in:
parent
3683399a95
commit
0fe8f09ba9
|
|
@ -127,13 +127,8 @@ fn draw_raster_glyph(
|
|||
Some(())
|
||||
}
|
||||
|
||||
/// Draws a glyph from the COLR table into the frame.
|
||||
fn draw_colr_glyph(
|
||||
frame: &mut Frame,
|
||||
font: &Font,
|
||||
upem: Abs,
|
||||
glyph_id: GlyphId,
|
||||
) -> Option<()> {
|
||||
/// Convert a COLR glyph into an SVG file.
|
||||
pub fn colr_glyph_to_svg(font: &Font, glyph_id: GlyphId) -> Option<String> {
|
||||
let mut svg = XmlWriter::new(xmlwriter::Options::default());
|
||||
|
||||
let ttf = font.ttf();
|
||||
|
|
@ -176,7 +171,25 @@ fn draw_colr_glyph(
|
|||
ttf.paint_color_glyph(glyph_id, 0, RgbaColor::new(0, 0, 0, 255), &mut glyph_painter)?;
|
||||
svg.end_element();
|
||||
|
||||
let data = Bytes::from_string(svg.end_document());
|
||||
Some(svg.end_document())
|
||||
}
|
||||
|
||||
/// Draws a glyph from the COLR table into the frame.
|
||||
fn draw_colr_glyph(
|
||||
frame: &mut Frame,
|
||||
font: &Font,
|
||||
upem: Abs,
|
||||
glyph_id: GlyphId,
|
||||
) -> Option<()> {
|
||||
let svg_string = colr_glyph_to_svg(font, glyph_id)?;
|
||||
|
||||
let ttf = font.ttf();
|
||||
let width = ttf.global_bounding_box().width() as f64;
|
||||
let height = ttf.global_bounding_box().height() as f64;
|
||||
let x_min = ttf.global_bounding_box().x_min as f64;
|
||||
let y_max = ttf.global_bounding_box().y_max as f64;
|
||||
|
||||
let data = Bytes::from_string(svg_string);
|
||||
let image = Image::plain(SvgImage::new(data).ok()?);
|
||||
|
||||
let y_shift = Abs::pt(upem.to_pt() - y_max);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use ecow::EcoString;
|
|||
use ttf_parser::GlyphId;
|
||||
use typst_library::foundations::Bytes;
|
||||
use typst_library::layout::{Abs, Point, Ratio, Size, Transform};
|
||||
use typst_library::text::color::colr_glyph_to_svg;
|
||||
use typst_library::text::{Font, TextItem};
|
||||
use typst_library::visualize::{
|
||||
ExchangeFormat, FillRule, Image, Paint, RasterImage, RelativeTo,
|
||||
|
|
@ -31,7 +32,8 @@ impl SVGRenderer<'_> {
|
|||
let x_offset = x + glyph.x_offset.at(text.size).to_pt();
|
||||
let y_offset = y + glyph.y_offset.at(text.size).to_pt();
|
||||
|
||||
self.render_svg_glyph(text, id, x_offset, y_offset, scale)
|
||||
self.render_colr_glyph(text, id, x_offset, y_offset, scale)
|
||||
.or_else(|| self.render_svg_glyph(text, id, x_offset, y_offset, scale))
|
||||
.or_else(|| self.render_bitmap_glyph(text, id, x_offset, y_offset))
|
||||
.or_else(|| {
|
||||
self.render_outline_glyph(
|
||||
|
|
@ -87,6 +89,42 @@ impl SVGRenderer<'_> {
|
|||
Some(())
|
||||
}
|
||||
|
||||
/// Render a glyph defined by COLR glyph descriptions.
|
||||
fn render_colr_glyph(
|
||||
&mut self,
|
||||
text: &TextItem,
|
||||
id: GlyphId,
|
||||
x_offset: f64,
|
||||
y_offset: f64,
|
||||
scale: f64,
|
||||
) -> Option<()> {
|
||||
let ttf = text.font.ttf();
|
||||
let converted = colr_glyph_to_svg(&text.font, id)?;
|
||||
let width = ttf.global_bounding_box().width() as f64;
|
||||
let height = ttf.global_bounding_box().height() as f64;
|
||||
let data_url = svg_to_base64(&converted);
|
||||
|
||||
let x_min = ttf.global_bounding_box().x_min as f64;
|
||||
let y_max = ttf.global_bounding_box().y_max as f64;
|
||||
|
||||
let glyph_hash = hash128(&(&text.font, id));
|
||||
let id = self.glyphs.insert_with(glyph_hash, || RenderedGlyph::Image {
|
||||
url: data_url,
|
||||
width,
|
||||
height,
|
||||
ts: Transform::scale(Ratio::new(scale), Ratio::new(-scale))
|
||||
.pre_concat(Transform::translate(Abs::pt(x_min), -Abs::pt(y_max))),
|
||||
});
|
||||
|
||||
self.xml.start_element("use");
|
||||
self.xml.write_attribute_fmt("xlink:href", format_args!("#{id}"));
|
||||
self.xml.write_attribute("x", &(x_offset));
|
||||
self.xml.write_attribute("y", &(y_offset));
|
||||
self.xml.end_element();
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Render a glyph defined by a bitmap.
|
||||
fn render_bitmap_glyph(
|
||||
&mut self,
|
||||
|
|
@ -320,10 +358,14 @@ fn convert_svg_glyph_to_base64_url(font: &Font, id: GlyphId) -> Option<EcoString
|
|||
);
|
||||
}
|
||||
|
||||
Some(svg_to_base64(&svg_str))
|
||||
}
|
||||
|
||||
fn svg_to_base64(svg_str: &str) -> EcoString {
|
||||
let mut url: EcoString = "data:image/svg+xml;base64,".into();
|
||||
let b64_encoded =
|
||||
base64::engine::general_purpose::STANDARD.encode(svg_str.as_bytes());
|
||||
url.push_str(&b64_encoded);
|
||||
|
||||
Some(url)
|
||||
url
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue