Syntax highlighting and zoomable view updates

This commit is contained in:
Kilian Schüttler 2025-03-25 17:51:35 +01:00
parent 4637c66185
commit fcfa06f691
13 changed files with 410 additions and 262 deletions

View File

@ -3,9 +3,10 @@
import { UploadOutline } from 'flowbite-svelte-icons';
import * as monaco from 'monaco-editor';
import type ContentModel from '../models/ContentModel.svelte';
import { opLookup } from '../models/OperatorList';
import { getPDFOperator, opLookup } from '../models/OperatorList';
import { OperatorList } from '../models/OperatorList.js';
import type { PDFOperator } from '../models/OperatorList';
const opIdRegex = /opId-(\d+)/;
monaco.languages.register({ id: 'pdf-content-stream' });
@ -85,7 +86,7 @@
minimap: { enabled: false },
scrollBeyondLastLine: false,
fontSize: 14,
automaticLayout: true
automaticLayout: true,
});
// Add change listener to detect edits
@ -95,6 +96,25 @@
loadContents(contents);
editor.onMouseDown((e) => {
if (!e.target.position) return;
const decorations = editor.getModel()?.getDecorationsInRange({
startLineNumber: e.target.position.lineNumber,
startColumn: e.target.position.column,
endLineNumber: e.target.position.lineNumber,
endColumn: e.target.position.column
});
decorations?.forEach(d => {
const match = d.options.className?.match(opIdRegex);
if (!match) {
return;
}
console.log(contents.opList.fnArray[+match[1]]);
console.log(contents.opList.argsArray[+match[1]]);
})
});
return () => {
editor.dispose();
};
@ -104,6 +124,44 @@
loadContents(contents);
});
function addDecorations(contents: ContentModel) {
if (contents.opList && contents.opList.fnArray.length > 0) {
const decorations = [];
for (let i = 0; i < contents.opList.fnArray.length; i++) {
const fnId = contents.opList.fnArray[i];
const args = contents.opList.argsArray[i];
const range = contents.opList.rangeArray[i];
const operator = getPDFOperator(fnId);
const model = editor.getModel();
if (model && operator && range) {
const startPos = model.getPositionAt(range[0]);
const endPos = model.getPositionAt(range[1]);
const monacoRange = new monaco.Range(
startPos.lineNumber,
startPos.column,
endPos.lineNumber,
endPos.column
);
let className = `${operator.class} highlightOp opId-${i}`;
// Add class-based decoration for operator highlighting
decorations.push({
range: monacoRange,
options: {
className: className,
hoverMessage: { value: OperatorList.formatOperatorToMarkdown(fnId, args) }
}
});
}
}
// Apply all decorations at once
editor.createDecorationsCollection(decorations);
}
}
function loadContents(contents: ContentModel | undefined) {
if (!contents || !editor) return;
@ -114,36 +172,8 @@
lastContents = contents;
editor.setValue(contents.toDisplay());
isEdited = false;
// Add decorations for operator ranges if available
if (contents.opList && contents.opList.fnArray.length > 0) {
const decorations = [];
for (let i = 0; i < contents.opList.fnArray.length; i++) {
const fnId = contents.opList.fnArray[i];
const range = contents.opList.rangeArray[i];
const operator = opLookup[fnId];
if (operator && range) {
const startPos = editor.getModel()!.getPositionAt(range[0]);
const endPos = editor.getModel()!.getPositionAt(range[1]);
const monacoRange = new monaco.Range(
startPos.lineNumber,
startPos.column,
endPos.lineNumber,
endPos.column);
if (fnId == 91) {
console.log(range, monacoRange)
}
decorations.push({
range: monacoRange,
options: {
className: operator.class,
hoverMessage: {value: OperatorList.formatOperatorToMarkdown(fnId)},
}
});
}
}
editor.createDecorationsCollection(decorations);
}
addDecorations(contents);
}
</script>
@ -170,4 +200,8 @@
background-color: #3c3d41;
}
:global(.highlightOp) {
@apply border border-forge-sec rounded;
}
</style>

View File

@ -12,7 +12,8 @@
<RefreshOutline class="animate-spin" size="xl" />
</div>
{:else}
<ZoomableContainer imgUrl={img} {height} />
<ZoomableContainer imgUrl={img} {height}>
</ZoomableContainer>
{/if}
</div>

View File

@ -1,48 +1,66 @@
<script lang="ts">
import {Pane, Splitpanes} from "svelte-splitpanes";
import type FileViewState from "../models/FileViewState.svelte";
import StreamEditor from "./StreamEditor.svelte";
import ContentModel from "../models/ContentModel.svelte";
import type DocumentWorker from '../models/Document.svelte';
import ContentEditor from './ContentEditor.svelte';
import { Pane, Splitpanes } from 'svelte-splitpanes';
import type FileViewState from '../models/FileViewState.svelte';
import ContentModel from '../models/ContentModel.svelte';
import type DocumentWorker from '../models/Document.svelte';
import ContentEditor from './ContentEditor.svelte';
import ZoomableContainer from './ZoomableContainer.svelte';
let display: HTMLCanvasElement;
let display: HTMLCanvasElement;
let {fState, height}: {fState: FileViewState, height: number} = $props();
let renderer: DocumentWorker = $derived(fState.document);
let pageNum = $derived(fState.currentPageNumber);
let contents: ContentModel | undefined = $state(undefined)
let { fState, height }: { fState: FileViewState, height: number } = $props();
let renderer: DocumentWorker = $derived(fState.document);
let pageNum = $derived(fState.currentPageNumber);
let contents: ContentModel | undefined = $state(undefined);
$effect(() => {
if (pageNum) {
renderer.renderPage(pageNum, display);
renderer.getContents(pageNum).then(parts => {
contents = parts;
}
);
} else {
contents = undefined;
}
})
$effect(() => {
refresh();
});
function refresh() {
if (pageNum) {
renderer.getContents(pageNum).then(parts => {
contents = parts;
renderer.renderPage(pageNum, display);
}
);
} else {
contents = undefined;
}
}
async function update(newData: string) {
if (pageNum) {
await fState.document.updateContents(pageNum, newData);
refresh();
}
}
</script>
{#if pageNum}
<Splitpanes theme="forge-movable">
<Pane minSize={1}>
<div class="overflow-hidden">
{#if contents}
<ContentEditor save={(newData) => console.log("Save")} contents={contents} height={height - 1}></ContentEditor>
{/if}
</div>
</Pane>
<Pane minSize={1}>
<canvas bind:this={display}></canvas>
</Pane>
</Splitpanes>
<Splitpanes theme="forge-movable">
<Pane minSize={1}>
<div class="overflow-hidden">
{#if contents}
<ContentEditor save={(newData) => update(newData)} contents={contents} height={height - 1}></ContentEditor>
{/if}
</div>
</Pane>
<Pane minSize={1}>
<ZoomableContainer bind:canvas={display} {height}></ZoomableContainer>
</Pane>
</Splitpanes>
{:else }
<h1>Please select a Page!</h1>
<h1>Please select a Page!</h1>
{/if}
<style lang="postcss">
.scroller {
@apply w-full h-full bg-forge-dark;
overflow: auto;
display: flex;
}
.container {
display: contents;
}
</style>

View File

@ -99,11 +99,6 @@
<div class="relative">
<div bind:this={editorContainer} style:height={height + "px"}></div>
{#if isEdited}
<button onclick={() => save(editor.getValue())} class="save-button">
<UploadOutline size="xl" />
</button>
{/if}
</div>
<style lang="postcss">

View File

@ -1,162 +1,177 @@
<script lang="ts">
import ZoomControls from "../components/ZoomControls.svelte";
import ZoomControls from '../components/ZoomControls.svelte';
import { onMount } from 'svelte';
type ZoomableProps = {
minZoom?: number;
maxZoom?: number;
zoomStep?: number;
imgUrl: string;
height?: string | number;
};
type ZoomableProps = {
minZoom?: number;
maxZoom?: number;
zoomStep?: number;
imgUrl?: string;
canvas?: HTMLCanvasElement;
height?: string | number;
};
let {
minZoom = 0.5,
maxZoom = 10,
zoomStep = 0.1,
imgUrl,
height,
}: ZoomableProps = $props();
let {
minZoom = 0.5,
maxZoom = 10,
zoomStep = 0.1,
imgUrl,
canvas = $bindable(),
height
}: ZoomableProps = $props();
let scale = $state(1);
let currentImgUrl = $state(imgUrl);
// DOM refs
let container = $state<HTMLElement>();
let image = $state<HTMLElement>();
type ViewState = {
x: number;
y: number;
scale: number;
}
// Drag state
type DragState = {
startX: number;
startY: number;
scrollLeft: number;
scrollTop: number;
};
let viewState: ViewState = $state({
x: 0,
y: 0,
scale: 1
});
let dragState: DragState | undefined = $state({
startX: 0,
startY: 0,
scrollLeft: 0,
scrollTop: 0,
});
// DOM refs
let container = $state<HTMLElement>();
// Zoom handlers
function handleZoom(event: WheelEvent) {
if (!container || !image || !event.ctrlKey) return;
// Drag state
type DragState = {
startX: number;
startY: number;
};
event.preventDefault();
const delta = -Math.sign(event.deltaY);
const newScale = Math.min(
Math.max(scale + delta * zoomStep, minZoom),
maxZoom,
);
let dragState: DragState | undefined = $state(undefined);
if (newScale !== scale) {
const rect = image.getBoundingClientRect();
const x = event.clientX;
const y = event.clientY;
// Zoom handlers
function handleZoom(event: WheelEvent) {
if (!container || !event.ctrlKey) return;
const scaleChange = newScale / scale;
container.scrollLeft = x * scaleChange - x + container.scrollLeft;
container.scrollTop = y * scaleChange - y + container.scrollTop;
event.preventDefault();
const delta = -Math.sign(event.deltaY);
const newScale = Math.min(
Math.max(viewState.scale + delta * zoomStep * viewState.scale, minZoom),
maxZoom
);
scale = newScale;
}
}
if (newScale !== viewState.scale) {
const x = event.clientX;
const y = event.clientY;
// Drag handlers
function handleDragStart(event: MouseEvent) {
if (!container) return;
const scaleChange = newScale / viewState.scale;
container.scrollLeft = x * scaleChange - x + container.scrollLeft;
container.scrollTop = y * scaleChange - y + container.scrollTop;
viewState.scale = newScale;
}
}
event.preventDefault();
dragState = {
startX: event.pageX - container.offsetLeft,
startY: event.pageY - container.offsetTop,
scrollLeft: container.scrollLeft,
scrollTop: container.scrollTop,
};
}
// Drag handlers
function handleDragStart(event: MouseEvent) {
if (!container || event.button) return;
function handleDragMove(event: MouseEvent) {
if (!container || !dragState) return;
event.preventDefault();
dragState = {
startX: event.pageX - viewState.x,
startY: event.pageY - viewState.y
};
}
event.preventDefault();
const x = event.pageX - container.offsetLeft;
const y = event.pageY - container.offsetTop;
function handleDragMove(event: MouseEvent) {
if (!container || !dragState) return;
container.scrollLeft = dragState.scrollLeft - (x - dragState.startX);
container.scrollTop = dragState.scrollTop - (y - dragState.startY);
}
event.preventDefault();
const x = event.pageX - container.offsetLeft;
const y = event.pageY - container.offsetTop;
function handleDragEnd(event: MouseEvent) {
event.preventDefault();
dragState = undefined;
}
viewState.x = (x - dragState.startX);
viewState.y = (y - dragState.startY);
}
function resetZoom() {
scale = 1;
if (container) {
container.scrollLeft = 0;
container.scrollTop = 0;
}
}
function handleDragEnd(event: MouseEvent) {
event.preventDefault();
dragState = undefined;
}
// Event listener management
$effect(() => {
if (container) {
container.addEventListener("wheel", handleZoom, { passive: false });
return () => container.removeEventListener("wheel", handleZoom);
}
});
function resetZoom() {
viewState.scale = 1;
if (container) {
viewState.x = 0;
viewState.y = 0;
}
}
// Event listener management
$effect(() => {
if (container) {
container.addEventListener('wheel', handleZoom, { passive: false });
return () => container?.removeEventListener('wheel', handleZoom);
}
});
$effect(() => {
if (canvas) {
currentImgUrl = canvas.toDataURL();
} else if (imgUrl) {
currentImgUrl = imgUrl;
}
});
</script>
<div class="relative w-full h-full">
<ZoomControls
{scale}
onZoomIn={() => (scale = Math.min(scale + zoomStep, maxZoom))}
onZoomOut={() => (scale = Math.max(scale - zoomStep, minZoom))}
{resetZoom}
/>
<div
class="container"
bind:this={container}
onmousedown={handleDragStart}
onmousemove={handleDragMove}
onmouseup={handleDragEnd}
onmouseleave={handleDragEnd}
role="presentation"
>
<div
class="image-container"
bind:this={image}
style:transform="scale({scale})"
style:height={height + "px"}
style:max-height={height + "px"}
>
<img
alt="rendered-page"
src={imgUrl}
/>
</div>
</div>
<ZoomControls
scale={viewState.scale}
onZoomIn={() => (viewState.scale = Math.min(viewState.scale + zoomStep, maxZoom))}
onZoomOut={() => (viewState.scale = Math.max(viewState.scale - zoomStep, minZoom))}
{resetZoom}
/>
<div
class="container"
bind:this={container}
onmousedown={handleDragStart}
onmousemove={handleDragMove}
onmouseup={handleDragEnd}
onmouseleave={handleDragEnd}
role="presentation"
style:height={height + "px"}
style:max-height={height + "px"}
>
{#if imgUrl}
<img
class="zoomimage"
alt="rendered-page"
src={currentImgUrl}
style:transform="translate({viewState.x}px, {viewState.y}px) scale({viewState.scale})"
style:max-height={height + "px"}
/>
{:else}
<canvas
bind:this={canvas}
class="zoomimage"
style:transform="translate({viewState.x}px, {viewState.y}px) scale({viewState.scale})"
>
</canvas>
{/if}
</div>
</div>
<style lang="postcss">
.container {
@apply w-full h-full bg-forge-dark;
overflow: auto;
overflow: hidden;
cursor: grab;
display: flex;
justify-content: center;
align-items: center;
}
.container:active {
cursor: grabbing;
}
.image-container {
transform-origin: 0 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.zoomimage {
position: relative;
}
</style>

View File

@ -1,24 +1,49 @@
import type { OperatorList } from './OperatorList';
export default class ContentModel {
parts: string[];
content: string;
opList: OperatorList;
constructor(parts: string[], opList: OperatorList) {
this.parts = parts;
constructor(content: string, opList: OperatorList) {
this.content = content;
this.opList = opList;
}
public toDisplay(): string {
let text = "";
if (this.parts.length > 1) {
for (const part of this.parts) {
text += part;
// PDF allows for \n\r and \n intermixed. Monaco does not.
// So we to recalculate the content and ranges to contain \n only
private unifyEOL() {
const newRanges: number[][] = [];
const result = this.replaceCRLF(this.content);
for (let i = 0; i < this.opList.rangeArray.length; i++){
const oldRange: number[] = this.opList.rangeArray[i];
if (!oldRange) {
newRanges.push([0, 0]);
continue;
}
} else if (this.parts.length == 1) {
text = this.parts[0];
newRanges.push([result.lookup[oldRange[0]], result.lookup[oldRange[1]]]);
}
return text;
this.content = result.newStr;
this.opList.setRangeArray(newRanges);
}
private replaceCRLF(str: string) {
let newStr = "";
let count = 0;
const lookup = [];
for (let i = 0; i < str.length; i++) {
if (str[i] !== "\r") {
newStr += str[i];
}
if (i > 0 && str[i-1] === "\r") {
count++;
}
lookup.push(i - count);
}
return {newStr: newStr, lookup: lookup};
}
public toDisplay(): string {
this.unifyEOL();
return this.content;
}
}

View File

@ -62,24 +62,22 @@ export default class DocumentWorker {
public async getContents(pageNumber: number): Promise<ContentModel> {
try {
const pathStem = `/Page${pageNumber}/Contents`;
const contents = await this.doc.getPrimitiveByPath(pathStem);
const promises = [];
if (contents && contents.ptype === "Array") {
for (const child of contents.children) {
promises.push(this.doc.getStreamAsString(`${pathStem}/${child.key}/Data`));
}
} else {
promises.push(this.doc.getStreamAsString(`${pathStem}/Data`));
}
const parts = await Promise.all(promises);
return new ContentModel(parts, await this.getOperatorList(pageNumber));
const page = await this.doc.getPage(pageNumber);
const contentsPromise = page.getContents();
const operatorList = await page.getOperatorList(pageNumber)
const opList = OperatorList.createUnfoldedOpList(operatorList.fnArray, operatorList.argsArray, operatorList.rangeArray);
return new ContentModel(await contentsPromise, opList);
} catch (error: unknown) {
console.error(error);
return new ContentModel([], new OperatorList([], [], []));
return new ContentModel("", new OperatorList([], [], []));
}
}
public async updateContents(pageNumber: number, newContents: string) {
const page = await this.doc.getPage(pageNumber);
await page.updateContents(newContents);
}
public dispose() {
this.doc.destroy();
}

View File

@ -19,15 +19,15 @@ export default class FileViewState {
public currentPageNumber: number | undefined = $state();
public treeMode: boolean = $state(true);
public pageMode: boolean = $state(false);
public treeMode: boolean = $state(false);
public pageMode: boolean = $state(true);
public xRefShowing: boolean = $state(false);
public notificationsShowing: boolean = $state(false);
public changesShowing: boolean = $state(false);
public canForward: boolean = $derived(this.pathHistory.canForward);
public canBack: boolean = $derived(this.pathHistory.canBack);
public path: string[] = $state(['Trailer']);
public path: string[] = $state(['Page1']);
public container_prim: Primitive | undefined = $state();
public selected_leaf_prim: Primitive | undefined = $state();
public xref_entries: XRefEntry[] = $state([]);
@ -214,6 +214,8 @@ export default class FileViewState {
if (!this.canBack) return;
const prevPath = this.pathHistory.goBack();
if (prevPath) {
// prevent browser from leaving page
history.pushState(null, "", location.href);
this.selectPath(prevPath);
}
}

View File

@ -8,32 +8,93 @@ export interface PDFOperator {
}
export class OperatorList {
fnArray: [];
fnArray: number[];
argsArray: [];
rangeArray: [];
rangeArray: number[][];
constructor(fnArray: [], argsArray: [], rangeArray: []) {
this.fnArray = fnArray;
this.argsArray = argsArray;
this.rangeArray = rangeArray;
}
public static formatOperatorToMarkdown(operatorId: keyof typeof opLookup): string {
public setRangeArray(rangeArray: number[][]) {
this.rangeArray = rangeArray;
}
public static createUnfoldedOpList(fnArray: [], argsArray: [], rangeArray: []) {
const unfoldedFnArray = [];
const unfoldedArgsArray = [];
const unfoldedRangeArray = [];
for (let i = 0; i < fnArray.length; i++) {
unfoldedFnArray.push(fnArray[i]);
unfoldedArgsArray.push(argsArray[i]);
unfoldedRangeArray.push(rangeArray[i]);
if (fnArray[i] == 91) {
const [subFnArray, subArgsArray, subRangeArray] = OperatorList.unfoldConstructPath(
argsArray[i]
);
unfoldedFnArray.push(...subFnArray);
unfoldedArgsArray.push(...subArgsArray);
unfoldedRangeArray.push(...subRangeArray);
}
}
return new OperatorList(unfoldedFnArray, unfoldedArgsArray, unfoldedRangeArray);
}
private static unfoldConstructPath(args: [[], [], [], []]) {
const [subFnArray, subArgs, , subRanges] = args;
const subArgsArray = [];
const subRangesArray = [];
let argsOffset = 0;
for (let i = 0; i < subFnArray.length; i++) {
const op = getPDFOperator(subFnArray[i]);
const rangeOffset = i * 2;
const range = subRanges.slice(rangeOffset, rangeOffset + 2);
subRangesArray.push(range);
const newSubArgs = [];
for (let j = 0; j < (op.numArgs ?? 0); j++) {
newSubArgs.push(subArgs[argsOffset++]);
}
subArgsArray.push(newSubArgs);
}
return [subFnArray, subArgsArray, subRangesArray];
}
public static formatOperatorToMarkdown(operatorId: number, args: any): string {
const operator = opLookup[operatorId] as PDFOperator;
if (!operator) {
return `Unknown operator: ${operatorId}`;
}
const keyword = operator.keyword ? `\`${operator.keyword}\`` : 'N/A';
const args = operator.numArgs !== undefined ?
`Arguments: ${operator.numArgs}${operator.variableArgs ? ' (variable)' : ''}` :
'No arguments';
return `**${operator.name}** (${keyword}) ` +
`${operator.description} ` +
`${args}`;
const numArgs =
operator.numArgs
? `Arguments: ${operator.numArgs}${operator.variableArgs ? ' (variable)' : ''}`
: 'No arguments';
if (operator.keyword === "Tj" || operator.keyword === "TJ" && Array.isArray(args)) {
let content = '';
content += "`"
for (const arg of args[0]) {
if (arg.unicode) {
content += arg.unicode;
}
}
content += "`"
return `**${operator.name}** (${keyword})
${operator.description}:
${content}
${numArgs}`;
}
return `**${operator.name}** (${keyword})
${operator.description}
${numArgs}`;
}
}
export function getPDFOperator(id: number | string): PDFOperator {
return opLookup[id];
}
export const opLookup = {
'1': {
name: 'dependency',
@ -129,7 +190,7 @@ export const opLookup = {
description: 'Modifies the current transformation matrix (CTM)',
numArgs: 6,
variableArgs: false,
class: 'matrix'
class: 'operator'
},
'13': {
name: 'moveTo',
@ -369,7 +430,7 @@ export const opLookup = {
description: 'Sets the text matrix and text line matrix',
numArgs: 6,
variableArgs: false,
class: 'matrix'
class: 'text-positioning'
},
'43': {
name: 'nextLine',
@ -382,7 +443,7 @@ export const opLookup = {
'44': {
name: 'showText',
keyword: 'Tj',
description: 'Shows a text string',
description: 'Shows the text string',
numArgs: 1,
variableArgs: false,
class: 'text-render'
@ -390,7 +451,7 @@ export const opLookup = {
'45': {
name: 'showSpacedText',
keyword: 'TJ',
description: 'Shows a text string with individual glyph positioning',
description: 'Shows the text string with individual glyph positioning',
numArgs: 1,
variableArgs: false,
class: 'text-render'
@ -669,9 +730,11 @@ export const opLookup = {
},
'85': {
name: 'paintImageXObject',
keyword: null,
description: 'Paints an image XObject',
class: 'named-element'
keyword: 'Do',
description: 'Paints an image XObject from ressources',
numArgs: 1,
variableArgs: false,
class: 'named-element',
},
'86': {
name: 'paintInlineImageXObject',
@ -707,7 +770,7 @@ export const opLookup = {
name: 'constructPath',
keyword: null,
description: 'Constructs a path from the specified segments',
class: 'path-construct'
class: 'constructPath'
},
'92': {
name: 'setStrokeTransparent',

View File

View File

@ -1,5 +1,8 @@
export default class TreeViewRequest {
static TRAILER = new TreeViewRequest("Trailer", [new TreeViewRequest("Root", [])]);
static PAGE = new TreeViewRequest("Page1", []);
public key: string;
public children: TreeViewRequest[];
public displayName: string;
@ -8,7 +11,6 @@ export default class TreeViewRequest {
constructor(
key: string,
children: TreeViewRequest[],
expand: boolean = false,
) {
if (key.startsWith("Page")) {
this.displayName = "Page " + key.slice(4);
@ -17,21 +19,19 @@ export default class TreeViewRequest {
}
this.key = key;
this.children = children;
this.expand = expand;
this.expand = true;
}
static fromPageCount(pageCount: number) {
let roots = [];
static initialRequest(pageCount: number) {
const initialRequest = [TreeViewRequest.TRAILER];
for (let i = 0; i < pageCount; i++) {
roots.push(new TreeViewRequest("Page" + (i + 1), []));
initialRequest.push(new TreeViewRequest("Page" + (i + 1), []));
}
return roots;
return initialRequest;
}
static TRAILER = new TreeViewRequest("Trailer", [new TreeViewRequest("Root", [], true)], true);
public clone(): TreeViewRequest {
return new TreeViewRequest(this.key, this.children.map(child => child.clone()), this.expand);
return new TreeViewRequest(this.key, this.children.map(child => child.clone()));
}
public getChild(key: string) {
@ -40,7 +40,7 @@ export default class TreeViewRequest {
public addChild(key: string) {
this.expand = true;
let child = new TreeViewRequest(key, [], true)
const child = new TreeViewRequest(key, [])
this.children.push(child);
return child;
}

View File

@ -20,12 +20,9 @@ export default class TreeViewState {
private height = 100;
constructor(doc: DocumentWorker) {
this.initialRequest = [TreeViewRequest.TRAILER];
this.initialRequest = this.initialRequest.concat(
TreeViewRequest.fromPageCount(+doc.getNumberOfPages())
);
this.initialRequest = TreeViewRequest.initialRequest(doc.getNumberOfPages());
this.doc = doc;
this.updateTreeViewRequest([this.initialRequest[0]]).then(() => this.updateTreeView(0, 1080));
this.updateTreeViewRequest(this.initialRequest.slice(0, 2)).then(() => this.updateTreeView(0, 1080));
}
public getEntryCount() {

File diff suppressed because one or more lines are too long