Improvements to bidi rendering
This commit is contained in:
parent
0b0ed1df68
commit
956331e152
4 changed files with 187 additions and 110 deletions
|
|
@ -1 +1 @@
|
|||
عندما يريد العالم أن يتكلّم ، فهو يتحدّث بلغة يونيكود. تسجّل الآن لحضور المؤتمر الدولي العاشر ليونيكود (Unicode Conference)، الذي سيعقد في 10-12 آذار 1997 بمدينة مَايِنْتْس، ألمانيا. و سيجمع المؤتمر بين خبراء من كافة قطاعات الصناعة على الشبكة العالمية انترنيت ويونيكود، حيث ستتم، على الصعيدين الدولي والمحلي على حد سواء مناقشة سبل استخدام يونكود في النظم القائمة وفيما يخص التطبيقات الحاسوبية، الخطوط، تصميم النصوص والحوسبة متعددة اللغات.
|
||||
عندما يريد العالم أن يتكلّم ، فهو يتحدّث بلغة يونيكود. تسجّل الآن لحضور المؤتمر الدولي العاشر ليونيكود (Unicode Conference)، الذي سيعقد في 10-12 آذار 1997 بمدينة مَايِنْتْس، ألمانيا. و سيجمع المؤتمر بين خبراء من كافة قطاعات الصناعة على الشبكة العالمية انترنيت ويونيكود، حيث ستتم، على الصعيدين الدولي والمحلي على حد سواء مناقشة سبل استخدام يونكود في النظم القائمة وفيما يخص التطبيقات الحاسوبية، الخطوط، تصميم النصوص والحوسبة متعددة اللغات.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
כאשר העולם רוצה לדבר, הוא מדבר ב־Unicode. הירשמו כעת לכנס Unicode הבינלאומי העשירי, שייערך בין התאריכים 12־10 במרץ 1997, בְּמָיְינְץ שבגרמניה. בכנס ישתתפו מומחים מכל ענפי התעשייה בנושא האינטרנט העולמי וה־Unicode, בהתאמה לשוק הבינלאומי והמקומי, ביישום Unicode במערכות הפעלה וביישומים, בגופנים, בפריסת טקסט ובמחשוב רב־לשוני.
|
||||
כאשר העולם רוצה לדבר, הוא מדבר ב־Unicode. הירשמו כעת לכנס Unicode הבינלאומי העשירי, שייערך בין התאריכים 12־10 במרץ 1997, בְּמָיְינְץ שבגרמניה. בכנס ישתתפו מומחים מכל ענפי התעשייה בנושא האינטרנט העולמי וה־Unicode, בהתאמה לשוק הבינלאומי והמקומי, ביישום Unicode במערכות הפעלה וביישומים, בגופנים, בפריסת טקסט ובמחשוב רב־לשוני.
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
use super::{Font, FontLineIndex, FontShapeGlyph, FontShapeLine, FontShapeSpan};
|
||||
use super::{Font, FontLineIndex, FontShapeGlyph, FontShapeWord, FontShapeLine, FontShapeSpan};
|
||||
|
||||
pub struct FontMatches<'a> {
|
||||
pub fonts: Vec<&'a Font<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FontMatches<'a> {
|
||||
fn shape_span(&self, line: &str, start_span: usize, end_span: usize) -> FontShapeSpan {
|
||||
let span = &line[start_span..end_span];
|
||||
fn shape_word(&self, line: &str, start_word: usize, end_word: usize) -> FontShapeWord {
|
||||
let word = &line[start_word..end_word];
|
||||
|
||||
let mut spans_by_font = Vec::with_capacity(self.fonts.len());
|
||||
let mut words_by_font = Vec::with_capacity(self.fonts.len());
|
||||
for (font_i, font) in self.fonts.iter().enumerate() {
|
||||
let font_scale = font.rustybuzz.units_per_em() as f32;
|
||||
|
||||
let mut buffer = rustybuzz::UnicodeBuffer::new();
|
||||
buffer.push_str(span);
|
||||
buffer.push_str(word);
|
||||
buffer.guess_segment_properties();
|
||||
let direction = buffer.direction();
|
||||
if font_i == 0 {
|
||||
//println!("{:?}, {:?}: '{}'", script, direction, span);
|
||||
println!("{:?}, {:?}: '{}'", buffer.script(), direction, word);
|
||||
}
|
||||
|
||||
let glyph_buffer = rustybuzz::shape(&font.rustybuzz, &[], buffer);
|
||||
|
|
@ -44,8 +44,8 @@ impl<'a> FontMatches<'a> {
|
|||
let inner = font.rusttype.glyph(rusttype::GlyphId(info.glyph_id as u16));
|
||||
|
||||
glyphs.push(FontShapeGlyph {
|
||||
start: start_span + info.cluster as usize,
|
||||
end: end_span, // Set later
|
||||
start: start_word + info.cluster as usize,
|
||||
end: end_word, // Set later
|
||||
x_advance,
|
||||
y_advance,
|
||||
x_offset,
|
||||
|
|
@ -86,20 +86,17 @@ impl<'a> FontMatches<'a> {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
let span = FontShapeSpan {
|
||||
direction,
|
||||
glyphs
|
||||
};
|
||||
let word = FontShapeWord { glyphs };
|
||||
if misses == 0 {
|
||||
return span;
|
||||
return word;
|
||||
} else {
|
||||
spans_by_font.push((misses, span));
|
||||
words_by_font.push((misses, word));
|
||||
}
|
||||
}
|
||||
|
||||
let mut least_misses_i = 0;
|
||||
let mut least_misses = usize::MAX;
|
||||
for (i, (misses, _)) in spans_by_font.iter().enumerate() {
|
||||
for (i, (misses, _)) in words_by_font.iter().enumerate() {
|
||||
if *misses < least_misses {
|
||||
least_misses_i = i;
|
||||
least_misses = *misses;
|
||||
|
|
@ -110,41 +107,65 @@ impl<'a> FontMatches<'a> {
|
|||
//println!("MISSES {}, {}", least_misses_i, least_misses);
|
||||
}
|
||||
|
||||
spans_by_font.remove(least_misses_i).1
|
||||
words_by_font.remove(least_misses_i).1
|
||||
}
|
||||
|
||||
fn shape_span(&self, line: &str, start_span: usize, end_span: usize, rtl: bool) -> FontShapeSpan {
|
||||
use unicode_script::{Script, UnicodeScript};
|
||||
|
||||
let span = &line[start_span..end_span];
|
||||
|
||||
let mut words = Vec::new();
|
||||
|
||||
let mut start = 0;
|
||||
/*
|
||||
let mut word_script = Script::Unknown;
|
||||
for (i, c) in span.char_indices() {
|
||||
let next_script = c.script();
|
||||
if word_script != next_script && word_script != Script::Unknown {
|
||||
// No combination, start new span
|
||||
words.push(self.shape_word(line, start_span + start, start_span + i));
|
||||
start = i;
|
||||
word_script = Script::Unknown;
|
||||
} else {
|
||||
word_script = next_script;
|
||||
}
|
||||
}
|
||||
*/
|
||||
words.push(self.shape_word(line, start_span + start, end_span));
|
||||
|
||||
FontShapeSpan {
|
||||
rtl,
|
||||
words,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape_line(&self, line_i: FontLineIndex, line: &str) -> FontShapeLine {
|
||||
use unicode_script::{Script, UnicodeScript};
|
||||
|
||||
//TODO: more special handling of characters
|
||||
let mut spans = Vec::new();
|
||||
|
||||
let mut start = 0;
|
||||
let mut prev = Script::Unknown;
|
||||
for (i, c) in line.char_indices() {
|
||||
if ! line.is_char_boundary(i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let cur = c.script();
|
||||
if prev != cur && prev != Script::Unknown {
|
||||
// No combination, start new span
|
||||
spans.push(self.shape_span(line, start, i));
|
||||
start = i;
|
||||
prev = Script::Unknown;
|
||||
} else {
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
|
||||
spans.push(self.shape_span(line, start, line.len()));
|
||||
|
||||
let bidi = unicode_bidi::BidiInfo::new(line, None);
|
||||
let rtl = if bidi.paragraphs.is_empty() {
|
||||
false
|
||||
} else {
|
||||
assert_eq!(bidi.paragraphs.len(), 1);
|
||||
bidi.paragraphs[0].level.is_rtl()
|
||||
let para_info = &bidi.paragraphs[0];
|
||||
let para_rtl = para_info.level.is_rtl();
|
||||
|
||||
let paragraph = unicode_bidi::Paragraph::new(&bidi, ¶_info);
|
||||
|
||||
let mut start = 0;
|
||||
let mut span_rtl = para_rtl;
|
||||
for i in paragraph.para.range.clone() {
|
||||
let next_rtl = paragraph.info.levels[i].is_rtl();
|
||||
if span_rtl != next_rtl {
|
||||
span_rtl = next_rtl;
|
||||
spans.push(self.shape_span(line, start, i, span_rtl));
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
spans.push(self.shape_span(line, start, line.len(), span_rtl));
|
||||
|
||||
para_rtl
|
||||
};
|
||||
|
||||
FontShapeLine {
|
||||
|
|
|
|||
|
|
@ -19,11 +19,15 @@ pub struct FontShapeGlyph<'a> {
|
|||
pub inner: rusttype::Glyph<'a>,
|
||||
}
|
||||
|
||||
pub struct FontShapeSpan<'a> {
|
||||
pub direction: rustybuzz::Direction,
|
||||
pub struct FontShapeWord<'a> {
|
||||
pub glyphs: Vec<FontShapeGlyph<'a>>,
|
||||
}
|
||||
|
||||
pub struct FontShapeSpan<'a> {
|
||||
pub rtl: bool,
|
||||
pub words: Vec<FontShapeWord<'a>>,
|
||||
}
|
||||
|
||||
pub struct FontShapeLine<'a> {
|
||||
pub line_i: FontLineIndex,
|
||||
pub rtl: bool,
|
||||
|
|
@ -31,47 +35,22 @@ pub struct FontShapeLine<'a> {
|
|||
}
|
||||
|
||||
impl<'a> FontShapeLine<'a> {
|
||||
pub fn layout(&self, font_size: i32, line_width: i32, layout_lines: &mut Vec<FontLayoutLine<'a>>, mut layout_i: usize) {
|
||||
|
||||
fn layout_ltr(&self, font_size: i32, line_width: i32, layout_lines: &mut Vec<FontLayoutLine<'a>>, mut layout_i: usize) {
|
||||
let mut push_line = true;
|
||||
let mut glyphs = Vec::new();
|
||||
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
for span in self.spans.iter() {
|
||||
let mut span_width = 0.0;
|
||||
for glyph in span.glyphs.iter() {
|
||||
let x_advance = font_size as f32 * glyph.x_advance;
|
||||
span_width += x_advance;
|
||||
}
|
||||
for word in span.words.iter() {
|
||||
for glyph in word.glyphs.iter() {
|
||||
let x_advance = font_size as f32 * glyph.x_advance;
|
||||
let y_advance = font_size as f32 * glyph.y_advance;
|
||||
let x_offset = font_size as f32 * glyph.x_offset;
|
||||
let y_offset = font_size as f32 * glyph.y_offset;
|
||||
|
||||
if self.rtl {
|
||||
if glyphs.is_empty() {
|
||||
x = line_width as f32;
|
||||
}
|
||||
x -= span_width;
|
||||
}
|
||||
|
||||
for glyph in span.glyphs.iter() {
|
||||
let x_advance = font_size as f32 * glyph.x_advance;
|
||||
let y_advance = font_size as f32 * glyph.y_advance;
|
||||
let x_offset = font_size as f32 * glyph.x_offset;
|
||||
let y_offset = font_size as f32 * glyph.y_offset;
|
||||
|
||||
//TODO: make wrapping optional
|
||||
if self.rtl {
|
||||
if x < 0.0 {
|
||||
let mut glyphs_swap = Vec::new();
|
||||
std::mem::swap(&mut glyphs, &mut glyphs_swap);
|
||||
layout_lines.push(FontLayoutLine {
|
||||
line_i: self.line_i,
|
||||
glyphs: glyphs_swap
|
||||
});
|
||||
layout_i += 1;
|
||||
|
||||
x = line_width as f32 - x_advance;
|
||||
y = 0.0;
|
||||
}
|
||||
} else {
|
||||
//TODO: make wrapping optional
|
||||
if x + x_advance > line_width as f32 {
|
||||
let mut glyphs_swap = Vec::new();
|
||||
std::mem::swap(&mut glyphs, &mut glyphs_swap);
|
||||
|
|
@ -84,43 +63,39 @@ impl<'a> FontShapeLine<'a> {
|
|||
x = 0.0;
|
||||
y = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ab_glyph")]
|
||||
let inner = glyph.font.outline_glyph(
|
||||
glyph.inner.with_scale_and_position(
|
||||
font_size as f32,
|
||||
ab_glyph::point(
|
||||
#[cfg(feature = "ab_glyph")]
|
||||
let inner = glyph.font.outline_glyph(
|
||||
glyph.inner.with_scale_and_position(
|
||||
font_size as f32,
|
||||
ab_glyph::point(
|
||||
x + x_offset,
|
||||
y + y_offset,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "rusttype")]
|
||||
let inner = glyph.inner.clone()
|
||||
.scaled(rusttype::Scale::uniform(font_size as f32))
|
||||
.positioned(rusttype::point(
|
||||
x + x_offset,
|
||||
y + y_offset,
|
||||
)
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
#[cfg(feature = "rusttype")]
|
||||
let inner = glyph.inner.clone()
|
||||
.scaled(rusttype::Scale::uniform(font_size as f32))
|
||||
.positioned(rusttype::point(
|
||||
x + x_offset,
|
||||
y + y_offset,
|
||||
));
|
||||
glyphs.push(FontLayoutGlyph {
|
||||
start: glyph.start,
|
||||
end: glyph.end,
|
||||
x,
|
||||
w: x_advance,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
});
|
||||
push_line = true;
|
||||
|
||||
glyphs.push(FontLayoutGlyph {
|
||||
start: glyph.start,
|
||||
end: glyph.end,
|
||||
x,
|
||||
w: x_advance,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
});
|
||||
push_line = true;
|
||||
|
||||
x += x_advance;
|
||||
y += y_advance;
|
||||
}
|
||||
|
||||
if self.rtl {
|
||||
x -= span_width;
|
||||
x += x_advance;
|
||||
y += y_advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,4 +106,85 @@ impl<'a> FontShapeLine<'a> {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_rtl(&self, font_size: i32, line_width: i32, layout_lines: &mut Vec<FontLayoutLine<'a>>, mut layout_i: usize) {
|
||||
let mut push_line = true;
|
||||
let mut glyphs = Vec::new();
|
||||
|
||||
let mut x = line_width as f32;
|
||||
let mut y = 0.0;
|
||||
for span in self.spans.iter() {
|
||||
for word in span.words.iter() {
|
||||
for glyph in word.glyphs.iter().rev() {
|
||||
let x_advance = font_size as f32 * glyph.x_advance;
|
||||
let y_advance = font_size as f32 * glyph.y_advance;
|
||||
let x_offset = font_size as f32 * glyph.x_offset;
|
||||
let y_offset = font_size as f32 * glyph.y_offset;
|
||||
|
||||
//TODO: make wrapping optional
|
||||
if x - x_advance < 0.0 {
|
||||
let mut glyphs_swap = Vec::new();
|
||||
std::mem::swap(&mut glyphs, &mut glyphs_swap);
|
||||
layout_lines.insert(layout_i, FontLayoutLine {
|
||||
line_i: self.line_i,
|
||||
glyphs: glyphs_swap
|
||||
});
|
||||
layout_i += 1;
|
||||
|
||||
x = line_width as f32;
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
x -= x_advance;
|
||||
|
||||
#[cfg(feature = "ab_glyph")]
|
||||
let inner = glyph.font.outline_glyph(
|
||||
glyph.inner.with_scale_and_position(
|
||||
font_size as f32,
|
||||
ab_glyph::point(
|
||||
x + x_offset,
|
||||
y + y_offset,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "rusttype")]
|
||||
let inner = glyph.inner.clone()
|
||||
.scaled(rusttype::Scale::uniform(font_size as f32))
|
||||
.positioned(rusttype::point(
|
||||
x + x_offset,
|
||||
y + y_offset,
|
||||
));
|
||||
|
||||
glyphs.push(FontLayoutGlyph {
|
||||
start: glyph.start,
|
||||
end: glyph.end,
|
||||
x,
|
||||
w: x_advance,
|
||||
inner,
|
||||
phantom: PhantomData,
|
||||
});
|
||||
push_line = true;
|
||||
|
||||
y += y_advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if push_line {
|
||||
layout_lines.insert(layout_i, FontLayoutLine {
|
||||
line_i: self.line_i,
|
||||
glyphs
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn layout(&self, font_size: i32, line_width: i32, layout_lines: &mut Vec<FontLayoutLine<'a>>, layout_i: usize) {
|
||||
if self.rtl {
|
||||
self.layout_rtl(font_size, line_width, layout_lines, layout_i);
|
||||
} else {
|
||||
self.layout_ltr(font_size, line_width, layout_lines, layout_i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue