[Editor] Add the possibility to query some ML stuff to guess an alt text for an image

It's only for an experimental purpose.
This commit is contained in:
Calixte Denizet 2024-02-20 09:49:20 +01:00
parent 99fa713fba
commit 46416bb131
11 changed files with 102 additions and 6 deletions

View File

@ -45,6 +45,10 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"enableML": {
"type": "boolean",
"default": false
},
"cursorToolOnLoad": { "cursorToolOnLoad": {
"title": "Cursor tool on load", "title": "Cursor tool on load",
"description": "The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.", "description": "The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.",

View File

@ -76,6 +76,10 @@ class AltText {
this.#altTextWasFromKeyBoard = false; this.#altTextWasFromKeyBoard = false;
} }
isEmpty() {
return !this.#altText && !this.#altTextDecorative;
}
get data() { get data() {
return { return {
altText: this.#altText, altText: this.#altText,

View File

@ -970,6 +970,10 @@ class AnnotationEditor {
this.#altText.data = data; this.#altText.data = data;
} }
hasAltText() {
return !this.#altText?.isEmpty();
}
/** /**
* Render this editor in a div. * Render this editor in a div.
* @returns {HTMLDivElement | null} * @returns {HTMLDivElement | null}

View File

@ -431,6 +431,42 @@ class StampEditor extends AnnotationEditor {
const bitmap = this.#isSvg const bitmap = this.#isSvg
? this.#bitmap ? this.#bitmap
: this.#scaleBitmap(width, height); : this.#scaleBitmap(width, height);
if (this._uiManager.hasMLManager && !this.hasAltText()) {
const offscreen = new OffscreenCanvas(width, height);
const ctx = offscreen.getContext("2d");
ctx.drawImage(
bitmap,
0,
0,
bitmap.width,
bitmap.height,
0,
0,
width,
height
);
offscreen.convertToBlob().then(blob => {
const fileReader = new FileReader();
fileReader.onload = () => {
const url = fileReader.result;
this._uiManager
.mlGuess({
service: "image-to-text",
request: {
imageData: url,
},
})
.then(response => {
const altText = response?.output || "";
if (this.parent && altText && !this.hasAltText()) {
this.altTextData = { altText, decorative: false };
}
});
};
fileReader.readAsDataURL(blob);
});
}
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
ctx.filter = this._uiManager.hcmFilter; ctx.filter = this._uiManager.hcmFilter;
ctx.drawImage( ctx.drawImage(

View File

@ -563,6 +563,8 @@ class AnnotationEditorUIManager {
#mainHighlightColorPicker = null; #mainHighlightColorPicker = null;
#mlManager = null;
#mode = AnnotationEditorType.NONE; #mode = AnnotationEditorType.NONE;
#selectedEditors = new Set(); #selectedEditors = new Set();
@ -749,7 +751,8 @@ class AnnotationEditorUIManager {
eventBus, eventBus,
pdfDocument, pdfDocument,
pageColors, pageColors,
highlightColors highlightColors,
mlManager
) { ) {
this.#container = container; this.#container = container;
this.#viewer = viewer; this.#viewer = viewer;
@ -763,6 +766,7 @@ class AnnotationEditorUIManager {
this.#filterFactory = pdfDocument.filterFactory; this.#filterFactory = pdfDocument.filterFactory;
this.#pageColors = pageColors; this.#pageColors = pageColors;
this.#highlightColors = highlightColors || null; this.#highlightColors = highlightColors || null;
this.#mlManager = mlManager || null;
this.viewParameters = { this.viewParameters = {
realScale: PixelsPerInch.PDF_TO_CSS_UNITS, realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
rotation: 0, rotation: 0,
@ -797,6 +801,14 @@ class AnnotationEditorUIManager {
} }
} }
async mlGuess(data) {
return this.#mlManager?.guess(data) || null;
}
get hasMLManager() {
return !!this.#mlManager;
}
get hcmFilter() { get hcmFilter() {
return shadow( return shadow(
this, this,

View File

@ -53,7 +53,7 @@ import {
} from "pdfjs-lib"; } from "pdfjs-lib";
import { AppOptions, OptionKind } from "./app_options.js"; import { AppOptions, OptionKind } from "./app_options.js";
import { AutomationEventBus, EventBus } from "./event_utils.js"; import { AutomationEventBus, EventBus } from "./event_utils.js";
import { ExternalServices, initCom } from "web-external_services"; import { ExternalServices, initCom, MLManager } from "web-external_services";
import { LinkTarget, PDFLinkService } from "./pdf_link_service.js"; import { LinkTarget, PDFLinkService } from "./pdf_link_service.js";
import { AltTextManager } from "web-alt_text_manager"; import { AltTextManager } from "web-alt_text_manager";
import { AnnotationEditorParams } from "web-annotation_editor_params"; import { AnnotationEditorParams } from "web-annotation_editor_params";
@ -420,6 +420,7 @@ const PDFViewerApplication = {
maxCanvasPixels: AppOptions.get("maxCanvasPixels"), maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
enablePermissions: AppOptions.get("enablePermissions"), enablePermissions: AppOptions.get("enablePermissions"),
pageColors, pageColors,
mlManager: this.mlManager,
}); });
this.pdfViewer = pdfViewer; this.pdfViewer = pdfViewer;
@ -682,6 +683,14 @@ const PDFViewerApplication = {
return shadow(this, "externalServices", new ExternalServices()); return shadow(this, "externalServices", new ExternalServices());
}, },
get mlManager() {
return shadow(
this,
"mlManager",
AppOptions.get("enableML") === true ? new MLManager() : null
);
},
get initialized() { get initialized() {
return this._initializedCapability.settled; return this._initializedCapability.settled;
}, },

View File

@ -143,6 +143,11 @@ const defaultOptions = {
value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"), value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"),
kind: OptionKind.VIEWER + OptionKind.PREFERENCE, kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
}, },
enableML: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
enablePermissions: { enablePermissions: {
/** @type {boolean} */ /** @type {boolean} */
value: false, value: false,

View File

@ -435,4 +435,10 @@ class ExternalServices extends BaseExternalServices {
} }
} }
export { ExternalServices, initCom, Preferences }; class MLManager {
async guess() {
return null;
}
}
export { ExternalServices, initCom, MLManager, Preferences };

View File

@ -314,6 +314,12 @@ class FirefoxScripting {
} }
} }
class MLManager {
guess(data) {
return FirefoxCom.requestAsync("mlGuess", data);
}
}
class ExternalServices extends BaseExternalServices { class ExternalServices extends BaseExternalServices {
updateFindControlState(data) { updateFindControlState(data) {
FirefoxCom.request("updateFindControlState", data); FirefoxCom.request("updateFindControlState", data);
@ -415,4 +421,4 @@ class ExternalServices extends BaseExternalServices {
} }
} }
export { DownloadManager, ExternalServices, initCom, Preferences }; export { DownloadManager, ExternalServices, initCom, MLManager, Preferences };

View File

@ -47,4 +47,10 @@ class ExternalServices extends BaseExternalServices {
} }
} }
export { ExternalServices, initCom, Preferences }; class MLManager {
async guess() {
return null;
}
}
export { ExternalServices, initCom, MLManager, Preferences };

View File

@ -216,6 +216,8 @@ class PDFViewer {
#enablePermissions = false; #enablePermissions = false;
#mlManager = null;
#getAllTextInProgress = false; #getAllTextInProgress = false;
#hiddenCopyElement = null; #hiddenCopyElement = null;
@ -292,6 +294,7 @@ class PDFViewer {
} }
this.#enablePermissions = options.enablePermissions || false; this.#enablePermissions = options.enablePermissions || false;
this.pageColors = options.pageColors || null; this.pageColors = options.pageColors || null;
this.#mlManager = options.mlManager || null;
this.defaultRenderingQueue = !options.renderingQueue; this.defaultRenderingQueue = !options.renderingQueue;
if ( if (
@ -857,7 +860,8 @@ class PDFViewer {
this.eventBus, this.eventBus,
pdfDocument, pdfDocument,
this.pageColors, this.pageColors,
this.#annotationEditorHighlightColors this.#annotationEditorHighlightColors,
this.#mlManager
); );
this.eventBus.dispatch("annotationeditoruimanager", { this.eventBus.dispatch("annotationeditoruimanager", {
source: this, source: this,