File indexing completed on 2024-04-21 04:33:59
0001 /* 0002 SPDX-FileCopyrightText: 2007 Aurélien Gâteau 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "svgpart.h" 0008 0009 // part 0010 #include "svgbrowserextension.h" 0011 #include "svgview.h" 0012 // KF 0013 #include <KActionCollection> 0014 #include <KLocalizedString> 0015 #include <KPluginFactory> 0016 #include <KPluginMetaData> 0017 #include <KStandardAction> 0018 // Qt 0019 #include <QGraphicsScene> 0020 #include <QGraphicsSvgItem> 0021 #include <QMimeDatabase> 0022 #include <QSvgRenderer> 0023 #include <QTimer> 0024 0025 // Factory Code 0026 K_PLUGIN_FACTORY_WITH_JSON(SvgPartFactory, "svgpart.json", registerPlugin<SvgPart>();) 0027 0028 SvgPart::SvgPart(QWidget *parentWidget, QObject *parent, const KPluginMetaData &metaData, const QVariantList &) 0029 : KParts::ReadOnlyPart(parent, metaData) 0030 , mItem(nullptr) 0031 , m_browserExtension(new SvgBrowserExtension(this)) 0032 { 0033 mRenderer = new QSvgRenderer(this); 0034 mScene = new QGraphicsScene(this); 0035 mView = new SvgView(mScene, parentWidget); 0036 setWidget(mView); 0037 0038 KStandardAction::actualSize(mView, &SvgView::zoomActualSize, actionCollection()); 0039 KStandardAction::zoomIn(mView, &SvgView::zoomIn, actionCollection()); 0040 KStandardAction::zoomOut(mView, &SvgView::zoomOut, actionCollection()); 0041 setXMLFile(QStringLiteral("svgpart.rc")); 0042 } 0043 0044 bool SvgPart::openUrl(const QUrl &url) 0045 { 0046 mCloseUrlFromOpen = true; 0047 0048 const auto success = KParts::ReadOnlyPart::openUrl(url); 0049 0050 mCloseUrlFromOpen = false; 0051 0052 return success; 0053 } 0054 0055 bool SvgPart::openFile() 0056 { 0057 if (!mRenderer->load(localFilePath())) { 0058 return false; 0059 } 0060 0061 createViewForDocument(); 0062 0063 return true; 0064 } 0065 0066 bool SvgPart::doOpenStream(const QString &mimeType) 0067 { 0068 auto mime = QMimeDatabase().mimeTypeForName(mimeType); 0069 if (!mime.inherits(QStringLiteral("image/svg+xml")) && !mime.inherits(QStringLiteral("image/svg+xml-compressed"))) { 0070 return false; 0071 } 0072 0073 mStreamedData.clear(); 0074 0075 return true; 0076 } 0077 0078 bool SvgPart::doWriteStream(const QByteArray &data) 0079 { 0080 mStreamedData.append(data); 0081 return true; 0082 } 0083 0084 bool SvgPart::doCloseStream() 0085 { 0086 // too bad QSvgRenderer supports QXmlStreamReader, but not its incremental parsing 0087 if (!mRenderer->load(mStreamedData)) { 0088 mStreamedData.clear(); 0089 return false; 0090 } 0091 0092 mStreamedData.clear(); 0093 0094 createViewForDocument(); 0095 0096 return true; 0097 } 0098 0099 bool SvgPart::closeUrl() 0100 { 0101 // protect against repeated call if already closed 0102 const auto currentUrl = url(); 0103 // remember old view state for a possible reload from same url 0104 if (currentUrl.isValid()) { 0105 mPreviousUrl = currentUrl; 0106 0107 mPreviousZoom = zoom(); 0108 mPreviousHorizontalScrollPosition = mView->horizontalScrollPosition(); 0109 mPreviousVerticalScrollPosition = mView->verticalScrollPosition(); 0110 } 0111 0112 mView->resetTransform(); 0113 // cannot reset the rect completely, as a null QRectF is ignored 0114 // so at least just a 1 pixel square one 0115 mScene->setSceneRect(QRectF(0, 0, 1, 1)); 0116 0117 delete mItem; 0118 mItem = nullptr; 0119 0120 // reset arguments 0121 if (!mCloseUrlFromOpen) { 0122 mHasExtendedRestoreArguments = false; 0123 } 0124 0125 return KParts::ReadOnlyPart::closeUrl(); 0126 } 0127 0128 void SvgPart::createViewForDocument() 0129 { 0130 mItem = new QGraphicsSvgItem(); 0131 mItem->setSharedRenderer(mRenderer); 0132 mScene->addItem(mItem); 0133 // we reuse the scene, whose scenerect though is not properly resetable, so ensure up-to-date one 0134 mScene->setSceneRect(mItem->boundingRect()); 0135 0136 // ideally the viewstate would be restored here, but at this point in time 0137 // the view has not yet been updated to the scene and is a wrong size, 0138 // so setting the scrollbars etc now will not have any effect 0139 // TODO: this results in flickering, needs to find a better way to hook into 0140 // updating of view state to new content before the first rendering is done 0141 QTimer::singleShot(0, this, &SvgPart::delayedRestoreViewState); 0142 } 0143 0144 void SvgPart::delayedRestoreViewState() 0145 { 0146 // arguments set by caller or restore method 0147 KParts::OpenUrlArguments args(arguments()); 0148 qreal zoomValue = mHasExtendedRestoreArguments ? mRestoreZoom : 1.0; 0149 0150 // reloading same url? 0151 // we can't tell if caller has explicitly set xOffset/yOffset of OpenUrlArguments 0152 // so in case of same url we just assume a reload and ignore the OpenUrlArguments xOffset/yOffset 0153 if (!mHasExtendedRestoreArguments && (url() == mPreviousUrl)) { 0154 // restore last view state instead 0155 zoomValue = mPreviousZoom; 0156 args.setXOffset(mPreviousHorizontalScrollPosition); 0157 args.setYOffset(mPreviousVerticalScrollPosition); 0158 } 0159 0160 // now restore view state 0161 mView->setZoom(zoomValue); 0162 0163 mView->setHorizontalScrollPosition(args.xOffset()); 0164 mView->setVerticalScrollPosition(args.yOffset()); 0165 } 0166 0167 void SvgPart::setExtendedRestoreArguments(qreal zoom) 0168 { 0169 mHasExtendedRestoreArguments = true; 0170 mRestoreZoom = zoom; 0171 } 0172 0173 0174 qreal SvgPart::zoom() const 0175 { 0176 return mView->zoom(); 0177 } 0178 0179 int SvgPart::horizontalScrollPosition() const 0180 { 0181 return mView->horizontalScrollPosition(); 0182 } 0183 0184 int SvgPart::verticalScrollPosition() const 0185 { 0186 return mView->verticalScrollPosition(); 0187 } 0188 0189 #include "moc_svgpart.cpp" 0190 #include "svgpart.moc"