mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
M2.6: non-embedded Mincho fonts draw half-width katakana (#484)
Since #368, a non-embedded Adobe-Japan1 font with a Mincho name (HeiseiMin-W3, MS-Mincho, ...) is drawn in document_japanese_font(), i.e. Shippori Mincho. Shippori Mincho has none of the 63 half-width katakana (U+FF61-U+FF9F), which are common in Japanese documents. hayro maps the CID through Adobe-Japan1-UCS2 to Unicode, finds no glyph in the face's cmap, and falls back to drawing the CID as a glyph index: half-width katakana came out as unrelated glyphs (ligatures such as fi and fl). The Mincho substitute is now BIZ UDMincho Regular, which covers half-width katakana (and was designed as a metric-compatible replacement for MS Mincho), falling back to document_japanese_font() when it isn't built in. The face used for Japanese text written into PDFs is unchanged. Without craft-fonts nothing changes. Tests: raster::tests::mincho_substitute_covers_half_width_katakana (with CRAFT_FONTS_DIR: the Mincho substitute's cmap maps U+FF61-U+FF9F and 日本; it lacked all 63 half-width katakana before; skips without craft-fonts). skrifa 0.44 is a dev-dependency of pdfcraft-render for it, the version pdfcraft-fonts already uses. The parity entry view.render-cid-fonts cites the test. Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
271139bd3b
commit
5f570eab01
5 files changed
+42
-3
No files matched your search
Generated
+1
@@ -3751,6 +3751,7 @@ dependencies = [
|
||||
"log",
|
||||
"lopdf",
|
||||
"pdfcraft-fonts",
|
||||
"skrifa 0.44.0",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
|
||||
@@ -59,3 +59,8 @@ sync_github_name = true
|
||||
|
||||
[people.amitabhthakur]
|
||||
sync_github_name = true
|
||||
|
||||
[people.chent11]
|
||||
real_name = "Tao Chen"
|
||||
display_name = "Tao Chen"
|
||||
sync_github_name = false
|
||||
@@ -20,3 +20,7 @@ log = { workspace = true }
|
||||
# lopdf (bootstrap) draws random bytes for encryption; on the web they come from the browser.
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.4", features = ["wasm_js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
# Checks the Japanese substitute's character coverage; the version pdfcraft-fonts already uses.
|
||||
skrifa = "0.44"
|
||||
@@ -73,7 +73,13 @@ fn japanese_fallback(query: &FontQuery) -> Option<(FontData, u32)> {
|
||||
return None;
|
||||
}
|
||||
let face = match japanese_face(f.post_script_name.as_deref().unwrap_or_default(), f.is_serif, f.is_bold || f.font_weight >= 600) {
|
||||
JapaneseFace::Mincho => pdfcraft_fonts::document_japanese_font(),
|
||||
// BIZ UDMincho before the document face (Shippori Mincho): it covers half-width katakana
|
||||
// (U+FF61–U+FF9F), which Shippori Mincho lacks, and hayro draws a CID the substitute
|
||||
// can't map by Unicode by glyph index, i.e. as an unrelated glyph (fi, fl, …).
|
||||
JapaneseFace::Mincho => pdfcraft_fonts::ui_japanese_fonts()
|
||||
.into_iter()
|
||||
.find(|c| c.family == "BIZ UDMincho" && c.style == "Regular")
|
||||
.or_else(pdfcraft_fonts::document_japanese_font),
|
||||
JapaneseFace::Gothic { bold } => {
|
||||
let faces = pdfcraft_fonts::ui_japanese_fonts();
|
||||
let style = if bold { "Bold" } else { "Regular" };
|
||||
@@ -1652,6 +1658,29 @@ trailer << /Root 1 0 R >>
|
||||
}
|
||||
}
|
||||
|
||||
/// Half-width katakana are common in Japanese documents set in a non-embedded Mincho font
|
||||
/// such as HeiseiMin-W3. The Mincho substitute must have those glyphs: one that lacks them
|
||||
/// (Shippori Mincho) draws each CID by glyph index instead, as unrelated glyphs (fi, fl).
|
||||
#[test]
|
||||
fn mincho_substitute_covers_half_width_katakana() {
|
||||
use hayro::hayro_interpret::font::FallbackFontQuery;
|
||||
use hayro::hayro_interpret::hayro_cmap::CharacterCollection;
|
||||
use skrifa::MetadataProvider;
|
||||
let query = FontQuery::Fallback(FallbackFontQuery {
|
||||
post_script_name: Some("HeiseiMin-W3".into()),
|
||||
character_collection: Some(CharacterCollection { family: CidFamily::AdobeJapan1, supplement: 2 }),
|
||||
..FallbackFontQuery::default()
|
||||
});
|
||||
let Some((data, index)) = super::japanese_fallback(&query) else {
|
||||
eprintln!("built without craft-fonts (CRAFT_FONTS_DIR unset): no Japanese face to check");
|
||||
return;
|
||||
};
|
||||
let font = skrifa::FontRef::from_index((*data).as_ref(), index).expect("a craft-fonts face parses");
|
||||
let charmap = font.charmap();
|
||||
let missing: Vec<char> = ('\u{FF61}'..='\u{FF9F}').chain(['日', '本']).filter(|&c| charmap.map(c).is_none()).collect();
|
||||
assert!(missing.is_empty(), "the Mincho substitute lacks {missing:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn japanese_font_names_pick_mincho_or_gothic() {
|
||||
use super::JapaneseFace::{Gothic, Mincho};
|
||||
|
||||
@@ -746,8 +746,8 @@ milestone = "M2"
|
||||
status = "partial"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = ["crates/render/src/raster.rs::huge_cid_width_ranges_terminate", "crates/render/src/raster.rs::non_embedded_japanese_cid_fonts_draw_with_a_craft_fonts_face"]
|
||||
notes = "CID-to-Unicode tables are M2.4; a non-embedded Adobe-Japan1 font is drawn in a craft-fonts Japanese face (Mincho or Gothic by name) when built with craft-fonts; non-embedded GB1, CNS1 and Korea1 fonts still draw nothing (no allowed face)"
|
||||
evidence = ["crates/render/src/raster.rs::huge_cid_width_ranges_terminate", "crates/render/src/raster.rs::non_embedded_japanese_cid_fonts_draw_with_a_craft_fonts_face", "crates/render/src/raster.rs::mincho_substitute_covers_half_width_katakana"]
|
||||
notes = "CID-to-Unicode tables are M2.4; a non-embedded Adobe-Japan1 font is drawn in a craft-fonts Japanese face (Mincho or Gothic by name; BIZ UDMincho for Mincho, which covers half-width katakana) when built with craft-fonts; non-embedded GB1, CNS1 and Korea1 fonts still draw nothing (no allowed face)"
|
||||
|
||||
[[feature]]
|
||||
id = "view.render-image-codecs"
|
||||
|
||||
Reference in new issue
Block a user