mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Issue #54: Bookmarks page - model, bookmarks manager
This commit is contained in:
		| @@ -69,6 +69,10 @@ add_library(Pdf4QtViewer SHARED | ||||
|     pdfcreatebitonaldocumentdialog.cpp | ||||
|     pdfcreatebitonaldocumentdialog.h | ||||
|     pdf4qtviewer.qrc | ||||
|     pdfbookmarkmanager.h | ||||
|     pdfbookmarkmanager.cpp | ||||
|     pdfbookmarkui.h | ||||
|     pdfbookmarkui.cpp | ||||
| ) | ||||
|  | ||||
| add_compile_definitions(QT_INSTALL_DIRECTORY="${QT6_INSTALL_PREFIX}") | ||||
|   | ||||
| @@ -104,5 +104,6 @@ | ||||
|         <file>resources/sidebar-thumbnails.svg</file> | ||||
|         <file>resources/sidebar-visibility.svg</file> | ||||
|         <file>resources/outline.svg</file> | ||||
|         <file>resources/sidebar-favourites.svg</file> | ||||
|     </qresource> | ||||
| </RCC> | ||||
|   | ||||
							
								
								
									
										272
									
								
								Pdf4QtViewer/pdfbookmarkmanager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								Pdf4QtViewer/pdfbookmarkmanager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,272 @@ | ||||
| //    Copyright (C) 2023 Jakub Melka | ||||
| // | ||||
| //    This file is part of PDF4QT. | ||||
| // | ||||
| //    PDF4QT is free software: you can redistribute it and/or modify | ||||
| //    it under the terms of the GNU Lesser General Public License as published by | ||||
| //    the Free Software Foundation, either version 3 of the License, or | ||||
| //    with the written consent of the copyright owner, any later version. | ||||
| // | ||||
| //    PDF4QT is distributed in the hope that it will be useful, | ||||
| //    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| //    GNU Lesser General Public License for more details. | ||||
| // | ||||
| //    You should have received a copy of the GNU Lesser General Public License | ||||
| //    along with PDF4QT.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| #include "pdfbookmarkmanager.h" | ||||
| #include "pdfaction.h" | ||||
|  | ||||
| #include <QJsonObject> | ||||
| #include <QJsonArray> | ||||
| #include <QJsonDocument> | ||||
|  | ||||
| namespace pdfviewer | ||||
| { | ||||
|  | ||||
| class PDFBookmarkManagerHelper | ||||
| { | ||||
| public: | ||||
|     constexpr PDFBookmarkManagerHelper() = delete; | ||||
|  | ||||
|     static QJsonObject convertBookmarkToJson(const PDFBookmarkManager::Bookmark& bookmark) | ||||
|     { | ||||
|         QJsonObject json; | ||||
|         json["isAuto"] = bookmark.isAuto; | ||||
|         json["name"] = bookmark.name; | ||||
|         json["pageIndex"] = bookmark.pageIndex; | ||||
|         return json; | ||||
|     } | ||||
|  | ||||
|     static PDFBookmarkManager::Bookmark convertJsonToBookmark(const QJsonObject& json) | ||||
|     { | ||||
|         PDFBookmarkManager::Bookmark bookmark; | ||||
|         bookmark.isAuto = json["isAuto"].toBool(); | ||||
|         bookmark.name = json["name"].toString(); | ||||
|         bookmark.pageIndex = json["pageIndex"].toInt(); | ||||
|         return bookmark; | ||||
|     } | ||||
|  | ||||
|     static QJsonObject convertBookmarksToJson(const PDFBookmarkManager::Bookmarks& bookmarks) | ||||
|     { | ||||
|         QJsonArray jsonArray; | ||||
|  | ||||
|         for (const auto& bookmark : bookmarks.bookmarks) | ||||
|         { | ||||
|             jsonArray.append(convertBookmarkToJson(bookmark)); | ||||
|         } | ||||
|  | ||||
|         QJsonObject jsonObject; | ||||
|         jsonObject["bookmarks"] = jsonArray; | ||||
|         return jsonObject; | ||||
|     } | ||||
|  | ||||
|     static PDFBookmarkManager::Bookmarks convertBookmarksFromJson(const QJsonObject& object) | ||||
|     { | ||||
|         PDFBookmarkManager::Bookmarks bookmarks; | ||||
|  | ||||
|         QJsonArray jsonArray = object["bookmarks"].toArray(); | ||||
|  | ||||
|         for (const auto& jsonValue : jsonArray) | ||||
|         { | ||||
|             bookmarks.bookmarks.push_back(convertJsonToBookmark(jsonValue.toObject())); | ||||
|         } | ||||
|  | ||||
|         return bookmarks; | ||||
|     } | ||||
|  | ||||
|     static QJsonDocument convertBookmarksMapToJsonDocument(const std::map<QString, PDFBookmarkManager::Bookmarks>& bookmarksMap) | ||||
|     { | ||||
|         QJsonObject mainObject; | ||||
|         for (const auto& pair : bookmarksMap) | ||||
|         { | ||||
|             mainObject[pair.first] = convertBookmarksToJson(pair.second); | ||||
|         } | ||||
|         return QJsonDocument(mainObject); | ||||
|     } | ||||
|  | ||||
|     static std::map<QString, PDFBookmarkManager::Bookmarks> convertBookmarksMapFromJsonDocument(const QJsonDocument &doc) | ||||
|     { | ||||
|         std::map<QString, PDFBookmarkManager::Bookmarks> container; | ||||
|         QJsonObject mainObject = doc.object(); | ||||
|  | ||||
|         for (auto it = mainObject.begin(); it != mainObject.end(); ++it) | ||||
|         { | ||||
|             container[it.key()] = convertBookmarksFromJson(it.value().toObject()); | ||||
|         } | ||||
|  | ||||
|         return container; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| PDFBookmarkManager::PDFBookmarkManager(QObject* parent) : | ||||
|     BaseClass(parent) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void PDFBookmarkManager::setDocument(const pdf::PDFModifiedDocument& document) | ||||
| { | ||||
|     Q_EMIT bookmarksAboutToBeChanged(); | ||||
|  | ||||
|     const bool init = !m_document; | ||||
|     m_document = document.getDocument(); | ||||
|  | ||||
|     QString key; | ||||
|  | ||||
|     if (document.hasPreserveView() && m_document) | ||||
|     { | ||||
|         // Pass the key | ||||
|         key = QString::fromLatin1(m_document->getSourceDataHash().toHex()); | ||||
|  | ||||
|         if (m_bookmarks.count(m_currentKey) && m_currentKey != key) | ||||
|         { | ||||
|             m_bookmarks[key] = m_bookmarks[m_currentKey]; | ||||
|             m_bookmarks.erase(m_currentKey); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (init && m_document) | ||||
|     { | ||||
|         key = QString::fromLatin1(m_document->getSourceDataHash().toHex()); | ||||
|     } | ||||
|  | ||||
|     if (key.isEmpty()) | ||||
|     { | ||||
|         key = "generic"; | ||||
|     } | ||||
|  | ||||
|     m_currentKey = key; | ||||
|  | ||||
|     if (document.hasReset() && !document.hasPreserveView()) | ||||
|     { | ||||
|         regenerateAutoBookmarks(); | ||||
|     } | ||||
|  | ||||
|     Q_EMIT bookmarksChanged(); | ||||
| } | ||||
|  | ||||
| void PDFBookmarkManager::saveToFile(QString fileName) | ||||
| { | ||||
|     QJsonDocument doc = PDFBookmarkManagerHelper::convertBookmarksMapToJsonDocument(m_bookmarks); | ||||
|  | ||||
|     // Příklad zápisu do souboru | ||||
|     QFile file(fileName); | ||||
|     if (file.open(QIODevice::WriteOnly)) | ||||
|     { | ||||
|         file.write(doc.toJson()); | ||||
|         file.close(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool PDFBookmarkManager::loadFromFile(QString fileName) | ||||
| { | ||||
|     QFile file(fileName); | ||||
|     if (file.open(QIODevice::ReadOnly)) | ||||
|     { | ||||
|         QJsonDocument loadedDoc = QJsonDocument::fromJson(file.readAll()); | ||||
|         file.close(); | ||||
|  | ||||
|         m_bookmarks = PDFBookmarkManagerHelper::convertBookmarksMapFromJsonDocument(loadedDoc); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int PDFBookmarkManager::getBookmarkCount() const | ||||
| { | ||||
|     if (m_bookmarks.count(m_currentKey)) | ||||
|     { | ||||
|         return m_bookmarks.at(m_currentKey).bookmarks.size(); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| PDFBookmarkManager::Bookmark PDFBookmarkManager::getBookmark(int index) const | ||||
| { | ||||
|     if (m_bookmarks.count(m_currentKey)) | ||||
|     { | ||||
|         return m_bookmarks.at(m_currentKey).bookmarks.at(index); | ||||
|     } | ||||
|  | ||||
|     return Bookmark(); | ||||
| } | ||||
|  | ||||
| void PDFBookmarkManager::regenerateAutoBookmarks() | ||||
| { | ||||
|     if (!m_document) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Create bookmarks for all main chapters | ||||
|     Bookmarks& bookmarks = m_bookmarks[m_currentKey]; | ||||
|  | ||||
|     for (auto it = bookmarks.bookmarks.begin(); it != bookmarks.bookmarks.end();) | ||||
|     { | ||||
|         if (it->isAuto) | ||||
|         { | ||||
|             it = bookmarks.bookmarks.erase(it); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ++it; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (auto outlineRoot = m_document->getCatalog()->getOutlineRootPtr()) | ||||
|     { | ||||
|         size_t childCount = outlineRoot->getChildCount(); | ||||
|         for (size_t i = 0; i < childCount; ++i) | ||||
|         { | ||||
|             Bookmark bookmark; | ||||
|             bookmark.isAuto = true; | ||||
|             bookmark.pageIndex = pdf::PDFCatalog::INVALID_PAGE_INDEX; | ||||
|  | ||||
|             const pdf::PDFOutlineItem* child = outlineRoot->getChild(i); | ||||
|             const pdf::PDFAction* action = child->getAction(); | ||||
|  | ||||
|             if (action) | ||||
|             { | ||||
|                 for (const pdf::PDFAction* currentAction : action->getActionList()) | ||||
|                 { | ||||
|                     if (currentAction->getType() != pdf::ActionType::GoTo) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     const pdf::PDFActionGoTo* typedAction = dynamic_cast<const pdf::PDFActionGoTo*>(currentAction); | ||||
|                     pdf::PDFDestination destination = typedAction->getDestination(); | ||||
|                     if (destination.getDestinationType() == pdf::DestinationType::Named) | ||||
|                     { | ||||
|                         if (const pdf::PDFDestination* targetDestination = m_document->getCatalog()->getNamedDestination(destination.getName())) | ||||
|                         { | ||||
|                             destination = *targetDestination; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (destination.getDestinationType() != pdf::DestinationType::Invalid && | ||||
|                         destination.getPageReference() != pdf::PDFObjectReference()) | ||||
|                     { | ||||
|                         const size_t pageIndex = m_document->getCatalog()->getPageIndexFromPageReference(destination.getPageReference()); | ||||
|                         if (pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX) | ||||
|                         { | ||||
|                             bookmark.pageIndex = pageIndex; | ||||
|                             bookmark.name = child->getTitle(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (bookmark.pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX) | ||||
|             { | ||||
|                 bookmarks.bookmarks.emplace_back(std::move(bookmark)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| }   // namespace pdf | ||||
							
								
								
									
										74
									
								
								Pdf4QtViewer/pdfbookmarkmanager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								Pdf4QtViewer/pdfbookmarkmanager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| //    Copyright (C) 2023 Jakub Melka | ||||
| // | ||||
| //    This file is part of PDF4QT. | ||||
| // | ||||
| //    PDF4QT is free software: you can redistribute it and/or modify | ||||
| //    it under the terms of the GNU Lesser General Public License as published by | ||||
| //    the Free Software Foundation, either version 3 of the License, or | ||||
| //    with the written consent of the copyright owner, any later version. | ||||
| // | ||||
| //    PDF4QT is distributed in the hope that it will be useful, | ||||
| //    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| //    GNU Lesser General Public License for more details. | ||||
| // | ||||
| //    You should have received a copy of the GNU Lesser General Public License | ||||
| //    along with PDF4QT.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| #ifndef PDFBOOKMARKMANAGER_H | ||||
| #define PDFBOOKMARKMANAGER_H | ||||
|  | ||||
| #include "pdfdocument.h" | ||||
|  | ||||
| #include <QObject> | ||||
|  | ||||
| namespace pdfviewer | ||||
| { | ||||
|  | ||||
| class PDFBookmarkManager : public QObject | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| private: | ||||
|     using BaseClass = QObject; | ||||
|  | ||||
| public: | ||||
|     PDFBookmarkManager(QObject* parent); | ||||
|  | ||||
|     void setDocument(const pdf::PDFModifiedDocument& document); | ||||
|  | ||||
|     void saveToFile(QString fileName); | ||||
|     bool loadFromFile(QString fileName); | ||||
|  | ||||
|     struct Bookmark | ||||
|     { | ||||
|         bool isAuto = false; | ||||
|         QString name; | ||||
|         pdf::PDFInteger pageIndex = -1; | ||||
|     }; | ||||
|  | ||||
|     int getBookmarkCount() const; | ||||
|     Bookmark getBookmark(int index) const; | ||||
|  | ||||
| signals: | ||||
|     void bookmarksAboutToBeChanged(); | ||||
|     void bookmarksChanged(); | ||||
|  | ||||
| private: | ||||
|     friend class PDFBookmarkManagerHelper; | ||||
|  | ||||
|     void regenerateAutoBookmarks(); | ||||
|  | ||||
|     struct Bookmarks | ||||
|     { | ||||
|         std::vector<Bookmark> bookmarks; | ||||
|     }; | ||||
|  | ||||
|     pdf::PDFDocument* m_document; | ||||
|     QString m_currentKey; | ||||
|     std::map<QString, Bookmarks> m_bookmarks; | ||||
| }; | ||||
|  | ||||
| }   // namespace pdf | ||||
|  | ||||
| #endif // PDFBOOKMARKMANAGER_H | ||||
							
								
								
									
										173
									
								
								Pdf4QtViewer/pdfbookmarkui.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								Pdf4QtViewer/pdfbookmarkui.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| //    Copyright (C) 2023 Jakub Melka | ||||
| // | ||||
| //    This file is part of PDF4QT. | ||||
| // | ||||
| //    PDF4QT is free software: you can redistribute it and/or modify | ||||
| //    it under the terms of the GNU Lesser General Public License as published by | ||||
| //    the Free Software Foundation, either version 3 of the License, or | ||||
| //    with the written consent of the copyright owner, any later version. | ||||
| // | ||||
| //    PDF4QT is distributed in the hope that it will be useful, | ||||
| //    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| //    GNU Lesser General Public License for more details. | ||||
| // | ||||
| //    You should have received a copy of the GNU Lesser General Public License | ||||
| //    along with PDF4QT.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| #include "pdfbookmarkui.h" | ||||
| #include "pdfwidgetutils.h" | ||||
|  | ||||
| #include <QPainter> | ||||
| #include <QPainterPath> | ||||
|  | ||||
| namespace pdfviewer | ||||
| { | ||||
|  | ||||
| PDFBookmarkItemModel::PDFBookmarkItemModel(PDFBookmarkManager* bookmarkManager, QObject* parent) : | ||||
|     BaseClass(parent), | ||||
|     m_bookmarkManager(bookmarkManager) | ||||
| { | ||||
|     connect(m_bookmarkManager, &PDFBookmarkManager::bookmarksAboutToBeChanged, this, &PDFBookmarkItemModel::beginResetModel); | ||||
|     connect(m_bookmarkManager, &PDFBookmarkManager::bookmarksChanged, this, &PDFBookmarkItemModel::endResetModel); | ||||
| } | ||||
|  | ||||
| QModelIndex PDFBookmarkItemModel::index(int row, int column, const QModelIndex& parent) const | ||||
| { | ||||
|     return createIndex(row, column, nullptr); | ||||
| } | ||||
|  | ||||
| QModelIndex PDFBookmarkItemModel::parent(const QModelIndex& child) const | ||||
| { | ||||
|     return QModelIndex(); | ||||
| } | ||||
|  | ||||
| int PDFBookmarkItemModel::rowCount(const QModelIndex& parent) const | ||||
| { | ||||
|     if (parent.isValid()) | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return m_bookmarkManager ? m_bookmarkManager->getBookmarkCount() : 0; | ||||
| } | ||||
|  | ||||
| int PDFBookmarkItemModel::columnCount(const QModelIndex& parent) const | ||||
| { | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| QVariant PDFBookmarkItemModel::data(const QModelIndex& index, int role) const | ||||
| { | ||||
|     if (role == Qt::DisplayRole) | ||||
|     { | ||||
|         return m_bookmarkManager->getBookmark(index.row()).name; | ||||
|     } | ||||
|  | ||||
|     return QVariant(); | ||||
| } | ||||
|  | ||||
| PDFBookmarkItemDelegate::PDFBookmarkItemDelegate(PDFBookmarkManager* bookmarkManager, QObject* parent) : | ||||
|     BaseClass(parent), | ||||
|     m_bookmarkManager(bookmarkManager) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void PDFBookmarkItemDelegate::paint(QPainter* painter, | ||||
|                                     const QStyleOptionViewItem& option, | ||||
|                                     const QModelIndex& index) const | ||||
| { | ||||
|     QStyleOptionViewItem options = option; | ||||
|     initStyleOption(&options, index); | ||||
|  | ||||
|     PDFBookmarkManager::Bookmark bookmark = m_bookmarkManager->getBookmark(index.row()); | ||||
|  | ||||
|     options.text = QString(); | ||||
|     options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); | ||||
|  | ||||
|     const int margin = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, MARGIN); | ||||
|     const int iconSize = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, ICON_SIZE); | ||||
|  | ||||
|     QRect rect = options.rect; | ||||
|     rect.marginsRemoved(QMargins(margin, margin, margin, margin)); | ||||
|  | ||||
|     QRect iconRect = rect; | ||||
|     iconRect.setWidth(iconSize); | ||||
|     iconRect.setHeight(iconSize); | ||||
|     iconRect.moveCenter(QPoint(rect.left() + iconSize / 2, rect.center().y())); | ||||
|     drawStar(*painter, iconRect.center(), iconRect.width() * 0.5, QColor(64, 64, 192)); | ||||
|  | ||||
|     QRect textRect = rect; | ||||
|     textRect.setLeft(iconRect.right() + margin); | ||||
|  | ||||
|     textRect.setHeight(options.fontMetrics.lineSpacing()); | ||||
|  | ||||
|     QFont font = options.font; | ||||
|     font.setBold(true); | ||||
|  | ||||
|     painter->setFont(font); | ||||
|     painter->drawText(textRect, getPageText(bookmark)); | ||||
|  | ||||
|     textRect.translate(0, textRect.height()); | ||||
|  | ||||
|     painter->setFont(options.font); | ||||
|     painter->drawText(textRect, bookmark.name); | ||||
| } | ||||
|  | ||||
| QSize PDFBookmarkItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const | ||||
| { | ||||
|     PDFBookmarkManager::Bookmark bookmark = m_bookmarkManager->getBookmark(index.row()); | ||||
|  | ||||
|     const int textWidthLine1 = option.fontMetrics.horizontalAdvance(getPageText(bookmark)); | ||||
|     const int textWidthLine2 = option.fontMetrics.horizontalAdvance(option.text); | ||||
|     const int textWidth = qMax(textWidthLine1, textWidthLine2); | ||||
|     const int textHeight = option.fontMetrics.lineSpacing() * 2; | ||||
|  | ||||
|     const int margin = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, MARGIN); | ||||
|     const int iconSize = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, ICON_SIZE); | ||||
|  | ||||
|     const int requiredWidth = 3 * margin + iconSize + textWidth; | ||||
|     const int requiredHeight = 2 * margin + qMax(iconSize, textHeight); | ||||
|  | ||||
|     return QSize(requiredWidth, requiredHeight); | ||||
| } | ||||
|  | ||||
| void PDFBookmarkItemDelegate::drawStar(QPainter& painter, const QPointF& center, double size, const QColor& color) const | ||||
| { | ||||
|     painter.save(); | ||||
|  | ||||
|     painter.setPen(Qt::NoPen); | ||||
|     painter.setBrush(color); | ||||
|  | ||||
|     QPainterPath path; | ||||
|     double angle = M_PI / 5; | ||||
|  | ||||
|     for (int i = 0; i < 10; ++i) | ||||
|     { | ||||
|         double radius = (i % 2 == 0) ? size : size / 2.5; | ||||
|         QPointF point(radius * cos(i * angle), radius * sin(i * angle)); | ||||
|         point += center; | ||||
|  | ||||
|         if (i == 0) | ||||
|         { | ||||
|             path.moveTo(point); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             path.lineTo(point); | ||||
|         } | ||||
|     } | ||||
|     path.closeSubpath(); | ||||
|  | ||||
|     painter.drawPath(path); | ||||
|  | ||||
|     painter.restore(); | ||||
| } | ||||
|  | ||||
| QString PDFBookmarkItemDelegate::getPageText(const PDFBookmarkManager::Bookmark& bookmark) const | ||||
| { | ||||
|     return tr("Page %1").arg(bookmark.pageIndex + 1); | ||||
| } | ||||
|  | ||||
| }   // namespace pdfviewer | ||||
							
								
								
									
										76
									
								
								Pdf4QtViewer/pdfbookmarkui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								Pdf4QtViewer/pdfbookmarkui.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| //    Copyright (C) 2023 Jakub Melka | ||||
| // | ||||
| //    This file is part of PDF4QT. | ||||
| // | ||||
| //    PDF4QT is free software: you can redistribute it and/or modify | ||||
| //    it under the terms of the GNU Lesser General Public License as published by | ||||
| //    the Free Software Foundation, either version 3 of the License, or | ||||
| //    with the written consent of the copyright owner, any later version. | ||||
| // | ||||
| //    PDF4QT is distributed in the hope that it will be useful, | ||||
| //    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| //    GNU Lesser General Public License for more details. | ||||
| // | ||||
| //    You should have received a copy of the GNU Lesser General Public License | ||||
| //    along with PDF4QT.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| #ifndef PDFBOOKMARKUI_H | ||||
| #define PDFBOOKMARKUI_H | ||||
|  | ||||
| #include "pdfviewerglobal.h" | ||||
| #include "pdfbookmarkmanager.h" | ||||
|  | ||||
| #include <QStyledItemDelegate> | ||||
| #include <QAbstractItemModel> | ||||
|  | ||||
| namespace pdfviewer | ||||
| { | ||||
|  | ||||
| class PDFBookmarkItemModel : public QAbstractItemModel | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| private: | ||||
|     using BaseClass = QAbstractItemModel; | ||||
|  | ||||
| public: | ||||
|     PDFBookmarkItemModel(PDFBookmarkManager* bookmarkManager, QObject* parent); | ||||
|  | ||||
|     virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; | ||||
|     virtual QModelIndex parent(const QModelIndex& child) const override; | ||||
|     virtual int rowCount(const QModelIndex& parent) const override; | ||||
|     virtual int columnCount(const QModelIndex& parent) const override; | ||||
|     virtual QVariant data(const QModelIndex& index, int role) const override; | ||||
|  | ||||
| private: | ||||
|     PDFBookmarkManager* m_bookmarkManager = nullptr; | ||||
| }; | ||||
|  | ||||
| class PDFBookmarkItemDelegate : public QStyledItemDelegate | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| private: | ||||
|     using BaseClass = QStyledItemDelegate; | ||||
|  | ||||
| public: | ||||
|     PDFBookmarkItemDelegate(PDFBookmarkManager* bookmarkManager, QObject* parent); | ||||
|  | ||||
|     virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; | ||||
|     virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; | ||||
|  | ||||
| private: | ||||
|     static constexpr int MARGIN = 6; | ||||
|     static constexpr int ICON_SIZE = 32; | ||||
|  | ||||
|     void drawStar(QPainter& painter, const QPointF& center, double size, const QColor& color) const; | ||||
|  | ||||
|     QString getPageText(const PDFBookmarkManager::Bookmark& bookmark) const; | ||||
|  | ||||
|     PDFBookmarkManager* m_bookmarkManager = nullptr; | ||||
| }; | ||||
|  | ||||
| }   // namespace pdfviewer | ||||
|  | ||||
| #endif // PDFBOOKMARKUI_H | ||||
| @@ -361,6 +361,7 @@ PDFProgramController::PDFProgramController(QObject* parent) : | ||||
|     m_toolManager(nullptr), | ||||
|     m_annotationManager(nullptr), | ||||
|     m_formManager(nullptr), | ||||
|     m_bookmarkManager(nullptr), | ||||
|     m_isBusy(false), | ||||
|     m_isFactorySettingsBeingRestored(false), | ||||
|     m_progress(nullptr) | ||||
| @@ -375,6 +376,9 @@ PDFProgramController::~PDFProgramController() | ||||
|  | ||||
|     delete m_annotationManager; | ||||
|     m_annotationManager = nullptr; | ||||
|  | ||||
|     delete m_bookmarkManager; | ||||
|     m_bookmarkManager = nullptr; | ||||
| } | ||||
|  | ||||
| void PDFProgramController::initializeAnnotationManager() | ||||
| @@ -396,6 +400,11 @@ void PDFProgramController::initializeFormManager() | ||||
|     connect(m_formManager, &pdf::PDFFormManager::documentModified, this, &PDFProgramController::onDocumentModified); | ||||
| } | ||||
|  | ||||
| void PDFProgramController::initializeBookmarkManager() | ||||
| { | ||||
|     m_bookmarkManager = new PDFBookmarkManager(this); | ||||
| } | ||||
|  | ||||
| void PDFProgramController::initialize(Features features, | ||||
|                                       QMainWindow* mainWindow, | ||||
|                                       IMainWindow* mainWindowInterface, | ||||
| @@ -605,6 +614,7 @@ void PDFProgramController::initialize(Features features, | ||||
|     } | ||||
|  | ||||
|     initializeAnnotationManager(); | ||||
|     initializeBookmarkManager(); | ||||
|  | ||||
|     if (features.testFlag(Forms)) | ||||
|     { | ||||
| @@ -1915,6 +1925,11 @@ void PDFProgramController::setDocument(pdf::PDFModifiedDocument document, bool i | ||||
|         m_annotationManager->setDocument(document); | ||||
|     } | ||||
|  | ||||
|     if (m_bookmarkManager) | ||||
|     { | ||||
|         m_bookmarkManager->setDocument(document); | ||||
|     } | ||||
|  | ||||
|     if (m_formManager) | ||||
|     { | ||||
|         m_formManager->setDocument(document); | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "pdfdocumentreader.h" | ||||
| #include "pdfdocumentpropertiesdialog.h" | ||||
| #include "pdfplugin.h" | ||||
| #include "pdfbookmarkmanager.h" | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QAction> | ||||
| @@ -275,6 +276,7 @@ public: | ||||
|     PDFViewerSettings* getSettings() const { return m_settings; } | ||||
|     pdf::PDFDocument* getDocument() const { return m_pdfDocument.data(); } | ||||
|     pdf::PDFCertificateStore* getCertificateStore() const { return const_cast<pdf::PDFCertificateStore*>(&m_certificateStore); } | ||||
|     PDFBookmarkManager* getBookmarkManager() const { return m_bookmarkManager; } | ||||
|     PDFTextToSpeech* getTextToSpeech() const { return m_textToSpeech; } | ||||
|     const std::vector<pdf::PDFSignatureVerificationResult>* getSignatures() const { return &m_signatures; } | ||||
|  | ||||
| @@ -326,6 +328,7 @@ private: | ||||
|     void initializeToolManager(); | ||||
|     void initializeAnnotationManager(); | ||||
|     void initializeFormManager(); | ||||
|     void initializeBookmarkManager(); | ||||
|  | ||||
|     void onActionGoToDocumentStartTriggered(); | ||||
|     void onActionGoToDocumentEndTriggered(); | ||||
| @@ -422,6 +425,7 @@ private: | ||||
|     pdf::PDFToolManager* m_toolManager; | ||||
|     pdf::PDFWidgetAnnotationManager* m_annotationManager; | ||||
|     pdf::PDFWidgetFormManager* m_formManager; | ||||
|     PDFBookmarkManager* m_bookmarkManager; | ||||
|  | ||||
|     PDFFileInfo m_fileInfo; | ||||
|     QFileSystemWatcher m_fileWatcher; | ||||
|   | ||||
| @@ -33,6 +33,7 @@ | ||||
| #include "pdfdrawspacecontroller.h" | ||||
| #include "pdfdocumentbuilder.h" | ||||
| #include "pdfwidgetutils.h" | ||||
| #include "pdfbookmarkui.h" | ||||
|  | ||||
| #include <QMenu> | ||||
| #include <QAction> | ||||
| @@ -60,6 +61,7 @@ constexpr const char* STYLESHEET = | ||||
| PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, | ||||
|                                    PDFTextToSpeech* textToSpeech, | ||||
|                                    pdf::PDFCertificateStore* certificateStore, | ||||
|                                    PDFBookmarkManager* bookmarkManager, | ||||
|                                    PDFViewerSettings* settings, | ||||
|                                    bool editableOutline, | ||||
|                                    QWidget* parent) : | ||||
| @@ -68,10 +70,12 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, | ||||
|     m_proxy(proxy), | ||||
|     m_textToSpeech(textToSpeech), | ||||
|     m_certificateStore(certificateStore), | ||||
|     m_bookmarkManager(bookmarkManager), | ||||
|     m_settings(settings), | ||||
|     m_outlineTreeModel(nullptr), | ||||
|     m_thumbnailsModel(nullptr), | ||||
|     m_optionalContentTreeModel(nullptr), | ||||
|     m_bookmarkItemModel(nullptr), | ||||
|     m_document(nullptr), | ||||
|     m_optionalContentActivity(nullptr), | ||||
|     m_attachmentsTreeModel(nullptr) | ||||
| @@ -126,6 +130,11 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, | ||||
|     ui->attachmentsTreeView->setContextMenuPolicy(Qt::CustomContextMenu); | ||||
|     connect(ui->attachmentsTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onAttachmentCustomContextMenuRequested); | ||||
|  | ||||
|     // Bookmarks | ||||
|     m_bookmarkItemModel = new PDFBookmarkItemModel(bookmarkManager, this); | ||||
|     ui->bookmarksView->setModel(m_bookmarkItemModel); | ||||
|     ui->bookmarksView->setItemDelegate(new PDFBookmarkItemDelegate(bookmarkManager, this)); | ||||
|  | ||||
|     m_pageInfo[Invalid] = { nullptr, ui->emptyPage }; | ||||
|     m_pageInfo[OptionalContent] = { ui->optionalContentButton, ui->optionalContentPage }; | ||||
|     m_pageInfo[Outline] = { ui->outlineButton, ui->outlinePage }; | ||||
| @@ -133,6 +142,7 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, | ||||
|     m_pageInfo[Attachments] = { ui->attachmentsButton, ui->attachmentsPage }; | ||||
|     m_pageInfo[Speech] = { ui->speechButton, ui->speechPage }; | ||||
|     m_pageInfo[Signatures] = { ui->signaturesButton, ui->signaturesPage }; | ||||
|     m_pageInfo[Bookmarks] = { ui->bookmarksButton, ui->bookmarksPage }; | ||||
|  | ||||
|     for (const auto& pageInfo : m_pageInfo) | ||||
|     { | ||||
| @@ -271,6 +281,9 @@ bool PDFSidebarWidget::isEmpty(Page page) const | ||||
|         case Attachments: | ||||
|             return m_attachmentsTreeModel->isEmpty(); | ||||
|  | ||||
|         case Bookmarks: | ||||
|             return !m_document || !m_bookmarkManager; | ||||
|  | ||||
|         case Speech: | ||||
|             return !m_textToSpeech->isValid(); | ||||
|  | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| #define PDFSIDEBARWIDGET_H | ||||
|  | ||||
| #include "pdfglobal.h" | ||||
| #include "pdfbookmarkmanager.h" | ||||
|  | ||||
| #include <QWidget> | ||||
|  | ||||
| @@ -52,6 +53,7 @@ namespace pdfviewer | ||||
| { | ||||
| class PDFTextToSpeech; | ||||
| class PDFViewerSettings; | ||||
| class PDFBookmarkItemModel; | ||||
|  | ||||
| class PDFSidebarWidget : public QWidget | ||||
| { | ||||
| @@ -61,6 +63,7 @@ public: | ||||
|     explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, | ||||
|                               PDFTextToSpeech* textToSpeech, | ||||
|                               pdf::PDFCertificateStore* certificateStore, | ||||
|                               PDFBookmarkManager* bookmarkManager, | ||||
|                               PDFViewerSettings* settings, | ||||
|                               bool editableOutline, | ||||
|                               QWidget* parent); | ||||
| @@ -78,6 +81,7 @@ public: | ||||
|         Attachments, | ||||
|         Speech, | ||||
|         Signatures, | ||||
|         Bookmarks, | ||||
|         _END | ||||
|     }; | ||||
|  | ||||
| @@ -126,10 +130,12 @@ private: | ||||
|     pdf::PDFDrawWidgetProxy* m_proxy; | ||||
|     PDFTextToSpeech* m_textToSpeech; | ||||
|     pdf::PDFCertificateStore* m_certificateStore; | ||||
|     PDFBookmarkManager* m_bookmarkManager; | ||||
|     PDFViewerSettings* m_settings; | ||||
|     pdf::PDFOutlineTreeItemModel* m_outlineTreeModel; | ||||
|     pdf::PDFThumbnailsItemModel* m_thumbnailsModel; | ||||
|     pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel; | ||||
|     PDFBookmarkItemModel* m_bookmarkItemModel; | ||||
|     const pdf::PDFDocument* m_document; | ||||
|     pdf::PDFOptionalContentActivity* m_optionalContentActivity; | ||||
|     pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel; | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>339</width> | ||||
|     <height>584</height> | ||||
|     <width>388</width> | ||||
|     <height>681</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <layout class="QHBoxLayout" name="sidebarLayout"> | ||||
| @@ -225,6 +225,35 @@ | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QToolButton" name="bookmarksButton"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>96</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>Bookmarks</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset resource="pdf4qtviewer.qrc"> | ||||
|          <normaloff>:/resources/sidebar-favourites.svg</normaloff>:/resources/sidebar-favourites.svg</iconset> | ||||
|        </property> | ||||
|        <property name="iconSize"> | ||||
|         <size> | ||||
|          <width>64</width> | ||||
|          <height>64</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="checkable"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|        <property name="toolButtonStyle"> | ||||
|         <enum>Qt::ToolButtonTextUnderIcon</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="buttonSpacer"> | ||||
|        <property name="orientation"> | ||||
| @@ -243,7 +272,7 @@ | ||||
|    <item> | ||||
|     <widget class="QStackedWidget" name="stackedWidget"> | ||||
|      <property name="currentIndex"> | ||||
|       <number>1</number> | ||||
|       <number>5</number> | ||||
|      </property> | ||||
|      <widget class="QWidget" name="emptyPage"/> | ||||
|      <widget class="QWidget" name="outlinePage"> | ||||
| @@ -383,6 +412,32 @@ | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|      <widget class="QWidget" name="bookmarksPage"> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|        <property name="leftMargin"> | ||||
|         <number>0</number> | ||||
|        </property> | ||||
|        <property name="topMargin"> | ||||
|         <number>0</number> | ||||
|        </property> | ||||
|        <property name="rightMargin"> | ||||
|         <number>0</number> | ||||
|        </property> | ||||
|        <property name="bottomMargin"> | ||||
|         <number>0</number> | ||||
|        </property> | ||||
|        <item> | ||||
|         <widget class="QListView" name="bookmarksView"> | ||||
|          <property name="editTriggers"> | ||||
|           <set>QAbstractItemView::NoEditTriggers</set> | ||||
|          </property> | ||||
|          <property name="wordWrap"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|      <widget class="QWidget" name="speechPage"> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|        <item> | ||||
|   | ||||
| @@ -269,7 +269,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : | ||||
|     setCentralWidget(m_programController->getPdfWidget()); | ||||
|     setFocusProxy(m_programController->getPdfWidget()); | ||||
|  | ||||
|     m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), true, this); | ||||
|     m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getBookmarkManager(), m_programController->getSettings(), true, this); | ||||
|     m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this); | ||||
|     m_sidebarDockWidget->setObjectName("SidebarDockWidget"); | ||||
|     m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); | ||||
|   | ||||
| @@ -202,7 +202,7 @@ PDFViewerMainWindowLite::PDFViewerMainWindowLite(QWidget* parent) : | ||||
|     setCentralWidget(m_programController->getPdfWidget()); | ||||
|     setFocusProxy(m_programController->getPdfWidget()); | ||||
|  | ||||
|     m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), false, this); | ||||
|     m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getBookmarkManager(), m_programController->getSettings(), false, this); | ||||
|     m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this); | ||||
|     m_sidebarDockWidget->setObjectName("SidebarDockWidget"); | ||||
|     m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); | ||||
|   | ||||
							
								
								
									
										58
									
								
								Pdf4QtViewer/resources/sidebar-favourites.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								Pdf4QtViewer/resources/sidebar-favourites.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    version="1.1" | ||||
|    id="svg815" | ||||
|    width="512" | ||||
|    height="512" | ||||
|    viewBox="0 0 512 512" | ||||
|    sodipodi:docname="sidebar-favourites.svg" | ||||
|    inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> | ||||
|   <metadata | ||||
|      id="metadata821"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <defs | ||||
|      id="defs819" /> | ||||
|   <sodipodi:namedview | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1" | ||||
|      objecttolerance="10" | ||||
|      gridtolerance="10" | ||||
|      guidetolerance="10" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:window-width="3840" | ||||
|      inkscape:window-height="2035" | ||||
|      id="namedview817" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="0.30348968" | ||||
|      inkscape:cx="-2950.1732" | ||||
|      inkscape:cy="277.06973" | ||||
|      inkscape:window-x="-13" | ||||
|      inkscape:window-y="-13" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="svg815" /> | ||||
|   <path | ||||
|      style="fill:#000000;stroke-width:0.5" | ||||
|      d="m 139.03566,421.94715 c -2.79737,-0.85185 -5.79201,-3.2299 -7.58405,-6.02256 -1.38453,-2.1576 -1.70923,-3.19414 -1.84701,-5.8962 -0.14818,-2.90575 0.12519,-4.10246 2.405,-10.52839 1.41469,-3.9875 4.55587,-13.1 6.98039,-20.25 2.42452,-7.15 8.16662,-23.9125 12.76022,-37.25 10.50154,-30.49115 19.75557,-58.03253 19.7309,-58.72206 -0.0189,-0.52992 -4.92747,-4.22387 -27.73111,-20.86941 -3.575,-2.60957 -18.45339,-13.61279 -33.0631,-24.4516 -14.609695,-10.83881 -29.86493,-22.13597 -33.900515,-25.1048 -8.016685,-5.89759 -9.791975,-7.96334 -10.83006,-12.60198 -1.16825,-5.22029 1.75494,-11.13603 6.780105,-13.72108 l 2.48643,-1.27907 64.45687,-0.25 64.45687,-0.25 4.95111,-14 c 9.84582,-27.84052 18.59349,-52.75333 22.91133,-65.25 12.57291,-36.388445 11.92763,-34.83447 15.41594,-37.124915 3.76617,-2.472895 7.65066,-3.013205 11.61282,-1.615275 4.54841,1.60477 7.5252,5.1016 9.52236,11.18591 3.41941,10.417175 24.25254,69.83815 32.84417,93.67928 l 4.81995,13.375 63.76786,0.022 c 68.86041,0.0237 65.60095,-0.10273 69.25773,2.68643 5.43773,4.14757 6.32897,13.06779 1.83878,18.40408 -0.72796,0.86513 -6.6122,5.50844 -13.07611,10.31847 -6.4639,4.81003 -21.12961,15.73706 -32.59047,24.28229 -11.46085,8.54523 -25.18585,18.73451 -30.5,22.64284 -5.31414,3.90832 -13.49778,9.92958 -18.18586,13.38056 l -8.52378,6.27452 4.60426,13.61944 c 2.53235,7.49068 7.23127,21.26943 10.44205,30.61943 3.21079,9.35 8.60821,25.1 11.99428,35 3.38606,9.9 8.20409,23.92662 10.70673,31.17026 5.00498,14.48644 5.27817,16.09668 3.52561,20.78082 -1.78384,4.76773 -6.69274,7.99046 -12.22536,8.02602 -3.72318,0.024 -4.28878,-0.29608 -17.25,-9.75992 -5.3625,-3.91551 -12.45,-9.03772 -15.75,-11.38269 -3.3,-2.34497 -8.71337,-6.19203 -12.02971,-8.54904 -3.31633,-2.357 -11.48374,-8.22295 -18.14978,-13.03545 -6.66605,-4.8125 -16.56996,-11.9 -22.0087,-15.75 -5.43875,-3.85 -13.8661,-9.86019 -18.72745,-13.35597 l -8.83882,-6.35598 -14.1826,10.17114 c -7.80043,5.59413 -18.88068,13.60572 -24.62277,17.80354 -10.5153,7.68731 -12.89484,9.43081 -30.69017,22.48681 -5.3625,3.93434 -12,8.80233 -14.75,10.81777 -2.75,2.01544 -8.2625,6.07362 -12.25,9.01819 -3.9875,2.94456 -8.21074,5.89541 -9.38497,6.55743 -2.44103,1.37625 -6.90762,1.86779 -9.57937,1.05421 z M 180,365.74416 c 12.20892,-9.0382 32.34222,-23.69689 54.93082,-39.99416 3.43044,-2.475 6.29932,-4.725 6.37529,-5 0.076,-0.275 -1.80418,-1.85 -4.17811,-3.5 -2.37392,-1.65 -7.70513,-5.4946 -11.84712,-8.54355 -16.45615,-12.11352 -27.85131,-20.30252 -28.01669,-20.13389 -0.35952,0.36659 -11.41106,32.16678 -21.26102,61.17744 -3.22606,9.50154 -6.69433,19.14572 -8.81029,24.49864 -0.3916,0.99065 -0.38294,0.99098 1.08118,0.0411 0.81176,-0.52666 6.08844,-4.37216 11.72594,-8.54558 z m 162.19338,7.13084 c -0.41924,-1.16875 -3.35012,-9.6625 -6.51306,-18.875 -15.3913,-44.82929 -22.70379,-65.48939 -23.18032,-65.49185 -0.28679,-0.002 -14.64506,10.30154 -27.5,19.7331 -4.8125,3.53088 -10.60625,7.7753 -12.875,9.43204 -2.26875,1.65674 -4.11976,3.13925 -4.11334,3.29448 0.006,0.15522 2.98766,2.43192 6.625,5.05931 3.63734,2.62741 11.78834,8.52736 18.11334,13.11103 10.32734,7.48413 23.19979,16.70141 39,27.92587 3.025,2.14896 6.625,4.80197 8,5.89558 1.375,1.0936 2.65877,2.00008 2.85282,2.01441 0.19405,0.0143 0.01,-0.93022 -0.40944,-2.09897 z M 198.69993,237.48095 c 4.91075,-14.58548 8.82349,-26.62419 8.695,-26.75269 -0.1285,-0.1285 -20.44134,-0.17618 -45.13965,-0.10594 l -44.90601,0.12768 9.87824,7.25 c 16.71033,12.2643 52.7714,38.9228 57.52249,42.52405 2.475,1.87601 4.6173,3.42556 4.76065,3.44344 0.14336,0.0179 4.27854,-11.90107 9.18928,-26.48654 z m 131.55489,18.96204 c 8.37364,-6.22386 20.01545,-14.80142 44.91847,-33.09545 5.54219,-4.07135 11.58831,-8.45885 13.43583,-9.75 1.84751,-1.29115 3.36627,-2.51629 3.375,-2.72254 0.009,-0.20625 -20.26035,-0.375 -45.04241,-0.375 h -45.05829 l 0.27138,1.125 c 0.26839,1.11263 13.59221,40.36652 15.99548,47.125 1.87238,5.26551 1.90355,5.32494 2.53311,4.83075 0.31163,-0.24462 4.61878,-3.45661 9.57143,-7.13776 z m -48.53136,-60.99938 c 0.42896,-0.42953 -25.58476,-76.76693 -26.54687,-77.90194 -0.0971,-0.11458 -0.29176,-0.0932 -0.43253,0.0476 C 254.16118,118.17216 228,193.90628 228,195.01079 c 0,0.24982 2.30625,0.53052 5.125,0.62377 8.72051,0.28849 48.27486,0.13307 48.59846,-0.19095 z" | ||||
|      id="path825" | ||||
|      inkscape:connector-curvature="0" /> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 5.8 KiB | 
		Reference in New Issue
	
	Block a user