Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
/package-lock.json
.DS_Store
/.idea
dist/
.history/
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"lib": "vue-cli-service build --target lib --name ofd --dest lib ./src/utils/ofd/ofd.js"
"lib": "vue-cli-service build --target lib --name ofd --dest lib ./src/utils/ofd/ofd.js",
"build:umd": "vue-cli-service build --target lib --name OFD --dest dist ./src/umd-entry.js"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.30",
Expand Down
4 changes: 2 additions & 2 deletions src/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ body { margin: 0; }
src: url("../assets/simhei.ttf");
}

.gray {
/* .gray {
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
filter: grayscale(100%);
filter: gray;
}
} */
108 changes: 108 additions & 0 deletions src/umd-entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
parseOfdDocument,
renderOfd,
renderOfdByScale,
setPageScale,
getPageScale
} from "./utils/ofd/ofd";

/**
* OFD.js UMD 入口
* 提供简单的 API 用于在任意网页中预览 OFD 文件
*
* 用法:
* // 通过 CDN 引入后,全局变量为 OFD
* OFD.preview('https://example.com/file.ofd', document.getElementById('ofd-container'));
*
* // 或使用选择器字符串
* OFD.preview('https://example.com/file.ofd', '#ofd-container');
*/
/**
* 预览 OFD 文件并渲染到指定 DOM 节点
* @param {string|File|ArrayBuffer} source - OFD 文件 URL、File 对象或 ArrayBuffer
* @param {HTMLElement|string} container - 渲染目标 DOM 节点或 CSS 选择器字符串
* @param {Object} options - 配置项
* @param {number} [options.width] - 容器宽度,默认使用容器实际宽度
* @param {Function} [options.onSuccess] - 解析成功回调,参数:(ofdData, divArray)
* @param {Function} [options.onFail] - 解析失败回调,参数:(error)
*/
const preview=(source, container, options = {}) => {
const { width, onSuccess, onFail } = options;

let targetContainer;
if (typeof container === "string") {
targetContainer = document.querySelector(container);
} else if (container instanceof HTMLElement) {
targetContainer = container;
}

if (!targetContainer) {
console.error("OFD Preview: 目标容器未找到,请检查传入的容器参数");
return;
}

const screenWidth =
width || targetContainer.clientWidth || document.body.clientWidth;

parseOfdDocument({
ofd: source,
success(ofdArray) {
const ofd = ofdArray[0];
const divs = renderOfd(screenWidth, ofd);

targetContainer.innerHTML = "";
for (const div of divs) {
targetContainer.appendChild(div);
}

if (onSuccess) {
onSuccess(ofd, divs);
}
},
fail(err) {
console.error("OFD Preview 加载失败:", err);
if (onFail) {
onFail(err);
}
},
});
}
/**
* 设置页面缩放比例
* @param {number} scale - 缩放比例
*/
const setScale=(scale)=> {
setPageScale(scale);
}

/**
* 获取当前页面缩放比例
* @returns {number} 当前缩放比例
*/
const getScale=() =>{
return getPageScale();
}
const OFD = {

preview,
setScale,
getScale,
// 暴露底层 API,便于高级用户自定义渲染逻辑
parseOfdDocument,
renderOfd,
renderOfdByScale,
setPageScale,
getPageScale,
};

export default OFD;
export {
preview,
setScale,
getScale,
parseOfdDocument,
renderOfd,
renderOfdByScale,
setPageScale,
getPageScale,
};
27 changes: 25 additions & 2 deletions src/utils/ofd/ofd_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,23 @@ const getMultiMediaRes = async function (zip, res, doc) {
if (file.indexOf(doc) === -1) {
file = `${doc}/${file}`
}
if (!file) {
console.warn(`Empty MediaFile for item ${item['@_ID']}, skipping.`);
continue;
}
if (item['@_Type'].toLowerCase() === 'image') {
const format = item['@_Format'];
const ext = getExtensionByPath(file);
if ((format && (format.toLowerCase() === 'gbig2' || format.toLowerCase() === 'jb2')) || ext && (ext.toLowerCase() === 'jb2' || ext.toLowerCase() === 'gbig2')) {
const jbig2 = await parseJbig2ImageFromZip(zip, file);
multiMediaResObj[item['@_ID']] = jbig2;
if (jbig2) {
multiMediaResObj[item['@_ID']] = jbig2;
}
} else {
const img = await parseOtherImageFromZip(zip, file);
multiMediaResObj[item['@_ID']] = {img, 'format': 'png'};
if (img) {
multiMediaResObj[item['@_ID']] = {img, 'format': 'png'};
}
}
} else {
multiMediaResObj[item['@_ID']] = file;
Expand Down Expand Up @@ -364,6 +372,10 @@ const getSignature = async function (zip, signatures, doc) {
}

const getFileData = async function (zip, name){
if (!zip.files[name]) {
console.warn(`File not found in zip: ${name}`);
return null;
}
return zip.files[name].async('uint8array');
}

Expand Down Expand Up @@ -415,6 +427,9 @@ const getSealDocumentObj = function (stampAnnot) {

const getJsonFromXmlContent = async function (zip, xmlName) {
return new Promise((resolve, reject) => {
if (!zip.files[xmlName]) {
return reject(new Error(`File not found in zip: ${xmlName}`));
}
zip.files[xmlName].async('string').then(function (content) {
let ops = {
attributeNamePrefix: "@_",
Expand All @@ -433,6 +448,10 @@ const getJsonFromXmlContent = async function (zip, xmlName) {

const parseJbig2ImageFromZip = async function (zip, name) {
return new Promise((resolve, reject) => {
if (!zip.files[name]) {
console.warn(`Image not found in zip: ${name}`);
return resolve(null);
}
zip.files[name].async('uint8array').then(function (bytes) {
let jbig2 = new Jbig2Image();
const img = jbig2.parse(bytes);
Expand All @@ -445,6 +464,10 @@ const parseJbig2ImageFromZip = async function (zip, name) {

const parseOtherImageFromZip = async function (zip, name) {
return new Promise((resolve, reject) => {
if (!zip.files[name]) {
console.warn(`Image not found in zip: ${name}`);
return resolve(null);
}
zip.files[name].async('base64').then(function (bytes) {
const img = 'data:image/png;base64,' + bytes;
resolve(img);
Expand Down
15 changes: 10 additions & 5 deletions src/utils/ofd/ofd_render.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,18 @@ export const renderImageObject = function (pageWidth, pageHeight, multiMediaResO
let boundary = parseStBox(imageObject['@_Boundary']);
boundary = converterBox(boundary);
const resId = imageObject['@_ResourceID'];
if (multiMediaResObj[resId].format === 'gbig2') {
const img = multiMediaResObj[resId].img;
const width = multiMediaResObj[resId].width;
const height = multiMediaResObj[resId].height;
const mediaRes = multiMediaResObj[resId];
if (!mediaRes) {
console.warn(`Image resource not found for ID: ${resId}`);
return document.createElement('div');
}
if (mediaRes.format === 'gbig2') {
const img = mediaRes.img;
const width = mediaRes.width;
const height = mediaRes.height;
return renderImageOnCanvas(img, width, height, boundary, imageObject['pfIndex']);
} else {
return renderImageOnDiv(pageWidth, pageHeight, multiMediaResObj[resId].img, boundary, false, false, null, null, imageObject['pfIndex']);
return renderImageOnDiv(pageWidth, pageHeight, mediaRes.img, boundary, false, false, null, null, imageObject['pfIndex']);
}
}

Expand Down
7 changes: 6 additions & 1 deletion vue.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
module.exports = {
publicPath: '/ofd',
lintOnSave: false
lintOnSave: false,
configureWebpack: {
externals: {
// UMD 模式下不需要排除依赖,打包成完整单文件
}
}
}