J-F-Liu / lopdf

A Rust library for PDF document manipulation.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is there any way to load your font?

LegendarySaiyan opened this issue · comments

I tried to add my fonts, but without success. Maybe anyone can share the way?

Here is the code:

let font_data = std::fs::read("src/fonts/Arial-Bold.ttf").unwrap();
    

    let font_dict_id = template_pdf.add_object(dictionary! {
        "Type" => "Font",
        "Subtype" => "TrueType",
        "BaseFont" => "Arial-Bold",
        "Encoding" => "WinAnsiEncoding",
    });
    
    let font_stream = Stream::new(dictionary!{}, font_data);
    let font_file_id = template_pdf.add_object(font_stream);
    
    let font_id = template_pdf.add_object(dictionary! {
        "Type" => "Font",
        "Subtype" => "TrueType",
        "BaseFont" => "Arial-Bold",
        "Encoding" => "WinAnsiEncoding",
        "FontDescriptor" => dictionary! {
            "Type" => "FontDescriptor",
            "FontName" => "Arial-Bold",
            "FontFile2" => font_file_id,
            "Flags" => 32,
            "ItalicAngle" => 0,
            "Ascent" => 905,
            "Descent" => -212,
            "CapHeight" => 728,
            "XHeight" => 519,
            "StemV" => 165,
            "FontBBox" => vec![-628, -376, 2000, 1010].into_iter().map(|n| Object::Integer(n)).collect::<Vec<_>>(),
            "CharSet" => "/WinAnsiEncoding",
            "MaxWidth" => 2000,
        },
    });
    
    let mut operations = vec![
        Operation::new("BT", vec![]),
    ];

    operations.push(Operation::new("BT", vec![]));
    operations.push(Operation::new("F1", vec![font_size.into()]));
    let color_string = format!("{} {} {} rg", color.0, color.1, color.2);
    operations.push(Operation::new(&color_string, vec![]));
    operations.push(Operation::new("Td", vec![position.0.into(), position.1.into()]));
    operations.push(Operation::new("Tf", vec![font_id.into(), font_size.into()]));
    operations.push(Operation::new("Tj", vec![Object::string_literal(text)]));
    operations.push(Operation::new("ET", vec![]));

    let content = Content {
        operations,
    };

    let text_xobject = xobject::form(
        vec![0.0, 0.0, 595.0, 842.0],
        vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
        content.encode().unwrap(),
    );

  
    let mut page = template_pdf.get_page(page_id).unwrap();
    let resources = page.resources_mut();
    resources.fonts_mut().insert(Name("F1".to_string()), font_id);
    page.add_content(text_xobject.to_content());

    let a = template_pdf.get_page_fonts(page_id);
    println!("{:?}", a);

    let p_o = existing_page_id.iter().next().unwrap();
    let p_oo = *p_o;

    let pages = dictionary! {
        "Type" => "Pages",
        "Kids" => vec![p_oo.into()],
        "Count" => 1,
        "Resources" => font_dict_id,
        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
    };

    template_pdf.objects.insert(pages_id, Object::Dictionary(pages));

}

@J-F-Liu Would you be so kind to give the example for exporting the fonts? :)

I haven't tried this before, seems also need to embed the font file, see PDF Reference document for details.

I've done this and it seems to be changing the font:

pub fn example() -> () {
    // Load the font data from a file
    let font_data = std::fs::read("ClimateCrisis-Regular-VariableFont_YEAR.ttf").unwrap();

    // Create a stream object for the font data
    let font_stream = Stream::new(dictionary! {}, font_data.clone());

    // Create a document object and add the font and font descriptor to it
    let mut doc = Document::with_version("1.5");
    // Create a font descriptor dictionary object
    let font_stream_id = doc.add_object(font_stream);
    let font_descriptor_dict = dictionary! {
        "Type" => "FontDescriptor",
        "FontName" => "MyFont",
        "FontFile2" => font_stream_id,
        "Flags" => 4,
        "Ascent" => 728,
        "Descent" => -212,
        "CapHeight" => 728,
        "ItalicAngle" => 0,
        "StemV" => 90,
    };
    let font_descriptor_id = doc.add_object(font_descriptor_dict);

    let encoding = dictionary! {
        "Type" => "Encoding",
        "BaseEncoding" => "WinAnsiEncoding",
    };

    // Create a font dictionary object
    let font_dict = dictionary! {
        "Type" => "Font",
        "Subtype" => "TrueType",
        "BaseFont" => "MyFont",
        "FirstChar" => 32,
        "LastChar" => 255,
        // "Widths" => Object::Array((32..=255).map(|c| (c, 600)).collect()),
        "FontDescriptor" => font_descriptor_id,
        "Encoding" => doc.add_object(encoding),
        "Length1" => font_data.len() as i32
    };

    let font_id = doc.add_object(font_dict);

    let content = Content {
        operations: vec![
            Operation::new("BT", vec![]),
            Operation::new("Tf", vec!["MyFont".into(), 48.into()]),
            Operation::new("Td", vec![100.into(), 600.into()]),
            Operation::new(
                "Tj",
                vec![Object::string_literal("Hello World!ążę Ą Ę Ż Ć")],
            ),
            Operation::new("ET", vec![]),
        ],
    };

    let resources_id = doc.add_object(dictionary! {
        "Font" => dictionary! {
            "MyFont" => font_id,
        },
    });

    let pages_id = doc.new_object_id();

    let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
    let page_id = doc.add_object(dictionary! {
        "Type" => "Page",
        "Parent" => pages_id,
        "Contents" => content_id,
    });
    let pages = dictionary! {
        "Type" => "Pages",
        "Kids" => vec![page_id.into()],
        "Count" => 1,
        "Resources" => resources_id,
        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
    };
    doc.objects.insert(pages_id, Object::Dictionary(pages));

    let catalog_id = doc.add_object(dictionary! {
        "Type" => "Catalog",
        "Pages" => pages_id,
    });
    doc.trailer.set("Root", catalog_id);
    doc.compress();
    // Create a resources dictionary object and add the font to it
    doc.save("output.pdf").unwrap();

    ()
}

ClimateCrisis
Screenshot from 2023-03-11 15-46-05

UbuntuMono
Screenshot from 2023-03-11 15-44-44

It feels to me like there's a problem in the parser somewhere and it doesn't treat multi-byte characters correctly.
Notice how on my screenshots the latin-extended characters are not shown correctly.

I'm completely unfamiliar with character encoding though, so I'm just guessing here.

Thanks for the external font loading code. I have the same problem with encoding regarding greek/cyrillic characters. I have realized that when you create a new pdf from scratch, the non regular characters show normally. However when editing an existing one even if it is written with fonts that support extended characters, the extra text that is added appears obfuscated.