Merge pull request #1504 from ispyisail/feat/qetz-recover

Add recovering what can be read of a damaged .qetz project (--recover)
This commit is contained in:
ispyisail
2026-10-16 11:26:55 +13:00
committed by GitHub
9 changed files with 348 additions and 53 deletions
+48
View File
@@ -28,6 +28,7 @@
#include "dxfexport.h"
#include "exportproperties.h"
#include "pdf_links.h"
#include "container/qetcontainer.h"
#include "qetgraphicsitem/conductor.h"
#include "qetgraphicsitem/diagramimageitem.h"
#include "qetgraphicsitem/element.h"
@@ -85,6 +86,7 @@ const QHash<QString, QString> &exportFlags()
{"--info", "info"},
{"--check-elements", "check"},
{"--resave", "resave"},
{"--recover", "recover"},
{"--set-titleblock", "settb"},
};
return flags;
@@ -870,6 +872,42 @@ int exportLinks(QETProject &project, const QString &output)
/// Round-trip: load the project and write its XML back out, so an external
/// diff can reveal markup QET silently normalises (tolerated-but-invalid XML).
/// What can be read of the damaged .qetz @p input, written to the .qet
/// @p output, and what was left out (QetContainer::recover()).
int recoverProject(const QString &input, const QString &output)
{
QFile file(input);
if (!file.open(QIODevice::ReadOnly)) {
err << "Cannot read '" << input << "': " << file.errorString() << "\n";
return 2;
}
const QByteArray content = file.readAll();
if (!QetContainer::isZip(content)) {
err << "'" << input << "' is not a .qetz project; a .qet opens as it is, or can be fixed in a text editor\n";
return 2;
}
QDomDocument document;
QStringList lost;
QString error;
if (!QetContainer::recover(content, &document, &lost, &error)) {
err << "Nothing can be recovered from '" << input << "': " << error << "\n";
return 1;
}
if (!QET::writeXmlFile(document, output, &error)) {
err << "Cannot write '" << output << "': " << error << "\n";
return 1;
}
out << "Recovered " << input << " -> " << output << "\n";
if (lost.isEmpty()) {
out << "Nothing was left out.\n";
} else {
out << "Left out:\n";
for (const QString &item : std::as_const(lost))
out << " " << item << "\n";
}
return 0;
}
int resaveProject(QETProject &project, const QString &output)
{
if (QETProject::isContainerPath(output)) {
@@ -1038,6 +1076,16 @@ int run(const QStringList &args)
return checkElements(rest.at(0));
}
// --recover reads what it can of a damaged .qetz, which a project
// cannot be opened from.
if (format == "recover") {
if (rest.size() < 2) {
err << "Usage: qelectrotech --recover <damaged.qetz> <output.qet>\n";
return 2;
}
return recoverProject(rest.at(0), rest.at(1));
}
const QString input = rest.value(0);
if (input.isEmpty()) {
err << "Usage: qelectrotech " << flag << " <project.qet> <output>\n";