mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-21 15:44:14 +02:00
fix
This commit is contained in:
+89
-29
@@ -32,7 +32,6 @@
|
|||||||
#include <QGraphicsTextItem>
|
#include <QGraphicsTextItem>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QTextCodec>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
@@ -399,8 +398,8 @@ void convertComponentInfoAnnotations(const QString &pdfPath,
|
|||||||
QByteArray data = f.readAll();
|
QByteArray data = f.readAll();
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
const QByteArray marker("http://componentinfo.local/");
|
const QByteArray markerPrefix("http://componentinfo.local/");
|
||||||
if (!data.contains(marker)) return;
|
if (!data.contains(markerPrefix)) return;
|
||||||
|
|
||||||
int xrefStart = data.lastIndexOf("\nxref\n");
|
int xrefStart = data.lastIndexOf("\nxref\n");
|
||||||
if (xrefStart == -1) xrefStart = data.lastIndexOf("\nxref ");
|
if (xrefStart == -1) xrefStart = data.lastIndexOf("\nxref ");
|
||||||
@@ -433,20 +432,34 @@ void convertComponentInfoAnnotations(const QString &pdfPath,
|
|||||||
out.reserve(data.size());
|
out.reserve(data.size());
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int annotIdx = 0;
|
bool anyConverted = false;
|
||||||
|
|
||||||
while (pos < body.size()) {
|
while (pos < body.size()) {
|
||||||
int markerPos = body.indexOf(marker, pos);
|
// Find next indexed marker: http://componentinfo.local/<N>
|
||||||
|
int markerPos = body.indexOf(markerPrefix, pos);
|
||||||
if (markerPos == -1) {
|
if (markerPos == -1) {
|
||||||
out.append(body.mid(pos));
|
out.append(body.mid(pos));
|
||||||
break;
|
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 uriOpen = body.lastIndexOf("/URI (", markerPos);
|
||||||
int closeParen = (uriOpen != -1) ? body.indexOf(')', markerPos) : -1;
|
int closeParen = (uriOpen != -1) ? body.indexOf(')', markerPos) : -1;
|
||||||
if (uriOpen == -1 || closeParen == -1 || uriOpen < pos) {
|
if (uriOpen == -1 || closeParen == -1 || uriOpen < pos) {
|
||||||
out.append(body.mid(pos, markerPos + marker.size() - pos));
|
out.append(body.mid(pos, idxEnd - pos));
|
||||||
pos = markerPos + marker.size();
|
pos = idxEnd;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,43 +468,63 @@ void convertComponentInfoAnnotations(const QString &pdfPath,
|
|||||||
|
|
||||||
// Find the /A << that opens the action dict containing /S /URI.
|
// Find the /A << that opens the action dict containing /S /URI.
|
||||||
// Qt always writes: /A <<\n/S /URI\n/URI (...)\n>>\n>>
|
// Qt always writes: /A <<\n/S /URI\n/URI (...)\n>>\n>>
|
||||||
// We need /Contents at the annotation level, NOT inside /A <<.
|
int aDictOpen = (sUriPos != -1)
|
||||||
int aDictOpen = body.lastIndexOf("/A <<", (sUriPos != -1) ? sUriPos : uriOpen);
|
? body.lastIndexOf("/A <<", sUriPos)
|
||||||
int copyEnd = (aDictOpen != -1 && aDictOpen >= pos) ? aDictOpen
|
: -1;
|
||||||
: (sUriPos != -1 && sUriPos >= pos) ? sUriPos
|
|
||||||
: uriOpen;
|
|
||||||
out.append(body.mid(pos, copyEnd - pos));
|
|
||||||
|
|
||||||
// Get text from annotations list (matched by order)
|
// If /A << is not found, leave this annotation untouched
|
||||||
if (annotIdx >= annotations.size()) {
|
if (aDictOpen == -1 || aDictOpen < pos) {
|
||||||
// Keep original action dict intact
|
out.append(body.mid(pos, closeParen + 1 - pos));
|
||||||
out.append(body.mid(copyEnd, closeParen + 1 - copyEnd));
|
|
||||||
pos = closeParen + 1;
|
pos = closeParen + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray contents = annotations[annotIdx].contents.toUtf8();
|
// Validate index
|
||||||
++annotIdx;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// Replace /Subtype /Link with /Subtype /Text
|
// Copy annotation dict header up to /A <<
|
||||||
int subTypePos = out.lastIndexOf("/Subtype /Link");
|
out.append(body.mid(pos, aDictOpen - pos));
|
||||||
|
|
||||||
|
QByteArray contents = annotations[annotIndex].contents.toUtf8();
|
||||||
|
|
||||||
|
// Replace /Subtype /Link with /Subtype /Text, bounded to current object.
|
||||||
|
// Search only the bytes we just appended (the current annotation header).
|
||||||
|
int searchStart = out.size() - (aDictOpen - pos);
|
||||||
|
int subTypePos = out.indexOf("/Subtype /Link", searchStart);
|
||||||
if (subTypePos != -1) {
|
if (subTypePos != -1) {
|
||||||
out.replace(subTypePos, 14, "/Subtype /Text");
|
out.replace(subTypePos, 14, "/Subtype /Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode as UTF-16BE hex with BOM for proper Unicode support (Umlauten etc.).
|
// 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.
|
// PDF spec: hex strings starting with FE FF are interpreted as UTF-16BE.
|
||||||
QTextCodec *codec = QTextCodec::codecForName("UTF-16BE");
|
|
||||||
QByteArray utf16be;
|
QByteArray utf16be;
|
||||||
utf16be.append('\xfe');
|
utf16be.append('\xfe');
|
||||||
utf16be.append('\xff');
|
utf16be.append('\xff');
|
||||||
if (codec) {
|
{
|
||||||
utf16be += codec->fromUnicode(QString::fromUtf8(contents));
|
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 {
|
} else {
|
||||||
// Fallback: manual UTF-16BE encoding
|
cp = 0xFFFD; // replacement character
|
||||||
QString str = QString::fromUtf8(contents);
|
++i;
|
||||||
for (int i = 0; i < str.size(); ++i) {
|
}
|
||||||
ushort cp = str.at(i).unicode();
|
|
||||||
utf16be.append(static_cast<char>((cp >> 8) & 0xFF));
|
utf16be.append(static_cast<char>((cp >> 8) & 0xFF));
|
||||||
utf16be.append(static_cast<char>(cp & 0xFF));
|
utf16be.append(static_cast<char>(cp & 0xFF));
|
||||||
}
|
}
|
||||||
@@ -509,9 +542,10 @@ void convertComponentInfoAnnotations(const QString &pdfPath,
|
|||||||
} else {
|
} else {
|
||||||
pos = closeParen + 1;
|
pos = closeParen + 1;
|
||||||
}
|
}
|
||||||
|
anyConverted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotIdx == 0) return;
|
if (!anyConverted) return;
|
||||||
|
|
||||||
// Append empty Form XObject (shared by all annotations for invisible appearance)
|
// Append empty Form XObject (shared by all annotations for invisible appearance)
|
||||||
QByteArray emptyXObj;
|
QByteArray emptyXObj;
|
||||||
@@ -556,6 +590,7 @@ void convertComponentInfoAnnotations(const QString &pdfPath,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy trailer and bump /Size to account for the new XObject
|
||||||
QByteArray trailer;
|
QByteArray trailer;
|
||||||
{
|
{
|
||||||
int tPos = data.indexOf("trailer", xrefStart);
|
int tPos = data.indexOf("trailer", xrefStart);
|
||||||
@@ -569,6 +604,31 @@ void convertComponentInfoAnnotations(const QString &pdfPath,
|
|||||||
if (trailer.isEmpty())
|
if (trailer.isEmpty())
|
||||||
trailer = "trailer\n<<>>\n%%EOF";
|
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;
|
QByteArray result;
|
||||||
result.reserve(out.size() + xref.size() + trailer.size() + 64);
|
result.reserve(out.size() + xref.size() + trailer.size() + 64);
|
||||||
result += out;
|
result += out;
|
||||||
|
|||||||
+11
-6
@@ -66,19 +66,24 @@ namespace PdfLinks {
|
|||||||
const QMap<Diagram *, int> &pageMap,
|
const QMap<Diagram *, int> &pageMap,
|
||||||
const QString &outputFileName);
|
const QString &outputFileName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Post-process a Qt-generated PDF file: rewrite every "/S /URI" link
|
Post-process a Qt-generated PDF file: rewrite every "/S /URI" link
|
||||||
annotation into a native internal "/S /GoTo" action (page + /FitR or
|
annotation into a native internal "/S /GoTo" action (page + /FitR or
|
||||||
/Fit destination) and rebuild the xref table. No-op if the file has no
|
/Fit destination) and rebuild the xref table. No-op if the file has no
|
||||||
such annotations.
|
such annotations.
|
||||||
*/
|
*/
|
||||||
void convertUriToGoTo(const QString &pdfPath);
|
void convertUriToGoTo(const QString &pdfPath);
|
||||||
|
|
||||||
struct ComponentInfo {
|
struct ComponentInfo {
|
||||||
QString contents;
|
QString contents;
|
||||||
};
|
};
|
||||||
|
|
||||||
void convertComponentInfoAnnotations(const QString &pdfPath,
|
/**
|
||||||
|
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);
|
const QList<ComponentInfo> &annotations);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -432,8 +432,9 @@ void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter
|
|||||||
|
|
||||||
////Collect component info for popup annotations////
|
////Collect component info for popup annotations////
|
||||||
if (ui->m_component_info_cb->isChecked()) {
|
if (ui->m_component_info_cb->isChecked()) {
|
||||||
|
int annotIndex = 0;
|
||||||
for (auto *item : diagram->items()) {
|
for (auto *item : diagram->items()) {
|
||||||
auto *el = dynamic_cast<Element*>(item);
|
auto *el = qgraphicsitem_cast<Element*>(item);
|
||||||
if (!el) continue;
|
if (!el) continue;
|
||||||
|
|
||||||
// Skip reports and slaves
|
// Skip reports and slaves
|
||||||
@@ -469,7 +470,7 @@ void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter
|
|||||||
|
|
||||||
// Create a link annotation as placeholder — post-processing
|
// Create a link annotation as placeholder — post-processing
|
||||||
// will convert it to an invisible text annotation with the actual content
|
// will convert it to an invisible text annotation with the actual content
|
||||||
pdfEngine->drawHyperlink(devRect, QUrl("http://componentinfo.local/"));
|
pdfEngine->drawHyperlink(devRect, QUrl(QString("http://componentinfo.local/%1").arg(annotIndex++)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
////Component info end////
|
////Component info end////
|
||||||
|
|||||||
Reference in New Issue
Block a user