fix(edit): editor font enumeration reads a face name without building font metrics (#6086)

This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-11-11 14:46:47 -05:00
1 parent 583ae29599
commit d46d9daea5
3 files changed
+27 -12

No files matched your search

+2 -2
View File
@@ -2,7 +2,7 @@ use super::blocks::{BlockStyle, TextBlock};
use super::fallback::MAX_TYPE3_GLYPHS;
use super::lines::{page_dict, text_lines};
use super::rewrite::rewrite_block_from;
use super::{DecodeBudget, Dict, Document, EditError, Metrics, Object, TextLine, TextSession};
use super::{DecodeBudget, Dict, Document, EditError, Object, TextLine, TextSession};
/// An embedded source font or an approved bundled document-font choice.
/// Source bytes stay in memory and are never installed or copied into another document.
@@ -45,7 +45,7 @@ pub fn text_editor_fonts(doc: &Document, page: usize) -> Result<Vec<TextEditorFo
budget -= bytes.len();
result.push(TextEditorFont {
resource: String::from_utf8_lossy(name).into_owned(),
face: Metrics::from_dict(doc, &font).base_font.rsplit('+').next().unwrap_or_default().to_owned(),
face: pdfcraft_fonts::pdf::base_font_name(doc, &font).rsplit('+').next().unwrap_or_default().to_owned(),
bytes,
choice_only: false,
});
+15 -10
View File
@@ -821,6 +821,20 @@ fn number(t: &Tok) -> Option<u32> {
}
}
/// The font's `/BaseFont` (a Type 3 font's descriptor `/FontName` when it has none), read without
/// building its metrics, so it decodes no CMap.
pub fn base_font_name(doc: &Document, font: &Dict) -> String {
font.name(b"BaseFont")
.map(|name| String::from_utf8_lossy(name).into_owned())
.or_else(|| {
(font.name(b"Subtype") == Some(b"Type3"))
.then(|| dict(doc, font.get(b"FontDescriptor")))
.flatten()
.and_then(|d| d.name(b"FontName").map(|name| String::from_utf8_lossy(name).into_owned()))
})
.unwrap_or_default()
}
/// Codespace ranges and CID mappings from an embedded CMap stream, at most
/// [`MAX_CMAP_RANGES`] of each (every glyph searches them).
#[allow(clippy::type_complexity)]
@@ -909,16 +923,7 @@ impl Metrics {
pub fn from_dict_within(doc: &Document, font: &Dict, budget: &mut DecodeBudget) -> Self {
let mut reads = Reads::new(doc, budget);
let mut m = Self::read_metrics(doc, font, &mut reads);
let base = font
.name(b"BaseFont")
.map(|name| String::from_utf8_lossy(name).into_owned())
.or_else(|| {
(font.name(b"Subtype") == Some(b"Type3"))
.then(|| dict(doc, font.get(b"FontDescriptor")))
.flatten()
.and_then(|d| d.name(b"FontName").map(|name| String::from_utf8_lossy(name).into_owned()))
})
.unwrap_or_default();
let base = base_font_name(doc, font);
m.subset = base.len() > 7 && base.as_bytes()[6] == b'+' && base[..6].bytes().all(|b| b.is_ascii_uppercase());
let (bold, italic) = style_from_name(&base);
m.bold |= bold;
+10
View File
@@ -1823,6 +1823,16 @@ fn low_codes_resolve_to_the_covering_range() {
assert_eq!((u.covering(0xFFFF).is_some(), u.covering(0x1_0001).is_some(), u.covering(0x1_0002).is_none()), (true, true, true));
}
#[test]
fn base_font_name_matches_the_metrics_name() {
let doc = Document::new_empty();
let mut f = Dict::new();
f.set(b"BaseFont".to_vec(), Object::name("ABCDEF+Body"));
assert_eq!(base_font_name(&doc, &f), "ABCDEF+Body");
assert_eq!(base_font_name(&doc, &f), Metrics::from_dict(&doc, &f).base_font);
assert_eq!(base_font_name(&doc, &Dict::new()), "");
}
#[test]
fn refused_subset_reads_do_not_encode_unverified_glyphs() {
let mut doc = Document::new_empty();