/* * This file contains reader annotation pane code. */ const CryptoJS = require("crypto-js"); import Knowledge4Zotero from "../addon"; import AddonBase from "../module"; import { EditorMessage } from "../utils"; class ReaderViews extends AddonBase { icons: object; constructor(parent: Knowledge4Zotero) { super(parent); this.icons = { createNote: ``, ocrTex: ``, }; } async addReaderAnnotationButton(reader: _ZoteroReaderInstance) { if (!reader) { return false; } await reader._initPromise; let updateCount = 0; const _document = reader._iframeWindow.document; for (const moreButton of _document.getElementsByClassName("more")) { if (moreButton.getAttribute("knowledgeinit") === "true") { updateCount += 1; continue; } moreButton.setAttribute("knowledgeinit", "true"); const createNoteButton = _document.createElement("div"); createNoteButton.setAttribute("style", "margin: 5px;"); createNoteButton.title = "Quick Note"; createNoteButton.innerHTML = this.icons["createNote"]; let annotationWrapper = moreButton; while (!annotationWrapper.getAttribute("data-sidebar-annotation-id")) { annotationWrapper = annotationWrapper.parentElement; } const itemKey = annotationWrapper.getAttribute( "data-sidebar-annotation-id" ); const libraryID = (Zotero.Items.get(reader.itemID) as Zotero.Item) .libraryID; const annotationItem = await Zotero.Items.getByLibraryAndKeyAsync( libraryID, itemKey ); createNoteButton.addEventListener("click", async (e) => { await this.createNoteFromAnnotation(annotationItem, e); e.preventDefault(); }); createNoteButton.addEventListener("mouseover", (e: XUL.XULEvent) => { createNoteButton.setAttribute( "style", "background: #F0F0F0; margin: 5px;" ); }); createNoteButton.addEventListener("mouseout", (e: XUL.XULEvent) => { createNoteButton.setAttribute("style", "margin: 5px;"); }); moreButton.before(createNoteButton); if (annotationItem.annotationType === "image") { // Image OCR const ocrButton = _document.createElement("div"); ocrButton.setAttribute("style", "margin: 5px;"); ocrButton.innerHTML = this.icons["ocrTex"]; ocrButton.title = "OCR LaTex"; ocrButton.addEventListener("click", async (e) => { await this.OCRImageAnnotation( ( ocrButton.parentElement.parentElement .nextSibling as HTMLImageElement ).src, annotationItem ); e.preventDefault(); }); ocrButton.addEventListener("mouseover", (e: XUL.XULEvent) => { ocrButton.setAttribute("style", "background: #F0F0F0; margin: 5px;"); }); ocrButton.addEventListener("mouseout", (e: XUL.XULEvent) => { ocrButton.setAttribute("style", "margin: 5px;"); }); moreButton.before(ocrButton); } updateCount += 1; } return reader.annotationItemIDs.length === updateCount; } public async buildReaderAnnotationButtons() { Zotero.debug("Knowledge4Zotero: buildReaderAnnotationButton"); for (const reader of Zotero.Reader._readers) { Zotero.debug("reader found"); let t = 0; while (t < 100 && !(await this.addReaderAnnotationButton(reader))) { await Zotero.Promise.delay(50); t += 1; } } } private async createNoteFromAnnotation( annotationItem: Zotero.Item, event: MouseEvent ) { if (annotationItem.annotationComment) { const text = annotationItem.annotationComment; let link = this._Addon.NoteParse.parseLinkInText(text); if (link) { const note = (await this._Addon.NoteUtils.getNoteFromLink(link)).item; if (note && note.id) { await this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("onNoteLink", { params: { item: note, infoText: "OK", forceStandalone: event.shiftKey, }, }) ); return; } } } const note: Zotero.Item = new Zotero.Item("note"); note.libraryID = annotationItem.libraryID; note.parentID = annotationItem.parentItem.parentID; await note.saveTx(); ZoteroPane.openNoteWindow(note.id); let editorInstance: Zotero.EditorInstance = this._Addon.WorkspaceWindow.getEditorInstance(note); let t = 0; // Wait for editor instance while (t < 10 && !editorInstance) { await Zotero.Promise.delay(500); t += 1; editorInstance = this._Addon.WorkspaceWindow.getEditorInstance(note); } const renderredTemplate = await this._Addon.TemplateController.renderTemplateAsync( "[QuickNoteV4]", "annotationItem, topItem, noteItem", [annotationItem, annotationItem.parentItem.parentItem, note] ); await this._Addon.NoteUtils.addLineToNote( note, renderredTemplate, 0, false, "before" ); const tags = annotationItem.getTags(); for (const tag of tags) { note.addTag(tag.tag, tag.type); } await note.saveTx(); annotationItem.annotationComment = `${ annotationItem.annotationComment ? annotationItem.annotationComment : "" }\nnote link: "${this._Addon.NoteUtils.getNoteLink(note)}"`; await annotationItem.saveTx(); } private async OCRImageAnnotation(src: string, annotationItem: Zotero.Item) { /* message.content = { params: { src: string, annotationItem: Zotero.Item } } */ let result: string; let success: boolean; const engine = Zotero.Prefs.get("Knowledge4Zotero.OCREngine"); if (engine === "mathpix") { const xhr = await Zotero.HTTP.request( "POST", "https://api.mathpix.com/v3/text", { headers: { "Content-Type": "application/json; charset=utf-8", app_id: Zotero.Prefs.get("Knowledge4Zotero.OCRMathpix.Appid"), app_key: Zotero.Prefs.get("Knowledge4Zotero.OCRMathpix.Appkey"), }, body: JSON.stringify({ src: src, math_inline_delimiters: ["$", "$"], math_display_delimiters: ["$$", "$$"], rm_spaces: true, }), responseType: "json", } ); console.log(xhr); if (xhr && xhr.status && xhr.status === 200 && xhr.response.text) { result = xhr.response.text; success = true; } else { result = xhr.status === 200 ? xhr.response.error : `${xhr.status} Error`; success = false; } } else if (engine === "xunfei") { /** * 1.Doc:https://www.xfyun.cn/doc/words/formula-discern/API.html * 2.Error code:https://www.xfyun.cn/document/error-code * @author iflytek */ const config = { hostUrl: "https://rest-api.xfyun.cn/v2/itr", host: "rest-api.xfyun.cn", appid: Zotero.Prefs.get("Knowledge4Zotero.OCRXunfei.APPID"), apiSecret: Zotero.Prefs.get("Knowledge4Zotero.OCRXunfei.APISecret"), apiKey: Zotero.Prefs.get("Knowledge4Zotero.OCRXunfei.APIKey"), uri: "/v2/itr", }; let date = new Date().toUTCString(); let postBody = getPostBody(); let digest = getDigest(postBody); const xhr = await Zotero.HTTP.request("POST", config.hostUrl, { headers: { "Content-Type": "application/json", Accept: "application/json,version=1.0", Host: config.host, Date: date, Digest: digest, Authorization: getAuthStr(date, digest), }, body: JSON.stringify(postBody), responseType: "json", }); if (xhr?.response?.code === 0) { result = xhr.response.data.region .filter((r) => r.type === "text") .map((r) => r.recog.content) .join(" ") .replace(/ifly-latex-(begin)?(end)?/g, "$"); console.log(xhr); success = true; } else { result = xhr.status === 200 ? `${xhr.response.code} ${xhr.response.message}` : `${xhr.status} Error`; success = false; } function getPostBody() { let digestObj = { common: { app_id: config.appid, }, business: { ent: "teach-photo-print", aue: "raw", }, data: { image: src.split(",").pop(), }, }; return digestObj; } function getDigest(body) { return ( "SHA-256=" + CryptoJS.enc.Base64.stringify(CryptoJS.SHA256(JSON.stringify(body))) ); } function getAuthStr(date, digest) { let signatureOrigin = `host: ${config.host}\ndate: ${date}\nPOST ${config.uri} HTTP/1.1\ndigest: ${digest}`; let signatureSha = CryptoJS.HmacSHA256( signatureOrigin, config.apiSecret ); let signature = CryptoJS.enc.Base64.stringify(signatureSha); let authorizationOrigin = `api_key="${config.apiKey}", algorithm="hmac-sha256", headers="host date request-line digest", signature="${signature}"`; return authorizationOrigin; } } else if (engine === "bing") { const xhr = await Zotero.HTTP.request( "POST", "https://www.bing.com/cameraexp/api/v1/getlatex", { headers: { "Content-Type": "application/json", }, body: JSON.stringify({ data: src.split(",").pop(), inputForm: "Image", clientInfo: { platform: "edge" }, }), responseType: "json", } ); if (xhr && xhr.status && xhr.status === 200 && !xhr.response.isError) { result = xhr.response.latex ? `$${xhr.response.latex}$` : xhr.response.ocrText; success = true; } else { result = xhr.status === 200 ? xhr.response.errorMessage : `${xhr.status} Error`; success = false; } } else { result = "OCR Engine Not Found"; success = false; } if (success) { annotationItem.annotationComment = `${ annotationItem.annotationComment ? `${annotationItem.annotationComment}\n` : "" }${result}`; await annotationItem.saveTx(); this._Addon.ZoteroViews.showProgressWindow( "Better Notes OCR", `OCR Result: ${result}` ); } else { this._Addon.ZoteroViews.showProgressWindow( "Better Notes OCR", result, "fail" ); } } } export default ReaderViews;