mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 07:10:13 +00:00
Merge pull request #7458 from dotCooCoo/fix/eps-cshow-composite-codes
EPS import: cshow runs once for each character of a composite font
This commit is contained in:
@@ -98,7 +98,7 @@ pub(crate) struct Interp<'a> {
|
||||
/// The font a `kshow` procedure sees as current while type in a CIDFont only named is set in
|
||||
/// a font of its own (see `text`).
|
||||
pub kshow_font: Option<DictRef>,
|
||||
/// The `cshow` procedure running over type in a CIDFont only named, with the font it sees as
|
||||
/// The `cshow` procedure running over a composite font's type, with the font it sees as
|
||||
/// current (see `text`).
|
||||
pub cshow: Option<super::text::CShowFont>,
|
||||
pub g: GState,
|
||||
|
||||
@@ -136,6 +136,31 @@ fn codes_map_by_their_codespace_with_notdef_fallbacks() {
|
||||
width("<01>", 30.0);
|
||||
}
|
||||
|
||||
/// `cshow` over a CMap runs its procedure once for each code, whatever its length, with the
|
||||
/// code's last byte and its width (PLRM 3rd ed., `cshow`).
|
||||
#[test]
|
||||
fn cshow_runs_once_for_each_cmap_code() {
|
||||
let fonts = String::from_utf8(cid_font(true)).unwrap() + CMAP;
|
||||
let check = |cond: &str| check(&format!("{fonts} /TestFont 50 selectfont {cond}"));
|
||||
check("[ {pop pop} <418001> cshow ] dup length 2 eq exch aload pop 1 eq exch 65 eq and and");
|
||||
check("0 {pop exch pop add} <418001> cshow 55 sub abs 0.01 lt");
|
||||
check(
|
||||
"{pop pop pop currentfont /CIDFontName get /seen exch def rootfont /FontName get /root exch def} <41> cshow seen /TestCID eq root /TestFont eq and",
|
||||
);
|
||||
// With the CIDFont current, the byte the procedure was given shows the glyph of its code's
|
||||
// CID: <8001>'s last byte draws CID 2, not code 01's CID 1. Another string is an error.
|
||||
let shown = "{pop pop ( ) dup 0 4 -1 roll put show} <418001> cshow";
|
||||
check(&format!("gsave nulldevice 10 20 moveto {shown} currentpoint grestore pop 65 sub abs 0.01 lt"));
|
||||
check("gsave nulldevice 10 20 moveto {{pop pop pop (B) show} <41> cshow} stopped grestore");
|
||||
let r = import(&program(true, &format!("/TestFont 50 selectfont 10 20 moveto {shown}"))).unwrap();
|
||||
let want = import(&program(true, "/TestFont 50 selectfont 10 20 moveto <418001> show")).unwrap();
|
||||
let (p, q) = (paths(&r), paths(&want));
|
||||
assert_eq!(p.len(), 2, "{p:?}");
|
||||
for (a, b) in p.iter().zip(&q) {
|
||||
assert!((a.x0 - b.x0).abs() < 0.01 && (a.width() - b.width()).abs() < 0.01 && (a.height() - b.height()).abs() < 0.01, "{p:?} {q:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// `kshow` over a CMap runs its procedure between each two codes with their values, whatever
|
||||
/// their length, and the next character follows where it moves the point (PLRM 3rd ed., `kshow`).
|
||||
#[test]
|
||||
@@ -188,6 +213,22 @@ fn type2_cidfonts_draw_their_truetype_glyphs_through_cidmap() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A `cshow` procedure over a Type 2 CIDFont without a `FontMatrix` sees the CIDFont current at
|
||||
/// the scale its glyphs are drawn at: an em of the composite font's size, as with the identity.
|
||||
#[test]
|
||||
fn cshow_over_a_type2_cidfont_without_a_matrix_sees_it_at_its_drawn_scale() {
|
||||
let (h, i) = (glyph('H').0, glyph('I').0);
|
||||
let with = cid2_font(&format!("<0000{h:04x}{i:04x}>"));
|
||||
let without = with.replace("/FontMatrix [1 0 0 1 0 0] def", "");
|
||||
assert_ne!(with, without);
|
||||
for fonts in [with, without] {
|
||||
check(&format!(
|
||||
"{fonts}/T2 /Identity-H [/TestCID2] composefont 50 scalefont setfont \
|
||||
{{pop pop pop currentfont /FontMatrix get 0 get /m exch def}} <0001> cshow m 50 sub abs 0.01 lt"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// `Identity-V`: each glyph an em below the one before (writing mode 1).
|
||||
#[test]
|
||||
fn identity_v_sets_glyphs_downwards() {
|
||||
@@ -385,6 +426,30 @@ fn cmaps_map_codes_to_base_fonts_codes_and_glyph_names() {
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(text, "ABC");
|
||||
// A `cshow` procedure that shows the byte it was given, in the base font current while it
|
||||
// runs, shows the character the code maps to, as `show` does.
|
||||
let body = format!(
|
||||
"{plain}{BF_CMAP}/BF /TestBF-H [/Plain42 /Helvetica] composefont 20 scalefont setfont 10 20 moveto \
|
||||
{{pop pop ( ) dup 0 4 -1 roll put show}} <001000110012> cshow"
|
||||
);
|
||||
let r = super::tests::read(&body);
|
||||
let text: String = all(&r.document)
|
||||
.iter()
|
||||
.filter_map(|n| match &n.kind {
|
||||
NodeKind::Text(t) => Some(t.plain_text()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(text, "ABC");
|
||||
let body = format!(
|
||||
"{plain}{BF_CMAP}/BF /TestBF-H [/Plain42 /Helvetica] composefont 50 scalefont setfont 10 20 moveto \
|
||||
{{pop pop ( ) dup 0 4 -1 roll put show}} <000100020003> cshow"
|
||||
);
|
||||
let q = paths(&super::tests::read(&body));
|
||||
assert_eq!(q.len(), 3, "{q:?}");
|
||||
for (a, b) in p.iter().zip(&q) {
|
||||
assert!((a.x0 - b.x0).abs() < 0.01 && (a.width() - b.width()).abs() < 0.01, "{p:?} {q:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A CID-keyed font over a predefined Unicode CMap the file doesn't define, its CIDFont only
|
||||
|
||||
@@ -119,6 +119,45 @@ fn widthshow_matches_a_composite_character_by_font_number_and_code() {
|
||||
check(format!("{} {} sub abs 0.01 lt", end(2), 10.0 + 100.0 * wh));
|
||||
}
|
||||
|
||||
/// `cshow` runs its procedure once for each character of a composite font, not for each byte,
|
||||
/// with the last byte of its code and its width, and with the descendant that draws it as the
|
||||
/// current font while `rootfont` gives the composite font (PLRM 3rd ed., `cshow` and `rootfont`).
|
||||
#[test]
|
||||
fn cshow_runs_once_for_each_composite_character_in_its_descendant() {
|
||||
let (_, wh) = glyph('H');
|
||||
let (_, wi) = glyph('I');
|
||||
let check = |cond: String| super::tests::check(&format!("{} /Comp findfont 50 scalefont setfont {cond}", fonts()));
|
||||
check("[ {pop pop} (\\000\\001\\000\\002) cshow ] dup length 2 eq exch aload pop 2 eq exch 1 eq and and".into());
|
||||
check(format!("0 {{pop exch pop add}} (\\000\\001\\000\\002) cshow {} sub abs 0.01 lt", 50.0 * (wh + wi)));
|
||||
check("{pop pop pop currentfont /FontName get /seen exch def rootfont /FontName get /root exch def} (\\000\\001) cshow seen /T42 eq root /Comp eq and".into());
|
||||
check("{pop pop pop} (\\000\\001) cshow currentfont /FontName get /Comp eq".into());
|
||||
// A character the procedure shows in the descendant is as wide as the composite font sets it.
|
||||
check(format!(
|
||||
"gsave nulldevice 10 20 moveto {{pop pop ( ) dup 0 4 -1 roll put show}} (\\000\\001\\000\\002) cshow currentpoint grestore pop {} sub abs 0.01 lt",
|
||||
10.0 + 50.0 * (wh + wi)
|
||||
));
|
||||
// A character in font number 1 still gives only the last byte of its code, not 258.
|
||||
let two = fonts().replace("/Encoding [0] def", "/Encoding [0 0] def");
|
||||
super::tests::check(&format!("{two} /Comp findfont 50 scalefont setfont [ {{pop pop}} (\\001\\002) cshow ] dup length 1 eq exch 0 get 2 eq and"));
|
||||
}
|
||||
|
||||
/// A `cshow` procedure over a Type 42 descendant without a `FontMatrix` sees it current at the
|
||||
/// scale its characters are drawn at: another character it measures there is as wide as the
|
||||
/// composite font sets it, as with the identity.
|
||||
#[test]
|
||||
fn cshow_over_a_type42_descendant_without_a_matrix_sees_it_at_its_drawn_scale() {
|
||||
let (_, wi) = glyph('I');
|
||||
let with = fonts();
|
||||
let without = with.replacen("/FontType 42 def /FontMatrix [1 0 0 1 0 0] def", "/FontType 42 def", 1);
|
||||
assert_ne!(with, without);
|
||||
for fonts in [with, without] {
|
||||
super::tests::check(&format!(
|
||||
"{fonts} /Comp findfont 50 scalefont setfont {{pop pop pop (\\002) stringwidth pop /w exch def}} (\\000\\001) cshow w {} sub abs 0.01 lt",
|
||||
50.0 * wi
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// `kshow` runs its procedure between each two characters of a composite font, with the values of
|
||||
/// their codes (font number times 256 plus code), and the next character follows where it moves
|
||||
/// the current point (PLRM 3rd ed., `kshow`).
|
||||
|
||||
@@ -251,6 +251,15 @@ impl Interp<'_> {
|
||||
self.g.font = root;
|
||||
return r;
|
||||
}
|
||||
// In a composite font the file carries, once for each character it maps.
|
||||
if let Some(glyphs) = self.composite_glyphs(&s)? {
|
||||
let root = self.g.font.clone();
|
||||
let before = self.cshow.take();
|
||||
let r = self.composite_cshow(&glyphs, &proc, root.clone());
|
||||
self.cshow = before;
|
||||
self.g.font = root;
|
||||
return r;
|
||||
}
|
||||
for b in s {
|
||||
let v = self.width_of(&[b])?;
|
||||
self.push(Obj::Int(i64::from(b)))?;
|
||||
@@ -992,6 +1001,7 @@ impl Spacing {
|
||||
/// A character of a composite font: the descendant font that draws it, its code there (its last
|
||||
/// byte for a CID), its CID and the notdef mapping's (`FMapType` 9), the composite font's matrix
|
||||
/// (over the descendant's own) and whether it is set vertically (`WMode` 1).
|
||||
#[derive(Clone)]
|
||||
struct Mapped {
|
||||
font: DictRef,
|
||||
/// The byte that picked the descendant (the font number).
|
||||
@@ -1006,6 +1016,13 @@ struct Mapped {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl Mapped {
|
||||
/// The last byte of the character's code in the shown string (what `cshow` gives its procedure).
|
||||
fn last(&self) -> u8 {
|
||||
(self.value & 0xff) as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Type in a CIDFont only named, over a predefined CMap ([`Interp::named_cid_type`]): its codes
|
||||
/// with their characters, the font it is set in, whether it runs down a column, and whether each
|
||||
/// code moves the point one em (down a column, or in a CMap VectorCraft can't read).
|
||||
@@ -1016,13 +1033,15 @@ struct NamedCid {
|
||||
ems: bool,
|
||||
}
|
||||
|
||||
/// A `cshow` procedure running over type in a CIDFont only named ([`Interp::named_cshow`]): the
|
||||
/// CIDFont it sees as current, the composite font `rootfont` gives while that CIDFont is current,
|
||||
/// and the type of the code it was given.
|
||||
/// A `cshow` procedure running over a composite font's type ([`Interp::named_cshow`],
|
||||
/// [`Interp::composite_cshow`]): the descendant it sees as current, the composite font `rootfont`
|
||||
/// gives while that descendant is current, and the code it was given: over a CIDFont only named,
|
||||
/// its type; over a composite font the file carries, the character it maps to.
|
||||
pub(super) struct CShowFont {
|
||||
selected: DictRef,
|
||||
root: DictRef,
|
||||
code: NamedCid,
|
||||
code: Option<NamedCid>,
|
||||
mapped: Option<Mapped>,
|
||||
}
|
||||
|
||||
/// What drawing a glyph does with it.
|
||||
@@ -1069,6 +1088,22 @@ impl Interp<'_> {
|
||||
/// codes mapped to a descendant and a CID, §5.11.5); `None` for other fonts and mappings,
|
||||
/// which are set as type in the font of the composite font's name.
|
||||
fn composite_glyphs(&mut self, s: &[u8]) -> Res<Option<Vec<Mapped>>> {
|
||||
// Inside a `cshow` procedure over a composite font the file carries, with the descendant
|
||||
// it selected current, the one byte the procedure was given stands for the character its
|
||||
// code maps to: a CMap's CID, base-font code or glyph name. In a CIDFont any other string
|
||||
// is a `rangecheck` error (PLRM 3rd ed., `cshow`: the glyph of the selected CID); in a
|
||||
// base font it is shown as it is.
|
||||
if let Some(c) = self.cshow.as_ref()
|
||||
&& let Some(mapped) = c.mapped.as_ref()
|
||||
&& self.g.font.as_ref().is_some_and(|f| f.same(&c.selected))
|
||||
{
|
||||
if s == [mapped.last()] {
|
||||
return Ok(Some(vec![mapped.clone()]));
|
||||
}
|
||||
if mapped.cid.is_some() {
|
||||
return ps_err("rangecheck", "a string shown in cshow");
|
||||
}
|
||||
}
|
||||
let Some(font) = self.g.font.clone() else { return Ok(None) };
|
||||
let map = {
|
||||
let f = font.borrow();
|
||||
@@ -1220,17 +1255,18 @@ impl Interp<'_> {
|
||||
/// that code when `code` and with none otherwise, while the CIDFont it selected is current.
|
||||
fn cshow_code(&self, code: bool) -> Option<NamedCid> {
|
||||
let c = self.cshow.as_ref()?;
|
||||
let given = c.code.as_ref()?;
|
||||
if !self.g.font.as_ref().is_some_and(|f| f.same(&c.selected)) {
|
||||
return None;
|
||||
}
|
||||
let mut codes = Decoded::default();
|
||||
if code {
|
||||
let mut chars = c.code.codes.text.chars();
|
||||
for &(value, n) in &c.code.codes.codes {
|
||||
let mut chars = given.codes.text.chars();
|
||||
for &(value, n) in &given.codes.codes {
|
||||
codes.push(value, chars.by_ref().take(n));
|
||||
}
|
||||
}
|
||||
Some(NamedCid { codes, font: c.code.font.clone(), vertical: c.code.vertical, ems: c.code.ems })
|
||||
Some(NamedCid { codes, font: given.font.clone(), vertical: given.vertical, ems: given.ems })
|
||||
}
|
||||
|
||||
/// The CMap's name, the composite font, the CIDFont's name and the writing mode of
|
||||
@@ -1318,7 +1354,7 @@ impl Interp<'_> {
|
||||
self.push(Obj::Int(i64::from(code & 0xff)))?;
|
||||
self.push_num(v.x)?;
|
||||
self.push_num(v.y)?;
|
||||
self.cshow = Some(CShowFont { selected: selected.clone(), root: composite.clone(), code: one });
|
||||
self.cshow = Some(CShowFont { selected: selected.clone(), root: composite.clone(), code: Some(one), mapped: None });
|
||||
self.g.font = Some(selected.clone());
|
||||
let go = self.body(proc);
|
||||
self.g.font = root.clone();
|
||||
@@ -1329,6 +1365,41 @@ impl Interp<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `cshow`'s procedure `proc` run for each character `glyphs` of composite font `root` maps,
|
||||
/// with the last byte of its code and its width (PLRM 3rd ed., `cshow`). While it runs the
|
||||
/// descendant that draws the character, at the size the composite font sets it, is the current
|
||||
/// font and `rootfont` gives `root`. The current font is restored when the procedure completes.
|
||||
fn composite_cshow(&mut self, glyphs: &[Mapped], proc: &Obj, root: Option<DictRef>) -> Res {
|
||||
let Some(composite) = root.clone() else { return Ok(()) };
|
||||
for g in glyphs {
|
||||
let v = self.composite_glyph(g, Point::ZERO, Draw::No)?;
|
||||
self.push(Obj::Int(i64::from(g.last())))?;
|
||||
self.push_num(v.x)?;
|
||||
self.push_num(v.y)?;
|
||||
// Scaling the descendant copies its dictionary for every character: charged by its
|
||||
// size, so a huge font dictionary in a loop runs out of the budget.
|
||||
let entries = g.font.borrow().len();
|
||||
self.spend(entries as u64)?;
|
||||
// Without a matrix, a Type 42 font or a Type 2 CIDFont is in ems, as `composite_glyph`
|
||||
// and `cid_glyph_at` draw it.
|
||||
let selected = {
|
||||
let f = g.font.borrow();
|
||||
let default = if units(&f) == 1.0 { 1.0 } else { 0.001 };
|
||||
let mut d = f.clone();
|
||||
d.insert(Key::name("FontMatrix"), matrix_obj(g.outer * font_matrix(&f, default)));
|
||||
DictRef::new(d)
|
||||
};
|
||||
self.cshow = Some(CShowFont { selected: selected.clone(), root: composite.clone(), code: None, mapped: Some(g.clone()) });
|
||||
self.g.font = Some(selected);
|
||||
let go = self.body(proc);
|
||||
self.g.font = root.clone();
|
||||
if !go? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// How far codes `codes` of type `named` move the current point (user space) when
|
||||
/// [`Self::show_named_cid`] shows them with no extra spacing.
|
||||
fn named_cid_width(&mut self, named: &NamedCid, codes: &Decoded) -> Res<Vec2> {
|
||||
|
||||
Reference in New Issue
Block a user