Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions minipdf-node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub fn convert_to_pdf(
output_path: String,
options: Option<ConversionOptions>,
) -> Result<()> {
minipdf::register_system_fonts().map_err(to_napi_error)?;
let options = to_core_options(options)?;
minipdf::convert_to_pdf_with_options(input_path, output_path, &options).map_err(to_napi_error)
}
Expand All @@ -36,6 +37,7 @@ pub fn convert_to_pdf_bytes(
input_path: String,
options: Option<ConversionOptions>,
) -> Result<Buffer> {
minipdf::register_system_fonts().map_err(to_napi_error)?;
let options = to_core_options(options)?;
minipdf::convert_to_pdf_bytes_with_options(input_path, &options)
.map(Buffer::from)
Expand All @@ -44,6 +46,7 @@ pub fn convert_to_pdf_bytes(

#[napi(js_name = "convertBytesToPdf")]
pub fn convert_bytes_to_pdf(input: Buffer, options: Option<ConversionOptions>) -> Result<Buffer> {
minipdf::register_system_fonts().map_err(to_napi_error)?;
let options = to_core_options(options)?;
minipdf::convert_bytes_to_pdf_with_options(input.as_ref(), &options)
.map(Buffer::from)
Expand Down
6 changes: 6 additions & 0 deletions minipdf-node/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ test('converts path and Buffer inputs to PDF', () => {

assert.equal(fromBuffer.subarray(0, 8).toString(), '%PDF-1.4')
assert.equal(fromPath.subarray(0, 8).toString(), '%PDF-1.4')
if (process.platform === 'win32') {
assert.equal(
minipdf.registeredFonts().filter((font) => font.name === 'arial').length,
1
)
}
})

test('writes converted PDF to an output path', (context) => {
Expand Down
168 changes: 1 addition & 167 deletions minipdf-rs/crates/minipdf-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,60 +171,7 @@ fn register_fonts_from_dir(font_dir: &Path) -> minipdf::Result<()> {
}

fn register_system_fallback_fonts() -> minipdf::Result<()> {
let candidates = system_fallback_font_paths();
for path in candidates.into_iter().filter(|path| path.is_file()) {
let name = path
.file_stem()
.and_then(|name| name.to_str())
.unwrap_or("font")
.to_owned();
minipdf::register_font(name, fs::read(path)?);
}
register_office_cloud_fonts()?;
Ok(())
}

#[cfg(target_os = "windows")]
fn register_office_cloud_fonts() -> minipdf::Result<()> {
let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") else {
return Ok(());
};
let cache_root = PathBuf::from(local_app_data)
.join("Microsoft")
.join("FontCache");
let Ok(cache_versions) = fs::read_dir(cache_root) else {
return Ok(());
};
for cloud_root in cache_versions
.filter_map(std::result::Result::ok)
.map(|entry| entry.path().join("CloudFonts"))
.filter(|path| path.is_dir())
{
for family in ["Grandview", "Grandview Display", "STKaiti"] {
let directory = cloud_root.join(family);
let Ok(entries) = fs::read_dir(directory) else {
continue;
};
for entry in entries.filter_map(std::result::Result::ok) {
let path = entry.path();
if path.extension().and_then(|ext| ext.to_str()) != Some("ttf") {
continue;
}
let Ok(data) = fs::read(path) else {
continue;
};
if let Some(name) = font_alias_from_data(&data) {
minipdf::register_font(name, data);
}
}
}
}
Ok(())
}

#[cfg(not(target_os = "windows"))]
fn register_office_cloud_fonts() -> minipdf::Result<()> {
Ok(())
minipdf::register_system_fonts()
}

fn font_alias_from_data(data: &[u8]) -> Option<String> {
Expand Down Expand Up @@ -270,106 +217,10 @@ fn normalize_font_alias(name: &str) -> String {
.collect()
}

fn system_fallback_font_paths() -> Vec<PathBuf> {
#[cfg(target_os = "windows")]
{
let directory = std::env::var_os("WINDIR")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(r"C:\Windows"))
.join("Fonts");
return [
"arial.ttf",
"arialbd.ttf",
"ariali.ttf",
"arialbi.ttf",
"calibri.ttf",
"calibrib.ttf",
"calibrii.ttf",
"calibriz.ttf",
"pala.ttf",
"palab.ttf",
"palai.ttf",
"palabi.ttf",
"corbel.ttf",
"corbelb.ttf",
"corbeli.ttf",
"corbelz.ttf",
"GARA.TTF",
"GARABD.TTF",
"GARAIT.TTF",
"TCM_____.TTF",
"TCB_____.TTF",
"TCMI____.TTF",
"TCBI____.TTF",
"verdana.ttf",
"verdanab.ttf",
"verdanai.ttf",
"verdanaz.ttf",
"NotoSans-Regular.ttf",
"NotoSansArmenian-Regular.ttf",
"NotoSansArmenian-Bold.ttf",
"NotoSansGeorgian-Regular.ttf",
"NotoSansGeorgian-Bold.ttf",
"ebrima.ttf",
"ebrimabd.ttf",
"YuGothR.ttc",
"NotoSansSC-VF.ttf",
"simkai.ttf",
"simsun.ttc",
"simhei.ttf",
"malgunsl.ttf",
"malgun.ttf",
"micross.ttf",
"LeelawUI.ttf",
"Nirmala.ttf",
"seguisym.ttf",
"seguiemj.ttf",
]
.into_iter()
.map(|name| directory.join(name))
.collect();
}

#[cfg(target_os = "linux")]
{
return [
"/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/noto/NotoSansThai-Regular.ttf",
"/usr/share/fonts/truetype/noto/NotoSansDevanagari-Regular.ttf",
"/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
]
.into_iter()
.map(PathBuf::from)
.collect();
}

#[cfg(target_os = "macos")]
{
return [
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
"/System/Library/Fonts/AppleSDGothicNeo.ttc",
"/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc",
"/System/Library/Fonts/Kohinoor.ttc",
"/System/Library/Fonts/Apple Color Emoji.ttc",
]
.into_iter()
.map(PathBuf::from)
.collect();
}

#[allow(unreachable_code)]
Vec::new()
}

#[cfg(test)]
mod tests {
use super::build_font_alias;

#[cfg(target_os = "windows")]
use super::system_fallback_font_paths;

#[test]
fn builds_stable_cloud_font_aliases() {
assert_eq!(
Expand All @@ -385,21 +236,4 @@ mod tests {
"grandviewdisplay"
);
}

#[cfg(target_os = "windows")]
#[test]
fn registers_all_arial_style_variants() {
let names = system_fallback_font_paths()
.into_iter()
.filter_map(|path| path.file_name().map(|name| name.to_owned()))
.collect::<Vec<_>>();

for name in ["arial.ttf", "arialbd.ttf", "ariali.ttf", "arialbi.ttf"] {
assert!(names.iter().any(|candidate| candidate == name));
}

for name in ["simkai.ttf", "simsun.ttc"] {
assert!(names.iter().any(|candidate| candidate == name));
}
}
}
Loading
Loading