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"