broken stream data
This commit is contained in:
parent
dcd1bd6d3f
commit
f6fa5d7269
@ -8,7 +8,7 @@ use crate::pdf::object::Resolve;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use pdf::file::{File, FileOptions, NoLog, ObjectCache, StreamCache};
|
use pdf::file::{File, FileOptions, NoLog, ObjectCache, StreamCache};
|
||||||
use pdf::object::{Object, ObjectWrite, PlainRef, Stream, Trace};
|
use pdf::object::{Object, ObjectWrite, PlainRef, Stream, Trace};
|
||||||
use pdf::primitive::Primitive;
|
use pdf::primitive::{Dictionary, Primitive};
|
||||||
use pdf::xref::XRef;
|
use pdf::xref::XRef;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -223,7 +223,11 @@ fn get_contents(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_stream_data(id: &str, path: &str, session: State<Mutex<Session>>) -> Result<String, String> {
|
fn get_stream_data(
|
||||||
|
id: &str,
|
||||||
|
path: &str,
|
||||||
|
session: State<Mutex<Session>>,
|
||||||
|
) -> Result<Vec<u8>, 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())?;
|
||||||
@ -231,7 +235,7 @@ fn get_stream_data(id: &str, path: &str, session: State<Mutex<Session>>) -> Resu
|
|||||||
get_stream_data_by_path_with_file(path, &file.cos_file)
|
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> {
|
fn get_stream_data_by_path_with_file(path: &str, file: &CosFile) -> Result<Vec<u8>, String> {
|
||||||
let mut steps = Step::parse(path);
|
let mut steps = Step::parse(path);
|
||||||
if steps
|
if steps
|
||||||
.pop_back()
|
.pop_back()
|
||||||
@ -247,7 +251,7 @@ fn get_stream_data_by_path_with_file(path: &str, file: &CosFile) -> Result<Strin
|
|||||||
};
|
};
|
||||||
let resolver = file.resolver();
|
let resolver = file.resolver();
|
||||||
let data = t!(t!(Stream::<Primitive>::from_stream(stream, &resolver)).data(&resolver));
|
let data = t!(t!(Stream::<Primitive>::from_stream(stream, &resolver)).data(&resolver));
|
||||||
Ok(String::from_utf8_lossy(&data).into_owned())
|
Ok(data.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -563,7 +567,7 @@ impl PrimitiveModel {
|
|||||||
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_lossy(),
|
||||||
Primitive::Stream(_) => "-".to_string(),
|
Primitive::Stream(_) => "-".to_string(),
|
||||||
Primitive::Dictionary(_) => "-".to_string(),
|
Primitive::Dictionary(dict) => PrimitiveModel::format_dict_content(dict),
|
||||||
Primitive::Array(arr) => PrimitiveModel::format_arr_content(arr),
|
Primitive::Array(arr) => PrimitiveModel::format_arr_content(arr),
|
||||||
Primitive::Reference(pref) => {
|
Primitive::Reference(pref) => {
|
||||||
format!("Obj Nr: {} Gen Nr: {}", pref.id, pref.gen)
|
format!("Obj Nr: {} Gen Nr: {}", pref.id, pref.gen)
|
||||||
@ -592,6 +596,26 @@ impl PrimitiveModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_dict_content(dict: &Dictionary) -> String {
|
||||||
|
let mut result = String::from("{");
|
||||||
|
let mut count = 0;
|
||||||
|
for (key, value) in dict.iter() {
|
||||||
|
result.push_str(&format!(
|
||||||
|
"{}: {}",
|
||||||
|
key,
|
||||||
|
PrimitiveModel::format_prim_short(value)
|
||||||
|
));
|
||||||
|
count += 1;
|
||||||
|
if count < 4 {
|
||||||
|
result.push_str(", ");
|
||||||
|
} else {
|
||||||
|
result.push_str(",...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_str("}");
|
||||||
|
result
|
||||||
|
}
|
||||||
fn format_arr_content(arr: &Vec<Primitive>) -> String {
|
fn format_arr_content(arr: &Vec<Primitive>) -> String {
|
||||||
if arr.len() == 0 {
|
if arr.len() == 0 {
|
||||||
return "[]".to_string();
|
return "[]".to_string();
|
||||||
@ -600,14 +624,7 @@ impl PrimitiveModel {
|
|||||||
let contents = if arr.len() > 4 { &arr[0..4] } else { &arr[..] };
|
let contents = if arr.len() > 4 { &arr[0..4] } else { &arr[..] };
|
||||||
for i in 0..contents.len() {
|
for i in 0..contents.len() {
|
||||||
let prim = contents.get(i).unwrap();
|
let prim = contents.get(i).unwrap();
|
||||||
result.push_str(&match prim {
|
result.push_str(&PrimitiveModel::format_prim_short(prim));
|
||||||
Primitive::Integer(i) => format!("{}", i),
|
|
||||||
Primitive::Number(n) => format!("{}", n),
|
|
||||||
Primitive::Boolean(b) => format!("{}", b),
|
|
||||||
Primitive::String(s) => s.to_string().unwrap_or(String::from("-")),
|
|
||||||
Primitive::Name(n) => n.as_str().to_string(),
|
|
||||||
_ => prim.get_debug_name().to_string(),
|
|
||||||
});
|
|
||||||
if i != contents.len() - 1 {
|
if i != contents.len() - 1 {
|
||||||
result.push_str(", ");
|
result.push_str(", ");
|
||||||
}
|
}
|
||||||
@ -620,6 +637,17 @@ impl PrimitiveModel {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_prim_short(prim: &Primitive) -> String {
|
||||||
|
match prim {
|
||||||
|
Primitive::Integer(i) => format!("{}", i),
|
||||||
|
Primitive::Number(n) => format!("{}", n),
|
||||||
|
Primitive::Boolean(b) => format!("{}", b),
|
||||||
|
Primitive::String(s) => s.to_string().unwrap_or(String::from("-")),
|
||||||
|
Primitive::Name(n) => n.as_str().to_string(),
|
||||||
|
_ => prim.get_debug_name().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_primitive_with_children(
|
fn from_primitive_with_children(
|
||||||
primitive: &Primitive,
|
primitive: &Primitive,
|
||||||
trace: Vec<PathTrace>,
|
trace: Vec<PathTrace>,
|
||||||
|
|||||||
@ -66,22 +66,28 @@ mod tests {
|
|||||||
children: vec![TreeViewRequest {
|
children: vec![TreeViewRequest {
|
||||||
key: "1".to_string(),
|
key: "1".to_string(),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
|
expand: true,
|
||||||
}],
|
}],
|
||||||
|
expand: true,
|
||||||
});
|
});
|
||||||
path.push(TreeViewRequest {
|
path.push(TreeViewRequest {
|
||||||
key: "Info".to_string(),
|
key: "Info".to_string(),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
|
expand: true,
|
||||||
});
|
});
|
||||||
path.push(TreeViewRequest {
|
path.push(TreeViewRequest {
|
||||||
key: "Root".to_string(),
|
key: "Root".to_string(),
|
||||||
children: vec![TreeViewRequest {
|
children: vec![TreeViewRequest {
|
||||||
key: "Pages".to_string(),
|
key: "Pages".to_string(),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
|
expand: true,
|
||||||
}],
|
}],
|
||||||
|
expand: true,
|
||||||
});
|
});
|
||||||
let root = TreeViewRequest {
|
let root = TreeViewRequest {
|
||||||
key: "Trailer".to_string(),
|
key: "Trailer".to_string(),
|
||||||
children: path,
|
children: path,
|
||||||
|
expand: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let message = format!("Retrieval of {:?}", root);
|
let message = format!("Retrieval of {:?}", root);
|
||||||
|
|||||||
@ -22,18 +22,19 @@
|
|||||||
|
|
||||||
let bodyHeight = $derived(height - headerOffset);
|
let bodyHeight = $derived(height - headerOffset);
|
||||||
let editorHeight = $derived(Math.max(800, bodyHeight - tableHeight));
|
let editorHeight = $derived(Math.max(800, bodyHeight - tableHeight));
|
||||||
$inspect(fState.highlightedPrim);
|
let imageUrl = "";
|
||||||
|
|
||||||
|
// Example: Simulating a binary Uint8Array (normally fetched from an API)
|
||||||
|
let binaryData = new Uint8Array([fState.stream_data]);
|
||||||
|
|
||||||
|
if (binaryData.length > 0) {
|
||||||
|
const blob = new Blob([binaryData], { type: "image/png" });
|
||||||
|
imageUrl = URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
function handlePrimSelect(prim: Primitive) {
|
function handlePrimSelect(prim: Primitive) {
|
||||||
if (prim.isContainer()) {
|
const _path: string[] = fState.copyPath();
|
||||||
if (!prim) {
|
_path.push(prim?.key);
|
||||||
fState.prim = prim;
|
fState.selectPath(_path);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const _path: string[] = fState.copyPath();
|
|
||||||
_path.push(prim?.key);
|
|
||||||
fState.selectPath(_path);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleScroll(event: Event & { currentTarget: HTMLElement }) {
|
function handleScroll(event: Event & { currentTarget: HTMLElement }) {
|
||||||
@ -45,6 +46,9 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if fState.stream_data}
|
||||||
|
<img src={fState.stream_data} />
|
||||||
|
{/if}
|
||||||
{#if prim && prim.children && prim.children.length > 0}
|
{#if prim && prim.children && prim.children.length > 0}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<div class="w-[851px]">
|
<div class="w-[851px]">
|
||||||
@ -72,10 +76,10 @@
|
|||||||
{#each entriesToDisplay as entry}
|
{#each entriesToDisplay as entry}
|
||||||
<tr
|
<tr
|
||||||
class:selected={entry.key ===
|
class:selected={entry.key ===
|
||||||
fState.highlightedPrim?.key}
|
fState.highlighted_prim}
|
||||||
class="row"
|
class="row"
|
||||||
onclick={() =>
|
onclick={() =>
|
||||||
(fState.highlightedPrim = entry)}
|
(fState.highlighted_prim = entry.key)}
|
||||||
ondblclick={() => handlePrimSelect(entry)}
|
ondblclick={() => handlePrimSelect(entry)}
|
||||||
>
|
>
|
||||||
<td class="page-cell t-data">
|
<td class="page-cell t-data">
|
||||||
@ -97,10 +101,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{#if fState.prim?.ptype === "Stream"}
|
{#if fState.stream_data}
|
||||||
<StreamEditor
|
<StreamEditor
|
||||||
fileId={fState.file.id}
|
stream_data={fState.stream_data}
|
||||||
path={fState.getMergedPath()}
|
|
||||||
height={editorHeight}
|
height={editorHeight}
|
||||||
></StreamEditor>
|
></StreamEditor>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -4,11 +4,8 @@
|
|||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
|
|
||||||
let {
|
let { stream_data, height }: { stream_data: string; height: number } =
|
||||||
fileId,
|
$props();
|
||||||
path,
|
|
||||||
height,
|
|
||||||
}: { fileId: string; path: string; height: number } = $props();
|
|
||||||
|
|
||||||
let contents: string | undefined = $state(undefined);
|
let contents: string | undefined = $state(undefined);
|
||||||
let editorContainer: HTMLElement;
|
let editorContainer: HTMLElement;
|
||||||
@ -30,20 +27,13 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
loadContents(path, fileId);
|
loadContents(stream_data);
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadContents(path: string | undefined, id: string) {
|
function loadContents(stream_data: string | undefined) {
|
||||||
if (!path || !id) return;
|
if (!stream_data || !editor) return;
|
||||||
path = path + "/Data";
|
|
||||||
invoke<string>("get_stream_data", { id, path })
|
editor.setValue(stream_data);
|
||||||
.then((result) => {
|
|
||||||
contents = result;
|
|
||||||
if (contents && editor) {
|
|
||||||
editor.setValue(contents);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => console.error(err));
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -123,20 +123,22 @@
|
|||||||
<span class="no-caret"></span>
|
<span class="no-caret"></span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<PrimitiveIcon ptype={entry.ptype} />
|
<div>
|
||||||
<p class="pl-1">
|
<PrimitiveIcon ptype={entry.ptype} />
|
||||||
|
</div>
|
||||||
|
<div class="pl-1 prim_name whitespace-nowrap">
|
||||||
{formatDisplayKey(entry.key)}
|
{formatDisplayKey(entry.key)}
|
||||||
</p>
|
<div
|
||||||
<p
|
class="details group-hover:text-forge-text_hint"
|
||||||
class="details group-hover:text-forge-text_hint whitespace-nowrap"
|
>
|
||||||
>
|
{" | " +
|
||||||
{" | " +
|
entry.value +
|
||||||
entry.value +
|
" | " +
|
||||||
" | " +
|
entry.sub_type +
|
||||||
entry.sub_type +
|
" | " +
|
||||||
" | " +
|
entry.ptype}
|
||||||
entry.ptype}
|
</div>
|
||||||
</p>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@ -145,6 +147,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
|
.prim_name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
.item {
|
.item {
|
||||||
@apply text-sm rounded-sm;
|
@apply text-sm rounded-sm;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -159,8 +169,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
@apply ml-1 text-forge-prim text-xs whitespace-nowrap font-extralight;
|
@apply ml-1 text-forge-prim whitespace-nowrap font-extralight;
|
||||||
text-align: right;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
|
|||||||
@ -12,8 +12,9 @@ export default class FileViewState {
|
|||||||
public path: string[] = $state(["Trailer"]);
|
public path: string[] = $state(["Trailer"]);
|
||||||
public file: PdfFile;
|
public file: PdfFile;
|
||||||
public prim: Primitive | undefined = $state();
|
public prim: Primitive | undefined = $state();
|
||||||
public highlightedPrim: Primitive | undefined = $state();
|
public highlighted_prim: string | undefined = $state();
|
||||||
public xref_entries: XRefEntry[] = $state([]);
|
public xref_entries: XRefEntry[] = $state([]);
|
||||||
|
public stream_data: string | undefined = $state();
|
||||||
|
|
||||||
constructor(file: PdfFile) {
|
constructor(file: PdfFile) {
|
||||||
|
|
||||||
@ -38,8 +39,19 @@ export default class FileViewState {
|
|||||||
.catch(err => console.error(err));
|
.catch(err => console.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectPath(newPath: string[]) {
|
public async selectPath(newPath: string[]) {
|
||||||
invoke<Primitive>("get_prim_by_path", { id: this.file.id, path: this.mergePaths(newPath) })
|
if (newPath.at(-1) === "Data") {
|
||||||
|
let _path = newPath.slice(0, newPath.length - 1)
|
||||||
|
const _prim = await invoke<Primitive>("get_prim_by_path", { id: this.file.id, path: this.formatPaths(_path) })
|
||||||
|
this.stream_data = await invoke<string>("get_stream_data", { id: this.file.id, path: this.formatPaths(newPath) })
|
||||||
|
this.prim = new Primitive(_prim);
|
||||||
|
this.path = _path;
|
||||||
|
this.highlighted_prim = "Data";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.stream_data = undefined;
|
||||||
|
}
|
||||||
|
invoke<Primitive>("get_prim_by_path", { id: this.file.id, path: this.formatPaths(newPath) })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
let _prim = new Primitive(result)
|
let _prim = new Primitive(result)
|
||||||
if (_prim.isContainer()) {
|
if (_prim.isContainer()) {
|
||||||
@ -47,7 +59,7 @@ export default class FileViewState {
|
|||||||
this.path = newPath
|
this.path = newPath
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
this.highlightedPrim = _prim;
|
this.highlighted_prim = _prim.key;
|
||||||
this.selectPath(newPath.slice(0, newPath.length - 1))
|
this.selectPath(newPath.slice(0, newPath.length - 1))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -63,7 +75,7 @@ export default class FileViewState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getMergedPath() {
|
public getMergedPath() {
|
||||||
return this.mergePaths(this.path);
|
return this.formatPaths(this.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public popPath() {
|
public popPath() {
|
||||||
@ -93,7 +105,7 @@ export default class FileViewState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public mergePaths(paths: string[]) {
|
public formatPaths(paths: string[]) {
|
||||||
if (paths.length == 0) {
|
if (paths.length == 0) {
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,6 @@ export default class TreeViewState {
|
|||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
if (i >= end) {
|
if (i >= end) {
|
||||||
console.log("end", i, end);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +57,6 @@ export default class TreeViewState {
|
|||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
if (i >= end) {
|
if (i >= end) {
|
||||||
console.log("end", i, end);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +139,6 @@ export default class TreeViewState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async collapseTree(path: string[]) {
|
public async collapseTree(path: string[]) {
|
||||||
console.log("collapseTree", path);
|
|
||||||
if (path.length == 0) {
|
if (path.length == 0) {
|
||||||
console.error("Empty path");
|
console.error("Empty path");
|
||||||
return;
|
return;
|
||||||
@ -153,7 +150,6 @@ export default class TreeViewState {
|
|||||||
}
|
}
|
||||||
let node: TreeViewRequest | undefined = root;
|
let node: TreeViewRequest | undefined = root;
|
||||||
for (let key of path.slice(1, path.length)) {
|
for (let key of path.slice(1, path.length)) {
|
||||||
console.log("collapseTree", key);
|
|
||||||
node = node?.getChild(key);
|
node = node?.getChild(key);
|
||||||
}
|
}
|
||||||
if (node) {
|
if (node) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user