use std::borrow::Cow;
use std::collections::HashMap;
use std::path;
use krilla::color::rgb;
use krilla::geom::Point;
use krilla::num::NormalizedF32;
use krilla::page::PageSettings;
use krilla::paint::Fill;
use krilla::text::Font;
use krilla::text::{GlyphId, KrillaGlyph};
use krilla::Document;
use parley::layout::Alignment;
use parley::style::{FontFamily, FontStack, FontWeight, LineHeight, StyleProperty};
use parley::{FontContext, LayoutContext};
fn main() {
let text = String::from(
"This is a long text. We want it to not be wider than 200pt, \
so that it fits on the page. Let's intersperse some emojis 💩👻💀emojis🦩🌚😁😆\
as well as complex scripts: हैलो वर्ल्ड and مرحبا بالعالم",
);
let max_advance = Some(200.0);
let text_color = rgb::Color::new(0, 0, 0);
let mut font_cx = FontContext::default();
let mut layout_cx = LayoutContext::new();
let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, 1.0, false);
let brush_style = StyleProperty::Brush(text_color);
builder.push_default(brush_style);
let font_stack = FontStack::List(Cow::Borrowed(&[
FontFamily::Named(Cow::Borrowed("Noto Sans")),
FontFamily::Named(Cow::Borrowed("Noto Sans Arabic")),
FontFamily::Named(Cow::Borrowed("Noto Sans Devanagari")),
FontFamily::Named(Cow::Borrowed("Noto Color Emoji")),
]));
let font_stack_style = StyleProperty::FontStack(font_stack);
builder.push_default(font_stack_style);
builder.push_default(StyleProperty::LineHeight(LineHeight::FontSizeRelative(1.3)));
builder.push_default(StyleProperty::FontSize(16.0));
let bold = FontWeight::new(600.0);
let bold_style = StyleProperty::FontWeight(bold);
builder.push(bold_style, 0..4);
let color_style = StyleProperty::Brush(rgb::Color::new(255, 0, 0));
builder.push(color_style, 2..12);
let mut layout = builder.build(&text);
layout.break_all_lines(max_advance);
layout.align(max_advance, Alignment::Start, Default::default());
let mut font_cache = HashMap::new();
let mut document = Document::new();
let mut page = document.start_page_with(PageSettings::from_wh(200.0, 300.0).unwrap());
let mut surface = page.surface();
for line in layout.lines() {
let y = line.metrics().baseline;
let mut x = 0.0;
for run in line.runs() {
let mut cur_x = x;
let font = run.font().clone();
let (font_data, id) = font.data.into_raw_parts();
let krilla_font = font_cache
.entry(id)
.or_insert_with(|| Font::new(font_data.into(), font.index).unwrap());
let font_size = run.font_size();
let mut cur_style = None;
let mut glyphs = Vec::<KrillaGlyph>::new();
for cluster in run.visual_clusters() {
if cluster.is_ligature_continuation() {
if let Some(glyph) = glyphs.last_mut() {
glyph.text_range.end = cluster.text_range().end;
}
continue;
}
for glyph in cluster.glyphs() {
let glyph_style = glyph.style_index;
if let Some(style) = cur_style {
if style != glyph_style {
cur_style = Some(glyph_style);
let style = layout.styles()[style as usize].brush;
surface.set_fill(Some(Fill {
paint: style.into(),
opacity: NormalizedF32::ONE,
rule: Default::default(),
}));
surface.draw_glyphs(
Point { x: cur_x, y },
&glyphs,
krilla_font.clone(),
&text,
font_size,
false,
);
glyphs.clear();
cur_x = x;
}
} else {
cur_style = Some(glyph_style);
}
glyphs.push(KrillaGlyph::new(
GlyphId::new(glyph.id as u32),
glyph.advance / font_size,
glyph.x / font_size,
glyph.y / font_size,
0.0,
cluster.text_range(),
None,
));
x += glyph.advance;
}
}
if !glyphs.is_empty() {
surface.set_fill(Some(Fill {
paint: layout.styles()[cur_style.unwrap() as usize].brush.into(),
opacity: NormalizedF32::ONE,
rule: Default::default(),
}));
surface.draw_glyphs(
Point::from_xy(cur_x, y),
&glyphs,
krilla_font.clone(),
&text,
font_size,
false,
);
}
}
}
surface.finish();
page.finish();
let pdf = document.finish().unwrap();
let path = path::absolute("parley.pdf").unwrap();
eprintln!("Saved PDF to '{}'", path.display());
std::fs::write(path, &pdf).unwrap();
}