Skip to content

Instantly share code, notes, and snippets.

@olsonjeffery
Last active February 18, 2026 13:41
Show Gist options
  • Select an option

  • Save olsonjeffery/a1b1dc18d287685164e0ab648c587ad9 to your computer and use it in GitHub Desktop.

Select an option

Save olsonjeffery/a1b1dc18d287685164e0ab648c587ad9 to your computer and use it in GitHub Desktop.
test case for font rendering bug
[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"
#![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