initial PDF support
This commit is contained in:
parent
e04f1b7950
commit
8d866bc9c8
File diff suppressed because it is too large
Load Diff
|
@ -69,3 +69,10 @@ default-members = [
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
pathfinder_geometry = { path = "geometry" }
|
pathfinder_geometry = { path = "geometry" }
|
||||||
pathfinder_simd = { path = "simd" }
|
pathfinder_simd = { path = "simd" }
|
||||||
|
|
||||||
|
[patch."https://github.com/s3bk/pathfinder"]
|
||||||
|
pathfinder_geometry = { path = "geometry" }
|
||||||
|
pathfinder_simd = { path = "simd" }
|
||||||
|
pathfinder_renderer = { path = "renderer" }
|
||||||
|
pathfinder_content = { path = "content" }
|
||||||
|
pathfinder_color = { path = "color" }
|
||||||
|
|
|
@ -54,6 +54,12 @@ path = "../../svg"
|
||||||
[dependencies.pathfinder_ui]
|
[dependencies.pathfinder_ui]
|
||||||
path = "../../ui"
|
path = "../../ui"
|
||||||
|
|
||||||
|
[dependencies.pdf]
|
||||||
|
git = "https://github.com/pdf-rs/pdf"
|
||||||
|
|
||||||
|
[dependencies.pdf_render]
|
||||||
|
git = "https://github.com/pdf-rs/pdf"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
metal = "0.18"
|
metal = "0.18"
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::camera::Camera;
|
||||||
use crate::concurrent::DemoExecutor;
|
use crate::concurrent::DemoExecutor;
|
||||||
use crate::device::{GroundProgram, GroundVertexArray};
|
use crate::device::{GroundProgram, GroundVertexArray};
|
||||||
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
|
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
|
||||||
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
|
use crate::window::{Event, Keycode, DataPath, Window, WindowSize};
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
|
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
|
||||||
use pathfinder_content::effects::PatternFilter;
|
use pathfinder_content::effects::PatternFilter;
|
||||||
|
@ -49,7 +49,9 @@ use std::io::{BufWriter, Read};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use usvg::{Options as UsvgOptions, Tree};
|
use usvg::{Options as UsvgOptions, Tree as SvgTree};
|
||||||
|
use pdf::file::File as PdfFile;
|
||||||
|
use pdf_render::Cache as PdfRenderCache;
|
||||||
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
||||||
use pathfinder_gl::GLDevice as DeviceImpl;
|
use pathfinder_gl::GLDevice as DeviceImpl;
|
||||||
|
@ -81,6 +83,11 @@ mod device;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
|
enum Content {
|
||||||
|
Svg(SvgTree),
|
||||||
|
Pdf(PdfFile<Vec<u8>>, PdfRenderCache, u32)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DemoApp<W> where W: Window {
|
pub struct DemoApp<W> where W: Window {
|
||||||
pub window: W,
|
pub window: W,
|
||||||
pub should_exit: bool,
|
pub should_exit: bool,
|
||||||
|
@ -88,7 +95,7 @@ pub struct DemoApp<W> where W: Window {
|
||||||
|
|
||||||
window_size: WindowSize,
|
window_size: WindowSize,
|
||||||
|
|
||||||
svg_tree: Tree,
|
content: Content,
|
||||||
scene_metadata: SceneMetadata,
|
scene_metadata: SceneMetadata,
|
||||||
render_transform: Option<RenderTransform>,
|
render_transform: Option<RenderTransform>,
|
||||||
|
|
||||||
|
@ -154,20 +161,18 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
|
|
||||||
let filter = build_filter(&ui_model);
|
let filter = build_filter(&ui_model);
|
||||||
|
|
||||||
let (mut built_svg, svg_tree) = load_scene(resources,
|
let viewport = window.viewport(options.mode.view(0));
|
||||||
&options.input_path,
|
let mut content = load_scene(resources, &options.input_path);
|
||||||
viewport.size(),
|
|
||||||
filter);
|
|
||||||
|
|
||||||
let message = get_svg_building_message(&built_svg);
|
let (mut scene, message) = content.render(viewport.size(), filter);
|
||||||
|
|
||||||
let renderer = Renderer::new(device, resources, render_mode, render_options);
|
let renderer = Renderer::new(device, resources, render_mode, render_options);
|
||||||
|
|
||||||
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene,
|
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut scene,
|
||||||
viewport.size());
|
viewport.size());
|
||||||
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
||||||
|
|
||||||
let scene_proxy = SceneProxy::from_scene(built_svg.scene, level, executor);
|
let scene_proxy = SceneProxy::from_scene(scene, level, executor);
|
||||||
|
|
||||||
let ground_program = GroundProgram::new(renderer.device(), resources);
|
let ground_program = GroundProgram::new(renderer.device(), resources);
|
||||||
let ground_vertex_array = GroundVertexArray::new(renderer.device(),
|
let ground_vertex_array = GroundVertexArray::new(renderer.device(),
|
||||||
|
@ -192,7 +197,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
|
|
||||||
window_size,
|
window_size,
|
||||||
|
|
||||||
svg_tree,
|
content,
|
||||||
scene_metadata,
|
scene_metadata,
|
||||||
render_transform: None,
|
render_transform: None,
|
||||||
|
|
||||||
|
@ -430,25 +435,22 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::OpenSVG(ref svg_path) => {
|
Event::OpenData(ref data_path) => {
|
||||||
let viewport = self.window.viewport(self.ui_model.mode.view(0));
|
let viewport = self.window.viewport(self.ui_model.mode.view(0));
|
||||||
let filter = build_filter(&self.ui_model);
|
let filter = build_filter(&self.ui_model);
|
||||||
let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(),
|
self.content = load_scene(self.window.resource_loader(), data_path);
|
||||||
svg_path,
|
|
||||||
viewport.size(),
|
|
||||||
filter);
|
|
||||||
|
|
||||||
self.ui_model.message = get_svg_building_message(&built_svg);
|
let (mut scene, message) = self.content.render(viewport.size(), filter);
|
||||||
|
self.ui_model.message = message;
|
||||||
|
|
||||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
||||||
self.scene_metadata =
|
self.scene_metadata =
|
||||||
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
|
SceneMetadata::new_clipping_view_box(&mut scene, viewport_size);
|
||||||
self.camera = Camera::new(self.ui_model.mode,
|
self.camera = Camera::new(self.ui_model.mode,
|
||||||
self.scene_metadata.view_box,
|
self.scene_metadata.view_box,
|
||||||
viewport_size);
|
viewport_size);
|
||||||
|
|
||||||
self.scene_proxy.replace_scene(built_svg.scene);
|
self.scene_proxy.replace_scene(scene);
|
||||||
self.svg_tree = svg_tree;
|
|
||||||
|
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -570,10 +572,10 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
UIAction::EffectsChanged => {
|
UIAction::EffectsChanged => {
|
||||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
||||||
let filter = build_filter(&self.ui_model);
|
let filter = build_filter(&self.ui_model);
|
||||||
let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, filter);
|
let (mut scene, message) = self.content.render(viewport_size, filter);
|
||||||
self.scene_metadata =
|
self.scene_metadata =
|
||||||
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
|
SceneMetadata::new_clipping_view_box(&mut scene, viewport_size);
|
||||||
self.scene_proxy.replace_scene(built_svg.scene);
|
self.scene_proxy.replace_scene(scene);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
UIAction::TakeScreenshot(ref info) => {
|
UIAction::TakeScreenshot(ref info) => {
|
||||||
|
@ -619,7 +621,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub jobs: Option<usize>,
|
pub jobs: Option<usize>,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub input_path: SVGPath,
|
pub input_path: DataPath,
|
||||||
pub ui: UIVisibility,
|
pub ui: UIVisibility,
|
||||||
pub background_color: BackgroundColor,
|
pub background_color: BackgroundColor,
|
||||||
pub high_performance_gpu: bool,
|
pub high_performance_gpu: bool,
|
||||||
|
@ -632,7 +634,7 @@ impl Default for Options {
|
||||||
Options {
|
Options {
|
||||||
jobs: None,
|
jobs: None,
|
||||||
mode: Mode::TwoD,
|
mode: Mode::TwoD,
|
||||||
input_path: SVGPath::Default,
|
input_path: DataPath::Default,
|
||||||
ui: UIVisibility::All,
|
ui: UIVisibility::All,
|
||||||
background_color: BackgroundColor::Light,
|
background_color: BackgroundColor::Light,
|
||||||
high_performance_gpu: false,
|
high_performance_gpu: false,
|
||||||
|
@ -743,8 +745,8 @@ impl Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = matches.value_of("INPUT") {
|
if let Some(path) = matches.value_of("INPUT") {
|
||||||
self.input_path = SVGPath::Path(PathBuf::from(path));
|
self.input_path = DataPath::Path(PathBuf::from(path));
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,30 +757,45 @@ pub enum UIVisibility {
|
||||||
All,
|
All,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_scene(resource_loader: &dyn ResourceLoader,
|
impl Content {
|
||||||
input_path: &SVGPath,
|
fn render(&mut self, viewport_size: Vector2I, filter: Option<PatternFilter>) -> (Scene, String) {
|
||||||
viewport_size: Vector2I,
|
match *self {
|
||||||
filter: Option<PatternFilter>)
|
Content::Svg(ref tree) => {
|
||||||
-> (SVGScene, Tree) {
|
let built_svg = build_svg_tree(&tree, viewport_size, filter);
|
||||||
let mut data;
|
let message = get_svg_building_message(&built_svg);
|
||||||
match *input_path {
|
(built_svg.scene, message)
|
||||||
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
|
||||||
SVGPath::Resource(ref name) => data = resource_loader.slurp(name).unwrap(),
|
|
||||||
SVGPath::Path(ref path) => {
|
|
||||||
data = vec![];
|
|
||||||
File::open(path).unwrap().read_to_end(&mut data).unwrap();
|
|
||||||
}
|
}
|
||||||
|
Content::Pdf(ref file, ref mut cache, page_nr) => {
|
||||||
|
let page = file.get_page(page_nr).expect("no such page");
|
||||||
|
let (scene, _) = cache.render_page(file, &page).unwrap();
|
||||||
|
(scene, String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_scene(resource_loader: &dyn ResourceLoader,
|
||||||
|
input_path: &DataPath,)
|
||||||
|
-> Content {
|
||||||
|
let data = match *input_path {
|
||||||
|
DataPath::Default => resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
||||||
|
DataPath::Resource(ref name) => resource_loader.slurp(name).unwrap(),
|
||||||
|
DataPath::Path(ref path) => std::fs::read(path).unwrap().into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!");
|
if let Ok(tree) = SvgTree::from_data(&data, &UsvgOptions::default()) {
|
||||||
let built_svg = build_svg_tree(&tree, viewport_size, filter);
|
Content::Svg(tree)
|
||||||
(built_svg, tree)
|
}
|
||||||
|
else if let Ok(pdf) = PdfFile::from_data(data) {
|
||||||
|
Content::Pdf(pdf, PdfRenderCache::new(), 0)
|
||||||
|
} else {
|
||||||
|
panic!("can't load data");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
|
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
|
||||||
// composite steps, breaking this approach.
|
// composite steps, breaking this approach.
|
||||||
fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFilter>)
|
fn build_svg_tree(tree: &SvgTree, viewport_size: Vector2I, filter: Option<PatternFilter>) -> SVGScene {
|
||||||
-> SVGScene {
|
|
||||||
let mut scene = Scene::new();
|
let mut scene = Scene::new();
|
||||||
let filter_info = filter.map(|filter| {
|
let filter_info = filter.map(|filter| {
|
||||||
let scale = match filter {
|
let scale = match filter {
|
||||||
|
|
|
@ -71,7 +71,7 @@ pub enum Event {
|
||||||
yaw: f32,
|
yaw: f32,
|
||||||
},
|
},
|
||||||
SetEyeTransforms(Vec<OcularTransform>),
|
SetEyeTransforms(Vec<OcularTransform>),
|
||||||
OpenSVG(SVGPath),
|
OpenData(DataPath),
|
||||||
User {
|
User {
|
||||||
message_type: u32,
|
message_type: u32,
|
||||||
message_data: u32,
|
message_data: u32,
|
||||||
|
@ -114,7 +114,7 @@ pub struct OcularTransform {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum SVGPath {
|
pub enum DataPath {
|
||||||
Default,
|
Default,
|
||||||
Resource(String),
|
Resource(String),
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
|
|
|
@ -18,7 +18,7 @@ extern crate objc;
|
||||||
|
|
||||||
use euclid::default::Size2D;
|
use euclid::default::Size2D;
|
||||||
use nfd::Response;
|
use nfd::Response;
|
||||||
use pathfinder_demo::window::{Event, Keycode, SVGPath, View, Window, WindowSize};
|
use pathfinder_demo::window::{Event, Keycode, DataPath, View, Window, WindowSize};
|
||||||
use pathfinder_demo::{DemoApp, Options};
|
use pathfinder_demo::{DemoApp, Options};
|
||||||
use pathfinder_geometry::rect::RectI;
|
use pathfinder_geometry::rect::RectI;
|
||||||
use pathfinder_geometry::vector::{Vector2I, vec2i};
|
use pathfinder_geometry::vector::{Vector2I, vec2i};
|
||||||
|
@ -140,7 +140,7 @@ struct EventQueue {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum CustomEvent {
|
enum CustomEvent {
|
||||||
User { message_type: u32, message_data: u32 },
|
User { message_type: u32, message_data: u32 },
|
||||||
OpenSVG(PathBuf),
|
OpenData(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window for WindowImpl {
|
impl Window for WindowImpl {
|
||||||
|
@ -212,10 +212,10 @@ impl Window for WindowImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn present_open_svg_dialog(&mut self) {
|
fn present_open_svg_dialog(&mut self) {
|
||||||
if let Ok(Response::Okay(path)) = nfd::open_file_dialog(Some("svg"), None) {
|
if let Ok(Response::Okay(path)) = nfd::open_file_dialog(Some("svg,pdf"), None) {
|
||||||
let mut event_queue = EVENT_QUEUE.lock().unwrap();
|
let mut event_queue = EVENT_QUEUE.lock().unwrap();
|
||||||
let event_queue = event_queue.as_mut().unwrap();
|
let event_queue = event_queue.as_mut().unwrap();
|
||||||
event_queue.pending_custom_events.push_back(CustomEvent::OpenSVG(PathBuf::from(path)));
|
event_queue.pending_custom_events.push_back(CustomEvent::OpenData(PathBuf::from(path)));
|
||||||
drop(event_queue.event_loop_proxy.wakeup());
|
drop(event_queue.event_loop_proxy.wakeup());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,7 +423,7 @@ fn convert_winit_event(winit_event: WinitEvent,
|
||||||
match event_queue.pending_custom_events
|
match event_queue.pending_custom_events
|
||||||
.pop_front()
|
.pop_front()
|
||||||
.expect("`Awakened` with no pending custom event!") {
|
.expect("`Awakened` with no pending custom event!") {
|
||||||
CustomEvent::OpenSVG(svg_path) => Some(Event::OpenSVG(SVGPath::Path(svg_path))),
|
CustomEvent::OpenData(data_path) => Some(Event::OpenData(DataPath::Path(data_path))),
|
||||||
CustomEvent::User { message_data, message_type } => {
|
CustomEvent::User { message_data, message_type } => {
|
||||||
Some(Event::User { message_data, message_type })
|
Some(Event::User { message_data, message_type })
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue