Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 11 additions & 7 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use rusttype::Font;
use image::{ImageBuffer, Rgba};
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{Theme, ThemeSet};
use rusttype::Font;
use std::path::Path;
use syntect::dumps::{from_binary};

use syntect::dumps::from_binary;
use syntect::highlighting::{Theme, ThemeSet};
use syntect::parsing::SyntaxSet;

pub struct AppState {
pub pastes: String,
Expand All @@ -15,14 +14,19 @@ pub struct AppState {
}

impl AppState {
pub fn new(pastes: String, base_img: ImageBuffer<Rgba<u8>, Vec<u8>>, font: Font<'static>) -> Self {
pub fn new(
pastes: String,
base_img: ImageBuffer<Rgba<u8>, Vec<u8>>,
font: Font<'static>,
) -> Self {
let theme_path = format!("{}/{}", &pastes, "TwoDark.tmTheme");
Self {
pastes,
base_img,
font,
syntaxes: from_binary::<SyntaxSet>(include_bytes!("../assets/syntaxes.bin")),
highlight_theme: ThemeSet::get_theme(Path::new(&theme_path)).unwrap_or_else(|_| panic!("Couldn't load the TwoDark theme!"))
highlight_theme: ThemeSet::get_theme(Path::new(&theme_path))
.unwrap_or_else(|_| panic!("Couldn't load the TwoDark theme!")),
}
}
}
10 changes: 4 additions & 6 deletions src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use rusttype::Scale;

pub const MARGINS: [i32; 18] = [
/* 1 2 3 4 5 6 7 8 9 */
56, 87, 118, 150, 182, 214, 248, 281, 314,
/* 10 11 12 13 14 15 16 17 18 */
346, 379, 412, 445, 478, 511, 544, 576, 607
pub const MARGINS: [i32; 33] = [
75, 91, 107, 123, 139, 155, 171, 187, 203, 219, 235, 251, 267, 283, 299, 315, 331, 347, 363,
379, 395, 411, 427, 443, 459, 475, 491, 507, 523, 539, 555, 571, 587,
];

pub const FONT_SCALE: Scale = Scale { x: 22., y: 22. };
pub const FONT_SCALE: Scale = Scale { x: 16., y: 16. };
26 changes: 19 additions & 7 deletions src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use crate::{app::AppState, syntax};
use tokio::task;
use tokio::fs;
use tokio::task;


async fn get_preview(state: &'static AppState, uuid: String, ext: Option<String>) -> Result<impl warp::Reply, warp::Rejection> {
async fn get_preview(
state: &'static AppState,
uuid: String,
ext: Option<String>,
) -> Result<impl warp::Reply, warp::Rejection> {
let f = format!("{}/{}", &state.pastes, uuid);
let code = fs::read_to_string(f).await.map_err(|_| warp::reject())?;

Expand All @@ -14,16 +17,25 @@ async fn get_preview(state: &'static AppState, uuid: String, ext: Option<String>
Err(warp::reject())
} else {
Ok(res.unwrap())
}
}).await.map_err(|_| warp::reject())?;
};
})
.await
.map_err(|_| warp::reject())?;

return rendered;
}

pub async fn root_handler_known(state: &'static AppState, uuid: String, ext: String) -> Result<impl warp::Reply, warp::Rejection> {
pub async fn root_handler_known(
state: &'static AppState,
uuid: String,
ext: String,
) -> Result<impl warp::Reply, warp::Rejection> {
get_preview(state, uuid, Some(ext)).await
}

pub async fn root_handler(state: &'static AppState, uuid: String) -> Result<impl warp::Reply, warp::Rejection> {
pub async fn root_handler(
state: &'static AppState,
uuid: String,
) -> Result<impl warp::Reply, warp::Rejection> {
get_preview(state, uuid, None).await
}
7 changes: 4 additions & 3 deletions src/ipri.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use rusttype::{point, Font, Scale};
use std::cmp::max;
use conv::ValueInto;
use image::Pixel;
use imageproc::definitions::Clamp;
use imageproc::drawing::Canvas;
use imageproc::pixelops::weighted_sum;
use rusttype::{point, Font, Scale};
use std::cmp::max;

// custom draw_text forked from improc
pub fn draw_text_mut_w<'a, C>(
Expand All @@ -15,7 +15,8 @@ pub fn draw_text_mut_w<'a, C>(
scale: Scale,
font: &'a Font<'a>,
text: &'a str,
) -> i32 where
) -> i32
where
C: Canvas,
<C::Pixel as Pixel>::Subpixel: ValueInto<f32> + Clamp<f32>,
{
Expand Down
31 changes: 18 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ mod utils;
use rusttype::Font;
use warp::Filter;

use warp::http::header::{HeaderMap, HeaderValue};
use std::env;
use warp::http::header::{HeaderMap, HeaderValue};

#[tokio::main]
async fn main() {
Expand All @@ -23,27 +23,32 @@ async fn main() {
png_header.insert("Content-type", HeaderValue::from_static("image/png"));

let image = image::open(format!("{}/{}", arg, "editor.png"))
.expect("ERR No template image found at provided path")
.to_rgba8();
.expect("ERR No template image found at provided path")
.to_rgba8();

let font: Font<'static> = Font::try_from_bytes(include_bytes!("FiraCode-Retina.ttf") as &[u8]).unwrap();
let font: Font<'static> =
Font::try_from_bytes(include_bytes!("FiraCode-Retina.ttf") as &[u8]).unwrap();

utils::init_state(arg, image, font);

println!("Starting warp server on port 3030");

let p = warp::path("p");
let ap = utils::with_state()
.and(warp::path::param())
.and_then(handlers::root_handler);
.and(warp::path::param())
.and_then(handlers::root_handler);

let pk = warp::path("pk");
let apk = utils::with_state()
.and(warp::path::param())
.and(warp::path::param())
.and_then(handlers::root_handler_known);

warp::serve(p.and(ap).or(pk.and(apk)).with(warp::reply::with::headers(png_header)))
.run(([127, 0, 0, 1], 3030))
.await;
.and(warp::path::param())
.and(warp::path::param())
.and_then(handlers::root_handler_known);

warp::serve(
p.and(ap)
.or(pk.and(apk))
.with(warp::reply::with::headers(png_header)),
)
.run(([127, 0, 0, 1], 3030))
.await;
}
32 changes: 24 additions & 8 deletions src/syntax.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
use crate::{ipri, utils, app::AppState, consts::{MARGINS, FONT_SCALE}};
use crate::{
app::AppState,
consts::{FONT_SCALE, MARGINS},
ipri, utils,
};
use image::Rgba;
use syntect::easy::HighlightLines;
use syntect::highlighting::Color;
use image::Rgba;

fn to_rgba8(color: &Color) -> Rgba<u8> {
Rgba::<u8>([color.r, color.g, color.b, color.a])
}

pub fn render_preview(state: &AppState, src: &str, ext: Option<String>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
pub fn render_preview(
state: &AppState,
src: &str,
ext: Option<String>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let syntax = ext
.map(|e| state.syntaxes.find_syntax_by_extension(&e))
.flatten()
.unwrap_or_else(|| &state.syntaxes.find_syntax_plain_text());

let mut h = HighlightLines::new(syntax, &state.highlight_theme);
let lines = src.split_inclusive("\n").take(18);
let lines = src.split_inclusive("\n").take(33);

let mut img2 = state.base_img.clone();

for (i, line) in lines.enumerate() {
let highlighted = h.highlight(&line, &state.syntaxes);
let mut w = 64;
let mut w = 40;
for (style, chunk) in highlighted.iter() {
let mut isspace = true; // whether the chunk is purely whitespace
let mut sc = 0; // number of spaces in chunk
Expand All @@ -43,11 +51,19 @@ pub fn render_preview(state: &AppState, src: &str, ext: Option<String>) -> Resul
}).collect();

if isspace {
w += utils::space_width()*sc;
w += utils::space_width() * sc;
} else {
w += ipri::draw_text_mut_w(&mut img2, to_rgba8(&style.foreground), w, MARGINS[i], FONT_SCALE, &state.font, &c_string);
w += ipri::draw_text_mut_w(
&mut img2,
to_rgba8(&style.foreground),
w,
MARGINS[i],
FONT_SCALE,
&state.font,
&c_string,
);
let trailing_spaces = c_s - cindx - 1;
w += utils::space_width()*trailing_spaces as i32; // rusttype doesn't handle trailing spaces :|
w += utils::space_width() * trailing_spaces as i32; // rusttype doesn't handle trailing spaces :|
}
}
}
Expand Down
24 changes: 17 additions & 7 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
use crate::{app::AppState, consts};
use std::ops::Deref;
use image::{png::PngEncoder, ImageBuffer, Rgba, ImageError, Pixel};
use image::{png::PngEncoder, ImageBuffer, ImageError, Pixel, Rgba};
use once_cell::sync::OnceCell;
use warp::Filter;
use rusttype::{point, Font};
use std::ops::Deref;
use warp::Filter;

static STATE: OnceCell<AppState> = OnceCell::new();
static SPACE_WIDTH: OnceCell<i32> = OnceCell::new();


// rusttype can't rasterize spaces :|
fn init_space_width(font: &Font<'static>) {
let offset = point(0.0, font.v_metrics(consts::FONT_SCALE).ascent);
let layout = font.layout(" r", consts::FONT_SCALE, offset);
let r = font.glyph('r').scaled(consts::FONT_SCALE).positioned(offset).pixel_bounding_box().unwrap().max.x;
let r = font
.glyph('r')
.scaled(consts::FONT_SCALE)
.positioned(offset)
.pixel_bounding_box()
.unwrap()
.max
.x;
let width = layout.last().unwrap().pixel_bounding_box().unwrap().max.x - r;
SPACE_WIDTH.set(width).ok().expect("INVALID INIT STATE!!");
}

pub fn init_state(pastes: String, base_img: ImageBuffer<Rgba<u8>, Vec<u8>>, font: Font<'static>) {
init_space_width(&font);
STATE.set(AppState::new(pastes, base_img, font)).ok().expect("INVALID INIT STATE!!");
STATE
.set(AppState::new(pastes, base_img, font))
.ok()
.expect("INVALID INIT STATE!!");
}

pub fn space_width() -> i32 {
*SPACE_WIDTH.get().unwrap()
}

pub fn with_state() -> impl Filter<Extract = (&'static AppState,), Error = std::convert::Infallible> + Clone {
pub fn with_state(
) -> impl Filter<Extract = (&'static AppState,), Error = std::convert::Infallible> + Clone {
warp::any().map(|| STATE.get().expect("AppState not initialized!!!"))
}

Expand Down