多功能PDF阅读器
多功能PDF阅读器用于PDF文档的打开和编辑,主要由文档收藏、页面交互、笔记系统三个部分组成。其中,文档收藏功能可搭配云盘工具(如坚果云)使用,实现与云端同步操作。笔记系统可兼容其它工具(如Foxit、Xodo等),由其它工具生成的笔记也可以使用本阅读器进行编辑。最后,概览功能用于在页面两侧显示注释文字,该功能非常适用于双栏文档(如论文)的阅读和回顾。
文档收藏系统
文档收藏系统主要用于提供不同位置目录的快捷入口,配合云盘工具(如坚果云)后,可以实现快速打开以及修改自动同步到云的功能。
笔记系统
本阅读器支持文本、几何(圆形、方形)、箭头及注释笔记的创建和编辑。选中笔记后,左键拖动可改变笔记位置,右键点击可弹出对话框,以修改笔记的各类参数。
概览模式
概览模式是我们针对双栏文档阅读设计的功能,概览模式下,原本隐藏的注释可以通过弹框显示在文档两侧。首次阅读文档时,用户可将每一部分内容的翻译、总结或批注用注释形式进行保存,概览模式下,这些笔记将显示在对应位置两侧,非常便于对双栏论文进行回顾。
// 返回PDF页面项的边界矩形
QRectF PaperItem::boundingRect() const
{
QImage **addr = (QImage **)(images + index*size);
return QRectF(0, 0, (*addr)->width(), (*addr)->height());
}
// 绘制PDF页面项
void PaperItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QImage **addr = (QImage **)(images + index*size);
painter->drawImage(0, 0, **addr);
}
bool MainScene::loadFile(const QString &addr)
{
this->filename = addr;
document->document = Poppler::Document::load(addr);
if (!document || document->document->isLocked()) {
delete document;
return false;
}
if(document->document->numPages()>30)
{
return false;
}
for(int i=0; i<document->document->numPages(); i++)
{
indexes.append(i);
}
document->document->setRenderHint(Poppler::Document::TextAntialiasing, 1);
document->document->setRenderHint(Poppler::Document::Antialiasing, 1);
/* set hidden */
for(int pageidx=0; pageidx<document->document->numPages(); pageidx++)
{
for(int i=0;i<document->document->page(pageidx)->annotations().length();i++)
{
int type = document->document->page(pageidx)->annotations().at(i)->subType();
if(type==1 || type==2 || type==3 || type==6)
{
document->document->page(pageidx)->annotations().at(i)->setFlags(Poppler::Annotation::Hidden);
}
}
}
QProgressDialog *progressDlg=new QProgressDialog(nullptr);
progressDlg->setWindowModality(Qt::WindowModal);
progressDlg->setMinimumDuration(5);
progressDlg->setWindowTitle(tr("载入进度"));
progressDlg->setLabelText(tr("正在加载"));
progressDlg->setRange(0, document->document->numPages());
QPushButton *button = new QPushButton(tr("取消"));
button->setEnabled(false);
progressDlg->setCancelButton(button);
for(int i=0; i<document->document->numPages(); i++)
{
QImage *image = new QImage(document->document->page(i)->renderToImage(xres, yres, -1, -1, -1, -1));
if(i==0)
{
width = image->width();
height = image->height()*document->document->numPages();
}
document->images[i] = image;
QImage *tmptmp = *(&(document->images[0]));
pages.append(new PaperItem(i, (long)(&(document->images[0]))));
pages.at(i)->setPos(0, image->height()*i+SPACING*i);
addItem(pages.at(i));
progressDlg->setValue(i+1);
}
setSceneRect(0, 0, width, height);
document->startTimer();
for(int pageidx=0; pageidx<document->document->numPages(); pageidx++)
{
for(int i=0;i<document->document->page(pageidx)->annotations().length();i++)
{
switch(document->document->page(pageidx)->annotations().at(i)->subType())
{
case 1: // Text Annotation
{
Poppler::TextAnnotation *annotation = (Poppler::TextAnnotation *)(document->document->page(pageidx)->annotations().at(i));
annotation->setFlags(Poppler::Annotation::Hidden);
if(annotation->textType()==1)
{
annotations.append(new PaperAnnotation::FlatTextAnnotation(pageidx, annotation, width, height/document->document->numPages(), scale));
this->addItem(annotations.at(annotations.length()-1));
connect(((PaperAnnotation::FlatTextAnnotation *)(annotations.last())),
&PaperAnnotation::FlatTextAnnotation::posChanged,
this, &MainScene::send_status_changed);
// 添加删除接口
connect((PaperAnnotation::FlatTextAnnotation *)(annotations.last()), &PaperAnnotation::FlatTextAnnotation::deleteSelf,
this, &MainScene::removeCertainItem);
// this->removeItem(tmprectitem);
}
else {
annotations.append(new PaperAnnotation::PopupTextAnnotation(pageidx, annotation, width, height/document->document->numPages(), scale));
this->addItem(annotations.at(annotations.length()-1));
connect((PaperAnnotation::PopupTextAnnotation *)(annotations.last()), &PaperAnnotation::PopupTextAnnotation::deleteSelf,
this, &MainScene::removeCertainItem);
//annotations.append(new PaperAnnotation::PreviewAnnotation(pageidx, annotation, width, height/document->document->numPages(), scale));
//this->addItem(annotations.last());
}
break;
}
case 3: // Geom Annotation
{
Poppler::GeomAnnotation *annotation = (Poppler::GeomAnnotation *)(document->document->page(pageidx)->annotations().at(i));
annotation->setFlags(Poppler::Annotation::Hidden);
annotations.append(new PaperAnnotation::GeomAnnotation(pageidx, annotation, width, height/document->document->numPages(), scale));
this->addItem(annotations.at(annotations.length()-1));
connect(((PaperAnnotation::GeomAnnotation *)(annotations.last())),
&PaperAnnotation::GeomAnnotation::posChanged,
this, &MainScene::send_status_changed);
// 添加删除接口
connect((PaperAnnotation::GeomAnnotation *)(annotations.last()), &PaperAnnotation::GeomAnnotation::deleteSelf,
this, &MainScene::removeCertainItem);
break;
}
case 2: // LineAnnotation
{
Poppler::LineAnnotation *annotation = (Poppler::LineAnnotation *)(document->document->page(pageidx)->annotations().at(i));
annotation->setFlags(Poppler::Annotation::Hidden);
annotations.append(new PaperAnnotation::LineAnnotation(pageidx, annotation, width, height/document->document->numPages(), scale));
this->addItem(annotations.at(annotations.length()-1));
connect(((PaperAnnotation::LineAnnotation *)(annotations.last())),
&PaperAnnotation::LineAnnotation::posChanged,
this, &MainScene::send_status_changed);
// 添加删除接口
connect((PaperAnnotation::LineAnnotation *)(annotations.last()), &PaperAnnotation::LineAnnotation::deleteSelf,
this, &MainScene::removeCertainItem);
QLinkedListIterator<QPointF> rwIterator(annotation->linePoints());
startPoint = rwIterator.next();
rwIterator.toBack();
endPoint = rwIterator.previous();
break;
}
case 6: // Ink Annotation
{
Poppler::InkAnnotation *annotation = (Poppler::InkAnnotation *)(document->document->page(pageidx)->annotations().at(i));
annotation->setFlags(Poppler::Annotation::Hidden);
annotations.append(new PaperAnnotation::InkAnnotation(pageidx, annotation, width, height/document->document->numPages(), scale));
this->addItem(annotations.at(annotations.length()-1));
break;
}
}
}
}
return true;
}
// 处理鼠标滚轮事件,实现页面缩放
void GraphicsView::wheelEvent(QWheelEvent *ev)
{
if(ev->modifiers() & Qt::ControlModifier)
{
if(ev->delta()>0 & scalefactor>2)
{
ev->ignore();
return;
}
else if(ev->delta()<0 & scalefactor<0.5)
{
ev->ignore();
return;
}
else
{
mainscene->document->indexes.clear();
if(!refreshtimer->isActive())
{
oldCenterPoint = mapToScene(QPoint(width()/2, height()/2));
currentScenePoint = mapToScene(ev->pos());
lengthPoint = oldCenterPoint-currentScenePoint;
mainscene->currentpage = (int)(currentScenePoint.y()/height()/scalefactor);
}
if(ev->delta()>0)
{
refreshtimer->stop();
point = mapToScene(QPoint(this->x()*1.2+this->width()/2*1.2, this->y()*1.2+this->height()/2*1.2));
scale(1.2, 1.2);
scalefactor*=1.2;
mainscene->scale = scalefactor;
centerPoint = currentScenePoint+lengthPoint/scalefactor*oldscalefactor;
centerOn(centerPoint);
refreshtimer->start(500);
}
else
{
refreshtimer->stop();
point = mapToScene(QPoint(this->x()/1.2+this->width()/2/1.2, this->y()/1.2+this->height()/2/1.2));
scale(1/1.2, 1/1.2);
scalefactor*=1/1.2;
mainscene->scale = scalefactor;
QPointF lengthPoint = oldCenterPoint-currentScenePoint;
centerPoint = currentScenePoint+lengthPoint/scalefactor*oldscalefactor;
centerOn(centerPoint);
refreshtimer->start(500);
}
ev->accept();
}
}
else
{
QGraphicsView::wheelEvent(ev);
}
}
环境要求
-
Poppler:Poppler
-
Qt Creator:Qt | Tools for Each Stage of Software Development Lifecycle
Qt: 5.15.2
因为Poppler是32位的,所以只能Qt 5.15.2 MinGW 32位编译