In pdf.js unable to get pdfdocument.annotationStorage to update, and not saving - javascript

Based on the examples, and using version 3.3.122 of the library, I am trying to fill out a form, and save the results.
I can see on the viewer.html, that when you update the field, pdfdocument.annotationStorage is updated, but when I attempt it, I can fill out the fields, but the values are not reflected in the annotation storage, and when I save, the values are not coming through.
Yes, I want to use pdf.js library!
` var url = '/test/fw4_2.pdf';
"use strict";
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.3.122/pdf.worker.min.js';
// Some PDFs need external cmaps.
//
// const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
const DEFAULT_URL = url;
const PAGE_TO_VIEW = 1;
const SCALE = 1.0;
const ENABLE_XFA = true;
const container = document.getElementById("pageContainer");
const eventBus = new pdfjsViewer.EventBus();
// Loading document.
const loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
// cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA,
});
var pdfDocument;
var pdfPage;
var pdfPageView;
var r = (async function () {
pdfDocument = await loadingTask.promise;
// Document loaded, retrieving the page.
pdfPage = await pdfDocument.getPage(PAGE_TO_VIEW);
// Creating the page view with default parameters.
pdfPageView = new pdfjsViewer.PDFPageView({
container,
id: PAGE_TO_VIEW,
scale: SCALE,
defaultViewport: pdfPage.getViewport({ scale: SCALE }),
eventBus,
// We can enable text/annotation/xfa/struct-layers, as needed.
textLayerMode :2,
annotationEditorMode : pdfjsLib.AnnotationEditorType.TextAnnotation,
xfaLayerFactory: pdfDocument.isPureXfa
? new pdfjsViewer.DefaultXfaLayerFactory()
: null,
});
// Associate the actual page with the view, and draw it.
pdfPageView.setPdfPage(pdfPage);
pdfPageView.draw();
})();`
I have tried different so many ways of trying to load the document, saveDocument, getData, and setting annotations directly, with no luck.
I also changed the library versions, and I feel there could be some inconsistencies with the library.

Related

Vscode move to line X after openTextDocument

I'm developing a VS Code extension that jump to a specific file:num, but I'm stuck at the step of moving the cursor to a specific line after opening a file.
How can I achieve this :
export const openAndMoveToLine = async (file_line: string) => {
// /home/user/some/path.php:10
let [filename, line_number] = file_line.split(":")
// opening the file => OK
let setting: vscode.Uri = vscode.Uri.parse(filename)
let doc = await vscode.workspace.openTextDocument(setting)
vscode.window.showTextDocument(doc, 1, false);
// FIXME: After being opened, now move to the line X => NOK **/
await vscode.commands.executeCommand("cursorMove", {
to: "down", by:'wrappedLine',
value: parseInt(line_number)
});
}
Thank you
It can be done with the TextDocumentShowOptions easily:
const showDocOptions = {
preserveFocus: false,
preview: false,
viewColumn: 1,
// replace with your line_number's
selection: new vscode.Range(314, 0, 314, 0)
};
let doc = await vscode.window.showTextDocument(setting, showDocOptions);
You will first need to get access to the active editor. This is done by adding a .then to the showTextDocument call (which is a Thenable function) that returns a text editor object. You will then be able to use the textEditor variable (as in the example) to set the position of the cursor using the selection property as follows:
vscode.window.showTextDocument(doc, 1, false).then((textEditor: TextEditor) => {
const lineNumber = 1;
const characterNumberOnLine = 1;
const position = new vscode.Position(lineNumber, characterNumberOnLine);
const newSelection = new vscode.Selection(position, position);
textEditor.selection = newSelection;
});
Reference to selection API can be found here.
The usecase that you are exploring has been discussed in GitHub issue that can be found here.

PDFLib.js field.constructor.name return "t" after build

I'm trying to fill pdf form usinf PDFLib js.
In my localhost it works fine but after deployment "field.constructor.name" return "t" type. and the data is no filled.
there is no error, but nothing happened.
const pdfDoc = await PDFDocument.load(formPdfBytes);
// Register the `fontkit` instance
pdfDoc.registerFontkit(fontkit);
// Embed our custom font in the document
const customFont = await pdfDoc.embedFont(fontBytes, { subset: true });
// Get the form containing all the fields
const form = pdfDoc.getForm();
const fields = form.getFields()
fields.forEach(field => {
//HERE THE PROBLEM!!
const type = field.constructor.name // GOT "t"
const name = field.getName();
I found the problem. The font file was problematic.

Svelte components store - load state into - from URL hash parameters

If we have a Single Page Application built with Svelte with a bunch of components and a store where we keep our current app state, is there an recommended way to store the store state changes into the # hash part of the current URL and be able to re-load the same state from the full URL?
It can be done manually by parsing the current URL with location.search().
Storing of parameters can be done with location.search("key", "value").
Some questions:
When to load the state from URL? What would be the App init entry
point?
When to store the state from the store to the URL? Is there a generic
way to do this?
Edit:
svelte-spa-router seems to offer querystring support out of the box.
I ended up using URLSearchParams and polyfill for it to write functions which serialize and deserialize config object which is kept in store:
import 'url-search-params-polyfill';
export function deserializeConfig(serializedConfig, resultConfig) {
let hashParams = new URLSearchParams(serializedConfig);
for (const hashParameterAndValue of hashParams.entries()) {
const key = hashParameterAndValue[0];
const value = hashParameterAndValue[1];
const decodedKey = decodeUrlParameterKey(key);
const decodedValue = decodeUrlParameterValue(value);
resultConfig[decodedKey] = decodedValue;
}
export function serializeConfig(config) {
const hashParams = new URLSearchParams("");
for (const key in config) {
const value = config[key];
const encodedValue = encodeParameterValue(value);
const encodedKey = encodeParameterKey(key);;
hashParams.set(encodedKey, encodedValue);
}
const serializedConfig = hashParams.toString();
return serializedConfig;
}
To use it with to serialize / deserialize state from the URL hash:
in main.js:
import { configFromStore } from "./stores.js";
let config = {};
// when config from store changes
configFromStore.subscribe(updatedConfig => {
config = updatedConfig;
// check if the config was really modified and does not match the default
if (!isEquivalent(updatedConfig, defaultConfig)) {
// update URL hash after store value has been changed
const serializedConfig = serializeConfig(updatedConfig);
window.location.hash = "#" + serializedConfig;
}
}
// on main app start, parse state from URL hash
const hash = window.location.hash;
if (hash && hash.length > 1) {
const serializedConfig = hash.substr(1);
deserializeConfig(serializedConfig, config);
configFromStore.set(config);
}
It is a bit more trickier than this since you need to take care about the default config and remove values from the serialized config that are same with the default.
Also the subscribe is called initially when the config is loaded even though the config is not yet modified at this point.

Is there a way to trigger validation manually in monaco editor?

I'm using the default TypeScript service and the models are initialized asynchronously with one model depending on the other. There's a case where the two models cannot detect each other so it shows a semantic error. If I make some edits in the dependent model, which causes the model to be re-validated, the errors disappear.
I have tried to setModel manually, which solves the problems. However, it destroys the undo history.
Is there a way to re-validate the model manually?
That's my solution, which is extracted from monaco-typescript:
async function revalidateModel(model) {
if (!model || model.isDisposed()) return;
const getWorker = await monaco.languages.typescript.getTypeScriptWorker();
const worker = await getWorker(model.uri);
const diagnostics = (await Promise.all([
worker.getSyntacticDiagnostics(model.uri.toString()),
worker.getSemanticDiagnostics(model.uri.toString())
])).reduce((a, it) => a.concat(it));
const markers = diagnostics.map(d => {
const start = model.getPositionAt(d.start);
const end = model.getPositionAt(d.start + d.length);
return {
severity: monaco.MarkerSeverity.Error,
startLineNumber: start.lineNumber,
startColumn: start.column,
endLineNumber: end.lineNumber,
endColumn: end.column,
message: flattenDiagnosticMessageText(d.messageText, "\n")
};
});
const owner = model.getLanguageIdentifier().language;
monaco.editor.setModelMarkers(model, owner, markers);
}
Call the function above when model is created asynchronizedly.
This is what I did to fix it:
setInterval(() => {
const range = new monaco.Range(1,1,1,1);
const addEmptySpace = {forceMoveMarkers: true, range, text: ' '};
for (const m of monaco.editor.getModels()) {
const toInvert = m.applyEdits([addEmptySpace]);
m.applyEdits(toInvert);
}
}, 50*1000)
Every fifty seconds you insert and immediately remove a space. I don't like it, but it works.

How to make Gnome Shell extension query for changes

I've been battling the horrendous Gnome API documentation and came up with this extension:
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const GLib = imports.gi.GLib;
let label;
function init() {
label = new St.Bin({ style_class: 'panel-label' });
let stuff = GLib.spawn_command_line_sync("cat /home/user/temp/hello")[1].toString();
let text = new St.Label({ text: stuff });
label.set_child(text);
}
function enable() {
Main.panel._rightBox.insert_child_at_index(label, 0);
}
function disable() {
Main.panel._rightBox.remove_child(label);
}
This should read whatever is in the hello file and display it in the top panel. However, if I change the contents of the hello file, I have to restart Gnome for that new content to be shown. Now, surely there is a way to do this dynamically but I just couldn't find anything in the documentation. The message in the panel should basically always mirror whatever is in the file. Any ideas how to do this?
You'll want to obtain a Gio.File handle for your hello file, and then monitor it:
let helloFile = Gio.File.new_for_path('/home/user/temp/hello');
let monitor = helloFile.monitor(Gio.FileMonitorFlags.NONE, null);
monitor.connect('changed', function (file, otherFile, eventType) {
// change your UI here
});
This worked for me. It will refresh the label value each 30 seconds.
Add the following import
const Mainloop = imports.mainloop;
In your init method
Mainloop.timeout_add(30000, function () {
let stuff = GLib.spawn_command_line_sync("your_command")[1].toString();
let label = new St.Label({ text: stuff });
button.set_child(label);return true});

Categories

Resources