/*
* 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;