diff --git a/plugin_exporter/metadata.txt b/plugin_exporter/metadata.txt
index 28beb2b..08aa060 100644
--- a/plugin_exporter/metadata.txt
+++ b/plugin_exporter/metadata.txt
@@ -7,11 +7,11 @@ name=Plugin Exporter
qgisMinimumVersion=3.20
qgisMaximumVersion=4.99
description=A QGIS plugin for exporting plugins
-version=0.2.2
+version=0.3.0
author=Francis Lapointe
email=francis.lapointe5@usherbrooke.ca
-about=Plugin Exporter is a QGIS plugin that can export installed plugins into a .csv or .json file. The user can export all the installed plugins or select the plugins they want to export. Plugin Exporter can also use the generated file to install the plugins back in QGIS. Third party repositories are also supported as of v0.2.0.
+about=Plugin Exporter is a QGIS plugin that can export installed plugins into a .csv, .json, .md or .pdf file. The user can export all the installed plugins or select the plugins they want to export. Plugin Exporter can also use a .csv or .json file to install the plugins back in QGIS. Third party repositories are also supported as of v0.2.0.
tracker=https://github.com/Scriptbash/PluginExporter/issues
repository=https://github.com/Scriptbash/PluginExporter
@@ -24,6 +24,8 @@ supportsQt6=True
hasProcessingProvider=no
# Uncomment the following line and add your changelog:
changelog=
+ v0.3.0
+ - Add Markdown (.md) and PDF (.pdf) export formats (export only)
v0.2.2
- Migrate to pyQt6
v0.2.1
diff --git a/plugin_exporter/plugin_exporter.py b/plugin_exporter/plugin_exporter.py
index 3a9e337..5559be0 100644
--- a/plugin_exporter/plugin_exporter.py
+++ b/plugin_exporter/plugin_exporter.py
@@ -23,7 +23,8 @@
"""
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
-from qgis.PyQt.QtGui import QIcon
+from qgis.PyQt.QtGui import QIcon, QTextDocument
+from qgis.PyQt.QtPrintSupport import QPrinter
from qgis.PyQt.QtWidgets import QAction, QLabel, QCheckBox
from qgis.core import Qgis, QgsSettings
from pyplugin_installer.installer_data import repositories
@@ -32,7 +33,9 @@
import os.path
import csv
import json
+import html
import pathlib
+from datetime import datetime
# Initialize Qt resources from file resources.py
from .resources import *
@@ -371,6 +374,22 @@ def export_plugins(self):
self.iface.messageBar().pushSuccess(
"Success", "Selected plugins were exported successfully."
)
+ elif file_format == ".md":
+ with open(output_file, "w", encoding="utf8") as file:
+ file.write(self.build_markdown(plugin_list, repos))
+ self.iface.messageBar().pushSuccess(
+ "Success", "Selected plugins were exported successfully."
+ )
+ elif file_format == ".pdf":
+ document = QTextDocument()
+ document.setHtml(self.build_html(plugin_list, repos))
+ printer = QPrinter()
+ printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat)
+ printer.setOutputFileName(output_file)
+ document.print(printer)
+ self.iface.messageBar().pushSuccess(
+ "Success", "Selected plugins were exported successfully."
+ )
except IsADirectoryError:
self.iface.messageBar().pushMessage(
"Error",
@@ -485,6 +504,98 @@ def add_repository(self, repo_info):
"Success", repo_name + " was added to the repositories."
)
+ # Collects the third party repositories and every metadata field of each
+ # selected plugin. Used by the human readable exports (.md and .pdf), which
+ # are export only: they cannot be imported back. Each plugin keeps all of
+ # the fields the plugin manager exposes for it (same data as the .json
+ # export), not just a fixed subset.
+ def build_report_data(self, plugin_list, repos):
+ repo_rows = []
+ if repos:
+ for name, value in repos.items():
+ if name == "QGIS Official Plugin Repository":
+ continue
+ repo_rows.append([name, value["url"]])
+
+ plugins = []
+ for plugin in plugin_list:
+ # Preserve the metadata key order; fall back to the id for the title
+ title = plugin.get("name") or plugin.get("id") or "Unknown plugin"
+ fields = [(str(k), "" if v is None else str(v)) for k, v in plugin.items()]
+ plugins.append((title, fields))
+ return repo_rows, plugins
+
+ # Builds a Markdown document listing the selected plugins
+ def build_markdown(self, plugin_list, repos):
+ repo_rows, plugins = self.build_report_data(plugin_list, repos)
+ generated = datetime.now().strftime("%Y-%m-%d %H:%M")
+
+ def cell(text):
+ return str(text).replace("|", "\\|").replace("\n", " ")
+
+ lines = [
+ "# QGIS Plugins Export",
+ "",
+ "_Generated by Plugin Exporter on " + generated + "_",
+ "",
+ ]
+ if repo_rows:
+ lines += ["## Third party repositories", "", "| Name | URL |", "| --- | --- |"]
+ for row in repo_rows:
+ lines.append("| " + " | ".join(cell(c) for c in row) + " |")
+ lines.append("")
+ lines += ["## Plugins", ""]
+ for title, fields in plugins:
+ lines += [
+ "### " + cell(title),
+ "",
+ "| Field | Value |",
+ "| --- | --- |",
+ ]
+ for key, value in fields:
+ lines.append("| " + cell(key) + " | " + cell(value) + " |")
+ lines.append("")
+ return "\n".join(lines)
+
+ # Builds an HTML document used to render the PDF export
+ def build_html(self, plugin_list, repos):
+ repo_rows, plugins = self.build_report_data(plugin_list, repos)
+ generated = datetime.now().strftime("%Y-%m-%d %H:%M")
+
+ def esc(text):
+ return html.escape(str(text)).replace("\n", "
")
+
+ def table(headers, rows):
+ html_rows = [
+ "
Generated by Plugin Exporter on " + html.escape(generated) + "
", + ] + if repo_rows: + parts.append("