From 3a3c4b8c8e44e7b630b26e6a81eea201f3c39223 Mon Sep 17 00:00:00 2001 From: kingdido999 Date: Sat, 8 Sep 2018 08:05:42 +0800 Subject: [PATCH] Format the rest of gfx #21373 --- components/gfx/font.rs | 198 ++++++++++-------- components/gfx/font_cache_thread.rs | 276 ++++++++++++++++---------- components/gfx/font_context.rs | 64 +++--- components/gfx/font_template.rs | 44 ++-- components/gfx/lib.rs | 41 ++-- components/gfx/tests/font_context.rs | 101 ++++++---- components/gfx/tests/font_template.rs | 67 ++++--- components/gfx/tests/text_util.rs | 112 +++-------- 8 files changed, 502 insertions(+), 401 deletions(-) diff --git a/components/gfx/font.rs b/components/gfx/font.rs index de056747bd9..a3591e3b41a 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -32,9 +32,9 @@ use unicode_script::Script; use webrender_api; macro_rules! ot_tag { - ($t1:expr, $t2:expr, $t3:expr, $t4:expr) => ( + ($t1:expr, $t2:expr, $t3:expr, $t4:expr) => { (($t1 as u32) << 24) | (($t2 as u32) << 16) | (($t3 as u32) << 8) | ($t4 as u32) - ); + }; } pub const GPOS: u32 = ot_tag!('G', 'P', 'O', 'S'); @@ -87,10 +87,12 @@ trait FontTableTagConversions { impl FontTableTagConversions for FontTableTag { fn tag_to_str(&self) -> String { - let bytes = [(self >> 24) as u8, - (self >> 16) as u8, - (self >> 8) as u8, - (self >> 0) as u8]; + let bytes = [ + (self >> 24) as u8, + (self >> 16) as u8, + (self >> 8) as u8, + (self >> 0) as u8, + ]; str::from_utf8(&bytes).unwrap().to_owned() } } @@ -101,18 +103,18 @@ pub trait FontTableMethods { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FontMetrics { - pub underline_size: Au, + pub underline_size: Au, pub underline_offset: Au, - pub strikeout_size: Au, + pub strikeout_size: Au, pub strikeout_offset: Au, - pub leading: Au, - pub x_height: Au, - pub em_size: Au, - pub ascent: Au, - pub descent: Au, - pub max_advance: Au, - pub average_advance: Au, - pub line_gap: Au, + pub leading: Au, + pub x_height: Au, + pub em_size: Au, + pub ascent: Au, + pub descent: Au, + pub max_advance: Au, + pub average_advance: Au, + pub line_gap: Au, } /// `FontDescriptor` describes the parameters of a `Font`. It represents rendering a given font @@ -149,10 +151,12 @@ pub struct Font { } impl Font { - pub fn new(handle: FontHandle, - descriptor: FontDescriptor, - actual_pt_size: Au, - font_key: webrender_api::FontInstanceKey) -> Font { + pub fn new( + handle: FontHandle, + descriptor: FontDescriptor, + actual_pt_size: Au, + font_key: webrender_api::FontInstanceKey, + ) -> Font { let metrics = handle.metrics(); Font { @@ -218,28 +222,39 @@ impl Font { text: text.to_owned(), options: *options, }; - let result = self.shape_cache.borrow_mut().entry(lookup_key).or_insert_with(|| { - let start_time = time::precise_time_ns(); - let mut glyphs = GlyphStore::new(text.len(), - options.flags.contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG), - options.flags.contains(ShapingFlags::RTL_FLAG)); + let result = self + .shape_cache + .borrow_mut() + .entry(lookup_key) + .or_insert_with(|| { + let start_time = time::precise_time_ns(); + let mut glyphs = GlyphStore::new( + text.len(), + options + .flags + .contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG), + options.flags.contains(ShapingFlags::RTL_FLAG), + ); - if self.can_do_fast_shaping(text, options) { - debug!("shape_text: Using ASCII fast path."); - self.shape_text_fast(text, options, &mut glyphs); - } else { - debug!("shape_text: Using Harfbuzz."); - if shaper.is_none() { - shaper = Some(Shaper::new(this)); + if self.can_do_fast_shaping(text, options) { + debug!("shape_text: Using ASCII fast path."); + self.shape_text_fast(text, options, &mut glyphs); + } else { + debug!("shape_text: Using Harfbuzz."); + if shaper.is_none() { + shaper = Some(Shaper::new(this)); + } + shaper + .as_ref() + .unwrap() + .shape_text(text, options, &mut glyphs); } - shaper.as_ref().unwrap().shape_text(text, options, &mut glyphs); - } - let end_time = time::precise_time_ns(); - TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add((end_time - start_time) as usize, - Ordering::Relaxed); - Arc::new(glyphs) - }).clone(); + let end_time = time::precise_time_ns(); + TEXT_SHAPING_PERFORMANCE_COUNTER + .fetch_add((end_time - start_time) as usize, Ordering::Relaxed); + Arc::new(glyphs) + }).clone(); self.shaper = shaper; result } @@ -285,12 +300,21 @@ impl Font { pub fn table_for_tag(&self, tag: FontTableTag) -> Option { let result = self.handle.table_for_tag(tag); - let status = if result.is_some() { "Found" } else { "Didn't find" }; + let status = if result.is_some() { + "Found" + } else { + "Didn't find" + }; - debug!("{} font table[{}] with family={}, face={}", - status, tag.tag_to_str(), - self.handle.family_name().unwrap_or("unavailable".to_owned()), - self.handle.face_name().unwrap_or("unavailable".to_owned())); + debug!( + "{} font table[{}] with family={}, face={}", + status, + tag.tag_to_str(), + self.handle + .family_name() + .unwrap_or("unavailable".to_owned()), + self.handle.face_name().unwrap_or("unavailable".to_owned()) + ); result } @@ -308,18 +332,21 @@ impl Font { self.glyph_index(codepoint).is_some() } - pub fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) - -> FractionalPixel { + pub fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel { self.handle.glyph_h_kerning(first_glyph, second_glyph) } pub fn glyph_h_advance(&self, glyph: GlyphId) -> FractionalPixel { - *self.glyph_advance_cache.borrow_mut().entry(glyph).or_insert_with(|| { - match self.handle.glyph_h_advance(glyph) { - Some(adv) => adv, - None => 10f64 as FractionalPixel // FIXME: Need fallback strategy - } - }) + *self + .glyph_advance_cache + .borrow_mut() + .entry(glyph) + .or_insert_with(|| { + match self.handle.glyph_h_advance(glyph) { + Some(adv) => adv, + None => 10f64 as FractionalPixel, // FIXME: Need fallback strategy + } + }) } } @@ -339,10 +366,12 @@ impl FontGroup { pub fn new(style: &FontStyleStruct) -> FontGroup { let descriptor = FontDescriptor::from(style); - let families = - style.font_family.0.iter() - .map(|family| FontGroupFamily::new(descriptor.clone(), &family)) - .collect(); + let families = style + .font_family + .0 + .iter() + .map(|family| FontGroupFamily::new(descriptor.clone(), &family)) + .collect(); FontGroup { descriptor, @@ -358,25 +387,25 @@ impl FontGroup { pub fn find_by_codepoint( &mut self, mut font_context: &mut FontContext, - codepoint: char + codepoint: char, ) -> Option { let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint); let font = self.find(&mut font_context, |font| has_glyph(font)); if font.is_some() { - return font + return font; } if let Some(ref fallback) = self.last_matching_fallback { if has_glyph(&fallback) { - return self.last_matching_fallback.clone() + return self.last_matching_fallback.clone(); } } let font = self.find_fallback(&mut font_context, Some(codepoint), has_glyph); if font.is_some() { self.last_matching_fallback = font.clone(); - return font + return font; } self.first(&mut font_context) @@ -385,7 +414,7 @@ impl FontGroup { /// Find the first available font in the group, or the first available fallback font. pub fn first( &mut self, - mut font_context: &mut FontContext + mut font_context: &mut FontContext, ) -> Option { self.find(&mut font_context, |_| true) .or_else(|| self.find_fallback(&mut font_context, None, |_| true)) @@ -393,16 +422,13 @@ impl FontGroup { /// Find a font which returns true for `predicate`. This method mutates because we may need to /// load new font data in the process of finding a suitable font. - fn find( - &mut self, - mut font_context: &mut FontContext, - predicate: P, - ) -> Option + fn find(&mut self, mut font_context: &mut FontContext, predicate: P) -> Option where S: FontSource, P: FnMut(&FontRef) -> bool, { - self.families.iter_mut() + self.families + .iter_mut() .filter_map(|family| family.font(&mut font_context)) .find(predicate) } @@ -422,15 +448,9 @@ impl FontGroup { P: FnMut(&FontRef) -> bool, { iter::once(FontFamilyDescriptor::default()) - .chain( - fallback_font_families(codepoint).into_iter().map(|family| { - FontFamilyDescriptor::new( - FontFamilyName::from(family), - FontSearchScope::Local, - ) - }) - ) - .filter_map(|family| font_context.font(&self.descriptor, &family)) + .chain(fallback_font_families(codepoint).into_iter().map(|family| { + FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Local) + })).filter_map(|family| font_context.font(&self.descriptor, &family)) .find(predicate) } } @@ -448,10 +468,8 @@ struct FontGroupFamily { impl FontGroupFamily { fn new(font_descriptor: FontDescriptor, family: &SingleFontFamily) -> FontGroupFamily { - let family_descriptor = FontFamilyDescriptor::new( - FontFamilyName::from(family), - FontSearchScope::Any - ); + let family_descriptor = + FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Any); FontGroupFamily { font_descriptor, @@ -477,17 +495,19 @@ impl FontGroupFamily { pub struct RunMetrics { // may be negative due to negative width (i.e., kerning of '.' in 'P.T.') pub advance_width: Au, - pub ascent: Au, // nonzero + pub ascent: Au, // nonzero pub descent: Au, // nonzero // this bounding box is relative to the left origin baseline. // so, bounding_box.position.y = -ascent - pub bounding_box: Rect + pub bounding_box: Rect, } impl RunMetrics { pub fn new(advance: Au, ascent: Au, descent: Au) -> RunMetrics { - let bounds = Rect::new(Point2D::new(Au(0), -ascent), - Size2D::new(advance, ascent + descent)); + let bounds = Rect::new( + Point2D::new(Au(0), -ascent), + Size2D::new(advance, ascent + descent), + ); // TODO(Issue #125): support loose and tight bounding boxes; using the // ascent+descent and advance is sometimes too generous and @@ -540,11 +560,13 @@ impl FontFamilyName { impl<'a> From<&'a SingleFontFamily> for FontFamilyName { fn from(other: &'a SingleFontFamily) -> FontFamilyName { match *other { - SingleFontFamily::FamilyName(ref family_name) => - FontFamilyName::Specific(family_name.name.clone()), + SingleFontFamily::FamilyName(ref family_name) => { + FontFamilyName::Specific(family_name.name.clone()) + }, - SingleFontFamily::Generic(ref generic_name) => - FontFamilyName::Generic(generic_name.clone()), + SingleFontFamily::Generic(ref generic_name) => { + FontFamilyName::Generic(generic_name.clone()) + }, } } } diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 334a1bb41ec..4bfa3e6dfc2 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -40,14 +40,15 @@ pub struct FontTemplateInfo { impl FontTemplates { pub fn new() -> FontTemplates { - FontTemplates { - templates: vec!(), - } + FontTemplates { templates: vec![] } } /// Find a font in this family that matches a given descriptor. - pub fn find_font_for_style(&mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle) - -> Option> { + pub fn find_font_for_style( + &mut self, + desc: &FontTemplateDescriptor, + fctx: &FontContextHandle, + ) -> Option> { // TODO(Issue #189): optimize lookup for // regular/bold/italic/bolditalic with fixed offsets and a // static decision table for fallback between these values. @@ -63,7 +64,8 @@ impl FontTemplates { let (mut best_template_data, mut best_distance) = (None, f32::MAX); for template in &mut self.templates { if let Some((template_data, distance)) = - template.data_for_approximate_descriptor(fctx, desc) { + template.data_for_approximate_descriptor(fctx, desc) + { if distance < best_distance { best_template_data = Some(template_data); best_distance = distance @@ -71,7 +73,7 @@ impl FontTemplates { } } if best_template_data.is_some() { - return best_template_data + return best_template_data; } // If a request is made for a font family that exists, @@ -103,8 +105,16 @@ impl FontTemplates { /// Commands that the FontContext sends to the font cache thread. #[derive(Debug, Deserialize, Serialize)] pub enum Command { - GetFontTemplate(FontTemplateDescriptor, FontFamilyDescriptor, IpcSender), - GetFontInstance(webrender_api::FontKey, Au, IpcSender), + GetFontTemplate( + FontTemplateDescriptor, + FontFamilyDescriptor, + IpcSender, + ), + GetFontInstance( + webrender_api::FontKey, + Au, + IpcSender, + ), AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>), AddDownloadedWebFont(LowercaseString, ServoUrl, Vec, IpcSender<()>), Exit(IpcSender<()>), @@ -148,7 +158,7 @@ fn populate_generic_fonts() -> HashMap { ) { let family_name = match system_default_family(generic_name) { Some(system_default) => LowercaseString::new(&system_default), - None => LowercaseString::new(mapped_name) + None => LowercaseString::new(mapped_name), }; let generic_name = FontFamilyName::Generic(Atom::from(generic_name)); @@ -156,7 +166,6 @@ fn populate_generic_fonts() -> HashMap { generic_fonts.insert(generic_name, family_name); } - generic_fonts } @@ -167,50 +176,50 @@ impl FontCache { match msg { Command::GetFontTemplate(template_descriptor, family_descriptor, result) => { - let maybe_font_template = self.find_font_template(&template_descriptor, &family_descriptor); + let maybe_font_template = + self.find_font_template(&template_descriptor, &family_descriptor); let _ = result.send(Reply::GetFontTemplateReply(maybe_font_template)); - } + }, Command::GetFontInstance(font_key, size, result) => { let webrender_api = &self.webrender_api; - let instance_key = *self.font_instances - .entry((font_key, size)) - .or_insert_with(|| { - let key = webrender_api.generate_font_instance_key(); - let mut txn = webrender_api::Transaction::new(); - txn.add_font_instance(key, - font_key, - size, - None, - None, - Vec::new()); - webrender_api.update_resources(txn.resource_updates); - key - }); + let instance_key = + *self + .font_instances + .entry((font_key, size)) + .or_insert_with(|| { + let key = webrender_api.generate_font_instance_key(); + let mut txn = webrender_api::Transaction::new(); + txn.add_font_instance(key, font_key, size, None, None, Vec::new()); + webrender_api.update_resources(txn.resource_updates); + key + }); let _ = result.send(instance_key); - } + }, Command::AddWebFont(family_name, sources, result) => { self.handle_add_web_font(family_name, sources, result); - } + }, Command::AddDownloadedWebFont(family_name, url, bytes, result) => { let templates = &mut self.web_families.get_mut(&family_name).unwrap(); templates.add_template(Atom::from(url.to_string()), Some(bytes)); drop(result.send(())); - } + }, Command::Ping => (), Command::Exit(result) => { let _ = result.send(()); break; - } + }, } } } - fn handle_add_web_font(&mut self, - family_name: LowercaseString, - mut sources: EffectiveSources, - sender: IpcSender<()>) { + fn handle_add_web_font( + &mut self, + family_name: LowercaseString, + mut sources: EffectiveSources, + sender: IpcSender<()>, + ) { let src = if let Some(src) = sources.next() { src } else { @@ -236,7 +245,7 @@ impl FontCache { destination: Destination::Font, // TODO: Add a proper origin - Can't import GlobalScope from gfx // We can leave origin to be set by default - .. RequestInit::default() + ..RequestInit::default() }; let channel_to_self = self.channel_to_self.clone(); @@ -248,19 +257,27 @@ impl FontCache { FetchResponseMsg::ProcessRequestBody | FetchResponseMsg::ProcessRequestEOF => (), FetchResponseMsg::ProcessResponse(meta_result) => { - trace!("@font-face {} metadata ok={:?}", family_name, meta_result.is_ok()); + trace!( + "@font-face {} metadata ok={:?}", + family_name, + meta_result.is_ok() + ); *response_valid.lock().unwrap() = meta_result.is_ok(); - } + }, FetchResponseMsg::ProcessResponseChunk(new_bytes) => { trace!("@font-face {} chunk={:?}", family_name, new_bytes); if *response_valid.lock().unwrap() { bytes.lock().unwrap().extend(new_bytes.into_iter()) } - } + }, FetchResponseMsg::ProcessResponseEOF(response) => { trace!("@font-face {} EOF={:?}", family_name, response); if response.is_err() || !*response_valid.lock().unwrap() { - let msg = Command::AddWebFont(family_name.clone(), sources.clone(), sender.clone()); + let msg = Command::AddWebFont( + family_name.clone(), + sources.clone(), + sender.clone(), + ); channel_to_self.send(msg).unwrap(); return; } @@ -270,23 +287,31 @@ impl FontCache { Ok(san) => san, Err(_) => { // FIXME(servo/fontsan#1): get an error message - debug!("Sanitiser rejected web font: \ - family={} url={:?}", family_name, url); - let msg = Command::AddWebFont(family_name.clone(), sources.clone(), sender.clone()); + debug!( + "Sanitiser rejected web font: \ + family={} url={:?}", + family_name, url + ); + let msg = Command::AddWebFont( + family_name.clone(), + sources.clone(), + sender.clone(), + ); channel_to_self.send(msg).unwrap(); return; }, }; - let command = - Command::AddDownloadedWebFont(family_name.clone(), - url.clone(), - bytes, - sender.clone()); + let command = Command::AddDownloadedWebFont( + family_name.clone(), + url.clone(), + bytes, + sender.clone(), + ); channel_to_self.send(command).unwrap(); - } + }, } }); - } + }, Source::Local(ref font) => { let font_face_name = LowercaseString::new(&font.name); let templates = &mut self.web_families.get_mut(&family_name).unwrap(); @@ -301,7 +326,7 @@ impl FontCache { let msg = Command::AddWebFont(family_name, sources, sender); self.channel_to_self.send(msg).unwrap(); } - } + }, } } @@ -319,7 +344,7 @@ impl FontCache { fn transform_family(&self, family_name: &FontFamilyName) -> LowercaseString { match self.generic_fonts.get(family_name) { None => LowercaseString::from(family_name), - Some(mapped_family) => (*mapped_family).clone() + Some(mapped_family) => (*mapped_family).clone(), } } @@ -347,7 +372,10 @@ impl FontCache { s.find_font_for_style(template_descriptor, &self.font_context) } else { - debug!("FontList: Couldn't find font family with name={}", &*family_name); + debug!( + "FontList: Couldn't find font family with name={}", + &*family_name + ); None } } @@ -371,17 +399,19 @@ impl FontCache { let webrender_api = &self.webrender_api; let webrender_fonts = &mut self.webrender_fonts; - let font_key = *webrender_fonts.entry(template.identifier.clone()).or_insert_with(|| { - let font_key = webrender_api.generate_font_key(); - let mut txn = webrender_api::Transaction::new(); - match (template.bytes_if_in_memory(), template.native_font()) { - (Some(bytes), _) => txn.add_raw_font(font_key, bytes, 0), - (None, Some(native_font)) => txn.add_native_font(font_key, native_font), - (None, None) => txn.add_raw_font(font_key, template.bytes().clone(), 0), - } - webrender_api.update_resources(txn.resource_updates); - font_key - }); + let font_key = *webrender_fonts + .entry(template.identifier.clone()) + .or_insert_with(|| { + let font_key = webrender_api.generate_font_key(); + let mut txn = webrender_api::Transaction::new(); + match (template.bytes_if_in_memory(), template.native_font()) { + (Some(bytes), _) => txn.add_raw_font(font_key, bytes, 0), + (None, Some(native_font)) => txn.add_native_font(font_key, native_font), + (None, None) => txn.add_raw_font(font_key, template.bytes().clone(), 0), + } + webrender_api.update_resources(txn.resource_updates); + font_key + }); FontTemplateInfo { font_template: template, @@ -395,14 +425,15 @@ impl FontCache { family_descriptor: &FontFamilyDescriptor, ) -> Option { match family_descriptor.scope { - FontSearchScope::Any => { - self.find_font_in_web_family(&template_descriptor, &family_descriptor.name) - .or_else(|| self.find_font_in_local_family(&template_descriptor, &family_descriptor.name)) - } + FontSearchScope::Any => self + .find_font_in_web_family(&template_descriptor, &family_descriptor.name) + .or_else(|| { + self.find_font_in_local_family(&template_descriptor, &family_descriptor.name) + }), FontSearchScope::Local => { self.find_font_in_local_family(&template_descriptor, &family_descriptor.name) - } + }, }.map(|t| self.get_font_template_info(t)) } } @@ -415,59 +446,82 @@ pub struct FontCacheThread { } impl FontCacheThread { - pub fn new(core_resource_thread: CoreResourceThread, - webrender_api: webrender_api::RenderApi) -> FontCacheThread { + pub fn new( + core_resource_thread: CoreResourceThread, + webrender_api: webrender_api::RenderApi, + ) -> FontCacheThread { let (chan, port) = ipc::channel().unwrap(); let channel_to_self = chan.clone(); - thread::Builder::new().name("FontCacheThread".to_owned()).spawn(move || { - // TODO: Allow users to specify these. - let generic_fonts = populate_generic_fonts(); + thread::Builder::new() + .name("FontCacheThread".to_owned()) + .spawn(move || { + // TODO: Allow users to specify these. + let generic_fonts = populate_generic_fonts(); - let mut cache = FontCache { - port: port, - channel_to_self, - generic_fonts, - local_families: HashMap::new(), - web_families: HashMap::new(), - font_context: FontContextHandle::new(), - core_resource_thread, - webrender_api, - webrender_fonts: HashMap::new(), - font_instances: HashMap::new(), - }; + let mut cache = FontCache { + port: port, + channel_to_self, + generic_fonts, + local_families: HashMap::new(), + web_families: HashMap::new(), + font_context: FontContextHandle::new(), + core_resource_thread, + webrender_api, + webrender_fonts: HashMap::new(), + font_instances: HashMap::new(), + }; - cache.refresh_local_families(); - cache.run(); - }).expect("Thread spawning failed"); + cache.refresh_local_families(); + cache.run(); + }).expect("Thread spawning failed"); - FontCacheThread { - chan: chan, - } + FontCacheThread { chan: chan } } - pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) { - self.chan.send(Command::AddWebFont(LowercaseString::new(&family.name), sources, sender)).unwrap(); + pub fn add_web_font( + &self, + family: FamilyName, + sources: EffectiveSources, + sender: IpcSender<()>, + ) { + self.chan + .send(Command::AddWebFont( + LowercaseString::new(&family.name), + sources, + sender, + )).unwrap(); } pub fn exit(&self) { let (response_chan, response_port) = ipc::channel().unwrap(); - self.chan.send(Command::Exit(response_chan)).expect("Couldn't send FontCacheThread exit message"); - response_port.recv().expect("Couldn't receive FontCacheThread reply"); + self.chan + .send(Command::Exit(response_chan)) + .expect("Couldn't send FontCacheThread exit message"); + response_port + .recv() + .expect("Couldn't receive FontCacheThread reply"); } } impl FontSource for FontCacheThread { - fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey { - let (response_chan, response_port) = - ipc::channel().expect("failed to create IPC channel"); - self.chan.send(Command::GetFontInstance(key, size, response_chan)) + fn get_font_instance( + &mut self, + key: webrender_api::FontKey, + size: Au, + ) -> webrender_api::FontInstanceKey { + let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); + self.chan + .send(Command::GetFontInstance(key, size, response_chan)) .expect("failed to send message to font cache thread"); let instance_key = response_port.recv(); if instance_key.is_err() { let font_thread_has_closed = self.chan.send(Command::Ping).is_err(); - assert!(font_thread_has_closed, "Failed to receive a response from live font cache"); + assert!( + font_thread_has_closed, + "Failed to receive a response from live font cache" + ); panic!("Font cache thread has already exited."); } instance_key.unwrap() @@ -478,23 +532,27 @@ impl FontSource for FontCacheThread { template_descriptor: FontTemplateDescriptor, family_descriptor: FontFamilyDescriptor, ) -> Option { - let (response_chan, response_port) = - ipc::channel().expect("failed to create IPC channel"); - self.chan.send(Command::GetFontTemplate(template_descriptor, family_descriptor, response_chan)) - .expect("failed to send message to font cache thread"); + let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); + self.chan + .send(Command::GetFontTemplate( + template_descriptor, + family_descriptor, + response_chan, + )).expect("failed to send message to font cache thread"); let reply = response_port.recv(); if reply.is_err() { let font_thread_has_closed = self.chan.send(Command::Ping).is_err(); - assert!(font_thread_has_closed, "Failed to receive a response from live font cache"); + assert!( + font_thread_has_closed, + "Failed to receive a response from live font cache" + ); panic!("Font cache thread has already exited."); } match reply.unwrap() { - Reply::GetFontTemplateReply(data) => { - data - } + Reply::GetFontTemplateReply(data) => data, } } } diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index a09e6cf7b57..6cb4d8e7ed6 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -21,14 +21,18 @@ use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::properties::style_structs::Font as FontStyleStruct; use webrender_api; -static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) +static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) /// An epoch for the font context cache. The cache is flushed if the current epoch does not match /// this one. static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT; pub trait FontSource { - fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey; + fn get_font_instance( + &mut self, + key: webrender_api::FontKey, + size: Au, + ) -> webrender_api::FontInstanceKey; fn font_template( &mut self, @@ -74,7 +78,7 @@ impl FontContext { fn expire_font_caches_if_necessary(&mut self) { let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst); if current_epoch == self.epoch { - return + return; } self.font_cache.clear(); @@ -95,7 +99,7 @@ impl FontContext { }; if let Some(ref font_group) = self.font_group_cache.get(&cache_key) { - return (*font_group).clone() + return (*font_group).clone(); } let font_group = Rc::new(RefCell::new(FontGroup::new(&cache_key.style))); @@ -115,27 +119,31 @@ impl FontContext { family_descriptor: family_descriptor.clone(), }; - self.font_cache.get(&cache_key).map(|v| v.clone()).unwrap_or_else(|| { - debug!( - "FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}", - font_descriptor, - family_descriptor - ); + self.font_cache + .get(&cache_key) + .map(|v| v.clone()) + .unwrap_or_else(|| { + debug!( + "FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}", + font_descriptor, family_descriptor + ); - let font = - self.font_template(&font_descriptor.template_descriptor, family_descriptor) - .and_then(|template_info| self.create_font(template_info, font_descriptor.to_owned()).ok()) - .map(|font| Rc::new(RefCell::new(font))); + let font = self + .font_template(&font_descriptor.template_descriptor, family_descriptor) + .and_then(|template_info| { + self.create_font(template_info, font_descriptor.to_owned()) + .ok() + }).map(|font| Rc::new(RefCell::new(font))); - self.font_cache.insert(cache_key, font.clone()); - font - }) + self.font_cache.insert(cache_key, font.clone()); + font + }) } fn font_template( &mut self, template_descriptor: &FontTemplateDescriptor, - family_descriptor: &FontFamilyDescriptor + family_descriptor: &FontFamilyDescriptor, ) -> Option { let cache_key = FontTemplateCacheKey { template_descriptor: template_descriptor.clone(), @@ -164,7 +172,7 @@ impl FontContext { fn create_font( &mut self, info: FontTemplateInfo, - descriptor: FontDescriptor + descriptor: FontDescriptor, ) -> Result { // TODO: (Bug #3463): Currently we only support fake small-caps // painting. We should also support true small-caps (where the @@ -177,11 +185,18 @@ impl FontContext { let handle = FontHandle::new_from_template( &self.platform_handle, info.font_template, - Some(actual_pt_size) + Some(actual_pt_size), )?; - let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size); - Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key)) + let font_instance_key = self + .font_source + .get_font_instance(info.font_key, actual_pt_size); + Ok(Font::new( + handle, + descriptor.to_owned(), + actual_pt_size, + font_instance_key, + )) } } @@ -219,7 +234,10 @@ impl PartialEq for FontGroupCacheKey { impl Eq for FontGroupCacheKey {} impl Hash for FontGroupCacheKey { - fn hash(&self, hasher: &mut H) where H: Hasher { + fn hash(&self, hasher: &mut H) + where + H: Hasher, + { self.style.hash.hash(hasher) } } diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 8a72360d0dc..80527dfae38 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -26,7 +26,6 @@ pub struct FontTemplateDescriptor { pub style: FontStyle, } - /// FontTemplateDescriptor contains floats, which are not Eq because of NaN. However, /// we know they will never be NaN, so we can manually implement Eq. impl Eq for FontTemplateDescriptor {} @@ -41,14 +40,9 @@ fn style_to_number(s: &FontStyle) -> f32 { } } - impl FontTemplateDescriptor { #[inline] - pub fn new( - weight: FontWeight, - stretch: FontStretch, - style: FontStyle, - ) -> Self { + pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self { Self { weight, stretch, @@ -138,7 +132,10 @@ impl FontTemplate { } /// Get the descriptor. Returns `None` when instantiating the data fails. - pub fn descriptor(&mut self, font_context: &FontContextHandle) -> Option { + pub fn descriptor( + &mut self, + font_context: &FontContextHandle, + ) -> Option { // The font template data can be unloaded when nothing is referencing // it (via the Weak reference to the Arc above). However, if we have // already loaded a font, store the style information about it separately, @@ -147,18 +144,22 @@ impl FontTemplate { self.descriptor.or_else(|| { if self.instantiate(font_context).is_err() { - return None + return None; }; - Some(self.descriptor.expect("Instantiation succeeded but no descriptor?")) + Some( + self.descriptor + .expect("Instantiation succeeded but no descriptor?"), + ) }) } /// Get the data for creating a font if it matches a given descriptor. - pub fn data_for_descriptor(&mut self, - fctx: &FontContextHandle, - requested_desc: &FontTemplateDescriptor) - -> Option> { + pub fn data_for_descriptor( + &mut self, + fctx: &FontContextHandle, + requested_desc: &FontTemplateDescriptor, + ) -> Option> { self.descriptor(&fctx).and_then(|descriptor| { if *requested_desc == descriptor { self.data().ok() @@ -176,21 +177,20 @@ impl FontTemplate { requested_descriptor: &FontTemplateDescriptor, ) -> Option<(Arc, f32)> { self.descriptor(&font_context).and_then(|descriptor| { - self.data().ok().map(|data| { - (data, descriptor.distance_from(requested_descriptor)) - }) + self.data() + .ok() + .map(|data| (data, descriptor.distance_from(requested_descriptor))) }) } fn instantiate(&mut self, font_context: &FontContextHandle) -> Result<(), ()> { if !self.is_valid { - return Err(()) + return Err(()); } let data = self.data().map_err(|_| ())?; - let handle: Result = FontHandleMethods::new_from_template(font_context, - data, - None); + let handle: Result = + FontHandleMethods::new_from_template(font_context, data, None); self.is_valid = handle.is_ok(); let handle = handle?; self.descriptor = Some(FontTemplateDescriptor::new( @@ -220,7 +220,7 @@ impl FontTemplate { }; if let Some(data) = maybe_data { - return Ok(data) + return Ok(data); } assert!(self.strong_ref.is_none()); diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index a1b72e4e0c9..e781c1c8776 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -9,14 +9,20 @@ extern crate app_units; extern crate bitflags; // Mac OS-specific library dependencies -#[cfg(target_os = "macos")] extern crate byteorder; -#[cfg(target_os = "macos")] extern crate core_foundation; -#[cfg(target_os = "macos")] extern crate core_graphics; -#[cfg(target_os = "macos")] extern crate core_text; +#[cfg(target_os = "macos")] +extern crate byteorder; +#[cfg(target_os = "macos")] +extern crate core_foundation; +#[cfg(target_os = "macos")] +extern crate core_graphics; +#[cfg(target_os = "macos")] +extern crate core_text; // Windows-specific library dependencies -#[cfg(target_os = "windows")] extern crate dwrote; -#[cfg(target_os = "windows")] extern crate truetype; +#[cfg(target_os = "windows")] +extern crate dwrote; +#[cfg(target_os = "windows")] +extern crate truetype; extern crate euclid; extern crate fnv; @@ -24,8 +30,10 @@ extern crate fnv; #[cfg(target_os = "linux")] extern crate fontconfig; extern crate fontsan; -#[cfg(any(target_os = "linux", target_os = "android"))] extern crate freetype; -#[cfg(any(target_os = "linux", target_os = "android"))] extern crate servo_allocator; +#[cfg(any(target_os = "linux", target_os = "android"))] +extern crate freetype; +#[cfg(any(target_os = "linux", target_os = "android"))] +extern crate servo_allocator; extern crate gfx_traits; // Eventually we would like the shaper to be pluggable, as many operating systems have their own @@ -35,19 +43,25 @@ extern crate harfbuzz_sys as harfbuzz; extern crate ipc_channel; #[macro_use] extern crate lazy_static; -#[cfg(any(target_os = "linux", target_os = "android"))] extern crate libc; +#[cfg(any(target_os = "linux", target_os = "android"))] +extern crate libc; #[macro_use] extern crate log; #[cfg_attr(target_os = "windows", macro_use)] extern crate malloc_size_of; extern crate net_traits; extern crate ordered_float; -#[cfg(all(feature = "unstable", any(target_feature = "sse2", target_feature = "neon")))] +#[cfg(all( + feature = "unstable", + any(target_feature = "sse2", target_feature = "neon") +))] extern crate packed_simd; extern crate range; -#[macro_use] extern crate serde; +#[macro_use] +extern crate serde; extern crate servo_arc; -#[macro_use] extern crate servo_atoms; +#[macro_use] +extern crate servo_atoms; extern crate servo_url; extern crate smallvec; extern crate style; @@ -61,7 +75,8 @@ extern crate xi_unicode; extern crate xml5ever; // Fonts -#[macro_use] pub mod font; +#[macro_use] +pub mod font; pub mod font_cache_thread; pub mod font_context; pub mod font_template; diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index dc9d1ac2df5..8608f38329e 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -58,26 +58,24 @@ impl TestFontSource { } fn add_face(family: &mut FontTemplates, name: &str, identifier: Option<&str>) { - let mut path: PathBuf = [ - env!("CARGO_MANIFEST_DIR"), - "tests", - "support", - "CSSTest", - ].iter().collect(); + let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"] + .iter() + .collect(); path.push(format!("{}.ttf", name)); let file = File::open(path).unwrap(); let identifier = Atom::from(identifier.unwrap_or(name)); - family.add_template( - identifier, - Some(file.bytes().map(|b| b.unwrap()).collect()) - ) + family.add_template(identifier, Some(file.bytes().map(|b| b.unwrap()).collect())) } } impl FontSource for TestFontSource { - fn get_font_instance(&mut self, _key: webrender_api::FontKey, _size: Au) -> webrender_api::FontInstanceKey { + fn get_font_instance( + &mut self, + _key: webrender_api::FontKey, + _size: Au, + ) -> webrender_api::FontInstanceKey { webrender_api::FontInstanceKey(webrender_api::IdNamespace(0), 0) } @@ -92,11 +90,9 @@ impl FontSource for TestFontSource { self.families .get_mut(family_descriptor.name()) .and_then(|family| family.find_font_for_style(&template_descriptor, handle)) - .map(|template| { - FontTemplateInfo { - font_template: template, - font_key: webrender_api::FontKey(webrender_api::IdNamespace(0), 0), - } + .map(|template| FontTemplateInfo { + font_template: template, + font_key: webrender_api::FontKey(webrender_api::IdNamespace(0), 0), }) } } @@ -116,12 +112,14 @@ fn style() -> FontStyleStruct { } fn font_family(names: Vec<&str>) -> FontFamily { - let names: Vec = names.into_iter().map(|name| - SingleFontFamily::FamilyName(FamilyName { - name: Atom::from(name), - syntax: FamilyNameSyntax::Quoted, - }) - ).collect(); + let names: Vec = names + .into_iter() + .map(|name| { + SingleFontFamily::FamilyName(FamilyName { + name: Atom::from(name), + syntax: FamilyNameSyntax::Quoted, + }) + }).collect(); FontFamily(FontFamilyList::new(names.into_boxed_slice())) } @@ -156,19 +154,36 @@ fn test_font_group_find_by_codepoint() { let mut context = FontContext::new(source); let mut style = style(); - style.set_font_family(font_family(vec!("CSSTest ASCII", "CSSTest Basic"))); + style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"])); let group = context.font_group(Arc::new(style)); - let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); + let font = group + .borrow_mut() + .find_by_codepoint(&mut context, 'a') + .unwrap(); assert_eq!(&*font.borrow().identifier(), "csstest-ascii"); - assert_eq!(count.get(), 1, "only the first font in the list should have been loaded"); + assert_eq!( + count.get(), + 1, + "only the first font in the list should have been loaded" + ); - let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); + let font = group + .borrow_mut() + .find_by_codepoint(&mut context, 'a') + .unwrap(); assert_eq!(&*font.borrow().identifier(), "csstest-ascii"); - assert_eq!(count.get(), 1, "we shouldn't load the same font a second time"); + assert_eq!( + count.get(), + 1, + "we shouldn't load the same font a second time" + ); - let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap(); + let font = group + .borrow_mut() + .find_by_codepoint(&mut context, 'á') + .unwrap(); assert_eq!(&*font.borrow().identifier(), "csstest-basic-regular"); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); } @@ -179,19 +194,27 @@ fn test_font_fallback() { let mut context = FontContext::new(source); let mut style = style(); - style.set_font_family(font_family(vec!("CSSTest ASCII"))); + style.set_font_family(font_family(vec!["CSSTest ASCII"])); let group = context.font_group(Arc::new(style)); - let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); + let font = group + .borrow_mut() + .find_by_codepoint(&mut context, 'a') + .unwrap(); assert_eq!( - &*font.borrow().identifier(), "csstest-ascii", + &*font.borrow().identifier(), + "csstest-ascii", "a family in the group should be used if there is a matching glyph" ); - let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap(); + let font = group + .borrow_mut() + .find_by_codepoint(&mut context, 'á') + .unwrap(); assert_eq!( - &*font.borrow().identifier(), "fallback", + &*font.borrow().identifier(), + "fallback", "a fallback font should be used if there is no matching glyph in the group" ); } @@ -212,10 +235,8 @@ fn test_font_template_is_cached() { pt_size: Au(10), }; - let family_descriptor = FontFamilyDescriptor::new( - FontFamilyName::from("CSSTest Basic"), - FontSearchScope::Any, - ); + let family_descriptor = + FontFamilyDescriptor::new(FontFamilyName::from("CSSTest Basic"), FontSearchScope::Any); let font1 = context.font(&font_descriptor, &family_descriptor).unwrap(); @@ -228,5 +249,9 @@ fn test_font_template_is_cached() { "the same font should not have been returned" ); - assert_eq!(count.get(), 1, "we should only have fetched the template data from the cache thread once"); + assert_eq!( + count.get(), + 1, + "we should only have fetched the template data from the cache thread once" + ); } diff --git a/components/gfx/tests/font_template.rs b/components/gfx/tests/font_template.rs index e5857ce4ef7..a6caea2f375 100644 --- a/components/gfx/tests/font_template.rs +++ b/components/gfx/tests/font_template.rs @@ -2,9 +2,12 @@ * 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/. */ -#[cfg(not(target_os = "macos"))] extern crate gfx; -#[cfg(not(target_os = "macos"))] extern crate servo_atoms; -#[cfg(not(target_os = "macos"))] extern crate style; +#[cfg(not(target_os = "macos"))] +extern crate gfx; +#[cfg(not(target_os = "macos"))] +extern crate servo_atoms; +#[cfg(not(target_os = "macos"))] +extern crate style; // Test doesn't yet run on Mac, see https://github.com/servo/servo/pull/19928 for explanation. #[cfg(not(target_os = "macos"))] @@ -28,14 +31,16 @@ fn test_font_template_descriptor() { "support", "dejavu-fonts-ttf-2.37", "ttf", - ].iter().collect(); + ] + .iter() + .collect(); path.push(format!("{}.ttf", filename)); let file = File::open(path).unwrap(); let mut template = FontTemplate::new( Atom::from(filename), - Some(file.bytes().map(|b| b.unwrap()).collect()) + Some(file.bytes().map(|b| b.unwrap()).collect()), ).unwrap(); let context = FontContextHandle::new(); @@ -43,27 +48,39 @@ fn test_font_template_descriptor() { template.descriptor(&context).unwrap() } - assert_eq!(descriptor("DejaVuSans"), FontTemplateDescriptor { - weight: FontWeight::normal(), - stretch: FontStretch::hundred(), - style: FontStyle::Normal, - }); + assert_eq!( + descriptor("DejaVuSans"), + FontTemplateDescriptor { + weight: FontWeight::normal(), + stretch: FontStretch::hundred(), + style: FontStyle::Normal, + } + ); - assert_eq!(descriptor("DejaVuSans-Bold"), FontTemplateDescriptor { - weight: FontWeight::bold(), - stretch: FontStretch::hundred(), - style: FontStyle::Normal, - }); + assert_eq!( + descriptor("DejaVuSans-Bold"), + FontTemplateDescriptor { + weight: FontWeight::bold(), + stretch: FontStretch::hundred(), + style: FontStyle::Normal, + } + ); - assert_eq!(descriptor("DejaVuSans-Oblique"), FontTemplateDescriptor { - weight: FontWeight::normal(), - stretch: FontStretch::hundred(), - style: FontStyle::Italic, - }); + assert_eq!( + descriptor("DejaVuSans-Oblique"), + FontTemplateDescriptor { + weight: FontWeight::normal(), + stretch: FontStretch::hundred(), + style: FontStyle::Italic, + } + ); - assert_eq!(descriptor("DejaVuSansCondensed-BoldOblique"), FontTemplateDescriptor { - weight: FontWeight::bold(), - stretch: FontStretch(NonNegative(Percentage(0.875))), - style: FontStyle::Italic, - }); + assert_eq!( + descriptor("DejaVuSansCondensed-BoldOblique"), + FontTemplateDescriptor { + weight: FontWeight::bold(), + stretch: FontStretch(NonNegative(Percentage(0.875))), + style: FontStyle::Italic, + } + ); } diff --git a/components/gfx/tests/text_util.rs b/components/gfx/tests/text_util.rs index 7729a6f78a7..0b8132e9c88 100644 --- a/components/gfx/tests/text_util.rs +++ b/components/gfx/tests/text_util.rs @@ -29,26 +29,13 @@ fn test_transform_compress_none() { #[test] fn test_transform_discard_newline() { let test_strs = [ - (" foo bar", - " foo bar"), - - ("foo bar ", - "foo bar "), - - ("foo\n bar", - "foo bar"), - - ("foo \nbar", - "foo bar"), - - (" foo bar \nbaz", - " foo bar baz"), - - ("foo bar baz", - "foo bar baz"), - - ("foobarbaz\n\n", - "foobarbaz"), + (" foo bar", " foo bar"), + ("foo bar ", "foo bar "), + ("foo\n bar", "foo bar"), + ("foo \nbar", "foo bar"), + (" foo bar \nbaz", " foo bar baz"), + ("foo bar baz", "foo bar baz"), + ("foobarbaz\n\n", "foobarbaz"), ]; let mode = CompressionMode::DiscardNewline; @@ -62,26 +49,13 @@ fn test_transform_discard_newline() { #[test] fn test_transform_compress_whitespace() { let test_strs = [ - (" foo bar", - "foo bar"), - - ("foo bar ", - "foo bar "), - - ("foo\n bar", - "foo\n bar"), - - ("foo \nbar", - "foo \nbar"), - - (" foo bar \nbaz", - "foo bar \nbaz"), - - ("foo bar baz", - "foo bar baz"), - - ("foobarbaz\n\n", - "foobarbaz\n\n"), + (" foo bar", "foo bar"), + ("foo bar ", "foo bar "), + ("foo\n bar", "foo\n bar"), + ("foo \nbar", "foo \nbar"), + (" foo bar \nbaz", "foo bar \nbaz"), + ("foo bar baz", "foo bar baz"), + ("foobarbaz\n\n", "foobarbaz\n\n"), ]; let mode = CompressionMode::CompressWhitespace; @@ -95,26 +69,13 @@ fn test_transform_compress_whitespace() { #[test] fn test_transform_compress_whitespace_newline() { let test_strs = vec![ - (" foo bar", - "foo bar"), - - ("foo bar ", - "foo bar "), - - ("foo\n bar", - "foo bar"), - - ("foo \nbar", - "foo bar"), - - (" foo bar \nbaz", - "foo bar baz"), - - ("foo bar baz", - "foo bar baz"), - - ("foobarbaz\n\n", - "foobarbaz "), + (" foo bar", "foo bar"), + ("foo bar ", "foo bar "), + ("foo\n bar", "foo bar"), + ("foo \nbar", "foo bar"), + (" foo bar \nbaz", "foo bar baz"), + ("foo bar baz", "foo bar baz"), + ("foobarbaz\n\n", "foobarbaz "), ]; let mode = CompressionMode::CompressWhitespaceNewline; @@ -128,29 +89,14 @@ fn test_transform_compress_whitespace_newline() { #[test] fn test_transform_compress_whitespace_newline_no_incoming() { let test_strs = [ - (" foo bar", - " foo bar"), - - ("\nfoo bar", - " foo bar"), - - ("foo bar ", - "foo bar "), - - ("foo\n bar", - "foo bar"), - - ("foo \nbar", - "foo bar"), - - (" foo bar \nbaz", - " foo bar baz"), - - ("foo bar baz", - "foo bar baz"), - - ("foobarbaz\n\n", - "foobarbaz "), + (" foo bar", " foo bar"), + ("\nfoo bar", " foo bar"), + ("foo bar ", "foo bar "), + ("foo\n bar", "foo bar"), + ("foo \nbar", "foo bar"), + (" foo bar \nbaz", " foo bar baz"), + ("foo bar baz", "foo bar baz"), + ("foobarbaz\n\n", "foobarbaz "), ]; let mode = CompressionMode::CompressWhitespaceNewline;