mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-22 00:04:14 +02:00
Merge pull request #739 from Kellermorph/info-box-pdf
Export component info as invisible PDF text annotations
This commit is contained in:
@@ -238,6 +238,15 @@ void convertUriToGoTo(const QString &pdfPath)
|
|||||||
|
|
||||||
QByteArray uriVal = data.mid(uriStart, closeParen - uriStart);
|
QByteArray uriVal = data.mid(uriStart, closeParen - uriStart);
|
||||||
|
|
||||||
|
// Skip component-info annotations — handled by
|
||||||
|
// convertComponentInfoAnnotations() in a separate pass.
|
||||||
|
if (uriVal.startsWith("componentinfo://")
|
||||||
|
|| uriVal.startsWith("http://componentinfo.local/")) {
|
||||||
|
out.append(data.mid(found, closeParen + 1 - found));
|
||||||
|
pos = closeParen + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Extract page number: look for #page=N or bare page=N
|
// Extract page number: look for #page=N or bare page=N
|
||||||
int pageNum = -1;
|
int pageNum = -1;
|
||||||
int hashPos = uriVal.lastIndexOf("#page=");
|
int hashPos = uriVal.lastIndexOf("#page=");
|
||||||
@@ -379,4 +388,269 @@ void convertUriToGoTo(const QString &pdfPath)
|
|||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void convertComponentInfoAnnotations(const QString &pdfPath,
|
||||||
|
const QList<ComponentInfo> &annotations)
|
||||||
|
{
|
||||||
|
if (annotations.isEmpty()) return;
|
||||||
|
|
||||||
|
QFile f(pdfPath);
|
||||||
|
if (!f.open(QIODevice::ReadOnly)) return;
|
||||||
|
QByteArray data = f.readAll();
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
const QByteArray markerPrefix("http://componentinfo.local/");
|
||||||
|
if (!data.contains(markerPrefix)) return;
|
||||||
|
|
||||||
|
int xrefStart = data.lastIndexOf("\nxref\n");
|
||||||
|
if (xrefStart == -1) xrefStart = data.lastIndexOf("\nxref ");
|
||||||
|
if (xrefStart == -1) return;
|
||||||
|
++xrefStart;
|
||||||
|
QByteArray body = data.left(xrefStart);
|
||||||
|
|
||||||
|
// Pre-scan body for highest object number
|
||||||
|
int maxObjNum = 0;
|
||||||
|
{
|
||||||
|
const QByteArray objMarker(" 0 obj");
|
||||||
|
int p = 0;
|
||||||
|
while ((p = body.indexOf(objMarker, p)) != -1) {
|
||||||
|
int numStart = p - 1;
|
||||||
|
while (numStart > 0 && body[numStart - 1] != '\n' && body[numStart - 1] != '\r')
|
||||||
|
--numStart;
|
||||||
|
QByteArray numStr = body.mid(numStart, p - numStart).trimmed();
|
||||||
|
bool ok = false;
|
||||||
|
int num = numStr.toInt(&ok);
|
||||||
|
if (ok && num > maxObjNum)
|
||||||
|
maxObjNum = num;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Empty Form XObject used as invisible appearance for all annotations.
|
||||||
|
// All annotations reference this single object.
|
||||||
|
int emptyXObjNum = maxObjNum + 1;
|
||||||
|
|
||||||
|
QByteArray out;
|
||||||
|
out.reserve(data.size());
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
bool anyConverted = false;
|
||||||
|
|
||||||
|
while (pos < body.size()) {
|
||||||
|
// Find next indexed marker: http://componentinfo.local/<N>
|
||||||
|
int markerPos = body.indexOf(markerPrefix, pos);
|
||||||
|
if (markerPos == -1) {
|
||||||
|
out.append(body.mid(pos));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract index from marker URL
|
||||||
|
int idxStart = markerPos + markerPrefix.size();
|
||||||
|
int idxEnd = idxStart;
|
||||||
|
while (idxEnd < body.size() && body[idxEnd] >= '0' && body[idxEnd] <= '9')
|
||||||
|
++idxEnd;
|
||||||
|
if (idxEnd == idxStart) {
|
||||||
|
// No index — skip malformed marker
|
||||||
|
out.append(body.mid(pos, markerPos + markerPrefix.size() - pos));
|
||||||
|
pos = markerPos + markerPrefix.size();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int annotIndex = body.mid(idxStart, idxEnd - idxStart).toInt();
|
||||||
|
|
||||||
|
int uriOpen = body.lastIndexOf("/URI (", markerPos);
|
||||||
|
int closeParen = (uriOpen != -1) ? body.indexOf(')', markerPos) : -1;
|
||||||
|
if (uriOpen == -1 || closeParen == -1 || uriOpen < pos) {
|
||||||
|
out.append(body.mid(pos, idxEnd - pos));
|
||||||
|
pos = idxEnd;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find /S /URI before /URI (
|
||||||
|
int sUriPos = body.lastIndexOf("/S /URI", uriOpen);
|
||||||
|
|
||||||
|
// Find the /A << that opens the action dict containing /S /URI.
|
||||||
|
// Qt always writes: /A <<\n/S /URI\n/URI (...)\n>>\n>>
|
||||||
|
int aDictOpen = (sUriPos != -1)
|
||||||
|
? body.lastIndexOf("/A <<", sUriPos)
|
||||||
|
: -1;
|
||||||
|
|
||||||
|
// If /A << is not found, leave this annotation untouched
|
||||||
|
if (aDictOpen == -1 || aDictOpen < pos) {
|
||||||
|
out.append(body.mid(pos, closeParen + 1 - pos));
|
||||||
|
pos = closeParen + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate index
|
||||||
|
if (annotIndex < 0 || annotIndex >= annotations.size()) {
|
||||||
|
// Index out of range — keep original annotation intact
|
||||||
|
out.append(body.mid(pos, closeParen + 1 - pos));
|
||||||
|
pos = closeParen + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy annotation dict header up to /A <<
|
||||||
|
out.append(body.mid(pos, aDictOpen - pos));
|
||||||
|
|
||||||
|
QByteArray contents = annotations[annotIndex].contents.toUtf8();
|
||||||
|
|
||||||
|
// Replace /Subtype /Link with /Subtype /Text, bounded to the enclosing
|
||||||
|
// PDF object. Anchoring to " 0 obj" prevents hitting /Subtype /Link
|
||||||
|
// from a cross-ref annotation that sits between the previous marker
|
||||||
|
// and the current annotation on the same page.
|
||||||
|
int objStart = body.lastIndexOf(" 0 obj", aDictOpen);
|
||||||
|
int subTypeInBody = (objStart != -1)
|
||||||
|
? body.indexOf("/Subtype /Link", objStart) : -1;
|
||||||
|
int subTypePos = (subTypeInBody != -1 && subTypeInBody < aDictOpen)
|
||||||
|
? out.size() - (aDictOpen - subTypeInBody) : -1;
|
||||||
|
if (subTypePos != -1)
|
||||||
|
// Acrobat draws its own sticky-note icon for /Subtype /Text
|
||||||
|
// whatever /AP says (verified on Reader 5.0). /Square with no
|
||||||
|
// /C and no /IC draws nothing anywhere, and is still a markup
|
||||||
|
// annotation, so the /Contents popup is unaffected.
|
||||||
|
out.replace(subTypePos, 14, "/Subtype /Square");
|
||||||
|
|
||||||
|
// Encode as UTF-16BE hex with BOM for proper Unicode support (Umlauten etc.).
|
||||||
|
// PDF spec: hex strings starting with FE FF are interpreted as UTF-16BE.
|
||||||
|
QByteArray utf16be;
|
||||||
|
utf16be.append('\xfe');
|
||||||
|
utf16be.append('\xff');
|
||||||
|
{
|
||||||
|
for (int i = 0; i < contents.size(); ) {
|
||||||
|
ushort cp = 0;
|
||||||
|
uchar c = static_cast<uchar>(contents.at(i));
|
||||||
|
if (c < 0x80) {
|
||||||
|
cp = c;
|
||||||
|
++i;
|
||||||
|
} else if ((c & 0xE0) == 0xC0 && i + 1 < contents.size()) {
|
||||||
|
cp = ((c & 0x1F) << 6)
|
||||||
|
| (static_cast<uchar>(contents.at(i + 1)) & 0x3F);
|
||||||
|
i += 2;
|
||||||
|
} else if ((c & 0xF0) == 0xE0 && i + 2 < contents.size()) {
|
||||||
|
cp = ((c & 0x0F) << 12)
|
||||||
|
| ((static_cast<uchar>(contents.at(i + 1)) & 0x3F) << 6)
|
||||||
|
| (static_cast<uchar>(contents.at(i + 2)) & 0x3F);
|
||||||
|
i += 3;
|
||||||
|
} else {
|
||||||
|
cp = 0xFFFD; // replacement character
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
utf16be.append(static_cast<char>((cp >> 8) & 0xFF));
|
||||||
|
utf16be.append(static_cast<char>(cp & 0xFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out += "/Contents <";
|
||||||
|
out += utf16be.toHex();
|
||||||
|
out += ">\n/AP << /N " + QByteArray::number(emptyXObjNum) + " 0 R >>\n";
|
||||||
|
|
||||||
|
// Skip the entire /A << ... >> action dict.
|
||||||
|
// After closeParen ')' we have: \n>> (closes /A dict) \n>> (closes annot dict)
|
||||||
|
int actionDictClose = body.indexOf(">>", closeParen + 1);
|
||||||
|
if (actionDictClose != -1) {
|
||||||
|
pos = actionDictClose + 2; // skip past >> that closes /A dict
|
||||||
|
} else {
|
||||||
|
pos = closeParen + 1;
|
||||||
|
}
|
||||||
|
anyConverted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyConverted) return;
|
||||||
|
|
||||||
|
// Append empty Form XObject (shared by all annotations for invisible appearance)
|
||||||
|
QByteArray emptyXObj;
|
||||||
|
emptyXObj += QByteArray::number(emptyXObjNum) + " 0 obj\n";
|
||||||
|
emptyXObj += "<< /Type /XObject /Subtype /Form /BBox [0 0 0 0] /Length 0 >>\n";
|
||||||
|
emptyXObj += "stream\nendstream\n";
|
||||||
|
emptyXObj += "endobj\n";
|
||||||
|
out += emptyXObj;
|
||||||
|
|
||||||
|
// Rebuild xref table
|
||||||
|
QMap<int, int> offsets;
|
||||||
|
{
|
||||||
|
const QByteArray objMarker(" 0 obj");
|
||||||
|
int p = 0;
|
||||||
|
while ((p = out.indexOf(objMarker, p)) != -1) {
|
||||||
|
int numStart = p - 1;
|
||||||
|
while (numStart > 0 && out[numStart - 1] != '\n' && out[numStart - 1] != '\r')
|
||||||
|
--numStart;
|
||||||
|
QByteArray numStr = out.mid(numStart, p - numStart).trimmed();
|
||||||
|
bool ok = false;
|
||||||
|
int objNum = numStr.toInt(&ok);
|
||||||
|
if (ok && objNum > 0)
|
||||||
|
offsets[objNum] = numStart;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsets.isEmpty()) return;
|
||||||
|
|
||||||
|
int maxObj = offsets.lastKey();
|
||||||
|
|
||||||
|
QByteArray xref;
|
||||||
|
xref += "xref\n";
|
||||||
|
xref += "0 " + QByteArray::number(maxObj + 1) + "\n";
|
||||||
|
xref += "0000000000 65535 f \n";
|
||||||
|
for (int i = 1; i <= maxObj; ++i) {
|
||||||
|
if (offsets.contains(i)) {
|
||||||
|
xref += QByteArray::number(offsets[i]).rightJustified(10, '0')
|
||||||
|
+ " 00000 n \n";
|
||||||
|
} else {
|
||||||
|
xref += "0000000000 65535 f \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy trailer and bump /Size to account for the new XObject
|
||||||
|
QByteArray trailer;
|
||||||
|
{
|
||||||
|
int tPos = data.indexOf("trailer", xrefStart);
|
||||||
|
if (tPos != -1) {
|
||||||
|
int tEnd = data.indexOf("%%EOF", tPos);
|
||||||
|
if (tEnd != -1) tEnd += 5;
|
||||||
|
if (tEnd != -1)
|
||||||
|
trailer = data.mid(tPos, tEnd - tPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trailer.isEmpty())
|
||||||
|
trailer = "trailer\n<<>>\n%%EOF";
|
||||||
|
|
||||||
|
// Bump /Size: original was maxObjNum+1, now it's emptyXObjNum+1
|
||||||
|
{
|
||||||
|
int sizePos = trailer.indexOf("/Size ");
|
||||||
|
if (sizePos != -1) {
|
||||||
|
int numStart = sizePos + 6;
|
||||||
|
int numEnd = numStart;
|
||||||
|
while (numEnd < trailer.size() && trailer[numEnd] >= '0' && trailer[numEnd] <= '9')
|
||||||
|
++numEnd;
|
||||||
|
if (numEnd > numStart) {
|
||||||
|
trailer.replace(numStart, numEnd - numStart,
|
||||||
|
QByteArray::number(emptyXObjNum + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicate startxref if present in copied trailer
|
||||||
|
{
|
||||||
|
int stPos = trailer.indexOf("\nstartxref\n");
|
||||||
|
if (stPos != -1)
|
||||||
|
trailer = trailer.left(stPos);
|
||||||
|
// Ensure trailer ends with %%EOF
|
||||||
|
if (!trailer.endsWith("%%EOF\n"))
|
||||||
|
trailer += "\n%%EOF\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray result;
|
||||||
|
result.reserve(out.size() + xref.size() + trailer.size() + 64);
|
||||||
|
result += out;
|
||||||
|
int newXrefOffset = out.size();
|
||||||
|
result += xref;
|
||||||
|
result += trailer;
|
||||||
|
result += "\nstartxref\n";
|
||||||
|
result += QByteArray::number(newXrefOffset);
|
||||||
|
result += "\n%%EOF\n";
|
||||||
|
|
||||||
|
QFile outF(pdfPath);
|
||||||
|
if (!outF.open(QIODevice::WriteOnly | QIODevice::Truncate)) return;
|
||||||
|
outF.write(result);
|
||||||
|
outF.close();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace PdfLinks
|
} // namespace PdfLinks
|
||||||
|
|||||||
@@ -74,6 +74,18 @@ namespace PdfLinks {
|
|||||||
*/
|
*/
|
||||||
void convertUriToGoTo(const QString &pdfPath);
|
void convertUriToGoTo(const QString &pdfPath);
|
||||||
|
|
||||||
|
struct ComponentInfo {
|
||||||
|
QString contents;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Post-process a Qt-generated PDF file: convert component-info placeholder
|
||||||
|
link annotations (http://componentinfo.local/<N>) into invisible text
|
||||||
|
annotations with the actual component info as /Contents.
|
||||||
|
*/
|
||||||
|
void convertComponentInfoAnnotations(const QString &pdfPath,
|
||||||
|
const QList<ComponentInfo> &annotations);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PDF_LINKS_H
|
#endif // PDF_LINKS_H
|
||||||
|
|||||||
@@ -20,10 +20,12 @@
|
|||||||
#include "../diagram.h"
|
#include "../diagram.h"
|
||||||
#include "../pdf_links.h"
|
#include "../pdf_links.h"
|
||||||
#include "../qeticons.h"
|
#include "../qeticons.h"
|
||||||
|
#include "../qetinformation.h"
|
||||||
#include "../qetproject.h"
|
#include "../qetproject.h"
|
||||||
#include "../qetversion.h"
|
#include "../qetversion.h"
|
||||||
#include "../qetgraphicsitem/crossrefitem.h"
|
#include "../qetgraphicsitem/crossrefitem.h"
|
||||||
#include "../qetgraphicsitem/dynamicelementtextitem.h"
|
#include "../qetgraphicsitem/dynamicelementtextitem.h"
|
||||||
|
#include "../qetgraphicsitem/element.h"
|
||||||
#include "../qetgraphicsitem/elementtextitemgroup.h"
|
#include "../qetgraphicsitem/elementtextitemgroup.h"
|
||||||
|
|
||||||
#include "ui_projectprintwindow.h"
|
#include "ui_projectprintwindow.h"
|
||||||
@@ -153,6 +155,7 @@ ProjectPrintWindow::ProjectPrintWindow(QETProject *project, QPrinter *printer, Q
|
|||||||
ui->m_button_box->addButton(pdf_button, QDialogButtonBox::ActionRole);
|
ui->m_button_box->addButton(pdf_button, QDialogButtonBox::ActionRole);
|
||||||
connect(pdf_button, &QPushButton::clicked, this, &ProjectPrintWindow::exportToPDF);
|
connect(pdf_button, &QPushButton::clicked, this, &ProjectPrintWindow::exportToPDF);
|
||||||
}
|
}
|
||||||
|
ui->m_component_info_cb->setVisible(m_printer->outputFormat() == QPrinter::PdfFormat);
|
||||||
|
|
||||||
auto exp = ExportProperties::defaultPrintProperties();
|
auto exp = ExportProperties::defaultPrintProperties();
|
||||||
ui->m_draw_border_cb->setChecked(exp.draw_border);
|
ui->m_draw_border_cb->setChecked(exp.draw_border);
|
||||||
@@ -248,6 +251,7 @@ void ProjectPrintWindow::requestPaint()
|
|||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
QPainter painter(m_printer);
|
QPainter painter(m_printer);
|
||||||
|
int annotIndex = 0;
|
||||||
|
|
||||||
// A real PDF export uses the QPdfEngine; the on-screen preview uses a
|
// A real PDF export uses the QPdfEngine; the on-screen preview uses a
|
||||||
// preview paint engine. We only post-process when actually writing a PDF.
|
// preview paint engine. We only post-process when actually writing a PDF.
|
||||||
@@ -261,7 +265,7 @@ void ProjectPrintWindow::requestPaint()
|
|||||||
for (auto diagram : selectedDiagram())
|
for (auto diagram : selectedDiagram())
|
||||||
{
|
{
|
||||||
first ? first = false : m_printer->newPage();
|
first ? first = false : m_printer->newPage();
|
||||||
printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer, diagramPageMap);
|
printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer, diagramPageMap, annotIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: do NOT call painter.end() or pdfConvertUriToGoTo() here.
|
// Note: do NOT call painter.end() or pdfConvertUriToGoTo() here.
|
||||||
@@ -279,7 +283,7 @@ void ProjectPrintWindow::requestPaint()
|
|||||||
* @param fit_page
|
* @param fit_page
|
||||||
* @param printer
|
* @param printer
|
||||||
*/
|
*/
|
||||||
void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap)
|
void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap, int &annotIndex)
|
||||||
{
|
{
|
||||||
|
|
||||||
////Prepare the print////
|
////Prepare the print////
|
||||||
@@ -415,6 +419,50 @@ void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter
|
|||||||
PdfLinks::injectCrossRefLinks(
|
PdfLinks::injectCrossRefLinks(
|
||||||
pdfEngine, diagram, geom, diagramPageMap,
|
pdfEngine, diagram, geom, diagramPageMap,
|
||||||
printer->outputFileName());
|
printer->outputFileName());
|
||||||
|
|
||||||
|
////Collect component info for popup annotations////
|
||||||
|
if (ui->m_component_info_cb->isChecked()) {
|
||||||
|
for (auto *item : diagram->items()) {
|
||||||
|
auto *el = qgraphicsitem_cast<Element*>(item);
|
||||||
|
if (!el) continue;
|
||||||
|
|
||||||
|
// Skip reports and slaves
|
||||||
|
auto lt = el->linkType();
|
||||||
|
if (lt & Element::AllReport) continue;
|
||||||
|
if (lt == Element::Slave) continue;
|
||||||
|
|
||||||
|
auto info = el->elementInformations();
|
||||||
|
if (info.count() == 0) continue;
|
||||||
|
|
||||||
|
// Build info text
|
||||||
|
QStringList lines;
|
||||||
|
for (const QString &key : {"label", "manufacturer", "designation", "description"}) {
|
||||||
|
if (info.contains(key) && !info.value(key).toString().isEmpty())
|
||||||
|
lines << QETInformation::translatedInfoKey(key) + ": " + info.value(key).toString();
|
||||||
|
}
|
||||||
|
for (const QString &key : info.keys()) {
|
||||||
|
if (key == "formula") continue;
|
||||||
|
QString translated = QETInformation::translatedInfoKey(key);
|
||||||
|
if (lines.contains(translated + ": " + info.value(key).toString())) continue;
|
||||||
|
if (info.value(key).toString().isEmpty()) continue;
|
||||||
|
lines << translated + ": " + info.value(key).toString();
|
||||||
|
}
|
||||||
|
if (lines.isEmpty()) continue;
|
||||||
|
|
||||||
|
// Compute element rect in device pixels
|
||||||
|
QRectF elemScene = el->mapRectToScene(el->boundingRect());
|
||||||
|
QRectF devRect = fit.mapRect(elemScene);
|
||||||
|
|
||||||
|
PdfLinks::ComponentInfo ci;
|
||||||
|
ci.contents = lines.join("\n");
|
||||||
|
m_componentInfoList.append(ci);
|
||||||
|
|
||||||
|
// Create a link annotation as placeholder — post-processing
|
||||||
|
// will convert it to an invisible text annotation with the actual content
|
||||||
|
pdfEngine->drawHyperlink(devRect, QUrl(QString("http://componentinfo.local/%1").arg(annotIndex++)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////Component info end////
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
////PDF links end////
|
////PDF links end////
|
||||||
@@ -715,6 +763,7 @@ void ProjectPrintWindow::on_m_draw_titleblock_cb_clicked() { m_preview->upd
|
|||||||
void ProjectPrintWindow::on_m_keep_conductor_color_cb_clicked() { m_preview->updatePreview(); }
|
void ProjectPrintWindow::on_m_keep_conductor_color_cb_clicked() { m_preview->updatePreview(); }
|
||||||
void ProjectPrintWindow::on_m_draw_terminal_cb_clicked() { m_preview->updatePreview(); }
|
void ProjectPrintWindow::on_m_draw_terminal_cb_clicked() { m_preview->updatePreview(); }
|
||||||
void ProjectPrintWindow::on_m_draw_terminal_names_cb_clicked() { m_preview->updatePreview(); }
|
void ProjectPrintWindow::on_m_draw_terminal_names_cb_clicked() { m_preview->updatePreview(); }
|
||||||
|
void ProjectPrintWindow::on_m_component_info_cb_clicked() { m_preview->updatePreview(); }
|
||||||
void ProjectPrintWindow::on_m_fit_in_page_cb_clicked() { m_preview->updatePreview(); }
|
void ProjectPrintWindow::on_m_fit_in_page_cb_clicked() { m_preview->updatePreview(); }
|
||||||
void ProjectPrintWindow::on_m_use_full_page_cb_clicked()
|
void ProjectPrintWindow::on_m_use_full_page_cb_clicked()
|
||||||
{
|
{
|
||||||
@@ -819,9 +868,16 @@ void ProjectPrintWindow::print()
|
|||||||
// which prevents the black screen observed on Apple Silicon under
|
// which prevents the black screen observed on Apple Silicon under
|
||||||
// macOS Sequoia (QPrintPreviewWidget + CALayer timing issue).
|
// macOS Sequoia (QPrintPreviewWidget + CALayer timing issue).
|
||||||
QTimer::singleShot(0, this, [this, pdfFile]() {
|
QTimer::singleShot(0, this, [this, pdfFile]() {
|
||||||
|
// Convert component-info link annotations into invisible text annotations
|
||||||
|
if (ui->m_component_info_cb->isChecked() && !m_componentInfoList.isEmpty()) {
|
||||||
|
PdfLinks::convertComponentInfoAnnotations(pdfFile, m_componentInfoList);
|
||||||
|
m_componentInfoList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Convert URI link annotations into native internal GoTo/FitR
|
// Convert URI link annotations into native internal GoTo/FitR
|
||||||
// actions so cross-references jump inside the document.
|
// actions so cross-references jump inside the document.
|
||||||
PdfLinks::convertUriToGoTo(pdfFile);
|
PdfLinks::convertUriToGoTo(pdfFile);
|
||||||
|
|
||||||
this->close();
|
this->close();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#define PROJECTPRINTWINDOW_H
|
#define PROJECTPRINTWINDOW_H
|
||||||
|
|
||||||
#include "../exportproperties.h"
|
#include "../exportproperties.h"
|
||||||
|
#include "../pdf_links.h"
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
@@ -56,6 +57,7 @@ class ProjectPrintWindow : public QMainWindow
|
|||||||
void on_m_keep_conductor_color_cb_clicked();
|
void on_m_keep_conductor_color_cb_clicked();
|
||||||
void on_m_draw_terminal_cb_clicked();
|
void on_m_draw_terminal_cb_clicked();
|
||||||
void on_m_draw_terminal_names_cb_clicked();
|
void on_m_draw_terminal_names_cb_clicked();
|
||||||
|
void on_m_component_info_cb_clicked();
|
||||||
void on_m_fit_in_page_cb_clicked();
|
void on_m_fit_in_page_cb_clicked();
|
||||||
void on_m_use_full_page_cb_clicked();
|
void on_m_use_full_page_cb_clicked();
|
||||||
void on_m_zoom_out_action_triggered();
|
void on_m_zoom_out_action_triggered();
|
||||||
@@ -81,7 +83,7 @@ class ProjectPrintWindow : public QMainWindow
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void requestPaint();
|
void requestPaint();
|
||||||
void printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap = {});
|
void printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap, int &annotIndex);
|
||||||
QRect diagramRect(Diagram *diagram, const ExportProperties &option) const;
|
QRect diagramRect(Diagram *diagram, const ExportProperties &option) const;
|
||||||
int horizontalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
int horizontalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
||||||
int verticalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
int verticalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
||||||
@@ -102,6 +104,7 @@ class ProjectPrintWindow : public QMainWindow
|
|||||||
QPrintPreviewWidget *m_preview=nullptr;
|
QPrintPreviewWidget *m_preview=nullptr;
|
||||||
QColor m_backup_diagram_background_color;
|
QColor m_backup_diagram_background_color;
|
||||||
QHash<Diagram *, QCheckBox *> m_diagram_list_hash;
|
QHash<Diagram *, QCheckBox *> m_diagram_list_hash;
|
||||||
|
QList<PdfLinks::ComponentInfo> m_componentInfoList;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PROJECTPRINTWINDOW_H
|
#endif // PROJECTPRINTWINDOW_H
|
||||||
|
|||||||
@@ -183,17 +183,27 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="m_draw_terminal_names_cb">
|
<widget class="QCheckBox" name="m_draw_terminal_names_cb">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Dessiner les noms des bornes</string>
|
<string>Dessiner les noms des bornes</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
<item>
|
||||||
|
<widget class="QCheckBox" name="m_component_info_cb">
|
||||||
|
<property name="text">
|
||||||
|
<string>Inscrire les informations des composants</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
Reference in New Issue
Block a user