broken stream data

This commit is contained in:
Kilian Schuettler 2025-02-02 02:49:56 +01:00
parent dcd1bd6d3f
commit f6fa5d7269
7 changed files with 116 additions and 71 deletions

View File

@ -8,7 +8,7 @@ use crate::pdf::object::Resolve;
use lazy_static::lazy_static;
use pdf::file::{File, FileOptions, NoLog, ObjectCache, StreamCache};
use pdf::object::{Object, ObjectWrite, PlainRef, Stream, Trace};
use pdf::primitive::Primitive;
use pdf::primitive::{Dictionary, Primitive};
use pdf::xref::XRef;
use regex::Regex;
use serde::{Deserialize, Serialize};
@ -223,7 +223,11 @@ fn get_contents(
}
#[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
.lock()
.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)
}
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);
if steps
.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 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]
@ -563,7 +567,7 @@ impl PrimitiveModel {
Primitive::Boolean(b) => b.to_string(),
Primitive::String(s) => s.to_string_lossy(),
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::Reference(pref) => {
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 {
if arr.len() == 0 {
return "[]".to_string();
@ -600,14 +624,7 @@ impl PrimitiveModel {
let contents = if arr.len() > 4 { &arr[0..4] } else { &arr[..] };
for i in 0..contents.len() {
let prim = contents.get(i).unwrap();
result.push_str(&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(),
});
result.push_str(&PrimitiveModel::format_prim_short(prim));
if i != contents.len() - 1 {
result.push_str(", ");
}
@ -620,6 +637,17 @@ impl PrimitiveModel {
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(
primitive: &Primitive,
trace: Vec<PathTrace>,

View File

@ -66,22 +66,28 @@ mod tests {
children: vec![TreeViewRequest {
key: "1".to_string(),
children: vec![],
expand: true,
}],
expand: true,
});
path.push(TreeViewRequest {
key: "Info".to_string(),
children: vec![],
expand: true,
});
path.push(TreeViewRequest {
key: "Root".to_string(),
children: vec![TreeViewRequest {
key: "Pages".to_string(),
children: vec![],
expand: true,
}],
expand: true,
});
let root = TreeViewRequest {
key: "Trailer".to_string(),
children: path,
expand: true,
};
let message = format!("Retrieval of {:?}", root);

View File

@ -22,18 +22,19 @@
let bodyHeight = $derived(height - headerOffset);
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) {
if (prim.isContainer()) {
if (!prim) {
fState.prim = prim;
return;
}
const _path: string[] = fState.copyPath();
_path.push(prim?.key);
fState.selectPath(_path);
}
return;
const _path: string[] = fState.copyPath();
_path.push(prim?.key);
fState.selectPath(_path);
}
function handleScroll(event: Event & { currentTarget: HTMLElement }) {
@ -45,6 +46,9 @@
}
</script>
{#if fState.stream_data}
<img src={fState.stream_data} />
{/if}
{#if prim && prim.children && prim.children.length > 0}
<div class="overflow-x-auto">
<div class="w-[851px]">
@ -72,10 +76,10 @@
{#each entriesToDisplay as entry}
<tr
class:selected={entry.key ===
fState.highlightedPrim?.key}
fState.highlighted_prim}
class="row"
onclick={() =>
(fState.highlightedPrim = entry)}
(fState.highlighted_prim = entry.key)}
ondblclick={() => handlePrimSelect(entry)}
>
<td class="page-cell t-data">
@ -97,10 +101,9 @@
</tbody>
</table>
</div>
{#if fState.prim?.ptype === "Stream"}
{#if fState.stream_data}
<StreamEditor
fileId={fState.file.id}
path={fState.getMergedPath()}
stream_data={fState.stream_data}
height={editorHeight}
></StreamEditor>
{/if}

View File

@ -4,11 +4,8 @@
import { onMount } from "svelte";
import * as monaco from "monaco-editor";
let {
fileId,
path,
height,
}: { fileId: string; path: string; height: number } = $props();
let { stream_data, height }: { stream_data: string; height: number } =
$props();
let contents: string | undefined = $state(undefined);
let editorContainer: HTMLElement;
@ -30,20 +27,13 @@
});
$effect(() => {
loadContents(path, fileId);
loadContents(stream_data);
});
function loadContents(path: string | undefined, id: string) {
if (!path || !id) return;
path = path + "/Data";
invoke<string>("get_stream_data", { id, path })
.then((result) => {
contents = result;
if (contents && editor) {
editor.setValue(contents);
}
})
.catch((err) => console.error(err));
function loadContents(stream_data: string | undefined) {
if (!stream_data || !editor) return;
editor.setValue(stream_data);
}
</script>

View File

@ -123,20 +123,22 @@
<span class="no-caret"></span>
{/if}
</div>
<PrimitiveIcon ptype={entry.ptype} />
<p class="pl-1">
<div>
<PrimitiveIcon ptype={entry.ptype} />
</div>
<div class="pl-1 prim_name whitespace-nowrap">
{formatDisplayKey(entry.key)}
</p>
<p
class="details group-hover:text-forge-text_hint whitespace-nowrap"
>
{" | " +
entry.value +
" | " +
entry.sub_type +
" | " +
entry.ptype}
</p>
<div
class="details group-hover:text-forge-text_hint"
>
{" | " +
entry.value +
" | " +
entry.sub_type +
" | " +
entry.ptype}
</div>
</div>
</button>
{/if}
{/each}
@ -145,6 +147,14 @@
</div>
<style lang="postcss">
.prim_name {
display: flex;
flex-direction: row;
position: relative;
bottom: 0;
width: 100%;
text-align: center;
}
.item {
@apply text-sm rounded-sm;
text-align: center;
@ -159,8 +169,8 @@
}
.details {
@apply ml-1 text-forge-prim text-xs whitespace-nowrap font-extralight;
text-align: right;
@apply ml-1 text-forge-prim whitespace-nowrap font-extralight;
padding-top: 2px;
}
.item {

View File

@ -12,8 +12,9 @@ export default class FileViewState {
public path: string[] = $state(["Trailer"]);
public file: PdfFile;
public prim: Primitive | undefined = $state();
public highlightedPrim: Primitive | undefined = $state();
public highlighted_prim: string | undefined = $state();
public xref_entries: XRefEntry[] = $state([]);
public stream_data: string | undefined = $state();
constructor(file: PdfFile) {
@ -38,8 +39,19 @@ export default class FileViewState {
.catch(err => console.error(err));
}
public selectPath(newPath: string[]) {
invoke<Primitive>("get_prim_by_path", { id: this.file.id, path: this.mergePaths(newPath) })
public async selectPath(newPath: string[]) {
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 => {
let _prim = new Primitive(result)
if (_prim.isContainer()) {
@ -47,7 +59,7 @@ export default class FileViewState {
this.path = newPath
return;
} else {
this.highlightedPrim = _prim;
this.highlighted_prim = _prim.key;
this.selectPath(newPath.slice(0, newPath.length - 1))
}
})
@ -63,7 +75,7 @@ export default class FileViewState {
}
public getMergedPath() {
return this.mergePaths(this.path);
return this.formatPaths(this.path);
}
public popPath() {
@ -93,7 +105,7 @@ export default class FileViewState {
}
public mergePaths(paths: string[]) {
public formatPaths(paths: string[]) {
if (paths.length == 0) {
return "/";
}

View File

@ -37,7 +37,6 @@ export default class TreeViewState {
}
i += 1;
if (i >= end) {
console.log("end", i, end);
return result;
}
}
@ -58,7 +57,6 @@ export default class TreeViewState {
}
i += 1;
if (i >= end) {
console.log("end", i, end);
return result;
}
}
@ -141,7 +139,6 @@ export default class TreeViewState {
}
public async collapseTree(path: string[]) {
console.log("collapseTree", path);
if (path.length == 0) {
console.error("Empty path");
return;
@ -153,7 +150,6 @@ export default class TreeViewState {
}
let node: TreeViewRequest | undefined = root;
for (let key of path.slice(1, path.length)) {
console.log("collapseTree", key);
node = node?.getChild(key);
}
if (node) {