зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #4325 - gfx: Implement `letter-spacing` per CSS 2.1 § 16.4 (from pcwalton:letter-spacing); r=mbrubeck
The ligature disabling code has been manually verified, but I was unable to reftest it. (The only way I could think of would be to create an Ahem-like font with a ligature table, but that would be an awful lot of work.) Near as I can tell, the method used to apply the spacing (manually inserting extra advance post-shaping) matches Gecko. r? @SimonSapin Source-Repo: https://github.com/servo/servo Source-Revision: 914f27263d60ffcbe2fd1f9e47a48e3aa3f1cd76
This commit is contained in:
Родитель
1d5fe80591
Коммит
e289ac1783
|
@ -13,9 +13,10 @@ use style::computed_values::{font_variant, font_weight};
|
|||
use style::style_structs::Font as FontStyle;
|
||||
use sync::Arc;
|
||||
|
||||
use servo_util::geometry::Au;
|
||||
use collections::hash::Hash;
|
||||
use platform::font_context::FontContextHandle;
|
||||
use platform::font::{FontHandle, FontTable};
|
||||
use servo_util::geometry::Au;
|
||||
use text::glyph::{GlyphStore, GlyphId};
|
||||
use text::shaping::ShaperMethods;
|
||||
use text::{Shaper, TextRun};
|
||||
|
@ -95,37 +96,85 @@ pub struct Font {
|
|||
pub requested_pt_size: Au,
|
||||
pub actual_pt_size: Au,
|
||||
pub shaper: Option<Shaper>,
|
||||
pub shape_cache: HashCache<String, Arc<GlyphStore>>,
|
||||
pub glyph_advance_cache: HashCache<u32, FractionalPixel>,
|
||||
pub shape_cache: HashCache<ShapeCacheEntry,Arc<GlyphStore>>,
|
||||
pub glyph_advance_cache: HashCache<u32,FractionalPixel>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
flags ShapingFlags: u8 {
|
||||
#[doc="Set if the text is entirely whitespace."]
|
||||
const IS_WHITESPACE_SHAPING_FLAG = 0x01,
|
||||
#[doc="Set if we are to ignore ligatures."]
|
||||
const IGNORE_LIGATURES_SHAPING_FLAG = 0x02
|
||||
}
|
||||
}
|
||||
|
||||
/// Various options that control text shaping.
|
||||
#[deriving(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct ShapingOptions {
|
||||
/// Spacing to add between each letter. Corresponds to the CSS 2.1 `letter-spacing` property.
|
||||
/// NB: You will probably want to set the `IGNORE_LIGATURES_SHAPING_FLAG` if this is non-null.
|
||||
pub letter_spacing: Option<Au>,
|
||||
/// Various flags.
|
||||
pub flags: ShapingFlags,
|
||||
}
|
||||
|
||||
/// An entry in the shape cache.
|
||||
#[deriving(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct ShapeCacheEntry {
|
||||
text: String,
|
||||
options: ShapingOptions,
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq, PartialEq, Hash)]
|
||||
struct ShapeCacheEntryRef<'a> {
|
||||
text: &'a str,
|
||||
options: &'a ShapingOptions,
|
||||
}
|
||||
|
||||
impl<'a> Equiv<ShapeCacheEntry> for ShapeCacheEntryRef<'a> {
|
||||
fn equiv(&self, other: &ShapeCacheEntry) -> bool {
|
||||
self.text == other.text.as_slice() && *self.options == other.options
|
||||
}
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn shape_text(&mut self, text: &str, is_whitespace: bool) -> Arc<GlyphStore> {
|
||||
self.make_shaper();
|
||||
pub fn shape_text(&mut self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> {
|
||||
self.make_shaper(options);
|
||||
|
||||
let shaper = &self.shaper;
|
||||
match self.shape_cache.find_equiv(text) {
|
||||
let lookup_key = ShapeCacheEntryRef {
|
||||
text: text,
|
||||
options: options,
|
||||
};
|
||||
match self.shape_cache.find_equiv(&lookup_key) {
|
||||
None => {}
|
||||
Some(glyphs) => return (*glyphs).clone(),
|
||||
}
|
||||
|
||||
let mut glyphs = GlyphStore::new(text.char_len() as int, is_whitespace);
|
||||
shaper.as_ref().unwrap().shape_text(text, &mut glyphs);
|
||||
let mut glyphs = GlyphStore::new(text.char_len() as int,
|
||||
options.flags.contains(IS_WHITESPACE_SHAPING_FLAG));
|
||||
shaper.as_ref().unwrap().shape_text(text, options, &mut glyphs);
|
||||
|
||||
let glyphs = Arc::new(glyphs);
|
||||
self.shape_cache.insert(text.to_string(), glyphs.clone());
|
||||
self.shape_cache.insert(ShapeCacheEntry {
|
||||
text: text.to_string(),
|
||||
options: *options,
|
||||
}, glyphs.clone());
|
||||
glyphs
|
||||
}
|
||||
|
||||
fn make_shaper<'a>(&'a mut self) -> &'a Shaper {
|
||||
fn make_shaper<'a>(&'a mut self, options: &ShapingOptions) -> &'a Shaper {
|
||||
// fast path: already created a shaper
|
||||
match self.shaper {
|
||||
Some(ref shaper) => {
|
||||
let s: &'a Shaper = shaper;
|
||||
return s;
|
||||
Some(ref mut shaper) => {
|
||||
shaper.set_options(options);
|
||||
return shaper
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
let shaper = Shaper::new(self);
|
||||
let shaper = Shaper::new(self, options);
|
||||
self.shaper = Some(shaper);
|
||||
self.shaper.as_ref().unwrap()
|
||||
}
|
||||
|
@ -149,7 +198,8 @@ impl Font {
|
|||
self.handle.glyph_index(codepoint)
|
||||
}
|
||||
|
||||
pub fn glyph_h_kerning(&mut self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
|
||||
pub fn glyph_h_kerning(&mut self, first_glyph: GlyphId, second_glyph: GlyphId)
|
||||
-> FractionalPixel {
|
||||
self.handle.glyph_h_kerning(first_glyph, second_glyph)
|
||||
}
|
||||
|
||||
|
@ -175,11 +225,11 @@ impl FontGroup {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_textrun(&self, text: String) -> TextRun {
|
||||
pub fn create_textrun(&self, text: String, options: &ShapingOptions) -> TextRun {
|
||||
assert!(self.fonts.len() > 0);
|
||||
|
||||
// TODO(Issue #177): Actually fall back through the FontGroup when a font is unsuitable.
|
||||
TextRun::new(&mut *self.fonts.get(0).borrow_mut(), text.clone())
|
||||
TextRun::new(&mut *self.fonts.get(0).borrow_mut(), text.clone(), options)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![feature(globs, macro_rules, phase, unsafe_destructor)]
|
||||
#![feature(globs, macro_rules, phase, unsafe_destructor, default_type_params)]
|
||||
|
||||
#![deny(unused_imports)]
|
||||
#![deny(unused_variables)]
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
extern crate harfbuzz;
|
||||
|
||||
use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag};
|
||||
use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag, IGNORE_LIGATURES_SHAPING_FLAG};
|
||||
use font::{ShapingOptions};
|
||||
use platform::font::FontTable;
|
||||
use text::glyph::{CharIndex, GlyphStore, GlyphId, GlyphData};
|
||||
use text::shaping::ShaperMethods;
|
||||
|
@ -18,9 +19,11 @@ use harfbuzz::{hb_bool_t};
|
|||
use harfbuzz::{hb_buffer_add_utf8};
|
||||
use harfbuzz::{hb_buffer_destroy};
|
||||
use harfbuzz::{hb_buffer_get_glyph_positions};
|
||||
use harfbuzz::{hb_buffer_get_length};
|
||||
use harfbuzz::{hb_buffer_set_direction};
|
||||
use harfbuzz::{hb_face_destroy};
|
||||
use harfbuzz::{hb_face_t, hb_font_t};
|
||||
use harfbuzz::{hb_feature_t};
|
||||
use harfbuzz::{hb_font_create};
|
||||
use harfbuzz::{hb_font_destroy, hb_buffer_create};
|
||||
use harfbuzz::{hb_font_funcs_create};
|
||||
|
@ -47,6 +50,9 @@ use std::ptr;
|
|||
static NO_GLYPH: i32 = -1;
|
||||
static CONTINUATION_BYTE: i32 = -2;
|
||||
|
||||
static LIGA: u32 = ((b'l' as u32) << 24) | ((b'i' as u32) << 16) | ((b'g' as u32) << 8) |
|
||||
(b'a' as u32);
|
||||
|
||||
pub struct ShapedGlyphData {
|
||||
count: int,
|
||||
glyph_infos: *mut hb_glyph_info_t,
|
||||
|
@ -131,10 +137,16 @@ impl ShapedGlyphData {
|
|||
}
|
||||
}
|
||||
|
||||
struct FontAndShapingOptions {
|
||||
font: *mut Font,
|
||||
options: ShapingOptions,
|
||||
}
|
||||
|
||||
pub struct Shaper {
|
||||
hb_face: *mut hb_face_t,
|
||||
hb_font: *mut hb_font_t,
|
||||
hb_funcs: *mut hb_font_funcs_t,
|
||||
font_and_shaping_options: Box<FontAndShapingOptions>,
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
|
@ -154,13 +166,18 @@ impl Drop for Shaper {
|
|||
}
|
||||
|
||||
impl Shaper {
|
||||
pub fn new(font: &mut Font) -> Shaper {
|
||||
pub fn new(font: &mut Font, options: &ShapingOptions) -> Shaper {
|
||||
unsafe {
|
||||
// Indirection for Rust Issue #6248, dynamic freeze scope artificially extended
|
||||
let font_ptr = font as *mut Font;
|
||||
let hb_face: *mut hb_face_t = hb_face_create_for_tables(get_font_table_func,
|
||||
font_ptr as *mut c_void,
|
||||
None);
|
||||
let mut font_and_shaping_options = box FontAndShapingOptions {
|
||||
font: font,
|
||||
options: *options,
|
||||
};
|
||||
let hb_face: *mut hb_face_t =
|
||||
hb_face_create_for_tables(get_font_table_func,
|
||||
(&mut *font_and_shaping_options)
|
||||
as *mut FontAndShapingOptions
|
||||
as *mut c_void,
|
||||
None);
|
||||
let hb_font: *mut hb_font_t = hb_font_create(hb_face);
|
||||
|
||||
// Set points-per-em. if zero, performs no hinting in that direction.
|
||||
|
@ -178,16 +195,21 @@ impl Shaper {
|
|||
hb_font_funcs_set_glyph_func(hb_funcs, glyph_func, ptr::null_mut(), None);
|
||||
hb_font_funcs_set_glyph_h_advance_func(hb_funcs, glyph_h_advance_func, ptr::null_mut(), None);
|
||||
hb_font_funcs_set_glyph_h_kerning_func(hb_funcs, glyph_h_kerning_func, ptr::null_mut(), ptr::null_mut());
|
||||
hb_font_set_funcs(hb_font, hb_funcs, font_ptr as *mut c_void, None);
|
||||
hb_font_set_funcs(hb_font, hb_funcs, font as *mut Font as *mut c_void, None);
|
||||
|
||||
Shaper {
|
||||
hb_face: hb_face,
|
||||
hb_font: hb_font,
|
||||
hb_funcs: hb_funcs,
|
||||
font_and_shaping_options: font_and_shaping_options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_options(&mut self, options: &ShapingOptions) {
|
||||
self.font_and_shaping_options.options = *options
|
||||
}
|
||||
|
||||
fn float_to_fixed(f: f64) -> i32 {
|
||||
float_to_fixed(16, f)
|
||||
}
|
||||
|
@ -200,7 +222,7 @@ impl Shaper {
|
|||
impl ShaperMethods for Shaper {
|
||||
/// Calculate the layout metrics associated with the given text when painted in a specific
|
||||
/// font.
|
||||
fn shape_text(&self, text: &str, glyphs: &mut GlyphStore) {
|
||||
fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
|
||||
unsafe {
|
||||
let hb_buffer: *mut hb_buffer_t = hb_buffer_create();
|
||||
hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR);
|
||||
|
@ -211,15 +233,29 @@ impl ShaperMethods for Shaper {
|
|||
0,
|
||||
text.len() as c_int);
|
||||
|
||||
hb_shape(self.hb_font, hb_buffer, ptr::null_mut(), 0);
|
||||
self.save_glyph_results(text, glyphs, hb_buffer);
|
||||
let mut features = Vec::new();
|
||||
if options.flags.contains(IGNORE_LIGATURES_SHAPING_FLAG) {
|
||||
features.push(hb_feature_t {
|
||||
_tag: LIGA,
|
||||
_value: 0,
|
||||
_start: 0,
|
||||
_end: hb_buffer_get_length(hb_buffer),
|
||||
})
|
||||
}
|
||||
|
||||
hb_shape(self.hb_font, hb_buffer, features.as_mut_ptr(), features.len() as u32);
|
||||
self.save_glyph_results(text, options, glyphs, hb_buffer);
|
||||
hb_buffer_destroy(hb_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shaper {
|
||||
fn save_glyph_results(&self, text: &str, glyphs: &mut GlyphStore, buffer: *mut hb_buffer_t) {
|
||||
fn save_glyph_results(&self,
|
||||
text: &str,
|
||||
options: &ShapingOptions,
|
||||
glyphs: &mut GlyphStore,
|
||||
buffer: *mut hb_buffer_t) {
|
||||
let glyph_data = ShapedGlyphData::new(buffer);
|
||||
let glyph_count = glyph_data.len();
|
||||
let byte_max = text.len() as int;
|
||||
|
@ -401,8 +437,9 @@ impl Shaper {
|
|||
// (i.e., pretend there are no combining character sequences).
|
||||
// 1-to-1 mapping of character to glyph also treated as ligature start.
|
||||
let shape = glyph_data.get_entry_for_glyph(glyph_span.begin(), &mut y_pos);
|
||||
let advance = self.advance_for_shaped_glyph(shape.advance, options);
|
||||
let data = GlyphData::new(shape.codepoint,
|
||||
shape.advance,
|
||||
advance,
|
||||
shape.offset,
|
||||
false,
|
||||
true,
|
||||
|
@ -450,6 +487,13 @@ impl Shaper {
|
|||
// lookup table for finding detailed glyphs by associated char index.
|
||||
glyphs.finalize_changes();
|
||||
}
|
||||
|
||||
fn advance_for_shaped_glyph(&self, advance: Au, options: &ShapingOptions) -> Au {
|
||||
match options.letter_spacing {
|
||||
None => advance,
|
||||
Some(spacing) => advance + spacing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Callbacks from Harfbuzz when font map and glyph advance lookup needed.
|
||||
|
@ -504,13 +548,19 @@ extern fn glyph_h_kerning_func(_: *mut hb_font_t,
|
|||
}
|
||||
|
||||
// Callback to get a font table out of a font.
|
||||
extern fn get_font_table_func(_: *mut hb_face_t, tag: hb_tag_t, user_data: *mut c_void) -> *mut hb_blob_t {
|
||||
extern fn get_font_table_func(_: *mut hb_face_t,
|
||||
tag: hb_tag_t,
|
||||
user_data: *mut c_void)
|
||||
-> *mut hb_blob_t {
|
||||
unsafe {
|
||||
let font: *const Font = user_data as *const Font;
|
||||
assert!(font.is_not_null());
|
||||
// NB: These asserts have security implications.
|
||||
let font_and_shaping_options: *const FontAndShapingOptions =
|
||||
user_data as *const FontAndShapingOptions;
|
||||
assert!(font_and_shaping_options.is_not_null());
|
||||
assert!((*font_and_shaping_options).font.is_not_null());
|
||||
|
||||
// TODO(Issue #197): reuse font table data, which will change the unsound trickery here.
|
||||
match (*font).get_table_for_tag(tag as FontTableTag) {
|
||||
match (*(*font_and_shaping_options).font).get_table_for_tag(tag as FontTableTag) {
|
||||
None => ptr::null_mut(),
|
||||
Some(ref font_table) => {
|
||||
let skinny_font_table_ptr: *const FontTable = font_table; // private context
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//!
|
||||
//! Currently, only harfbuzz bindings are implemented.
|
||||
|
||||
use font::ShapingOptions;
|
||||
use text::glyph::GlyphStore;
|
||||
|
||||
pub use text::shaping::harfbuzz::Shaper;
|
||||
|
@ -14,6 +15,6 @@ pub use text::shaping::harfbuzz::Shaper;
|
|||
pub mod harfbuzz;
|
||||
|
||||
pub trait ShaperMethods {
|
||||
fn shape_text(&self, text: &str, glyphs: &mut GlyphStore);
|
||||
fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use font::{Font, RunMetrics, FontMetrics};
|
||||
use font::{Font, FontHandleMethods, FontMetrics, IS_WHITESPACE_SHAPING_FLAG, RunMetrics};
|
||||
use font::{ShapingOptions};
|
||||
use platform::font_template::FontTemplateData;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::range::Range;
|
||||
use servo_util::vec::{Comparator, FullBinarySearchMethods};
|
||||
use std::slice::Items;
|
||||
use sync::Arc;
|
||||
use text::glyph::{CharIndex, GlyphStore};
|
||||
use font::FontHandleMethods;
|
||||
use platform::font_template::FontTemplateData;
|
||||
|
||||
/// A single "paragraph" of text in one font size and style.
|
||||
#[deriving(Clone)]
|
||||
|
@ -117,8 +117,8 @@ impl<'a> Iterator<Range<CharIndex>> for LineIterator<'a> {
|
|||
}
|
||||
|
||||
impl<'a> TextRun {
|
||||
pub fn new(font: &mut Font, text: String) -> TextRun {
|
||||
let glyphs = TextRun::break_and_shape(font, text.as_slice());
|
||||
pub fn new(font: &mut Font, text: String, options: &ShapingOptions) -> TextRun {
|
||||
let glyphs = TextRun::break_and_shape(font, text.as_slice(), options);
|
||||
let run = TextRun {
|
||||
text: Arc::new(text),
|
||||
font_metrics: font.metrics.clone(),
|
||||
|
@ -129,7 +129,8 @@ impl<'a> TextRun {
|
|||
return run;
|
||||
}
|
||||
|
||||
pub fn break_and_shape(font: &mut Font, text: &str) -> Vec<GlyphRun> {
|
||||
pub fn break_and_shape(font: &mut Font, text: &str, options: &ShapingOptions)
|
||||
-> Vec<GlyphRun> {
|
||||
// TODO(Issue #230): do a better job. See Gecko's LineBreaker.
|
||||
let mut glyphs = vec!();
|
||||
let (mut byte_i, mut char_i) = (0u, CharIndex(0));
|
||||
|
@ -165,8 +166,14 @@ impl<'a> TextRun {
|
|||
let slice = text.slice(byte_last_boundary, byte_i);
|
||||
debug!("creating glyph store for slice {} (ws? {}), {} - {} in run {}",
|
||||
slice, !cur_slice_is_whitespace, byte_last_boundary, byte_i, text);
|
||||
|
||||
let mut options = *options;
|
||||
if !cur_slice_is_whitespace {
|
||||
options.flags.insert(IS_WHITESPACE_SHAPING_FLAG);
|
||||
}
|
||||
|
||||
glyphs.push(GlyphRun {
|
||||
glyph_store: font.shape_text(slice, !cur_slice_is_whitespace),
|
||||
glyph_store: font.shape_text(slice, &options),
|
||||
range: Range::new(char_last_boundary, char_i - char_last_boundary),
|
||||
});
|
||||
byte_last_boundary = byte_i;
|
||||
|
@ -182,8 +189,14 @@ impl<'a> TextRun {
|
|||
let slice = text.slice_from(byte_last_boundary);
|
||||
debug!("creating glyph store for final slice {} (ws? {}), {} - {} in run {}",
|
||||
slice, cur_slice_is_whitespace, byte_last_boundary, text.len(), text);
|
||||
|
||||
let mut options = *options;
|
||||
if cur_slice_is_whitespace {
|
||||
options.flags.insert(IS_WHITESPACE_SHAPING_FLAG);
|
||||
}
|
||||
|
||||
glyphs.push(GlyphRun {
|
||||
glyph_store: font.shape_text(slice, cur_slice_is_whitespace),
|
||||
glyph_store: font.shape_text(slice, &options),
|
||||
range: Range::new(char_last_boundary, char_i - char_last_boundary),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
use fragment::{Fragment, ScannedTextFragmentInfo, UnscannedTextFragment};
|
||||
use inline::InlineFragments;
|
||||
|
||||
use gfx::font::{FontMetrics,RunMetrics};
|
||||
use gfx::font::{FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG, RunMetrics, ShapingFlags};
|
||||
use gfx::font::{ShapingOptions};
|
||||
use gfx::font_context::FontContext;
|
||||
use gfx::text::glyph::CharIndex;
|
||||
use gfx::text::text_run::TextRun;
|
||||
|
@ -105,6 +106,7 @@ impl TextRunScanner {
|
|||
let fontgroup;
|
||||
let compression;
|
||||
let text_transform;
|
||||
let letter_spacing;
|
||||
{
|
||||
let in_fragment = self.clump.front().unwrap();
|
||||
let font_style = in_fragment.style().get_font_arc();
|
||||
|
@ -114,6 +116,7 @@ impl TextRunScanner {
|
|||
white_space::pre => CompressNone,
|
||||
};
|
||||
text_transform = in_fragment.style().get_inheritedtext().text_transform;
|
||||
letter_spacing = in_fragment.style().get_inheritedtext().letter_spacing;
|
||||
}
|
||||
|
||||
// First, transform/compress text of all the nodes.
|
||||
|
@ -150,7 +153,22 @@ impl TextRunScanner {
|
|||
self.clump = DList::new();
|
||||
return last_whitespace
|
||||
}
|
||||
Arc::new(box TextRun::new(&mut *fontgroup.fonts.get(0).borrow_mut(), run_text))
|
||||
|
||||
// Per CSS 2.1 § 16.4, "when the resultant space between two characters is not the same
|
||||
// as the default space, user agents should not use ligatures." This ensures that, for
|
||||
// example, `finally` with a wide `letter-spacing` renders as `f i n a l l y` and not
|
||||
// `fi n a l l y`.
|
||||
let options = ShapingOptions {
|
||||
letter_spacing: letter_spacing,
|
||||
flags: match letter_spacing {
|
||||
Some(Au(0)) | None => ShapingFlags::empty(),
|
||||
Some(_) => IGNORE_LIGATURES_SHAPING_FLAG,
|
||||
},
|
||||
};
|
||||
|
||||
Arc::new(box TextRun::new(&mut *fontgroup.fonts.get(0).borrow_mut(),
|
||||
run_text,
|
||||
&options))
|
||||
};
|
||||
|
||||
// Make new fragments with the run and adjusted text indices.
|
||||
|
|
|
@ -1088,6 +1088,29 @@ pub mod longhands {
|
|||
// TODO: initial value should be 'start' (CSS Text Level 3, direction-dependent.)
|
||||
${single_keyword("text-align", "left right center justify")}
|
||||
|
||||
<%self:single_component_value name="letter-spacing">
|
||||
pub type SpecifiedValue = Option<specified::Length>;
|
||||
pub mod computed_value {
|
||||
use super::super::Au;
|
||||
pub type T = Option<Au>;
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
None
|
||||
}
|
||||
#[inline]
|
||||
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
|
||||
-> computed_value::T {
|
||||
value.map(|length| computed::compute_Au(length, context))
|
||||
}
|
||||
pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> {
|
||||
match input {
|
||||
&Ident(ref value) if value.eq_ignore_ascii_case("normal") => Ok(None),
|
||||
_ => specified::Length::parse_non_negative(input).map(|length| Some(length)),
|
||||
}
|
||||
}
|
||||
</%self:single_component_value>
|
||||
|
||||
${new_style_struct("Text", is_inherited=False)}
|
||||
|
||||
<%self:longhand name="text-decoration">
|
||||
|
|
|
@ -64,7 +64,7 @@ pub enum PagePx {}
|
|||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info.
|
||||
//
|
||||
// FIXME: Implement Au using Length and ScaleFactor instead of a custom type.
|
||||
#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Zero)]
|
||||
#[deriving(Clone, Hash, PartialEq, PartialOrd, Eq, Ord, Zero)]
|
||||
pub struct Au(pub i32);
|
||||
|
||||
impl Default for Au {
|
||||
|
|
Загрузка…
Ссылка в новой задаче