good initial page state

This commit is contained in:
Kilian Schuettler 2025-01-31 22:14:00 +01:00
parent 2cd689b468
commit 7e71cb947b
16 changed files with 342 additions and 314 deletions

View File

@ -250,13 +250,9 @@ fn get_prim_by_path(
}
fn get_prim_model_by_path_with_file(path: &str, file: &CosFile) -> Result<PrimitiveModel, String> {
let (step, prim, detail_path) = get_prim_by_path_with_file(path, file)?;
let (step, prim, trace) = get_prim_by_path_with_file(path, file)?;
Ok(PrimitiveModel::from_primitive_with_children(
step.get_key(),
&prim,
detail_path,
))
Ok(PrimitiveModel::from_primitive_with_children(&prim, trace))
}
fn get_prim_by_path_with_file(
@ -286,7 +282,7 @@ fn get_prim_by_steps_with_file(
current_prim = resolve_step(&current_prim, &step)?;
if let Primitive::Reference(xref) = current_prim {
last_jump = xref.id.to_string();
parent = resolve_pref(xref.clone(), file)?;
parent = resolve_p_ref(xref.clone(), file)?;
current_prim = &parent;
}
trace.push(PathTrace::new(step.get_key(), last_jump.clone()));
@ -296,9 +292,9 @@ fn get_prim_by_steps_with_file(
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),
Step::Page(page_num) => retrieve_page(page_num, file)?,
_ => return Err(String::from(format!("{:?} is not a valid path!", step))),
};
Ok((parent, PathTrace::new(step.get_key(), step.get_key())))
@ -323,12 +319,11 @@ fn get_prim_tree_by_path_with_file(
file: &CosFile,
) -> Result<PrimitiveModel, String> {
let step = node.step()?;
let (mut parent, trace) = resolve_parent(step.clone(), file)?;
let (parent, trace) = resolve_parent(step.clone(), file)?;
let path = vec![trace];
let mut parent_model = PrimitiveModel::from_primitive_with_children(&parent, path);
let mut parent_model =
PrimitiveModel::from_primitive_with_children(step.get_key(), &parent, path);
for child in node.children.iter() {
expand(child, &mut parent_model, &parent, file)?;
}
@ -420,7 +415,6 @@ 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())),
})
}
@ -430,9 +424,13 @@ fn retrieve_trailer(file: &CosFile) -> Primitive {
file.trailer.to_primitive(&mut updater).unwrap()
}
fn retrieve_page(page_num: u32, file: &CosFile) -> Result<Primitive, String> {
fn retrieve_page(page_num: u32, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
let page_rc = t!(file.get_page(page_num - 1));
resolve_pref(page_rc.get_ref().get_inner(), file)
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 {
@ -498,10 +496,10 @@ impl Step {
fn resolve_xref(id: u64, file: &CosFile) -> Result<Primitive, String> {
let plain_ref = PlainRef { id, gen: 0 };
resolve_pref(plain_ref, file)
resolve_p_ref(plain_ref, file)
}
fn resolve_pref(plain_ref: PlainRef, file: &CosFile) -> Result<Primitive, String> {
fn resolve_p_ref(plain_ref: PlainRef, file: &CosFile) -> Result<Primitive, String> {
file.resolver()
.resolve(plain_ref)
.map_err(|e| e.to_string())
@ -595,12 +593,12 @@ impl PrimitiveModel {
result
}
fn from_primitive_with_children(
key: String,
primitive: &Primitive,
path: Vec<PathTrace>,
) -> PrimitiveModel {
let mut model = PrimitiveModel::from_primitive(key, primitive, path.clone());
fn from_primitive_with_children(primitive: &Primitive, path: Vec<PathTrace>) -> PrimitiveModel {
let mut model = PrimitiveModel::from_primitive(
path.last().unwrap().key.clone(),
primitive,
path.clone(),
);
model.add_children(primitive, path);
model
}

View File

@ -23,7 +23,7 @@
let treeShowing: boolean = $state(true);
let pagesShowing: boolean = $state(false);
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>();
@ -109,7 +109,7 @@
<div style="height: {titleBarHeight}px">
<TitleBar></TitleBar>
</div>
<div style="height: {fileViewHeight}px">
<div class="fileview_container" style="height: {fileViewHeight + 30}px">
<Splitpanes theme="forge-movable" dblClickSplitter={false}>
<Pane size={2.5} minSize={1.5} maxSize={4}>
<ToolbarLeft bind:tree={treeShowing} bind:pages={pagesShowing}

View File

@ -7,8 +7,8 @@
let { fState, height }: { fState: FileViewState; height: number } =
$props();
let h = $derived(height - 34);
let path = $derived(fState.prim?.getLastJump().toString());
let h = $derived(height);
let path = $derived(fState.prim?.getFirstJump()?.toString());
let id = $derived(fState.file.id);
let contents: ContentModel | undefined = $state(undefined);
let editorContainer: HTMLElement;
@ -41,6 +41,7 @@
invoke<ContentModel>("get_contents", { id, path })
.then((result) => {
console.log("Contents loaded", result);
contents = result;
if (contents && editor) {
const text = contents.parts
@ -49,13 +50,6 @@
"\n\n%-------------------% EOF %-------------------%\n\n",
);
editor.setValue(text);
const model = editor.getModel();
if (model) {
monaco.editor.setModelLanguage(
model,
"pdf-content-stream",
);
}
}
})
.catch((err) => console.error(err));

View File

@ -4,9 +4,12 @@
import PrimitiveView from "./PrimitiveView.svelte";
import TreeView from "./TreeView.svelte";
import type FileViewState from "../models/FileViewState.svelte";
import { onMount } from "svelte";
import { createEventDispatcher, onMount } from "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 {
treeShowing,
@ -49,37 +52,33 @@
window.removeEventListener("mousedown", handleMouseButton);
};
});
function pathSelectedHandler(event: PathSelectedEvent) {
console.log("Path selected", event.detail.path);
fState.selectPath(event.detail.path);
}
</script>
<div bind:clientWidth={width} class="file-view-container">
<div style="width: {splitPanesWidth}px">
<div
class="flex flex-row"
style="width: {splitPanesWidth}px; height: {height}px"
>
<Splitpanes theme="forge-movable" pushOtherPanes={false}>
<Pane
size={treeShowing || pagesShowing ? 15 : 0}
minSize={treeShowing || pagesShowing ? 1 : 0}
minSize={treeShowing || pagesShowing ? 2 : 0}
maxSize={treeShowing || pagesShowing ? 100 : 0}
>
<Splitpanes
theme="forge-movable"
horizontal
pushOtherPanes={false}
style="height: {height}px"
>
<Pane
size={treeShowing ? (pagesShowing ? 50 : 100) : 0}
minSize={treeShowing ? 2 : 0}
maxSize={treeShowing ? 100 : 0}
>
<TreeView {fState}></TreeView>
</Pane>
<Pane
size={pagesShowing ? (treeShowing ? 50 : 100) : 0}
minSize={pagesShowing ? 2 : 0}
maxSize={pagesShowing ? 100 : 0}
>
<PageList {fState}></PageList>
</Pane>
</Splitpanes>
{#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 minSize={1}>
@ -98,7 +97,7 @@
</div>
{#if xrefTableShowing}
<div class="xref-modal" class:visible={xrefTableShowing}>
<XRefTable {fState}></XRefTable>
<XRefTable {fState} {height}></XRefTable>
</div>
{/if}
</div>
@ -110,21 +109,13 @@
width: 100%;
}
.xref-modal-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 0;
overflow: visible;
}
.xref-modal {
position: absolute;
border-left: 1px solid var(--border-color);
top: 0;
right: 0;
bottom: 0;
width: 281px;
width: 280px;
background: var(--background-color);
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
z-index: 1000;

View File

@ -24,8 +24,6 @@
fState && fState.prim ? toElements(fState.prim.trace) : undefined,
);
$inspect(fState?.path);
$inspect(fState?.prim?.trace);
function toElements(path: Trace[]): Path[] {
if (path.length == 0) {
return [new Path("Trailer", 0)];
@ -52,38 +50,30 @@
}
</script>
<div
class="bg-forge-prim border border-forge-bound"
style="height: {footerHeight}px"
>
<div class="flex flex-row items-center" style="height: {footerHeight}px">
{#if elements}
{#each elements as path}
<button
class="flex flex-row items-center ml-2"
onclick={() => selectPath(path)}
>
{#if path.jump}
<div
class="flex items-center mr-1 ml-1"
style="height: {footerHeight}px"
>
<PrimitiveIcon ptype={"Reference"}></PrimitiveIcon>
</div>
{:else}
<div
class="flex items-center"
style="height: {footerHeight}px"
>
<CaretRightOutline class="text-forge-sec" />
</div>
{/if}
<p class="text-xs ml-1">{path.value}</p>
</button>
{/each}
{/if}
</div>
<div class="footer" style="height: {footerHeight}px">
{#if elements}
{#each elements as path}
<button
class="flex flex-row items-center ml-2"
onclick={() => selectPath(path)}
>
{#if path.jump}
<PrimitiveIcon ptype={"Reference"}></PrimitiveIcon>
{:else}
<CaretRightOutline class="text-forge-sec" />
{/if}
<p class="text-xs ml-1">{path.value}</p>
</button>
{/each}
{/if}
</div>
<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>

View File

@ -3,8 +3,7 @@
import PrimitiveIcon from "./PrimitiveIcon.svelte";
import type PageModel from "../models/PageModel";
let { fState }: { fState: FileViewState } = $props();
let h: number = $state(100);
let { fState, h }: { fState: FileViewState; h: number } = $props();
let selected: PageModel | undefined = $state(undefined);
function handlePageSelect(page: PageModel) {
@ -13,43 +12,37 @@
}
</script>
<div bind:clientHeight={h} class="full-container">
<div class="overflow-x-auto">
<div class="w-[251px]">
<table>
<thead>
<tr>
<td class="page-cell t-header border-forge-prim"
>Page</td
>
<td class="ref-cell t-header border-forge-sec">Ref</td>
<div class="overflow-x-auto">
<table>
<thead>
<tr>
<td class="page-cell t-header border-forge-prim">Page</td>
<td class="ref-cell t-header border-forge-sec">Ref</td>
</tr>
</thead>
</table>
<div class="overflow-y-auto" style="height: {h - 25}px">
<table>
<tbody>
{#each fState.file.pages as page}
<tr
class:selected={page === selected}
class="hover:bg-forge-sec"
ondblclick={() => handlePageSelect(page)}
>
<td class="page-cell t-data">
<div class="key-field">
<PrimitiveIcon ptype={"Reference"} />
<p class="text-left">
{page.key}
</p>
</div>
</td>
<td class="ref-cell t-data">{page.obj_num}</td>
</tr>
</thead>
</table>
<div class="overflow-y-auto" style="height: {h - 55}px">
<table>
<tbody>
{#each fState.file.pages as page}
<tr
class:selected={page === selected}
class="hover:bg-forge-sec"
ondblclick={() => handlePageSelect(page)}
>
<td class="page-cell t-data">
<div class="key-field">
<PrimitiveIcon ptype={"Reference"} />
<p class="text-left">
{page.key}
</p>
</div>
</td>
<td class="ref-cell t-data">{page.id}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
{/each}
</tbody>
</table>
</div>
</div>
@ -71,7 +64,7 @@
}
.t-data {
@apply border border-forge-sec text-sm text-forge-text text-left;
@apply border border-forge-sec text-xs text-forge-text text-left;
text-align: left;
cursor: default;
}

View File

@ -6,11 +6,10 @@
let { fState, height }: { fState: FileViewState; height: number } =
$props();
let bodyHeight = $derived(height - 54);
let selected: Primitive | undefined = $state(undefined);
let bodyHeight = $derived(height - 24);
let prim = $derived(fState.prim);
let showContents = $state(false);
$inspect(fState.highlightedPrim);
function handlePrimSelect(prim: Primitive) {
if (prim.isContainer()) {
if (!prim) {
@ -44,9 +43,10 @@
<tbody>
{#each prim.children as entry}
<tr
class:selected={entry === selected}
class:selected={entry.key ===
fState.highlightedPrim?.key}
class="hover:bg-forge-sec"
onclick={() => (selected = entry)}
onclick={() => (fState.highlightedPrim = entry)}
ondblclick={() => handlePrimSelect(entry)}
>
<td class="page-cell t-data">

View File

@ -1,33 +1,48 @@
<script lang="ts">
import {BookOpenSolid, CodeBranchSolid} from "flowbite-svelte-icons";
let {tree = $bindable(false), pages = $bindable(false)}: { tree: boolean, pages: boolean } = $props();
import { BookOpenSolid, CodeBranchSolid } from "flowbite-svelte-icons";
let {
tree = $bindable(false),
pages = $bindable(false),
}: { tree: boolean; pages: boolean } = $props();
function toggleTree() {
tree = !tree;
if (tree) {
pages = false;
}
}
function togglePages() {
pages = !pages;
if (pages) {
tree = false;
}
}
</script>
<div class="grid grid-cols-1">
<button class={ tree ? "tool-button active" : "tool-button" } onclick={toggleTree} >
<button
class={tree ? "tool-button active" : "tool-button"}
onclick={toggleTree}
>
<div class="justify-center flex m-0">
<CodeBranchSolid class="rotate-180 scale-x-[-1]"/>
<CodeBranchSolid class="rotate-180 scale-x-[-1]" />
</div>
<b class="button-title">Tree</b>
</button>
<button id="#page" class={ pages ? "tool-button active" : "tool-button" } onclick={togglePages}>
<div class="justify-center flex ">
<BookOpenSolid/>
<button
id="#page"
class={pages ? "tool-button active" : "tool-button"}
onclick={togglePages}
>
<div class="justify-center flex">
<BookOpenSolid />
</div>
<b class="button-title">Page</b>
</button>
</div>
<style lang="postcss">
.button-title {
@apply text-forge-text_sec text-xs;
@ -41,4 +56,4 @@
.tool-button.active {
@apply bg-forge-active focus:bg-forge-acc;
}
</style>
</style>

View File

@ -2,16 +2,21 @@
import type Primitive from "../models/Primitive.svelte";
import PrimitiveIcon from "./PrimitiveIcon.svelte";
import { CaretRightOutline, CaretDownOutline } from "flowbite-svelte-icons";
import type FileViewState from "../models/FileViewState.svelte";
import { arraysAreEqual } from "../utils.js";
import type TreeViewState from "../models/TreeViewState.svelte";
import { PathSelectedEvent } from "../events/PathSelectedEvent";
import { createEventDispatcher } from "svelte";
let {
prim,
path,
fState,
}: { prim: Primitive | undefined; path: string[]; fState: FileViewState } =
$props();
let active = $derived(arraysAreEqual(path, fState.path));
parent_view,
parent_path,
treeState,
pathSelectedHandler = $bindable(),
}: {
parent_view: Primitive;
parent_path: string[];
treeState: TreeViewState;
pathSelectedHandler: any;
} = $props();
function copyPathAndAppend(key: string): string[] {
const _path = copyPath();
@ -22,33 +27,31 @@
function copyPath(): string[] {
const _path: string[] = [];
for (let item of path) {
for (let item of parent_path) {
_path.push(item);
}
return _path;
}
function selectItem(child: Primitive) {
if (child.isContainer()) {
let _path = copyPathAndAppend(child.key);
fState.expandTree(_path);
fState.selectPath(_path);
} else {
let _path = copyPath();
fState.selectPath(_path);
}
let _path = copyPathAndAppend(child.key);
console.log("Selecting from tree", _path);
treeState.expandTree(_path);
const event = new PathSelectedEvent(_path);
pathSelectedHandler(event);
}
</script>
{#if prim}
{#if parent_view}
<ul class="active">
{#each prim.children as child}
{#each parent_view.children as child}
<li>
<div class="item">
{#if child.children.length > 0}
<button
onclick={() =>
fState.collapseTree(
treeState.collapseTree(
copyPathAndAppend(child.key),
)}
><span class="caret"><CaretDownOutline /></span
@ -57,7 +60,9 @@
{:else if child.isContainer()}
<button
onclick={() =>
fState.expandTree(copyPathAndAppend(child.key))}
treeState.expandTree(
copyPathAndAppend(child.key),
)}
><span class="caret"><CaretRightOutline /></span
></button
>
@ -85,9 +90,10 @@
</div>
{#if child.children.length > 0}
<svelte:self
prim={child}
path={copyPathAndAppend(child.key)}
{fState}
parent_view={child}
parent_path={copyPathAndAppend(child.key)}
{treeState}
bind:pathSelectedHandler
></svelte:self>
{/if}
</li>

View File

@ -1,30 +1,48 @@
<script lang="ts">
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 TreeViewState from "../models/TreeViewState.svelte";
import type TreeViewNode from "../models/TreeViewNode.svelte";
let { fState }: { fState: FileViewState } = $props();
let prim: Primitive | undefined = $derived(fState.treeView);
let {
file_id,
root,
pathSelectedHandler = $bindable(),
active = true,
}: {
file_id: string;
root: TreeViewNode;
pathSelectedHandler: any;
active: boolean;
} = $props();
let h = $state(100);
let treeState = $state(new TreeViewState(file_id, root));
</script>
<div bind:clientHeight={h} class="full-container">
<div class="overflow-auto" style="height: {h}px">
<ul id="myUL">
{#if prim}
{#if treeState.view}
<li>
<div class="item">
<PrimitiveIcon ptype={"Dictionary"} />
{"Trailer "}
</div>
<TreeNode {prim} path={prim.getTrace()} {fState}></TreeNode>
{#if active}
<TreeNode
bind:pathSelectedHandler
parent_view={treeState.view}
parent_path={treeState.view.getPath()}
{treeState}
></TreeNode>
{/if}
</li>
{/if}
</ul>
</div>
getTrace
</div>
getPath
<style lang="postcss">
.item {

View File

@ -3,10 +3,10 @@
import type XRefEntry from "../models/XRefEntry";
const cellH = 25;
const headerOffset = 80;
let { fState }: { fState: FileViewState } = $props();
const headerOffset = 2 * cellH;
let { fState, height }: { fState: FileViewState; height: number } =
$props();
let viewHeight: number = $state(100);
let fillerHeight: number = $state(0);
let firstEntry = $state(0);
@ -15,9 +15,8 @@
fState.xref_entries.slice(firstEntry, lastEntry),
);
let bodyViewHeight: number = $derived(
Math.max(viewHeight - headerOffset, 0),
);
let bodyViewHeight: number = $derived(Math.max(height - headerOffset, 0));
$inspect(bodyViewHeight);
let selectedPath: number | string | undefined = $derived(
fState.getLastJump(),
);
@ -38,7 +37,7 @@
if (
targetY - startY > 0 &&
targetY - startY < Math.max(viewHeight - headerOffset, 0)
targetY - startY < Math.max(height - headerOffset, 0)
) {
return;
}
@ -74,61 +73,55 @@
}
</script>
<div bind:clientHeight={viewHeight} class="full-container">
<div class="overflow-x-auto">
<div class="w-[281px]">
<table>
<thead>
<tr>
<td class="cell t-header border-forge-prim">Obj Nr</td>
<td class="cell t-header border-forge-prim">Gen Nr</td>
<td class="cell t-header border-forge-prim">Type</td>
<td class="cell t-header border-forge-sec">Offset</td>
</tr>
<tr
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">65535</td>
<td class="cell t-data">Dictionary</td>
<td class="cell t-data">End of file</td>
</tr>
</thead>
</table>
<div
class="scrollContainer"
style="height: {bodyViewHeight}px"
onscroll={handleScroll}
bind:this={scrollContainer}
>
<div
class="container"
style="height: {totalBodyHeight - headerOffset}px"
<div class="overflow-x-auto" style="height: {height}px;">
<div class="w-[281px]">
<table>
<thead>
<tr>
<td class="cell t-header border-forge-prim">Obj Nr</td>
<td class="cell t-header border-forge-prim">Gen Nr</td>
<td class="cell t-header border-forge-prim">Type</td>
<td class="cell t-header border-forge-sec">Offset</td>
</tr>
<tr
class={selectedPath === "/"
? "bg-forge-acc"
: "hover:bg-forge-sec"}
ondblclick={() => fState.selectXref(undefined)}
>
<table>
<tbody>
<tr class="filler" style="height: {fillerHeight}px"
></tr>
{#each entriesToDisplay as entry}
<tr
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.gen_num}</td>
<td class="cell t-data">{entry.obj_type}</td
>
<td class="cell t-data">{entry.offset}</td>
</tr>
{/each}
</tbody>
</table>
</div>
<td class="cell t-data">Trailer</td>
<td class="cell t-data">65535</td>
<td class="cell t-data">Dictionary</td>
<td class="cell t-data">End of file</td>
</tr>
</thead>
</table>
<div
class="scrollContainer"
style="height: {bodyViewHeight}px"
onscroll={handleScroll}
bind:this={scrollContainer}
>
<div class="container" style="height: {totalBodyHeight}px">
<table>
<tbody>
<tr class="filler" style="height: {fillerHeight}px"
></tr>
{#each entriesToDisplay as entry}
<tr
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.gen_num}</td>
<td class="cell t-data">{entry.obj_type}</td>
<td class="cell t-data">{entry.offset}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,10 @@
export class PathSelectedEvent extends CustomEvent<{ path: string[] }> {
static readonly eventName = 'pathselected';
constructor(path: string[]) {
super(PathSelectedEvent.eventName, {
detail: { path },
bubbles: true
});
}
}

View File

@ -8,10 +8,9 @@ import type XRefTable from "./XRefTable";
export default class FileViewState {
public path: string[] = $state(["Trailer"]);
public treeRoot: TreeViewNode = $state(new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]));
public file: PdfFile;
public prim: Primitive | undefined = $state();
public treeView: Primitive | undefined = $state();
public highlightedPrim: Primitive | undefined = $state();
public xref_entries: XRefEntry[] = $state([]);
@ -19,7 +18,6 @@ export default class FileViewState {
this.file = file;
this.selectPath(this.path);
this.loadTreeView();
this.loadXrefEntries()
}
@ -42,83 +40,30 @@ export default class FileViewState {
public selectPath(newPath: string[]) {
invoke<Primitive>("get_prim_by_path", { id: this.file.id, path: this.mergePaths(newPath) })
.then(result => {
this.prim = new Primitive(result)
this.path = newPath
let _prim = new Primitive(result)
if (_prim.isContainer()) {
this.prim = _prim;
this.path = newPath
return;
} else {
this.highlightedPrim = _prim;
this.selectPath(newPath.slice(0, newPath.length - 1))
}
})
.catch(err => console.error(err));
}
public loadTreeView() {
invoke<Primitive>("get_prim_tree_by_path", { id: this.file.id, path: this.treeRoot })
.then(result => {
this.treeView = new Primitive(result);
}
).catch(err => console.error(err))
}
public getTreeRoot() {
return this.treeRoot;
return this.treeState;
}
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() {
return this.mergePaths(this.path);
}
public displayPath() {
}
public popPath() {
let path = this.copyPath();
if (path.length == 1) {

View File

@ -27,7 +27,7 @@ export default class Primitive {
return this.ptype === "Dictionary" || this.ptype === "Array" || this.ptype === "Reference" || this.ptype === "Stream";
}
public getTrace(): string[] {
public getPath(): string[] {
return this.trace.map(path => path.key);
}
@ -38,7 +38,14 @@ export default class Primitive {
}
public isPage(): boolean {
return this.trace[0].last_jump.startsWith("Page");
return this.trace[0].key.startsWith("Page") && !isNaN(+this.trace[0].key.replace("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 {

View File

@ -10,6 +10,9 @@ export default class TreeViewNode {
this.children = children;
}
static TRAILER = new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]);
public getChild(key: string) {
return this.children.find(child => child.key === key);
}

View File

@ -0,0 +1,65 @@
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();
}
}