From d6767219ff6ff38a0937c398d017f98561c14244 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 30 Aug 2017 19:48:18 -0700 Subject: [PATCH] Automatically load some built-in fonts and SVGs --- demo/client/css/pathfinder.css | 3 +- demo/client/html/3d-demo.html | 1 - demo/client/html/svg-demo.html | 1 - demo/client/html/text-demo.html | 1 - demo/client/src/app-controller.ts | 15 +++++ demo/client/src/svg-demo.ts | 8 +++ demo/client/src/text-demo.ts | 14 ++++- demo/server/src/main.rs | 99 ++++++++++++++++++++++++------- 8 files changed, 114 insertions(+), 28 deletions(-) diff --git a/demo/client/css/pathfinder.css b/demo/client/css/pathfinder.css index ea2f4ba8..29d998d6 100644 --- a/demo/client/css/pathfinder.css +++ b/demo/client/css/pathfinder.css @@ -7,7 +7,7 @@ user-select: none; -moz-user-select: none; opacity: 1.0; - transition: opacity 300ms, transform 300ms; + transition: opacity 300ms, transform 300ms, visibility 300ms; transform: translateY(0); } #pf-settings-button:not(:hover) { @@ -20,6 +20,7 @@ #pf-settings.pf-invisible { opacity: 0.0; transform: translateY(1em); + visibility: hidden; } button > svg { width: 1.25em; diff --git a/demo/client/html/3d-demo.html b/demo/client/html/3d-demo.html index 330c27b9..47915f8f 100644 --- a/demo/client/html/3d-demo.html +++ b/demo/client/html/3d-demo.html @@ -17,7 +17,6 @@ aria-label="Close"> -

Settings

diff --git a/demo/client/html/svg-demo.html b/demo/client/html/svg-demo.html index 0df1bddb..41709ce7 100644 --- a/demo/client/html/svg-demo.html +++ b/demo/client/html/svg-demo.html @@ -18,7 +18,6 @@ aria-label="Close"> -

Settings

diff --git a/demo/client/html/text-demo.html b/demo/client/html/text-demo.html index b900d372..f48a295a 100644 --- a/demo/client/html/text-demo.html +++ b/demo/client/html/text-demo.html @@ -17,7 +17,6 @@ aria-label="Close"> -

Settings

diff --git a/demo/client/src/app-controller.ts b/demo/client/src/app-controller.ts index 0540eeb4..f900246f 100644 --- a/demo/client/src/app-controller.ts +++ b/demo/client/src/app-controller.ts @@ -50,6 +50,10 @@ export default abstract class AppController { this.updateAALevel(); } + protected loadInitialFile() { + this.fileSelectionChanged(); + } + private updateAALevel() { const selectedOption = this.aaLevelSelect.selectedOptions[0]; const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value)); @@ -83,13 +87,24 @@ export default abstract class AppController { return; } + // Remove the "Custom…" placeholder if it exists. const placeholder = document.getElementById('pf-custom-option-placeholder'); if (placeholder != null) this.selectFileElement.removeChild(placeholder); + + // Fetch the file. + window.fetch(`${this.builtinFileURI}/${selectedOption.value}`) + .then(response => response.arrayBuffer()) + .then(data => { + this.fileData = data; + this.fileLoaded(); + }); } protected abstract fileLoaded(): void; + protected abstract get builtinFileURI(): string; + protected abstract createView(canvas: HTMLCanvasElement, commonShaderSource: string, shaderSources: ShaderMap): View; diff --git a/demo/client/src/svg-demo.ts b/demo/client/src/svg-demo.ts index 86fb5df8..4beb73cb 100644 --- a/demo/client/src/svg-demo.ts +++ b/demo/client/src/svg-demo.ts @@ -29,6 +29,8 @@ const SVG_NS: string = "http://www.w3.org/2000/svg"; const PARTITION_SVG_PATHS_ENDPOINT_URL: string = "/partition-svg-paths"; +const BUILTIN_SVG_URI: string = "/svg/demo"; + const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { none: NoAAStrategy, ssaa: SSAAStrategy, @@ -59,6 +61,8 @@ class SVGDemoController extends AppController { this.svg = document.getElementById('pf-svg') as Element as SVGSVGElement; this.pathElements = []; + + this.loadInitialFile(); } protected fileLoaded() { @@ -141,6 +145,10 @@ class SVGDemoController extends AppController { }); } + protected get builtinFileURI(): string { + return BUILTIN_SVG_URI; + } + private meshesReceived() { this.view.then(view => { view.uploadPathData(this.pathElements); diff --git a/demo/client/src/text-demo.ts b/demo/client/src/text-demo.ts index d3d0d171..b8d3dab2 100644 --- a/demo/client/src/text-demo.ts +++ b/demo/client/src/text-demo.ts @@ -68,6 +68,8 @@ const INITIAL_FONT_SIZE: number = 72.0; const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font"; +const BUILTIN_FONT_URI: string = "/otf/demo"; + const B_POSITION_SIZE: number = 8; const B_PATH_INDEX_SIZE: number = 2; @@ -119,6 +121,8 @@ class TextDemoController extends AppController { this.fontSize = INITIAL_FONT_SIZE; this.fpsLabel = unwrapNull(document.getElementById('pf-fps-label')); + + this.loadInitialFile(); } protected createView(canvas: HTMLCanvasElement, @@ -144,8 +148,12 @@ class TextDemoController extends AppController { this.uniqueGlyphs = _.sortedUniqBy(this.uniqueGlyphs, glyph => glyph.index); // Build the partitioning request to the server. + // + // FIXME(pcwalton): If this is a builtin font, don't resend it to the server! const request = { - otf: base64js.fromByteArray(new Uint8Array(this.fileData)), + face: { + Custom: base64js.fromByteArray(new Uint8Array(this.fileData)), + }, fontIndex: 0, glyphs: this.uniqueGlyphs.map(glyph => { const metrics = glyph.metrics; @@ -200,6 +208,10 @@ class TextDemoController extends AppController { this.view.then(view => view.attachText()); } + protected get builtinFileURI(): string { + return BUILTIN_FONT_URI; + } + private fpsLabel: HTMLElement; font: opentype.Font; diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index aafb04a9..ecdd6ebc 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -33,7 +33,7 @@ use rocket::response::{NamedFile, Responder, Response}; use rocket_contrib::json::Json; use serde::Serialize; use std::fs::File; -use std::io; +use std::io::{self, Read}; use std::mem; use std::path::{Path, PathBuf}; use std::u32; @@ -51,6 +51,16 @@ static STATIC_JS_PATHFINDER_PATH: &'static str = "../client"; static STATIC_SVG_OCTICONS_PATH: &'static str = "../client/node_modules/octicons/build/svg"; static STATIC_GLSL_PATH: &'static str = "../../shaders"; +static BUILTIN_FONTS: [(&'static str, &'static str); 3] = [ + ("open-sans", "../../resources/fonts/open-sans/OpenSans-Regular.ttf"), + ("nimbus-sans", "../../resources/fonts/nimbus-sans/NimbusSanL-Regu.ttf"), + ("eb-garamond", "../../resources/fonts/eb-garamond/EBGaramond12-Regular.ttf"), +]; + +static BUILTIN_SVGS: [(&'static str, &'static str); 1] = [ + ("tiger", "../../resources/svg/Ghostscript_Tiger.svg"), +]; + #[derive(Clone, Copy, Debug, Serialize, Deserialize)] struct SubpathRange { start: u32, @@ -77,14 +87,22 @@ impl IndexRange { } } -#[allow(non_snake_case)] #[derive(Clone, Serialize, Deserialize)] struct PartitionFontRequest { - // Base64 encoded. - otf: String, - fontIndex: u32, + face: PartitionFontRequestFace, + #[serde(rename = "fontIndex")] + font_index: u32, glyphs: Vec, - pointSize: f64, + #[serde(rename = "pointSize")] + point_size: f64, +} + +#[derive(Clone, Serialize, Deserialize)] +enum PartitionFontRequestFace { + /// One of the builtin fonts in `BUILTIN_FONTS`. + Builtin(String), + /// Base64-encoded OTF data. + Custom(String), } #[derive(Clone, Copy, Serialize, Deserialize)] @@ -182,6 +200,7 @@ struct PartitionEncodedPathData { #[derive(Clone, Copy, Serialize, Deserialize)] enum PartitionFontError { + UnknownBuiltinFont, Base64DecodingFailed, FontSanitizationFailed, FontLoadingFailed, @@ -312,26 +331,44 @@ fn partition_paths(partitioner: &mut Partitioner, subpath_indices: &[SubpathRang #[post("/partition-font", format = "application/json", data = "")] fn partition_font(request: Json) -> Json> { - // Decode Base64-encoded OTF data. - let unsafe_otf_data = match base64::decode(&request.otf) { - Ok(unsafe_otf_data) => unsafe_otf_data, - Err(_) => return Json(Err(PartitionFontError::Base64DecodingFailed)), - }; + // 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!"); + data + } + None => return Json(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 Json(Err(PartitionFontError::Base64DecodingFailed)), + }; - // Sanitize. - let otf_data = match fontsan::process(&unsafe_otf_data) { - Ok(otf_data) => otf_data, - Err(_) => return Json(Err(PartitionFontError::FontSanitizationFailed)), + // Sanitize. + match fontsan::process(&unsafe_otf_data) { + Ok(otf_data) => otf_data, + Err(_) => return Json(Err(PartitionFontError::FontSanitizationFailed)), + } + } }; // Parse glyph data. let font_key = FontKey::new(); let font_instance_key = FontInstanceKey { font_key: font_key, - size: Au::from_f64_px(request.pointSize), + size: Au::from_f64_px(request.point_size), }; let mut font_context = FontContext::new(); - if font_context.add_font_from_memory(&font_key, otf_data, request.fontIndex).is_err() { + if font_context.add_font_from_memory(&font_key, otf_data, request.font_index).is_err() { return Json(Err(PartitionFontError::FontLoadingFailed)) } @@ -476,15 +513,15 @@ fn partition_svg_paths(request: Json) // Static files #[get("/")] -fn static_text_demo() -> io::Result { +fn static_demo_text() -> io::Result { NamedFile::open(STATIC_TEXT_DEMO_PATH) } #[get("/demo/svg")] -fn static_svg_demo() -> io::Result { +fn static_demo_svg() -> io::Result { NamedFile::open(STATIC_SVG_DEMO_PATH) } #[get("/demo/3d")] -fn static_3d_demo() -> io::Result { +fn static_demo_3d() -> io::Result { NamedFile::open(STATIC_3D_DEMO_PATH) } #[get("/css/bootstrap/")] @@ -523,6 +560,20 @@ fn static_svg_octicons(file: PathBuf) -> Option { fn static_glsl(file: PathBuf) -> Option { Shader::open(Path::new(STATIC_GLSL_PATH).join(file)).ok() } +#[get("/otf/demo/")] +fn static_otf_demo(font_name: String) -> Option { + BUILTIN_FONTS.iter() + .filter(|& &(name, _)| name == font_name) + .next() + .and_then(|&(_, path)| NamedFile::open(Path::new(path)).ok()) +} +#[get("/svg/demo/")] +fn static_svg_demo(svg_name: String) -> Option { + BUILTIN_SVGS.iter() + .filter(|& &(name, _)| name == svg_name) + .next() + .and_then(|&(_, path)| NamedFile::open(Path::new(path)).ok()) +} struct Shader { file: File, @@ -546,9 +597,9 @@ fn main() { rocket::ignite().mount("/", routes![ partition_font, partition_svg_paths, - static_text_demo, - static_svg_demo, - static_3d_demo, + static_demo_text, + static_demo_svg, + static_demo_3d, static_css_bootstrap, static_css_octicons, static_css_pathfinder_css, @@ -558,5 +609,7 @@ fn main() { static_js_pathfinder, static_svg_octicons, static_glsl, + static_otf_demo, + static_svg_demo, ]).launch(); }