3D PDF: Code generator for PRC (initial commit)

This commit is contained in:
Jakub Melka
2023-01-29 18:08:33 +01:00
parent d1218fe2de
commit 3a7ff58a31
10 changed files with 470 additions and 5 deletions

View File

@@ -1913,4 +1913,252 @@ QString XFACodeGenerator::generateHeader() const
return QString::fromUtf8(ba);
}
void PRCCodeGenerator::generateCode(const QDomDocument& document, QString headerName, QString sourceName)
{
QString startMark = "/* START GENERATED CODE */";
QString endMark = "/* END GENERATED CODE */";
loadClasses(document);
QFile headerFile(headerName);
if (headerFile.exists())
{
if (headerFile.open(QFile::ReadOnly | QFile::Text))
{
QString utfCode = QString::fromUtf8(headerFile.readAll());
headerFile.close();
int startIndex = utfCode.indexOf(startMark, Qt::CaseSensitive) + startMark.length();
int endIndex = utfCode.indexOf(endMark, Qt::CaseSensitive);
QString frontPart = utfCode.left(startIndex);
QString backPart = utfCode.mid(endIndex);
QString headerGeneratedCode = generateHeader();
QString allCode = frontPart + headerGeneratedCode + backPart;
headerFile.open(QFile::WriteOnly | QFile::Truncate);
headerFile.write(allCode.toUtf8());
headerFile.close();
}
}
QFile sourceFile(sourceName);
if (sourceFile.exists())
{
if (sourceFile.open(QFile::ReadOnly | QFile::Text))
{
QString utfCode = QString::fromUtf8(sourceFile.readAll());
sourceFile.close();
int startIndex = utfCode.indexOf(startMark, Qt::CaseSensitive) + startMark.length();
int endIndex = utfCode.indexOf(endMark, Qt::CaseSensitive);
QString frontPart = utfCode.left(startIndex);
QString backPart = utfCode.mid(endIndex);
QString sourceGeneratedCode = generateSource();
QString allCode = frontPart + sourceGeneratedCode + backPart;
sourceFile.open(QFile::WriteOnly | QFile::Truncate);
sourceFile.write(allCode.toUtf8());
sourceFile.close();
}
}
}
void PRCCodeGenerator::loadClasses(const QDomDocument& document)
{
QDomElement prcElement = document.firstChildElement("prc");
if (prcElement.isNull())
{
return;
}
QDomElement element = prcElement.firstChildElement("objects");
if (element.isNull())
{
return;
}
QDomNodeList childNodes = element.elementsByTagName("object");
for (int i = 0; i < childNodes.length(); ++i)
{
QDomNode node = childNodes.item(i);
QDomElement classElement = node.toElement();
QString className = classElement.attribute("name");
Class myClass;
myClass.classType = className;
myClass.isFlat = classElement.attribute("flat") == "true";
myClass.valueType = myClass.isFlat ? className : QString("std::shared_ptr<%1>").arg(myClass.classType);
QDomNodeList loadItems = classElement.childNodes();
for (int j = 0; j < loadItems.length(); ++j)
{
QDomElement loadElement = loadItems.item(j).toElement();
LoadItem loadItem;
loadItem.name = loadElement.attribute("name");
loadItem.type = loadElement.attribute("type");
loadItem.constant = loadElement.attribute("constant");
loadItem.bits = loadElement.attribute("bits");
if (loadElement.tagName() == "field")
{
loadItem.loadItemType = Field;
}
if (loadElement.tagName() == "array")
{
loadItem.loadItemType = Array;
}
if (loadElement.attribute("value") == "true")
{
myClass.isValue = true;
myClass.valueType = loadElement.attribute("type");
}
myClass.items.emplace_back(std::move(loadItem));
}
m_classes[className] = (std::move(myClass));
}
}
QString PRCCodeGenerator::generateHeader() const
{
QByteArray ba;
{
QTextStream stream(&ba, QIODevice::WriteOnly);
stream.setEncoding(QStringConverter::Utf8);
stream.setRealNumberPrecision(3);
stream.setRealNumberNotation(QTextStream::FixedNotation);
stream << Qt::endl << Qt::endl;
for (const auto& item : m_classes)
{
const Class& myClass = item.second;
if (myClass.isValue)
{
continue;
}
stream << "class " << myClass.classType << Qt::endl;
stream << "{" << Qt::endl;
stream << "public:" << Qt::endl;
stream << QString(" explicit %1() = default;").arg(myClass.classType) << Qt::endl;
stream << QString(" ~%1() = default;").arg(myClass.classType) << Qt::endl << Qt::endl;
// Generate getters/setters
for (const LoadItem& loadItem : myClass.items)
{
if (!loadItem.isFieldOrArray())
{
continue;
}
stream << QString(" const %1& %2() const { return %3; }").arg(getValueTypeForLoadItem(loadItem), getGetterFunctionNameForLoadItem(loadItem), getClassFieldNameForValueItem(loadItem)) << Qt::endl;
stream << QString(" void %2(%1 value) { return %3 = std::move(value); }").arg(getValueTypeForLoadItem(loadItem), getSetterFunctionNameForLoadItem(loadItem), getClassFieldNameForValueItem(loadItem)) << Qt::endl;
stream << Qt::endl;
}
stream << Qt::endl;
stream << "private:" << Qt::endl;
// Generate fields
for (const LoadItem& loadItem : myClass.items)
{
if (!loadItem.isFieldOrArray())
{
continue;
}
stream << QString(" %1 %2 = %1();").arg(getValueTypeForLoadItem(loadItem), getClassFieldNameForValueItem(loadItem)) << Qt::endl;
}
stream << "};" << Qt::endl << Qt::endl;
}
stream << Qt::endl << Qt::endl;
}
return QString::fromUtf8(ba);
}
QString PRCCodeGenerator::generateSource() const
{
return QString();
}
QString PRCCodeGenerator::getCamelCase(QString string) const
{
QString str;
bool upperCase = false;
for (QChar ch : string)
{
if (ch == '_')
{
upperCase = true;
continue;
}
if (upperCase)
{
upperCase = false;
ch = ch.toUpper();
}
str += ch;
}
return str;
}
QString PRCCodeGenerator::getValueTypeForLoadItem(const LoadItem& item) const
{
auto it = m_classes.find(item.type);
if (it != m_classes.cend())
{
const Class& myClass = (*it).second;
QString valueType = myClass.valueType;
if (item.isField())
{
return valueType;
}
if (item.isArray())
{
return QString("std::vector<%1>").arg(valueType);
}
}
else
{
qWarning() << "Class " << item.type << " not found!";
}
return QString();
}
QString PRCCodeGenerator::getClassFieldNameForValueItem(const LoadItem& item) const
{
return QString("m_") + getCamelCase(item.name);
}
QString PRCCodeGenerator::getGetterFunctionNameForLoadItem(const LoadItem& item) const
{
return getCamelCase(QString("is_") + item.name);
}
QString PRCCodeGenerator::getSetterFunctionNameForLoadItem(const LoadItem& item) const
{
return getCamelCase(QString("set_") + item.name);
}
}

View File

@@ -498,6 +498,62 @@ private:
std::set<QString> m_usedTypes;
};
class PRCCodeGenerator
{
public:
PRCCodeGenerator() = default;
void generateCode(const QDomDocument& document, QString headerName, QString sourceName);
private:
enum LoadItemType
{
Field,
Array
};
struct LoadItem
{
bool isField() const { return loadItemType == Field; }
bool isArray() const { return loadItemType == Array; }
bool isFieldOrArray() const { return isField() || loadItemType == Array; }
LoadItemType loadItemType = Field;
QString name;
QString type;
QString constant;
QString bits;
};
struct Class
{
QString classType;
QString valueType;
/// Use class by value, not by shared pointer?
bool isFlat = false;
/// Use value type instead of the class?
bool isValue = false;
std::vector<LoadItem> items;
};
void loadClasses(const QDomDocument& document);
QString generateHeader() const;
QString generateSource() const;
QString getCamelCase(QString string) const;
QString getValueTypeForLoadItem(const LoadItem& item) const;
QString getClassFieldNameForValueItem(const LoadItem& item) const;
QString getGetterFunctionNameForLoadItem(const LoadItem& item) const;
QString getSetterFunctionNameForLoadItem(const LoadItem& item) const;
std::map<QString, Class> m_classes;
};
} // namespace codegen
Q_DECLARE_METATYPE(codegen::GeneratedCodeStorage*)

View File

@@ -91,6 +91,9 @@ void GeneratorMainWindow::saveSettings()
settings.setValue("XFAdefinitionFileName", m_XFAdefinitionFileName);
settings.setValue("XFAheaderFileName", m_XFAheaderFileName);
settings.setValue("XFAsourceFileName", m_XFAsourceFileName);
settings.setValue("PRCdefinitionFileName", m_PRCdefinitionFileName);
settings.setValue("PRCheaderFileName", m_PRCheaderFileName);
settings.setValue("PRCsourceFileName", m_PRCsourceFileName);
}
void GeneratorMainWindow::loadGeneratedSettings()
@@ -362,6 +365,9 @@ void GeneratorMainWindow::loadSettings()
m_XFAdefinitionFileName = settings.value("XFAdefinitionFileName", QVariant()).toString();
m_XFAheaderFileName = settings.value("XFAheaderFileName", QVariant()).toString();
m_XFAsourceFileName = settings.value("XFAsourceFileName", QVariant()).toString();
m_PRCdefinitionFileName = settings.value("PRCdefinitionFileName", QVariant()).toString();
m_PRCheaderFileName = settings.value("PRCheaderFileName", QVariant()).toString();
m_PRCsourceFileName = settings.value("PRCsourceFileName", QVariant()).toString();
}
void GeneratorMainWindow::save(const QString& fileName)
@@ -572,3 +578,48 @@ void GeneratorMainWindow::on_actionGenerate_XFA_code_triggered()
generator.generateCode(document, m_XFAheaderFileName, m_XFAsourceFileName);
}
}
void GeneratorMainWindow::on_actionSet_code_header_PRC_triggered()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Select cpp header"), QString(), "cpp header (*.h)");
if (!fileName.isEmpty())
{
m_PRCheaderFileName = fileName;
saveSettings();
}
}
void GeneratorMainWindow::on_actionSet_code_source_PRC_triggered()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Select cpp source"), QString(), "cpp source (*.cpp)");
if (!fileName.isEmpty())
{
m_PRCsourceFileName = fileName;
saveSettings();
}
}
void GeneratorMainWindow::on_actionSet_PRC_description_triggered()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Select xml definition"), QString(), "XML file (*.xml)");
if (!fileName.isEmpty())
{
m_PRCdefinitionFileName = fileName;
saveSettings();
}
}
void GeneratorMainWindow::on_actionGenerate_PRC_code_triggered()
{
codegen::PRCCodeGenerator generator;
QFile file(m_PRCdefinitionFileName);
if (file.open(QFile::ReadOnly))
{
QDomDocument document;
document.setContent(&file);
file.close();
generator.generateCode(document, m_PRCheaderFileName, m_PRCsourceFileName);
}
}

View File

@@ -84,6 +84,10 @@ private slots:
void on_actionSet_code_source_XFA_triggered();
void on_actionGenerate_XFA_code_triggered();
void on_actionSet_XFA_description_triggered();
void on_actionSet_code_header_PRC_triggered();
void on_actionSet_code_source_PRC_triggered();
void on_actionGenerate_PRC_code_triggered();
void on_actionSet_PRC_description_triggered();
private:
void loadSettings();
@@ -112,6 +116,10 @@ private:
QString m_XFAdefinitionFileName;
QString m_XFAheaderFileName;
QString m_XFAsourceFileName;
QString m_PRCdefinitionFileName;
QString m_PRCheaderFileName;
QString m_PRCsourceFileName;
};
#endif // GENERATORMAINWINDOW_H

View File

@@ -250,9 +250,19 @@
<addaction name="actionSet_code_source_XFA"/>
<addaction name="actionGenerate_XFA_code"/>
</widget>
<widget class="QMenu" name="menuPRC">
<property name="title">
<string>PRC</string>
</property>
<addaction name="actionSet_PRC_description"/>
<addaction name="actionSet_code_header_PRC"/>
<addaction name="actionSet_code_source_PRC"/>
<addaction name="actionGenerate_PRC_code"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuCode"/>
<addaction name="menuXFA"/>
<addaction name="menuPRC"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionLoad">
@@ -314,6 +324,26 @@
<string>Set XFA description</string>
</property>
</action>
<action name="actionSet_code_header_PRC">
<property name="text">
<string>Set code header PRC</string>
</property>
</action>
<action name="actionSet_code_source_PRC">
<property name="text">
<string>Set code source PRC</string>
</property>
</action>
<action name="actionGenerate_PRC_code">
<property name="text">
<string>Generate PRC code</string>
</property>
</action>
<action name="actionSet_PRC_description">
<property name="text">
<string>Set PRC description</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@@ -101,6 +101,8 @@ add_library(Pdf4QtLib SHARED
sources/pdfobjecteditorwidget_impl.h
sources/pdfdocumentsanitizer.h
sources/pdfdocumentsanitizer.cpp
sources/pdf3d_prc.h
sources/pdf3d_prc.cpp
cmaps.qrc
)

View File

@@ -0,0 +1,30 @@
// 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 "pdf3d_prc.h"
namespace pdf
{
namespace prc
{
/* START GENERATED CODE *//* END GENERATED CODE */
} // namespace prc
} // namespace pdf

View File

@@ -0,0 +1,39 @@
// 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 PDF3D_PRC_H
#define PDF3D_PRC_H
#include "pdfglobal.h"
#include <memory>
namespace pdf
{
namespace prc
{
/* START GENERATED CODE */
/* END GENERATED CODE */
} // namespace prc
} // namespace pdf
#endif // PDF3D_PRC_H

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2022 Jakub Melka
// Copyright (C) 2022-2023 Jakub Melka
//
// This file is part of PDF4QT.
//

View File

@@ -1,3 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!--
// Copyright (C) 2022-2023 Jakub Melka
//
@@ -18,7 +20,6 @@
-->
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<prc>
<!--
@@ -480,12 +481,12 @@
<field name="v_param_coeff_b" type="Double" />
</object>
<object name="Vector2d" flat="true">
<object name="Vector2D" flat="true">
<field name="x_value" type="Double" />
<field name="y_value" type="Double" />
</object>
<object name="Vector3d" flat="true">
<object name="Vector3D" flat="true">
<field name="x_value" type="Double" />
<field name="y_value" type="Double" />
<field name="z_value" type="Double" />
@@ -875,7 +876,7 @@
<array name="homogenous" dim="4" type="Double" condition="behavior &amp; PRC_TRANSFORMATION_Homogenous == TRUE" />
</object>
<object name="2DTransformation">
<object name="_2DTransformation">
<field name="behavior" type="Character" />
<field name="translation" type="Vector3D" condition="behavior &amp; PRC_TRANSFORMATION_Translate == TRUE" />
<array name="non_ortho_matrix" dim="3" type="Vector3D" condition="behavior &amp; PRC_TRANSFORMATION_NonOrtho == TRUE" />