docs: 添加涉密文件自检工具实施计划
This commit is contained in:
312
UmiOCR-data/qt_res/qml/TabPages/BatchDOC/BatchDOC.qml
Normal file
312
UmiOCR-data/qt_res/qml/TabPages/BatchDOC/BatchDOC.qml
Normal file
@@ -0,0 +1,312 @@
|
||||
// ==================================================
|
||||
// =============== 功能页:批量文档处理 ===============
|
||||
// ==================================================
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import ".."
|
||||
import "../../Widgets"
|
||||
import "../../Widgets/ResultLayout"
|
||||
import "../../Widgets/IgnoreArea"
|
||||
|
||||
/* 文档参数:
|
||||
path 路径
|
||||
pages 页数显示
|
||||
state 状态显示
|
||||
page_count 总页数
|
||||
range_start 范围开始
|
||||
range_end 范围结束
|
||||
is_encrypted 需要密码
|
||||
is_authenticate 密码正确
|
||||
password 密码
|
||||
*/
|
||||
|
||||
TabPage {
|
||||
id: tabPage
|
||||
|
||||
// ========================= 【逻辑】 =========================
|
||||
|
||||
property string msnID: "" // 当前任务ID
|
||||
|
||||
// 异步加载一批文档路径
|
||||
function addDocs(paths) {
|
||||
if(paths.length <= 0) return
|
||||
const isRecurrence = configsComp.getValue("mission.recurrence")
|
||||
qmlapp.asynFilesLoader.run(paths,"doc",isRecurrence,onAddDocs)
|
||||
}
|
||||
// 完毕后,将合法表格加入表格
|
||||
function onAddDocs(docs) {
|
||||
if(docs.length <= 0) return
|
||||
let encryptedCount = 0
|
||||
for(let i in docs) {
|
||||
const info = docs[i]
|
||||
filesTableView.add({
|
||||
// 显示:路径,状态,页范围
|
||||
path: info.path,
|
||||
pages: `${info.page_count}`, // 如果范围为整本,只显示总页数。否则显示 起始-结束
|
||||
state: info.is_encrypted ? qsTr("加密") : "" ,
|
||||
// 数据
|
||||
page_count: info.page_count,
|
||||
range_start: 1,
|
||||
range_end: info.page_count,
|
||||
is_encrypted: info.is_encrypted, // 有密码
|
||||
is_authenticate: !info.is_encrypted, // 已解密(密码正确)
|
||||
password: "",
|
||||
})
|
||||
if(info.is_encrypted) encryptedCount++
|
||||
}
|
||||
if(encryptedCount > 0) {
|
||||
qmlapp.popup.simple(qsTr("%1个加密文档").arg(encryptedCount),
|
||||
qsTr("请点击文件名填写密码"))
|
||||
}
|
||||
}
|
||||
|
||||
// 运行文档任务
|
||||
function docStart() {
|
||||
const fileCount = filesTableView.rowCount
|
||||
if(fileCount <= 0) {
|
||||
ctrlPanel.stopFinished()
|
||||
return
|
||||
}
|
||||
// 获取信息
|
||||
const docs = filesTableView.getColumnsValues([
|
||||
"path","range_start", "range_end", "page_count", "is_encrypted", "is_authenticate", "password"])
|
||||
// 第1次遍历:检查密码填写
|
||||
for(let i = 0; i < fileCount; i++) {
|
||||
const d = docs[i]
|
||||
if(d.is_encrypted && !d.is_authenticate) {
|
||||
qmlapp.popup.message(qsTr("文档已加密"), qsTr("【%1】\n请点击文档名,设置密码").arg(d.path), "warning")
|
||||
ctrlPanel.stopFinished()
|
||||
return
|
||||
}
|
||||
}
|
||||
// 第2次遍历:刷新信息
|
||||
let allPages = 0 // 页总数
|
||||
for(let i = 0; i < fileCount; i++) {
|
||||
const d = docs[i]
|
||||
allPages += d.range_end - d.range_start + 1
|
||||
filesTableView.setProperty(d.path, "state", qsTr("排队"))
|
||||
}
|
||||
// 若tabPanel面板的下标没有变化过,则切换到记录页
|
||||
if(tabPanel.indexChangeNum < 2)
|
||||
tabPanel.currentIndex = 1
|
||||
// 任务进度 开始计时
|
||||
ctrlPanel.runFinished(allPages)
|
||||
// 提交任务
|
||||
const argd = configsComp.getValueDict()
|
||||
tabPage.callPy("msnDocs", docs, argd)
|
||||
}
|
||||
|
||||
// 停止文档任务
|
||||
function docStop() {
|
||||
tabPage.callPy("msnStop")
|
||||
// 刷新表格,清空未执行的任务的状态
|
||||
let msnLength = filesTableView.rowCount
|
||||
for(let i = 0; i < msnLength; i++) {
|
||||
const row = filesTableView.get(i)
|
||||
const s = row.state
|
||||
if(s.length > 0 && s[0] !== "√" && s[0] !== "×") {
|
||||
filesTableView.setProperty(i, "state", "")
|
||||
}
|
||||
}
|
||||
ctrlPanel.stopFinished()
|
||||
}
|
||||
|
||||
// 文件表格中单击文档
|
||||
function onClickDoc(index) {
|
||||
if(ctrlPanel.state_ !== "stop") return
|
||||
const info = filesTableView.get(index)
|
||||
if(Object.keys(info).length > 0)
|
||||
previewDoc.show(info)
|
||||
}
|
||||
|
||||
// 关闭页面
|
||||
function closePage() {
|
||||
if(ctrlPanel.state_ !== "stop") {
|
||||
const argd = { yesText: qsTr("依然关闭") }
|
||||
const callback = (flag)=>{
|
||||
if(flag) {
|
||||
docStop()
|
||||
delPage()
|
||||
}
|
||||
}
|
||||
qmlapp.popup.dialog("", qsTr("任务正在进行中。\n要结束任务并关闭页面吗?"), callback, "warning", argd)
|
||||
}
|
||||
else {
|
||||
delPage()
|
||||
}
|
||||
}
|
||||
|
||||
// ========================= 【python调用qml】 =========================
|
||||
|
||||
// 准备开始处理一个文档
|
||||
function onDocStart(path) {
|
||||
// 刷新表格显示
|
||||
const d = filesTableView.get(path)
|
||||
let state = `0/${d.range_end - d.range_start + 1}`
|
||||
filesTableView.setProperty(path, "state", state)
|
||||
}
|
||||
|
||||
// 获取一个文档的一页的结果
|
||||
function onDocGet(path, page, res) {
|
||||
// 刷新单个文档的信息
|
||||
const d = filesTableView.get(path)
|
||||
const state = `${page - d.range_start + 1}/${d.range_end - d.range_start + 1}`
|
||||
filesTableView.setProperty(path, "state", state)
|
||||
// 提取文字,添加到结果表格
|
||||
const title = path2name(path)
|
||||
res.title = `${title} - ${page}`
|
||||
resultsTableView.addOcrResult(res)
|
||||
ctrlPanel.msnStep(1)
|
||||
}
|
||||
|
||||
// 一个文档处理完毕。 isAll==true 时所有文档处理完毕。
|
||||
function onDocEnd(path, msg, isAll) {
|
||||
const errTitle = qsTr("文档识别异常")
|
||||
// 成功结束
|
||||
if(msg.startsWith("[Success]")) {
|
||||
filesTableView.setProperty(path, "state", "√")
|
||||
msg = ""
|
||||
}
|
||||
// 单个文档任务失败,总体未结束
|
||||
else if(!isAll) {
|
||||
filesTableView.setProperty(path, "state", "× "+ qsTr("失败"))
|
||||
qmlapp.popup.simple(errTitle, msg)
|
||||
}
|
||||
// 所有文档处理完毕
|
||||
if(isAll) {
|
||||
const simpleType = configsComp.getValue("other.simpleNotificationType")
|
||||
qmlapp.popup.simple(qsTr("批量识别完成"), "", simpleType)
|
||||
if(msg) // 如果有异常,则弹窗
|
||||
qmlapp.popup.message(errTitle, msg, "error")
|
||||
ctrlPanel.stopFinished()
|
||||
// 任务完成后续操作
|
||||
qmlapp.globalConfigs.utilsDicts.postTaskHardwareCtrl(
|
||||
configsComp.getValue("postTaskActions.system")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 路径转文件名
|
||||
function path2name(path) {
|
||||
const parts = path.split("/")
|
||||
return parts[parts.length - 1]
|
||||
}
|
||||
|
||||
// ========================= 【布局】 =========================
|
||||
|
||||
// 配置
|
||||
configsComp: BatchDOCConfigs {}
|
||||
|
||||
// 主区域:可切换双栏面板
|
||||
DoubleSwitchableLayout {
|
||||
id: doubleLayout
|
||||
saveKey: "BatchDOC_1"
|
||||
anchors.fill: parent
|
||||
|
||||
// 面板A:控制板+文件表格
|
||||
itemA: Panel {
|
||||
anchors.fill: parent
|
||||
|
||||
// 上方控制板
|
||||
MissionCtrlPanel {
|
||||
id: ctrlPanel
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: size_.spacing
|
||||
height: size_.line * 2
|
||||
|
||||
onRunClicked: tabPage.docStart()
|
||||
onPauseClicked: {
|
||||
tabPage.callPy("msnPause")
|
||||
pauseFinished()
|
||||
}
|
||||
onResumeClicked: {
|
||||
tabPage.callPy("msnResume")
|
||||
resumeFinished()
|
||||
}
|
||||
onStopClicked: tabPage.docStop()
|
||||
}
|
||||
|
||||
// 下方文件表格
|
||||
FilesTableView {
|
||||
id: filesTableView
|
||||
anchors.top: ctrlPanel.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: size_.spacing
|
||||
anchors.topMargin: size_.smallSpacing
|
||||
headers: [
|
||||
{key: "path", title: qsTr("文档"), left: true, display: path2name,
|
||||
btn: true, onClicked:onClickDoc},
|
||||
{key: "state", title: qsTr("状态"), btn: true, onClicked:onClickDoc},
|
||||
{key: "pages", title: qsTr("页数"), btn: true, onClicked:onClickDoc},
|
||||
]
|
||||
openBtnText: qsTr("打开文档")
|
||||
clearBtnText: qsTr("清空")
|
||||
defaultTips: qsTr("拖入文档或文件夹")
|
||||
fileDialogTitle: qsTr("请选择文档")
|
||||
fileDialogNameFilters: [qsTr("文档")+" (*.pdf *.xps *.epub *.mobi *.fb2 *.cbz)"]
|
||||
isLock: ctrlPanel.state_ !== "stop"
|
||||
onAddPaths: {
|
||||
tabPage.addDocs(paths)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 面板B:文字输出 & 设置
|
||||
itemB: Panel {
|
||||
anchors.fill: parent
|
||||
|
||||
// 配置项控制板
|
||||
TabPanel {
|
||||
id: tabPanel
|
||||
anchors.fill: parent
|
||||
anchors.margins: size_.spacing
|
||||
isMenuTop: doubleLayout.isRow // 左右布局时,菜单在顶部;上下布局时菜单在底部
|
||||
menuHeight: size_.line * 2
|
||||
|
||||
// 结果面板
|
||||
ResultsTableView {
|
||||
id: resultsTableView
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
}
|
||||
|
||||
tabsModel: [
|
||||
{
|
||||
"key": "configs",
|
||||
"title": qsTr("设置"),
|
||||
"component": configsComp.panelComponent,
|
||||
},
|
||||
{
|
||||
"key": "ocrResult",
|
||||
"title": qsTr("记录"),
|
||||
"component": resultsTableView,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标拖入文档
|
||||
DropArea_ {
|
||||
id: "addDocsDropArea"
|
||||
anchors.fill: parent
|
||||
callback: tabPage.addDocs
|
||||
}
|
||||
|
||||
// 预览面板
|
||||
PreviewDoc {
|
||||
id: previewDoc
|
||||
anchors.fill: parent
|
||||
configsComp: tabPage.configsComp
|
||||
updateInfo: (path, info) => {
|
||||
let infoA = filesTableView.get(path)
|
||||
Object.assign(infoA, info)
|
||||
filesTableView.set(path, infoA)
|
||||
}
|
||||
}
|
||||
}
|
||||
149
UmiOCR-data/qt_res/qml/TabPages/BatchDOC/BatchDOCConfigs.qml
Normal file
149
UmiOCR-data/qt_res/qml/TabPages/BatchDOC/BatchDOCConfigs.qml
Normal file
@@ -0,0 +1,149 @@
|
||||
// ==============================================
|
||||
// =============== 批量PDF的配置项 ===============
|
||||
// ==============================================
|
||||
|
||||
import QtQuick 2.15
|
||||
import "../../Configs"
|
||||
|
||||
Configs {
|
||||
category_: "BatchPDF"
|
||||
signal clickIgnoreArea() // 打开忽略区域
|
||||
|
||||
configDict: {
|
||||
// OCR参数
|
||||
"ocr": qmlapp.globalConfigs.ocrManager.deploy(this, "ocr"),
|
||||
|
||||
// 后处理
|
||||
"tbpu": {
|
||||
"title": qsTr("OCR文本后处理"),
|
||||
"type": "group",
|
||||
|
||||
"parser": qmlapp.globalConfigs.utilsDicts.getTbpuParser(),
|
||||
"btns": {
|
||||
"title": "👈"+qsTr("点击表格,可设置更多内容"),
|
||||
"btnsList": [],
|
||||
},
|
||||
"ignoreArea": {
|
||||
"type": "var",
|
||||
"save": false,
|
||||
},
|
||||
"ignoreRangeStart": { // 忽略区域范围
|
||||
"default": 1,
|
||||
"save": false,
|
||||
},
|
||||
"ignoreRangeEnd": {
|
||||
"default": -1,
|
||||
"save": false,
|
||||
},
|
||||
},
|
||||
|
||||
// 文档参数
|
||||
"doc": {
|
||||
"title": qsTr("文档处理"),
|
||||
"type": "group",
|
||||
|
||||
"extractionMode": {
|
||||
"title": qsTr("内容提取模式"),
|
||||
"toolTip": qsTr("若一页文档既存在图片又存在文本,如何进行处理"),
|
||||
"optionsList": [
|
||||
["mixed", qsTr("混合OCR/原文本")],
|
||||
["fullPage", qsTr("整页强制OCR")],
|
||||
["imageOnly", qsTr("仅OCR图片")],
|
||||
["textOnly", qsTr("仅拷贝原有文本")],
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// 任务参数
|
||||
"mission": {
|
||||
"title": qsTr("批量任务"),
|
||||
"type": "group",
|
||||
|
||||
"recurrence": {
|
||||
"title": qsTr("递归读取子文件夹"),
|
||||
"toolTip": qsTr("导入文件夹时,导入子文件夹中全部文档"),
|
||||
"default": false,
|
||||
},
|
||||
"dirType": {
|
||||
"title": qsTr("保存到"),
|
||||
"optionsList": [
|
||||
["source", qsTr("文档原目录")],
|
||||
["specify", qsTr("指定目录")],
|
||||
],
|
||||
},
|
||||
"dir": {
|
||||
"title": qsTr("指定目录"),
|
||||
"toolTip": qsTr("必须先指定“保存到指定目录”才生效"),
|
||||
"type": "file",
|
||||
"selectExisting": true, // 选择现有
|
||||
"selectFolder": true, // 选择文件夹
|
||||
"dialogTitle": qsTr("OCR结果保存目录"),
|
||||
},
|
||||
"fileNameFormat": {
|
||||
"title": qsTr("文件名格式"),
|
||||
"toolTip": qsTr("无需填写拓展名。支持插入以下占位符:\n%date 日期时间\n%name 原文档名\n%range 识别页数范围。只有识别页数小于总页数时才会显示。\n举例:[OCR]_%name%range_%date\n生成:[OCR]_文档A(p2-10)_20230901_1213.txt\n添加占位符可以避免旧文件被新文件覆盖。"),
|
||||
"default": "[OCR]_%name%range_%date",
|
||||
"advanced": true,
|
||||
},
|
||||
"datetimeFormat": {
|
||||
"title": qsTr("日期时间格式"),
|
||||
"toolTip": qsTr("文件名中 %date 的日期格式。支持插入以下占位符:\n%Y 年、 %m 月、 %d 日、 %H 小时、 \n%M 分钟、 %S 秒 、 %unix 时间戳 \n举例:%Y年%m月%d日_%H-%M\n生成:2023年09月01日_12-13.txt"),
|
||||
"default": "%Y%m%d_%H%M",
|
||||
"advanced": true,
|
||||
},
|
||||
|
||||
"filesType": {
|
||||
"title": qsTr("保存文件类型"),
|
||||
"type": "group",
|
||||
"enabledFold": true,
|
||||
"fold": false,
|
||||
|
||||
"pdfLayered": {
|
||||
"title": qsTr("layered.pdf 双层可搜索文档"),
|
||||
"toolTip": qsTr("保留原有图片,叠加一层透明文字,可以搜索和复制"),
|
||||
"default": true,
|
||||
},
|
||||
"pdfOneLayer": {
|
||||
"title": qsTr("text.pdf 单层纯文本文档"),
|
||||
"toolTip": qsTr("创建空白PDF文档,只写入识别文字,不含图片"),
|
||||
"default": false,
|
||||
},
|
||||
"txt": {
|
||||
"title": qsTr("txt 标准格式"),
|
||||
"toolTip": qsTr("含识别文字和页数信息"),
|
||||
"default": false,
|
||||
},
|
||||
"txtPlain": {
|
||||
"title": qsTr("p.txt 纯文字格式"),
|
||||
"toolTip": qsTr("输出所有识别文字"),
|
||||
"default": false,
|
||||
},
|
||||
"csv": {
|
||||
"title": qsTr("csv 表格文件(Excel)"),
|
||||
"toolTip": qsTr("将页数信息和识别内容写入csv表格文件。可用Excel打开,另存为xlsx格式。"),
|
||||
"default": false,
|
||||
},
|
||||
"jsonl": {
|
||||
"title": qsTr("jsonl 原始信息"),
|
||||
"toolTip": qsTr("每行为一条json数据,便于第三方程序读取操作"),
|
||||
"default": false,
|
||||
},
|
||||
},
|
||||
|
||||
"ignoreBlank": {
|
||||
"title": qsTr("忽略空白页"),
|
||||
"toolTip": qsTr("若某一页没有文字或识别失败,也不会输出错误提示信息"),
|
||||
"default": true,
|
||||
},
|
||||
},
|
||||
|
||||
// 任务完成后续操作
|
||||
"postTaskActions": qmlapp.globalConfigs.utilsDicts.getPostTaskActions(),
|
||||
|
||||
"other": {
|
||||
"title": qsTr("其它"),
|
||||
"type": "group",
|
||||
"simpleNotificationType": qmlapp.globalConfigs.utilsDicts.getSimpleNotificationType()
|
||||
},
|
||||
}
|
||||
}
|
||||
431
UmiOCR-data/qt_res/qml/TabPages/BatchDOC/PreviewDoc.qml
Normal file
431
UmiOCR-data/qt_res/qml/TabPages/BatchDOC/PreviewDoc.qml
Normal file
@@ -0,0 +1,431 @@
|
||||
// ===========================================
|
||||
// =============== 文档预览面板 ===============
|
||||
// ===========================================
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import DocPreviewConnector 1.0
|
||||
|
||||
import "../../Widgets"
|
||||
import "../../Widgets/IgnoreArea"
|
||||
|
||||
ModalLayer {
|
||||
id: pRoot
|
||||
property var updateInfo // 更新信息函数
|
||||
property var configsComp: undefined // 设置组件
|
||||
|
||||
property string previewPath: ""
|
||||
property string password: ""
|
||||
property bool isEncrypted: false // 已加密
|
||||
property bool isAuthenticate: false // 密码正确
|
||||
property int previewPage: -1
|
||||
property int pageCount: -1
|
||||
property int rangeStart: -1
|
||||
property int rangeEnd: -1
|
||||
property int ignoreRangeStart: 1
|
||||
property int ignoreRangeEnd: -1
|
||||
property bool previewOCR: false // 是否预览OCR
|
||||
property bool ocrRunning: false // 是否预览OCR正在执行
|
||||
|
||||
// 展示文档
|
||||
// info: path, page_count, range_start, range_end, is_encrypted, password, is_authenticate
|
||||
function show(info) {
|
||||
imgViewer.clear()
|
||||
visible = true
|
||||
previewPath = info.path
|
||||
pageCount = info.page_count
|
||||
previewPage = info.range_start
|
||||
rangeStart = info.range_start
|
||||
rangeEnd = info.range_end
|
||||
password = info.password
|
||||
isEncrypted = info.is_encrypted
|
||||
isAuthenticate = info.is_authenticate
|
||||
// 读取忽略区域设置
|
||||
let initArea = configsComp.getValue("tbpu.ignoreArea")
|
||||
if(initArea && initArea.length>0) {
|
||||
// 读取设置,反格式化
|
||||
let ig1 = []
|
||||
for(let i=0,l=initArea.length; i<l; i++) {
|
||||
const b = initArea[i]
|
||||
ig1.push({
|
||||
"x": b[0][0],
|
||||
"y": b[0][1],
|
||||
"width": b[2][0] - b[0][0],
|
||||
"height": b[2][1] - b[0][1],
|
||||
})
|
||||
}
|
||||
imgViewer.ig1Boxes = ig1
|
||||
}
|
||||
ignoreRangeStart = configsComp.getValue("tbpu.ignoreRangeStart")
|
||||
ignoreRangeEnd = configsComp.getValue("tbpu.ignoreRangeEnd")
|
||||
toPreview()
|
||||
}
|
||||
|
||||
// 返回上层,更新信息
|
||||
onVisibleChanged: {
|
||||
if(visible) return
|
||||
// 负数转倒数
|
||||
if(rangeStart < 0) rangeStart += pageCount + 1
|
||||
if(rangeEnd < 0) rangeEnd += pageCount + 1
|
||||
// 范围检查
|
||||
if(rangeStart < 1) rangeStart = 1
|
||||
if(rangeStart > pageCount) rangeStart = pageCount
|
||||
if(rangeEnd < rangeStart) rangeEnd = rangeStart
|
||||
if(rangeEnd > pageCount) rangeEnd = pageCount
|
||||
let pages_ = `${pageCount}` // 如果范围为整本,只显示总页数。否则显示 起始-结束
|
||||
if(rangeEnd - rangeStart + 1 < pageCount)
|
||||
pages_ = `${rangeStart}-${rangeEnd}`
|
||||
if(updateInfo) {
|
||||
updateInfo(previewPath, {
|
||||
pages: pages_,
|
||||
state: isAuthenticate ? "" : qsTr("加密"),
|
||||
range_start: rangeStart,
|
||||
range_end: rangeEnd,
|
||||
password: password,
|
||||
is_authenticate: isAuthenticate,
|
||||
})
|
||||
}
|
||||
// 更新忽略区域
|
||||
if(imgViewer.ig1Boxes.length > 0) {
|
||||
// 格式化,存入设置
|
||||
let ig1 = []
|
||||
for(let i=0,l=imgViewer.ig1Boxes.length; i<l; i++) {
|
||||
const b = imgViewer.ig1Boxes[i]
|
||||
const x = Math.round(b.x)
|
||||
const y = Math.round(b.y)
|
||||
const w = Math.round(b.width)
|
||||
const h = Math.round(b.height)
|
||||
ig1.push([[x, y], [x+w, y], [x+w, y+h], [x, y+h]])
|
||||
}
|
||||
configsComp.setValue("tbpu.ignoreArea", ig1)
|
||||
}
|
||||
else {
|
||||
configsComp.setValue("tbpu.ignoreArea", undefined)
|
||||
}
|
||||
configsComp.setValue("tbpu.ignoreRangeStart", ignoreRangeStart)
|
||||
configsComp.setValue("tbpu.ignoreRangeEnd", ignoreRangeEnd)
|
||||
imgViewer.clear()
|
||||
prevConn.clear() // 清除文档缓存
|
||||
previewPath = ""
|
||||
}
|
||||
|
||||
// 翻页。to直接翻页,flag加减页。
|
||||
function changePage(to, flag=0) {
|
||||
if (typeof to === "string") {
|
||||
to = parseInt(to, 10)
|
||||
}
|
||||
if(flag != 0) {
|
||||
to = previewPage + flag
|
||||
}
|
||||
if(previewPage != to && to > 0 && to <= pageCount) {
|
||||
previewPage = to
|
||||
toPreview()
|
||||
}
|
||||
}
|
||||
Keys.onLeftPressed: changePage(-1, -1) // 上一页
|
||||
Keys.onUpPressed: changePage(-1, -1)
|
||||
Keys.onRightPressed: changePage(-1, 1) // 下一页
|
||||
Keys.onDownPressed: changePage(-1, 1)
|
||||
|
||||
// 预览一页文档
|
||||
function toPreview() {
|
||||
if(!previewPath) return
|
||||
if(previewPage < 1) previewPage = 1
|
||||
if(previewPage > pageCount) previewPage = pageCount
|
||||
prevConn.preview(previewPath, previewPage, password)
|
||||
if(previewOCR) { // 预览OCR
|
||||
ocrRunning = true
|
||||
const argd = configsComp.getValueDict()
|
||||
argd["tbpu.parser"] = "None" // 去除排版解析
|
||||
prevConn.ocr(previewPath, previewPage, password, argd)
|
||||
}
|
||||
}
|
||||
// 预览连接器
|
||||
DocPreviewConnector {
|
||||
id: prevConn
|
||||
// 图片渲染的回调
|
||||
onPreviewImg: function(imgID) {
|
||||
const title = qsTr("打开文档失败")
|
||||
if(imgID === "[Warning] is_encrypted") {
|
||||
qmlapp.popup.simple(title, qsTr("请填写正确的密码"))
|
||||
isAuthenticate = false
|
||||
}
|
||||
else if(imgID.startsWith("[Error]")) {
|
||||
qmlapp.popup.message(title, imgID, "error")
|
||||
}
|
||||
else {
|
||||
imgViewer.showImgID(imgID)
|
||||
if(!isAuthenticate) {
|
||||
qmlapp.popup.simple(qsTr("密码正确"), password)
|
||||
isAuthenticate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// ocr预览的回调
|
||||
onPreviewOcr: function(info) {
|
||||
let path = info[0], page = info[1], res = info[2]
|
||||
if(res.code!=100&&res.code!=101) { // 遇到异常
|
||||
qmlapp.popup.message(qsTr("文档预览异常"), res.data, "error")
|
||||
return
|
||||
}
|
||||
if(path != previewPath || page != previewPage) {
|
||||
console.warn("文档OCR预览回调不匹配")
|
||||
return
|
||||
}
|
||||
ocrRunning = false
|
||||
imgViewer.showTextBoxes(res)
|
||||
}
|
||||
}
|
||||
|
||||
property string ignoreTips: qsTr("忽略区域说明:\n右键拖拽,绘制矩形区域,包含在区域内的文字框将被忽略。可用于排除水印、页眉页脚。\n范围允许填写负数,表示倒数第x页。如-1表示最后一页,-2表示倒数第2页。\n忽略区域的设置对所有文档生效。")
|
||||
|
||||
contentItem: DoubleRowLayout {
|
||||
anchors.fill: parent
|
||||
initSplitterX: size_.line * 13
|
||||
// 左:控制面板
|
||||
leftItem: Panel {
|
||||
anchors.fill: parent
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
contentWidth: width // 内容宽度
|
||||
clip: true // 溢出隐藏
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: size_.spacing
|
||||
spacing: size_.smallSpacing
|
||||
// ===== 文件名 =====
|
||||
Text_ {
|
||||
text: previewPath
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
wrapMode: TextEdit.WrapAnywhere // 任意换行
|
||||
maximumLineCount: 4 // 限制行数
|
||||
color: theme.subTextColor
|
||||
font.pixelSize: size_.smallText
|
||||
}
|
||||
// ===== 密码 =====
|
||||
Row {
|
||||
visible: isEncrypted && !isAuthenticate // 已加密,未填密码,才显示
|
||||
spacing: size_.spacing
|
||||
height: size_.line + size_.spacing * 2
|
||||
Text_ {
|
||||
color: theme.noColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("密码:")
|
||||
}
|
||||
TextField_ {
|
||||
width: size_.line * 6
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
text: password
|
||||
onTextChanged: password = text
|
||||
}
|
||||
IconButton {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: height
|
||||
icon_: "yes"
|
||||
onClicked: toPreview()
|
||||
}
|
||||
}
|
||||
// ===== 控制项 =====
|
||||
Column {
|
||||
visible: !isEncrypted || isAuthenticate
|
||||
spacing: size_.smallSpacing
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
// ===== 页数 =====
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 1
|
||||
color: theme.coverColor4
|
||||
}
|
||||
Row {
|
||||
spacing: size_.line
|
||||
height: size_.line
|
||||
Text_ {
|
||||
text: qsTr("预览页面")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
CheckButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: size_.line
|
||||
enabledAnime: true
|
||||
checked: previewOCR
|
||||
textColor_: theme.subTextColor
|
||||
onCheckedChanged: {
|
||||
if(!previewOCR&&checked) {
|
||||
previewOCR = true
|
||||
toPreview()
|
||||
}
|
||||
else {
|
||||
previewOCR = ocrRunning = false
|
||||
}
|
||||
}
|
||||
text_: "OCR"
|
||||
toolTip: qsTr("预览PDF时,是否预览OCR结果")
|
||||
}
|
||||
}
|
||||
Row {
|
||||
spacing: size_.spacing
|
||||
height: size_.line + size_.smallSpacing * 2
|
||||
Button_ {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
text_: "<"
|
||||
onClicked: changePage(0, -1)
|
||||
}
|
||||
Button_ {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
text_: ">"
|
||||
onClicked: changePage(0, 1)
|
||||
}
|
||||
TextField_ {
|
||||
width: size_.line * 3
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
validator: IntValidator{bottom: 1; top: pageCount;}
|
||||
text: previewPage
|
||||
onTextChanged: changePage(text)
|
||||
}
|
||||
Text_ {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "/ "+pageCount
|
||||
}
|
||||
}
|
||||
// ===== OCR范围 =====
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 1
|
||||
color: theme.coverColor4
|
||||
}
|
||||
Text_ {
|
||||
text: qsTr("OCR页数")
|
||||
}
|
||||
Row {
|
||||
height: size_.line + size_.smallSpacing * 2
|
||||
spacing: size_.spacing
|
||||
Text_ {
|
||||
font.pixelSize: size_.smallText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("范围")
|
||||
}
|
||||
TextField_ {
|
||||
width: size_.line * 3
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
validator: IntValidator{bottom: -pageCount; top: pageCount;}
|
||||
text: rangeStart
|
||||
onTextChanged: {
|
||||
if(text !== "" && text !== "-") rangeStart = text
|
||||
}
|
||||
}
|
||||
Text_ {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "-"
|
||||
}
|
||||
TextField_ {
|
||||
width: size_.line * 3
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
validator: IntValidator{bottom: -pageCount; top: pageCount;}
|
||||
text: rangeEnd
|
||||
onTextChanged: {
|
||||
if(text !== "" && text !== "-") rangeEnd = text
|
||||
}
|
||||
}
|
||||
}
|
||||
// ===== 忽略区域 =====
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 1
|
||||
color: theme.coverColor4
|
||||
}
|
||||
Text_ {
|
||||
text: qsTr("忽略区域页数(全局)")
|
||||
}
|
||||
Row {
|
||||
height: size_.line + size_.smallSpacing * 2
|
||||
spacing: size_.spacing
|
||||
Text_ {
|
||||
font.pixelSize: size_.smallText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("范围")
|
||||
}
|
||||
TextField_ {
|
||||
width: size_.line * 3
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
validator: IntValidator {}
|
||||
text: ignoreRangeStart
|
||||
onTextChanged: {
|
||||
if(text !== "" && text !== "-") ignoreRangeStart = text
|
||||
}
|
||||
}
|
||||
Text_ {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "-"
|
||||
}
|
||||
TextField_ {
|
||||
width: size_.line * 3
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
validator: IntValidator {}
|
||||
text: ignoreRangeEnd
|
||||
onTextChanged: {
|
||||
if(text !== "" && text !== "-") ignoreRangeEnd = text
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
spacing: size_.spacing
|
||||
height: size_.smallLine + size_.smallSpacing * 2
|
||||
Button_ {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: size_.smallLine + size_.smallSpacing
|
||||
bgColor_: theme.coverColor1
|
||||
text_: qsTr("撤销")
|
||||
onClicked: imgViewer.revokeIg()
|
||||
textSize: size_.smallText
|
||||
}
|
||||
Button_ {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: size_.smallLine + size_.smallSpacing
|
||||
bgColor_: theme.coverColor1
|
||||
textColor_: theme.noColor
|
||||
text_: qsTr("清空")
|
||||
onClicked: imgViewer.clearIg()
|
||||
textSize: size_.smallText
|
||||
}
|
||||
}
|
||||
Text_ {
|
||||
text: ignoreTips
|
||||
color: theme.subTextColor
|
||||
font.pixelSize: size_.smallText
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
wrapMode: TextEdit.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 右:图片查看面板
|
||||
rightItem: ImageWithIgnore {
|
||||
id: imgViewer
|
||||
anchors.fill: parent
|
||||
// 加载中 动态图标
|
||||
Loading {
|
||||
visible: ocrRunning
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user