7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-21 10:51:41 +00:00

Map a full hierarchy during PDF plotting

Re-create the schematic hierarchy in PDF plots to ease navigation
relative to the on screen schematic

Fixes https://gitlab.com/kicad/code/kicad/-/issues/12154
This commit is contained in:
Seth Hillbrand 2025-03-05 13:26:41 -08:00
parent f0e287a87b
commit 4a3b33df4e
5 changed files with 99 additions and 15 deletions

View File

@ -29,6 +29,7 @@
#include <algorithm>
#include <cstdio> // snprintf
#include <stack>
#include <wx/filename.h>
#include <wx/mstream.h>
@ -658,13 +659,19 @@ void PDF_PLOTTER::closePdfStream()
}
void PDF_PLOTTER::StartPage( const wxString& aPageNumber, const wxString& aPageName )
void PDF_PLOTTER::StartPage( const wxString& aPageNumber, const wxString& aPageName,
const wxString& aParentPageNumber, const wxString& aParentPageName )
{
wxASSERT( m_outputFile );
wxASSERT( !m_workFile );
m_pageNumbers.push_back( aPageNumber );
m_pageName = aPageName;
m_pageName = aPageName.IsEmpty()
? wxString::Format( _( "Page %s" ), aPageNumber )
: wxString::Format( _( "%s (Page %s)" ), aPageName, aPageNumber );
m_parentPageName = aParentPageName.IsEmpty()
? wxString::Format( _( "Page %s" ), aParentPageNumber )
: wxString::Format( _( "%s (Page %s)" ), aParentPageName, aParentPageNumber );
// Compute the paper size in IUs
m_paperSize = m_pageInfo.GetSizeMils();
@ -889,20 +896,34 @@ void PDF_PLOTTER::ClosePage()
// Mark the page stream as idle
m_pageStreamHandle = 0;
wxString pageOutlineName = wxEmptyString;
int actionHandle = emitGoToAction( pageHandle );
PDF_PLOTTER::OUTLINE_NODE* parent_node = m_outlineRoot.get();
if( m_pageName.IsEmpty() )
if( !m_parentPageName.IsEmpty() )
{
pageOutlineName = wxString::Format( _( "Page %s" ), m_pageNumbers.back() );
}
else
{
pageOutlineName = wxString::Format( _( "%s (Page %s)" ), m_pageName, m_pageNumbers.back() );
// Search for the parent node iteratively through the entire tree
std::stack<OUTLINE_NODE*> nodes;
nodes.push( m_outlineRoot.get() );
while( !nodes.empty() )
{
OUTLINE_NODE* node = nodes.top();
nodes.pop();
// Check if this node matches
if( node->title == m_parentPageName )
{
parent_node = node;
break;
}
// Add all children to the stack
for( OUTLINE_NODE* child : node->children )
nodes.push( child );
}
}
int actionHandle = emitGoToAction( pageHandle );
OUTLINE_NODE* pageOutlineNode =
addOutlineNode( m_outlineRoot.get(), actionHandle, pageOutlineName );
OUTLINE_NODE* pageOutlineNode = addOutlineNode( parent_node, actionHandle, m_pageName );
// let's reorg the symbol bookmarks under a page handle
// let's reorg the symbol bookmarks under a page handle

View File

@ -122,7 +122,7 @@ void SCH_PLOTTER::createPDFFile( const SCH_PLOT_OPTS& aPlotOpts,
if( aPlotOpts.m_plotAll || aPlotOpts.m_plotPages.size() > 0 )
{
sheetList.BuildSheetList( &m_schematic->Root(), true );
sheetList.SortByPageNumbers();
sheetList.SortByHierarchicalPageNumbers();
// remove the non-selected pages if we are in plot pages mode
if( aPlotOpts.m_plotPages.size() > 0 )
@ -226,7 +226,20 @@ void SCH_PLOTTER::createPDFFile( const SCH_PLOT_OPTS& aPlotOpts,
* reconfigure, and then start a new one */
plotter->ClosePage();
setupPlotPagePDF( plotter, screen, aPlotOpts );
plotter->StartPage( sheetList[i].GetPageNumber(), sheetName );
SCH_SHEET_PATH parentSheet = sheetList[i];
if( parentSheet.size() > 1 )
{
// The sheet path is the full path to the sheet, so we need to remove the last
// sheet name to get the parent sheet path
parentSheet.pop_back();
}
wxString parentSheetName =
parentSheet.Last()->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
plotter->StartPage( sheetList[i].GetPageNumber(), sheetName,
parentSheet.GetPageNumber(), parentSheetName );
}
plotOneSheetPDF( plotter, screen, aPlotOpts );

View File

@ -811,6 +811,44 @@ void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
}
void SCH_SHEET_LIST::SortByHierarchicalPageNumbers( bool aUpdateVirtualPageNums )
{
for( const SCH_SHEET_PATH& path : *this )
path.CachePageNumber();
std::sort( begin(), end(),
[]( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
{
if( a.size() != b.size() )
return a.size() < b.size();
int retval = SCH_SHEET::ComparePageNum( a.GetCachedPageNumber(),
b.GetCachedPageNumber() );
if( retval < 0 )
return true;
else if( retval > 0 )
return false;
if( a.GetVirtualPageNumber() < b.GetVirtualPageNumber() )
return true;
else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
return false;
// Enforce strict ordering. If the page numbers are the same, use UUIDs
return a.GetCurrentHash() < b.GetCurrentHash();
} );
if( aUpdateVirtualPageNums )
{
int virtualPageNum = 1;
for( SCH_SHEET_PATH& sheet : *this )
sheet.SetVirtualPageNumber( virtualPageNum++ );
}
}
void SCH_SHEET_LIST::SortByPageNumbers( bool aUpdateVirtualPageNums )
{
for( const SCH_SHEET_PATH& path : *this )

View File

@ -617,6 +617,13 @@ public:
*/
void SortByPageNumbers( bool aUpdateVirtualPageNums = true );
/**
* This works like #SortByPageNumbers, but it sorts the sheets first by their hierarchical
* depth and then by their page numbers. This ensures that printouts follow the
* hierarchical structure of the schematic.
*/
void SortByHierarchicalPageNumbers( bool aUpdateVirtualPageNums = true );
bool NameExists( const wxString& aSheetName ) const;
bool PageNumberExists( const wxString& aPageNumber ) const;

View File

@ -289,7 +289,9 @@ public:
* Start a new page in the PDF document.
*/
virtual void StartPage( const wxString& aPageNumber,
const wxString& aPageName = wxEmptyString );
const wxString& aPageName = wxEmptyString,
const wxString& aParentPageNumber = wxEmptyString,
const wxString& aParentPageName = wxEmptyString );
/**
* Close the current page in the PDF document (and emit its compressed stream).
@ -498,8 +500,11 @@ protected:
std::vector<int> m_pageHandles; ///< Handles to the page objects.
int m_pageStreamHandle; ///< Handle of the page content object.
int m_streamLengthHandle; ///< Handle to the deferred stream length.
wxString m_workFilename;
wxString m_pageName;
wxString m_parentPageName;
FILE* m_workFile; ///< Temporary file to construct the stream before zipping.
std::vector<long> m_xrefTable; ///< The PDF xref offset table.