Don't hang quotes
@ -497,16 +497,6 @@ fn prepare<'a>(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Segment::Node(node) => {
|
Segment::Node(node) => {
|
||||||
// Prevent margin overhang in the inline node except if there's
|
|
||||||
// just this one.
|
|
||||||
let local;
|
|
||||||
let styles = if par.0.len() != 1 {
|
|
||||||
local = StyleMap::with(TextNode::OVERHANG, false);
|
|
||||||
local.chain(&styles)
|
|
||||||
} else {
|
|
||||||
styles
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = Size::new(regions.first.x, regions.base.y);
|
let size = Size::new(regions.first.x, regions.base.y);
|
||||||
let pod = Regions::one(size, regions.base, Spec::splat(false));
|
let pod = Regions::one(size, regions.base, Spec::splat(false));
|
||||||
let frame = node.layout(ctx, &pod, styles)?.remove(0);
|
let frame = node.layout(ctx, &pod, styles)?.remove(0);
|
||||||
@ -994,9 +984,11 @@ fn commit(
|
|||||||
// Handle hanging punctuation to the left.
|
// Handle hanging punctuation to the left.
|
||||||
if let Some(Item::Text(text)) = reordered.first() {
|
if let Some(Item::Text(text)) = reordered.first() {
|
||||||
if let Some(glyph) = text.glyphs.first() {
|
if let Some(glyph) = text.glyphs.first() {
|
||||||
if text.styles.get(TextNode::OVERHANG) {
|
if !text.dir.is_positive()
|
||||||
let start = text.dir.is_positive();
|
&& text.styles.get(TextNode::OVERHANG)
|
||||||
let amount = overhang(glyph.c, start) * glyph.x_advance.at(text.size);
|
&& (reordered.len() > 1 || text.glyphs.len() > 1)
|
||||||
|
{
|
||||||
|
let amount = overhang(glyph.c) * glyph.x_advance.at(text.size);
|
||||||
offset -= amount;
|
offset -= amount;
|
||||||
remaining += amount;
|
remaining += amount;
|
||||||
}
|
}
|
||||||
@ -1006,11 +998,11 @@ fn commit(
|
|||||||
// Handle hanging punctuation to the right.
|
// Handle hanging punctuation to the right.
|
||||||
if let Some(Item::Text(text)) = reordered.last() {
|
if let Some(Item::Text(text)) = reordered.last() {
|
||||||
if let Some(glyph) = text.glyphs.last() {
|
if let Some(glyph) = text.glyphs.last() {
|
||||||
if text.styles.get(TextNode::OVERHANG)
|
if text.dir.is_positive()
|
||||||
|
&& text.styles.get(TextNode::OVERHANG)
|
||||||
&& (reordered.len() > 1 || text.glyphs.len() > 1)
|
&& (reordered.len() > 1 || text.glyphs.len() > 1)
|
||||||
{
|
{
|
||||||
let start = !text.dir.is_positive();
|
let amount = overhang(glyph.c) * glyph.x_advance.at(text.size);
|
||||||
let amount = overhang(glyph.c, start) * glyph.x_advance.at(text.size);
|
|
||||||
remaining += amount;
|
remaining += amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1110,24 +1102,19 @@ fn reorder<'a>(line: &'a Line<'a>) -> Vec<&'a Item<'a>> {
|
|||||||
reordered
|
reordered
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much a character should hang into the margin.
|
/// How much a character should hang into the end margin.
|
||||||
///
|
///
|
||||||
/// For selection of overhang characters, see also:
|
/// For more discussion, see:
|
||||||
/// https://recoveringphysicist.com/21/
|
/// https://recoveringphysicist.com/21/
|
||||||
fn overhang(c: char, start: bool) -> f64 {
|
fn overhang(c: char) -> f64 {
|
||||||
match c {
|
match c {
|
||||||
'“' | '”' | '„' | '‟' | '"' if start => 1.0,
|
// Dashes.
|
||||||
'‘' | '’' | '‚' | '‛' | '\'' if start => 1.0,
|
'–' | '—' => 0.2,
|
||||||
|
'-' => 0.55,
|
||||||
'“' | '”' | '„' | '‟' | '"' if !start => 0.6,
|
|
||||||
'‘' | '’' | '‚' | '‛' | '\'' if !start => 0.6,
|
|
||||||
'–' | '—' if !start => 0.2,
|
|
||||||
'-' if !start => 0.55,
|
|
||||||
|
|
||||||
|
// Punctuation.
|
||||||
'.' | ',' => 0.8,
|
'.' | ',' => 0.8,
|
||||||
':' | ';' => 0.3,
|
':' | ';' => 0.3,
|
||||||
'«' | '»' => 0.2,
|
|
||||||
'‹' | '›' => 0.4,
|
|
||||||
|
|
||||||
// Arabic and Ideographic
|
// Arabic and Ideographic
|
||||||
'\u{60C}' | '\u{6D4}' => 0.4,
|
'\u{60C}' | '\u{6D4}' => 0.4,
|
||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
@ -1,34 +1,23 @@
|
|||||||
// Test micro-typographical shenanigans.
|
// Test micro-typographical shenanigans.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test that overhang is off by default in boxes.
|
// Test hanging punctuation.
|
||||||
A#box["]B
|
#set page(width: 130pt, margins: 15pt)
|
||||||
|
#set par(justify: true, linebreaks: "simple")
|
||||||
|
#set text(lang: "en", size: 9pt)
|
||||||
|
#rect(fill: rgb(repr(teal) + "00"), width: 100%)[
|
||||||
|
This is a little bit of text that builds up to
|
||||||
|
hang-ing hyphens and dash---es and then, you know,
|
||||||
|
some punctuation in the margin.
|
||||||
|
]
|
||||||
|
|
||||||
---
|
// Test hanging punctuation with RTL.
|
||||||
// Test justified quotes.
|
#set text(lang: "he")
|
||||||
#set par(justify: true)
|
בנייה נכונה של משפטים ארוכים דורשת ידע בשפה. אז בואו נדבר על מזג האוויר.
|
||||||
“A quote that hangs a bit into the margin.” \
|
|
||||||
--- somebody
|
|
||||||
|
|
||||||
---
|
|
||||||
// Test fancy quotes in the left margin.
|
|
||||||
#set par(align: right)
|
|
||||||
»Book quotes are even smarter.« \
|
|
||||||
›Book quotes are even smarter.‹ \
|
|
||||||
|
|
||||||
---
|
|
||||||
// Test fancy quotes in the right margin.
|
|
||||||
#set par(align: left)
|
|
||||||
«Book quotes are even smarter.» \
|
|
||||||
‹Book quotes are even smarter.› \
|
|
||||||
|
|
||||||
---
|
|
||||||
#set text(lang: "ar", "Noto Sans Arabic", "IBM Plex Sans")
|
|
||||||
"المطر هو الحياة" \
|
|
||||||
المطر هو الحياة
|
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test that lone punctuation doesn't overhang into the margin.
|
// Test that lone punctuation doesn't overhang into the margin.
|
||||||
#set page(margins: 0pt)
|
#set page(margins: 0pt)
|
||||||
#set par(align: right)
|
#set par(align: end)
|
||||||
|
#set text(dir: rtl)
|
||||||
:
|
:
|
||||||
|