js
复制代码
/**
* @fileoverview 这个文件提供了保存和恢复PDF注释的功能。
*/
class AnnotationsManager {
constructor() {
// 初始化事件监听器
this._initListeners();
}
/**
* 初始化事件监听器
* @private
*/
_initListeners() {
// 主工具栏按钮
const saveButton = document.getElementById("saveAnnotationsButton");
const restoreButton = document.getElementById("restoreAnnotationsButton");
// 二级工具栏按钮
const secondarySaveButton = document.getElementById("secondarySaveAnnotations");
const secondaryRestoreButton = document.getElementById("secondaryRestoreAnnotations");
// 添加事件监听器
if (saveButton) {
saveButton.addEventListener("click", this.saveAnnotations.bind(this));
}
if (restoreButton) {
restoreButton.addEventListener("click", this.restoreAnnotations.bind(this));
}
if (secondarySaveButton) {
secondarySaveButton.addEventListener("click", this.saveAnnotations.bind(this));
}
if (secondaryRestoreButton) {
secondaryRestoreButton.addEventListener("click", this.restoreAnnotations.bind(this));
}
}
/**
* 获取当前PDF文件的唯一标识符
* @returns {string} PDF文件的唯一标识符
* @private
*/
_getPdfId() {
const pdfDocument = PDFViewerApplication.pdfDocument;
if (!pdfDocument) {
return null;
}
// 使用文件名和指纹作为唯一标识符
const url = PDFViewerApplication.url;
const fileName = url.split("/").pop();
const fingerprint = pdfDocument.fingerprints?.[0] || "";
return `${fileName}-${fingerprint}`;
}
/**
* 保存注释到localStorage
*/
saveAnnotations() {
const pdfDocument = PDFViewerApplication.pdfDocument;
if (!pdfDocument) {
console.error("没有加载PDF文档");
alert("请先加载PDF文档");
return;
}
const pdfId = this._getPdfId();
if (!pdfId) {
console.error("无法获取PDF文档ID");
alert("无法获取PDF文档ID");
return;
}
try {
// 获取注释数据
const { annotationStorage } = pdfDocument;
// 检查annotationStorage对象
console.log("完整的annotationStorage对象:", annotationStorage);
console.log("annotationStorage类型:", Object.prototype.toString.call(annotationStorage));
console.log("annotationStorage方法:", Object.getOwnPropertyNames(Object.getPrototypeOf(annotationStorage)));
console.log("annotationStorage大小:", annotationStorage.size);
// 遍历所有存储的注释
console.log("遍历annotationStorage中的所有条目:");
const allAnnotations = {};
for (const [key, value] of annotationStorage) {
console.log("键:", key);
console.log("值:", value);
console.log("值类型:", Object.prototype.toString.call(value));
// 手动序列化每个注释
if (value && typeof value.serialize === 'function') {
const serialized = value.serialize(false);
// 特殊处理手绘注释的类型化数组
if (serialized && serialized.paths) {
if (serialized.paths.lines) {
serialized.paths.lines = serialized.paths.lines.map(line =>
line instanceof Float32Array ? Array.from(line) : line
);
}
if (serialized.paths.points) {
serialized.paths.points = serialized.paths.points.map(points =>
points instanceof Float32Array ? Array.from(points) : points
);
}
}
allAnnotations[key] = serialized;
} else {
allAnnotations[key] = value;
}
}
// 如果没有注释,则不保存
if (Object.keys(allAnnotations).length === 0) {
console.log("没有注释需要保存");
alert("当前文档没有批注需要保存,请先添加批注");
return;
}
// 将注释数据转换为可存储的格式
const storageKey = `pdf_annotations_${pdfId}`;
const storageData = {
timestamp: Date.now(),
fileName: PDFViewerApplication.url.split("/").pop(),
annotations: allAnnotations
};
// 打印存储信息
console.log("存储键值:", storageKey);
console.log("存储数据:", storageData);
console.log("存储数据JSON字符串长度:", JSON.stringify(storageData).length);
// 保存到localStorage
localStorage.setItem(storageKey, JSON.stringify(storageData));
console.log("注释已保存到localStorage", storageKey);
alert("批注已成功保存");
} catch (error) {
console.error("保存注释时出错:", error);
alert(`保存批注时出错: ${error.message}`);
}
}
/**
* 从localStorage恢复注释
*/
async restoreAnnotations() {
try {
// 检查PDF文档是否已加载
if (!PDFViewerApplication.pdfDocument) {
console.error("PDF文档未加载,无法恢复注释");
alert("请先加载PDF文档");
return;
}
// 获取PDF文档对象
const pdfDocument = PDFViewerApplication.pdfDocument;
// 获取PDF ID
const pdfId = this._getPdfId();
if (!pdfId) {
console.error("无法获取PDF ID,无法恢复注释");
alert("无法获取文档ID,恢复批注失败");
return;
}
// 从localStorage获取保存的注释
const storageKey = `pdf_annotations_${pdfId}`;
const storageData = localStorage.getItem(storageKey);
if (!storageData) {
console.log("未找到保存的注释");
alert("没有找到该文档的保存批注");
return;
}
const parsedData = JSON.parse(storageData);
const { annotations } = parsedData;
if (!annotations || Object.keys(annotations).length === 0) {
console.log("恢复的注释数据为空");
alert("恢复的批注数据为空");
return;
}
// 获取当前页面的注释层
const pdfViewer = PDFViewerApplication.pdfViewer;
const annotationStorage = pdfDocument.annotationStorage;
// 清除现有注释
const existingKeys = [];
for (const [key] of annotationStorage) {
existingKeys.push(key);
}
// 删除所有现有注释
for (const key of existingKeys) {
annotationStorage.remove(key);
}
// 恢复保存的注释
console.log("开始恢复注释...");
let restoredCount = 0;
// 打印所有要恢复的注释键
console.log("要恢复的注释键:", Object.keys(annotations));
// 删除这行:await this._ensurePagesRendered(pdfViewer);
// 获取所有页面的注释编辑器层
const annotationEditorLayers = pdfViewer._pages.map(page => page.annotationEditorLayer);
// 使用deserialize方法恢复注释
for (const [key, value] of Object.entries(annotations)) {
try {
console.log(`尝试恢复注释: ${key}`, value);
// 特殊处理手绘注释的类型化数组恢复
if (value.paths) {
if (value.paths.lines && Array.isArray(value.paths.lines)) {
value.paths.lines = value.paths.lines.map(line =>
Array.isArray(line) ? new Float32Array(line) : line
);
}
if (value.paths.points && Array.isArray(value.paths.points)) {
value.paths.points = value.paths.points.map(points =>
Array.isArray(points) ? new Float32Array(points) : points
);
}
}
// 特殊处理高亮注释 - 修正注释类型判断
if (value.annotationType === 9) { // AnnotationEditorType.HIGHLIGHT = 9
console.log(`处理高亮注释: ${key}`, value);
// 修正 outlines 格式
if (value.outlines && typeof value.outlines === 'object' && !Array.isArray(value.outlines)) {
value.outlines = Object.values(value.outlines);
}
// 获取页面索引
const pageIndex = value.pageIndex || 0;
// 确保页面尺寸和偏移数据存在
if (!value.pageDimensions || !value.pageTranslation) {
console.log(`注释 ${key} 缺少页面数据,尝试从页面获取`);
// 从 pdfViewer 获取页面数据
const page = pdfViewer._pages[pageIndex];
if (page && page.pdfPage) {
const viewport = page.pdfPage.getViewport({ scale: 1 });
value.pageDimensions = [viewport.width, viewport.height];
value.pageTranslation = [0, 0]; // 通常为 [0, 0]
console.log(`从页面获取的数据:`, {
pageDimensions: value.pageDimensions,
pageTranslation: value.pageTranslation
});
} else {
console.error(`无法获取页面 ${pageIndex} 的数据`);
continue;
}
}
// 处理 quadPoints
let quadPoints = value.quadPoints;
if (!quadPoints || !Array.isArray(quadPoints) || quadPoints.length === 0) {
console.log(`注释 ${key} 缺少quadPoints,尝试从rect重建`);
if (value.rect && Array.isArray(value.rect) && value.rect.length === 4) {
const [x, y, width, height] = value.rect;
// 严格验证 rect 数据
if (typeof x === 'number' && typeof y === 'number' &&
typeof width === 'number' && typeof height === 'number' &&
isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height) &&
width > 0 && height > 0) {
quadPoints = [[
x, y + height,
x + width, y + height,
x + width, y,
x, y
]];
console.log(`从rect重建的quadPoints:`, quadPoints);
} else {
console.error(`注释 ${key} 的rect数据无效:`, value.rect);
continue;
}
} else {
console.error(`注释 ${key} 缺少有效的rect数据`);
continue;
}
}
// 将 quadPoints 对象转换为二维数组并严格验证
if (quadPoints && typeof quadPoints === 'object' && !Array.isArray(quadPoints)) {
quadPoints = Object.values(quadPoints);
}
// 验证 quadPoints 数据
if (!Array.isArray(quadPoints) || quadPoints.length === 0) {
console.error(`注释 ${key} quadPoints格式无效`);
continue;
}
// 严格验证每个 quadPoints 片段
const validQuadPoints = [];
for (const quad of quadPoints) {
if (Array.isArray(quad) && quad.length === 8) {
// 检查所有坐标是否为有效数字
const allValid = quad.every(coord =>
typeof coord === 'number' &&
isFinite(coord) &&
!isNaN(coord)
);
if (allValid) {
// 额外检查坐标范围是否合理(避免极值)
const hasReasonableValues = quad.every(coord =>
Math.abs(coord) < 1e6 // 避免过大的坐标值
);
if (hasReasonableValues) {
validQuadPoints.push(quad);
} else {
console.warn(`注释 ${key} 的quadPoints包含异常大的坐标值:`, quad);
}
} else {
console.warn(`注释 ${key} 的quadPoints包含无效坐标:`, quad);
}
} else {
console.warn(`注释 ${key} 的quadPoints片段格式错误:`, quad);
}
}
if (validQuadPoints.length === 0) {
console.error(`注释 ${key} 没有有效的quadPoints数据`);
continue;
}
console.log(`高亮注释 ${key} 数据验证通过,quadPoints数量: ${validQuadPoints.length}`);
// 更新 value 对象
value.quadPoints = validQuadPoints;
// 清理可能存在的无效 boxes 字段
if (value.boxes) {
delete value.boxes;
}
// 添加详细的页面尺寸和偏移调试信息
console.log(`🔍 调试页面数据 ${key}:`, {
pageDimensions: value.pageDimensions,
pageTranslation: value.pageTranslation,
pageIndex: value.pageIndex,
rect: value.rect
});
// 确保页面尺寸数据有效
if (value.pageDimensions) {
const [pageWidth, pageHeight] = value.pageDimensions;
if (!isFinite(pageWidth) || !isFinite(pageHeight) || pageWidth <= 0 || pageHeight <= 0) {
console.error(`注释 ${key} 的页面尺寸无效:`, value.pageDimensions);
continue;
}
}
// 确保页面偏移数据有效
if (value.pageTranslation) {
const [pageX, pageY] = value.pageTranslation;
if (!isFinite(pageX) || !isFinite(pageY)) {
console.error(`注释 ${key} 的页面偏移无效:`, value.pageTranslation);
continue;
}
}
}
// 获取页面索引
const pageIndex = value.pageIndex || 0;
// 获取对应页面的注释编辑器层构建器
const editorLayerBuilder = annotationEditorLayers[pageIndex];
if (editorLayerBuilder) {
// 确保注释编辑器层已经渲染(简化版本,不等待)
debugger
if (!editorLayerBuilder.annotationEditorLayer) {
const page = pdfViewer._pages[pageIndex];
if (page && page.pdfPage) {
// 尝试触发渲染,但不等待
try {
page._renderAnnotationEditorLayer();
} catch (e) {
console.warn(`无法渲染页面 ${pageIndex} 的注释编辑器层:`, e);
continue;
}
}
}
const editorLayer = editorLayerBuilder.annotationEditorLayer;
// 使用deserialize方法创建编辑器实例
console.log("PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP")
console.log(value)
console.log("打印编辑层")
console.log(editorLayer)
debugger
const editor = await editorLayer.deserialize(value);
if (editor) {
console.log(`✅ 成功创建编辑器: ${key}`, {
editorType: editor.constructor.name,
hasDiv: !!editor.div,
divDisplay: editor.div?.style.display,
divVisibility: editor.div?.style.visibility,
editorId: editor.id
});
// 检查render前的状态
console.log(`📋 render前状态: ${key}`, {
hasDiv: !!editor.div,
hasRenderMethod: typeof editor.render === 'function'
});
// 确保编辑器已渲染
if (!editor.div) {
console.log(`🔄 调用render方法: ${key}`);
editor.render();
console.log(`📋 render后状态: ${key}`, {
hasDiv: !!editor.div,
divInnerHTML: editor.div?.innerHTML?.substring(0, 100),
divStyle: editor.div?.style.cssText
});
}
// 特殊处理文字注释的位置修正
if (value.annotationType === 3) { // FREETEXT
const [pageWidth, pageHeight] = editor.pageDimensions;
const rect = value.rect;
editor.x = rect[0] / pageWidth;
editor.y = (pageHeight - rect[3]) / pageHeight;
editor.width = (rect[2] - rect[0]) / pageWidth;
editor.height = (rect[3] - rect[1]) / pageHeight;
editor.fixAndSetPosition();
if (value.value && editor.editorDiv) {
editor.editorDiv.textContent = value.value;
}
}
// 确保编辑器可见
if (editor.div) {
editor.div.style.display = '';
}
// 将编辑器添加到层中
editorLayer.add(editor);
// 将编辑器实例添加到存储中
annotationStorage.setValue(key, editor);
restoredCount++;
console.log(`成功恢复注释: ${key}`);
} else {
console.warn(`无法反序列化注释: ${key}`);
}
} else {
console.warn(`找不到页面 ${pageIndex} 的注释编辑器层`);
}
} catch (err) {
console.error(`恢复注释 ${key} 时出错:`, err);
}
}
// 刷新视图以显示恢复的注释
pdfViewer.update();
// 触发重新渲染
// 详细检查每个页面的注释层状态
for (let i = 0; i < pdfViewer._pages.length; i++) {
const page = pdfViewer._pages[i];
if (page.annotationEditorLayer?.annotationEditorLayer) {
const layer = page.annotationEditorLayer.annotationEditorLayer;
layer.div.hidden = false;
// 检查每个编辑器
if (layer._editors) {
let editorIndex = 0;
for (const editor of layer._editors) {
console.log(`📝 页面 ${i} 编辑器 ${editorIndex}:`, {
type: editor.constructor.name,
hasDiv: !!editor.div,
divVisible: editor.div && editor.div.offsetWidth > 0 && editor.div.offsetHeight > 0,
divRect: editor.div?.getBoundingClientRect()
});
editorIndex++;
}
}
}
}
console.log(`注释已从localStorage恢复,共恢复 ${restoredCount} 个注释`);
alert(`批注已成功恢复,共恢复 ${restoredCount} 个批注`);
} catch (error) {
console.error("恢复注释时出错:", error);
alert(`恢复批注时出错: ${error.message}`);
}
}
/**
* 确保所有页面都已渲染
* @private
*/
async _ensurePagesRendered(pdfViewer) {
const promises = [];
for (let i = 0; i < pdfViewer._pages.length; i++) {
const page = pdfViewer._pages[i];
if (page && page.pdfPage && !page.annotationEditorLayer) {
// 如果页面还没有注释编辑器层,等待页面渲染完成
promises.push(new Promise(resolve => {
const checkRendered = () => {
if (page.annotationEditorLayer) {
resolve();
} else {
setTimeout(checkRendered, 100);
}
};
checkRendered();
}));
}
}
if (promises.length > 0) {
await Promise.all(promises);
}
}
}
// 创建并导出AnnotationsManager实例
const annotationsManager = new AnnotationsManager();
export { annotationsManager };