printpdf
printpdf copied to clipboard
Potential Way get length of text at certain fontsize
I have been wondering if it would be possible to get the length of a line of text for a certain font and at a certain font size. Something along the lines of
get_length( text: String, font_size: i64 , font: &IndirectFontRef) -> Mm;
Any idea how I might go about implementing something like this ? Or where I should start ? I'm newish to rust, but I'm keen to give it a shot.
Wrong crate, this is a PDF library. I've written azul_text_layout
for this: https://crates.io/crates/azul-text-layout
See here:
use azul_text_layout::{
text_layout::{split_text_into_words, words_to_scaled_words},
text_shaping::get_font_metrics_freetype,
};
let font = include_bytes!("Helvetica.ttf");
let font_index = 0; // only for fonts with font collections
let font_metrics = get_font_metrics_freetype(&font, font_index);
let words = split_text_into_words("hello");
let scaled_words = words_to_scaled_words(&words, font, font_index, font_metrics, font_size);
let total_width = scaled_words.items.iter().map(|i| i.word_width).sum();
Note: this doesn't take into account the width of the spaces between the words.
If you want to do a full text layout (including more advanced things like line breaking, line offset, custom character / word spacing) azul-text-layout does support that too, see here: https://github.com/fschutt/printpdf/issues/39#issuecomment-559118743
Fantastic stuff, if want to account for the width of spaces as well It looks like I can also use scaled_words.space_advance_px
,
These are some great libraries. Thank you for pointing me in the right direction !
Potentially related, is there a way to set the local origin on a line of text to be the top left, as opposed to bottom left ? (i.e. such that the text draws downwards from the origin)
Alternatively, I've been trying to offset the y coordinate on the .use_text(text, fontsize, x, y, &font);
method by the height of the font in the following manner:
let font_metrics = get_font_metrics_freetype(font, font_index as i32);
let font_height = font_metrics.get_height(fontsize);
Unfortunately this font_height
does not correspond nicely to the Mm()
scale expected by .use_text()
, and even after tinkering to scale the value used to offset, I haven't managed to find a good way to do this that is consistent for many different fonts.
@fschutt You wrote printpdf is the wrong crate. I am curious how would you implement something where you have to "right align" numeric values and so on. From my naive point of view I would see the need to calculate the width of a text with specific parameters (font etc.). Is this a use case where printpdf is not the right tool for the job?
@maku
Is this a use case where printpdf is not the right tool for the job?
Yes. printpdf does not do text layout. I've written another crate azul-text-layout with which you can use to calculate the per-line offset. PDF does not know what "right align" means, it only knows left-align. So you have to calculate an "offset" for each line of text that you want to right-align.
Text layout / text shaping is more complicated that most people realize. Which is why it's the job of a text layout library, not a PDF library.
@fschutt thanks for answering, but I still don't understand correctly, azul-text-layout is not a PDF document creation lib, right? What would be the way when I have to generate a PDF with Rust that should display numbers right-aligned? You mean use azul-text-layout only for calculation purposes and printpdf for the rest?. Somehow this has to be possible since it is also possible in libraries in other programming languages. Even if it might be complicated ... can you give me some advice?
You mean use azul-text-layout only for calculation purposes and printpdf for the rest?
Yes. There is some code here to get you started: https://github.com/fschutt/printpdf/issues/39#issuecomment-559118743 and on the docs.rs page
Essentially you'll want to set horizontal_alignment = StyleTextAlignmentHorz::Right
and set text_layout_options.max_horizontal_width = None
. Then just copy the rest of the code - the "right aligning" is done by calling set_text_cursor
, which places the text relative to the page. So if you want to right align a text block of numbers, all you'll have to do is to add the coordinate of the line to the coordinate of the text block.
For anyone else stumbling upon this, I went for an alternative route and used rusttype and extracted this function from genpdf-rs to do the text measurements
pub fn str_width(&self, font_cache: &FontCache, s: &str, font_size: u8) -> Mm {
let str_width: Mm = font_cache
.get_rt_font(*self)
.glyphs_for(s.chars())
.map(|g| g.scaled(self.scale).h_metrics().advance_width)
.map(|w| Mm::from(printpdf::Pt(f64::from(w * f32::from(font_size)))))
.sum();
let kerning_width: Mm = self
.kerning(font_cache, s.chars())
.into_iter()
.map(|val| val * f32::from(font_size))
.map(|val| Mm::from(printpdf::Pt(f64::from(val))))
.sum();
str_width + kerning_width
}
And converted the width of the text to MM in 72dpi like (str_width + kerning_width) * (25.4 / 72.0)
@ninjacato You just have to be aware that rusttype has very rudimentary font shaping and no WOFF2 file support (while azul-text-layout uses the allsorts font shaping engine and does have these features).