Last active
February 18, 2026 13:41
-
-
Save olsonjeffery/a1b1dc18d287685164e0ab648c587ad9 to your computer and use it in GitHub Desktop.
test case for font rendering bug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [package] | |
| name = "oxidizepdf-embedded-font-rendering-firefox" | |
| version = "0.1.0" | |
| edition = "2024" | |
| [dependencies] | |
| oxidize-pdf = "1.7.0" | |
| tracing = "0.1.44" | |
| anyhow = "1.0" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![deny(warnings)] | |
| #![macro_use] | |
| use std::fs::{self}; | |
| use oxidize_pdf::{ | |
| Color, CoordinateSystem, Document, Font, Page, PdfError, | |
| advanced_tables::{ | |
| AdvancedTable, AdvancedTableBuilder, AdvancedTableExt, BorderStyle, CellStyle, | |
| TableRenderer, | |
| }, | |
| page::LayoutManager, | |
| }; | |
| use anyhow::{Result, anyhow}; | |
| // NOTE: Proxima Nova font family is available for download (free) at: | |
| // https://freefontsfamily.com/proxima-nova-font-family-free-download/ | |
| // | |
| // It is a zip file. The contents of the zip will need to be extracted, | |
| // with the .otf files within placed in the `content/` folder within this | |
| // repository | |
| const PROXIMA_REGULAR_BYTES: &[u8] = include_bytes!("../content/Proxima Nova Regular.otf"); | |
| const PROXIMA_REGULAR: &str = "Proxima Nova Regular"; | |
| const PROXIMA_SEMIBOLD_BYTES: &[u8] = include_bytes!("../content/Proxima Nova Semibold.otf"); | |
| const PROXIMA_SEMIBOLD: &str = "Proxima Nova Semibold"; | |
| const TC_BLACK: &str = "#000000"; | |
| pub struct ExampleContent { | |
| pub name: String, | |
| pub details: String, | |
| } | |
| pub fn test_case_for_font_bug(input_data: &[ExampleContent]) -> Result<Vec<u8>> { | |
| // step 1: initialize PDF doc, load custom font | |
| let mut export_pdf = Document::new(); | |
| export_pdf.add_font_from_bytes(PROXIMA_REGULAR, PROXIMA_REGULAR_BYTES.to_vec())?; | |
| export_pdf.add_font_from_bytes(PROXIMA_SEMIBOLD, PROXIMA_SEMIBOLD_BYTES.to_vec())?; | |
| export_pdf.set_title("FireFox font rendering bug"); | |
| let mut curr_page = Page::a4(); | |
| let mut layout_mgr = | |
| LayoutManager::new(&curr_page, CoordinateSystem::PdfStandard).with_element_spacing(15.0); | |
| let renderer = TableRenderer::new(); | |
| // add styles | |
| let name_style = CellStyle::new() | |
| .text_wrap(true) | |
| .max_height(140.0) | |
| .font(Font::Custom(PROXIMA_SEMIBOLD.to_owned())) | |
| .font(Font::HelveticaBold) | |
| .font_size(14.0) | |
| .border(BorderStyle::None, 0.0, Color::white()) | |
| .text_color(Color::hex(TC_BLACK)); | |
| let details_style = CellStyle::new() | |
| .text_wrap(true) | |
| .max_height(120.0) | |
| .font(Font::Custom(PROXIMA_REGULAR.to_owned())) | |
| .font(Font::Helvetica) | |
| .font_size(12.0) | |
| .border(BorderStyle::None, 0.0, Color::white()) | |
| .text_color(Color::hex(TC_BLACK)); | |
| // Step 3: render PDF | |
| let mut inst_tbl_builder = AdvancedTableBuilder::new() | |
| .table_border(false) | |
| .show_header(false) | |
| .add_column("0", 250.0) | |
| .add_column("1", 250.0) | |
| .total_width(500.0); | |
| for entry in input_data { | |
| inst_tbl_builder = inst_tbl_builder.add_row_with_mixed_styles(vec![ | |
| (name_style.clone(), &entry.name), | |
| (details_style.clone(), &entry.details), | |
| ]); | |
| } | |
| // | |
| // this loop is for adding page in case of table | |
| // overflow; it should run twice at most | |
| let inst_tbl = inst_tbl_builder.build()?; | |
| loop { | |
| let table_height = renderer.calculate_table_height(&inst_tbl); | |
| if !try_add_to_page(&mut curr_page, &mut layout_mgr, &inst_tbl, table_height)? { | |
| // couldn't add to page because of overflow, | |
| // so we add this page to the doc, then make | |
| // a new page and retry this process | |
| export_pdf.add_page(curr_page); | |
| curr_page = Page::a4(); | |
| layout_mgr = LayoutManager::new(&curr_page, CoordinateSystem::PdfStandard); | |
| } else { | |
| break; | |
| } | |
| } | |
| // add the last page | |
| export_pdf.add_page(curr_page); | |
| let output_bytes = export_pdf.to_bytes()?; | |
| Ok(output_bytes) | |
| } | |
| fn try_add_to_page( | |
| page: &mut Page, | |
| layout_mgr: &mut LayoutManager, | |
| input_tbl: &AdvancedTable, | |
| table_height: f64, | |
| ) -> Result<bool, PdfError> { | |
| if let Some(y_pos) = layout_mgr.add_element(table_height) { | |
| let x_pos = layout_mgr.center_x(input_tbl.calculate_width()); | |
| page.add_advanced_table(input_tbl, x_pos, y_pos)?; | |
| Ok(true) | |
| } else { | |
| Ok(false) | |
| } | |
| } | |
| pub fn main() -> Result<()> { | |
| // | |
| let input_data = vec![ExampleContent { | |
| name: "Foo".to_owned(), | |
| details: "Bar!!".to_owned(), | |
| }]; | |
| let bytes = match test_case_for_font_bug(&input_data) { | |
| Ok(r) => r, | |
| Err(e) => { | |
| return Err(anyhow!("blorf!!!! {:?}", e)); | |
| } | |
| }; | |
| fs::write("test-case.pdf", &bytes)?; | |
| Ok(()) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment