mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-06-10 12:53:14 +02:00
This adds native, clickable hyperlinks to PDF exports: cross-references jump
directly to the related component on its folio, framing the target element.
When a project is exported to PDF, every cross-reference becomes an internal
link. Four kinds are covered:
- **Master → contact**: the contact list on a coil/relay (`CrossRefItem`)
- **Folio report → report**: report element labels (`DynamicElementTextItem`)
- **Slave → master**: the `(folio-position)` reference shown on a slave
(both standalone `DynamicElementTextItem` and grouped `ElementTextItemGroup`)
Clicking a link navigates **inside** the open document (no new viewer
instance) and zooms to frame the target element.
1. **Injection** (`printDiagram`, only when the paint engine is a `QPdfEngine`):
link rectangles are added with `QPdfEngine::drawHyperlink()`. The scene→page
mapping is rebuilt to match exactly what `QGraphicsScene::render()` does
(top-left anchored, `KeepAspectRatio`, **no centering**), and rectangles are
passed in device pixels — `pageMatrix()` already applies the 72/resolution
scale and Y-flip internally.
2. Each link URL encodes the target page and the target element's rectangle, in
PDF points on its own page: `#page=N&fitr=L_B_R_T`.
3. **Post-processing** (`pdfConvertUriToGoTo`, run after the painter is closed):
the `/S /URI` annotations are rewritten to native `/S /GoTo` actions with a
`/D [pageObj 0 R /FitR L B R T]` destination, and the xref table is rebuilt.
Pages are enumerated from the `/Pages /Kids` tree (reliable), not by scanning
for `/Type /Page` in raw bytes.
- `sources/print/projectprintwindow.{cpp,h}` — injection + post-processing
- `sources/qetgraphicsitem/crossrefitem.{cpp,h}` — `hoveredContactsMap()` accessor; store text rect for hit area
- `sources/qetgraphicsitem/dynamicelementtextitem.h` — `slaveXrefItem()` / `masterElement()` accessors
- `sources/qetgraphicsitem/elementtextitemgroup.h` — `slaveXrefItem()` accessor
- `qelectrotech.pro`, `cmake/qet_compilation_vars.cmake` — enable Qt gui-private headers (`<private/qpdf_p.h>`)
- **Fit-to-page mode only.** Links are not injected in tiled mode (multiple
pages per folio), which would require a per-tile transform.
- Uses Qt private API (`QPdfEngine::drawHyperlink`), stable since Qt 4 but not
part of the public API; the build links against `gui-private`.
- Page-tree enumeration assumes the flat `/Kids` array Qt produces (no nested
page trees).
- The frame zoom is controlled by two constants in `destRectPdf` (`pad`,
`minSide`) and can be tuned.
- Tested on Qt5; the `/Kids` parsing and `pageMatrix` behaviour are identical on
Qt6.
This commit is contained in:
@@ -928,7 +928,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
|
||||
if (m_update_map)
|
||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
||||
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||
|
||||
++m_drawed_contacts;
|
||||
}
|
||||
@@ -1012,7 +1012,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
|
||||
if (m_update_map)
|
||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
||||
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||
|
||||
//a switch contact take place of two normal contact
|
||||
m_drawed_contacts += 2;
|
||||
@@ -1044,7 +1044,7 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, in
|
||||
bounding_rect = bounding_rect.united(text_rect);
|
||||
|
||||
if (m_update_map)
|
||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
||||
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||
++m_drawed_contacts;
|
||||
}
|
||||
return bounding_rect;
|
||||
|
||||
@@ -126,6 +126,12 @@ class CrossRefItem : public QGraphicsObject
|
||||
ElementTextItemGroup *m_group = nullptr;
|
||||
QList <QMetaObject::Connection> m_slave_connection;
|
||||
QList <QMetaObject::Connection> m_update_connection;
|
||||
|
||||
public:
|
||||
/// Returns the map of linked elements and their clickable rects (local coords).
|
||||
/// Used by the PDF export to inject hyperlink annotations.
|
||||
const QMultiMap<Element *, QRectF> &hoveredContactsMap() const
|
||||
{ return m_hovered_contacts_map; }
|
||||
};
|
||||
|
||||
#endif // CROSSREFITEM_H
|
||||
|
||||
@@ -86,6 +86,9 @@ class DynamicElementTextItem : public DiagramTextItem
|
||||
void fromXml(const QDomElement &dom_elmt) override;
|
||||
|
||||
Element *parentElement() const;
|
||||
/// PDF export: slave cross-reference text item ("(folio-pos)") and its master target.
|
||||
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||
Element *masterElement() const { return m_master_element.data(); }
|
||||
ElementTextItemGroup *parentGroup() const;
|
||||
Element *elementUseForInfo() const;
|
||||
void refreshLabelConnection();
|
||||
|
||||
@@ -76,6 +76,8 @@ class ElementTextItemGroup : public QObject, public QGraphicsItemGroup
|
||||
QList<DynamicElementTextItem *> texts() const;
|
||||
Diagram *diagram() const;
|
||||
Element *parentElement() const;
|
||||
/// PDF export: slave cross-reference text item of the group, if any.
|
||||
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||
|
||||
QDomElement toXml(QDomDocument &dom_document) const;
|
||||
void fromXml(QDomElement &dom_element);
|
||||
|
||||
Reference in New Issue
Block a user