Compare commits
No commits in common. "7e71cb947bd5b515e64f5aa3e217a76124568a61" and "30a0f1beb7fde5f9cac156e9cb57df2dbaa60fca" have entirely different histories.
7e71cb947b
...
30a0f1beb7
@ -14,18 +14,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@geoffcox/svelte-splitter": "^1.0.1",
|
"@geoffcox/svelte-splitter": "^1.0.1",
|
||||||
"@monaco-editor/loader": "^1.4.0",
|
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-dialog": "~2",
|
"@tauri-apps/plugin-dialog": "~2",
|
||||||
"@tauri-apps/plugin-fs": "~2",
|
"@tauri-apps/plugin-fs": "~2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"flowbite-svelte": "^0.47.4",
|
"flowbite-svelte": "^0.47.4",
|
||||||
"flowbite-svelte-icons": "^2.0.2",
|
"flowbite-svelte-icons": "^2.0.2",
|
||||||
"monaco-editor": "^0.52.2",
|
|
||||||
"paths": "^0.1.1",
|
"paths": "^0.1.1",
|
||||||
"svelte-split-pane": "^0.1.2",
|
"svelte-split-pane": "^0.1.2",
|
||||||
"svelte-splitpanes": "^8.0.9",
|
"svelte-splitpanes": "^8.0.9"
|
||||||
"vite-plugin-monaco-editor": "^1.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-static": "^3.0.6",
|
"@sveltejs/adapter-static": "^3.0.6",
|
||||||
@ -41,6 +38,6 @@
|
|||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "~5.6.2",
|
"typescript": "~5.6.2",
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
//! Models of PDF types
|
//! Models of PDF types
|
||||||
|
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
use datasize::DataSize;
|
use datasize::DataSize;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate as pdf;
|
use crate as pdf;
|
||||||
use crate::content::deep_clone_op;
|
use crate::content::deep_clone_op;
|
||||||
use crate::content::{parse_ops, serialize_ops, Content, FormXObject, Matrix, Op};
|
|
||||||
use crate::enc::StreamFilter;
|
|
||||||
use crate::error::*;
|
|
||||||
use crate::font::Font;
|
|
||||||
use crate::object::*;
|
use crate::object::*;
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::content::{Content, FormXObject, Matrix, parse_ops, serialize_ops, Op};
|
||||||
|
use crate::font::Font;
|
||||||
|
use crate::enc::StreamFilter;
|
||||||
|
|
||||||
/// Node in a page tree - type is either `Page` or `PageTree`
|
/// Node in a page tree - type is either `Page` or `PageTree`
|
||||||
#[derive(Debug, Clone, DataSize)]
|
#[derive(Debug, Clone, DataSize)]
|
||||||
@ -24,10 +24,7 @@ impl Object for PagesNode {
|
|||||||
match dict.require("PagesNode", "Type")?.as_name()? {
|
match dict.require("PagesNode", "Type")?.as_name()? {
|
||||||
"Page" => Ok(PagesNode::Leaf(t!(Page::from_dict(dict, resolve)))),
|
"Page" => Ok(PagesNode::Leaf(t!(Page::from_dict(dict, resolve)))),
|
||||||
"Pages" => Ok(PagesNode::Tree(t!(PageTree::from_dict(dict, resolve)))),
|
"Pages" => Ok(PagesNode::Tree(t!(PageTree::from_dict(dict, resolve)))),
|
||||||
other => Err(PdfError::WrongDictionaryType {
|
other => Err(PdfError::WrongDictionaryType {expected: "Page or Pages".into(), found: other.into()}),
|
||||||
expected: "Page or Pages".into(),
|
|
||||||
found: other.into(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +60,7 @@ impl Deref for PageRc {
|
|||||||
fn deref(&self) -> &Page {
|
fn deref(&self) -> &Page {
|
||||||
match *self.0 {
|
match *self.0 {
|
||||||
PagesNode::Leaf(ref page) => page,
|
PagesNode::Leaf(ref page) => page,
|
||||||
_ => unreachable!(),
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,11 +76,8 @@ impl Object for PageRc {
|
|||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<PageRc> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<PageRc> {
|
||||||
let node = t!(RcRef::from_primitive(p, resolve));
|
let node = t!(RcRef::from_primitive(p, resolve));
|
||||||
match *node {
|
match *node {
|
||||||
PagesNode::Tree(_) => Err(PdfError::WrongDictionaryType {
|
PagesNode::Tree(_) => Err(PdfError::WrongDictionaryType {expected: "Page".into(), found: "Pages".into()}),
|
||||||
expected: "Page".into(),
|
PagesNode::Leaf(_) => Ok(PageRc(node))
|
||||||
found: "Pages".into(),
|
|
||||||
}),
|
|
||||||
PagesNode::Leaf(_) => Ok(PageRc(node)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +96,7 @@ impl Deref for PagesRc {
|
|||||||
fn deref(&self) -> &PageTree {
|
fn deref(&self) -> &PageTree {
|
||||||
match *self.0 {
|
match *self.0 {
|
||||||
PagesNode::Tree(ref tree) => tree,
|
PagesNode::Tree(ref tree) => tree,
|
||||||
_ => unreachable!(),
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,11 +109,8 @@ impl Object for PagesRc {
|
|||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<PagesRc> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<PagesRc> {
|
||||||
let node = t!(RcRef::from_primitive(p, resolve));
|
let node = t!(RcRef::from_primitive(p, resolve));
|
||||||
match *node {
|
match *node {
|
||||||
PagesNode::Leaf(_) => Err(PdfError::WrongDictionaryType {
|
PagesNode::Leaf(_) => Err(PdfError::WrongDictionaryType {expected: "Pages".into(), found: "Page".into()}),
|
||||||
expected: "Pages".into(),
|
PagesNode::Tree(_) => Ok(PagesRc(node))
|
||||||
found: "Page".into(),
|
|
||||||
}),
|
|
||||||
PagesNode::Tree(_) => Ok(PagesRc(node)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,6 +141,7 @@ pub struct Catalog {
|
|||||||
// ViewerPreferences: dict
|
// ViewerPreferences: dict
|
||||||
// PageLayout: name
|
// PageLayout: name
|
||||||
// PageMode: name
|
// PageMode: name
|
||||||
|
|
||||||
#[pdf(key="Outlines")]
|
#[pdf(key="Outlines")]
|
||||||
pub outlines: Option<Outlines>,
|
pub outlines: Option<Outlines>,
|
||||||
// Threads: array
|
// Threads: array
|
||||||
@ -166,6 +158,7 @@ pub struct Catalog {
|
|||||||
|
|
||||||
#[pdf(key="StructTreeRoot")]
|
#[pdf(key="StructTreeRoot")]
|
||||||
pub struct_tree_root: Option<StructTreeRoot>,
|
pub struct_tree_root: Option<StructTreeRoot>,
|
||||||
|
|
||||||
// MarkInfo: dict
|
// MarkInfo: dict
|
||||||
// Lang: text string
|
// Lang: text string
|
||||||
// SpiderInfo: dict
|
// SpiderInfo: dict
|
||||||
@ -308,14 +301,13 @@ pub struct Page {
|
|||||||
pub other: Dictionary,
|
pub other: Dictionary,
|
||||||
}
|
}
|
||||||
fn inherit<'a, T: 'a, F>(mut parent: &'a PageTree, f: F) -> Result<Option<T>>
|
fn inherit<'a, T: 'a, F>(mut parent: &'a PageTree, f: F) -> Result<Option<T>>
|
||||||
where
|
where F: Fn(&'a PageTree) -> Option<T>
|
||||||
F: Fn(&'a PageTree) -> Option<T>,
|
|
||||||
{
|
{
|
||||||
loop {
|
loop {
|
||||||
match (&parent.parent, f(parent)) {
|
match (&parent.parent, f(parent)) {
|
||||||
(_, Some(t)) => return Ok(Some(t)),
|
(_, Some(t)) => return Ok(Some(t)),
|
||||||
(Some(ref p), None) => parent = p,
|
(Some(ref p), None) => parent = p,
|
||||||
(None, None) => return Ok(None),
|
(None, None) => return Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -340,12 +332,8 @@ impl Page {
|
|||||||
pub fn media_box(&self) -> Result<Rectangle> {
|
pub fn media_box(&self) -> Result<Rectangle> {
|
||||||
match self.media_box {
|
match self.media_box {
|
||||||
Some(b) => Ok(b),
|
Some(b) => Ok(b),
|
||||||
None => {
|
None => inherit(&self.parent, |pt| pt.media_box)?
|
||||||
inherit(&self.parent, |pt| pt.media_box)?.ok_or_else(|| PdfError::MissingEntry {
|
.ok_or_else(|| PdfError::MissingEntry { typ: "Page", field: "MediaBox".into() })
|
||||||
typ: "Page",
|
|
||||||
field: "MediaBox".into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn crop_box(&self) -> Result<Rectangle> {
|
pub fn crop_box(&self) -> Result<Rectangle> {
|
||||||
@ -353,24 +341,21 @@ impl Page {
|
|||||||
Some(b) => Ok(b),
|
Some(b) => Ok(b),
|
||||||
None => match inherit(&self.parent, |pt| pt.crop_box)? {
|
None => match inherit(&self.parent, |pt| pt.crop_box)? {
|
||||||
Some(b) => Ok(b),
|
Some(b) => Ok(b),
|
||||||
None => self.media_box(),
|
None => self.media_box()
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn resources(&self) -> Result<&MaybeRef<Resources>> {
|
pub fn resources(&self) -> Result<&MaybeRef<Resources>> {
|
||||||
match self.resources {
|
match self.resources {
|
||||||
Some(ref r) => Ok(r),
|
Some(ref r) => Ok(r),
|
||||||
None => inherit(&self.parent, |pt| pt.resources.as_ref())?.ok_or_else(|| {
|
None => inherit(&self.parent, |pt| pt.resources.as_ref())?
|
||||||
PdfError::MissingEntry {
|
.ok_or_else(|| PdfError::MissingEntry { typ: "Page", field: "Resources".into() })
|
||||||
typ: "Page",
|
|
||||||
field: "Resources".into(),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SubType<PagesNode> for Page {}
|
impl SubType<PagesNode> for Page {}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Object, DataSize, Debug, ObjectWrite)]
|
#[derive(Object, DataSize, Debug, ObjectWrite)]
|
||||||
pub struct PageLabel {
|
pub struct PageLabel {
|
||||||
#[pdf(key="S")]
|
#[pdf(key="S")]
|
||||||
@ -380,7 +365,7 @@ pub struct PageLabel {
|
|||||||
pub prefix: Option<PdfString>,
|
pub prefix: Option<PdfString>,
|
||||||
|
|
||||||
#[pdf(key="St")]
|
#[pdf(key="St")]
|
||||||
pub start: Option<usize>,
|
pub start: Option<usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug, DataSize, Default, DeepClone, Clone)]
|
#[derive(Object, ObjectWrite, Debug, DataSize, Default, DeepClone, Clone)]
|
||||||
@ -405,6 +390,7 @@ pub struct Resources {
|
|||||||
pub properties: HashMap<Name, MaybeRef<Dictionary>>,
|
pub properties: HashMap<Name, MaybeRef<Dictionary>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Object, ObjectWrite, DataSize, Clone, DeepClone)]
|
#[derive(Debug, Object, ObjectWrite, DataSize, Clone, DeepClone)]
|
||||||
pub struct PatternDict {
|
pub struct PatternDict {
|
||||||
#[pdf(key="PaintType")]
|
#[pdf(key="PaintType")]
|
||||||
@ -446,9 +432,7 @@ impl Object for Pattern {
|
|||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
||||||
let p = p.resolve(resolve)?;
|
let p = p.resolve(resolve)?;
|
||||||
match p {
|
match p {
|
||||||
Primitive::Dictionary(dict) => {
|
Primitive::Dictionary(dict) => Ok(Pattern::Dict(PatternDict::from_dict(dict, resolve)?)),
|
||||||
Ok(Pattern::Dict(PatternDict::from_dict(dict, resolve)?))
|
|
||||||
}
|
|
||||||
Primitive::Stream(s) => {
|
Primitive::Stream(s) => {
|
||||||
let stream: Stream<PatternDict> = Stream::from_stream(s, resolve)?;
|
let stream: Stream<PatternDict> = Stream::from_stream(s, resolve)?;
|
||||||
let data = stream.data(resolve)?;
|
let data = stream.data(resolve)?;
|
||||||
@ -456,10 +440,7 @@ impl Object for Pattern {
|
|||||||
let dict = stream.info.info;
|
let dict = stream.info.info;
|
||||||
Ok(Pattern::Stream(dict, ops))
|
Ok(Pattern::Stream(dict, ops))
|
||||||
}
|
}
|
||||||
p => Err(PdfError::UnexpectedPrimitive {
|
p => Err(PdfError::UnexpectedPrimitive { expected: "Dictionary or Stream", found: p.get_debug_name() })
|
||||||
expected: "Dictionary or Stream",
|
|
||||||
found: p.get_debug_name(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,10 +463,7 @@ impl DeepClone for Pattern {
|
|||||||
Pattern::Stream(ref dict, ref ops) => {
|
Pattern::Stream(ref dict, ref ops) => {
|
||||||
let old_resources = cloner.get(dict.resources)?;
|
let old_resources = cloner.get(dict.resources)?;
|
||||||
let mut resources = Resources::default();
|
let mut resources = Resources::default();
|
||||||
let ops: Vec<Op> = ops
|
let ops: Vec<Op> = ops.iter().map(|op| deep_clone_op(op, cloner, &old_resources, &mut resources)).collect::<Result<Vec<_>>>()?;
|
||||||
.iter()
|
|
||||||
.map(|op| deep_clone_op(op, cloner, &old_resources, &mut resources))
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
let dict = PatternDict {
|
let dict = PatternDict {
|
||||||
resources: cloner.create(resources)?.get_ref(),
|
resources: cloner.create(resources)?.get_ref(),
|
||||||
.. *dict
|
.. *dict
|
||||||
@ -500,13 +478,13 @@ impl DeepClone for Pattern {
|
|||||||
pub enum LineCap {
|
pub enum LineCap {
|
||||||
Butt = 0,
|
Butt = 0,
|
||||||
Round = 1,
|
Round = 1,
|
||||||
Square = 2,
|
Square = 2
|
||||||
}
|
}
|
||||||
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Copy, Clone)]
|
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Copy, Clone)]
|
||||||
pub enum LineJoin {
|
pub enum LineJoin {
|
||||||
Miter = 0,
|
Miter = 0,
|
||||||
Round = 1,
|
Round = 1,
|
||||||
Bevel = 2,
|
Bevel = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Clone)]
|
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Clone)]
|
||||||
@ -553,12 +531,14 @@ pub struct GraphicsStateParameters {
|
|||||||
// FL
|
// FL
|
||||||
// SM
|
// SM
|
||||||
// SA
|
// SA
|
||||||
|
|
||||||
#[pdf(key="BM")]
|
#[pdf(key="BM")]
|
||||||
pub blend_mode: Option<Primitive>,
|
pub blend_mode: Option<Primitive>,
|
||||||
|
|
||||||
#[pdf(key="SMask")]
|
#[pdf(key="SMask")]
|
||||||
pub smask: Option<Primitive>,
|
pub smask: Option<Primitive>,
|
||||||
|
|
||||||
|
|
||||||
#[pdf(key="CA")]
|
#[pdf(key="CA")]
|
||||||
pub stroke_alpha: Option<f32>,
|
pub stroke_alpha: Option<f32>,
|
||||||
|
|
||||||
@ -572,7 +552,7 @@ pub struct GraphicsStateParameters {
|
|||||||
pub text_knockout: Option<bool>,
|
pub text_knockout: Option<bool>,
|
||||||
|
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
_other: Dictionary,
|
_other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, Debug, DataSize, DeepClone)]
|
#[derive(Object, Debug, DataSize, DeepClone)]
|
||||||
@ -601,7 +581,7 @@ pub type PostScriptXObject = Stream<PostScriptDict>;
|
|||||||
|
|
||||||
#[derive(Debug, DataSize, Clone, DeepClone)]
|
#[derive(Debug, DataSize, Clone, DeepClone)]
|
||||||
pub struct ImageXObject {
|
pub struct ImageXObject {
|
||||||
pub inner: Stream<ImageDict>,
|
pub inner: Stream<ImageDict>
|
||||||
}
|
}
|
||||||
impl Object for ImageXObject {
|
impl Object for ImageXObject {
|
||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
||||||
@ -627,7 +607,7 @@ pub enum ImageFormat {
|
|||||||
Jp2k,
|
Jp2k,
|
||||||
Jbig2,
|
Jbig2,
|
||||||
CittFax,
|
CittFax,
|
||||||
Png,
|
Png
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageXObject {
|
impl ImageXObject {
|
||||||
@ -637,38 +617,32 @@ impl ImageXObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decode everything except for the final image encoding (jpeg, jbig2, jp2k, ...)
|
/// Decode everything except for the final image encoding (jpeg, jbig2, jp2k, ...)
|
||||||
pub fn raw_image_data(
|
pub fn raw_image_data(&self, resolve: &impl Resolve) -> Result<(Arc<[u8]>, Option<&StreamFilter>)> {
|
||||||
&self,
|
|
||||||
resolve: &impl Resolve,
|
|
||||||
) -> Result<(Arc<[u8]>, Option<&StreamFilter>)> {
|
|
||||||
match self.inner.inner_data {
|
match self.inner.inner_data {
|
||||||
StreamData::Generated(_) => Ok((self.inner.data(resolve)?, None)),
|
StreamData::Generated(_) => Ok((self.inner.data(resolve)?, None)),
|
||||||
StreamData::Original(ref file_range, id) => {
|
StreamData::Original(ref file_range, id) => {
|
||||||
let filters = self.inner.filters.as_slice();
|
let filters = self.inner.filters.as_slice();
|
||||||
// decode all non image filters
|
// decode all non image filters
|
||||||
let end = filters
|
let end = filters.iter().rposition(|f| match f {
|
||||||
.iter()
|
|
||||||
.rposition(|f| match f {
|
|
||||||
StreamFilter::ASCIIHexDecode => false,
|
StreamFilter::ASCIIHexDecode => false,
|
||||||
StreamFilter::ASCII85Decode => false,
|
StreamFilter::ASCII85Decode => false,
|
||||||
StreamFilter::LZWDecode(_) => false,
|
StreamFilter::LZWDecode(_) => false,
|
||||||
StreamFilter::RunLengthDecode => false,
|
StreamFilter::RunLengthDecode => false,
|
||||||
StreamFilter::Crypt => true,
|
StreamFilter::Crypt => true,
|
||||||
_ => true,
|
_ => true
|
||||||
})
|
}).unwrap_or(filters.len());
|
||||||
.unwrap_or(filters.len());
|
|
||||||
|
|
||||||
let (normal_filters, image_filters) = filters.split_at(end);
|
let (normal_filters, image_filters) = filters.split_at(end);
|
||||||
let data = resolve.get_data_or_decode(id, file_range.clone(), normal_filters)?;
|
let data = resolve.get_data_or_decode(id, file_range.clone(), normal_filters)?;
|
||||||
|
|
||||||
match image_filters {
|
match image_filters {
|
||||||
[] => Ok((data, None)),
|
[] => Ok((data, None)),
|
||||||
[StreamFilter::DCTDecode(_)]
|
[StreamFilter::DCTDecode(_)] |
|
||||||
| [StreamFilter::CCITTFaxDecode(_)]
|
[StreamFilter::CCITTFaxDecode(_)] |
|
||||||
| [StreamFilter::JPXDecode]
|
[StreamFilter::JPXDecode] |
|
||||||
| [StreamFilter::FlateDecode(_)]
|
[StreamFilter::FlateDecode(_)] |
|
||||||
| [StreamFilter::JBIG2Decode(_)] => Ok((data, Some(&image_filters[0]))),
|
[StreamFilter::JBIG2Decode(_)] => Ok((data, Some(&image_filters[0]))),
|
||||||
_ => bail!("??? filters={:?}", image_filters),
|
_ => bail!("??? filters={:?}", image_filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,16 +652,12 @@ impl ImageXObject {
|
|||||||
let (data, filter) = self.raw_image_data(resolve)?;
|
let (data, filter) = self.raw_image_data(resolve)?;
|
||||||
let filter = match filter {
|
let filter = match filter {
|
||||||
Some(f) => f,
|
Some(f) => f,
|
||||||
None => return Ok(data),
|
None => return Ok(data)
|
||||||
};
|
};
|
||||||
let mut data = match filter {
|
let mut data = match filter {
|
||||||
StreamFilter::CCITTFaxDecode(ref params) => {
|
StreamFilter::CCITTFaxDecode(ref params) => {
|
||||||
if self.inner.info.width != params.columns {
|
if self.inner.info.width != params.columns {
|
||||||
bail!(
|
bail!("image width mismatch {} != {}", self.inner.info.width, params.columns);
|
||||||
"image width mismatch {} != {}",
|
|
||||||
self.inner.info.width,
|
|
||||||
params.columns
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let mut data = fax_decode(&data, params)?;
|
let mut data = fax_decode(&data, params)?;
|
||||||
if params.rows == 0 {
|
if params.rows == 0 {
|
||||||
@ -701,9 +671,9 @@ impl ImageXObject {
|
|||||||
StreamFilter::JBIG2Decode(ref p) => {
|
StreamFilter::JBIG2Decode(ref p) => {
|
||||||
let global_data = p.globals.as_ref().map(|s| s.data(resolve)).transpose()?;
|
let global_data = p.globals.as_ref().map(|s| s.data(resolve)).transpose()?;
|
||||||
jbig2_decode(&data, global_data.as_deref().unwrap_or_default())?
|
jbig2_decode(&data, global_data.as_deref().unwrap_or_default())?
|
||||||
}
|
},
|
||||||
StreamFilter::FlateDecode(ref p) => flate_decode(&data, p)?,
|
StreamFilter::FlateDecode(ref p) => flate_decode(&data, p)?,
|
||||||
_ => unreachable!(),
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
if let Some(ref decode) = self.decode {
|
if let Some(ref decode) = self.decode {
|
||||||
if decode == &[1.0, 0.0] && self.bits_per_component == Some(1) {
|
if decode == &[1.0, 0.0] && self.bits_per_component == Some(1) {
|
||||||
@ -719,7 +689,7 @@ impl ImageXObject {
|
|||||||
pub struct PostScriptDict {
|
pub struct PostScriptDict {
|
||||||
// TODO
|
// TODO
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
pub other: Dictionary,
|
pub other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, Debug, Clone, DataSize, DeepClone, ObjectWrite, Default)]
|
#[derive(Object, Debug, Clone, DataSize, DeepClone, ObjectWrite, Default)]
|
||||||
@ -737,10 +707,12 @@ pub struct ImageDict {
|
|||||||
#[pdf(key="BitsPerComponent")]
|
#[pdf(key="BitsPerComponent")]
|
||||||
pub bits_per_component: Option<i32>,
|
pub bits_per_component: Option<i32>,
|
||||||
// Note: only allowed values are 1, 2, 4, 8, 16. Enum?
|
// Note: only allowed values are 1, 2, 4, 8, 16. Enum?
|
||||||
|
|
||||||
#[pdf(key="Intent")]
|
#[pdf(key="Intent")]
|
||||||
pub intent: Option<RenderingIntent>,
|
pub intent: Option<RenderingIntent>,
|
||||||
// Note: default: "the current rendering intent in the graphics state" - I don't think this
|
// Note: default: "the current rendering intent in the graphics state" - I don't think this
|
||||||
// ought to have a default then
|
// ought to have a default then
|
||||||
|
|
||||||
#[pdf(key="ImageMask", default="false")]
|
#[pdf(key="ImageMask", default="false")]
|
||||||
pub image_mask: bool,
|
pub image_mask: bool,
|
||||||
|
|
||||||
@ -775,10 +747,12 @@ pub struct ImageDict {
|
|||||||
// OPI: dict
|
// OPI: dict
|
||||||
// Metadata: stream
|
// Metadata: stream
|
||||||
// OC: dict
|
// OC: dict
|
||||||
|
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
pub other: Dictionary,
|
pub other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Object, Debug, Copy, Clone, DataSize, DeepClone, ObjectWrite)]
|
#[derive(Object, Debug, Copy, Clone, DataSize, DeepClone, ObjectWrite)]
|
||||||
pub enum RenderingIntent {
|
pub enum RenderingIntent {
|
||||||
AbsoluteColorimetric,
|
AbsoluteColorimetric,
|
||||||
@ -793,7 +767,7 @@ impl RenderingIntent {
|
|||||||
"RelativeColorimetric" => Some(RenderingIntent::RelativeColorimetric),
|
"RelativeColorimetric" => Some(RenderingIntent::RelativeColorimetric),
|
||||||
"Perceptual" => Some(RenderingIntent::Perceptual),
|
"Perceptual" => Some(RenderingIntent::Perceptual),
|
||||||
"Saturation" => Some(RenderingIntent::Saturation),
|
"Saturation" => Some(RenderingIntent::Saturation),
|
||||||
_ => None,
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn to_str(self) -> &'static str {
|
pub fn to_str(self) -> &'static str {
|
||||||
@ -852,6 +826,7 @@ pub struct FormDict {
|
|||||||
pub other: Dictionary,
|
pub other: Dictionary,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug, Clone, DataSize)]
|
#[derive(Object, ObjectWrite, Debug, Clone, DataSize)]
|
||||||
pub struct InteractiveFormDictionary {
|
pub struct InteractiveFormDictionary {
|
||||||
#[pdf(key="Fields")]
|
#[pdf(key="Fields")]
|
||||||
@ -907,7 +882,7 @@ pub struct SeedValueDictionary {
|
|||||||
#[pdf(key="DigestMethod")]
|
#[pdf(key="DigestMethod")]
|
||||||
pub digest_method: Vec<PdfString>,
|
pub digest_method: Vec<PdfString>,
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
pub other: Dictionary,
|
pub other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug)]
|
#[derive(Object, ObjectWrite, Debug)]
|
||||||
@ -946,7 +921,7 @@ pub struct SignatureDictionary {
|
|||||||
#[pdf(key="Prop_AuthType")]
|
#[pdf(key="Prop_AuthType")]
|
||||||
pub prop_auth_type: Name,
|
pub prop_auth_type: Name,
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
pub other: Dictionary,
|
pub other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug)]
|
#[derive(Object, ObjectWrite, Debug)]
|
||||||
@ -961,9 +936,10 @@ pub struct SignatureReferenceDictionary {
|
|||||||
#[pdf(key="DigestMethod")]
|
#[pdf(key="DigestMethod")]
|
||||||
pub digest_method: Option<Name>,
|
pub digest_method: Option<Name>,
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
pub other: Dictionary,
|
pub other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug, Clone, DataSize)]
|
#[derive(Object, ObjectWrite, Debug, Clone, DataSize)]
|
||||||
#[pdf(Type="Annot?")]
|
#[pdf(Type="Annot?")]
|
||||||
pub struct Annot {
|
pub struct Annot {
|
||||||
@ -1055,7 +1031,7 @@ pub struct FieldDictionary {
|
|||||||
pub subtype: Option<Name>,
|
pub subtype: Option<Name>,
|
||||||
|
|
||||||
#[pdf(other)]
|
#[pdf(other)]
|
||||||
pub other: Dictionary,
|
pub other: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug, DataSize, Clone, DeepClone)]
|
#[derive(Object, ObjectWrite, Debug, DataSize, Clone, DeepClone)]
|
||||||
@ -1073,21 +1049,14 @@ pub struct AppearanceStreams {
|
|||||||
#[derive(Clone, Debug, DeepClone)]
|
#[derive(Clone, Debug, DeepClone)]
|
||||||
pub enum AppearanceStreamEntry {
|
pub enum AppearanceStreamEntry {
|
||||||
Single(FormXObject),
|
Single(FormXObject),
|
||||||
Dict(HashMap<Name, AppearanceStreamEntry>),
|
Dict(HashMap<Name, AppearanceStreamEntry>)
|
||||||
}
|
}
|
||||||
impl Object for AppearanceStreamEntry {
|
impl Object for AppearanceStreamEntry {
|
||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
||||||
match p.resolve(resolve)? {
|
match p.resolve(resolve)? {
|
||||||
p @ Primitive::Dictionary(_) => {
|
p @ Primitive::Dictionary(_) => Object::from_primitive(p, resolve).map(AppearanceStreamEntry::Dict),
|
||||||
Object::from_primitive(p, resolve).map(AppearanceStreamEntry::Dict)
|
p @ Primitive::Stream(_) => Object::from_primitive(p, resolve).map(AppearanceStreamEntry::Single),
|
||||||
}
|
p => Err(PdfError::UnexpectedPrimitive {expected: "Dict or Stream", found: p.get_debug_name()})
|
||||||
p @ Primitive::Stream(_) => {
|
|
||||||
Object::from_primitive(p, resolve).map(AppearanceStreamEntry::Single)
|
|
||||||
}
|
|
||||||
p => Err(PdfError::UnexpectedPrimitive {
|
|
||||||
expected: "Dict or Stream",
|
|
||||||
found: p.get_debug_name(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1105,7 +1074,7 @@ impl DataSize for AppearanceStreamEntry {
|
|||||||
fn estimate_heap_size(&self) -> usize {
|
fn estimate_heap_size(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
AppearanceStreamEntry::Dict(d) => d.estimate_heap_size(),
|
AppearanceStreamEntry::Dict(d) => d.estimate_heap_size(),
|
||||||
AppearanceStreamEntry::Single(s) => s.estimate_heap_size(),
|
AppearanceStreamEntry::Single(s) => s.estimate_heap_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1121,7 +1090,7 @@ pub enum Counter {
|
|||||||
#[pdf(name="a")]
|
#[pdf(name="a")]
|
||||||
AlphaUpper,
|
AlphaUpper,
|
||||||
#[pdf(name="A")]
|
#[pdf(name="A")]
|
||||||
AlphaLower,
|
AlphaLower
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, DataSize)]
|
#[derive(Debug, DataSize)]
|
||||||
@ -1129,7 +1098,8 @@ pub enum NameTreeNode<T> {
|
|||||||
///
|
///
|
||||||
Intermediate (Vec<Ref<NameTree<T>>>),
|
Intermediate (Vec<Ref<NameTree<T>>>),
|
||||||
///
|
///
|
||||||
Leaf(Vec<(PdfString, T)>),
|
Leaf (Vec<(PdfString, T)>)
|
||||||
|
|
||||||
}
|
}
|
||||||
/// Note: The PDF concept of 'root' node is an intermediate or leaf node which has no 'Limits'
|
/// Note: The PDF concept of 'root' node is an intermediate or leaf node which has no 'Limits'
|
||||||
/// entry. Hence, `limits`,
|
/// entry. Hence, `limits`,
|
||||||
@ -1139,11 +1109,7 @@ pub struct NameTree<T> {
|
|||||||
pub node: NameTreeNode<T>,
|
pub node: NameTreeNode<T>,
|
||||||
}
|
}
|
||||||
impl<T: Object+DataSize> NameTree<T> {
|
impl<T: Object+DataSize> NameTree<T> {
|
||||||
pub fn walk(
|
pub fn walk(&self, r: &impl Resolve, callback: &mut dyn FnMut(&PdfString, &T)) -> Result<(), PdfError> {
|
||||||
&self,
|
|
||||||
r: &impl Resolve,
|
|
||||||
callback: &mut dyn FnMut(&PdfString, &T),
|
|
||||||
) -> Result<(), PdfError> {
|
|
||||||
match self.node {
|
match self.node {
|
||||||
NameTreeNode::Leaf(ref items) => {
|
NameTreeNode::Leaf(ref items) => {
|
||||||
for (name, val) in items {
|
for (name, val) in items {
|
||||||
@ -1176,7 +1142,7 @@ impl<T: Object> Object for NameTree<T> {
|
|||||||
|
|
||||||
Some((min, max))
|
Some((min, max))
|
||||||
}
|
}
|
||||||
None => None,
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let kids = dict.remove("Kids");
|
let kids = dict.remove("Kids");
|
||||||
@ -1184,15 +1150,12 @@ impl<T: Object> Object for NameTree<T> {
|
|||||||
// If no `kids`, try `names`. Else there is an error.
|
// If no `kids`, try `names`. Else there is an error.
|
||||||
Ok(match (kids, names) {
|
Ok(match (kids, names) {
|
||||||
(Some(kids), _) => {
|
(Some(kids), _) => {
|
||||||
let kids = t!(kids
|
let kids = t!(kids.resolve(resolve)?.into_array()?.iter().map(|kid|
|
||||||
.resolve(resolve)?
|
Ref::<NameTree<T>>::from_primitive(kid.clone(), resolve)
|
||||||
.into_array()?
|
).collect::<Result<Vec<_>>>());
|
||||||
.iter()
|
|
||||||
.map(|kid| Ref::<NameTree<T>>::from_primitive(kid.clone(), resolve))
|
|
||||||
.collect::<Result<Vec<_>>>());
|
|
||||||
NameTree {
|
NameTree {
|
||||||
limits,
|
limits,
|
||||||
node: NameTreeNode::Intermediate(kids),
|
node: NameTreeNode::Intermediate (kids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(None, Some(names)) => {
|
(None, Some(names)) => {
|
||||||
@ -1212,7 +1175,7 @@ impl<T: Object> Object for NameTree<T> {
|
|||||||
warn!("Neither Kids nor Names present in NameTree node.");
|
warn!("Neither Kids nor Names present in NameTree node.");
|
||||||
NameTree {
|
NameTree {
|
||||||
limits,
|
limits,
|
||||||
node: NameTreeNode::Intermediate(vec![]),
|
node: NameTreeNode::Intermediate(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1251,22 +1214,19 @@ impl<T: Object> Object for NumberTree<T> {
|
|||||||
|
|
||||||
Some((min, max))
|
Some((min, max))
|
||||||
}
|
}
|
||||||
None => None,
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let kids = dict.remove("Kids");
|
let kids = dict.remove("Kids");
|
||||||
let nums = dict.remove("Nums");
|
let nums = dict.remove("Nums");
|
||||||
match (kids, nums) {
|
match (kids, nums) {
|
||||||
(Some(kids), _) => {
|
(Some(kids), _) => {
|
||||||
let kids = t!(kids
|
let kids = t!(kids.resolve(resolve)?.into_array()?.iter().map(|kid|
|
||||||
.resolve(resolve)?
|
Ref::<NumberTree<T>>::from_primitive(kid.clone(), resolve)
|
||||||
.into_array()?
|
).collect::<Result<Vec<_>>>());
|
||||||
.iter()
|
|
||||||
.map(|kid| Ref::<NumberTree<T>>::from_primitive(kid.clone(), resolve))
|
|
||||||
.collect::<Result<Vec<_>>>());
|
|
||||||
Ok(NumberTree {
|
Ok(NumberTree {
|
||||||
limits,
|
limits,
|
||||||
node: NumberTreeNode::Intermediate(kids),
|
node: NumberTreeNode::Intermediate (kids)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
(None, Some(nums)) => {
|
(None, Some(nums)) => {
|
||||||
@ -1279,14 +1239,14 @@ impl<T: Object> Object for NumberTree<T> {
|
|||||||
}
|
}
|
||||||
Ok(NumberTree {
|
Ok(NumberTree {
|
||||||
limits,
|
limits,
|
||||||
node: NumberTreeNode::Leaf(items),
|
node: NumberTreeNode::Leaf(items)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
warn!("Neither Kids nor Names present in NumberTree node.");
|
warn!("Neither Kids nor Names present in NumberTree node.");
|
||||||
Ok(NumberTree {
|
Ok(NumberTree {
|
||||||
limits,
|
limits,
|
||||||
node: NumberTreeNode::Intermediate(vec![]),
|
node: NumberTreeNode::Intermediate(vec![])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1308,21 +1268,14 @@ impl<T: ObjectWrite> ObjectWrite for NumberTree<T> {
|
|||||||
dict.insert("Nums", nums);
|
dict.insert("Nums", nums);
|
||||||
}
|
}
|
||||||
NumberTreeNode::Intermediate(ref kids) => {
|
NumberTreeNode::Intermediate(ref kids) => {
|
||||||
dict.insert(
|
dict.insert("Kids", kids.iter().map(|r| r.get_inner().into()).collect_vec());
|
||||||
"Kids",
|
|
||||||
kids.iter().map(|r| r.get_inner().into()).collect_vec(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(dict.into())
|
Ok(dict.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Object+DataSize> NumberTree<T> {
|
impl<T: Object+DataSize> NumberTree<T> {
|
||||||
pub fn walk(
|
pub fn walk(&self, r: &impl Resolve, callback: &mut dyn FnMut(i32, &T)) -> Result<(), PdfError> {
|
||||||
&self,
|
|
||||||
r: &impl Resolve,
|
|
||||||
callback: &mut dyn FnMut(i32, &T),
|
|
||||||
) -> Result<(), PdfError> {
|
|
||||||
match self.node {
|
match self.node {
|
||||||
NumberTreeNode::Leaf(ref items) => {
|
NumberTreeNode::Leaf(ref items) => {
|
||||||
for &(idx, ref val) in items {
|
for &(idx, ref val) in items {
|
||||||
@ -1355,23 +1308,13 @@ pub struct LageLabel {
|
|||||||
#[derive(Debug, Clone, DataSize)]
|
#[derive(Debug, Clone, DataSize)]
|
||||||
pub enum DestView {
|
pub enum DestView {
|
||||||
// left, top, zoom
|
// left, top, zoom
|
||||||
XYZ {
|
XYZ { left: Option<f32>, top: Option<f32>, zoom: f32 },
|
||||||
left: Option<f32>,
|
|
||||||
top: Option<f32>,
|
|
||||||
zoom: f32,
|
|
||||||
},
|
|
||||||
Fit,
|
Fit,
|
||||||
FitH {
|
FitH { top: f32 },
|
||||||
top: f32,
|
FitV { left: f32 },
|
||||||
},
|
|
||||||
FitV {
|
|
||||||
left: f32,
|
|
||||||
},
|
|
||||||
FitR(Rectangle),
|
FitR(Rectangle),
|
||||||
FitB,
|
FitB,
|
||||||
FitBH {
|
FitBH { top: f32 }
|
||||||
top: f32,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, DataSize)]
|
#[derive(Debug, Clone, DataSize)]
|
||||||
@ -1383,17 +1326,17 @@ pub enum MaybeNamedDest {
|
|||||||
#[derive(Debug, Clone, DataSize)]
|
#[derive(Debug, Clone, DataSize)]
|
||||||
pub struct Dest {
|
pub struct Dest {
|
||||||
pub page: Option<Ref<Page>>,
|
pub page: Option<Ref<Page>>,
|
||||||
pub view: DestView,
|
pub view: DestView
|
||||||
}
|
}
|
||||||
impl Object for Dest {
|
impl Object for Dest {
|
||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
||||||
let p = match p {
|
let p = match p {
|
||||||
Primitive::Reference(r) => resolve.resolve(r)?,
|
Primitive::Reference(r) => resolve.resolve(r)?,
|
||||||
p => p,
|
p => p
|
||||||
};
|
};
|
||||||
let p = match p {
|
let p = match p {
|
||||||
Primitive::Dictionary(mut dict) => dict.require("Dest", "D")?,
|
Primitive::Dictionary(mut dict) => dict.require("Dest", "D")?,
|
||||||
p => p,
|
p => p
|
||||||
};
|
};
|
||||||
let array = t!(p.as_array(), p);
|
let array = t!(p.as_array(), p);
|
||||||
Dest::from_array(array, resolve)
|
Dest::from_array(array, resolve)
|
||||||
@ -1409,43 +1352,28 @@ impl Dest {
|
|||||||
Primitive::Null => None,
|
Primitive::Null => None,
|
||||||
Primitive::Integer(n) => Some(n as f32),
|
Primitive::Integer(n) => Some(n as f32),
|
||||||
Primitive::Number(f) => Some(f),
|
Primitive::Number(f) => Some(f),
|
||||||
ref p => {
|
ref p => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
|
||||||
return Err(PdfError::UnexpectedPrimitive {
|
|
||||||
expected: "Number | Integer | Null",
|
|
||||||
found: p.get_debug_name(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
top: match *try_opt!(array.get(3)) {
|
top: match *try_opt!(array.get(3)) {
|
||||||
Primitive::Null => None,
|
Primitive::Null => None,
|
||||||
Primitive::Integer(n) => Some(n as f32),
|
Primitive::Integer(n) => Some(n as f32),
|
||||||
Primitive::Number(f) => Some(f),
|
Primitive::Number(f) => Some(f),
|
||||||
ref p => {
|
ref p => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
|
||||||
return Err(PdfError::UnexpectedPrimitive {
|
|
||||||
expected: "Number | Integer | Null",
|
|
||||||
found: p.get_debug_name(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
zoom: match array.get(4) {
|
zoom: match array.get(4) {
|
||||||
Some(Primitive::Null) => 0.0,
|
Some(Primitive::Null) => 0.0,
|
||||||
Some(&Primitive::Integer(n)) => n as f32,
|
Some(&Primitive::Integer(n)) => n as f32,
|
||||||
Some(&Primitive::Number(f)) => f,
|
Some(&Primitive::Number(f)) => f,
|
||||||
Some(p) => {
|
Some(p) => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
|
||||||
return Err(PdfError::UnexpectedPrimitive {
|
|
||||||
expected: "Number | Integer | Null",
|
|
||||||
found: p.get_debug_name(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => 0.0,
|
None => 0.0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Fit" => DestView::Fit,
|
"Fit" => DestView::Fit,
|
||||||
"FitH" => DestView::FitH {
|
"FitH" => DestView::FitH {
|
||||||
top: try_opt!(array.get(2)).as_number()?,
|
top: try_opt!(array.get(2)).as_number()?
|
||||||
},
|
},
|
||||||
"FitV" => DestView::FitV {
|
"FitV" => DestView::FitV {
|
||||||
left: try_opt!(array.get(2)).as_number()?,
|
left: try_opt!(array.get(2)).as_number()?
|
||||||
},
|
},
|
||||||
"FitR" => DestView::FitR(Rectangle {
|
"FitR" => DestView::FitR(Rectangle {
|
||||||
left: try_opt!(array.get(2)).as_number()?,
|
left: try_opt!(array.get(2)).as_number()?,
|
||||||
@ -1455,28 +1383,26 @@ impl Dest {
|
|||||||
}),
|
}),
|
||||||
"FitB" => DestView::FitB,
|
"FitB" => DestView::FitB,
|
||||||
"FitBH" => DestView::FitBH {
|
"FitBH" => DestView::FitBH {
|
||||||
top: try_opt!(array.get(2)).as_number()?,
|
top: try_opt!(array.get(2)).as_number()?
|
||||||
},
|
},
|
||||||
name => {
|
name => return Err(PdfError::UnknownVariant { id: "Dest", name: name.into() })
|
||||||
return Err(PdfError::UnknownVariant {
|
|
||||||
id: "Dest",
|
|
||||||
name: name.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(Dest { page, view })
|
Ok(Dest {
|
||||||
|
page,
|
||||||
|
view
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Object for MaybeNamedDest {
|
impl Object for MaybeNamedDest {
|
||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
||||||
let p = match p {
|
let p = match p {
|
||||||
Primitive::Reference(r) => resolve.resolve(r)?,
|
Primitive::Reference(r) => resolve.resolve(r)?,
|
||||||
p => p,
|
p => p
|
||||||
};
|
};
|
||||||
let p = match p {
|
let p = match p {
|
||||||
Primitive::Dictionary(mut dict) => dict.require("Dest", "D")?,
|
Primitive::Dictionary(mut dict) => dict.require("Dest", "D")?,
|
||||||
Primitive::String(s) => return Ok(MaybeNamedDest::Named(s)),
|
Primitive::String(s) => return Ok(MaybeNamedDest::Named(s)),
|
||||||
p => p,
|
p => p
|
||||||
};
|
};
|
||||||
let array = t!(p.as_array(), p);
|
let array = t!(p.as_array(), p);
|
||||||
Dest::from_array(array, resolve).map(MaybeNamedDest::Direct)
|
Dest::from_array(array, resolve).map(MaybeNamedDest::Direct)
|
||||||
@ -1486,7 +1412,7 @@ impl ObjectWrite for MaybeNamedDest {
|
|||||||
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
|
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
|
||||||
match self {
|
match self {
|
||||||
MaybeNamedDest::Named(s) => Ok(Primitive::String(s.clone())),
|
MaybeNamedDest::Named(s) => Ok(Primitive::String(s.clone())),
|
||||||
MaybeNamedDest::Direct(d) => d.to_primitive(update),
|
MaybeNamedDest::Direct(d) => d.to_primitive(update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1600,28 +1526,13 @@ impl<T: DataSize> DataSize for Files<T> {
|
|||||||
const STATIC_HEAP_SIZE: usize = 5 * Option::<T>::STATIC_HEAP_SIZE;
|
const STATIC_HEAP_SIZE: usize = 5 * Option::<T>::STATIC_HEAP_SIZE;
|
||||||
|
|
||||||
fn estimate_heap_size(&self) -> usize {
|
fn estimate_heap_size(&self) -> usize {
|
||||||
self.f.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0)
|
self.f.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
|
||||||
+ self
|
self.uf.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
|
||||||
.uf
|
self.dos.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
|
||||||
.as_ref()
|
self.mac.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
|
||||||
.map(|t| t.estimate_heap_size())
|
self.unix.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0)
|
||||||
.unwrap_or(0)
|
|
||||||
+ self
|
|
||||||
.dos
|
|
||||||
.as_ref()
|
|
||||||
.map(|t| t.estimate_heap_size())
|
|
||||||
.unwrap_or(0)
|
|
||||||
+ self
|
|
||||||
.mac
|
|
||||||
.as_ref()
|
|
||||||
.map(|t| t.estimate_heap_size())
|
|
||||||
.unwrap_or(0)
|
|
||||||
+ self
|
|
||||||
.unix
|
|
||||||
.as_ref()
|
|
||||||
.map(|t| t.estimate_heap_size())
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PDF Embedded File Stream.
|
/// PDF Embedded File Stream.
|
||||||
@ -1691,7 +1602,7 @@ pub struct OutlineItem {
|
|||||||
#[derive(Clone, Debug, DataSize)]
|
#[derive(Clone, Debug, DataSize)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Goto(MaybeNamedDest),
|
Goto(MaybeNamedDest),
|
||||||
Other(Dictionary),
|
Other(Dictionary)
|
||||||
}
|
}
|
||||||
impl Object for Action {
|
impl Object for Action {
|
||||||
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
|
||||||
@ -1699,13 +1610,10 @@ impl Object for Action {
|
|||||||
let s = try_opt!(d.get("S")).as_name()?;
|
let s = try_opt!(d.get("S")).as_name()?;
|
||||||
match s {
|
match s {
|
||||||
"GoTo" => {
|
"GoTo" => {
|
||||||
let dest = t!(MaybeNamedDest::from_primitive(
|
let dest = t!(MaybeNamedDest::from_primitive(try_opt!(d.remove("D")), resolve));
|
||||||
try_opt!(d.remove("D")),
|
|
||||||
resolve
|
|
||||||
));
|
|
||||||
Ok(Action::Goto(dest))
|
Ok(Action::Goto(dest))
|
||||||
}
|
}
|
||||||
_ => Ok(Action::Other(d)),
|
_ => Ok(Action::Other(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1717,7 +1625,7 @@ impl ObjectWrite for Action {
|
|||||||
dict.insert("D", dest.to_primitive(update)?);
|
dict.insert("D", dest.to_primitive(update)?);
|
||||||
Ok(Primitive::Dictionary(dict))
|
Ok(Primitive::Dictionary(dict))
|
||||||
}
|
}
|
||||||
Action::Other(dict) => Ok(Primitive::Dictionary(dict.clone())),
|
Action::Other(dict) => Ok(Primitive::Dictionary(dict.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1733,6 +1641,7 @@ pub struct Outlines {
|
|||||||
|
|
||||||
#[pdf(key="Last")]
|
#[pdf(key="Last")]
|
||||||
pub last: Option<Ref<OutlineItem>>,
|
pub last: Option<Ref<OutlineItem>>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ISO 32000-2:2020(E) 7.9.5 Rectangles (Pg 134)
|
/// ISO 32000-2:2020(E) 7.9.5 Rectangles (Pg 134)
|
||||||
@ -1764,24 +1673,21 @@ impl Object for Rectangle {
|
|||||||
left: arr[0].as_number()?,
|
left: arr[0].as_number()?,
|
||||||
bottom: arr[1].as_number()?,
|
bottom: arr[1].as_number()?,
|
||||||
right: arr[2].as_number()?,
|
right: arr[2].as_number()?,
|
||||||
top: arr[3].as_number()?,
|
top: arr[3].as_number()?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ObjectWrite for Rectangle {
|
impl ObjectWrite for Rectangle {
|
||||||
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
|
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
|
||||||
Primitive::array::<f32, _, _, _>(
|
Primitive::array::<f32, _, _, _>([self.left, self.bottom, self.right, self.top].iter(), update)
|
||||||
[self.left, self.bottom, self.right, self.top].iter(),
|
|
||||||
update,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Stuff from chapter 10 of the PDF 1.7 ref
|
// Stuff from chapter 10 of the PDF 1.7 ref
|
||||||
|
|
||||||
#[derive(Object, ObjectWrite, Debug, DataSize)]
|
#[derive(Object, ObjectWrite, Debug, DataSize)]
|
||||||
pub struct MarkInformation {
|
pub struct MarkInformation { // TODO no /Type
|
||||||
// TODO no /Type
|
|
||||||
/// indicating whether the document conforms to Tagged PDF conventions
|
/// indicating whether the document conforms to Tagged PDF conventions
|
||||||
#[pdf(key="Marked", default="false")]
|
#[pdf(key="Marked", default="false")]
|
||||||
pub marked: bool,
|
pub marked: bool,
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
use std::ops::{Deref, Range, RangeFrom};
|
|
||||||
/// Lexing an input file, in the sense of breaking it up into substrings based on delimiters and
|
/// Lexing an input file, in the sense of breaking it up into substrings based on delimiters and
|
||||||
/// whitespace.
|
/// whitespace.
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::ops::{Range, Deref, RangeFrom};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::primitive::Name;
|
use crate::primitive::Name;
|
||||||
|
|
||||||
mod str;
|
mod str;
|
||||||
pub use self::str::{HexStringLexer, StringLexer};
|
pub use self::str::{StringLexer, HexStringLexer};
|
||||||
|
|
||||||
|
|
||||||
/// `Lexer` has functionality to jump around and traverse the PDF lexemes of a string in any direction.
|
/// `Lexer` has functionality to jump around and traverse the PDF lexemes of a string in any direction.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -24,7 +26,7 @@ pub struct Lexer<'a> {
|
|||||||
fn boundary_rev(data: &[u8], pos: usize, condition: impl Fn(u8) -> bool) -> usize {
|
fn boundary_rev(data: &[u8], pos: usize, condition: impl Fn(u8) -> bool) -> usize {
|
||||||
match data[.. pos].iter().rposition(|&b| !condition(b)) {
|
match data[.. pos].iter().rposition(|&b| !condition(b)) {
|
||||||
Some(start) => start + 1,
|
Some(start) => start + 1,
|
||||||
None => 0,
|
None => 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ fn boundary_rev(data: &[u8], pos: usize, condition: impl Fn(u8) -> bool) -> usiz
|
|||||||
fn boundary(data: &[u8], pos: usize, condition: impl Fn(u8) -> bool) -> usize {
|
fn boundary(data: &[u8], pos: usize, condition: impl Fn(u8) -> bool) -> usize {
|
||||||
match data[pos ..].iter().position(|&b| !condition(b)) {
|
match data[pos ..].iter().position(|&b| !condition(b)) {
|
||||||
Some(start) => pos + start,
|
Some(start) => pos + start,
|
||||||
None => data.len(),
|
None => data.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,14 +52,14 @@ impl<'a> Lexer<'a> {
|
|||||||
Lexer {
|
Lexer {
|
||||||
pos: 0,
|
pos: 0,
|
||||||
buf,
|
buf,
|
||||||
file_offset: 0,
|
file_offset: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_offset(buf: &'a [u8], file_offset: usize) -> Lexer<'a> {
|
pub fn with_offset(buf: &'a [u8], file_offset: usize) -> Lexer<'a> {
|
||||||
Lexer {
|
Lexer {
|
||||||
pos: 0,
|
pos: 0,
|
||||||
buf,
|
buf,
|
||||||
file_offset,
|
file_offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +112,7 @@ impl<'a> Lexer<'a> {
|
|||||||
Err(PdfError::EOF) => Ok(self.new_substr(self.pos..self.pos)),
|
Err(PdfError::EOF) => Ok(self.new_substr(self.pos..self.pos)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `Ok` if the next lexeme matches `expected` - else `Err`.
|
/// Returns `Ok` if the next lexeme matches `expected` - else `Err`.
|
||||||
@ -121,7 +124,7 @@ impl<'a> Lexer<'a> {
|
|||||||
Err(PdfError::UnexpectedLexeme {
|
Err(PdfError::UnexpectedLexeme {
|
||||||
pos: self.pos,
|
pos: self.pos,
|
||||||
lexeme: word.to_string(),
|
lexeme: word.to_string(),
|
||||||
expected,
|
expected
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,9 +213,7 @@ impl<'a> Lexer<'a> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_as<T>(&mut self) -> Result<T>
|
pub fn next_as<T>(&mut self) -> Result<T>
|
||||||
where
|
where T: FromStr, T::Err: std::error::Error + Send + Sync + 'static
|
||||||
T: FromStr,
|
|
||||||
T::Err: std::error::Error + Send + Sync + 'static,
|
|
||||||
{
|
{
|
||||||
self.next().and_then(|word| word.to::<T>())
|
self.next().and_then(|word| word.to::<T>())
|
||||||
}
|
}
|
||||||
@ -266,12 +267,14 @@ impl<'a> Lexer<'a> {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn seek_newline(&mut self) -> Substr{
|
pub fn seek_newline(&mut self) -> Substr{
|
||||||
let start = self.pos;
|
let start = self.pos;
|
||||||
while self.buf[self.pos] != b'\n' && self.incr_pos() {}
|
while self.buf[self.pos] != b'\n'
|
||||||
|
&& self.incr_pos() { }
|
||||||
self.incr_pos();
|
self.incr_pos();
|
||||||
|
|
||||||
self.new_substr(start..self.pos)
|
self.new_substr(start..self.pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: seek_substr and seek_substr_back should use next() or back()?
|
// TODO: seek_substr and seek_substr_back should use next() or back()?
|
||||||
/// Moves pos to after the found `substr`. Returns Substr with traversed text if `substr` is found.
|
/// Moves pos to after the found `substr`. Returns Substr with traversed text if `substr` is found.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -282,7 +285,7 @@ impl<'a> Lexer<'a> {
|
|||||||
let mut matched = 0;
|
let mut matched = 0;
|
||||||
loop {
|
loop {
|
||||||
if self.pos >= self.buf.len() {
|
if self.pos >= self.buf.len() {
|
||||||
return None;
|
return None
|
||||||
}
|
}
|
||||||
if self.buf[self.pos] == substr[matched] {
|
if self.buf[self.pos] == substr[matched] {
|
||||||
matched += 1;
|
matched += 1;
|
||||||
@ -303,17 +306,12 @@ impl<'a> Lexer<'a> {
|
|||||||
/// Substr if found.
|
/// Substr if found.
|
||||||
pub fn seek_substr_back(&mut self, substr: &[u8]) -> Result<Substr<'a>> {
|
pub fn seek_substr_back(&mut self, substr: &[u8]) -> Result<Substr<'a>> {
|
||||||
let end = self.pos;
|
let end = self.pos;
|
||||||
match self.buf[..end]
|
match self.buf[.. end].windows(substr.len()).rposition(|w| w == substr) {
|
||||||
.windows(substr.len())
|
|
||||||
.rposition(|w| w == substr)
|
|
||||||
{
|
|
||||||
Some(start) => {
|
Some(start) => {
|
||||||
self.pos = start + substr.len();
|
self.pos = start + substr.len();
|
||||||
Ok(self.new_substr(self.pos .. end))
|
Ok(self.new_substr(self.pos .. end))
|
||||||
}
|
}
|
||||||
None => Err(PdfError::NotFound {
|
None => Err(PdfError::NotFound {word: String::from_utf8_lossy(substr).into() })
|
||||||
word: String::from_utf8_lossy(substr).into(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,9 +338,7 @@ impl<'a> Lexer<'a> {
|
|||||||
|
|
||||||
/// for debugging
|
/// for debugging
|
||||||
pub fn ctx(&self) -> Cow<str> {
|
pub fn ctx(&self) -> Cow<str> {
|
||||||
String::from_utf8_lossy(
|
String::from_utf8_lossy(&self.buf[self.pos.saturating_sub(40)..self.buf.len().min(self.pos+40)])
|
||||||
&self.buf[self.pos.saturating_sub(40)..self.buf.len().min(self.pos + 40)],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -356,21 +352,18 @@ impl<'a> Lexer<'a> {
|
|||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_whitespace(&self, pos: usize) -> bool {
|
fn is_whitespace(&self, pos: usize) -> bool {
|
||||||
self.buf
|
self.buf.get(pos).map(|&b| is_whitespace(b)).unwrap_or(false)
|
||||||
.get(pos)
|
|
||||||
.map(|&b| is_whitespace(b))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_delimiter(&self, pos: usize) -> bool {
|
fn is_delimiter(&self, pos: usize) -> bool {
|
||||||
self.buf
|
self.buf.get(pos).map(|b| b"()<>[]{}/%".contains(b)).unwrap_or(false)
|
||||||
.get(pos)
|
|
||||||
.map(|b| b"()<>[]{}/%".contains(b))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// A slice from some original string - a lexeme.
|
/// A slice from some original string - a lexeme.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Substr<'a> {
|
pub struct Substr<'a> {
|
||||||
@ -379,10 +372,7 @@ pub struct Substr<'a> {
|
|||||||
}
|
}
|
||||||
impl<'a> Substr<'a> {
|
impl<'a> Substr<'a> {
|
||||||
pub fn new<T: AsRef<[u8]> + ?Sized>(data: &'a T, file_offset: usize) -> Self {
|
pub fn new<T: AsRef<[u8]> + ?Sized>(data: &'a T, file_offset: usize) -> Self {
|
||||||
Substr {
|
Substr { slice: data.as_ref(), file_offset }
|
||||||
slice: data.as_ref(),
|
|
||||||
file_offset,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// to: &S -> U. Possibly expensive conversion.
|
// to: &S -> U. Possibly expensive conversion.
|
||||||
// as: &S -> &U. Cheap borrow conversion
|
// as: &S -> &U. Cheap borrow conversion
|
||||||
@ -399,13 +389,9 @@ impl<'a> Substr<'a> {
|
|||||||
self.slice.to_vec()
|
self.slice.to_vec()
|
||||||
}
|
}
|
||||||
pub fn to<T>(&self) -> Result<T>
|
pub fn to<T>(&self) -> Result<T>
|
||||||
where
|
where T: FromStr, T::Err: std::error::Error + Send + Sync + 'static
|
||||||
T: FromStr,
|
|
||||||
T::Err: std::error::Error + Send + Sync + 'static,
|
|
||||||
{
|
{
|
||||||
std::str::from_utf8(self.slice)?
|
std::str::from_utf8(self.slice)?.parse::<T>().map_err(|e| PdfError::Parse { source: e.into() })
|
||||||
.parse::<T>()
|
|
||||||
.map_err(|e| PdfError::Parse { source: e.into() })
|
|
||||||
}
|
}
|
||||||
pub fn is_integer(&self) -> bool {
|
pub fn is_integer(&self) -> bool {
|
||||||
if self.slice.len() == 0 {
|
if self.slice.len() == 0 {
|
||||||
@ -447,7 +433,7 @@ impl<'a> Substr<'a> {
|
|||||||
let end = self.slice.len() - slice.len() + len;
|
let end = self.slice.len() - slice.len() + len;
|
||||||
Some(Substr {
|
Some(Substr {
|
||||||
file_offset: self.file_offset,
|
file_offset: self.file_offset,
|
||||||
slice: &self.slice[..end],
|
slice: &self.slice[..end]
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Some(*self)
|
Some(*self)
|
||||||
@ -505,7 +491,6 @@ mod tests {
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::u32::MAX;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_boundary_rev() {
|
fn test_boundary_rev() {
|
||||||
@ -537,19 +522,14 @@ mod tests {
|
|||||||
fn test_lexed() {
|
fn test_lexed() {
|
||||||
let file_data = fs::read("/home/kschuettler/Dokumente/TestFiles/18 - EVIDIS - Corrosao Irritacao ocular aguda.pdf").expect("File not found!");
|
let file_data = fs::read("/home/kschuettler/Dokumente/TestFiles/18 - EVIDIS - Corrosao Irritacao ocular aguda.pdf").expect("File not found!");
|
||||||
println!("{}", file_data.len());
|
println!("{}", file_data.len());
|
||||||
let mut lexer = Lexer::new(&file_data[0..]);
|
let mut lexer = Lexer::new(&*file_data);
|
||||||
let file = File::create("/tmp/pdf.txt").unwrap();
|
let file = File::create("/tmp/pdf.txt").unwrap();
|
||||||
|
|
||||||
let mut writer = BufWriter::new(file);
|
let mut writer = BufWriter::new(file);
|
||||||
let mut depth = false;
|
let mut depth = false;
|
||||||
let mut stream = false;
|
let mut stream = false;
|
||||||
let mut dict = 0u32;
|
let mut dict = 0;
|
||||||
let lex_count = MAX;
|
|
||||||
let mut lex_count_left = lex_count;
|
|
||||||
while let Ok(s) = lexer.next() {
|
while let Ok(s) = lexer.next() {
|
||||||
if lex_count_left == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if stream && s.to_string().as_str() == "endstream" {
|
if stream && s.to_string().as_str() == "endstream" {
|
||||||
stream = false;
|
stream = false;
|
||||||
writer
|
writer
|
||||||
@ -559,7 +539,6 @@ mod tests {
|
|||||||
} else if stream {
|
} else if stream {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lex_count_left -= 1;
|
|
||||||
|
|
||||||
match s.to_string().as_str() {
|
match s.to_string().as_str() {
|
||||||
"obj" => depth = true,
|
"obj" => depth = true,
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
@ -2767,9 +2767,7 @@ dependencies = [
|
|||||||
name = "pdf-forge"
|
name = "pdf-forge"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
|
||||||
"pdf",
|
"pdf",
|
||||||
"regex",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
|
|||||||
@ -26,6 +26,4 @@ pdf = { path = "../src-pdfrs/pdf", features = ["cache"] }
|
|||||||
tauri-plugin-fs = "2"
|
tauri-plugin-fs = "2"
|
||||||
tauri-plugin-dialog = "2"
|
tauri-plugin-dialog = "2"
|
||||||
uuid = { version = "1.12.0", features = ["v4"] }
|
uuid = { version = "1.12.0", features = ["v4"] }
|
||||||
regex = "1.10.3"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 974 B |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 903 B |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 14 KiB |
@ -1,18 +1,15 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
extern crate pdf;
|
extern crate pdf;
|
||||||
|
|
||||||
use crate::pdf::object::Resolve;
|
use crate::pdf::object::Resolve;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use pdf::content::Op;
|
||||||
use pdf::file::{File, FileOptions, NoLog, ObjectCache, StreamCache};
|
use pdf::file::{File, FileOptions, NoLog, ObjectCache, StreamCache};
|
||||||
use pdf::object::{Object, ObjectWrite, PlainRef, Stream};
|
use pdf::object::{InfoDict, Object, ObjectWrite, PlainRef};
|
||||||
use pdf::primitive::Primitive;
|
use pdf::primitive::Primitive;
|
||||||
use pdf::xref::XRef;
|
use pdf::xref::XRef;
|
||||||
use regex::Regex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
use std::fmt::format;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
@ -51,6 +48,7 @@ pub struct PdfFile {
|
|||||||
pub pages: Vec<PageModel>,
|
pub pages: Vec<PageModel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone)]
|
#[derive(Serialize, Debug, Clone)]
|
||||||
pub struct PrimitiveModel {
|
pub struct PrimitiveModel {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
@ -58,25 +56,25 @@ pub struct PrimitiveModel {
|
|||||||
pub sub_type: String,
|
pub sub_type: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub children: Vec<PrimitiveModel>,
|
pub children: Vec<PrimitiveModel>,
|
||||||
pub trace: Vec<PathTrace>,
|
pub detail_path: Vec<DetailPathStep>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone)]
|
#[derive(Serialize, Debug, Clone)]
|
||||||
pub struct PathTrace {
|
pub struct DetailPathStep {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
pub last_jump: String,
|
pub last_jump: String,
|
||||||
}
|
}
|
||||||
impl PathTrace {
|
impl DetailPathStep {
|
||||||
fn new(key: String, last_jump: String) -> PathTrace {
|
fn new(key: String, last_jump: String) -> DetailPathStep {
|
||||||
PathTrace { key, last_jump }
|
DetailPathStep { key, last_jump }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone)]
|
#[derive(Serialize, Debug, Clone)]
|
||||||
pub struct PageModel {
|
pub struct PageModel {
|
||||||
key: String,
|
key: String,
|
||||||
obj_num: u64,
|
id: u64,
|
||||||
page_num: u64,
|
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct TreeViewNode {
|
pub struct TreeViewNode {
|
||||||
@ -85,14 +83,14 @@ pub struct TreeViewNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TreeViewNode {
|
impl TreeViewNode {
|
||||||
fn step(&self) -> Result<Step, String> {
|
fn step(&self) -> Step {
|
||||||
Step::parse_step(&self.key)
|
Step::parse_step(&self.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
pub struct ContentsModel {
|
pub struct ContentsModel {
|
||||||
parts: Vec<Vec<String>>,
|
parts: Vec<Vec<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -143,30 +141,22 @@ fn upload(path: &str, session: State<Mutex<Session>>) -> Result<String, String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_pdf_file(path: &str, file: &CosFile) -> Result<PdfFile, String> {
|
fn to_pdf_file(path: &str, file: &CosFile) -> Result<PdfFile, String> {
|
||||||
|
|
||||||
fn parse_title_from_path(path: &str) -> Option<String> {
|
fn parse_title_from_path(path: &str) -> Option<String> {
|
||||||
Path::new(path)
|
Path::new(path).file_name()
|
||||||
.file_name()
|
|
||||||
.and_then(|f| f.to_str().map(|s| s.to_string()))
|
.and_then(|f| f.to_str().map(|s| s.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_name = if let Some(ref info) = file.trailer.info_dict {
|
let file_name = if let Some(ref info) = file.trailer.info_dict {
|
||||||
info.title
|
info.title.as_ref().map(|p| p.to_string_lossy())
|
||||||
.as_ref()
|
.unwrap_or( parse_title_from_path(path)
|
||||||
.map(|p| p.to_string_lossy())
|
.unwrap_or_else(|| "Not found".to_string()))
|
||||||
.unwrap_or(parse_title_from_path(path).unwrap_or_else(|| "Not found".to_string()))
|
|
||||||
} else {
|
} else {
|
||||||
"Not found".to_string()
|
"Not found".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let pages = file
|
|
||||||
.pages()
|
let pages = file.pages().enumerate().map(|(i, page_ref)| PageModel { key: format!("Page {}", i + 1), id: page_ref.unwrap().get_ref().get_inner().id }).collect();
|
||||||
.enumerate()
|
|
||||||
.map(|(i, page_ref)| PageModel {
|
|
||||||
key: format!("Page {}", i + 1),
|
|
||||||
obj_num: page_ref.unwrap().get_ref().get_inner().id,
|
|
||||||
page_num: (i + 1) as u64,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let pdf_file = PdfFile {
|
let pdf_file = PdfFile {
|
||||||
id: Uuid::new_v4().to_string(),
|
id: Uuid::new_v4().to_string(),
|
||||||
@ -180,17 +170,13 @@ fn to_pdf_file(path: &str, file: &CosFile) -> Result<PdfFile, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_contents(
|
fn get_contents(id: &str, path: &str, session: State<Mutex<Session>>) -> Result<ContentsModel, String> {
|
||||||
id: &str,
|
|
||||||
path: &str,
|
|
||||||
session: State<Mutex<Session>>,
|
|
||||||
) -> Result<ContentsModel, String> {
|
|
||||||
let session_guard = session
|
let session_guard = session
|
||||||
.lock()
|
.lock()
|
||||||
.map_err(|_| "Failed to lock the session mutex.".to_string())?;
|
.map_err(|_| "Failed to lock the session mutex.".to_string())?;
|
||||||
let file = get_file_from_state(id, &session_guard)?;
|
let file = get_file_from_state(path, &session_guard)?;
|
||||||
|
|
||||||
let (_, page_prim, _) = get_prim_by_path_with_file(path, &file.cos_file)?;
|
let (_, page_prim, _) = get_prim_by_path_with_file(id, &file.cos_file)?;
|
||||||
let resolver = file.cos_file.resolver();
|
let resolver = file.cos_file.resolver();
|
||||||
|
|
||||||
let page = t!(pdf::object::Page::from_primitive(page_prim, &resolver));
|
let page = t!(pdf::object::Page::from_primitive(page_prim, &resolver));
|
||||||
@ -201,40 +187,12 @@ fn get_contents(
|
|||||||
let ops = t!(pdf::content::parse_ops(&data, &resolver));
|
let ops = t!(pdf::content::parse_ops(&data, &resolver));
|
||||||
let part = t!(pdf::content::display_ops(&ops));
|
let part = t!(pdf::content::display_ops(&ops));
|
||||||
parts.push(part);
|
parts.push(part);
|
||||||
}
|
};
|
||||||
return Ok(ContentsModel {parts});
|
return Ok(ContentsModel {parts});
|
||||||
}
|
}
|
||||||
Err(String::from("Error occurred"))
|
Err(String::from("Error occurred"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
fn get_stream_data(id: &str, path: &str, session: State<Mutex<Session>>) -> Result<String, String> {
|
|
||||||
let session_guard = session
|
|
||||||
.lock()
|
|
||||||
.map_err(|_| "Failed to lock the session mutex.".to_string())?;
|
|
||||||
let file = get_file_from_state(id, &session_guard)?;
|
|
||||||
get_stream_data_by_path_with_file(path, &file.cos_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_stream_data_by_path_with_file(path: &str, file: &CosFile) -> Result<String, String> {
|
|
||||||
let mut steps = Step::parse(path);
|
|
||||||
if steps
|
|
||||||
.pop_back()
|
|
||||||
.filter(|last| *last == Step::Data)
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
return Err(format!("Path {} does not end with Data", path));
|
|
||||||
}
|
|
||||||
let (_, prim, _) = get_prim_by_steps_with_file(steps, file)?;
|
|
||||||
|
|
||||||
let Primitive::Stream(stream) = prim else {
|
|
||||||
return Err(format!("Path {} does not point to a stream", path));
|
|
||||||
};
|
|
||||||
let resolver = file.resolver();
|
|
||||||
let data = t!(t!(Stream::<Primitive>::from_stream(stream, &resolver)).data(&resolver));
|
|
||||||
Ok(String::from_utf8_lossy(&data).into_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_prim_by_path(
|
fn get_prim_by_path(
|
||||||
id: &str,
|
id: &str,
|
||||||
@ -248,56 +206,44 @@ fn get_prim_by_path(
|
|||||||
|
|
||||||
get_prim_model_by_path_with_file(path, &file.cos_file)
|
get_prim_model_by_path_with_file(path, &file.cos_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prim_model_by_path_with_file(path: &str, file: &CosFile) -> Result<PrimitiveModel, String> {
|
fn get_prim_model_by_path_with_file(path: &str, file: &CosFile) -> Result<PrimitiveModel, String> {
|
||||||
let (step, prim, trace) = get_prim_by_path_with_file(path, file)?;
|
let (key, prim, detail_path) = get_prim_by_path_with_file(path, file)?;
|
||||||
|
|
||||||
Ok(PrimitiveModel::from_primitive_with_children(&prim, trace))
|
Ok(PrimitiveModel::from_primitive_with_children(
|
||||||
|
key,
|
||||||
|
&prim,
|
||||||
|
detail_path
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prim_by_path_with_file(
|
fn get_prim_by_path_with_file(path: &str, file: &CosFile) -> Result<(String, Primitive, Vec<DetailPathStep>), String> {
|
||||||
path: &str,
|
let mut steps = Step::parse(path);
|
||||||
file: &CosFile,
|
|
||||||
) -> Result<(Step, Primitive, Vec<PathTrace>), String> {
|
|
||||||
get_prim_by_steps_with_file(Step::parse(path), file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_prim_by_steps_with_file(
|
|
||||||
mut steps: VecDeque<Step>,
|
|
||||||
file: &CosFile,
|
|
||||||
) -> Result<(Step, Primitive, Vec<PathTrace>), String> {
|
|
||||||
if steps.len() == 0 {
|
if steps.len() == 0 {
|
||||||
return Err(String::from(format!("{:?} is not a valid path!", steps)));
|
return Err(String::from(format!("{} is not a valid path!", path)));
|
||||||
}
|
}
|
||||||
let step = steps.pop_front().unwrap();
|
let mut step = steps.pop_front().unwrap();
|
||||||
let (mut parent, trace) = resolve_parent(step.clone(), file)?;
|
let mut parent = match step {
|
||||||
|
Step::Number(obj_num) => resolve_xref(obj_num, file)?,
|
||||||
|
Step::Trailer => retrieve_trailer(file),
|
||||||
|
_ => return Err(String::from(format!("{} is not a valid path!", path))),
|
||||||
|
};
|
||||||
|
|
||||||
let mut last_jump = trace.last_jump.clone();
|
let mut detail_path = vec![DetailPathStep::new(step.get_key(), step.get_key())];
|
||||||
let mut trace = vec![trace];
|
let mut last_jump = step.get_key();
|
||||||
|
|
||||||
let mut current_prim = &parent;
|
let mut current_prim = &parent;
|
||||||
while !steps.is_empty() {
|
while !steps.is_empty() {
|
||||||
let step = steps.pop_front().unwrap();
|
step = steps.pop_front().unwrap();
|
||||||
|
|
||||||
current_prim = resolve_step(¤t_prim, &step)?;
|
current_prim = resolve_step(¤t_prim, &step)?;
|
||||||
if let Primitive::Reference(xref) = current_prim {
|
if let Primitive::Reference(xref) = current_prim {
|
||||||
last_jump = xref.id.to_string();
|
last_jump = xref.id.to_string();
|
||||||
parent = resolve_p_ref(xref.clone(), file)?;
|
parent = resolve_xref(xref.id, file)?;
|
||||||
current_prim = &parent;
|
current_prim = &parent;
|
||||||
}
|
}
|
||||||
trace.push(PathTrace::new(step.get_key(), last_jump.clone()));
|
detail_path.push(DetailPathStep::new(step.get_key(), last_jump.clone()));
|
||||||
}
|
}
|
||||||
Ok((step, current_prim.clone(), trace))
|
Ok((step.get_key(), current_prim.clone(), detail_path))
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_parent(step: Step, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
|
|
||||||
let parent = match step {
|
|
||||||
Step::Page(page_num) => return retrieve_page(page_num, file),
|
|
||||||
Step::Number(obj_num) => resolve_xref(obj_num, file)?,
|
|
||||||
Step::Trailer => retrieve_trailer(file),
|
|
||||||
_ => return Err(String::from(format!("{:?} is not a valid path!", step))),
|
|
||||||
};
|
|
||||||
Ok((parent, PathTrace::new(step.get_key(), step.get_key())))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -318,12 +264,15 @@ fn get_prim_tree_by_path_with_file(
|
|||||||
node: TreeViewNode,
|
node: TreeViewNode,
|
||||||
file: &CosFile,
|
file: &CosFile,
|
||||||
) -> Result<PrimitiveModel, String> {
|
) -> Result<PrimitiveModel, String> {
|
||||||
let step = node.step()?;
|
let step = node.step();
|
||||||
let (parent, trace) = resolve_parent(step.clone(), file)?;
|
let parent = match step {
|
||||||
|
Step::Number(obj_num) => resolve_xref(obj_num, file)?,
|
||||||
let path = vec![trace];
|
Step::Trailer => retrieve_trailer(file),
|
||||||
let mut parent_model = PrimitiveModel::from_primitive_with_children(&parent, path);
|
_ => return Err(String::from(format!("{:?} is not a valid path!", node))),
|
||||||
|
};
|
||||||
|
let path = vec![DetailPathStep::new(step.get_key(), step.get_key())];
|
||||||
|
|
||||||
|
let mut parent_model = PrimitiveModel::from_primitive_with_children(step.get_key(), &parent, path);
|
||||||
for child in node.children.iter() {
|
for child in node.children.iter() {
|
||||||
expand(child, &mut parent_model, &parent, file)?;
|
expand(child, &mut parent_model, &parent, file)?;
|
||||||
}
|
}
|
||||||
@ -337,20 +286,17 @@ fn expand(
|
|||||||
parent: &Primitive,
|
parent: &Primitive,
|
||||||
file: &CosFile,
|
file: &CosFile,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let step = node.step()?;
|
let step = node.step();
|
||||||
let prim = resolve_step(parent, &step)?;
|
let prim = resolve_step(parent, &step)?;
|
||||||
if let Primitive::Reference(x_ref) = prim {
|
if let Primitive::Reference(x_ref) = prim {
|
||||||
let jump = resolve_xref(x_ref.id, file)?;
|
let jump = resolve_xref(x_ref.id, file)?;
|
||||||
// parent_model.ptype = format!("{}-Reference", jump.get_debug_name());
|
// parent_model.ptype = format!("{}-Reference", jump.get_debug_name());
|
||||||
let mut to_expand = parent_model.get_child(step.get_key()).unwrap();
|
let mut to_expand = parent_model.get_child(step.get_key()).unwrap();
|
||||||
to_expand.add_children(
|
to_expand.add_children(&jump, append_path_with_jump(step.get_key(), x_ref.id.to_string(), &to_expand.detail_path));
|
||||||
&jump,
|
|
||||||
append_path_with_jump(step.get_key(), x_ref.id.to_string(), &to_expand.trace),
|
|
||||||
);
|
|
||||||
expand_children(node, file, &jump, &mut to_expand)?;
|
expand_children(node, file, &jump, &mut to_expand)?;
|
||||||
} else {
|
} else {
|
||||||
let mut to_expand = parent_model.get_child(step.get_key()).unwrap();
|
let mut to_expand = parent_model.get_child(step.get_key()).unwrap();
|
||||||
to_expand.add_children(prim, append_path(step.get_key(), &to_expand.trace));
|
to_expand.add_children(prim, append_path(step.get_key(), &to_expand.detail_path));
|
||||||
expand_children(node, file, prim, &mut to_expand)?;
|
expand_children(node, file, prim, &mut to_expand)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -415,6 +361,7 @@ fn resolve_step<'a>(current_prim: &'a Primitive, step: &Step) -> Result<&'a Prim
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Step::Data => return Err("Not implemented!".to_string()),
|
||||||
_ => return Err(format!("Invalid Step: {}", step.get_key())),
|
_ => return Err(format!("Invalid Step: {}", step.get_key())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -424,71 +371,53 @@ fn retrieve_trailer(file: &CosFile) -> Primitive {
|
|||||||
file.trailer.to_primitive(&mut updater).unwrap()
|
file.trailer.to_primitive(&mut updater).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retrieve_page(page_num: u32, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
|
#[derive(Debug)]
|
||||||
let page_rc = t!(file.get_page(page_num - 1));
|
|
||||||
let p_ref = page_rc.get_ref().get_inner();
|
|
||||||
Ok((
|
|
||||||
resolve_p_ref(p_ref, file)?,
|
|
||||||
PathTrace::new(format!("Page{}", page_num), p_ref.id.to_string()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Step {
|
pub enum Step {
|
||||||
String(String),
|
String(String),
|
||||||
Page(u32),
|
|
||||||
Number(u64),
|
Number(u64),
|
||||||
Trailer,
|
Trailer,
|
||||||
Data,
|
Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step {
|
impl Step {
|
||||||
fn parse_step(path: &str) -> Result<Step, String> {
|
fn parse_step(path: &str) -> Step {
|
||||||
lazy_static! {
|
match &path.parse::<u64>().ok() {
|
||||||
static ref PAGE_RE: Regex = Regex::new(r"^Page(\d+)$").unwrap();
|
|
||||||
}
|
|
||||||
if path.len() == 0 {
|
|
||||||
return Err(String::from("Path is empty"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(match &path.parse::<u64>().ok() {
|
|
||||||
Some(i) => Step::Number(*i),
|
Some(i) => Step::Number(*i),
|
||||||
None => match &path[..] {
|
None => match &path[..] {
|
||||||
"Data" => Step::Data,
|
"Data" => Step::Data,
|
||||||
"Trailer" => Step::Trailer,
|
|
||||||
"/" => Step::Trailer,
|
"/" => Step::Trailer,
|
||||||
_ => {
|
_ => Step::String(path.to_string().clone()),
|
||||||
if let Some(caps) = PAGE_RE.captures(path) {
|
|
||||||
Step::Page(
|
|
||||||
caps[1]
|
|
||||||
.parse::<u32>()
|
|
||||||
.map_err(|_| format!("Invalid page number in {}", path))?,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Step::String(path.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(path: &str) -> VecDeque<Step> {
|
fn parse(path: &str) -> VecDeque<Step> {
|
||||||
let mut steps = VecDeque::new();
|
let mut steps = VecDeque::new();
|
||||||
|
|
||||||
if path.starts_with("/") {
|
if path.starts_with("/") {
|
||||||
steps.push_back(Step::Trailer);
|
steps.push_back(Step::Trailer);
|
||||||
}
|
}
|
||||||
let split_path = path.split("/").collect::<VecDeque<&str>>();
|
let split_path = path.split("/").collect::<VecDeque<&str>>();
|
||||||
split_path
|
for path_component in split_path {
|
||||||
.iter()
|
if path_component.len() == 0 {
|
||||||
.filter_map(|s| Step::parse_step(s).ok())
|
continue;
|
||||||
.collect::<VecDeque<Step>>()
|
}
|
||||||
|
let step = match &path_component.parse::<u64>().ok() {
|
||||||
|
Some(i) => Step::Number(*i),
|
||||||
|
None => match path_component {
|
||||||
|
"Data" => Step::Data,
|
||||||
|
_ => Step::String(path_component.to_string().clone()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
steps.push_back(step);
|
||||||
|
}
|
||||||
|
steps
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_key(&self) -> String {
|
fn get_key(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Step::String(s) => s.clone(),
|
Step::String(s) => s.clone(),
|
||||||
Step::Number(i) => i.to_string(),
|
Step::Number(i) => i.to_string(),
|
||||||
Step::Trailer => "Trailer".to_string(),
|
Step::Trailer => "/".to_string(),
|
||||||
Step::Page(n) => format!("Page{}", n),
|
|
||||||
Step::Data => "Data".into(),
|
Step::Data => "Data".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -496,10 +425,6 @@ impl Step {
|
|||||||
|
|
||||||
fn resolve_xref(id: u64, file: &CosFile) -> Result<Primitive, String> {
|
fn resolve_xref(id: u64, file: &CosFile) -> Result<Primitive, String> {
|
||||||
let plain_ref = PlainRef { id, gen: 0 };
|
let plain_ref = PlainRef { id, gen: 0 };
|
||||||
resolve_p_ref(plain_ref, file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_p_ref(plain_ref: PlainRef, file: &CosFile) -> Result<Primitive, String> {
|
|
||||||
file.resolver()
|
file.resolver()
|
||||||
.resolve(plain_ref)
|
.resolve(plain_ref)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@ -515,27 +440,28 @@ fn get_file_from_state<'a>(
|
|||||||
.ok_or_else(|| format!("File with id {} does not exist!", id))
|
.ok_or_else(|| format!("File with id {} does not exist!", id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_path_with_jump(key: String, last_jump: String, path: &Vec<PathTrace>) -> Vec<PathTrace> {
|
fn append_path_with_jump(key: String, last_jump: String, path: &Vec<DetailPathStep>) -> Vec<DetailPathStep> {
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(PathTrace::new(key, last_jump));
|
new_path.push(DetailPathStep::new(key, last_jump));
|
||||||
new_path
|
new_path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_path(key: String, path: &Vec<PathTrace>) -> Vec<PathTrace> {
|
fn append_path(key: String, path: &Vec<DetailPathStep>) -> Vec<DetailPathStep> {
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
let last_jump = new_path.last().unwrap().last_jump.clone();
|
let last_jump = new_path.last().unwrap().last_jump.clone();
|
||||||
new_path.push(PathTrace::new(key, last_jump));
|
new_path.push(DetailPathStep::new(key, last_jump));
|
||||||
new_path
|
new_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl PrimitiveModel {
|
impl PrimitiveModel {
|
||||||
fn from_primitive(key: String, primitive: &Primitive, path: Vec<PathTrace>) -> PrimitiveModel {
|
fn from_primitive(key: String, primitive: &Primitive, path: Vec<DetailPathStep>) -> PrimitiveModel {
|
||||||
let value: String = match primitive {
|
let value: String = match primitive {
|
||||||
Primitive::Null => "Null".to_string(),
|
Primitive::Null => "Null".to_string(),
|
||||||
Primitive::Integer(i) => i.to_string(),
|
Primitive::Integer(i) => i.to_string(),
|
||||||
Primitive::Number(f) => f.to_string(),
|
Primitive::Number(f) => f.to_string(),
|
||||||
Primitive::Boolean(b) => b.to_string(),
|
Primitive::Boolean(b) => b.to_string(),
|
||||||
Primitive::String(s) => s.to_string_lossy(),
|
Primitive::String(s) => s.to_string().unwrap_or(String::new()),
|
||||||
Primitive::Stream(_) => "-".to_string(),
|
Primitive::Stream(_) => "-".to_string(),
|
||||||
Primitive::Dictionary(_) => "-".to_string(),
|
Primitive::Dictionary(_) => "-".to_string(),
|
||||||
Primitive::Array(arr) =>PrimitiveModel::format_arr_content(arr),
|
Primitive::Array(arr) =>PrimitiveModel::format_arr_content(arr),
|
||||||
@ -550,10 +476,10 @@ impl PrimitiveModel {
|
|||||||
.get("Type")
|
.get("Type")
|
||||||
.and_then(|value| match value {
|
.and_then(|value| match value {
|
||||||
Primitive::Name(name) => Some(name.clone().as_str().to_string()),
|
Primitive::Name(name) => Some(name.clone().as_str().to_string()),
|
||||||
_ => None,
|
_ => None
|
||||||
})
|
})
|
||||||
.unwrap_or(String::from("-")),
|
.unwrap_or(String::from("-")),
|
||||||
_ => String::from("-"),
|
_ => String::from("-")
|
||||||
};
|
};
|
||||||
PrimitiveModel {
|
PrimitiveModel {
|
||||||
key: key,
|
key: key,
|
||||||
@ -561,7 +487,7 @@ impl PrimitiveModel {
|
|||||||
sub_type: sub_type,
|
sub_type: sub_type,
|
||||||
value: value,
|
value: value,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
trace: path,
|
detail_path: path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,24 +519,16 @@ impl PrimitiveModel {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_primitive_with_children(primitive: &Primitive, path: Vec<PathTrace>) -> PrimitiveModel {
|
fn from_primitive_with_children(key: String, primitive: &Primitive, path: Vec<DetailPathStep>) -> PrimitiveModel {
|
||||||
let mut model = PrimitiveModel::from_primitive(
|
let mut model = PrimitiveModel::from_primitive(key, primitive, path.clone());
|
||||||
path.last().unwrap().key.clone(),
|
|
||||||
primitive,
|
|
||||||
path.clone(),
|
|
||||||
);
|
|
||||||
model.add_children(primitive, path);
|
model.add_children(primitive, path);
|
||||||
model
|
model
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_children(&mut self, primitive: &Primitive, path: Vec<PathTrace>) {
|
fn add_children(&mut self, primitive: &Primitive, path: Vec<DetailPathStep>) {
|
||||||
match primitive {
|
match primitive {
|
||||||
Primitive::Dictionary(dict) => dict.iter().for_each(|(name, value)| {
|
Primitive::Dictionary(dict) => dict.iter().for_each(|(name, value)| {
|
||||||
self.add_child(
|
self.add_child(name.clone().as_str().to_string(), value, append_path(name.clone().as_str().to_string(), &path));
|
||||||
name.clone().as_str().to_string(),
|
|
||||||
value,
|
|
||||||
append_path(name.clone().as_str().to_string(), &path),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
Primitive::Array(arr) => arr.iter().enumerate().for_each(|(i, obj)| {
|
Primitive::Array(arr) => arr.iter().enumerate().for_each(|(i, obj)| {
|
||||||
self.add_child(i.to_string(), obj, append_path(i.to_string(), &path));
|
self.add_child(i.to_string(), obj, append_path(i.to_string(), &path));
|
||||||
@ -622,26 +540,17 @@ impl PrimitiveModel {
|
|||||||
sub_type: "-".to_string(),
|
sub_type: "-".to_string(),
|
||||||
value: "".to_string(),
|
value: "".to_string(),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
trace: append_path("Data".to_string(), &path),
|
detail_path: append_path("Data".to_string(), &path),
|
||||||
});
|
});
|
||||||
stream.info.iter().for_each(|(name, value)| {
|
stream.info.iter().for_each(|(name, value)| {
|
||||||
self.add_child(
|
self.add_child(name.clone().as_str().to_string(), value, append_path(name.clone().as_str().to_string(), &path));
|
||||||
name.clone().as_str().to_string(),
|
|
||||||
value,
|
|
||||||
append_path(name.clone().as_str().to_string(), &path),
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_child(
|
fn add_child(&mut self, key: String, child: &Primitive, path: Vec<DetailPathStep>) -> &PrimitiveModel {
|
||||||
&mut self,
|
|
||||||
key: String,
|
|
||||||
child: &Primitive,
|
|
||||||
path: Vec<PathTrace>,
|
|
||||||
) -> &PrimitiveModel {
|
|
||||||
let child_model = Self::from_primitive(key, child, path);
|
let child_model = Self::from_primitive(key, child, path);
|
||||||
self.children.push(child_model);
|
self.children.push(child_model);
|
||||||
&self.children[self.children.len() - 1]
|
&self.children[self.children.len() - 1]
|
||||||
@ -767,9 +676,9 @@ pub fn run() {
|
|||||||
get_prim_by_path,
|
get_prim_by_path,
|
||||||
get_prim_tree_by_path,
|
get_prim_tree_by_path,
|
||||||
get_xref_table,
|
get_xref_table,
|
||||||
get_contents,
|
get_contents
|
||||||
get_stream_data
|
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,7 @@ extern crate pdf;
|
|||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
get_prim_by_path_with_file, get_prim_model_by_path_with_file,
|
get_prim_by_path_with_file, get_prim_model_by_path_with_file, get_prim_tree_by_path_with_file, get_xref_table_model_with_file, to_pdf_file, DetailPathStep, PrimitiveModel, TreeViewNode
|
||||||
get_prim_tree_by_path_with_file, get_stream_data_by_path_with_file,
|
|
||||||
get_xref_table_model_with_file, to_pdf_file, PathTrace, PrimitiveModel, TreeViewNode,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use pdf::content::{display_ops, serialize_ops, Op};
|
use pdf::content::{display_ops, serialize_ops, Op};
|
||||||
@ -103,7 +101,7 @@ mod tests {
|
|||||||
|
|
||||||
fn print_node(node: PrimitiveModel, depth: usize) {
|
fn print_node(node: PrimitiveModel, depth: usize) {
|
||||||
let spaces = " ".repeat(depth);
|
let spaces = " ".repeat(depth);
|
||||||
println!("{:?}", node.trace);
|
println!("{:?}", node.detail_path);
|
||||||
println!("{}{} | {} | {}", spaces, node.key, node.ptype, node.value);
|
println!("{}{} | {} | {}", spaces, node.key, node.ptype, node.value);
|
||||||
for child in node.children {
|
for child in node.children {
|
||||||
print_node(child, depth + 1);
|
print_node(child, depth + 1);
|
||||||
@ -115,17 +113,14 @@ mod tests {
|
|||||||
FileOptions::cached().open(FILE_PATH).unwrap(),
|
FileOptions::cached().open(FILE_PATH).unwrap(),
|
||||||
"Loading file"
|
"Loading file"
|
||||||
);
|
);
|
||||||
let mut file2 = timed!(FileOptions::uncached().storage(), "Loading storage");
|
let mut file2 = timed!(
|
||||||
|
FileOptions::uncached().storage(),
|
||||||
|
"Loading storage"
|
||||||
|
);
|
||||||
|
|
||||||
let trail = timed!(
|
|
||||||
file.trailer.to_primitive(&mut file2).unwrap(),
|
let trail = timed!(file.trailer.to_primitive(&mut file2).unwrap(), "writing trailer");
|
||||||
"writing trailer"
|
let trail_model = PrimitiveModel::from_primitive_with_children("Trailer".to_string(), &trail, vec![DetailPathStep::new("/".to_string(), "/".to_string())]);
|
||||||
);
|
|
||||||
let trail_model = PrimitiveModel::from_primitive_with_children(
|
|
||||||
"Trailer".to_string(),
|
|
||||||
&trail,
|
|
||||||
vec![PathTrace::new("/".to_string(), "/".to_string())],
|
|
||||||
);
|
|
||||||
print_node(trail_model, 5);
|
print_node(trail_model, 5);
|
||||||
println!("{:?}", file.trailer.info_dict);
|
println!("{:?}", file.trailer.info_dict);
|
||||||
}
|
}
|
||||||
@ -144,6 +139,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_contents() {
|
fn test_read_contents() {
|
||||||
|
|
||||||
let file = timed!(
|
let file = timed!(
|
||||||
FileOptions::cached().open(FILE_PATH).unwrap(),
|
FileOptions::cached().open(FILE_PATH).unwrap(),
|
||||||
"Loading file"
|
"Loading file"
|
||||||
@ -152,10 +148,7 @@ mod tests {
|
|||||||
let (_, page2_prim, _) = get_prim_by_path_with_file("1", &file).unwrap();
|
let (_, page2_prim, _) = get_prim_by_path_with_file("1", &file).unwrap();
|
||||||
let resolver = file.resolver();
|
let resolver = file.resolver();
|
||||||
let page2 = Page::from_primitive(page2_prim, &resolver).unwrap();
|
let page2 = Page::from_primitive(page2_prim, &resolver).unwrap();
|
||||||
let mut ops: Vec<Op> = timed!(
|
let mut ops: Vec<Op> = timed!(page2.contents.unwrap().operations(&resolver).unwrap(), "parse ops");
|
||||||
page2.contents.unwrap().operations(&resolver).unwrap(),
|
|
||||||
"parse ops"
|
|
||||||
);
|
|
||||||
let serialized = timed!(serialize_ops(&mut ops).unwrap(), "serializing");
|
let serialized = timed!(serialize_ops(&mut ops).unwrap(), "serializing");
|
||||||
let display = timed!(display_ops(&mut ops).unwrap(), "displaying");
|
let display = timed!(display_ops(&mut ops).unwrap(), "displaying");
|
||||||
println!("Serialized -----------------------------------------------------------------");
|
println!("Serialized -----------------------------------------------------------------");
|
||||||
@ -164,22 +157,6 @@ mod tests {
|
|||||||
for (line, s) in display.iter().enumerate() {
|
for (line, s) in display.iter().enumerate() {
|
||||||
println!("{}: {}", line, s);
|
println!("{}: {}", line, s);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_read_stream() {
|
|
||||||
let file = timed!(
|
|
||||||
FileOptions::cached().open(FILE_PATH).unwrap(),
|
|
||||||
"Loading file"
|
|
||||||
);
|
|
||||||
let prim = timed!(
|
|
||||||
get_prim_model_by_path_with_file("1/Contents/1", &file).unwrap(),
|
|
||||||
"get prim"
|
|
||||||
);
|
|
||||||
print_node(prim, 3);
|
|
||||||
let content1 = timed!(
|
|
||||||
get_stream_data_by_path_with_file("1/Contents/1/Data", &file).unwrap(),
|
|
||||||
"get content 1"
|
|
||||||
);
|
|
||||||
println!("{}", content1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,14 +19,14 @@ body {
|
|||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
border-color: var(--secondary-color)
|
border-color: var(--secondary-color)
|
||||||
}
|
}
|
||||||
|
::before, ::after {
|
||||||
::before,
|
|
||||||
::after {
|
|
||||||
border-color: var(--secondary-color);
|
border-color: var(--secondary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-container {
|
.full-container {
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import WelcomeScreen from "./WelcomeScreen.svelte";
|
import WelcomeScreen from "./WelcomeScreen.svelte";
|
||||||
import { Pane, Splitpanes } from "svelte-splitpanes";
|
import {Pane, Splitpanes} from 'svelte-splitpanes';
|
||||||
import {open} from "@tauri-apps/plugin-dialog";
|
import {open} from "@tauri-apps/plugin-dialog";
|
||||||
import {invoke} from "@tauri-apps/api/core";
|
import {invoke} from "@tauri-apps/api/core";
|
||||||
import type PdfFile from "../models/PdfFile";
|
import type PdfFile from "../models/PdfFile";
|
||||||
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
const footerHeight: number = 30;
|
const footerHeight: number = 30;
|
||||||
const titleBarHeight: number = 30;
|
const titleBarHeight: number = 30;
|
||||||
const tabBarHeight: number = 30;
|
|
||||||
let files: PdfFile[] = $state([]);
|
let files: PdfFile[] = $state([]);
|
||||||
let innerHeight: number = $state(1060);
|
let innerHeight: number = $state(1060);
|
||||||
let errorMessage: string = $state("");
|
let errorMessage: string = $state("");
|
||||||
@ -22,27 +21,23 @@
|
|||||||
let xrefTableShowing: boolean = $state(false);
|
let xrefTableShowing: boolean = $state(false);
|
||||||
let treeShowing: boolean = $state(true);
|
let treeShowing: boolean = $state(true);
|
||||||
let pagesShowing: boolean = $state(false);
|
let pagesShowing: boolean = $state(false);
|
||||||
let fileViewHeight: number = $derived(
|
let fileViewHeight: number = $derived(Math.max(innerHeight - footerHeight - titleBarHeight, 0));
|
||||||
Math.max(innerHeight - footerHeight - tabBarHeight - titleBarHeight, 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let fStates: Map<string, FileViewState> = new Map<string, FileViewState>();
|
let fStates: Map<string, FileViewState> = new Map<string, FileViewState>();
|
||||||
let fState: FileViewState | undefined = $state();
|
let fState: FileViewState | undefined = $state();
|
||||||
let selected_file = $derived(fState ? fState.file : undefined);
|
let selected_file = $derived(fState ? fState.file : undefined);
|
||||||
initialLoadAllFiles();
|
initialLoadAllFiles()
|
||||||
|
|
||||||
function initialLoadAllFiles() {
|
function initialLoadAllFiles() {
|
||||||
invoke<PdfFile[]>("get_all_files")
|
invoke<PdfFile[]>("get_all_files").then(result_list => {
|
||||||
.then((result_list) => {
|
|
||||||
files = result_list;
|
files = result_list;
|
||||||
createFileStates(files);
|
createFileStates(files);
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
selectFile(files[files.length - 1]);
|
selectFile(files[files.length - 1])
|
||||||
}
|
}
|
||||||
})
|
}).catch(error => {
|
||||||
.catch((error) => {
|
errorMessage = error
|
||||||
errorMessage = error;
|
console.error("File retrieval failed: " + error)
|
||||||
console.error("File retrieval failed: " + error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +46,7 @@
|
|||||||
if (fStates.has(file.id)) {
|
if (fStates.has(file.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let fState = new FileViewState(file);
|
let fState = new FileViewState(file)
|
||||||
fStates.set(file.id, fState);
|
fStates.set(file.id, fState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,31 +55,27 @@
|
|||||||
let file_path = await open({
|
let file_path = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
directory: false,
|
directory: false,
|
||||||
filters: [{ name: "pdf", extensions: ["pdf"] }],
|
filters: [{name: 'pdf', extensions: ['pdf']}]
|
||||||
});
|
})
|
||||||
if (file_path === null || Array.isArray(file_path)) {
|
if (file_path === null || Array.isArray(file_path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke<String>("upload", { path: file_path })
|
invoke<String>("upload", {path: file_path}).then(result => {
|
||||||
.then((result) => {
|
invoke<PdfFile[]>("get_all_files").then(result_list => {
|
||||||
invoke<PdfFile[]>("get_all_files")
|
|
||||||
.then((result_list) => {
|
|
||||||
files = result_list;
|
files = result_list;
|
||||||
createFileStates(files);
|
createFileStates(files);
|
||||||
let file = files.find((file) => file.id === result);
|
let file = files.find(file => file.id === result);
|
||||||
if (file) {
|
if (file) {
|
||||||
selectFile(file);
|
selectFile(file);
|
||||||
}
|
}
|
||||||
})
|
}).catch(error => {
|
||||||
.catch((error) => {
|
errorMessage = error
|
||||||
errorMessage = error;
|
console.error("Fetch all files failed with: " + error)
|
||||||
console.error("Fetch all files failed with: " + error);
|
|
||||||
});
|
});
|
||||||
})
|
}).catch(error => {
|
||||||
.catch((error) => {
|
errorMessage = error
|
||||||
errorMessage = error;
|
console.error("File upload failed with: " + error)
|
||||||
console.error("File upload failed with: " + error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,44 +84,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeFile(file: PdfFile) {
|
function closeFile(file: PdfFile) {
|
||||||
invoke("close_file", { id: file.id })
|
invoke("close_file", {id: file.id}).then(_ => {
|
||||||
.then((_) => {
|
files = files.filter(f => f.id != file.id)
|
||||||
files = files.filter((f) => f.id != file.id);
|
|
||||||
if (file === selected_file) {
|
if (file === selected_file) {
|
||||||
fState = undefined;
|
fState = undefined;
|
||||||
}
|
}
|
||||||
})
|
}).catch(err => console.error(err));
|
||||||
.catch((err) => console.error(err));
|
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
|
</script>
|
||||||
<svelte:window bind:innerHeight/>
|
<svelte:window bind:innerHeight/>
|
||||||
<main style="height: {innerHeight}px; overflow: hidden;">
|
<main style="height: {innerHeight}px">
|
||||||
<div style="height: {titleBarHeight}px">
|
<div style="height: {titleBarHeight}px">
|
||||||
<TitleBar></TitleBar>
|
<TitleBar></TitleBar>
|
||||||
</div>
|
</div>
|
||||||
<div class="fileview_container" style="height: {fileViewHeight + 30}px">
|
<div style="height: {fileViewHeight}px">
|
||||||
<Splitpanes theme="forge-movable" dblClickSplitter={false}>
|
<Splitpanes theme="forge-movable" dblClickSplitter={false}>
|
||||||
<Pane size={2.5} minSize={1.5} maxSize={4}>
|
<Pane size={2.5} minSize={1.5} maxSize={4}>
|
||||||
<ToolbarLeft bind:tree={treeShowing} bind:pages={pagesShowing}
|
<ToolbarLeft bind:tree={treeShowing} bind:pages={pagesShowing}></ToolbarLeft>
|
||||||
></ToolbarLeft>
|
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane>
|
<Pane>
|
||||||
<TabBar
|
<TabBar {files} {selected_file} closeTab={closeFile} openTab={upload} selectTab={selectFile}></TabBar>
|
||||||
{files}
|
{#if (fState)}
|
||||||
{selected_file}
|
<FileView {treeShowing} {xrefTableShowing} {pagesShowing} {fState} height={fileViewHeight}></FileView>
|
||||||
closeTab={closeFile}
|
|
||||||
openTab={upload}
|
|
||||||
selectTab={selectFile}
|
|
||||||
></TabBar>
|
|
||||||
{#if fState}
|
|
||||||
<FileView
|
|
||||||
{treeShowing}
|
|
||||||
{xrefTableShowing}
|
|
||||||
{pagesShowing}
|
|
||||||
{fState}
|
|
||||||
height={fileViewHeight}
|
|
||||||
></FileView>
|
|
||||||
{:else}
|
{:else}
|
||||||
<WelcomeScreen {upload}></WelcomeScreen>
|
<WelcomeScreen {upload}></WelcomeScreen>
|
||||||
{/if}
|
{/if}
|
||||||
@ -143,6 +119,7 @@
|
|||||||
<Footer {fState} {footerHeight} ></Footer>
|
<Footer {fState} {footerHeight} ></Footer>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
||||||
<style global>
|
<style global>
|
||||||
/* Resiable layout */
|
/* Resiable layout */
|
||||||
:global(.splitpanes.forge-movable) :global(.splitpanes__pane) {
|
:global(.splitpanes.forge-movable) :global(.splitpanes__pane) {
|
||||||
@ -156,8 +133,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.splitpanes.forge-movable) :global(.splitpanes__splitter:before),
|
:global(.splitpanes.forge-movable) :global(.splitpanes__splitter:before), :global(.splitpanes.forge-movable) :global(.splitpanes__splitter:after) {
|
||||||
:global(.splitpanes.forge-movable) :global(.splitpanes__splitter:after) {
|
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@ -167,103 +143,61 @@
|
|||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.splitpanes.forge-movable)
|
:global(.splitpanes.forge-movable) :global(.splitpanes__splitter:hover:before), :global(.splitpanes.forge-movable) :global(.splitpanes__splitter:hover:after) {
|
||||||
:global(.splitpanes__splitter:hover:before),
|
|
||||||
:global(.splitpanes.forge-movable)
|
|
||||||
:global(.splitpanes__splitter:hover:after) {
|
|
||||||
background-color: var(--boundary-color);
|
background-color: var(--boundary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.splitpanes.forge-movable)
|
:global(.splitpanes.forge-movable) :global(.splitpanes__splitter:first-child) {
|
||||||
:global(.splitpanes__splitter:first-child) {
|
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes)
|
:global(.forge-movable.splitpanes) :global(.splitpanes) :global(.splitpanes__splitter) {
|
||||||
:global(.splitpanes)
|
|
||||||
:global(.splitpanes__splitter) {
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--vertical)
|
:global(.forge-movable.splitpanes--vertical) > :global(.splitpanes__splitter),
|
||||||
> :global(.splitpanes__splitter),
|
:global(.forge-movable) :global(.splitpanes--vertical) > :global(.splitpanes__splitter) {
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--vertical)
|
|
||||||
> :global(.splitpanes__splitter) {
|
|
||||||
width: 2px;
|
width: 2px;
|
||||||
border-left: 1px solid var(--boundary-color);
|
border-left: 1px solid var(--boundary-color);
|
||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--vertical)
|
:global(.forge-movable.splitpanes--vertical) > :global(.splitpanes__splitter:before), :global(.forge-movable.splitpanes--vertical) > :global(.splitpanes__splitter:after), :global(.forge-movable) :global(.splitpanes--vertical) > :global(.splitpanes__splitter:before), :global(.forge-movable) :global(.splitpanes--vertical) > :global(.splitpanes__splitter:after) {
|
||||||
> :global(.splitpanes__splitter:before),
|
|
||||||
:global(.forge-movable.splitpanes--vertical)
|
|
||||||
> :global(.splitpanes__splitter:after),
|
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--vertical)
|
|
||||||
> :global(.splitpanes__splitter:before),
|
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--vertical)
|
|
||||||
> :global(.splitpanes__splitter:after) {
|
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--vertical)
|
:global(.forge-movable.splitpanes--vertical) > :global(.splitpanes__splitter:before),
|
||||||
> :global(.splitpanes__splitter:before),
|
:global(.forge-movable) :global(.splitpanes--vertical) > :global(.splitpanes__splitter:before) {
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--vertical)
|
|
||||||
> :global(.splitpanes__splitter:before) {
|
|
||||||
margin-left: -2px;
|
margin-left: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--vertical)
|
:global(.forge-movable.splitpanes--vertical) > :global(.splitpanes__splitter:after),
|
||||||
> :global(.splitpanes__splitter:after),
|
:global(.forge-movable) :global(.splitpanes--vertical) > :global(.splitpanes__splitter:after) {
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--vertical)
|
|
||||||
> :global(.splitpanes__splitter:after) {
|
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--horizontal)
|
:global(.forge-movable.splitpanes--horizontal) > :global(.splitpanes__splitter),
|
||||||
> :global(.splitpanes__splitter),
|
:global(.forge-movable) :global(.splitpanes--horizontal) > :global(.splitpanes__splitter) {
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--horizontal)
|
|
||||||
> :global(.splitpanes__splitter) {
|
|
||||||
height: 2px;
|
height: 2px;
|
||||||
border-top: 1px solid var(--boundary-color);
|
border-top: 1px solid var(--boundary-color);
|
||||||
cursor: row-resize;
|
cursor: row-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--horizontal)
|
:global(.forge-movable.splitpanes--horizontal) > :global(.splitpanes__splitter:before), :global(.forge-movable.splitpanes--horizontal) > :global(.splitpanes__splitter:after), :global(.forge-movable) :global(.splitpanes--horizontal) > :global(.splitpanes__splitter:before), :global(.forge-movable) :global(.splitpanes--horizontal) > :global(.splitpanes__splitter:after) {
|
||||||
> :global(.splitpanes__splitter:before),
|
|
||||||
:global(.forge-movable.splitpanes--horizontal)
|
|
||||||
> :global(.splitpanes__splitter:after),
|
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--horizontal)
|
|
||||||
> :global(.splitpanes__splitter:before),
|
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--horizontal)
|
|
||||||
> :global(.splitpanes__splitter:after) {
|
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--horizontal)
|
:global(.forge-movable.splitpanes--horizontal) > :global(.splitpanes__splitter:before),
|
||||||
> :global(.splitpanes__splitter:before),
|
:global(.forge-movable) :global(.splitpanes--horizontal) > :global(.splitpanes__splitter:before) {
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--horizontal)
|
|
||||||
> :global(.splitpanes__splitter:before) {
|
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.forge-movable.splitpanes--horizontal)
|
:global(.forge-movable.splitpanes--horizontal) > :global(.splitpanes__splitter:after),
|
||||||
> :global(.splitpanes__splitter:after),
|
:global(.forge-movable) :global(.splitpanes--horizontal) > :global(.splitpanes__splitter:after) {
|
||||||
:global(.forge-movable)
|
|
||||||
:global(.splitpanes--horizontal)
|
|
||||||
> :global(.splitpanes__splitter:after) {
|
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,6 +211,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:global(div.splitpanes--horizontal.splitpanes--dragging) {
|
:global(div.splitpanes--horizontal.splitpanes--dragging) {
|
||||||
cursor: row-resize;
|
cursor: row-resize;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,62 +1,36 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import type ContentModel from "../models/ContentModel.svelte";
|
import type ContentModel from "../models/ContentModel.svelte";
|
||||||
import { onMount } from "svelte";
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import type FileViewState from "../models/FileViewState.svelte";
|
|
||||||
|
|
||||||
let { fState, height }: { fState: FileViewState; height: number } =
|
let {id, path, h}: {id: string, path: string, h: number} = $props();
|
||||||
$props();
|
|
||||||
let h = $derived(height);
|
|
||||||
let path = $derived(fState.prim?.getFirstJump()?.toString());
|
|
||||||
let id = $derived(fState.file.id);
|
|
||||||
let contents: ContentModel | undefined = $state(undefined);
|
let contents: ContentModel | undefined = $state(undefined);
|
||||||
let editorContainer: HTMLElement;
|
|
||||||
let editor: monaco.editor.IStandaloneCodeEditor;
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
editor = monaco.editor.create(editorContainer, {
|
|
||||||
value: "",
|
|
||||||
language: "plaintext",
|
|
||||||
theme: "vs-dark",
|
|
||||||
minimap: { enabled: false },
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
fontSize: 14,
|
|
||||||
automaticLayout: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
editor.dispose();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
$effect( () => {
|
$effect( () => {
|
||||||
loadContents(path, id);
|
loadContents(path, id)
|
||||||
});
|
|
||||||
|
|
||||||
$inspect(contents);
|
|
||||||
function loadContents(path: string | undefined, id: string) {
|
|
||||||
console.log("Loading contents for", path, id);
|
|
||||||
if (!path || !id) return;
|
|
||||||
|
|
||||||
invoke<ContentModel>("get_contents", { id, path })
|
|
||||||
.then((result) => {
|
|
||||||
console.log("Contents loaded", result);
|
|
||||||
contents = result;
|
|
||||||
if (contents && editor) {
|
|
||||||
const text = contents.parts
|
|
||||||
.map((part) => part.join("\n"))
|
|
||||||
.join(
|
|
||||||
"\n\n%-------------------% EOF %-------------------%\n\n",
|
|
||||||
);
|
|
||||||
editor.setValue(text);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((err) => console.error(err));
|
|
||||||
|
function loadContents(id: string, path: string) {
|
||||||
|
invoke<ContentModel>("get_contents", {id: id, path: path})
|
||||||
|
.then(result => contents = result)
|
||||||
|
.catch(err => console.error(err));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
{#if contents}
|
||||||
|
<div class="overflow-auto">
|
||||||
|
<div class="whitespace-nowrap" style="height: {h}px">
|
||||||
|
{#each contents.parts as part }
|
||||||
|
<div class="part">
|
||||||
|
{#each part as line}
|
||||||
|
<div class="line">{line}</div>
|
||||||
|
{/each}
|
||||||
|
|
||||||
<div bind:this={editorContainer} style="height: {h}px; width: 100%;"></div>
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
{"Loading id: " + id + " Path: " + path}
|
||||||
|
{/if}
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@ -4,34 +4,20 @@
|
|||||||
import PrimitiveView from "./PrimitiveView.svelte";
|
import PrimitiveView from "./PrimitiveView.svelte";
|
||||||
import TreeView from "./TreeView.svelte";
|
import TreeView from "./TreeView.svelte";
|
||||||
import type FileViewState from "../models/FileViewState.svelte";
|
import type FileViewState from "../models/FileViewState.svelte";
|
||||||
import { createEventDispatcher, onMount } from "svelte";
|
import {onMount} from 'svelte';
|
||||||
import PageList from "./PageList.svelte";
|
import PageList from "./PageList.svelte";
|
||||||
import ContentsView from "./ContentsView.svelte";
|
|
||||||
import TreeViewNode from "../models/TreeViewNode.svelte";
|
|
||||||
import TreeViewState from "../models/TreeViewState.svelte";
|
|
||||||
import type { PathSelectedEvent } from "../events/PathSelectedEvent";
|
|
||||||
|
|
||||||
let {
|
let {treeShowing, xrefTableShowing, pagesShowing, fState, height}: {
|
||||||
treeShowing,
|
treeShowing: boolean,
|
||||||
xrefTableShowing,
|
xrefTableShowing: boolean,
|
||||||
pagesShowing,
|
pagesShowing: boolean,
|
||||||
fState,
|
fState: FileViewState,
|
||||||
height,
|
height: number
|
||||||
}: {
|
} = $props()
|
||||||
treeShowing: boolean;
|
|
||||||
xrefTableShowing: boolean;
|
|
||||||
pagesShowing: boolean;
|
|
||||||
fState: FileViewState;
|
|
||||||
height: number;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let width: number = $state(0);
|
|
||||||
let xRefTableWidth = $derived(xrefTableShowing ? 281 : 0);
|
|
||||||
let splitPanesWidth: number = $derived(width - xRefTableWidth);
|
|
||||||
|
|
||||||
function handleKeydown(event: KeyboardEvent) {
|
function handleKeydown(event: KeyboardEvent) {
|
||||||
// Check for "Alt + Left Arrow"
|
// Check for "Alt + Left Arrow"
|
||||||
if (event.altKey && event.key === "ArrowLeft") {
|
if (event.altKey && event.key === 'ArrowLeft') {
|
||||||
fState.popPath();
|
fState.popPath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,87 +30,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
window.addEventListener("keydown", handleKeydown);
|
window.addEventListener('keydown', handleKeydown);
|
||||||
window.addEventListener("mousedown", handleMouseButton);
|
window.addEventListener('mousedown', handleMouseButton);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", handleKeydown);
|
window.removeEventListener('keydown', handleKeydown);
|
||||||
window.removeEventListener("mousedown", handleMouseButton);
|
window.removeEventListener('mousedown', handleMouseButton);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function pathSelectedHandler(event: PathSelectedEvent) {
|
|
||||||
console.log("Path selected", event.detail.path);
|
|
||||||
fState.selectPath(event.detail.path);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:clientWidth={width} class="file-view-container">
|
<Splitpanes theme="forge-movable">
|
||||||
<div
|
<Pane size={treeShowing || pagesShowing ? 15 : 0} minSize={treeShowing || pagesShowing ? 1 : 0} maxSize={treeShowing || pagesShowing ? 100 : 0}>
|
||||||
class="flex flex-row"
|
<Splitpanes theme="forge-movable" horizontal>
|
||||||
style="width: {splitPanesWidth}px; height: {height}px"
|
<Pane size={treeShowing ? pagesShowing ? 50 : 100 : 0} minSize={treeShowing ? 2 : 0} maxSize={treeShowing ? 100 : 0}>
|
||||||
>
|
<TreeView {fState}></TreeView>
|
||||||
<Splitpanes theme="forge-movable" pushOtherPanes={false}>
|
|
||||||
<Pane
|
|
||||||
size={treeShowing || pagesShowing ? 15 : 0}
|
|
||||||
minSize={treeShowing || pagesShowing ? 2 : 0}
|
|
||||||
maxSize={treeShowing || pagesShowing ? 100 : 0}
|
|
||||||
>
|
|
||||||
{#if pagesShowing}
|
|
||||||
<PageList {fState} h={height}></PageList>
|
|
||||||
{:else if treeShowing}
|
|
||||||
<TreeView
|
|
||||||
bind:pathSelectedHandler
|
|
||||||
file_id={fState.file.id}
|
|
||||||
root={TreeViewNode.TRAILER}
|
|
||||||
></TreeView>
|
|
||||||
{/if}
|
|
||||||
</Pane>
|
</Pane>
|
||||||
|
<Pane size={pagesShowing ? treeShowing ? 50 : 100 : 0} minSize={pagesShowing ? 2 : 0} maxSize={pagesShowing ? 100 : 0}>
|
||||||
|
<PageList {fState}></PageList>
|
||||||
|
</Pane>
|
||||||
|
</Splitpanes>
|
||||||
|
</Pane>
|
||||||
|
|
||||||
|
|
||||||
<Pane minSize={1}>
|
<Pane minSize={1}>
|
||||||
<div>
|
<div>
|
||||||
<PrimitiveView {fState} {height}></PrimitiveView>
|
<PrimitiveView {fState} {height}></PrimitiveView>
|
||||||
</div>
|
</div>
|
||||||
</Pane>
|
</Pane>
|
||||||
{#if fState.prim?.isPage()}
|
|
||||||
<Pane minSize={1}>
|
<Pane size={xrefTableShowing ? 16 : 0} minSize={xrefTableShowing ? 1 : 0} maxSize={xrefTableShowing ? 100 : 0}>
|
||||||
<div class="overflow-hidden">
|
<XRefTable {fState}></XRefTable>
|
||||||
<ContentsView {fState} {height}></ContentsView>
|
|
||||||
</div>
|
|
||||||
</Pane>
|
</Pane>
|
||||||
{/if}
|
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</div>
|
|
||||||
{#if xrefTableShowing}
|
|
||||||
<div class="xref-modal" class:visible={xrefTableShowing}>
|
|
||||||
<XRefTable {fState} {height}></XRefTable>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.file-view-container {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xref-modal {
|
|
||||||
position: absolute;
|
|
||||||
border-left: 1px solid var(--border-color);
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 280px;
|
|
||||||
background: var(--background-color);
|
|
||||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
|
||||||
z-index: 1000;
|
|
||||||
overflow-y: auto;
|
|
||||||
transform: translateX(100%);
|
|
||||||
transition: transform 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xref-modal.visible {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@ -1,18 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type FileViewState from "../models/FileViewState.svelte";
|
import type FileViewState from "../models/FileViewState.svelte";
|
||||||
import type { Trace } from "../models/Primitive.svelte";
|
import type { DetailPath } from "../models/Primitive.svelte";
|
||||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||||
import { CaretRightOutline } from "flowbite-svelte-icons";
|
|
||||||
|
|
||||||
class Path {
|
class Path {
|
||||||
public value: string;
|
public value: string;
|
||||||
public jump?: string;
|
public jump?: string;
|
||||||
public index: number;
|
|
||||||
|
|
||||||
constructor(value: string, index: number, jump?: string) {
|
constructor(value: string, jump?: string) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.jump = jump;
|
this.jump = jump;
|
||||||
this.index = index;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,59 +18,57 @@
|
|||||||
footerHeight,
|
footerHeight,
|
||||||
}: { fState: FileViewState | undefined; footerHeight: number } = $props();
|
}: { fState: FileViewState | undefined; footerHeight: number } = $props();
|
||||||
let elements: Path[] | undefined = $derived(
|
let elements: Path[] | undefined = $derived(
|
||||||
fState && fState.prim ? toElements(fState.prim.trace) : undefined,
|
fState && fState.prim ? toElements(fState.prim.detail_path) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
function toElements(path: Trace[]): Path[] {
|
$inspect(elements);
|
||||||
|
$inspect(fState?.prim?.detail_path);
|
||||||
|
function toElements(path: DetailPath[]): Path[] {
|
||||||
if (path.length == 0) {
|
if (path.length == 0) {
|
||||||
return [new Path("Trailer", 0)];
|
return [{ value: "Trailer" } as Path];
|
||||||
}
|
}
|
||||||
|
|
||||||
let display: Path[] = [];
|
let display: Path[] = [];
|
||||||
let lastJump: string | undefined;
|
if (path[0].key === "/") {
|
||||||
for (let i = 0; i < path.length; i++) {
|
display.push(new Path("Trailer"));
|
||||||
const pathElement = path[i];
|
} else {
|
||||||
|
display.push(new Path(path[0].key, path[0].last_jump));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastJump = path[0].last_jump;
|
||||||
|
|
||||||
|
for (const pathElement of path.slice(1, path.length)) {
|
||||||
if (pathElement.last_jump !== lastJump) {
|
if (pathElement.last_jump !== lastJump) {
|
||||||
lastJump = pathElement.last_jump;
|
lastJump = pathElement.last_jump;
|
||||||
display.push(new Path(pathElement.key, i, lastJump));
|
display.push(new Path(pathElement.key, lastJump));
|
||||||
} else {
|
} else {
|
||||||
display.push(new Path(pathElement.key, i, undefined));
|
display.push(new Path(pathElement.key, undefined));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectPath(path: Path) {
|
|
||||||
if (fState) {
|
|
||||||
let newPath = fState.copyPath().slice(0, path.index + 1);
|
|
||||||
fState.selectPath(newPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="footer" style="height: {footerHeight}px">
|
<div
|
||||||
|
class="bg-forge-prim border border-forge-bound"
|
||||||
|
style="height: {footerHeight}px"
|
||||||
|
>
|
||||||
|
<div class="m-1 flex flex-row">
|
||||||
{#if elements}
|
{#if elements}
|
||||||
{#each elements as path}
|
{#each elements as path}
|
||||||
<button
|
<button class="m-1 flex flex-row mt-auto mb-auto">
|
||||||
class="flex flex-row items-center ml-2"
|
|
||||||
onclick={() => selectPath(path)}
|
|
||||||
>
|
|
||||||
{#if path.jump}
|
{#if path.jump}
|
||||||
|
<div class="m-auto" style="height: {footerHeight}px">
|
||||||
<PrimitiveIcon ptype={"Reference"}></PrimitiveIcon>
|
<PrimitiveIcon ptype={"Reference"}></PrimitiveIcon>
|
||||||
{:else}
|
</div>
|
||||||
<CaretRightOutline class="text-forge-sec" />
|
|
||||||
{/if}
|
{/if}
|
||||||
<p class="text-xs ml-1">{path.value}</p>
|
<p class="text-xs ml-1 c">{path.value}</p>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.footer {
|
|
||||||
@apply bg-forge-prim border border-forge-bound flex flex-row items-center;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -3,16 +3,19 @@
|
|||||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||||
import type PageModel from "../models/PageModel";
|
import type PageModel from "../models/PageModel";
|
||||||
|
|
||||||
let { fState, h }: { fState: FileViewState; h: number } = $props();
|
let {fState}: { fState: FileViewState } = $props();
|
||||||
|
let h: number = $state(100);
|
||||||
let selected: PageModel | undefined = $state(undefined);
|
let selected: PageModel | undefined = $state(undefined);
|
||||||
|
|
||||||
function handlePageSelect(page: PageModel) {
|
function handlePageSelect(page: PageModel) {
|
||||||
selected = page;
|
selected = page
|
||||||
fState.selectPath(["Page" + page.page_num]);
|
fState.selectPath([page.id]);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div bind:clientHeight={h} class="full-container">
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
|
<div class="w-[251px]">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -21,15 +24,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
<div class="overflow-y-auto" style="height: {h - 25}px">
|
<div class="overflow-y-auto" style="height: {h-55}px">
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each fState.file.pages as page}
|
{#each fState.file.pages as page}
|
||||||
<tr
|
<tr class:selected={page === selected} class="hover:bg-forge-sec" ondblclick={() => handlePageSelect(page)}>
|
||||||
class:selected={page === selected}
|
|
||||||
class="hover:bg-forge-sec"
|
|
||||||
ondblclick={() => handlePageSelect(page)}
|
|
||||||
>
|
|
||||||
<td class="page-cell t-data">
|
<td class="page-cell t-data">
|
||||||
<div class="key-field">
|
<div class="key-field">
|
||||||
<PrimitiveIcon ptype={"Reference"}/>
|
<PrimitiveIcon ptype={"Reference"}/>
|
||||||
@ -38,20 +37,21 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="ref-cell t-data">{page.obj_num}</td>
|
<td class="ref-cell t-data">{page.id}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.key-field {
|
.key-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3px;
|
gap: 3px
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
@ -64,7 +64,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.t-data {
|
.t-data {
|
||||||
@apply border border-forge-sec text-xs text-forge-text text-left;
|
@apply border border-forge-sec text-sm text-forge-text text-left;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
@ -78,4 +78,5 @@
|
|||||||
@apply min-w-[150px] w-[150px] min-h-[20px] h-[20px] p-1 m-0;
|
@apply min-w-[150px] w-[150px] min-h-[20px] h-[20px] p-1 m-0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
let { fState, height }: { fState: FileViewState; height: number } =
|
let { fState, height }: { fState: FileViewState; height: number } =
|
||||||
$props();
|
$props();
|
||||||
let bodyHeight = $derived(height - 24);
|
let bodyHeight = $derived(height - 54);
|
||||||
|
let selected: Primitive | undefined = $state(undefined);
|
||||||
let prim = $derived(fState.prim);
|
let prim = $derived(fState.prim);
|
||||||
let showContents = $state(false);
|
let showContents = $state(false);
|
||||||
$inspect(fState.highlightedPrim);
|
|
||||||
function handlePrimSelect(prim: Primitive) {
|
function handlePrimSelect(prim: Primitive) {
|
||||||
if (prim.isContainer()) {
|
if (prim.isContainer()) {
|
||||||
if (!prim) {
|
if (!prim) {
|
||||||
@ -30,10 +30,8 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="page-cell t-header border-forge-prim">Key</td
|
<td class="page-cell t-header border-forge-prim">Key</td>
|
||||||
>
|
<td class="ref-cell t-header border-forge-prim">Type</td>
|
||||||
<td class="ref-cell t-header border-forge-prim">Type</td
|
|
||||||
>
|
|
||||||
<td class="cell t-header border-forge-sec">Value</td>
|
<td class="cell t-header border-forge-sec">Value</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -43,10 +41,9 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{#each prim.children as entry}
|
{#each prim.children as entry}
|
||||||
<tr
|
<tr
|
||||||
class:selected={entry.key ===
|
class:selected={entry === selected}
|
||||||
fState.highlightedPrim?.key}
|
|
||||||
class="hover:bg-forge-sec"
|
class="hover:bg-forge-sec"
|
||||||
onclick={() => (fState.highlightedPrim = entry)}
|
onclick={() => (selected = entry)}
|
||||||
ondblclick={() => handlePrimSelect(entry)}
|
ondblclick={() => handlePrimSelect(entry)}
|
||||||
>
|
>
|
||||||
<td class="page-cell t-data">
|
<td class="page-cell t-data">
|
||||||
@ -63,6 +60,21 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<p class="flex flex-row">
|
||||||
|
Dict Type: {prim.sub_type}
|
||||||
|
</p>
|
||||||
|
{#if prim.isPage()}
|
||||||
|
<button onclick={() => (showContents = true)}
|
||||||
|
>View contents</button
|
||||||
|
>
|
||||||
|
{#if showContents}
|
||||||
|
<ContentsView
|
||||||
|
id={fState.file.id}
|
||||||
|
path={prim.getLastJump().toString()}
|
||||||
|
h={500}
|
||||||
|
></ContentsView>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,41 +1,25 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {BookOpenSolid, CodeBranchSolid} from "flowbite-svelte-icons";
|
import {BookOpenSolid, CodeBranchSolid} from "flowbite-svelte-icons";
|
||||||
|
|
||||||
let {
|
let {tree = $bindable(false), pages = $bindable(false)}: { tree: boolean, pages: boolean } = $props();
|
||||||
tree = $bindable(false),
|
|
||||||
pages = $bindable(false),
|
|
||||||
}: { tree: boolean; pages: boolean } = $props();
|
|
||||||
|
|
||||||
function toggleTree() {
|
function toggleTree() {
|
||||||
tree = !tree;
|
tree = !tree;
|
||||||
if (tree) {
|
|
||||||
pages = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePages() {
|
function togglePages() {
|
||||||
pages = !pages;
|
pages = !pages;
|
||||||
if (pages) {
|
|
||||||
tree = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="grid grid-cols-1">
|
<div class="grid grid-cols-1">
|
||||||
<button
|
<button class={ tree ? "tool-button active" : "tool-button" } onclick={toggleTree} >
|
||||||
class={tree ? "tool-button active" : "tool-button"}
|
|
||||||
onclick={toggleTree}
|
|
||||||
>
|
|
||||||
<div class="justify-center flex m-0">
|
<div class="justify-center flex m-0">
|
||||||
<CodeBranchSolid class="rotate-180 scale-x-[-1]"/>
|
<CodeBranchSolid class="rotate-180 scale-x-[-1]"/>
|
||||||
</div>
|
</div>
|
||||||
<b class="button-title">Tree</b>
|
<b class="button-title">Tree</b>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button id="#page" class={ pages ? "tool-button active" : "tool-button" } onclick={togglePages}>
|
||||||
id="#page"
|
|
||||||
class={pages ? "tool-button active" : "tool-button"}
|
|
||||||
onclick={togglePages}
|
|
||||||
>
|
|
||||||
<div class="justify-center flex ">
|
<div class="justify-center flex ">
|
||||||
<BookOpenSolid/>
|
<BookOpenSolid/>
|
||||||
</div>
|
</div>
|
||||||
@ -43,6 +27,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.button-title {
|
.button-title {
|
||||||
@apply text-forge-text_sec text-xs;
|
@apply text-forge-text_sec text-xs;
|
||||||
|
|||||||
@ -2,21 +2,12 @@
|
|||||||
import type Primitive from "../models/Primitive.svelte";
|
import type Primitive from "../models/Primitive.svelte";
|
||||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||||
import {CaretRightOutline, CaretDownOutline} from "flowbite-svelte-icons";
|
import {CaretRightOutline, CaretDownOutline} from "flowbite-svelte-icons";
|
||||||
import type TreeViewState from "../models/TreeViewState.svelte";
|
import type FileViewState from "../models/FileViewState.svelte";
|
||||||
import { PathSelectedEvent } from "../events/PathSelectedEvent";
|
import {arraysAreEqual} from "../utils.js";
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
|
let {prim, path, fState}: { prim: Primitive | undefined, path: string[], fState: FileViewState } = $props();
|
||||||
|
let active = $derived(arraysAreEqual(path, fState.path))
|
||||||
|
|
||||||
let {
|
|
||||||
parent_view,
|
|
||||||
parent_path,
|
|
||||||
treeState,
|
|
||||||
pathSelectedHandler = $bindable(),
|
|
||||||
}: {
|
|
||||||
parent_view: Primitive;
|
|
||||||
parent_path: string[];
|
|
||||||
treeState: TreeViewState;
|
|
||||||
pathSelectedHandler: any;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
function copyPathAndAppend(key: string): string[] {
|
function copyPathAndAppend(key: string): string[] {
|
||||||
const _path = copyPath();
|
const _path = copyPath();
|
||||||
@ -27,61 +18,47 @@
|
|||||||
function copyPath(): string[] {
|
function copyPath(): string[] {
|
||||||
const _path: string[] = [];
|
const _path: string[] = [];
|
||||||
|
|
||||||
for (let item of parent_path) {
|
for (let item of path) {
|
||||||
_path.push(item);
|
_path.push(item);
|
||||||
}
|
}
|
||||||
return _path;
|
return _path;
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectItem(child: Primitive) {
|
function selectItem(child: Primitive) {
|
||||||
|
if (child.isContainer()) {
|
||||||
let _path = copyPathAndAppend(child.key);
|
let _path = copyPathAndAppend(child.key);
|
||||||
console.log("Selecting from tree", _path);
|
fState.expandTree(_path);
|
||||||
treeState.expandTree(_path);
|
fState.selectPath(_path);
|
||||||
|
} else {
|
||||||
const event = new PathSelectedEvent(_path);
|
let _path = copyPath();
|
||||||
pathSelectedHandler(event);
|
fState.selectPath(_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if parent_view}
|
|
||||||
|
</script>
|
||||||
|
{#if prim}
|
||||||
<ul class="active">
|
<ul class="active">
|
||||||
{#each parent_view.children as child}
|
{#each prim.children as child}
|
||||||
<li>
|
<li>
|
||||||
<div class="item ">
|
<div class="item ">
|
||||||
{#if child.children.length > 0}
|
{#if child.children.length > 0}
|
||||||
<button
|
<button onclick={() => fState.collapseTree(copyPathAndAppend(child.key))}><span class="caret"><CaretDownOutline/></span></button>
|
||||||
onclick={() =>
|
|
||||||
treeState.collapseTree(
|
|
||||||
copyPathAndAppend(child.key),
|
|
||||||
)}
|
|
||||||
><span class="caret"><CaretDownOutline /></span
|
|
||||||
></button
|
|
||||||
>
|
|
||||||
{:else if child.isContainer()}
|
{:else if child.isContainer()}
|
||||||
<button
|
<button onclick={() => fState.expandTree(copyPathAndAppend(child.key))}><span class="caret"><CaretRightOutline/></span></button>
|
||||||
onclick={() =>
|
|
||||||
treeState.expandTree(
|
|
||||||
copyPathAndAppend(child.key),
|
|
||||||
)}
|
|
||||||
><span class="caret"><CaretRightOutline /></span
|
|
||||||
></button
|
|
||||||
>
|
|
||||||
{:else}
|
{:else}
|
||||||
<span class="no-caret"></span>
|
<span class="no-caret"></span>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<button class="select-button" ondblclick={() => selectItem(child)}>
|
||||||
class="select-button"
|
|
||||||
ondblclick={() => selectItem(child)}
|
|
||||||
>
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<PrimitiveIcon ptype={child.ptype}/>
|
<PrimitiveIcon ptype={child.ptype}/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<p>
|
<p>
|
||||||
{child.key}
|
{child.key}
|
||||||
</p>
|
</p>
|
||||||
{#if child.sub_type}
|
{#if child.subType}
|
||||||
<p class="ml-1 text-forge-sec small">
|
<p class="ml-1 text-forge-sec small">
|
||||||
{child.sub_type}
|
{child.subType}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@ -89,18 +66,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{#if child.children.length > 0}
|
{#if child.children.length > 0}
|
||||||
<svelte:self
|
<svelte:self prim={child} path={copyPathAndAppend(child.key)} {fState}></svelte:self>
|
||||||
parent_view={child}
|
|
||||||
parent_path={copyPathAndAppend(child.key)}
|
|
||||||
{treeState}
|
|
||||||
bind:pathSelectedHandler
|
|
||||||
></svelte:self>
|
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.select-button {
|
.select-button {
|
||||||
@apply hover:bg-forge-sec pr-3;
|
@apply hover:bg-forge-sec pr-3;
|
||||||
@ -130,8 +103,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Remove default bullets */
|
/* Remove default bullets */
|
||||||
ul,
|
ul, #myUL {
|
||||||
#myUL {
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +111,7 @@
|
|||||||
@apply pl-5;
|
@apply pl-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.no-caret {
|
.no-caret {
|
||||||
@apply pl-5;
|
@apply pl-5;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -149,6 +122,7 @@
|
|||||||
@apply text-forge-sec;
|
@apply text-forge-sec;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none; /* Prevent text selection */
|
user-select: none; /* Prevent text selection */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the caret/arrow with a unicode, and style it */
|
/* Create the caret/arrow with a unicode, and style it */
|
||||||
|
|||||||
@ -1,58 +1,39 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import TreeNode from "./TreeNode.svelte";
|
import TreeNode from "./TreeNode.svelte";
|
||||||
|
import type FileViewState from "../models/FileViewState.svelte";
|
||||||
|
import type Primitive from "../models/Primitive.svelte";
|
||||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||||
import TreeViewState from "../models/TreeViewState.svelte";
|
|
||||||
import type TreeViewNode from "../models/TreeViewNode.svelte";
|
|
||||||
|
|
||||||
let {
|
let {fState}: { fState: FileViewState } = $props()
|
||||||
file_id,
|
let prim: Primitive | undefined = $derived(fState.treeView);
|
||||||
root,
|
|
||||||
pathSelectedHandler = $bindable(),
|
|
||||||
active = true,
|
|
||||||
}: {
|
|
||||||
file_id: string;
|
|
||||||
root: TreeViewNode;
|
|
||||||
pathSelectedHandler: any;
|
|
||||||
active: boolean;
|
|
||||||
} = $props();
|
|
||||||
let h = $state(100);
|
let h = $state(100);
|
||||||
let treeState = $state(new TreeViewState(file_id, root));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
</script>
|
||||||
<div bind:clientHeight={h} class="full-container">
|
<div bind:clientHeight={h} class="full-container">
|
||||||
<div class="overflow-auto" style="height: {h}px">
|
<div class="overflow-auto" style="height: {h}px">
|
||||||
<ul id="myUL">
|
<ul id="myUL">
|
||||||
{#if treeState.view}
|
{#if prim}
|
||||||
<li>
|
<li>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<PrimitiveIcon ptype={"Dictionary"}/>
|
<PrimitiveIcon ptype={"Dictionary"}/>
|
||||||
{"Trailer "}
|
{"Trailer "}
|
||||||
</div>
|
</div>
|
||||||
{#if active}
|
<TreeNode {prim} path={["/"]} {fState}></TreeNode>
|
||||||
<TreeNode
|
|
||||||
bind:pathSelectedHandler
|
|
||||||
parent_view={treeState.view}
|
|
||||||
parent_path={treeState.view.getPath()}
|
|
||||||
{treeState}
|
|
||||||
></TreeNode>
|
|
||||||
{/if}
|
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
getTrace
|
|
||||||
</div>
|
</div>
|
||||||
getPath
|
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.item {
|
.item {
|
||||||
@apply text-sm rounded-sm;
|
@apply text-sm rounded-sm;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
}
|
}
|
||||||
ul,
|
ul, #myUL {
|
||||||
#myUL {
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,44 +1,38 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import type FileViewState from "../models/FileViewState.svelte";
|
import type FileViewState from "../models/FileViewState.svelte";
|
||||||
import type XRefEntry from "../models/XRefEntry";
|
import type XRefEntry from "../models/XRefEntry";
|
||||||
|
|
||||||
const cellH = 25;
|
const cellH = 25;
|
||||||
const headerOffset = 2 * cellH;
|
const headerOffset = 80;
|
||||||
let { fState, height }: { fState: FileViewState; height: number } =
|
let {fState}: { fState: FileViewState } = $props();
|
||||||
$props();
|
|
||||||
|
|
||||||
|
|
||||||
|
let viewHeight: number = $state(100);
|
||||||
let fillerHeight: number = $state(0);
|
let fillerHeight: number = $state(0);
|
||||||
|
|
||||||
let firstEntry = $state(0);
|
let firstEntry = $state(0);
|
||||||
let lastEntry = $state(100);
|
let lastEntry = $state(100);
|
||||||
let entriesToDisplay: XRefEntry[] = $derived(
|
let entriesToDisplay: XRefEntry[] = $derived(fState.xref_entries.slice(firstEntry, lastEntry));
|
||||||
fState.xref_entries.slice(firstEntry, lastEntry),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bodyViewHeight: number = $derived(Math.max(height - headerOffset, 0));
|
let bodyViewHeight: number = $derived(Math.max(viewHeight - headerOffset, 0));
|
||||||
$inspect(bodyViewHeight);
|
let selectedPath: number | string | undefined = $derived(fState.getLastJump());
|
||||||
let selectedPath: number | string | undefined = $derived(
|
let totalBodyHeight: number = $derived(Math.max(0, (fState.file.xref_entries * cellH) - headerOffset));
|
||||||
fState.getLastJump(),
|
|
||||||
);
|
|
||||||
let totalBodyHeight: number = $derived(
|
|
||||||
Math.max(0, fState.file.xref_entries * cellH - headerOffset),
|
|
||||||
);
|
|
||||||
let scrollContainer: HTMLElement;
|
let scrollContainer: HTMLElement;
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (typeof selectedPath === "number") {
|
if (typeof selectedPath === "number") {
|
||||||
smoothScrollTo(scrollContainer, selectedPath);
|
smoothScrollTo(scrollContainer, selectedPath)
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
)
|
||||||
|
|
||||||
function smoothScrollTo(scrollContainer: HTMLElement, target: number) {
|
function smoothScrollTo(scrollContainer: HTMLElement, target: number) {
|
||||||
|
|
||||||
const targetY = Math.max(target, 0) * cellH;
|
const targetY = Math.max(target, 0) * cellH;
|
||||||
const startY = scrollContainer.scrollTop;
|
const startY = scrollContainer.scrollTop;
|
||||||
|
|
||||||
if (
|
if (targetY - startY > 0 && targetY - startY < Math.max(viewHeight - headerOffset, 0)) {
|
||||||
targetY - startY > 0 &&
|
|
||||||
targetY - startY < Math.max(height - headerOffset, 0)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +65,11 @@
|
|||||||
|
|
||||||
fillerHeight = firstEntry * cellH;
|
fillerHeight = firstEntry * cellH;
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="overflow-x-auto" style="height: {height}px;">
|
|
||||||
|
</script>
|
||||||
|
<div bind:clientHeight={viewHeight} class="full-container">
|
||||||
|
<div class="overflow-x-auto">
|
||||||
<div class="w-[281px]">
|
<div class="w-[281px]">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@ -83,12 +79,7 @@
|
|||||||
<td class="cell t-header border-forge-prim">Type</td>
|
<td class="cell t-header border-forge-prim">Type</td>
|
||||||
<td class="cell t-header border-forge-sec">Offset</td>
|
<td class="cell t-header border-forge-sec">Offset</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr class={selectedPath === "/" ? "bg-forge-acc" : "hover:bg-forge-sec"} ondblclick={() => fState.selectXref(undefined)}>
|
||||||
class={selectedPath === "/"
|
|
||||||
? "bg-forge-acc"
|
|
||||||
: "hover:bg-forge-sec"}
|
|
||||||
ondblclick={() => fState.selectXref(undefined)}
|
|
||||||
>
|
|
||||||
<td class="cell t-data">Trailer</td>
|
<td class="cell t-data">Trailer</td>
|
||||||
<td class="cell t-data">65535</td>
|
<td class="cell t-data">65535</td>
|
||||||
<td class="cell t-data">Dictionary</td>
|
<td class="cell t-data">Dictionary</td>
|
||||||
@ -96,24 +87,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
<div
|
<div class="scrollContainer" style="height: {bodyViewHeight}px" onscroll={handleScroll} bind:this={scrollContainer}>
|
||||||
class="scrollContainer"
|
<div class="container" style="height: {totalBodyHeight - headerOffset}px">
|
||||||
style="height: {bodyViewHeight}px"
|
|
||||||
onscroll={handleScroll}
|
|
||||||
bind:this={scrollContainer}
|
|
||||||
>
|
|
||||||
<div class="container" style="height: {totalBodyHeight}px">
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="filler" style="height: {fillerHeight}px"
|
<tr class="filler" style="height: {fillerHeight}px"></tr>
|
||||||
></tr>
|
|
||||||
{#each entriesToDisplay as entry}
|
{#each entriesToDisplay as entry}
|
||||||
<tr
|
<tr class={selectedPath === entry.obj_num ? "bg-forge-acc" : "hover:bg-forge-sec"} ondblclick={() => fState.selectXref(entry)}>
|
||||||
class={selectedPath === entry.obj_num
|
|
||||||
? "bg-forge-acc"
|
|
||||||
: "hover:bg-forge-sec"}
|
|
||||||
ondblclick={() => fState.selectXref(entry)}
|
|
||||||
>
|
|
||||||
<td class="cell t-data">{entry.obj_num}</td>
|
<td class="cell t-data">{entry.obj_num}</td>
|
||||||
<td class="cell t-data">{entry.gen_num}</td>
|
<td class="cell t-data">{entry.gen_num}</td>
|
||||||
<td class="cell t-data">{entry.obj_type}</td>
|
<td class="cell t-data">{entry.obj_type}</td>
|
||||||
@ -126,6 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.t-header {
|
.t-header {
|
||||||
@ -149,15 +130,5 @@
|
|||||||
.scrollContainer {
|
.scrollContainer {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
.xref-modal {
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 281px;
|
|
||||||
background: var(--background-color);
|
|
||||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
|
||||||
z-index: 1000;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@ -1,10 +0,0 @@
|
|||||||
export class PathSelectedEvent extends CustomEvent<{ path: string[] }> {
|
|
||||||
static readonly eventName = 'pathselected';
|
|
||||||
|
|
||||||
constructor(path: string[]) {
|
|
||||||
super(PathSelectedEvent.eventName, {
|
|
||||||
detail: { path },
|
|
||||||
bubbles: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,10 +7,11 @@ import type XRefTable from "./XRefTable";
|
|||||||
|
|
||||||
export default class FileViewState {
|
export default class FileViewState {
|
||||||
|
|
||||||
public path: string[] = $state(["Trailer"]);
|
public path: string[] = $state(["/"]);
|
||||||
|
public treeRoot: TreeViewNode = $state(new TreeViewNode("/", [new TreeViewNode("Root", [])]));
|
||||||
public file: PdfFile;
|
public file: PdfFile;
|
||||||
public prim: Primitive | undefined = $state();
|
public prim: Primitive | undefined = $state();
|
||||||
public highlightedPrim: Primitive | undefined = $state();
|
public treeView: Primitive | undefined = $state();
|
||||||
public xref_entries: XRefEntry[] = $state([]);
|
public xref_entries: XRefEntry[] = $state([]);
|
||||||
|
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ export default class FileViewState {
|
|||||||
|
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.selectPath(this.path);
|
this.selectPath(this.path);
|
||||||
|
this.loadTreeView();
|
||||||
this.loadXrefEntries()
|
this.loadXrefEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,10 +27,6 @@ export default class FileViewState {
|
|||||||
return this.prim?.getLastJump()
|
return this.prim?.getLastJump()
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstJump(): string | number | undefined {
|
|
||||||
return this.prim?.getFirstJump()
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadXrefEntries() {
|
public loadXrefEntries() {
|
||||||
invoke<XRefTable>("get_xref_table", {id: this.file.id})
|
invoke<XRefTable>("get_xref_table", {id: this.file.id})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
@ -40,30 +38,83 @@ export default class FileViewState {
|
|||||||
public selectPath(newPath: string[]) {
|
public selectPath(newPath: string[]) {
|
||||||
invoke<Primitive>("get_prim_by_path", {id: this.file.id, path: this.mergePaths(newPath)})
|
invoke<Primitive>("get_prim_by_path", {id: this.file.id, path: this.mergePaths(newPath)})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
let _prim = new Primitive(result)
|
this.prim = new Primitive(result)
|
||||||
if (_prim.isContainer()) {
|
|
||||||
this.prim = _prim;
|
|
||||||
this.path = newPath
|
this.path = newPath
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.highlightedPrim = _prim;
|
|
||||||
this.selectPath(newPath.slice(0, newPath.length - 1))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(err => console.error(err));
|
.catch(err => console.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public loadTreeView() {
|
||||||
public getTreeRoot() {
|
invoke<Primitive>("get_prim_tree_by_path", {id: this.file.id, path: this.treeRoot})
|
||||||
return this.treeState;
|
.then(result => {
|
||||||
|
this.treeView = new Primitive(result);
|
||||||
|
}
|
||||||
|
).catch(err => console.error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTreeRoot() {
|
||||||
|
return this.treeRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public expandTree(path: string[]) {
|
||||||
|
if (path.length == 0) {
|
||||||
|
console.error("Empty path")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let node;
|
||||||
|
if (path[0] === "/") {
|
||||||
|
node = this.treeRoot;
|
||||||
|
} else {
|
||||||
|
console.error("Invalid Path " + path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let key of path.slice(1, path.length)) {
|
||||||
|
let _node: TreeViewNode | undefined = node.getChild(key)
|
||||||
|
if (_node) {
|
||||||
|
node = _node;
|
||||||
|
} else {
|
||||||
|
node = node.addChild(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.loadTreeView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public collapseTree(path: string[]) {
|
||||||
|
if (path.length == 0) {
|
||||||
|
console.error("Empty path")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let node;
|
||||||
|
if (path[0] === "/") {
|
||||||
|
node = this.treeRoot;
|
||||||
|
} else {
|
||||||
|
console.error("Invalid Path " + path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (path.length == 1) {
|
||||||
|
this.treeRoot.clearChildren();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let key of path.slice(1, path.length - 1)) {
|
||||||
|
if (node) {
|
||||||
|
node = node.getChild(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node) {
|
||||||
|
node.removeChild(path[path.length - 1]);
|
||||||
|
}
|
||||||
|
this.loadTreeView()
|
||||||
|
}
|
||||||
|
|
||||||
public getMergedPath() {
|
public getMergedPath() {
|
||||||
return this.mergePaths(this.path);
|
return this.mergePaths(this.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public displayPath() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public popPath() {
|
public popPath() {
|
||||||
let path = this.copyPath();
|
let path = this.copyPath();
|
||||||
if (path.length == 1) {
|
if (path.length == 1) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
export default interface PageModel {
|
export default interface PageModel {
|
||||||
key: string,
|
key: string,
|
||||||
obj_num: number,
|
id: number
|
||||||
page_num: number,
|
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ export default class Primitive {
|
|||||||
public sub_type: string;
|
public sub_type: string;
|
||||||
public value: string;
|
public value: string;
|
||||||
public children: Primitive[];
|
public children: Primitive[];
|
||||||
public trace: Trace[] = $state([]);
|
public detail_path: DetailPath[] = $state([]);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
p: Primitive
|
p: Primitive
|
||||||
@ -17,9 +17,9 @@ export default class Primitive {
|
|||||||
for (let child of p.children) {
|
for (let child of p.children) {
|
||||||
this.children.push(new Primitive(child));
|
this.children.push(new Primitive(child));
|
||||||
}
|
}
|
||||||
this.trace = [];
|
this.detail_path = [];
|
||||||
for (let path of p.trace) {
|
for (let path of p.detail_path) {
|
||||||
this.trace.push(path);
|
this.detail_path.push(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,36 +27,18 @@ export default class Primitive {
|
|||||||
return this.ptype === "Dictionary" || this.ptype === "Array" || this.ptype === "Reference" || this.ptype === "Stream";
|
return this.ptype === "Dictionary" || this.ptype === "Array" || this.ptype === "Reference" || this.ptype === "Stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPath(): string[] {
|
|
||||||
return this.trace.map(path => path.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getLastJump(): string | number {
|
public getLastJump(): string | number {
|
||||||
let path = this.trace[this.trace.length - 1].last_jump;
|
let path = this.detail_path[this.detail_path.length - 1].last_jump;
|
||||||
if (path === "/") {return path};
|
if (path === "/") {return path};
|
||||||
return +path;
|
return +path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isPage(): boolean {
|
public isPage(): boolean {
|
||||||
return this.trace[0].key.startsWith("Page") && !isNaN(+this.trace[0].key.replace("Page", ""));
|
return this.sub_type === "Page";
|
||||||
}
|
|
||||||
|
|
||||||
public getPageNumber(): number {
|
|
||||||
if (!this.isPage()) {
|
|
||||||
throw new Error("Primitive is not a page");
|
|
||||||
}
|
|
||||||
return +this.trace[0].key.replace("Page", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public getFirstJump(): string | number | undefined {
|
|
||||||
let path = this.trace[0].last_jump;
|
|
||||||
if (path === "Trailer") { return path };
|
|
||||||
if (path.startsWith("Page")) { return path };
|
|
||||||
return +path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Trace {
|
export interface DetailPath {
|
||||||
readonly key: string ;
|
readonly key: string ;
|
||||||
readonly last_jump: string ;
|
readonly last_jump: string ;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,6 @@ export default class TreeViewNode {
|
|||||||
this.children = children;
|
this.children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TRAILER = new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]);
|
|
||||||
|
|
||||||
|
|
||||||
public getChild(key: string) {
|
public getChild(key: string) {
|
||||||
return this.children.find(child => child.key === key);
|
return this.children.find(child => child.key === key);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
import Primitive from "./Primitive.svelte";
|
|
||||||
import TreeViewNode from "./TreeViewNode.svelte";
|
|
||||||
import type FileViewState from "./FileViewState.svelte";
|
|
||||||
|
|
||||||
export default class TreeViewState {
|
|
||||||
public root: TreeViewNode = $state(new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]));
|
|
||||||
public view: Primitive | undefined = $state();
|
|
||||||
file_id: string;
|
|
||||||
|
|
||||||
constructor(file_id: string, root: TreeViewNode) {
|
|
||||||
console.log("Creating tree view state", file_id, root);
|
|
||||||
this.file_id = file_id;
|
|
||||||
this.root = root;
|
|
||||||
this.loadTreeView();
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadTreeView() {
|
|
||||||
console.log("Loading tree view", this.root);
|
|
||||||
invoke<Primitive>("get_prim_tree_by_path", {
|
|
||||||
id: this.file_id,
|
|
||||||
path: this.root,
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
this.view = new Primitive(result);
|
|
||||||
})
|
|
||||||
.catch((err) => console.error(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
public expandTree(path: string[]) {
|
|
||||||
if (path.length == 0) {
|
|
||||||
console.error("Empty path");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let node = this.root;
|
|
||||||
for (let key of path.slice(1, path.length)) {
|
|
||||||
let _node: TreeViewNode | undefined = node.getChild(key);
|
|
||||||
if (_node) {
|
|
||||||
node = _node;
|
|
||||||
} else {
|
|
||||||
node = node.addChild(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.loadTreeView();
|
|
||||||
}
|
|
||||||
|
|
||||||
public collapseTree(path: string[]) {
|
|
||||||
if (path.length == 0) {
|
|
||||||
console.error("Empty path");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (path.length == 1) {
|
|
||||||
this.root.clearChildren();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let node: TreeViewNode | undefined = this.root;
|
|
||||||
for (let key of path.slice(1, path.length - 1)) {
|
|
||||||
node = node?.getChild(key);
|
|
||||||
}
|
|
||||||
if (node) {
|
|
||||||
node.removeChild(path[path.length - 1]);
|
|
||||||
}
|
|
||||||
this.loadTreeView();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,9 +11,6 @@ const config = {
|
|||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
},
|
},
|
||||||
preprocess: vitePreprocess(),
|
preprocess: vitePreprocess(),
|
||||||
// compilerOptions: {
|
|
||||||
// runes: true
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@ -1,15 +1,12 @@
|
|||||||
import {defineConfig} from "vite";
|
import {defineConfig} from "vite";
|
||||||
import {sveltekit} from "@sveltejs/kit/vite";
|
import {sveltekit} from "@sveltejs/kit/vite";
|
||||||
// import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
|
||||||
// @ts-expect-error process is a nodejs global
|
// @ts-expect-error process is a nodejs global
|
||||||
const host = process.env.TAURI_DEV_HOST;
|
const host = process.env.TAURI_DEV_HOST;
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig(async () => ({
|
export default defineConfig(async () => ({
|
||||||
plugins: [
|
plugins: [sveltekit()],
|
||||||
sveltekit(),
|
|
||||||
// monacoEditorPlugin({})
|
|
||||||
],
|
|
||||||
css: {
|
css: {
|
||||||
postcss: './postcss.config.cjs', // Path to PostCSS config
|
postcss: './postcss.config.cjs', // Path to PostCSS config
|
||||||
},
|
},
|
||||||