This commit is contained in:
Vanessa 2023-01-07 22:15:11 +08:00
parent 17067c4d49
commit aa677abb62
2 changed files with 218 additions and 218 deletions

View File

@ -11,30 +11,31 @@ import {getCurrentWindow} from "@electron/remote";
import {focusByRange} from "../protyle/util/selection"; import {focusByRange} from "../protyle/util/selection";
import {Constants} from "../constants"; import {Constants} from "../constants";
const rectAnno = (config: any, pdf: any, element: HTMLElement) => { export const initAnno = (file: string, element: HTMLElement, annoId: string, pdf: any, pdfConfig: any) => {
const rectAnnoElement = config.toolbar.rectAnno; getConfig(pdf);
const rectAnnoElement = pdfConfig.toolbar.rectAnno;
rectAnnoElement.addEventListener("click", () => { rectAnnoElement.addEventListener("click", () => {
if (rectAnnoElement.classList.contains("toggled")) { if (rectAnnoElement.classList.contains("toggled")) {
rectAnnoElement.classList.remove("toggled"); rectAnnoElement.classList.remove("toggled");
config.mainContainer.classList.remove("rect-to-annotation"); pdfConfig.mainContainer.classList.remove("rect-to-annotation");
} else { } else {
pdf.pdfCursorTools.switchTool(0); pdf.pdfCursorTools.switchTool(0);
rectAnnoElement.classList.add("toggled"); rectAnnoElement.classList.add("toggled");
config.mainContainer.classList.add("rect-to-annotation"); pdfConfig.mainContainer.classList.add("rect-to-annotation");
if (getSelection().rangeCount > 0) { if (getSelection().rangeCount > 0) {
getSelection().getRangeAt(0).collapse(true); getSelection().getRangeAt(0).collapse(true);
} }
hideToolbar(element); hideToolbar(element);
} }
}); });
const rectResizeElement = config.mainContainer.lastElementChild; const rectResizeElement = pdfConfig.mainContainer.lastElementChild;
config.mainContainer.addEventListener("mousedown", (event: MouseEvent) => { pdfConfig.mainContainer.addEventListener("mousedown", (event: MouseEvent) => {
if (event.button === 2 || !rectAnnoElement.classList.contains("toggled")) { if (event.button === 2 || !rectAnnoElement.classList.contains("toggled")) {
// 右键 // 右键
return; return;
} }
const canvasRect = pdf.pdfViewer._getVisiblePages().first.view.canvas.getBoundingClientRect(); const canvasRect = pdf.pdfViewer._getVisiblePages().first.view.canvas.getBoundingClientRect();
const containerRet = config.mainContainer.getBoundingClientRect(); const containerRet = pdfConfig.mainContainer.getBoundingClientRect();
const mostLeft = canvasRect.left; const mostLeft = canvasRect.left;
const mostRight = canvasRect.right; const mostRight = canvasRect.right;
const mostBottom = containerRet.bottom; const mostBottom = containerRet.bottom;
@ -104,22 +105,18 @@ const rectAnno = (config: any, pdf: any, element: HTMLElement) => {
documentSelf.onselectstart = null; documentSelf.onselectstart = null;
documentSelf.onselect = null; documentSelf.onselect = null;
rectAnnoElement.classList.remove("toggled"); rectAnnoElement.classList.remove("toggled");
config.mainContainer.classList.remove("rect-to-annotation"); pdfConfig.mainContainer.classList.remove("rect-to-annotation");
rectResizeElement.setAttribute("data-render", "true"); rectResizeElement.setAttribute("data-render", "true");
const utilElement = element.querySelector(".pdf__util") as HTMLElement; const utilElement = element.querySelector(".pdf__util") as HTMLElement;
utilElement.classList.remove("pdf__util--hide", "fn__none"); utilElement.classList.remove("pdf__util--hide", "fn__none");
utilElement.setAttribute("data-mode", "rect");
const targetRect = rectResizeElement.getBoundingClientRect(); const targetRect = rectResizeElement.getBoundingClientRect();
setPosition(utilElement, targetRect.left, setPosition(utilElement, targetRect.left,
targetRect.top + targetRect.height + 4); targetRect.top + targetRect.height + 4);
rectElement = null; rectElement = null;
element.querySelector(".pdf__util").classList.add("pdf__util--hide"); utilElement.classList.add("pdf__util--hide");
}; };
}); });
};
export const initAnno = (file: string, element: HTMLElement, annoId: string, pdf: any, pdfConfig: any) => {
getConfig(pdf);
rectAnno(pdfConfig, pdf, element);
element.addEventListener("click", (event) => { element.addEventListener("click", (event) => {
let processed = false; let processed = false;
@ -129,20 +126,37 @@ export const initAnno = (file: string, element: HTMLElement, annoId: string, pdf
if (target.classList.contains("b3-color__square")) { if (target.classList.contains("b3-color__square")) {
const color = target.style.backgroundColor; const color = target.style.backgroundColor;
if (rectElement) { if (rectElement) {
updateColor(pdf, color); const config = getConfig(pdf);
const annoItem = config[rectElement.getAttribute("data-node-id")];
annoItem.color = color;
Array.from(rectElement.children).forEach((item: HTMLElement) => {
item.style.border = "2px solid " + color;
if (annoItem.type === "text") {
item.style.backgroundColor = color;
} else {
item.style.backgroundColor = "transparent";
}
});
fetchPost("/api/asset/setFileAnnotation", {
path: pdf.appConfig.file.replace(location.origin, "").substr(1) + ".sya",
data: JSON.stringify(config),
});
} else { } else {
let coords; let coords;
if (pdfConfig.mainContainer.lastElementChild.classList.contains( if (pdfConfig.mainContainer.lastElementChild.classList.contains("fn__none")) {
"fn__none")) {
coords = getHightlightCoordsByRange(pdf, color); coords = getHightlightCoordsByRange(pdf, color);
} else { } else {
coords = getHightlightCoordsByRect(pdf, color, coords = getHightlightCoordsByRect(pdf, color, pdfConfig.mainContainer.lastElementChild);
pdfConfig.mainContainer.lastElementChild);
pdfConfig.mainContainer.lastElementChild.classList.add("fn__none"); pdfConfig.mainContainer.lastElementChild.classList.add("fn__none");
} }
if (coords) { if (coords) {
coords.forEach(item => { coords.forEach((item, index) => {
showHighlight(item, pdf); const newElement = showHighlight(item, pdf);
if (index === 0) {
rectElement = newElement;
copyAnno(`${pdf.appConfig.file.replace(location.origin, "").substr(1)}/${rectElement.getAttribute("data-node-id")}`,
pdf.appConfig.file.replace(location.origin, "").substr(8).replace(/-\d{14}-\w{7}.pdf$/, ""))
}
}); });
} }
} }
@ -159,49 +173,46 @@ export const initAnno = (file: string, element: HTMLElement, annoId: string, pdf
processed = true; processed = true;
break; break;
} else if (type === "remove") { } else if (type === "remove") {
removeConfig(pdf, rectElement.getAttribute("data-node-id")); const urlPath = pdf.appConfig.file.replace(location.origin, "").substr(1);
const config = getConfig(pdf);
delete config[rectElement.getAttribute("data-node-id")];
rectElement.remove();
fetchPost("/api/asset/setFileAnnotation", {
path: urlPath + ".sya",
data: JSON.stringify(config),
});
hideToolbar(element); hideToolbar(element);
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
processed = true; processed = true;
break; break;
} else if (type === "copy") { } else if (type === "copy") {
const id = rectElement.getAttribute("data-node-id");
const text = rectElement.getAttribute("data-content");
if (rectElement.childElementCount === 1) {
/// #if !BROWSER
const rect = rectElement.firstElementChild.getBoundingClientRect();
getCurrentWindow().webContents.capturePage({
x: Math.floor(rect.x),
y: Math.floor(rect.y),
width: Math.floor(rect.width) + 2,
height: Math.floor(rect.height) + 2
}).then((image: NativeImage) => {
fetch(image.toDataURL()).then((response) => {
return response.blob();
}).then((blob) => {
const formData = new FormData();
const imageName = text + ".png";
formData.append("file[]", blob, imageName);
fetchPost(Constants.UPLOAD_ADDRESS, formData, (response) => {
writeText(`<<${pdf.appConfig.file.replace(location.origin, "").substr(1)}/${id} "${text}">>
![](${response.data.succMap[imageName]})`);
});
});
});
/// #else
writeText(`<<${pdf.appConfig.file.replace(location.origin, "").substr(1)}/${id} "${text}">>`);
/// #endif
} else {
writeText(`<<${pdf.appConfig.file.replace(location.origin, "").substr(1)}/${id} "${text}">>`);
}
hideToolbar(element); hideToolbar(element);
copyAnno(`${pdf.appConfig.file.replace(location.origin, "").substr(1)}/${rectElement.getAttribute("data-node-id")}`,
pdf.appConfig.file.replace(location.origin, "").substr(8).replace(/-\d{14}-\w{7}.pdf$/, ""))
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
processed = true; processed = true;
break; break;
} else if (type === "toggle") { } else if (type === "toggle") {
updateType(pdf); const config = getConfig(pdf);
const annoItem = config[rectElement.getAttribute("data-node-id")];
if (annoItem.type === "border") {
annoItem.type = "text";
} else {
annoItem.type = "border";
}
Array.from(rectElement.children).forEach((item: HTMLElement) => {
if (annoItem.type === "text") {
item.style.backgroundColor = item.style.border.replace("2px solid ", "");
} else {
item.style.backgroundColor = "";
}
});
fetchPost("/api/asset/setFileAnnotation", {
path: pdf.appConfig.file.replace(location.origin, "").substr(1) + ".sya",
data: JSON.stringify(config),
});
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
processed = true; processed = true;
@ -252,47 +263,10 @@ export const initAnno = (file: string, element: HTMLElement, annoId: string, pdf
return pdf; return pdf;
}; };
const updateType = (pdf: any) => {
const config = getConfig(pdf);
const annoItem = config[rectElement.getAttribute("data-node-id")];
if (annoItem.type === "border") {
annoItem.type = "text";
} else {
annoItem.type = "border";
}
Array.from(rectElement.children).forEach((item: HTMLElement) => {
if (annoItem.type === "text") {
item.style.backgroundColor = item.style.border.replace("2px solid ", "");
} else {
item.style.backgroundColor = "";
}
});
fetchPost("/api/asset/setFileAnnotation", {
path: pdf.appConfig.file.replace(location.origin, "").substr(1) + ".sya",
data: JSON.stringify(config),
});
};
const updateColor = (pdf: any, color: string) => {
const config = getConfig(pdf);
const annoItem = config[rectElement.getAttribute("data-node-id")];
annoItem.color = color;
Array.from(rectElement.children).forEach((item: HTMLElement) => {
item.style.border = "2px solid " + color;
if (annoItem.type === "text") {
item.style.backgroundColor = color;
} else {
item.style.backgroundColor = "transparent";
}
});
fetchPost("/api/asset/setFileAnnotation", {
path: pdf.appConfig.file.replace(location.origin, "").substr(1) + ".sya",
data: JSON.stringify(config),
});
};
const hideToolbar = (element: HTMLElement) => { const hideToolbar = (element: HTMLElement) => {
element.querySelector(".pdf__util").classList.add("fn__none"); const utilElement = element.querySelector(".pdf__util")
utilElement.classList.add("fn__none");
utilElement.removeAttribute("data-mode");
}; };
let rectElement: HTMLElement; let rectElement: HTMLElement;
@ -309,6 +283,7 @@ const showToolbar = (element: HTMLElement, range: Range, target?: HTMLElement) =
utilElement.classList.remove("fn__none"); utilElement.classList.remove("fn__none");
if (range) { if (range) {
utilElement.setAttribute("data-mode", "text");
utilElement.classList.add("pdf__util--hide"); utilElement.classList.add("pdf__util--hide");
const rects = range.getClientRects(); const rects = range.getClientRects();
const rect = rects[rects.length - 1]; const rect = rects[rects.length - 1];
@ -322,103 +297,6 @@ const showToolbar = (element: HTMLElement, range: Range, target?: HTMLElement) =
setPosition(utilElement, targetRect.left, targetRect.top + targetRect.height + 4); setPosition(utilElement, targetRect.left, targetRect.top + targetRect.height + 4);
}; };
const getHightlightCoordsByRect = (pdf: any, color: string, rectResizeElement: HTMLElement) => {
const rect = rectResizeElement.getBoundingClientRect();
const startPageElement = hasClosestByClassName(document.elementFromPoint(rect.left, rect.top - 1), "page");
if (!startPageElement) {
return;
}
const startIndex = parseInt(
startPageElement.getAttribute("data-page-number")) - 1;
const startPage = pdf.pdfViewer.getPageView(startIndex);
const startPageRect = startPage.canvas.getClientRects()[0];
const startViewport = startPage.viewport;
const startSelected = startViewport.convertToPdfPoint(
rect.left - startPageRect.x,
rect.top - startPageRect.y).concat(startViewport.convertToPdfPoint(rect.right - startPageRect.x,
rect.bottom - startPageRect.y));
const pages: {
index: number
positions: number[]
}[] = [
{
index: startPage.id - 1,
positions: [startSelected],
}];
const id = Lute.NewNodeID();
const content = pdf.appConfig.file.replace(location.origin, "").substr(8).replace(/-\d{14}-\w{7}.pdf$/, "") +
`-P${startPage.id}-${dayjs().format("YYYYMMDDHHmmss")}`;
const result = [
{
index: startPage.id - 1,
coords: [startSelected],
id,
color,
content,
type: "border",
}];
let endPageElement = document.elementFromPoint(rect.right, rect.bottom + 1);
endPageElement = hasClosestByClassName(endPageElement, "page") as HTMLElement;
if (endPageElement) {
const endIndex = parseInt(
endPageElement.getAttribute("data-page-number")) - 1;
if (endIndex !== startIndex) {
const endPage = pdf.pdfViewer.getPageView(endIndex);
const endPageRect = endPage.canvas.getClientRects()[0];
const endViewport = endPage.viewport;
const endSelected = endViewport.convertToPdfPoint(
rect.left - endPageRect.x,
rect.top - endPageRect.y).concat(endViewport.convertToPdfPoint(rect.right - endPageRect.x,
rect.bottom - endPageRect.y));
pages.push({
index: endPage.id - 1,
positions: [endSelected],
});
result.push({
index: endPage.id - 1,
coords: [endSelected],
id,
color,
content,
type: "border",
});
}
}
setConfig(pdf, id, {
pages,
content,
color,
type: "border",
});
return result;
};
const mergeRects = (range: Range) => {
const rects = range.getClientRects();
const mergedRects: { left: number, top: number, right: number, bottom: number }[] = [];
let lastTop: number = undefined;
Array.from(rects).forEach(item => {
if (item.height === 0 || item.width === 0) {
return;
}
if (typeof lastTop === "undefined" || Math.abs(lastTop - item.top) > 4) {
mergedRects.push({left: item.left, top: item.top, right: item.right, bottom: item.bottom});
lastTop = item.top;
} else {
mergedRects[mergedRects.length - 1].right = item.right;
}
});
return mergedRects;
};
const getHightlightCoordsByRange = (pdf: any, color: string) => { const getHightlightCoordsByRange = (pdf: any, color: string) => {
const range = window.getSelection().getRangeAt(0); const range = window.getSelection().getRangeAt(0);
const startPageElement = hasClosestByClassName(range.startContainer, "page"); const startPageElement = hasClosestByClassName(range.startContainer, "page");
@ -503,6 +381,7 @@ const getHightlightCoordsByRange = (pdf: any, color: string) => {
color, color,
content, content,
type: "text", type: "text",
mode: "text",
}); });
} }
if (endSelected.length > 0) { if (endSelected.length > 0) {
@ -510,8 +389,7 @@ const getHightlightCoordsByRange = (pdf: any, color: string) => {
index: endIndex, index: endIndex,
positions: endSelected, positions: endSelected,
}); });
results.push( results.push({index: endIndex, coords: endSelected, id, color, content, type: "text", mode: "text"});
{index: endIndex, coords: endSelected, id, color, content, type: "text"});
} }
if (pages.length === 0) { if (pages.length === 0) {
return; return;
@ -521,10 +399,109 @@ const getHightlightCoordsByRange = (pdf: any, color: string) => {
content, content,
color, color,
type: "text", type: "text",
mode: "text",
}); });
return results; return results;
}; };
const getHightlightCoordsByRect = (pdf: any, color: string, rectResizeElement: HTMLElement) => {
const rect = rectResizeElement.getBoundingClientRect();
const startPageElement = hasClosestByClassName(document.elementFromPoint(rect.left, rect.top - 1), "page");
if (!startPageElement) {
return;
}
const startIndex = parseInt(
startPageElement.getAttribute("data-page-number")) - 1;
const startPage = pdf.pdfViewer.getPageView(startIndex);
const startPageRect = startPage.canvas.getClientRects()[0];
const startViewport = startPage.viewport;
const startSelected = startViewport.convertToPdfPoint(
rect.left - startPageRect.x,
rect.top - startPageRect.y).concat(startViewport.convertToPdfPoint(rect.right - startPageRect.x,
rect.bottom - startPageRect.y));
const pages: {
index: number
positions: number[]
}[] = [
{
index: startPage.id - 1,
positions: [startSelected],
}];
const id = Lute.NewNodeID();
const content = pdf.appConfig.file.replace(location.origin, "").substr(8).replace(/-\d{14}-\w{7}.pdf$/, "") +
`-P${startPage.id}-${dayjs().format("YYYYMMDDHHmmss")}`;
const result = [{
index: startPage.id - 1,
coords: [startSelected],
id,
color,
content,
type: "border",
mode: "rect",
}];
let endPageElement = document.elementFromPoint(rect.right, rect.bottom + 1);
endPageElement = hasClosestByClassName(endPageElement, "page") as HTMLElement;
if (endPageElement) {
const endIndex = parseInt(
endPageElement.getAttribute("data-page-number")) - 1;
if (endIndex !== startIndex) {
const endPage = pdf.pdfViewer.getPageView(endIndex);
const endPageRect = endPage.canvas.getClientRects()[0];
const endViewport = endPage.viewport;
const endSelected = endViewport.convertToPdfPoint(
rect.left - endPageRect.x,
rect.top - endPageRect.y).concat(endViewport.convertToPdfPoint(rect.right - endPageRect.x,
rect.bottom - endPageRect.y));
pages.push({
index: endPage.id - 1,
positions: [endSelected],
});
result.push({
index: endPage.id - 1,
coords: [endSelected],
id,
color,
content,
type: "border",
mode: "rect",
});
}
}
setConfig(pdf, id, {
pages,
content,
color,
type: "border",
mode: "rect",
});
return result;
};
const mergeRects = (range: Range) => {
const rects = range.getClientRects();
const mergedRects: { left: number, top: number, right: number, bottom: number }[] = [];
let lastTop: number = undefined;
Array.from(rects).forEach(item => {
if (item.height === 0 || item.width === 0) {
return;
}
if (typeof lastTop === "undefined" || Math.abs(lastTop - item.top) > 4) {
mergedRects.push({left: item.left, top: item.top, right: item.right, bottom: item.bottom});
lastTop = item.top;
} else {
mergedRects[mergedRects.length - 1].right = item.right;
}
});
return mergedRects;
};
export const getPdfInstance = (element: HTMLElement) => { export const getPdfInstance = (element: HTMLElement) => {
let pdfInstance; let pdfInstance;
@ -537,7 +514,6 @@ export const getPdfInstance = (element: HTMLElement) => {
return pdfInstance; return pdfInstance;
}; };
export const getHighlight = (element: HTMLElement) => { export const getHighlight = (element: HTMLElement) => {
const pdfInstance: any = getPdfInstance(element); const pdfInstance: any = getPdfInstance(element);
if (!pdfInstance) { if (!pdfInstance) {
@ -556,14 +532,14 @@ export const getHighlight = (element: HTMLElement) => {
if (page) { if (page) {
showHighlight({ showHighlight({
index: pageIndex, index: pageIndex,
coords: page.positions, coords: page.positions,
id: key, id: key,
color: item.color, color: item.color,
content: item.content, content: item.content,
type: item.type, type: item.type,
}, mode: item.mode || ""
pdfInstance, pdfInstance.annoId === key); }, pdfInstance, pdfInstance.annoId === key);
} }
}); });
}; };
@ -581,7 +557,7 @@ const showHighlight = (selected: IPdfAnno, pdf: any, hl?: boolean) => {
} }
textLayerElement = textLayerElement.lastElementChild; textLayerElement = textLayerElement.lastElementChild;
let html = `<div class="pdf__rect popover__block" data-node-id="${selected.id}">`; let html = `<div class="pdf__rect popover__block" data-node-id="${selected.id}" data-mode="${selected.mode}">`;
selected.coords.forEach((rect) => { selected.coords.forEach((rect) => {
const bounds = viewport.convertToViewportRectangle(rect); const bounds = viewport.convertToViewportRectangle(rect);
const width = Math.abs(bounds[0] - bounds[2]); const width = Math.abs(bounds[0] - bounds[2]);
@ -599,11 +575,11 @@ const showHighlight = (selected: IPdfAnno, pdf: any, hl?: boolean) => {
height: ${Math.abs(bounds[1] - bounds[3])}px"></div>`; height: ${Math.abs(bounds[1] - bounds[3])}px"></div>`;
}); });
textLayerElement.insertAdjacentHTML("beforeend", html + "</div>"); textLayerElement.insertAdjacentHTML("beforeend", html + "</div>");
textLayerElement.lastElementChild.setAttribute("data-content", textLayerElement.lastElementChild.setAttribute("data-content", selected.content);
selected.content);
if (hl) { if (hl) {
hlPDFRect(textLayerElement, selected.id); hlPDFRect(textLayerElement, selected.id);
} }
return textLayerElement.lastElementChild;
}; };
export const hlPDFRect = (element: HTMLElement, id: string) => { export const hlPDFRect = (element: HTMLElement, id: string) => {
@ -632,23 +608,46 @@ export const hlPDFRect = (element: HTMLElement, id: string) => {
} }
}; };
const copyAnno = (idPath: string, fileName: string) => {
const mode = rectElement.getAttribute("data-mode")
const content = rectElement.getAttribute("data-content")
setTimeout(() => {
/// #if !BROWSER
if (mode === "rect" ||
(mode === "" && rectElement.childElementCount === 1 && content.startsWith(fileName)) // 兼容历史,以前没有 mode
) {
const rect = rectElement.firstElementChild.getBoundingClientRect();
getCurrentWindow().webContents.capturePage({
x: Math.floor(rect.x),
y: Math.floor(rect.y),
width: Math.floor(rect.width),
height: Math.floor(rect.height)
}).then((image: NativeImage) => {
fetch(image.toDataURL()).then((response) => {
return response.blob();
}).then((blob) => {
const formData = new FormData();
const imageName = content + ".png";
formData.append("file[]", blob, imageName);
fetchPost(Constants.UPLOAD_ADDRESS, formData, (response) => {
writeText(`<<${idPath} "${content}">>
![](${response.data.succMap[imageName]})`);
});
});
});
} else {
writeText(`<<${idPath} "${content}">>`);
}
/// #else
writeText(`<<${idPath} "${content}">>`);
/// #endif
}, Constants.TIMEOUT_DBLCLICK);
}
const setConfig = (pdf: any, id: string, data: IPdfAnno) => { const setConfig = (pdf: any, id: string, data: IPdfAnno) => {
const urlPath = pdf.appConfig.file.replace(location.origin, "").substr(1); const urlPath = pdf.appConfig.file.replace(location.origin, "").substr(1);
const config = getConfig(pdf); const config = getConfig(pdf);
config[id] = data; config[id] = data;
fetchPost("/api/asset/setFileAnnotation", {
path: urlPath + ".sya",
data: JSON.stringify(config),
}, () => {
writeText(`<<${urlPath}/${id} "${data.content}">>`);
});
};
const removeConfig = (pdf: any, id: string) => {
const urlPath = pdf.appConfig.file.replace(location.origin, "").substr(1);
const config = getConfig(pdf);
delete config[id];
rectElement.remove();
fetchPost("/api/asset/setFileAnnotation", { fetchPost("/api/asset/setFileAnnotation", {
path: urlPath + ".sya", path: urlPath + ".sya",
data: JSON.stringify(config), data: JSON.stringify(config),

View File

@ -113,8 +113,9 @@ interface IPdfAnno {
}[] }[]
index?: number, index?: number,
color: string, color: string,
type: string, type: string, // border, text
content: string, content: string, // rect, text
mode: string,
id?: string, id?: string,
coords?: number[] coords?: number[]
} }