mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 21:44:57 +00:00
Merge pull request #7557 from dotCooCoo/fix/eps-kshow-composite
EPS import: kshow runs its procedure between the characters of a composite font
This commit is contained in:
@@ -70,6 +70,8 @@ pub(super) struct CidChar {
|
||||
/// The base-font code or glyph name a `bfchar` or `bfrange` mapping gives it, instead of a
|
||||
/// CID.
|
||||
pub base: Option<Base>,
|
||||
/// The value of its code (`<8001>` is 32769).
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
impl CMap {
|
||||
@@ -146,12 +148,12 @@ impl CMap {
|
||||
work += at.map_or(self.bases.len(), |k| k + 1) as u64;
|
||||
let base = at.and_then(|k| self.bases.get(k)).and_then(|m| Some((m.4, base_of(&m.3, c - m.1)?)));
|
||||
out.push(match (matched.is_some(), find(&self.cids, &mut work), base) {
|
||||
(true, Some((font, cid, _)), _) => CidChar { font, cid, notdef, base: None },
|
||||
(true, None, Some((font, base))) => CidChar { font, cid: 0, notdef: None, base: Some(base) },
|
||||
(true, Some((font, cid, _)), _) => CidChar { font, cid, notdef, base: None, value: c },
|
||||
(true, None, Some((font, base))) => CidChar { font, cid: 0, notdef: None, base: Some(base), value: c },
|
||||
// No character mapping: the notdef mapping's CID, else font 0's CID 0.
|
||||
_ => match find(&self.notdefs, &mut work) {
|
||||
Some((font, _, base)) if matched.is_some() => CidChar { font, cid: base, notdef: None, base: None },
|
||||
_ => CidChar { font: 0, cid: 0, notdef: None, base: None },
|
||||
Some((font, _, base)) if matched.is_some() => CidChar { font, cid: base, notdef: None, base: None, value: c },
|
||||
_ => CidChar { font: 0, cid: 0, notdef: None, base: None, value: c },
|
||||
},
|
||||
});
|
||||
i += n;
|
||||
|
||||
@@ -136,6 +136,16 @@ fn codes_map_by_their_codespace_with_notdef_fallbacks() {
|
||||
width("<01>", 30.0);
|
||||
}
|
||||
|
||||
/// `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]
|
||||
fn kshow_runs_between_each_two_cmap_codes() {
|
||||
let fonts = String::from_utf8(cid_font(true)).unwrap() + CMAP;
|
||||
let check = |cond: &str| check(&format!("{fonts} /TestFont 50 selectfont {cond}"));
|
||||
check("gsave nulldevice 10 20 moveto [ {} <418001> kshow ] grestore aload length 2 eq exch 16#8001 eq and exch 65 eq and");
|
||||
check("gsave nulldevice 10 20 moveto {pop pop 100 0 rmoveto} <418001> kshow currentpoint grestore pop 165 sub abs 0.01 lt");
|
||||
}
|
||||
|
||||
/// `usecmap` brings in another CMap's ranges and mappings; the CMap's own come after them.
|
||||
#[test]
|
||||
fn usecmap_takes_the_parents_mappings() {
|
||||
|
||||
@@ -119,6 +119,35 @@ 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));
|
||||
}
|
||||
|
||||
/// `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`).
|
||||
#[test]
|
||||
fn kshow_runs_between_each_two_composite_characters() {
|
||||
let (_, wh) = glyph('H');
|
||||
let (_, wi) = glyph('I');
|
||||
let fonts = fonts().replace("/Encoding [0] def", "/Encoding [0 0] def");
|
||||
let check = |cond: String| super::tests::check(&format!("{fonts} /Comp findfont 50 scalefont setfont {cond}"));
|
||||
check("gsave nulldevice 10 20 moveto [ {} (\\000\\001\\001\\002\\000\\001) kshow ] grestore aload length 4 eq exch 1 eq and exch 258 eq and exch 258 eq and exch 1 eq and".into());
|
||||
check(format!(
|
||||
"gsave nulldevice 10 20 moveto {{pop pop 100 0 rmoveto}} (\\000\\001\\000\\002) kshow currentpoint grestore pop {} sub abs 0.01 lt",
|
||||
10.0 + 50.0 * (wh + wi) + 100.0
|
||||
));
|
||||
// Drawn, the I starts where the procedure moved the point.
|
||||
let r = read(&format!("{fonts}/Comp findfont 50 scalefont setfont 10 20 moveto {{pop pop 100 0 rmoveto}} (\\000\\001\\000\\002) kshow"));
|
||||
let paths: Vec<_> = all(&r.document).iter().filter(|n| matches!(n.kind, NodeKind::Path { .. })).map(|n| bounds(n)).collect();
|
||||
assert_eq!(paths.len(), 2, "{paths:?}");
|
||||
assert!(paths[1].x0 > 10.0 + 50.0 * wh + 100.0 - 1.0, "{paths:?}");
|
||||
// A procedure that draws: its square goes after the glyphs' group, outside it.
|
||||
let r = read(&format!(
|
||||
"{fonts}/Comp findfont 50 scalefont setfont 10 20 moveto {{pop pop currentpoint 2 2 rectfill}} (\\000\\001\\000\\002\\000\\001) kshow"
|
||||
));
|
||||
let top = super::tests::objects(&r.document);
|
||||
assert_eq!(top.len(), 3, "{top:?}");
|
||||
assert_eq!(top[0].children().map(Vec::len), Some(3));
|
||||
assert!(top[1..].iter().all(|n| matches!(n.kind, NodeKind::Path { .. }) && (bounds(n).width() - 2.0).abs() < 0.01), "{top:?}");
|
||||
}
|
||||
|
||||
/// Measuring a composite font's Type 42 glyphs (`stringwidth` on a long string in a `loop`)
|
||||
/// counts against the operation budget, as drawing them does.
|
||||
#[test]
|
||||
|
||||
@@ -999,6 +999,8 @@ struct Mapped {
|
||||
cid: Option<(u32, Option<u32>)>,
|
||||
outer: Affine,
|
||||
vertical: bool,
|
||||
/// The value of the character's code in the shown string (what `kshow` gives its procedure).
|
||||
value: u32,
|
||||
}
|
||||
|
||||
/// Type in a CIDFont only named, over a predefined CMap ([`Interp::named_cid_type`]): its codes
|
||||
@@ -1097,7 +1099,8 @@ impl Interp<'_> {
|
||||
if let Some(Obj::Dict(d)) = index.and_then(|i| fonts.get(i as usize))
|
||||
&& font_type(&d.borrow()) != Some(0.0)
|
||||
{
|
||||
out.push(Mapped { font: d.clone(), pick, code, name: None, cid: None, outer, vertical: false });
|
||||
let value = u32::from(pick) * 256 + u32::from(code);
|
||||
out.push(Mapped { font: d.clone(), pick, code, name: None, cid: None, outer, vertical: false, value });
|
||||
}
|
||||
}
|
||||
Ok(Some(out))
|
||||
@@ -1124,21 +1127,28 @@ impl Interp<'_> {
|
||||
.filter_map(|c| match (fonts.get(c.font), c.base) {
|
||||
// A base font's code, or its glyph name (at the code its encoding gives it, if any).
|
||||
(Some(Obj::Dict(d)), Some(Base::Code(code))) => {
|
||||
Some(Mapped { font: d.clone(), pick: 0, code, name: None, cid: None, outer, vertical })
|
||||
Some(Mapped { font: d.clone(), pick: 0, code, name: None, cid: None, outer, vertical, value: c.value })
|
||||
}
|
||||
(Some(Obj::Dict(d)), Some(Base::Name(name))) => {
|
||||
let code = encoded(&d.borrow(), &name);
|
||||
let name = code.is_none().then_some(name);
|
||||
Some(Mapped { font: d.clone(), pick: 0, code: code.unwrap_or(0), name, cid: None, outer, vertical })
|
||||
Some(Mapped { font: d.clone(), pick: 0, code: code.unwrap_or(0), name, cid: None, outer, vertical, value: c.value })
|
||||
}
|
||||
// A code without a mapping in a base font (not a CIDFont): its code 0, the
|
||||
// `.notdef` glyph CID 0 stands for in a CIDFont.
|
||||
(Some(Obj::Dict(d)), None) if !d.borrow().contains_key(&Key::name("CIDFontType")) => {
|
||||
Some(Mapped { font: d.clone(), pick: 0, code: 0, name: None, cid: None, outer, vertical })
|
||||
}
|
||||
(Some(Obj::Dict(d)), None) => {
|
||||
Some(Mapped { font: d.clone(), pick: 0, code: (c.cid & 0xff) as u8, name: None, cid: Some((c.cid, c.notdef)), outer, vertical })
|
||||
Some(Mapped { font: d.clone(), pick: 0, code: 0, name: None, cid: None, outer, vertical, value: c.value })
|
||||
}
|
||||
(Some(Obj::Dict(d)), None) => Some(Mapped {
|
||||
font: d.clone(),
|
||||
pick: 0,
|
||||
code: (c.cid & 0xff) as u8,
|
||||
name: None,
|
||||
cid: Some((c.cid, c.notdef)),
|
||||
outer,
|
||||
vertical,
|
||||
value: c.value,
|
||||
}),
|
||||
_ => None,
|
||||
})
|
||||
.collect())
|
||||
@@ -1346,6 +1356,12 @@ impl Interp<'_> {
|
||||
fn show_composite(&mut self, glyphs: &[Mapped], spacing: &Spacing) -> Res {
|
||||
let start = self.out.drawn.len();
|
||||
let mut text = String::new();
|
||||
let kern: Vec<u32> = match spacing {
|
||||
Spacing::Kern(_) => glyphs.iter().map(|g| g.value).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
// Where in the objects drawn each run of `kshow`'s procedure put its art.
|
||||
let mut kerned = Vec::new();
|
||||
for (i, g) in glyphs.iter().enumerate() {
|
||||
let at = self.user_point()?;
|
||||
let w = self.composite_glyph(g, at, Draw::Fill)?;
|
||||
@@ -1353,6 +1369,12 @@ impl Interp<'_> {
|
||||
// times 256 plus its code (PLRM 3rd ed., `widthshow` in chapter 8).
|
||||
let d = spacing.advance(i, i64::from(g.pick) * 256 + i64::from(g.code), w);
|
||||
self.g.cur = Some(self.xf() * (at + d));
|
||||
// `kshow`'s procedure runs between each two characters with the values of their codes.
|
||||
let before = self.out.drawn.len();
|
||||
self.kern(spacing, &kern, i, at + d)?;
|
||||
if self.out.drawn.len() > before {
|
||||
kerned.push(before..self.out.drawn.len());
|
||||
}
|
||||
// A CID stands for no character (its code is only the CID's low byte).
|
||||
let c = if g.cid.is_some() { '\u{2022}' } else { g.name.as_deref().map_or_else(|| code_char(&g.font.borrow(), g.code), glyph_char) };
|
||||
if c != '\u{2022}' {
|
||||
@@ -1364,7 +1386,13 @@ impl Interp<'_> {
|
||||
{
|
||||
text = name.to_string();
|
||||
}
|
||||
// What `kshow`'s procedure draws goes after the type, outside its group.
|
||||
let mut art = Vec::new();
|
||||
for run in kerned.into_iter().rev() {
|
||||
art.splice(0..0, self.out.drawn.drain(run));
|
||||
}
|
||||
self.group_glyphs(start, &text);
|
||||
self.out.drawn.extend(art);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user