不知道如何格式化我的pdf与“Printpdf 0.5.3”板条箱在 rust

r3i60tvu  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(67)

我是相对较新的编程在铁 rust 和需要一点方向就如何实现我的项目。
目前,我正在尝试建立一个CLI工具,需要一个人的名字,他们也申请的位置,他们也申请的公司和该公司的位置,并自动魔术格式的PDF与他们的求职信的内容。最终,我想把这个变成一个GUI工具,我可以发送给我的朋友,让他们可以在他们的windows/mac机器上使用它。
现在的问题是我使用的“Printpdf”板条箱来生成文档本身.正如你可以看到与下面的图片,文本不自动 Package /line-break/format当它到达行的末尾.不幸的是,我不能简单地打破这到不同的字符串变量作为程序需要能够添加任意用户输入和返回一个完美格式化的PDF文件.
我该如何设置这份文档的格式?我应该使用一个不是“printpdf”的板条箱吗?或者我可以使用其他的东西来让文本按照我想要的方式表现吗?
下面是rust代码和PDF输出的图片。

use std::io; 
use printpdf::*;
use std::fs::File;
use std::io::BufWriter;

fn main() {
    println!("Enter: Position");
    let mut position = String::new(); 
    io:: stdin()
        .read_line(&mut position)
        .expect("failed to read"); 

        
    println!("Enter: Company Name");
    let mut coname = String::new(); 
    io:: stdin()
        .read_line(&mut coname)
        .expect("failed to read"); 

    println!("Enter: Company Location");
    let mut location = String::new(); 
    io:: stdin()
        .read_line(&mut location)
        .expect("failed to read"); 

    let ntxt = " ";

    let sample = "This is some random Sample text, This text should eventaully be a user input. Currently, this text is not a user input. this text is supposed to be a text of long string data that will eventually be added to the document via user input";

    let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(216.0), Mm(279.0), "Layer 1");
    let current_layer = doc.get_page(page1).get_layer(layer1);

    let font = doc.add_external_font(File::open("./fonts/TNR-Regular.ttf").unwrap()).unwrap();

    current_layer.use_text(position.clone(), 14.0, Mm(25.0), Mm(250.0), &font);
    current_layer.use_text(coname.clone(), 14.0, Mm(25.0), Mm(240.0), &font);
    current_layer.use_text(location.clone(), 14.0, Mm(25.0), Mm(230.0), &font);
    current_layer.use_text(ntxt.clone(), 14.0, Mm(25.0), Mm(220.0), &font);

    current_layer.begin_text_section();

        current_layer.set_font(&font, 14.0);
        current_layer.set_text_cursor(Mm(25.0), Mm(210.0));

        // write one line, but write text2 in superscript
        current_layer.write_text(sample.clone(), &font);

    current_layer.end_text_section();

    doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap();

}

the pdf output
我真的不知道从哪里开始解决这个问题。
我试着看了“textwrap”的文档,但它对我来说也是一个谜,我也看了“printpdf”的文档,但它说它不支持格式和对齐。

yhuiod9q

yhuiod9q1#

也许有一个更理智的方法来做到这一点,但这是有效的:
1.我们将font_data作为字节向量Vec<u8>加载;然后,X1 E0 F1 X和X1 E1 F1 X都独立地将该字体作为字节片X1 M4 N1 X来使用。
1.我们使用glyph_brush_layout来计算字形 * 的位置(我只是修改了README中的示例),具体来说就是在160 mm宽的框中获得y的垂直位置。
1.我们将这些符号分组在y位置(它们是每行文本基线的垂直位置),沿着它们在文本中的索引(其中text = sample)。
1.我们只取每组中的第一个字形(group.next().unwrap().0),得到它的索引(和y位置)。这些索引是我们将文本分成单独行的位置。这些索引被收集到一个Vector中。
1.我们在Vector上循环,将文本写入PDF。为了分割文本,我们创建了一个可查看的迭代器,这样我们就可以将文本切片sample作为当前索引和下一个(查看的)索引。由于printpdfglyph_brush_layout处理布局的方式不同,我们需要做一些垂直偏移和转换。

  • 这里假设一个字形等于文本中的一个字符,即assert_eq!(glyphs.len(), sample.chars().count());。如果不是这样,也许你应该考虑一次直接定位一个字形。

main.rs

use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use std::io::{self, Read};

fn main() {
    println!("Enter: Position");
    let mut position = String::new();
    io::stdin()
        .read_line(&mut position)
        .expect("failed to read");

    println!("Enter: Company Name");
    let mut coname = String::new();
    io::stdin().read_line(&mut coname).expect("failed to read");

    println!("Enter: Company Location");
    let mut location = String::new();
    io::stdin()
        .read_line(&mut location)
        .expect("failed to read");

    let ntxt = " ";

    let sample = "This is some random Sample text, This text should eventaully be a user input. Currently, this text is not a user input. this text is supposed to be a text of long string data that will eventually be added to the document via user input";

    let (doc, page1, layer1) =
        PdfDocument::new("PDF_Document_title", Mm(216.0), Mm(279.0), "Layer 1");
    let current_layer = doc.get_page(page1).get_layer(layer1);

    // load the font data for the font "Times New Roman"
    let font_data = {
        let mut font_file = File::open("./times-new-roman.ttf").unwrap();
        let mut font_data = Vec::with_capacity(font_file.metadata().unwrap().len() as usize);
        font_file.read_to_end(&mut font_data).unwrap();
        font_data
    };

    // load the font reference for glyph_brush_layout
    let gbl_font = glyph_brush_layout::ab_glyph::FontRef::try_from_slice(&font_data).unwrap();
    // put it into a slice of glyph_brush_layout font references
    let gbl_fonts = &[gbl_font];

    // load the font reference for printpdf
    let font = doc.add_external_font(font_data.as_slice()).unwrap();

    current_layer.use_text(position.clone(), 14.0, Mm(25.0), Mm(250.0), &font);
    current_layer.use_text(coname.clone(), 14.0, Mm(25.0), Mm(240.0), &font);
    current_layer.use_text(location.clone(), 14.0, Mm(25.0), Mm(230.0), &font);
    current_layer.use_text(ntxt, 14.0, Mm(25.0), Mm(220.0), &font);

    // calculate the glyph positions using glyph_brush_layout
    use glyph_brush_layout::ab_glyph::Font;
    use glyph_brush_layout::GlyphPositioner;
    let glyphs = glyph_brush_layout::Layout::default().calculate_glyphs(
        gbl_fonts,
        &glyph_brush_layout::SectionGeometry {
            // width 160mm = 210mm - 2 * 25mm margins; height unbounded
            bounds: (mm_to_px(160.0), f32::INFINITY),
            ..Default::default()
        },
        &[glyph_brush_layout::SectionText {
            text: sample,
            scale: gbl_fonts[0].pt_to_px_scale(14.0).unwrap(),
            font_id: glyph_brush_layout::FontId(0),
        }],
    );

    // make sure the number of glyphs matches the number of chars in the sample text
    assert_eq!(glyphs.len(), sample.chars().count());

    // group the glyphs by y position
    use itertools::Itertools;
    let line_starts = glyphs
        .iter()
        .enumerate() // enumerate will give us the start index into the sample text of the start of the line
        .group_by(|(_, glyph)| glyph.glyph.position.y) // group by "y" which is effectively equivalent to the index of the line
        .into_iter()
        .map(|(y, mut group)| (y, group.next().unwrap().0))
        .collect::<Vec<_>>();

    // get the minimum y position
    // you could get the max a similar way, if you needed to calculate the vertical size of the text,
    // for example if you wanted to lay out text below it
    let min = glyphs
        .iter()
        .map(|glyph| glyph.glyph.position.y)
        .fold(f32::INFINITY, |a, b| a.min(b));

    // we need a peekable iterator so we can see where the next line starts
    let mut iter = line_starts.iter().peekable();

    // iterate over the line_starts and draw the text
    loop {
        // get the next line start, if there is none then we break out of the loop
        let Some((y, start)) = iter.next() else {
            break;
        };
        // peek into the line start after that to get the end index,
        // if there is none (we're at the last line in the loop), then we use the length of the sample text
        let end = if let Some((_, end)) = iter.peek() {
            *end
        } else {
            sample.chars().count()
        };

        // slice up the text
        // if you know you're only dealing with ASCII characters you can simplify this as
        // `let line = &sample[*start..end];`
        // which saves on an allocation to a String;
        // or you can use char_indices to get the byte indices and slice that way
        let line = sample
            .chars()
            .skip(*start)
            .take(end - start)
            .collect::<String>();

        // draw the text
        current_layer.use_text(
            line.trim(),
            14.0,
            Mm(25.0),
            // printpdf up = y positive, but glyph_brush_layout down = y positive
            Mm(210.0 + px_to_mm(min) - px_to_mm(*y)),
            &font,
        );
    }

    doc.save(&mut BufWriter::new(
        File::create("test_working.pdf").unwrap(),
    ))
    .unwrap();
}

/// glyph_brush_layout deals with f32 pixels, but printpdf deals with f64 mm.
fn px_to_mm(px: f32) -> f64 {
    px as f64 * 3175.0 / 12000.0
}

/// printpdf deals with f64 mm, but glyph_brush_layout deals with f32 pixels.
fn mm_to_px(mm: f64) -> f32 {
    (mm * 12000.0 / 3175.0) as f32
}

cargo.toml

[package]
name = "generate-pdf"
version = "0.1.0"
edition = "2021"

[dependencies]
glyph_brush_layout = "0.2.3"
itertools = "0.10.5"
printpdf = "0.5.3"

相关问题