diff --git a/src/components/ContentEditor.svelte b/src/components/ContentEditor.svelte
index 160e9ed..9522a83 100644
--- a/src/components/ContentEditor.svelte
+++ b/src/components/ContentEditor.svelte
@@ -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);
}
@@ -170,4 +200,8 @@
background-color: #3c3d41;
}
+ :global(.highlightOp) {
+ @apply border border-forge-sec rounded;
+ }
+
diff --git a/src/components/ImageViewer.svelte b/src/components/ImageViewer.svelte
index 80c1abe..ecd4125 100644
--- a/src/components/ImageViewer.svelte
+++ b/src/components/ImageViewer.svelte
@@ -12,7 +12,8 @@
=w||c<0||c>=y?l<<=1:l=l<<1|r[o][c]}const g=k.readBit(C,l);t[s]=g}}return S}function decodeTextRegion(e,t,a,r,i,n,s,o,c,l,h,u,d,f,g,p,m,b,y){if(e&&t)throw new Jbig2Error("refinement with Huffman is not supported");const w=[];let x,S;for(x=0;x=0&&e=0;t--){d[t]=o[a];a=l[a]}}else d[f++]=d[0]}if(i){l[s]=u;c[s]=c[u]+1;o[s]=d[0];s++;h=s+n&s+n-1?h:0|Math.min(Math.log(s+n)/.6931471805599453+1,12)}u=e;g+=f;if(r127))){n=0;break}}if(2!==n)continue;if(!t){warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");continue}const o=new Lexer(new Stream(e.peekBytes(75)),t);o._hexStringWarn=()=>{};let c=0;for(;;){const e=o.getObj();if(e===ma){n=0;break}if(e instanceof Cmd){const a=t[e.cmd];if(!a){n=0;break}if(a.variableArgs?c<=a.numArgs:c===a.numArgs)break;c=0}else c++}if(2===n)break}else n=0;if(-1===r){warn("findDefaultInlineStreamEnd: Reached the end of the stream without finding a valid EI marker");if(i){warn('... trying to recover by using the last "EI" occurrence.');e.skip(-(e.pos-i))}}let s=4;e.skip(-s);r=e.peekByte();e.skip(s);isWhiteSpace(r)||s--;return e.pos-s-a}findDCTDecodeInlineStreamEnd(e){const t=e.pos;let a,r,i=!1;for(;-1!==(a=e.getByte());)if(255===a){switch(e.getByte()){case 0:break;case 255:e.skip(-1);break;case 217:i=!0;break;case 192:case 193:case 194:case 195:case 197:case 198:case 199:case 201:case 202:case 203:case 205:case 206:case 207:case 196:case 204:case 218:case 219:case 220:case 221:case 222:case 223:case 224:case 225:case 226:case 227:case 228:case 229:case 230:case 231:case 232:case 233:case 234:case 235:case 236:case 237:case 238:case 239:case 254:r=e.getUint16();r>2?e.skip(r-2):e.skip(-2)}if(i)break}const n=e.pos-t;if(-1===a){warn("Inline DCTDecode image stream: EOI marker not found, searching for /EI/ instead.");e.skip(-n);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return n}findASCII85DecodeInlineStreamEnd(e){const t=e.pos;let a;for(;-1!==(a=e.getByte());)if(126===a){const t=e.pos;a=e.peekByte();for(;isWhiteSpace(a);){e.skip();a=e.peekByte()}if(62===a){e.skip();break}if(e.pos>t){const t=e.peekBytes(2);if(69===t[0]&&73===t[1])break}}const r=e.pos-t;if(-1===a){warn("Inline ASCII85Decode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}findASCIIHexDecodeInlineStreamEnd(e){const t=e.pos;let a;for(;-1!==(a=e.getByte())&&62!==a;);const r=e.pos-t;if(-1===a){warn("Inline ASCIIHexDecode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}inlineStreamSkipEI(e){let t,a=0;for(;-1!==(t=e.getByte());)if(0===a)a=69===t?1:0;else if(1===a)a=73===t?2:0;else if(2===a)break}makeInlineImage(e){const t=this.lexer,a=t.stream,r=Object.create(null);let i;for(;!isCmd(this.buf1,"ID")&&this.buf1!==ma;){if(!(this.buf1 instanceof Name))throw new FormatError("Dictionary key must be a name object");const t=this.buf1.name;this.shift();if(this.buf1===ma)break;r[t]=this.getObj(e)}-1!==t.beginInlineImagePos&&(i=a.pos-t.beginInlineImagePos);const n=this.xref.fetchIfRef(r.F||r.Filter);let s;if(n instanceof Name)s=n.name;else if(Array.isArray(n)){const e=this.xref.fetchIfRef(n[0]);e instanceof Name&&(s=e.name)}const o=a.pos;let c,l;switch(s){case"DCT":case"DCTDecode":c=this.findDCTDecodeInlineStreamEnd(a);break;case"A85":case"ASCII85Decode":c=this.findASCII85DecodeInlineStreamEnd(a);break;case"AHx":case"ASCIIHexDecode":c=this.findASCIIHexDecodeInlineStreamEnd(a);break;default:c=this.findDefaultInlineStreamEnd(a)}if(c<1e3&&i>0){const e=a.pos;a.pos=t.beginInlineImagePos;l=function getInlineImageCacheKey(e){const t=[],a=e.length;let r=0;for(;r=4){i-=4;if(this.seacAnalysisEnabled){e.seac=n.slice(i,i+4);return!1}}l=Sr[c]}else if(c>=32&&c<=246){n[i]=c-139;i++}else if(c>=247&&c<=254){n[i]=c<251?(c-247<<8)+t[o]+108:-(c-251<<8)-t[o]-108;o++;i++}else if(255===c){n[i]=(t[o]<<24|t[o+1]<<16|t[o+2]<<8|t[o+3])/65536;o+=4;i++}else if(19===c||20===c){e.hints+=i>>1;if(0===e.hints){t.copyWithin(o-1,o,-1);o-=1;s-=1;continue}o+=e.hints+7>>3;i%=2;l=Sr[c]}else{if(10===c||29===c){const t=10===c?a:r;if(!t){l=Sr[c];warn("Missing subrsIndex for "+l.id);return!1}let s=32768;t.count<1240?s=107:t.count<33900&&(s=1131);const o=n[--i]+s;if(o<0||o>=t.count||isNaN(o)){l=Sr[c];warn("Out of bounds subrIndex for "+l.id);return!1}e.stackSize=i;e.callDepth++;if(!this.parseCharString(e,t.get(o),a,r))return!1;e.callDepth--;i=e.stackSize;continue}if(11===c){e.stackSize=i;return!0}if(0===c&&o===t.length){t[o-1]=14;l=Sr[14]}else{if(9===c){t.copyWithin(o-1,o,-1);o-=1;s-=1;continue}l=Sr[c]}}if(l){if(l.stem){e.hints+=i>>1;if(3===c||23===c)e.hasVStems=!0;else if(e.hasVStems&&(1===c||18===c)){warn("CFF stem hints are in wrong order");t[o-1]=1===c?3:23}}if("min"in l&&!e.undefStack&&i0){w+="ÿÿ";y+="ÿÿ";x+="\0";S+="\0\0"}const v="\0\0"+string16(2*d)+string16(f.range)+string16(f.entry)+string16(f.rangeShift)+w+"\0\0"+y+x+S+k;let F="",O="";if(i>1){l+="\0\0\n"+string32(4+8*i+4+v.length);F="";for(n=0,s=r.length;ne||!o)&&(o=e);c=0){reverseValues(hn,e,n);e=-1}}else e<0&&(e=n);e>=0&&reverseValues(hn,e,c.length)}for(n=0,s=hn.length;n"!==e||(hn[n]="")}return createBidiText(hn.join(""),r)}const dn={style:"normal",weight:"normal"},fn={style:"normal",weight:"bold"},gn={style:"italic",weight:"normal"},pn={style:"italic",weight:"bold"},mn=new Map([["Times-Roman",{local:["Times New Roman","Times-Roman","Times","Liberation Serif","Nimbus Roman","Nimbus Roman L","Tinos","Thorndale","TeX Gyre Termes","FreeSerif","Linux Libertine O","Libertinus Serif","DejaVu Serif","Bitstream Vera Serif","Ubuntu"],style:dn,ultimate:"serif"}],["Times-Bold",{alias:"Times-Roman",style:fn,ultimate:"serif"}],["Times-Italic",{alias:"Times-Roman",style:gn,ultimate:"serif"}],["Times-BoldItalic",{alias:"Times-Roman",style:pn,ultimate:"serif"}],["Helvetica",{local:["Helvetica","Helvetica Neue","Arial","Arial Nova","Liberation Sans","Arimo","Nimbus Sans","Nimbus Sans L","A030","TeX Gyre Heros","FreeSans","DejaVu Sans","Albany","Bitstream Vera Sans","Arial Unicode MS","Microsoft Sans Serif","Apple Symbols","Cantarell"],path:"LiberationSans-Regular.ttf",style:dn,ultimate:"sans-serif"}],["Helvetica-Bold",{alias:"Helvetica",path:"LiberationSans-Bold.ttf",style:fn,ultimate:"sans-serif"}],["Helvetica-Oblique",{alias:"Helvetica",path:"LiberationSans-Italic.ttf",style:gn,ultimate:"sans-serif"}],["Helvetica-BoldOblique",{alias:"Helvetica",path:"LiberationSans-BoldItalic.ttf",style:pn,ultimate:"sans-serif"}],["Courier",{local:["Courier","Courier New","Liberation Mono","Nimbus Mono","Nimbus Mono L","Cousine","Cumberland","TeX Gyre Cursor","FreeMono","Linux Libertine Mono O","Libertinus Mono"],style:dn,ultimate:"monospace"}],["Courier-Bold",{alias:"Courier",style:fn,ultimate:"monospace"}],["Courier-Oblique",{alias:"Courier",style:gn,ultimate:"monospace"}],["Courier-BoldOblique",{alias:"Courier",style:pn,ultimate:"monospace"}],["ArialBlack",{local:["Arial Black"],style:{style:"normal",weight:"900"},fallback:"Helvetica-Bold"}],["ArialBlack-Bold",{alias:"ArialBlack"}],["ArialBlack-Italic",{alias:"ArialBlack",style:{style:"italic",weight:"900"},fallback:"Helvetica-BoldOblique"}],["ArialBlack-BoldItalic",{alias:"ArialBlack-Italic"}],["ArialNarrow",{local:["Arial Narrow","Liberation Sans Narrow","Helvetica Condensed","Nimbus Sans Narrow","TeX Gyre Heros Cn"],style:dn,fallback:"Helvetica"}],["ArialNarrow-Bold",{alias:"ArialNarrow",style:fn,fallback:"Helvetica-Bold"}],["ArialNarrow-Italic",{alias:"ArialNarrow",style:gn,fallback:"Helvetica-Oblique"}],["ArialNarrow-BoldItalic",{alias:"ArialNarrow",style:pn,fallback:"Helvetica-BoldOblique"}],["Calibri",{local:["Calibri","Carlito"],style:dn,fallback:"Helvetica"}],["Calibri-Bold",{alias:"Calibri",style:fn,fallback:"Helvetica-Bold"}],["Calibri-Italic",{alias:"Calibri",style:gn,fallback:"Helvetica-Oblique"}],["Calibri-BoldItalic",{alias:"Calibri",style:pn,fallback:"Helvetica-BoldOblique"}],["Wingdings",{local:["Wingdings","URW Dingbats"],style:dn}],["Wingdings-Regular",{alias:"Wingdings"}],["Wingdings-Bold",{alias:"Wingdings"}]]),bn=new Map([["Arial-Black","ArialBlack"]]);function getFamilyName(e){const t=new Set(["thin","extralight","ultralight","demilight","semilight","light","book","regular","normal","medium","demibold","semibold","bold","extrabold","ultrabold","black","heavy","extrablack","ultrablack","roman","italic","oblique","ultracondensed","extracondensed","condensed","semicondensed","normal","semiexpanded","expanded","extraexpanded","ultraexpanded","bolditalic"]);return e.split(/[- ,+]+/g).filter((e=>!t.has(e.toLowerCase()))).join(" ")}function generateFont({alias:e,local:t,path:a,fallback:r,style:i,ultimate:n},s,o,c=!0,l=!0,h=""){const u={style:null,ultimate:null};if(t){const e=h?` ${h}`:"";for(const a of t)s.push(`local(${a}${e})`)}if(e){const t=mn.get(e),n=h||function getStyleToAppend(e){switch(e){case fn:return"Bold";case gn:return"Italic";case pn:return"Bold Italic";default:if("bold"===e?.weight)return"Bold";if("italic"===e?.style)return"Italic"}return""}(i);Object.assign(u,generateFont(t,s,o,c&&!r,l&&!a,n))}i&&(u.style=i);n&&(u.ultimate=n);if(c&&r){const e=mn.get(r),{ultimate:t}=generateFont(e,s,o,c,l&&!a,h);u.ultimate||=t}l&&a&&o&&s.push(`url(${o}${a})`);return u}function getFontSubstitution(e,t,a,r,i,n){if(r.startsWith("InvalidPDFjsFont_"))return null;"TrueType"!==n&&"Type1"!==n||!/^[A-Z]{6}\+/.test(r)||(r=r.slice(7));const s=r=normalizeFontName(r);let o=e.get(s);if(o)return o;let c=mn.get(r);if(!c)for(const[e,t]of bn)if(r.startsWith(e)){r=`${t}${r.substring(e.length)}`;c=mn.get(r);break}let l=!1;if(!c){c=mn.get(i);l=!0}const h=`${t.getDocId()}_s${t.createFontId()}`;if(!c){if(!validateFontName(r)){warn(`Cannot substitute the font because of its name: ${r}`);e.set(s,null);return null}const t=/bold/gi.test(r),a=/oblique|italic/gi.test(r),i=t&&a&&pn||t&&fn||a&&gn||dn;o={css:`"${getFamilyName(r)}",${h}`,guessFallback:!0,loadedName:h,baseFontName:r,src:`local(${r})`,style:i};e.set(s,o);return o}const u=[];l&&validateFontName(r)&&u.push(`local(${r})`);const{style:d,ultimate:f}=generateFont(c,u,a),g=null===f,p=g?"":`,${f}`;o={css:`"${getFamilyName(r)}",${h}${p}`,guessFallback:g,loadedName:h,baseFontName:r,src:u.join(","),style:d};e.set(s,o);return o}const yn=3285377520,wn=4294901760,xn=65535;class MurmurHash3_64{constructor(e){this.h1=e?4294967295&e:yn;this.h2=e?4294967295&e:yn}update(e){let t,a;if("string"==typeof e){t=new Uint8Array(2*e.length);a=0;for(let r=0,i=e.length;r>>8;t[a++]=255&i}}}else{if(!ArrayBuffer.isView(e))throw new Error("Invalid data format, must be a string or TypedArray.");t=e.slice();a=t.byteLength}const r=a>>2,i=a-4*r,n=new Uint32Array(t.buffer,0,r);let s=0,o=0,c=this.h1,l=this.h2;const h=3432918353,u=461845907,d=11601,f=13715;for(let e=0;e.5*p.width){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(t)>p.width){appendEOL();return!0}e<=s*p.notASpace&&resetLastChars();if(e<=s*p.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({height:Math.abs(e)})}else p.height+=e;else if(!addFakeSpaces(e,p.prevTransform,s))if(0===p.str.length){resetLastChars();pushWhitespace({height:Math.abs(e)})}else p.height+=e;Math.abs(t)>.25*p.width&&flushTextContentItem();return!0}const o=(a-i)/p.textAdvanceScale,c=r-n,h=Math.sign(p.width);if(o