Automatically load some built-in fonts and SVGs
This commit is contained in:
parent
58b558de64
commit
d6767219ff
|
@ -7,7 +7,7 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
opacity: 1.0;
|
opacity: 1.0;
|
||||||
transition: opacity 300ms, transform 300ms;
|
transition: opacity 300ms, transform 300ms, visibility 300ms;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
#pf-settings-button:not(:hover) {
|
#pf-settings-button:not(:hover) {
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
#pf-settings.pf-invisible {
|
#pf-settings.pf-invisible {
|
||||||
opacity: 0.0;
|
opacity: 0.0;
|
||||||
transform: translateY(1em);
|
transform: translateY(1em);
|
||||||
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
button > svg {
|
button > svg {
|
||||||
width: 1.25em;
|
width: 1.25em;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
aria-label="Close">
|
aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
<h4 class="card-title">Settings</h4>
|
|
||||||
<form>
|
<form>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pf-aa-level-select">Antialiasing</label>
|
<label for="pf-aa-level-select">Antialiasing</label>
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
aria-label="Close">
|
aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
<h4 class="card-title">Settings</h4>
|
|
||||||
<form>
|
<form>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pf-select-file">SVG document</label>
|
<label for="pf-select-file">SVG document</label>
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
aria-label="Close">
|
aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
<h4 class="card-title">Settings</h4>
|
|
||||||
<form>
|
<form>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pf-select-file">Font</label>
|
<label for="pf-select-file">Font</label>
|
||||||
|
|
|
@ -50,6 +50,10 @@ export default abstract class AppController<View extends PathfinderView> {
|
||||||
this.updateAALevel();
|
this.updateAALevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected loadInitialFile() {
|
||||||
|
this.fileSelectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private updateAALevel() {
|
private updateAALevel() {
|
||||||
const selectedOption = this.aaLevelSelect.selectedOptions[0];
|
const selectedOption = this.aaLevelSelect.selectedOptions[0];
|
||||||
const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value));
|
const aaValues = unwrapNull(/^([a-z-]+)(?:-([0-9]+))?$/.exec(selectedOption.value));
|
||||||
|
@ -83,13 +87,24 @@ export default abstract class AppController<View extends PathfinderView> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the "Custom…" placeholder if it exists.
|
||||||
const placeholder = document.getElementById('pf-custom-option-placeholder');
|
const placeholder = document.getElementById('pf-custom-option-placeholder');
|
||||||
if (placeholder != null)
|
if (placeholder != null)
|
||||||
this.selectFileElement.removeChild(placeholder);
|
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 fileLoaded(): void;
|
||||||
|
|
||||||
|
protected abstract get builtinFileURI(): string;
|
||||||
|
|
||||||
protected abstract createView(canvas: HTMLCanvasElement,
|
protected abstract createView(canvas: HTMLCanvasElement,
|
||||||
commonShaderSource: string,
|
commonShaderSource: string,
|
||||||
shaderSources: ShaderMap<ShaderProgramSource>): View;
|
shaderSources: ShaderMap<ShaderProgramSource>): View;
|
||||||
|
|
|
@ -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 PARTITION_SVG_PATHS_ENDPOINT_URL: string = "/partition-svg-paths";
|
||||||
|
|
||||||
|
const BUILTIN_SVG_URI: string = "/svg/demo";
|
||||||
|
|
||||||
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
|
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
|
||||||
none: NoAAStrategy,
|
none: NoAAStrategy,
|
||||||
ssaa: SSAAStrategy,
|
ssaa: SSAAStrategy,
|
||||||
|
@ -59,6 +61,8 @@ class SVGDemoController extends AppController<SVGDemoView> {
|
||||||
this.svg = document.getElementById('pf-svg') as Element as SVGSVGElement;
|
this.svg = document.getElementById('pf-svg') as Element as SVGSVGElement;
|
||||||
|
|
||||||
this.pathElements = [];
|
this.pathElements = [];
|
||||||
|
|
||||||
|
this.loadInitialFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fileLoaded() {
|
protected fileLoaded() {
|
||||||
|
@ -141,6 +145,10 @@ class SVGDemoController extends AppController<SVGDemoView> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get builtinFileURI(): string {
|
||||||
|
return BUILTIN_SVG_URI;
|
||||||
|
}
|
||||||
|
|
||||||
private meshesReceived() {
|
private meshesReceived() {
|
||||||
this.view.then(view => {
|
this.view.then(view => {
|
||||||
view.uploadPathData(this.pathElements);
|
view.uploadPathData(this.pathElements);
|
||||||
|
|
|
@ -68,6 +68,8 @@ const INITIAL_FONT_SIZE: number = 72.0;
|
||||||
|
|
||||||
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
|
const PARTITION_FONT_ENDPOINT_URL: string = "/partition-font";
|
||||||
|
|
||||||
|
const BUILTIN_FONT_URI: string = "/otf/demo";
|
||||||
|
|
||||||
const B_POSITION_SIZE: number = 8;
|
const B_POSITION_SIZE: number = 8;
|
||||||
|
|
||||||
const B_PATH_INDEX_SIZE: number = 2;
|
const B_PATH_INDEX_SIZE: number = 2;
|
||||||
|
@ -119,6 +121,8 @@ class TextDemoController extends AppController<TextDemoView> {
|
||||||
this.fontSize = INITIAL_FONT_SIZE;
|
this.fontSize = INITIAL_FONT_SIZE;
|
||||||
|
|
||||||
this.fpsLabel = unwrapNull(document.getElementById('pf-fps-label'));
|
this.fpsLabel = unwrapNull(document.getElementById('pf-fps-label'));
|
||||||
|
|
||||||
|
this.loadInitialFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createView(canvas: HTMLCanvasElement,
|
protected createView(canvas: HTMLCanvasElement,
|
||||||
|
@ -144,8 +148,12 @@ class TextDemoController extends AppController<TextDemoView> {
|
||||||
this.uniqueGlyphs = _.sortedUniqBy(this.uniqueGlyphs, glyph => glyph.index);
|
this.uniqueGlyphs = _.sortedUniqBy(this.uniqueGlyphs, glyph => glyph.index);
|
||||||
|
|
||||||
// Build the partitioning request to the server.
|
// Build the partitioning request to the server.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): If this is a builtin font, don't resend it to the server!
|
||||||
const request = {
|
const request = {
|
||||||
otf: base64js.fromByteArray(new Uint8Array(this.fileData)),
|
face: {
|
||||||
|
Custom: base64js.fromByteArray(new Uint8Array(this.fileData)),
|
||||||
|
},
|
||||||
fontIndex: 0,
|
fontIndex: 0,
|
||||||
glyphs: this.uniqueGlyphs.map(glyph => {
|
glyphs: this.uniqueGlyphs.map(glyph => {
|
||||||
const metrics = glyph.metrics;
|
const metrics = glyph.metrics;
|
||||||
|
@ -200,6 +208,10 @@ class TextDemoController extends AppController<TextDemoView> {
|
||||||
this.view.then(view => view.attachText());
|
this.view.then(view => view.attachText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get builtinFileURI(): string {
|
||||||
|
return BUILTIN_FONT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
private fpsLabel: HTMLElement;
|
private fpsLabel: HTMLElement;
|
||||||
|
|
||||||
font: opentype.Font;
|
font: opentype.Font;
|
||||||
|
|
|
@ -33,7 +33,7 @@ use rocket::response::{NamedFile, Responder, Response};
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io::{self, Read};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::u32;
|
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_SVG_OCTICONS_PATH: &'static str = "../client/node_modules/octicons/build/svg";
|
||||||
static STATIC_GLSL_PATH: &'static str = "../../shaders";
|
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)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
struct SubpathRange {
|
struct SubpathRange {
|
||||||
start: u32,
|
start: u32,
|
||||||
|
@ -77,14 +87,22 @@ impl IndexRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
struct PartitionFontRequest {
|
struct PartitionFontRequest {
|
||||||
// Base64 encoded.
|
face: PartitionFontRequestFace,
|
||||||
otf: String,
|
#[serde(rename = "fontIndex")]
|
||||||
fontIndex: u32,
|
font_index: u32,
|
||||||
glyphs: Vec<PartitionGlyph>,
|
glyphs: Vec<PartitionGlyph>,
|
||||||
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)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
|
@ -182,6 +200,7 @@ struct PartitionEncodedPathData {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
enum PartitionFontError {
|
enum PartitionFontError {
|
||||||
|
UnknownBuiltinFont,
|
||||||
Base64DecodingFailed,
|
Base64DecodingFailed,
|
||||||
FontSanitizationFailed,
|
FontSanitizationFailed,
|
||||||
FontLoadingFailed,
|
FontLoadingFailed,
|
||||||
|
@ -312,26 +331,44 @@ fn partition_paths(partitioner: &mut Partitioner, subpath_indices: &[SubpathRang
|
||||||
#[post("/partition-font", format = "application/json", data = "<request>")]
|
#[post("/partition-font", format = "application/json", data = "<request>")]
|
||||||
fn partition_font(request: Json<PartitionFontRequest>)
|
fn partition_font(request: Json<PartitionFontRequest>)
|
||||||
-> Json<Result<PartitionFontResponse, PartitionFontError>> {
|
-> Json<Result<PartitionFontResponse, PartitionFontError>> {
|
||||||
// Decode Base64-encoded OTF data.
|
// Fetch the OTF data.
|
||||||
let unsafe_otf_data = match base64::decode(&request.otf) {
|
let otf_data = match request.face {
|
||||||
Ok(unsafe_otf_data) => unsafe_otf_data,
|
PartitionFontRequestFace::Builtin(ref builtin_font_name) => {
|
||||||
Err(_) => return Json(Err(PartitionFontError::Base64DecodingFailed)),
|
// 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.
|
// Sanitize.
|
||||||
let otf_data = match fontsan::process(&unsafe_otf_data) {
|
match fontsan::process(&unsafe_otf_data) {
|
||||||
Ok(otf_data) => otf_data,
|
Ok(otf_data) => otf_data,
|
||||||
Err(_) => return Json(Err(PartitionFontError::FontSanitizationFailed)),
|
Err(_) => return Json(Err(PartitionFontError::FontSanitizationFailed)),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse glyph data.
|
// Parse glyph data.
|
||||||
let font_key = FontKey::new();
|
let font_key = FontKey::new();
|
||||||
let font_instance_key = FontInstanceKey {
|
let font_instance_key = FontInstanceKey {
|
||||||
font_key: font_key,
|
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();
|
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))
|
return Json(Err(PartitionFontError::FontLoadingFailed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,15 +513,15 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
|
||||||
|
|
||||||
// Static files
|
// Static files
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn static_text_demo() -> io::Result<NamedFile> {
|
fn static_demo_text() -> io::Result<NamedFile> {
|
||||||
NamedFile::open(STATIC_TEXT_DEMO_PATH)
|
NamedFile::open(STATIC_TEXT_DEMO_PATH)
|
||||||
}
|
}
|
||||||
#[get("/demo/svg")]
|
#[get("/demo/svg")]
|
||||||
fn static_svg_demo() -> io::Result<NamedFile> {
|
fn static_demo_svg() -> io::Result<NamedFile> {
|
||||||
NamedFile::open(STATIC_SVG_DEMO_PATH)
|
NamedFile::open(STATIC_SVG_DEMO_PATH)
|
||||||
}
|
}
|
||||||
#[get("/demo/3d")]
|
#[get("/demo/3d")]
|
||||||
fn static_3d_demo() -> io::Result<NamedFile> {
|
fn static_demo_3d() -> io::Result<NamedFile> {
|
||||||
NamedFile::open(STATIC_3D_DEMO_PATH)
|
NamedFile::open(STATIC_3D_DEMO_PATH)
|
||||||
}
|
}
|
||||||
#[get("/css/bootstrap/<file..>")]
|
#[get("/css/bootstrap/<file..>")]
|
||||||
|
@ -523,6 +560,20 @@ fn static_svg_octicons(file: PathBuf) -> Option<NamedFile> {
|
||||||
fn static_glsl(file: PathBuf) -> Option<Shader> {
|
fn static_glsl(file: PathBuf) -> Option<Shader> {
|
||||||
Shader::open(Path::new(STATIC_GLSL_PATH).join(file)).ok()
|
Shader::open(Path::new(STATIC_GLSL_PATH).join(file)).ok()
|
||||||
}
|
}
|
||||||
|
#[get("/otf/demo/<font_name>")]
|
||||||
|
fn static_otf_demo(font_name: String) -> Option<NamedFile> {
|
||||||
|
BUILTIN_FONTS.iter()
|
||||||
|
.filter(|& &(name, _)| name == font_name)
|
||||||
|
.next()
|
||||||
|
.and_then(|&(_, path)| NamedFile::open(Path::new(path)).ok())
|
||||||
|
}
|
||||||
|
#[get("/svg/demo/<svg_name>")]
|
||||||
|
fn static_svg_demo(svg_name: String) -> Option<NamedFile> {
|
||||||
|
BUILTIN_SVGS.iter()
|
||||||
|
.filter(|& &(name, _)| name == svg_name)
|
||||||
|
.next()
|
||||||
|
.and_then(|&(_, path)| NamedFile::open(Path::new(path)).ok())
|
||||||
|
}
|
||||||
|
|
||||||
struct Shader {
|
struct Shader {
|
||||||
file: File,
|
file: File,
|
||||||
|
@ -546,9 +597,9 @@ fn main() {
|
||||||
rocket::ignite().mount("/", routes![
|
rocket::ignite().mount("/", routes![
|
||||||
partition_font,
|
partition_font,
|
||||||
partition_svg_paths,
|
partition_svg_paths,
|
||||||
static_text_demo,
|
static_demo_text,
|
||||||
static_svg_demo,
|
static_demo_svg,
|
||||||
static_3d_demo,
|
static_demo_3d,
|
||||||
static_css_bootstrap,
|
static_css_bootstrap,
|
||||||
static_css_octicons,
|
static_css_octicons,
|
||||||
static_css_pathfinder_css,
|
static_css_pathfinder_css,
|
||||||
|
@ -558,5 +609,7 @@ fn main() {
|
||||||
static_js_pathfinder,
|
static_js_pathfinder,
|
||||||
static_svg_octicons,
|
static_svg_octicons,
|
||||||
static_glsl,
|
static_glsl,
|
||||||
|
static_otf_demo,
|
||||||
|
static_svg_demo,
|
||||||
]).launch();
|
]).launch();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue