Render reference images on the server, untested as of yet
This commit is contained in:
parent
75474b19e6
commit
c9948a844d
|
@ -8,12 +8,31 @@
|
||||||
</head>
|
</head>
|
||||||
<body class="pf-unscrollable">
|
<body class="pf-unscrollable">
|
||||||
{{>partials/navbar.html isTool=true}}
|
{{>partials/navbar.html isTool=true}}
|
||||||
<div id="pf-test-pane">
|
<div class="container-fluid">
|
||||||
<form>
|
<div class="row py-3">
|
||||||
Font
|
<div id="pf-test-pane" class="col">
|
||||||
</form>
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pf-select-file">Font</label>
|
||||||
|
<select id="pf-select-file" class="form-control custom-select">
|
||||||
|
<option value="open-sans">Open Sans</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pf-font-size">Font Size</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="pf-font-size" type="number" class="form-control"
|
||||||
|
value="32">
|
||||||
|
<div class="input-group-addon">px</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<canvas id="pf-reference-pane" class="pf-draggable col" width="300" height="400">
|
||||||
|
</canvas>
|
||||||
|
<canvas id="pf-rendering-pane" class="pf-draggable col" width="300" height="400">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="pf-center-canvas" class="pf-draggable" width="300" height="400"></canvas>
|
|
||||||
<canvas id="pf-right-canvas" class="pf-draggable" width="300" height="400"></canvas>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -9,6 +9,7 @@ base64 = "0.6"
|
||||||
bincode = "0.8"
|
bincode = "0.8"
|
||||||
env_logger = "0.4"
|
env_logger = "0.4"
|
||||||
euclid = "0.15"
|
euclid = "0.15"
|
||||||
|
image = "0.17"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
lru-cache = "0.1"
|
lru-cache = "0.1"
|
||||||
|
|
|
@ -16,6 +16,7 @@ extern crate base64;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate euclid;
|
extern crate euclid;
|
||||||
extern crate fontsan;
|
extern crate fontsan;
|
||||||
|
extern crate image;
|
||||||
extern crate lru_cache;
|
extern crate lru_cache;
|
||||||
extern crate pathfinder_font_renderer;
|
extern crate pathfinder_font_renderer;
|
||||||
extern crate pathfinder_partitioner;
|
extern crate pathfinder_partitioner;
|
||||||
|
@ -30,6 +31,7 @@ extern crate serde_derive;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use euclid::{Point2D, Transform2D};
|
use euclid::{Point2D, Transform2D};
|
||||||
|
use image::{DynamicImage, ImageBuffer, ImageFormat, ImageLuma8};
|
||||||
use lru_cache::LruCache;
|
use lru_cache::LruCache;
|
||||||
use pathfinder_font_renderer::{FontContext, FontInstance, FontKey, GlyphKey, SubpixelOffset};
|
use pathfinder_font_renderer::{FontContext, FontInstance, FontKey, GlyphKey, SubpixelOffset};
|
||||||
use pathfinder_partitioner::mesh_library::MeshLibrary;
|
use pathfinder_partitioner::mesh_library::MeshLibrary;
|
||||||
|
@ -104,7 +106,7 @@ struct MeshLibraryCacheKey {
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
struct PartitionFontRequest {
|
struct PartitionFontRequest {
|
||||||
face: PartitionFontRequestFace,
|
face: FontRequestFace,
|
||||||
#[serde(rename = "fontIndex")]
|
#[serde(rename = "fontIndex")]
|
||||||
font_index: u32,
|
font_index: u32,
|
||||||
glyphs: Vec<PartitionGlyph>,
|
glyphs: Vec<PartitionGlyph>,
|
||||||
|
@ -113,13 +115,24 @@ struct PartitionFontRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
enum PartitionFontRequestFace {
|
enum FontRequestFace {
|
||||||
/// One of the builtin fonts in `BUILTIN_FONTS`.
|
/// One of the builtin fonts in `BUILTIN_FONTS`.
|
||||||
Builtin(String),
|
Builtin(String),
|
||||||
/// Base64-encoded OTF data.
|
/// Base64-encoded OTF data.
|
||||||
Custom(String),
|
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)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
struct PartitionGlyph {
|
struct PartitionGlyph {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
@ -133,11 +146,12 @@ struct PartitionFontResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
enum PartitionFontError {
|
enum FontError {
|
||||||
UnknownBuiltinFont,
|
UnknownBuiltinFont,
|
||||||
Base64DecodingFailed,
|
Base64DecodingFailed,
|
||||||
FontSanitizationFailed,
|
FontSanitizationFailed,
|
||||||
FontLoadingFailed,
|
FontLoadingFailed,
|
||||||
|
RasterizationFailed,
|
||||||
Unimplemented,
|
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<Response<'r>, 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<FontKey, FontError> {
|
||||||
|
// 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 = "<request>")]
|
#[post("/partition-font", format = "application/json", data = "<request>")]
|
||||||
fn partition_font(request: Json<PartitionFontRequest>)
|
fn partition_font(request: Json<PartitionFontRequest>) -> Result<PartitionResponder, FontError> {
|
||||||
-> Result<PartitionResponder, PartitionFontError> {
|
// Check the cache.
|
||||||
let cache_key = match request.face {
|
let cache_key = match request.face {
|
||||||
PartitionFontRequestFace::Builtin(ref builtin_font_name) => {
|
FontRequestFace::Builtin(ref builtin_font_name) => {
|
||||||
Some(MeshLibraryCacheKey {
|
Some(MeshLibraryCacheKey {
|
||||||
builtin_font_name: (*builtin_font_name).clone(),
|
builtin_font_name: (*builtin_font_name).clone(),
|
||||||
glyph_ids: request.glyphs.iter().map(|glyph| glyph.id).collect(),
|
glyph_ids: request.glyphs.iter().map(|glyph| glyph.id).collect(),
|
||||||
|
@ -242,53 +317,21 @@ fn partition_font(request: Json<PartitionFontRequest>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// 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() {
|
let mut font_context = match FontContext::new() {
|
||||||
Ok(font_context) => font_context,
|
Ok(font_context) => font_context,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Failed to create a font context!");
|
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() {
|
let font_key = try!(add_font_from_request(&mut font_context,
|
||||||
println!("Failed to add font from memory!");
|
&request.face,
|
||||||
return Err(PartitionFontError::FontLoadingFailed)
|
request.font_index));
|
||||||
}
|
let font_instance = FontInstance {
|
||||||
|
font_key: font_key,
|
||||||
|
size: Au::from_f64_px(request.point_size),
|
||||||
|
};
|
||||||
|
|
||||||
// Read glyph info.
|
// Read glyph info.
|
||||||
let mut path_buffer = PathBuffer::new();
|
let mut path_buffer = PathBuffer::new();
|
||||||
|
@ -417,6 +460,41 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/render-reference", format = "application/json", data = "<request>")]
|
||||||
|
fn render_reference(request: Json<RenderReferenceRequest>)
|
||||||
|
-> Result<ReferenceImage, FontError> {
|
||||||
|
// 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
|
// Static files
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn static_index() -> io::Result<NamedFile> {
|
fn static_index() -> io::Result<NamedFile> {
|
||||||
|
@ -553,6 +631,7 @@ fn main() {
|
||||||
rocket.mount("/", routes![
|
rocket.mount("/", routes![
|
||||||
partition_font,
|
partition_font,
|
||||||
partition_svg_paths,
|
partition_svg_paths,
|
||||||
|
render_reference,
|
||||||
static_index,
|
static_index,
|
||||||
static_demo_text,
|
static_demo_text,
|
||||||
static_demo_svg,
|
static_demo_svg,
|
||||||
|
|
Loading…
Reference in New Issue