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> { 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( Ok(PrimitiveModel::from_primitive_with_children(&prim, trace))
step.get_key(),
&prim,
detail_path,
))
} }
fn get_prim_by_path_with_file( 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)?; current_prim = resolve_step(&current_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_pref(xref.clone(), file)?; parent = resolve_p_ref(xref.clone(), file)?;
current_prim = &parent; current_prim = &parent;
} }
trace.push(PathTrace::new(step.get_key(), last_jump.clone())); 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> { fn resolve_parent(step: Step, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
let parent = match step { let parent = match step {
Step::Page(page_num) => return retrieve_page(page_num, file),
Step::Number(obj_num) => resolve_xref(obj_num, file)?, Step::Number(obj_num) => resolve_xref(obj_num, file)?,
Step::Trailer => retrieve_trailer(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))), _ => return Err(String::from(format!("{:?} is not a valid path!", step))),
}; };
Ok((parent, PathTrace::new(step.get_key(), step.get_key()))) Ok((parent, PathTrace::new(step.get_key(), step.get_key())))
@ -323,12 +319,11 @@ fn get_prim_tree_by_path_with_file(
file: &CosFile, file: &CosFile,
) -> Result<PrimitiveModel, String> { ) -> Result<PrimitiveModel, String> {
let step = node.step()?; 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 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() { for child in node.children.iter() {
expand(child, &mut parent_model, &parent, file)?; 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())), _ => 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() 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)); 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)] #[derive(Debug, PartialEq, Clone)]
pub enum Step { pub enum Step {
@ -498,10 +496,10 @@ 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_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() file.resolver()
.resolve(plain_ref) .resolve(plain_ref)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
@ -595,12 +593,12 @@ impl PrimitiveModel {
result result
} }
fn from_primitive_with_children( fn from_primitive_with_children(primitive: &Primitive, path: Vec<PathTrace>) -> PrimitiveModel {
key: String, let mut model = PrimitiveModel::from_primitive(
primitive: &Primitive, path.last().unwrap().key.clone(),
path: Vec<PathTrace>, primitive,
) -> PrimitiveModel { path.clone(),
let mut model = PrimitiveModel::from_primitive(key, primitive, path.clone()); );
model.add_children(primitive, path); model.add_children(primitive, path);
model model
} }

View File

@ -23,7 +23,7 @@
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>();
@ -109,7 +109,7 @@
<div style="height: {titleBarHeight}px"> <div style="height: {titleBarHeight}px">
<TitleBar></TitleBar> <TitleBar></TitleBar>
</div> </div>
<div style="height: {fileViewHeight}px"> <div class="fileview_container" style="height: {fileViewHeight + 30}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}

View File

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

View File

@ -4,9 +4,12 @@
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 { onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import PageList from "./PageList.svelte"; import PageList from "./PageList.svelte";
import ContentsView from "./ContentsView.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, treeShowing,
@ -49,37 +52,33 @@
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"> <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}> <Splitpanes theme="forge-movable" pushOtherPanes={false}>
<Pane <Pane
size={treeShowing || pagesShowing ? 15 : 0} size={treeShowing || pagesShowing ? 15 : 0}
minSize={treeShowing || pagesShowing ? 1 : 0} minSize={treeShowing || pagesShowing ? 2 : 0}
maxSize={treeShowing || pagesShowing ? 100 : 0} maxSize={treeShowing || pagesShowing ? 100 : 0}
> >
<Splitpanes {#if pagesShowing}
theme="forge-movable" <PageList {fState} h={height}></PageList>
horizontal {:else if treeShowing}
pushOtherPanes={false} <TreeView
style="height: {height}px" bind:pathSelectedHandler
> file_id={fState.file.id}
<Pane root={TreeViewNode.TRAILER}
size={treeShowing ? (pagesShowing ? 50 : 100) : 0} ></TreeView>
minSize={treeShowing ? 2 : 0} {/if}
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>
</Pane> </Pane>
<Pane minSize={1}> <Pane minSize={1}>
@ -98,7 +97,7 @@
</div> </div>
{#if xrefTableShowing} {#if xrefTableShowing}
<div class="xref-modal" class:visible={xrefTableShowing}> <div class="xref-modal" class:visible={xrefTableShowing}>
<XRefTable {fState}></XRefTable> <XRefTable {fState} {height}></XRefTable>
</div> </div>
{/if} {/if}
</div> </div>
@ -110,21 +109,13 @@
width: 100%; width: 100%;
} }
.xref-modal-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 0;
overflow: visible;
}
.xref-modal { .xref-modal {
position: absolute; position: absolute;
border-left: 1px solid var(--border-color);
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
width: 281px; width: 280px;
background: var(--background-color); background: var(--background-color);
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
z-index: 1000; z-index: 1000;

View File

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

View File

@ -3,8 +3,7 @@
import PrimitiveIcon from "./PrimitiveIcon.svelte"; import PrimitiveIcon from "./PrimitiveIcon.svelte";
import type PageModel from "../models/PageModel"; import type PageModel from "../models/PageModel";
let { fState }: { fState: FileViewState } = $props(); let { fState, h }: { fState: FileViewState; h: number } = $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) {
@ -13,20 +12,16 @@
} }
</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>
<td class="page-cell t-header border-forge-prim" <td class="page-cell t-header border-forge-prim">Page</td>
>Page</td
>
<td class="ref-cell t-header border-forge-sec">Ref</td> <td class="ref-cell t-header border-forge-sec">Ref</td>
</tr> </tr>
</thead> </thead>
</table> </table>
<div class="overflow-y-auto" style="height: {h - 55}px"> <div class="overflow-y-auto" style="height: {h - 25}px">
<table> <table>
<tbody> <tbody>
{#each fState.file.pages as page} {#each fState.file.pages as page}
@ -43,14 +38,12 @@
</p> </p>
</div> </div>
</td> </td>
<td class="ref-cell t-data">{page.id}</td> <td class="ref-cell t-data">{page.obj_num}</td>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div>
</div> </div>
<style lang="postcss"> <style lang="postcss">
@ -71,7 +64,7 @@
} }
.t-data { .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; text-align: left;
cursor: default; cursor: default;
} }

View File

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

View File

@ -1,33 +1,48 @@
<script lang="ts"> <script lang="ts">
import {BookOpenSolid, CodeBranchSolid} from "flowbite-svelte-icons"; import { BookOpenSolid, CodeBranchSolid } from "flowbite-svelte-icons";
let {tree = $bindable(false), pages = $bindable(false)}: { tree: boolean, pages: boolean } = $props();
let {
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 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"> <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 id="#page" class={ pages ? "tool-button active" : "tool-button" } onclick={togglePages}> <button
<div class="justify-center flex "> id="#page"
<BookOpenSolid/> class={pages ? "tool-button active" : "tool-button"}
onclick={togglePages}
>
<div class="justify-center flex">
<BookOpenSolid />
</div> </div>
<b class="button-title">Page</b> <b class="button-title">Page</b>
</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;

View File

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

View File

@ -1,30 +1,48 @@
<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 { fState }: { fState: FileViewState } = $props(); let {
let prim: Primitive | undefined = $derived(fState.treeView); file_id,
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 prim} {#if treeState.view}
<li> <li>
<div class="item"> <div class="item">
<PrimitiveIcon ptype={"Dictionary"} /> <PrimitiveIcon ptype={"Dictionary"} />
{"Trailer "} {"Trailer "}
</div> </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> </li>
{/if} {/if}
</ul> </ul>
</div> </div>
getTrace getTrace
</div> </div>
getPath
<style lang="postcss"> <style lang="postcss">
.item { .item {

View File

@ -3,10 +3,10 @@
import type XRefEntry from "../models/XRefEntry"; import type XRefEntry from "../models/XRefEntry";
const cellH = 25; const cellH = 25;
const headerOffset = 80; const headerOffset = 2 * cellH;
let { fState }: { fState: FileViewState } = $props(); let { fState, height }: { fState: FileViewState; height: number } =
$props();
let viewHeight: number = $state(100);
let fillerHeight: number = $state(0); let fillerHeight: number = $state(0);
let firstEntry = $state(0); let firstEntry = $state(0);
@ -15,9 +15,8 @@
fState.xref_entries.slice(firstEntry, lastEntry), fState.xref_entries.slice(firstEntry, lastEntry),
); );
let bodyViewHeight: number = $derived( let bodyViewHeight: number = $derived(Math.max(height - headerOffset, 0));
Math.max(viewHeight - headerOffset, 0), $inspect(bodyViewHeight);
);
let selectedPath: number | string | undefined = $derived( let selectedPath: number | string | undefined = $derived(
fState.getLastJump(), fState.getLastJump(),
); );
@ -38,7 +37,7 @@
if ( if (
targetY - startY > 0 && targetY - startY > 0 &&
targetY - startY < Math.max(viewHeight - headerOffset, 0) targetY - startY < Math.max(height - headerOffset, 0)
) { ) {
return; return;
} }
@ -74,8 +73,7 @@
} }
</script> </script>
<div bind:clientHeight={viewHeight} class="full-container"> <div class="overflow-x-auto" style="height: {height}px;">
<div class="overflow-x-auto">
<div class="w-[281px]"> <div class="w-[281px]">
<table> <table>
<thead> <thead>
@ -104,10 +102,7 @@
onscroll={handleScroll} onscroll={handleScroll}
bind:this={scrollContainer} bind:this={scrollContainer}
> >
<div <div class="container" style="height: {totalBodyHeight}px">
class="container"
style="height: {totalBodyHeight - headerOffset}px"
>
<table> <table>
<tbody> <tbody>
<tr class="filler" style="height: {fillerHeight}px" <tr class="filler" style="height: {fillerHeight}px"
@ -121,8 +116,7 @@
> >
<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>
>
<td class="cell t-data">{entry.offset}</td> <td class="cell t-data">{entry.offset}</td>
</tr> </tr>
{/each} {/each}
@ -131,7 +125,6 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
<style lang="postcss"> <style lang="postcss">

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 { export default class FileViewState {
public path: string[] = $state(["Trailer"]); public path: string[] = $state(["Trailer"]);
public treeRoot: TreeViewNode = $state(new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]));
public file: PdfFile; public file: PdfFile;
public prim: Primitive | undefined = $state(); public prim: Primitive | undefined = $state();
public treeView: Primitive | undefined = $state(); public highlightedPrim: Primitive | undefined = $state();
public xref_entries: XRefEntry[] = $state([]); public xref_entries: XRefEntry[] = $state([]);
@ -19,7 +18,6 @@ export default class FileViewState {
this.file = file; this.file = file;
this.selectPath(this.path); this.selectPath(this.path);
this.loadTreeView();
this.loadXrefEntries() this.loadXrefEntries()
} }
@ -42,83 +40,30 @@ 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 => {
this.prim = new Primitive(result) let _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() {
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() { 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() { 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) {

View File

@ -27,7 +27,7 @@ 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 getTrace(): string[] { public getPath(): string[] {
return this.trace.map(path => path.key); return this.trace.map(path => path.key);
} }
@ -38,7 +38,14 @@ export default class Primitive {
} }
public isPage(): boolean { 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 { public getFirstJump(): string | number | undefined {

View File

@ -10,6 +10,9 @@ 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);
} }

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();
}
}