mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-03 17:34:12 +02:00
51d20abca4
Two exports of one project differed in the creation and modification dates (the time of the export, in local time) and in the document id (random). With SOURCE_DATE_EPOCH set (reproducible-builds.org), --export-pdf now uses the time it names, in UTC, and a document id derived from the project: version 5 of the project uuid with the SHA-256 of the project file, so it is the same for the same file and changes when the file does. Qt has no setter for the dates, so PdfLinks::setDocumentDate() rewrites them in the document information and the XMP metadata after the file is written, fixing the XMP /Length and shifting the xref table. Without the variable nothing changes. Only --export-pdf reads it; the print window's PDF export does not, as a person exporting by hand wants the real date. qet_export (misc/qet-mcp) gains "reproducible" and "source_date_epoch". It reports whether the build honoured the variable, since an older one ignores it. tst_pdfreproducible exports examples/741.qet four times and requires identical bytes and the epoch's date; it fails with either this change or the hash seed fix reverted. Projects with cross-reference links also need their links in a fixed order, which is a separate fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
4.0 KiB
C++
112 lines
4.0 KiB
C++
/*
|
|
Copyright 2006-2025 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef PDF_LINKS_H
|
|
#define PDF_LINKS_H
|
|
|
|
#include <QDateTime>
|
|
#include <QMap>
|
|
#include <QPointF>
|
|
#include <QRectF>
|
|
#include <QString>
|
|
#include <QTransform>
|
|
#include <functional>
|
|
|
|
class QPdfEngine;
|
|
class Diagram;
|
|
|
|
/**
|
|
Shared helper that turns a project's cross-references and folio reports
|
|
into clickable internal hyperlinks in a Qt-generated PDF. Used by both the
|
|
GUI print path (ProjectPrintWindow) and the headless CLI export, each of
|
|
which builds its own page geometry and passes it in — this code never
|
|
computes the scene-to-page mapping itself.
|
|
*/
|
|
namespace PdfLinks {
|
|
|
|
/**
|
|
Geometry mapping for one rendered PDF page. Each caller builds this
|
|
from its OWN page setup (printer page layout vs QPdfWriter), since the
|
|
device-pixel and point conversions differ between them.
|
|
*/
|
|
struct PageGeometry {
|
|
/// scene coordinates -> device pixels (the same "fit" render() applied)
|
|
QTransform sceneToDevice;
|
|
/// device paint rectangle, in pixels (the page area)
|
|
QRectF target;
|
|
/// links whose rectangle falls outside this are dropped
|
|
QRectF pageBounds;
|
|
/// device pixels -> PDF points (replicates the engine's page matrix)
|
|
std::function<QPointF(const QPointF &)> devToPdf;
|
|
/// a diagram -> its source rectangle in scene pixels (for /FitR framing)
|
|
std::function<QRectF(Diagram *)> sourceRectOf;
|
|
};
|
|
|
|
/**
|
|
Inject clickable cross-reference / folio-report hyperlinks for @p diagram
|
|
into the current page of @p engine. Each link is emitted as a URI
|
|
annotation encoding the target page and a /FitR rectangle;
|
|
convertUriToGoTo() then rewrites those into native internal GoTo actions.
|
|
*/
|
|
void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram,
|
|
const PageGeometry &geom,
|
|
const QMap<Diagram *, int> &pageMap,
|
|
const QString &outputFileName);
|
|
|
|
/**
|
|
Post-process a Qt-generated PDF file: rewrite every "/S /URI" link
|
|
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
|
|
such annotations.
|
|
*/
|
|
void convertUriToGoTo(const QString &pdfPath);
|
|
|
|
/**
|
|
Post-process a Qt-generated PDF file: blank out the PDF/X namespace
|
|
declaration Qt 6 writes into the XMP metadata of every PDF. Adobe
|
|
Acrobat draws small text too bold when it is present (bugtracker #340).
|
|
Replaced in place with spaces, so no offset changes. No-op for a real
|
|
PDF/X file or when the declaration is absent.
|
|
*/
|
|
void removeUnusedPdfxNamespace(const QString &pdfPath);
|
|
|
|
/**
|
|
Post-process a Qt-generated PDF file: replace the creation and
|
|
modification dates Qt wrote (the time of the export, in local time)
|
|
with @p when, in UTC, in the document information and the XMP
|
|
metadata, and shift the xref table to match. Used for a repeatable
|
|
export (SOURCE_DATE_EPOCH). Leaves the file alone if it is not laid
|
|
out as Qt writes it.
|
|
*/
|
|
void setDocumentDate(const QString &pdfPath, const QDateTime &when);
|
|
|
|
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
|