From c9948a844dced4e00b25734fc9c2a8b0d433b739 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 10 Nov 2017 17:04:59 -0800 Subject: [PATCH] Render reference images on the server, untested as of yet --- demo/client/html/integration-test.html.hbs | 31 +++- demo/server/Cargo.toml | 1 + demo/server/src/main.rs | 171 +++++++++++++++------ 3 files changed, 151 insertions(+), 52 deletions(-) diff --git a/demo/client/html/integration-test.html.hbs b/demo/client/html/integration-test.html.hbs index a02cf9c2..1b1f3cd1 100644 --- a/demo/client/html/integration-test.html.hbs +++ b/demo/client/html/integration-test.html.hbs @@ -8,12 +8,31 @@ {{>partials/navbar.html isTool=true}} -
-
- Font -
+
+
+
+
+
+ + +
+
+ +
+ +
px
+
+
+
+
+ + + + +
- - diff --git a/demo/server/Cargo.toml b/demo/server/Cargo.toml index 95b9c588..b634a2a1 100644 --- a/demo/server/Cargo.toml +++ b/demo/server/Cargo.toml @@ -9,6 +9,7 @@ base64 = "0.6" bincode = "0.8" env_logger = "0.4" euclid = "0.15" +image = "0.17" lazy_static = "0.2" log = "0.3" lru-cache = "0.1" diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index b0ee2495..a32edc41 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -16,6 +16,7 @@ extern crate base64; extern crate env_logger; extern crate euclid; extern crate fontsan; +extern crate image; extern crate lru_cache; extern crate pathfinder_font_renderer; extern crate pathfinder_partitioner; @@ -30,6 +31,7 @@ extern crate serde_derive; use app_units::Au; use euclid::{Point2D, Transform2D}; +use image::{DynamicImage, ImageBuffer, ImageFormat, ImageLuma8}; use lru_cache::LruCache; use pathfinder_font_renderer::{FontContext, FontInstance, FontKey, GlyphKey, SubpixelOffset}; use pathfinder_partitioner::mesh_library::MeshLibrary; @@ -104,7 +106,7 @@ struct MeshLibraryCacheKey { #[derive(Clone, Serialize, Deserialize)] struct PartitionFontRequest { - face: PartitionFontRequestFace, + face: FontRequestFace, #[serde(rename = "fontIndex")] font_index: u32, glyphs: Vec, @@ -113,13 +115,24 @@ struct PartitionFontRequest { } #[derive(Clone, Serialize, Deserialize)] -enum PartitionFontRequestFace { +enum FontRequestFace { /// One of the builtin fonts in `BUILTIN_FONTS`. Builtin(String), /// Base64-encoded OTF data. Custom(String), } +#[derive(Clone, Serialize, Deserialize)] +struct RenderReferenceRequest { + face: FontRequestFace, + #[serde(rename = "fontIndex")] + font_index: u32, + glyph: u32, + + #[serde(rename = "pointSize")] + point_size: f64, +} + #[derive(Clone, Copy, Serialize, Deserialize)] struct PartitionGlyph { id: u32, @@ -133,11 +146,12 @@ struct PartitionFontResponse { } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] -enum PartitionFontError { +enum FontError { UnknownBuiltinFont, Base64DecodingFailed, FontSanitizationFailed, FontLoadingFailed, + RasterizationFailed, Unimplemented, } @@ -221,11 +235,72 @@ impl<'r> Responder<'r> for PartitionResponder { } } +#[derive(Clone)] +struct ReferenceImage { + image: DynamicImage, +} + +impl<'r> Responder<'r> for ReferenceImage { + fn respond_to(self, _: &Request) -> Result, Status> { + let mut builder = Response::build(); + builder.header(ContentType::PNG); + + let mut bytes = vec![]; + try!(self.image + .save(&mut bytes, ImageFormat::PNG) + .map_err(|_| Status::InternalServerError)); + builder.sized_body(Cursor::new(bytes)); + builder.ok() + } +} + +fn add_font_from_request(font_context: &mut FontContext, face: &FontRequestFace, font_index: u32) + -> Result { + // Fetch the OTF data. + let otf_data = match *face { + FontRequestFace::Builtin(ref builtin_font_name) => { + // Read in the builtin font. + match BUILTIN_FONTS.iter().filter(|& &(name, _)| name == builtin_font_name).next() { + Some(&(_, path)) => { + let mut data = vec![]; + File::open(path).expect("Couldn't find builtin font!") + .read_to_end(&mut data) + .expect("Couldn't read builtin font!"); + Arc::new(data) + } + None => return Err(FontError::UnknownBuiltinFont), + } + } + FontRequestFace::Custom(ref encoded_data) => { + // Decode Base64-encoded OTF data. + let unsafe_otf_data = match base64::decode(encoded_data) { + Ok(unsafe_otf_data) => unsafe_otf_data, + Err(_) => return Err(FontError::Base64DecodingFailed), + }; + + // Sanitize. + match fontsan::process(&unsafe_otf_data) { + Ok(otf_data) => Arc::new(otf_data), + Err(_) => return Err(FontError::FontSanitizationFailed), + } + } + }; + + // Parse glyph data. + let font_key = FontKey::new(); + if font_context.add_font_from_memory(&font_key, otf_data, font_index).is_err() { + println!("Failed to add font from memory!"); + return Err(FontError::FontLoadingFailed) + } + + Ok(font_key) +} + #[post("/partition-font", format = "application/json", data = "")] -fn partition_font(request: Json) - -> Result { +fn partition_font(request: Json) -> Result { + // Check the cache. let cache_key = match request.face { - PartitionFontRequestFace::Builtin(ref builtin_font_name) => { + FontRequestFace::Builtin(ref builtin_font_name) => { Some(MeshLibraryCacheKey { builtin_font_name: (*builtin_font_name).clone(), glyph_ids: request.glyphs.iter().map(|glyph| glyph.id).collect(), @@ -242,53 +317,21 @@ fn partition_font(request: Json) } } - // Fetch the OTF data. - let otf_data = match request.face { - PartitionFontRequestFace::Builtin(ref builtin_font_name) => { - // Read in the builtin font. - match BUILTIN_FONTS.iter().filter(|& &(name, _)| name == builtin_font_name).next() { - Some(&(_, path)) => { - let mut data = vec![]; - File::open(path).expect("Couldn't find builtin font!") - .read_to_end(&mut data) - .expect("Couldn't read builtin font!"); - Arc::new(data) - } - None => return Err(PartitionFontError::UnknownBuiltinFont), - } - } - PartitionFontRequestFace::Custom(ref encoded_data) => { - // Decode Base64-encoded OTF data. - let unsafe_otf_data = match base64::decode(encoded_data) { - Ok(unsafe_otf_data) => unsafe_otf_data, - Err(_) => return Err(PartitionFontError::Base64DecodingFailed), - }; - - // Sanitize. - match fontsan::process(&unsafe_otf_data) { - Ok(otf_data) => Arc::new(otf_data), - Err(_) => return Err(PartitionFontError::FontSanitizationFailed), - } - } - }; - // Parse glyph data. - let font_key = FontKey::new(); - let font_instance = FontInstance { - font_key: font_key, - size: Au::from_f64_px(request.point_size), - }; let mut font_context = match FontContext::new() { Ok(font_context) => font_context, Err(_) => { println!("Failed to create a font context!"); - return Err(PartitionFontError::FontLoadingFailed) + return Err(FontError::FontLoadingFailed) } }; - if font_context.add_font_from_memory(&font_key, otf_data, request.font_index).is_err() { - println!("Failed to add font from memory!"); - return Err(PartitionFontError::FontLoadingFailed) - } + let font_key = try!(add_font_from_request(&mut font_context, + &request.face, + request.font_index)); + let font_instance = FontInstance { + font_key: font_key, + size: Au::from_f64_px(request.point_size), + }; // Read glyph info. let mut path_buffer = PathBuffer::new(); @@ -417,6 +460,41 @@ fn partition_svg_paths(request: Json) }) } +#[post("/render-reference", format = "application/json", data = "")] +fn render_reference(request: Json) + -> Result { + // Load the font. + let mut font_context = match FontContext::new() { + Ok(font_context) => font_context, + Err(_) => { + println!("Failed to create a font context!"); + return Err(FontError::FontLoadingFailed) + } + }; + let font_key = try!(add_font_from_request(&mut font_context, + &request.face, + request.font_index)); + let font_instance = FontInstance { + font_key: font_key, + size: Au::from_f64_px(request.point_size), + }; + + // Render the image. + let glyph_key = GlyphKey::new(request.glyph, SubpixelOffset(0)); + let glyph_image = try!(font_context.rasterize_glyph_with_native_rasterizer(&font_instance, + &glyph_key) + .map_err(|_| FontError::RasterizationFailed)); + let dimensions = &glyph_image.dimensions; + let image_buffer = ImageBuffer::from_raw(dimensions.size.width, + dimensions.size.height, + glyph_image.pixels).unwrap(); + let reference_image = ReferenceImage { + image: ImageLuma8(image_buffer), + }; + + Ok(reference_image) +} + // Static files #[get("/")] fn static_index() -> io::Result { @@ -553,6 +631,7 @@ fn main() { rocket.mount("/", routes![ partition_font, partition_svg_paths, + render_reference, static_index, static_demo_text, static_demo_svg,