Add support for text shaping
As I understood the code, printpdf does no text shaping and generates a glyph ID for each code point.
In #73 @fschutt mentioned that, ideally, the shaping engine should be pluggable. Recently, yeslogic/allsorts has emerged as a viable option for text shaping and font substitution in pure Rust, which is already being used in production.
Is that plan still valid? If so, would it be acceptable to switch font subsetting to allsorts along with text shaping?
Yeah I know about allsorts, but I never got around to migrating to allsorts.
ParsedFont now uses allsorts, but there's no text shaping yet. There is, however, an HTML API in progress.
Shaping was added in 0.8.2, as a pre-step towards HTML content generation.
[dependencies.printpdf]
version = "0.8.2"
default-features = false
features = ["text_layout"]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut doc = PdfDocument::new("PDF test");
let font_bytes = std::fs::read("C:\\Windows\\Fonts\\Roboto-Regular.ttf")?;
let font = ParsedFont::from_bytes(&font_bytes, 0, &mut Vec::new())?;
let font_id = doc.add_font(&font);
let page_width = Mm(210.0);
let page_height = Mm(297.0);
let text = "Hello World!";
let options = TextShapingOptions {
font_size: Pt(12.0),
// automatically break the text into lines on overflow
max_width: Some(page_width.into_pt()),
// will automatically center all lines
align: TextAlign::Center,
..Default::default()
};
let shaped_text = doc.shape_text(text, &font_id, &options)?;
// println!("{} x {}", shaped_text.width, shaped_text.height);
//
// note: only useful if max_width = None to manually center text
// Top left corner of where you want the text to be positioned
let text_origin = Point {
// X coordinate is already handled by TextAlign::Center
x: Pt(0.0),
// Y coordinate has to be inversed due to PDF starting the Y at the bottom
y: (page_height - Mm(10.0)).into()
};
// Position the text in the page
let text_drawing_ops = shaped_text.get_ops(text_origin);
// Page drawing operations
let mut ops = Vec::new();
// ops.push(Op::SetStrokeColor { .. });
// ops.push(Op::SetFillColor { .. });
// ops.push(Op::SetTextRenderingMode { .. });
ops.extend(text_drawing_ops);
let page = PdfPage::new(page_width, page_height, ops);
let pdf_bytes = doc
.with_pages(vec!\[page\])
.save(&PdfSaveOptions::default(), &mut Vec::new());
std::fs::write("doc.pdf", pdf_bytes)?;
Ok(())
}
Proper layouting support will come later, but this should be a start.