Merge remote-tracking branch 'refs/remotes/origin/dev-qt5.1x' into dev-

qt5.1x_mac
preferencesAboutTextFull
jpapale 4 years ago
commit 2cc6d51e34
  1. 6
      OpenBoard.pro
  2. 4
      release_scripts/linux/package.sh
  3. 20
      release_scripts/osx/release.macx.sh
  4. 18
      release_scripts/windows/OpenBoard.iss
  5. 2
      release_scripts/windows/release.win7.vc9.bat
  6. 1
      resources/etc/OpenBoard.config
  7. 4
      src/adaptors/UBExportPDF.cpp
  8. 10
      src/adaptors/UBImportDocumentSetAdaptor.cpp
  9. 4
      src/adaptors/UBThumbnailAdaptor.cpp
  10. 5
      src/board/UBBoardController.cpp
  11. 4
      src/core/UBPersistenceManager.cpp
  12. 2
      src/core/UBSettings.cpp
  13. 2
      src/document/UBDocumentController.cpp
  14. 17
      src/domain/UBGraphicsPDFItem.cpp
  15. 7
      src/domain/UBGraphicsPDFItem.h
  16. 3
      src/domain/UBGraphicsScene.cpp
  17. 2
      src/domain/UBGraphicsScene.h
  18. 13
      src/domain/UBItem.h
  19. 7
      src/frameworks/UBFileSystemUtils.cpp
  20. 2
      src/frameworks/UBFileSystemUtils.h
  21. 1
      src/gui/UBDocumentNavigator.cpp
  22. 14
      src/pdf/GraphicsPDFItem.cpp
  23. 7
      src/pdf/GraphicsPDFItem.h
  24. 3
      src/pdf/PDFRenderer.h
  25. 4
      src/pdf/UBWebPluginPDFWidget.cpp
  26. 220
      src/pdf/XPDFRenderer.cpp
  27. 89
      src/pdf/XPDFRenderer.h

@ -9,9 +9,9 @@ CONFIG += debug_and_release \
VERSION_MAJ = 1 VERSION_MAJ = 1
VERSION_MIN = 6 VERSION_MIN = 6
VERSION_PATCH = 0 VERSION_PATCH = 1
VERSION_TYPE = r # a = alpha, b = beta, rc = release candidate, r = release, other => error VERSION_TYPE = a # a = alpha, b = beta, rc = release candidate, r = release, other => error
VERSION_BUILD = 0 VERSION_BUILD = 1116
VERSION = "$${VERSION_MAJ}.$${VERSION_MIN}.$${VERSION_PATCH}-$${VERSION_TYPE}.$${VERSION_BUILD}" VERSION = "$${VERSION_MAJ}.$${VERSION_MIN}.$${VERSION_PATCH}-$${VERSION_TYPE}.$${VERSION_BUILD}"

@ -358,7 +358,9 @@ for ((i=0;i<${#tab[@]};i++)); do
if [[ "${tab[$i]}" == *"libavcodec"* ]]; then if [[ "${tab[$i]}" == *"libavcodec"* ]]; then
depName="${tab[$i]::-2}" depName="${tab[$i]::-2}"
versionNumber="${tab[$i]: -2}" versionNumber="${tab[$i]: -2}"
echo -n "${depName}${versionNumber} (>= ${depdVer}) | ${depName}-extra${versionNumber} (>= ${depdVer})" >> "$CONTROL_FILE" depdVer_part1=`echo ${depdVer} | awk -F'.' '{print $1}'`
depdVer_part2=`echo ${depdVer} | awk -F'.' '{print $2}'`
echo -n "${depName}${versionNumber} (>= ${depdVer_part1}.${depdVer_part2}) | ${depName}-extra${versionNumber} (>= ${depdVer_part1}.${depdVer_part2})" >> "$CONTROL_FILE"
else else
echo -n "${tab[$i]} (>= ${depdVer})" >> "$CONTROL_FILE" echo -n "${tab[$i]} (>= ${depdVer})" >> "$CONTROL_FILE"
fi fi

@ -19,7 +19,7 @@ PROJECT_ROOT="$SCRIPT_PATH/../.."
APPLICATION_NAME="OpenBoard" APPLICATION_NAME="OpenBoard"
BASE_QT_DIR=~/Qt/5.13.1/clang_64 BASE_QT_DIR=~/Qt/5.14.2/clang_64
# Executables # Executables
QMAKE=$BASE_QT_DIR/bin/qmake QMAKE=$BASE_QT_DIR/bin/qmake
MACDEPLOYQT=$BASE_QT_DIR/bin/macdeployqt MACDEPLOYQT=$BASE_QT_DIR/bin/macdeployqt
@ -203,25 +203,25 @@ cd -
# make sure libs installed via homebrew 2.0 refer to in-app libs # make sure libs installed via homebrew 2.0 refer to in-app libs
notify "relinking libs ..." notify "relinking libs ..."
# libavformat # libavformat
install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libavcodec.58.dylib @executable_path/../Frameworks/libavcodec.58.dylib install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavcodec.58.dylib @executable_path/../Frameworks/libavcodec.58.dylib
install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib
install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
# libavcodec # libavcodec
install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib
install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
#libswresample #libswresample
install_name_tool "$APP/Contents/Frameworks/libswresample.3.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib install_name_tool "$APP/Contents/Frameworks/libswresample.3.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
#libswscale #libswscale
install_name_tool "$APP/Contents/Frameworks/libswscale.5.dylib" -change /usr/local/Cellar/ffmpeg/4.2.1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib install_name_tool "$APP/Contents/Frameworks/libswscale.5.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib
# libhogweed # libhogweed
install_name_tool "$APP/Contents/Frameworks/libhogweed.4.dylib" -change /usr/local/Cellar/nettle/3.4.1/lib/libnettle.6.dylib @executable_path/../Frameworks/libnettle.6.dylib install_name_tool "$APP/Contents/Frameworks/libhogweed.6.dylib" -change /usr/local/Cellar/nettle/3.6/lib/libnettle.8.dylib @executable_path/../Frameworks/libnettle.8.dylib
# libssl # libssl
install_name_tool "$APP/Contents/Frameworks/libssl.1.1.dylib" -change /usr/local/Cellar/openssl@1.1/1.1.1d/lib/libcrypto.1.1.dylib @executable_path/../Frameworks/libcrypto.1.1.dylib install_name_tool "$APP/Contents/Frameworks/libssl.1.1.dylib" -change /usr/local/Cellar/openssl@1.1/1.1.1h/lib/libcrypto.1.1.dylib @executable_path/../Frameworks/libcrypto.1.1.dylib
# libvorbis # libvorbis
install_name_tool "$APP/Contents/Frameworks/libvorbisenc.2.dylib" -change /usr/local/Cellar/libvorbis/1.3.6/lib/libvorbis.0.dylib @executable_path/../Frameworks/libvorbis.0.dylib install_name_tool "$APP/Contents/Frameworks/libvorbisenc.2.dylib" -change /usr/local/Cellar/libvorbis/1.3.6/lib/libvorbis.0.dylib @executable_path/../Frameworks/libvorbis.0.dylib

@ -56,7 +56,8 @@ Type: files ; Name: "{app}\*.dll"
#define QtDir GetEnv('QT_DIR') #define QtDir GetEnv('QT_DIR')
[Files] [Files]
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\microsoft\vcredist_x86.exe"; DestDir:"{tmp}" Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\microsoft\vcredist_2013.x64.exe"; DestDir:"{tmp}"
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\microsoft\vcredist_2015_2019.x64.exe"; DestDir:"{tmp}"
Source: "{#ProjectRoot}\build\win32\release\product\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "{#ProjectRoot}\build\win32\release\product\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
;OpenSSL ;OpenSSL
@ -87,6 +88,12 @@ Source: "{#QtLibs}\libGLESv2.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt5Quick.dll"; DestDir: "{app}" Source: "{#QtLibs}\Qt5Quick.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt5Positioning.dll"; DestDir: "{app}" Source: "{#QtLibs}\Qt5Positioning.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt5Sensors.dll"; DestDir: "{app}" Source: "{#QtLibs}\Qt5Sensors.dll"; DestDir: "{app}"
Source: "{#QtLibs}\icuuc65.dll"; DestDir: "{app}"
Source: "{#QtLibs}\icuin65.dll"; DestDir: "{app}"
Source: "{#QtLibs}\icudt65.dll"; DestDir: "{app}"
Source: "{#QtLibs}\libxslt.dll"; DestDir: "{app}"
Source: "{#QtLibs}\libxml2.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt5QmlModels.dll"; DestDir: "{app}"
Source: "{#QtLibs}\Qt5WebChannel.dll"; DestDir: "{app}" Source: "{#QtLibs}\Qt5WebChannel.dll"; DestDir: "{app}"
Source: "{#QtLibs}\libEGL.dll"; DestDir: "{app}" Source: "{#QtLibs}\libEGL.dll"; DestDir: "{app}"
;Source: "/etc/freezedWidgetWrapper.html"; DestDir: "{app}" ;Source: "/etc/freezedWidgetWrapper.html"; DestDir: "{app}"
@ -94,12 +101,6 @@ Source: "{#QtLibs}\libEGL.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\zlib\1.2.11\bin\zlib.dll"; DestDir:"{app}"; Flags: ignoreversion Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\zlib\1.2.11\bin\zlib.dll"; DestDir:"{app}"; Flags: ignoreversion
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\qtwebkit\bin\icudt64.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\qtwebkit\bin\icuin64.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\qtwebkit\bin\icuuc64.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\qtwebkit\bin\libxml2.dll"; DestDir: "{app}"
Source: "{#ProjectRoot}\..\OpenBoard-ThirdParty\qtwebkit\bin\libxslt.dll"; DestDir: "{app}"
; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
;Qt windows plugins ;Qt windows plugins
@ -166,7 +167,8 @@ Root: HKLM64; Subkey: "SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Low Righ
Root: HKLM64; Subkey: "SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: string; ValueName: "AppPath"; ValueData: "{app}"; Flags: uninsdeletevalue; Check: isProcessorX64 Root: HKLM64; Subkey: "SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Low Rights\DragDrop\{{E63D17F8-D9DA-479D-B9B5-0D101A03703B}"; ValueType: string; ValueName: "AppPath"; ValueData: "{app}"; Flags: uninsdeletevalue; Check: isProcessorX64
[Run] [Run]
Filename: "{tmp}\vcredist_x86.exe";WorkingDir:"{tmp}"; Parameters: "/q /norestart"; StatusMsg: Installing CRT... Filename: "{tmp}\vcredist_2013.x64.exe";WorkingDir:"{tmp}"; Parameters: "/norestart"; StatusMsg: Installing CRT 2013...
Filename: "{tmp}\vcredist_2015_2019.x64.exe";WorkingDir:"{tmp}"; Parameters: "/norestart"; StatusMsg: Installing CRT 2015-2019 ...
Filename: "{app}\OpenBoard.exe"; Description: "{cm:LaunchProgram,OpenBoard}"; Flags: nowait postinstall skipifsilent Filename: "{app}\OpenBoard.exe"; Description: "{cm:LaunchProgram,OpenBoard}"; Flags: nowait postinstall skipifsilent
[UninstallDelete] [UninstallDelete]

@ -19,7 +19,7 @@ set SCRIPT_PATH=%~dp0
set PROJECT_ROOT=%SCRIPT_PATH%\..\.. set PROJECT_ROOT=%SCRIPT_PATH%\..\..
set APPLICATION_NAME=OpenBoard set APPLICATION_NAME=OpenBoard
set QT_DIR=C:\Qt\5.13.2\msvc2017_64 set QT_DIR=C:\Qt\5.14.2\msvc2017_64
set QT_BIN=%QT_DIR%\bin set QT_BIN=%QT_DIR%\bin
set PROGRAMS_FILE_PATH=C:\Program Files (x86) set PROGRAMS_FILE_PATH=C:\Program Files (x86)

@ -123,6 +123,7 @@ RefreshRateInFramePerSecond=2
Margin=20 Margin=20
PageFormat=A4 PageFormat=A4
Resolution=300 Resolution=300
ZoomBehavior=4
[Podcast] [Podcast]
AudioRecordingDevice=Default AudioRecordingDevice=Default

@ -113,7 +113,7 @@ bool UBExportPDF::persistsDocument(UBDocumentProxy* pDocumentProxy, const QStrin
QSize pageSize = scene->sceneSize(); QSize pageSize = scene->sceneSize();
// set high res rendering // set high res rendering
scene->setRenderingQuality(UBItem::RenderingQualityHigh); scene->setRenderingQuality(UBItem::RenderingQualityHigh, UBItem::CacheNotAllowed);
scene->setRenderingContext(UBGraphicsScene::NonScreen); scene->setRenderingContext(UBGraphicsScene::NonScreen);
// Setting output page size // Setting output page size
@ -132,7 +132,7 @@ bool UBExportPDF::persistsDocument(UBDocumentProxy* pDocumentProxy, const QStrin
// Restore screen rendering quality // Restore screen rendering quality
scene->setRenderingContext(UBGraphicsScene::Screen); scene->setRenderingContext(UBGraphicsScene::Screen);
scene->setRenderingQuality(UBItem::RenderingQualityNormal); scene->setRenderingQuality(UBItem::RenderingQualityNormal, UBItem::CacheAllowed);
// Restore background state // Restore background state
scene->setBackground(isDark, pageBackground); scene->setBackground(isDark, pageBackground);

@ -94,11 +94,17 @@ QFileInfoList UBImportDocumentSetAdaptor::importData(const QString &zipFile, con
foreach(QFileInfo readDir, tDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden , QDir::Name)) { foreach(QFileInfo readDir, tDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden , QDir::Name)) {
QString newFileName = readDir.fileName(); QString newFileName = readDir.fileName();
if (QFileInfo(destination + "/" + readDir.fileName()).exists()) { if (QFileInfo(destination + "/" + newFileName).exists())
{
//if the generateUniqueDocumentPath is called twice in the same millisecond, the destination files are overwritten
do
{
newFileName = QFileInfo(UBPersistenceManager::persistenceManager()->generateUniqueDocumentPath(tmpDir)).fileName(); newFileName = QFileInfo(UBPersistenceManager::persistenceManager()->generateUniqueDocumentPath(tmpDir)).fileName();
} while (QFileInfo(destination + "/" + newFileName).exists());
} }
QString newFilePath = destination + "/" + newFileName; QString newFilePath = destination + "/" + newFileName;
if (UBFileSystemUtils::copy(readDir.absoluteFilePath(), newFilePath)) { if (UBFileSystemUtils::copy(readDir.absoluteFilePath(), newFilePath, true)) {
result.append(newFilePath); result.append(newFilePath);
} }
} }

@ -153,12 +153,12 @@ void UBThumbnailAdaptor::persistScene(UBDocumentProxy* proxy, UBGraphicsScene* p
} }
pScene->setRenderingContext(UBGraphicsScene::NonScreen); pScene->setRenderingContext(UBGraphicsScene::NonScreen);
pScene->setRenderingQuality(UBItem::RenderingQualityHigh); pScene->setRenderingQuality(UBItem::RenderingQualityHigh, UBItem::CacheNotAllowed);
pScene->render(&painter, imageRect, sceneRect, Qt::KeepAspectRatio); pScene->render(&painter, imageRect, sceneRect, Qt::KeepAspectRatio);
pScene->setRenderingContext(UBGraphicsScene::Screen); pScene->setRenderingContext(UBGraphicsScene::Screen);
pScene->setRenderingQuality(UBItem::RenderingQualityNormal); pScene->setRenderingQuality(UBItem::RenderingQualityNormal, UBItem::CacheAllowed);
thumb.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(fileName, "JPG"); thumb.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(fileName, "JPG");
} }

@ -779,6 +779,7 @@ void UBBoardController::deleteScene(int nIndex)
scIndexes << nIndex; scIndexes << nIndex;
deletePages(scIndexes); deletePages(scIndexes);
selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime()));
UBMetadataDcSubsetAdaptor::persist(selectedDocument());
if (nIndex >= pageCount()) if (nIndex >= pageCount())
nIndex = pageCount()-1; nIndex = pageCount()-1;
@ -2178,13 +2179,13 @@ void UBBoardController::grabScene(const QRectF& pSceneRect)
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
mActiveScene->setRenderingContext(UBGraphicsScene::NonScreen); mActiveScene->setRenderingContext(UBGraphicsScene::NonScreen);
mActiveScene->setRenderingQuality(UBItem::RenderingQualityHigh); mActiveScene->setRenderingQuality(UBItem::RenderingQualityHigh, UBItem::CacheNotAllowed);
mActiveScene->render(&painter, targetRect, pSceneRect); mActiveScene->render(&painter, targetRect, pSceneRect);
mActiveScene->setRenderingContext(UBGraphicsScene::Screen); mActiveScene->setRenderingContext(UBGraphicsScene::Screen);
// mActiveScene->setRenderingQuality(UBItem::RenderingQualityNormal); // mActiveScene->setRenderingQuality(UBItem::RenderingQualityNormal);
mActiveScene->setRenderingQuality(UBItem::RenderingQualityHigh); mActiveScene->setRenderingQuality(UBItem::RenderingQualityHigh, UBItem::CacheAllowed);
mPaletteManager->addItem(QPixmap::fromImage(image)); mPaletteManager->addItem(QPixmap::fromImage(image));

@ -867,12 +867,12 @@ void UBPersistenceManager::insertDocumentSceneAt(UBDocumentProxy* proxy, UBGraph
mSceneCache.insert(proxy, index, scene); mSceneCache.insert(proxy, index, scene);
proxy->incPageCount();
if (persist) { if (persist) {
persistDocumentScene(proxy, scene, index); persistDocumentScene(proxy, scene, index);
} }
proxy->incPageCount();
emit documentSceneCreated(proxy, index); emit documentSceneCreated(proxy, index);
} }

@ -408,7 +408,7 @@ void UBSettings::init()
pdfMargin = new UBSetting(this, "PDF", "Margin", "20"); pdfMargin = new UBSetting(this, "PDF", "Margin", "20");
pdfPageFormat = new UBSetting(this, "PDF", "PageFormat", "A4"); pdfPageFormat = new UBSetting(this, "PDF", "PageFormat", "A4");
pdfResolution = new UBSetting(this, "PDF", "Resolution", "300"); pdfResolution = new UBSetting(this, "PDF", "Resolution", "300");
pdfZoomBehavior = new UBSetting(this, "PDF", "ZoomBehavior", "1"); pdfZoomBehavior = new UBSetting(this, "PDF", "ZoomBehavior", "4");
podcastFramesPerSecond = new UBSetting(this, "Podcast", "FramesPerSecond", 10); podcastFramesPerSecond = new UBSetting(this, "Podcast", "FramesPerSecond", 10);
podcastVideoSize = new UBSetting(this, "Podcast", "VideoSize", "Medium"); podcastVideoSize = new UBSetting(this, "Podcast", "VideoSize", "Medium");

@ -2786,6 +2786,8 @@ void UBDocumentController::importFile()
if (fileInfo.suffix().toLower() == "ubx") { if (fileInfo.suffix().toLower() == "ubx") {
UBPersistenceManager::persistenceManager()->createDocumentProxiesStructure(docManager->importUbx(filePath, UBSettings::userDocumentDirectory()), true); UBPersistenceManager::persistenceManager()->createDocumentProxiesStructure(docManager->importUbx(filePath, UBSettings::userDocumentDirectory()), true);
emit documentThumbnailsUpdated(this); // some documents might have been overwritten while not having the same page count
} else { } else {
UBSettings::settings()->lastImportFilePath->set(QVariant(fileInfo.absolutePath())); UBSettings::settings()->lastImportFilePath->set(QVariant(fileInfo.absolutePath()));

@ -58,6 +58,16 @@ QVariant UBGraphicsPDFItem::itemChange(GraphicsItemChange change, const QVariant
return GraphicsPDFItem::itemChange(change, newValue); return GraphicsPDFItem::itemChange(change, newValue);
} }
void UBGraphicsPDFItem::updateChild()
{
CacheMode prevCacheMode = cacheMode();
GraphicsPDFItem::update();
// Workaround: Necessary, otherwise only the control scene is updated, the display scene refresh is ignored for an unknown reason.
setCacheMode(prevCacheMode);
}
void UBGraphicsPDFItem::setUuid(const QUuid &pUuid) void UBGraphicsPDFItem::setUuid(const QUuid &pUuid)
{ {
UBItem::setUuid(pUuid); UBItem::setUuid(pUuid);
@ -142,6 +152,11 @@ void UBGraphicsPDFItem::setRenderingQuality(RenderingQuality pRenderingQuality)
} }
} }
void UBGraphicsPDFItem::setCacheBehavior(UBItem::CacheBehavior cacheBehavior)
{
UBItem::setCacheBehavior(cacheBehavior);
GraphicsPDFItem::setCacheAllowed(cacheBehavior == UBItem::CacheAllowed);
}
UBGraphicsScene* UBGraphicsPDFItem::scene() UBGraphicsScene* UBGraphicsPDFItem::scene()
{ {
@ -153,7 +168,7 @@ UBGraphicsPixmapItem* UBGraphicsPDFItem::toPixmapItem() const
{ {
QPixmap pixmap(mRenderer->pageSizeF(mPageNumber).toSize()); QPixmap pixmap(mRenderer->pageSizeF(mPageNumber).toSize());
QPainter painter(&pixmap); QPainter painter(&pixmap);
mRenderer->render(&painter, mPageNumber); mRenderer->render(&painter, mPageNumber, false /* Cache allowed */);
UBGraphicsPixmapItem *pixmapItem = new UBGraphicsPixmapItem(); UBGraphicsPixmapItem *pixmapItem = new UBGraphicsPixmapItem();
pixmapItem->setPixmap(pixmap); pixmapItem->setPixmap(pixmap);

@ -59,13 +59,14 @@ class UBGraphicsPDFItem: public GraphicsPDFItem, public UBItem, public UBGraphic
virtual void setRenderingQuality(RenderingQuality pRenderingQuality); virtual void setRenderingQuality(RenderingQuality pRenderingQuality);
virtual void setCacheBehavior(CacheBehavior cacheBehavior);
virtual UBGraphicsScene* scene(); virtual UBGraphicsScene* scene();
virtual UBGraphicsPixmapItem* toPixmapItem() const; virtual UBGraphicsPixmapItem* toPixmapItem() const;
virtual void clearSource(){;} virtual void clearSource(){;}
virtual void setUuid(const QUuid &pUuid); virtual void setUuid(const QUuid &pUuid);
protected: protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
@ -74,7 +75,9 @@ class UBGraphicsPDFItem: public GraphicsPDFItem, public UBItem, public UBGraphic
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value); virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
virtual void updateChild();
private slots:
void OnRequireUpdate();
}; };
#endif /* UBGRAPHICSPDFITEM_H_ */ #endif /* UBGRAPHICSPDFITEM_H_ */

@ -2395,7 +2395,7 @@ void UBGraphicsScene::addMask(const QPointF &center)
curtain->setSelected(true); curtain->setSelected(true);
} }
void UBGraphicsScene::setRenderingQuality(UBItem::RenderingQuality pRenderingQuality) void UBGraphicsScene::setRenderingQuality(UBItem::RenderingQuality pRenderingQuality, UBItem::CacheBehavior cacheBehavior)
{ {
QListIterator<QGraphicsItem*> itItems(mFastAccessItems); QListIterator<QGraphicsItem*> itItems(mFastAccessItems);
@ -2408,6 +2408,7 @@ void UBGraphicsScene::setRenderingQuality(UBItem::RenderingQuality pRenderingQua
if (ubItem) if (ubItem)
{ {
ubItem->setRenderingQuality(pRenderingQuality); ubItem->setRenderingQuality(pRenderingQuality);
ubItem->setCacheBehavior(cacheBehavior);
} }
} }
} }

@ -295,7 +295,7 @@ class UBGraphicsScene: public UBCoreGraphicsScene, public UBItem
mViewState = pViewState; mViewState = pViewState;
} }
virtual void setRenderingQuality(UBItem::RenderingQuality pRenderingQuality); virtual void setRenderingQuality(UBItem::RenderingQuality pRenderingQuality, UBItem::CacheBehavior cacheBehavior);
QList<QUrl> relativeDependencies() const; QList<QUrl> relativeDependencies() const;

@ -51,6 +51,13 @@ class UBItem
RenderingQualityNormal = 0, RenderingQualityHigh RenderingQualityNormal = 0, RenderingQualityHigh
}; };
enum CacheBehavior
{
CacheNotAllowed,
CacheAllowed,
nbrCacheBehavior
};
virtual QUuid uuid() const virtual QUuid uuid() const
{ {
return mUuid; return mUuid;
@ -71,6 +78,11 @@ class UBItem
mRenderingQuality = pRenderingQuality; mRenderingQuality = pRenderingQuality;
} }
virtual void setCacheBehavior(CacheBehavior cacheBehavior)
{
mCacheBehavior = cacheBehavior;
}
virtual UBItem* deepCopy() const = 0; virtual UBItem* deepCopy() const = 0;
virtual void copyItemParameters(UBItem *copy) const = 0; virtual void copyItemParameters(UBItem *copy) const = 0;
@ -98,6 +110,7 @@ class UBItem
QUrl mSourceUrl; QUrl mSourceUrl;
CacheBehavior mCacheBehavior;
}; };
class UBGraphicsItem class UBGraphicsItem

@ -121,7 +121,7 @@ bool UBFileSystemUtils::copyFile(const QString &source, const QString &destinati
bool UBFileSystemUtils::copy(const QString &source, const QString &destination, bool overwrite) bool UBFileSystemUtils::copy(const QString &source, const QString &destination, bool overwrite)
{ {
if (QFileInfo(source).isDir()) { if (QFileInfo(source).isDir()) {
return copyDir(source, destination); return copyDir(source, destination, overwrite);
} else { } else {
return copyFile(source, destination, overwrite); return copyFile(source, destination, overwrite);
} }
@ -280,7 +280,7 @@ bool UBFileSystemUtils::deleteDir(const QString& pDirPath)
} }
bool UBFileSystemUtils::copyDir(const QString& pSourceDirPath, const QString& pTargetDirPath) bool UBFileSystemUtils::copyDir(const QString& pSourceDirPath, const QString& pTargetDirPath, bool overwite)
{ {
if (pSourceDirPath == "" || pSourceDirPath == "." || pSourceDirPath == "..") if (pSourceDirPath == "" || pSourceDirPath == "." || pSourceDirPath == "..")
return false; return false;
@ -304,8 +304,7 @@ bool UBFileSystemUtils::copyDir(const QString& pSourceDirPath, const QString& pT
} }
else else
{ {
QFile f(pSourceDirPath + "/" + dirContent.fileName()); successSoFar = copyFile(pSourceDirPath + "/" + dirContent.fileName(), pTargetDirPath + "/" + dirContent.fileName(), overwite);
successSoFar = f.copy(pTargetDirPath + "/" + dirContent.fileName());
} }
} }
else else

@ -62,7 +62,7 @@ class UBFileSystemUtils : public QObject
static bool deleteDir(const QString& pDirPath); static bool deleteDir(const QString& pDirPath);
static bool copyDir(const QString& pSourceDirPath, const QString& pTargetDirPath); static bool copyDir(const QString& pSourceDirPath, const QString& pTargetDirPath, bool overwrite = false);
static bool moveDir(const QString& pSourceDirPath, const QString& pTargetDirPath); static bool moveDir(const QString& pSourceDirPath, const QString& pTargetDirPath);

@ -358,7 +358,6 @@ void UBDocumentNavigator::mouseReleaseEvent(QMouseEvent *event)
void UBDocumentNavigator::keyPressEvent(QKeyEvent *event) void UBDocumentNavigator::keyPressEvent(QKeyEvent *event)
{ {
return;
UBBoardController* controller = UBApplication::boardController; UBBoardController* controller = UBApplication::boardController;
// send to the scene anyway // send to the scene anyway
QApplication::sendEvent (scene (), event); QApplication::sendEvent (scene (), event);

@ -39,13 +39,16 @@ GraphicsPDFItem::GraphicsPDFItem(PDFRenderer *renderer, int pageNumber, QGraphic
: QObject(0), QGraphicsItem(parentItem) : QObject(0), QGraphicsItem(parentItem)
, mRenderer(renderer) , mRenderer(renderer)
, mPageNumber(pageNumber) , mPageNumber(pageNumber)
, mIsCacheAllowed(true)
{ {
setCacheMode(QGraphicsItem::DeviceCoordinateCache); setCacheMode(QGraphicsItem::DeviceCoordinateCache);
mRenderer->attach(); mRenderer->attach();
connect(mRenderer, SIGNAL(signalUpdateParent()), this, SLOT(OnRequireUpdate()));
} }
GraphicsPDFItem::~GraphicsPDFItem() GraphicsPDFItem::~GraphicsPDFItem()
{ {
disconnect(mRenderer, SIGNAL(signalUpdateParent()), this, SLOT(OnRequireUpdate()));
mRenderer->detach(); mRenderer->detach();
} }
@ -70,8 +73,15 @@ void GraphicsPDFItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o
} }
if (option) if (option)
mRenderer->render(painter, mPageNumber, option->exposedRect); {
else mRenderer->render(painter, mPageNumber, mIsCacheAllowed, option->exposedRect);
} else
qWarning("GraphicsPDFItem::paint: option is null, ignoring painting"); qWarning("GraphicsPDFItem::paint: option is null, ignoring painting");
} }
void GraphicsPDFItem::OnRequireUpdate()
{
updateChild();
}

@ -51,10 +51,15 @@ class GraphicsPDFItem : public QObject, public QGraphicsItem
int pageNumber() const { return mPageNumber; } int pageNumber() const { return mPageNumber; }
QUuid fileUuid() const { return mRenderer->fileUuid(); } QUuid fileUuid() const { return mRenderer->fileUuid(); }
QByteArray fileData() const { return mRenderer->fileData(); } QByteArray fileData() const { return mRenderer->fileData(); }
void setCacheAllowed(bool const value) { mIsCacheAllowed = value; }
virtual void updateChild() = 0;
protected: protected:
PDFRenderer *mRenderer; PDFRenderer *mRenderer;
int mPageNumber; int mPageNumber;
bool mIsCacheAllowed;
private slots:
void OnRequireUpdate();
}; };
#endif // GRAPHICSPDFITEM_H #endif // GRAPHICSPDFITEM_H

@ -66,8 +66,7 @@ class PDFRenderer : public QObject
void setDPI(int desiredDPI) { this->dpiForRendering = desiredDPI; } void setDPI(int desiredDPI) { this->dpiForRendering = desiredDPI; }
public slots: virtual void render(QPainter *p, int pageNumber, bool const cacheAllowed, const QRectF &bounds = QRectF()) = 0;
virtual void render(QPainter *p, int pageNumber, const QRectF &bounds = QRectF()) = 0;
private: private:
QAtomicInt mRefCount; QAtomicInt mRefCount;

@ -152,7 +152,9 @@ void UBWebPluginPDFWidget::paintEvent(QPaintEvent *event)
painter.translate((geometry().width() - (pageSize.width() * mScale)) / 2, 0); painter.translate((geometry().width() - (pageSize.width() * mScale)) / 2, 0);
painter.scale(mScale, mScale); painter.scale(mScale, mScale);
mRenderer->render(&painter, mPageNumber, event->rect()); // Note: If you ever want to use the cache, you need to make sure the object 'update' is called
// when the processing is completed. See 'signalUpdateParent'.
mRenderer->render(&painter, mPageNumber, false /* Cache allowed */, event->rect());
painter.setPen(QPen(Qt::gray, 1)); painter.setPen(QPen(Qt::gray, 1));
painter.drawRect(0, 0, pageSize.width(), pageSize.height()); painter.drawRect(0, 0, pageSize.width(), pageSize.height());
} }

@ -43,26 +43,41 @@
QAtomicInt XPDFRenderer::sInstancesCount = 0; QAtomicInt XPDFRenderer::sInstancesCount = 0;
namespace constants{ namespace constants{
const double mode1_zoomFactor = 3.0; SplashColor paperColor = {0xFF, 0xFF, 0xFF}; // white
const double mode2_zoomFactorStage1 = 2.5;
const double mode2_zoomFactorStage2 = 5.0;
const double mode2_zoomFactorStage3 = 10.0;
} }
XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile)
mpSplashBitmapHistorical(nullptr), mSplashHistorical(nullptr), mDocument(nullptr) : m_pdfZoomMode(UBSettings::settings()->pdfZoomBehavior->get().toUInt())
, mpSplashBitmapHistorical(nullptr)
, mSplashHistorical(nullptr)
, mDocument(nullptr)
{ {
switch (UBSettings::settings()->pdfZoomBehavior->get().toUInt()) { switch (m_pdfZoomMode) {
case 0: // Render each time (historical initial implementation). case 0: // Render each time (historical initial implementation).
break; break;
case 1: // Render a single image, degradated quality when zoomed big. case 1: // Render a single image, degradated quality when zoomed big.
default: default:
m_pdfZoomCache.push_back(constants::mode1_zoomFactor); m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode1_zoomFactor);
break;
case 2: // Render three images, use downsampling, optimal quality all the time, slower.
m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode2_zoomFactorStage1);
m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode2_zoomFactorStage2);
m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode2_zoomFactorStage3);
break; break;
case 2: // Render three images, optimal quality all the time. case 3: // Do not downsample, minimal loss, faster. Not necessarily the expected result,
m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage1); // because a 'zoom factor 1' here does not correspond to a user choice 'zoom factor 1'.
m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage2); // The zoom requested is dependent on many factors, including the input pdf, the output screen resolution
m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage3); // and the zoom user choice. Thus, the 'mode3_zoomFactorStage1' might be fine on one screen, but
// fuzzy on another one.
m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode3_zoomFactorStage1);
m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode3_zoomFactorStage2);
break;
case 4: // Multithreaded, several steps, downsampled.
for (int i = 0; i < XPDFRendererZoomFactor::mode4_zoomFactorIterations; i++ )
{
double const zoomValue = XPDFRendererZoomFactor::mode4_zoomFactorStart+XPDFRendererZoomFactor::mode4_zoomFactorStepSquare*static_cast<double>(i*i);
m_pdfZoomCache.push_back(zoomValue);
}
break; break;
} }
@ -84,18 +99,19 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) :
mDocument = new PDFDoc(new GooString(filename.toLocal8Bit()), 0, 0, 0); // the filename GString is deleted on PDFDoc desctruction mDocument = new PDFDoc(new GooString(filename.toLocal8Bit()), 0, 0, 0); // the filename GString is deleted on PDFDoc desctruction
#endif #endif
sInstancesCount.ref(); sInstancesCount.ref();
connect(&m_cacheThread, SIGNAL(finished()), this, SLOT(OnThreadFinished()));
} }
XPDFRenderer::~XPDFRenderer() XPDFRenderer::~XPDFRenderer()
{ {
disconnect(&m_cacheThread, SIGNAL(finished()), this, SLOT(OnThreadFinished()));
m_cacheThread.cancelPending();
m_cacheThread.wait(); // Would crash if data deleted during processing.
for(int i = 0; i < m_pdfZoomCache.size(); i++) for(int i = 0; i < m_pdfZoomCache.size(); i++)
{ {
PdfZoomCacheData &cacheData = m_pdfZoomCache[i]; PdfZoomCacheData &cacheData = m_pdfZoomCache[i];
if(cacheData.splash != nullptr){ cacheData.cleanup();
cacheData.cachedImage = QImage(); // The 'cachedImage' uses a buffer from 'splash'.
delete cacheData.splash;
cacheData.splash = nullptr;
}
} }
if(mSplashHistorical) if(mSplashHistorical)
@ -209,10 +225,10 @@ QImage* XPDFRenderer::createPDFImageHistorical(int pageNumber, qreal xscale, qre
{ {
if (isValid()) if (isValid())
{ {
SplashColor paperColor = {0xFF, 0xFF, 0xFF}; // white
if(mSplashHistorical) if(mSplashHistorical)
delete mSplashHistorical; delete mSplashHistorical;
mSplashHistorical = new SplashOutputDev(splashModeRGB8, 1, false, paperColor);
mSplashHistorical = new SplashOutputDev(splashModeRGB8, 1, false, constants::paperColor);
#ifdef USE_XPDF #ifdef USE_XPDF
mSplashHistorical->startDoc(mDocument->getXRef()); mSplashHistorical->startDoc(mDocument->getXRef());
#else #else
@ -246,23 +262,55 @@ QImage* XPDFRenderer::createPDFImageHistorical(int pageNumber, qreal xscale, qre
return new QImage(mpSplashBitmapHistorical->getDataPtr(), mpSplashBitmapHistorical->getWidth(), mpSplashBitmapHistorical->getHeight(), mpSplashBitmapHistorical->getWidth() * 3, QImage::Format_RGB888); return new QImage(mpSplashBitmapHistorical->getDataPtr(), mpSplashBitmapHistorical->getWidth(), mpSplashBitmapHistorical->getHeight(), mpSplashBitmapHistorical->getWidth() * 3, QImage::Format_RGB888);
} }
void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds) void XPDFRenderer::OnThreadFinished()
{
emit signalUpdateParent();
if (m_cacheThread.isJobPending())
m_cacheThread.start();
}
void XPDFRenderer::render(QPainter *p, int pageNumber, bool const cacheAllowed, const QRectF &bounds)
{ {
//qDebug() << "render enter";
Q_UNUSED(bounds); Q_UNUSED(bounds);
if (isValid()) if (isValid())
{ {
if (m_pdfZoomCache.size() > 0) if (m_pdfZoomCache.size() > 0 && cacheAllowed)
{ {
qreal xscale = p->worldTransform().m11(); qreal xscale = p->worldTransform().m11();
qreal yscale = p->worldTransform().m22(); qreal yscale = p->worldTransform().m22();
Q_ASSERT(qFuzzyCompare(xscale, yscale)); // Zoom equal in all axes expected. Q_ASSERT(qFuzzyCompare(xscale, yscale)); // Zoom equal in all axes expected.
Q_ASSERT(xscale > 0.0); // Potential Div0 later if this assert fail. Q_ASSERT(xscale > 0.0); // Potential Div0 later if this assert fail.
qreal zoomRequested = xscale;
int zoomIndex = 0; int zoomIndex = 0;
if (m_pdfZoomMode == 3)
{
// Choose a zoom which is inferior or equivalent than the user choice (= minor loss, downscaling).
bool foundIndex = false;
for (zoomIndex = m_pdfZoomCache.size()-1; zoomIndex >= 0 && !foundIndex;)
{
if (zoomRequested >= m_pdfZoomCache[zoomIndex].ratio) {
foundIndex = true;
} else {
zoomIndex--;
}
}
if (!foundIndex) // Use the smallest one.
zoomIndex = 0;
if (zoomIndex == 0 && m_pdfZoomCache[zoomIndex].ratio != zoomRequested)
{
m_pdfZoomCache[zoomIndex].cleanup();
m_pdfZoomCache[zoomIndex] = PdfZoomCacheData(zoomRequested);
}
} else {
// Choose a zoom which is superior or equivalent than the user choice (= no loss, upscaling).
bool foundIndex = false; bool foundIndex = false;
for (; zoomIndex < m_pdfZoomCache.size() && !foundIndex;) for (; zoomIndex < m_pdfZoomCache.size() && !foundIndex;)
{ {
if (xscale <= m_pdfZoomCache[zoomIndex].ratio) { if (zoomRequested <= (m_pdfZoomCache[zoomIndex].ratio+0.1)) {
foundIndex = true; foundIndex = true;
} else { } else {
zoomIndex++; zoomIndex++;
@ -271,16 +319,63 @@ void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds)
if (!foundIndex) // Use the previous one. if (!foundIndex) // Use the previous one.
zoomIndex--; zoomIndex--;
}
QImage pdfImage = createPDFImageCached(pageNumber, m_pdfZoomCache[zoomIndex]);
qreal ratioExpected = m_pdfZoomCache[zoomIndex].ratio;
qreal ratioObtained = ratioExpected;
int const initialZoomIndex = zoomIndex;
if (pdfImage == QImage() && m_pdfZoomCache[zoomIndex].hasToBeProcessed)
{
// Try to temporarily fallback on a valid image, for a fuzzy or downsampled preview.
// The actual result will be updated after the processing.
bool isCurrent = true;
while (zoomIndex < m_pdfZoomCache.size()-1 && (m_pdfZoomCache[zoomIndex].cachedImage == QImage() || (m_pdfZoomCache[zoomIndex].cachedPageNumber != pageNumber && !isCurrent)))
{
zoomIndex = zoomIndex+1;
isCurrent = false;
}
while (zoomIndex > 0 && (m_pdfZoomCache[zoomIndex].cachedImage == QImage() || m_pdfZoomCache[zoomIndex].cachedPageNumber != pageNumber))
zoomIndex = zoomIndex-1;
ratioObtained = m_pdfZoomCache[zoomIndex].ratio;
}
if (m_pdfZoomCache[zoomIndex].cachedImage == QImage() || m_pdfZoomCache[zoomIndex].cachedPageNumber != pageNumber)
{
// No alternate image found. Build an alternate image in order to display some progress.
// Also make sure we fallback to the initial ratio request.
zoomIndex = initialZoomIndex;
qreal ratioDiff = m_pdfZoomCache[zoomIndex].ratio;
pdfImage = QImage(bounds.width()*ratioDiff, bounds.height()*ratioDiff, QImage::Format_RGB888);
pdfImage.fill("white");
QPainter painter(&pdfImage);
QString const text = tr("Processing...");
QFont font = painter.font();
if (font.pixelSize() != -1)
font.setPixelSize(ratioDiff*font.pixelSize());
else
font.setPointSizeF(ratioDiff*font.pointSizeF());
painter.setFont(font);
QFontMetrics textMetric(font, &pdfImage);
QSize textSize = textMetric.size(0, text);
painter.drawText((bounds.width()*ratioDiff-textSize.width())/2, (bounds.height()*ratioDiff-textSize.height())/2, text);
} else {
pdfImage = m_pdfZoomCache[zoomIndex].cachedImage;
}
QImage const &pdfImage = createPDFImageCached(pageNumber, m_pdfZoomCache[zoomIndex]);
QTransform savedTransform = p->worldTransform(); QTransform savedTransform = p->worldTransform();
double const ratioDifferenceBetweenWorldAndImage = 1.0/m_pdfZoomCache[zoomIndex].ratio; double const ratioDifferenceBetweenWorldAndImage = 1.0/m_pdfZoomCache[zoomIndex].ratio;
// The 'pdfImage' is rendered with a quality equal or superior. We adjust the 'transform' to zoom it // The 'pdfImage' is maybe rendered with a different quality than requested. We adjust the 'transform' to zoom it
// out the required ratio. // in or out of the required ratio.
QTransform newTransform = savedTransform.scale(ratioDifferenceBetweenWorldAndImage, ratioDifferenceBetweenWorldAndImage); QTransform newTransform = savedTransform.scale(ratioDifferenceBetweenWorldAndImage, ratioDifferenceBetweenWorldAndImage);
p->setWorldTransform(newTransform); p->setWorldTransform(newTransform);
/* qDebug() << "drawImage size=" << p->viewport() << "bounds" << bounds <<
"pdfImage" << pdfImage.size() << "savedTransform" << savedTransform.m11() <<
"ratioDiff" << ratioDifferenceBetweenWorldAndImage << "zoomRequested" << zoomRequested <<
"zoomIndex" << zoomIndex; */
p->drawImage(QPointF( mSliceX, mSliceY), pdfImage); p->drawImage(QPointF( mSliceX, mSliceY), pdfImage);
p->setWorldTransform(savedTransform); p->setWorldTransform(savedTransform);
@ -291,46 +386,89 @@ void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds)
QImage *pdfImage = createPDFImageHistorical(pageNumber, xscale, yscale, bounds); QImage *pdfImage = createPDFImageHistorical(pageNumber, xscale, yscale, bounds);
QTransform savedTransform = p->worldTransform(); QTransform savedTransform = p->worldTransform();
p->resetTransform(); p->resetTransform();
//qDebug() << "drawImage size=" << p->viewport() << "bounds" << bounds << "pdfImage" << pdfImage->size() << "savedTransform" << savedTransform.m11();
p->drawImage(QPointF(savedTransform.dx() + mSliceX, savedTransform.dy() + mSliceY), *pdfImage); p->drawImage(QPointF(savedTransform.dx() + mSliceX, savedTransform.dy() + mSliceY), *pdfImage);
p->setWorldTransform(savedTransform); p->setWorldTransform(savedTransform);
delete pdfImage; delete pdfImage;
} }
} }
//qDebug() << "render leave";
} }
QImage& XPDFRenderer::createPDFImageCached(int pageNumber, PdfZoomCacheData &cacheData) QImage& XPDFRenderer::createPDFImageCached(int pageNumber, PdfZoomCacheData &cacheData)
{ {
if (isValid()) if (isValid())
{ {
SplashColor paperColor = {0xFF, 0xFF, 0xFF}; // white if (cacheData.requireUpdateImage(pageNumber) && !cacheData.hasToBeProcessed)
if (cacheData.requireUpdateImage(pageNumber)) {
mSliceX = 0.;
mSliceY = 0.;
CacheThread::JobData jobData;
jobData.cacheData = &cacheData;
jobData.document = mDocument;
jobData.dpiForRendering = this->dpiForRendering;
jobData.pageNumber = pageNumber;
jobData.cacheData->hasToBeProcessed = true;
// Make sure we reset that image, because the data uses 'splash' buffer, which will be deallocated and
// reallocated when the job is started.
jobData.cacheData->cachedImage = QImage();
m_cacheThread.pushJob(jobData);
if (m_pdfZoomMode == 4)
{
// Start the job multithreaded. The item will be refreshed when the signal 'finished' is emitted.
m_cacheThread.start();
} else {
// Perform the job now. Note this will lock the GUI until the job is done.
m_cacheThread.run();
}
}
} else {
cacheData.cachedImage = QImage();
}
return cacheData.cachedImage;
}
void XPDFRenderer::CacheThread::run()
{ {
cacheData.prepareNewSplash(pageNumber, paperColor); m_jobMutex.lock();
CacheThread::JobData jobData = m_nextJob.first();
m_nextJob.pop_front();
/* qDebug() << "XPDFRenderer::CacheThread starting page" << jobData.pageNumber
<< "ratio" << jobData.cacheData->ratio; */
jobData.cacheData->prepareNewSplash(jobData.pageNumber, constants::paperColor);
#ifdef USE_XPDF #ifdef USE_XPDF
cacheData.splash->startDoc(mDocument->getXRef()); jobData.cacheData->splash->startDoc(jobData.document->getXRef());
#else #else
cacheData.splash->startDoc(mDocument); jobData.cacheData->splash->startDoc(jobData.document);
#endif #endif
m_jobMutex.unlock();
int rotation = 0; // in degrees (get it from the worldTransform if we want to support rotation) int rotation = 0; // in degrees (get it from the worldTransform if we want to support rotation)
bool useMediaBox = false; bool useMediaBox = false;
bool crop = true; bool crop = true;
bool printing = false; bool printing = false;
mSliceX = 0.;
mSliceY = 0.;
mDocument->displayPage(cacheData.splash, pageNumber, this->dpiForRendering * cacheData.ratio, this->dpiForRendering * cacheData.ratio, jobData.document->displayPage(jobData.cacheData->splash, jobData.pageNumber, jobData.dpiForRendering * jobData.cacheData->ratio,
jobData.dpiForRendering * jobData.cacheData->ratio,
rotation, useMediaBox, crop, printing); rotation, useMediaBox, crop, printing);
cacheData.splashBitmap = cacheData.splash->getBitmap();
}
m_jobMutex.lock();
jobData.cacheData->splashBitmap = jobData.cacheData->splash->getBitmap();
// Note this uses the 'cacheData.splash->getBitmap()->getDataPtr()' as data buffer. // Note this uses the 'cacheData.splash->getBitmap()->getDataPtr()' as data buffer.
cacheData.cachedImage = QImage(cacheData.splashBitmap->getDataPtr(), cacheData.splashBitmap->getWidth(), cacheData.splashBitmap->getHeight(), jobData.cacheData->cachedImage = QImage(jobData.cacheData->splashBitmap->getDataPtr(), jobData.cacheData->splashBitmap->getWidth(), jobData.cacheData->splashBitmap->getHeight(),
cacheData.splashBitmap->getWidth() * 3 /* bytesPerLine, 24 bits for RGB888, = 3 bytes */, jobData.cacheData->splashBitmap->getWidth() * 3 /* bytesPerLine, 24 bits for RGB888, = 3 bytes */,
QImage::Format_RGB888); QImage::Format_RGB888);
} else {
cacheData.cachedImage = QImage();
}
return cacheData.cachedImage; /* qDebug() << "XPDFRenderer::CacheThread completed page" << jobData.pageNumber
<< "ratio" << jobData.cacheData->ratio << "final size is" << jobData.cacheData->cachedImage.size(); */
jobData.cacheData->hasToBeProcessed = false;
m_jobMutex.unlock();
} }

@ -30,6 +30,8 @@
#ifndef XPDFRENDERER_H #ifndef XPDFRENDERER_H
#define XPDFRENDERER_H #define XPDFRENDERER_H
#include <QImage> #include <QImage>
#include <QThread>
#include <QMutexLocker>
#include "PDFRenderer.h" #include "PDFRenderer.h"
#include <splash/SplashBitmap.h> #include <splash/SplashBitmap.h>
@ -51,6 +53,20 @@
class PDFDoc; class PDFDoc;
namespace XPDFRendererZoomFactor
{
const double mode1_zoomFactor = 3.0;
const double mode2_zoomFactorStage1 = 2.5;
const double mode2_zoomFactorStage2 = 5.0;
const double mode2_zoomFactorStage3 = 10.0;
const double mode3_zoomFactorStage1 = 1.0;
const double mode3_zoomFactorStage2 = 3.0;
const double mode4_zoomFactorStart = .25;
const double mode4_zoomFactorStepSquare = .25;
const double mode4_zoomFactorIterations = 7;
}
class XPDFRenderer : public PDFRenderer class XPDFRenderer : public PDFRenderer
{ {
Q_OBJECT Q_OBJECT
@ -59,31 +75,31 @@ class XPDFRenderer : public PDFRenderer
XPDFRenderer(const QString &filename, bool importingFile = false); XPDFRenderer(const QString &filename, bool importingFile = false);
virtual ~XPDFRenderer(); virtual ~XPDFRenderer();
bool isValid() const; virtual bool isValid() const override;
virtual int pageCount() const override;
virtual int pageCount() const; virtual QSizeF pageSizeF(int pageNumber) const override;
virtual int pageRotation(int pageNumber) const override;
virtual QSizeF pageSizeF(int pageNumber) const; virtual QString title() const override;
virtual void render(QPainter *p, int pageNumber, const bool cacheAllowed, const QRectF &bounds = QRectF()) override;
virtual int pageRotation(int pageNumber) const;
virtual QString title() const; signals:
void signalUpdateParent();
public slots:
void render(QPainter *p, int pageNumber, const QRectF &bounds = QRectF());
private: private:
void init(); void init();
struct PdfZoomCacheData { struct PdfZoomCacheData {
PdfZoomCacheData() : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(1.0) {}; PdfZoomCacheData() : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(1.0), hasToBeProcessed(false) {};
PdfZoomCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio) {}; PdfZoomCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio), hasToBeProcessed(false) {};
~PdfZoomCacheData() {}; ~PdfZoomCacheData() {};
SplashBitmap* splashBitmap; SplashBitmap* splashBitmap;
//! Note: The 'cachedImage' uses a buffer from 'splash'. Make sure it is invalidated BEFORE 'splash' deallocation.
QImage cachedImage; QImage cachedImage;
int cachedPageNumber; int cachedPageNumber;
SplashOutputDev* splash; SplashOutputDev* splash;
double const ratio; double ratio;
bool hasToBeProcessed;
QList<QObject *> updateListAfterProcessing;
bool requireUpdateImage(int const pageNumber) const { bool requireUpdateImage(int const pageNumber) const {
return (pageNumber != cachedPageNumber) || (splash == nullptr); return (pageNumber != cachedPageNumber) || (splash == nullptr);
@ -99,15 +115,55 @@ class XPDFRenderer : public PDFRenderer
splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor);
cachedPageNumber = pageNumber; cachedPageNumber = pageNumber;
} }
void cleanup()
{
if(splash != nullptr){
cachedImage = QImage();
delete splash;
splash = nullptr;
}
}
};
//! Spawned when a pdf processing is required, when no matching image is found in cache.
class CacheThread : public QThread
{
public:
struct JobData {
PdfZoomCacheData* cacheData;
PDFDoc *document;
int pageNumber;
double dpiForRendering;
};
CacheThread() {}
~CacheThread() {}
void pushJob(JobData &jobData) {
QMutexLocker lock(&m_jobMutex);
m_nextJob.push_back(jobData);
}
virtual void run() override;
bool isJobPending() { QMutexLocker lock(&m_jobMutex); return m_nextJob.size() > 0; }
void cancelPending() { QMutexLocker lock(&m_jobMutex); m_nextJob.clear(); }
private:
QList<JobData> m_nextJob;
QMutex m_jobMutex;
}; };
CacheThread m_cacheThread;
QImage &createPDFImageCached(int pageNumber, PdfZoomCacheData &cacheData); QImage &createPDFImageCached(int pageNumber, PdfZoomCacheData &cacheData);
QImage* createPDFImageHistorical(int pageNumber, qreal xscale, qreal yscale, const QRectF &bounds); QImage* createPDFImageHistorical(int pageNumber, qreal xscale, qreal yscale, const QRectF &bounds);
// Used when 'ZoomBehavior == 1 or 2'. // Used when 'ZoomBehavior == 1, 2, 3 or 4'.
// =1 has only x3 zoom in cache (= loss if user zoom > 3.0). // =1 has only x3 zoom in cache (= loss if user zoom > 3.0).
// =2, has 2.5, 5 and 10 (= no loss, but a bit slower). // =2, has 2.5, 5 and 10 (= no loss, but a bit slower).
// =3, has 1.0, 2.5, 5 and 10, but downsampled instead of upsampled (= minor quality loss, a bit faster).
// =4, multithreaded, multiple level of zoom.
QVector<PdfZoomCacheData> m_pdfZoomCache; QVector<PdfZoomCacheData> m_pdfZoomCache;
int const m_pdfZoomMode;
// Used when 'ZoomBehavior == 0' (no cache). // Used when 'ZoomBehavior == 0' (no cache).
SplashBitmap* mpSplashBitmapHistorical; SplashBitmap* mpSplashBitmapHistorical;
@ -118,6 +174,9 @@ class XPDFRenderer : public PDFRenderer
static QAtomicInt sInstancesCount; static QAtomicInt sInstancesCount;
qreal mSliceX; qreal mSliceX;
qreal mSliceY; qreal mSliceY;
private slots:
void OnThreadFinished();
}; };
#endif // XPDFRENDERER_H #endif // XPDFRENDERER_H

Loading…
Cancel
Save