ADDED: Add outline support to schematic PDF and PCB plot
This commit is contained in:
parent
44c2782d39
commit
6f8205235f
|
@ -660,7 +660,7 @@ void PDF_PLOTTER::closePdfStream()
|
|||
}
|
||||
|
||||
delete[] inbuf;
|
||||
fputs( "endstream\n", m_outputFile );
|
||||
fputs( "\nendstream\n", m_outputFile );
|
||||
closePdfObject();
|
||||
|
||||
// Writing the deferred length as an indirect object
|
||||
|
@ -782,7 +782,8 @@ void PDF_PLOTTER::ClosePage()
|
|||
}
|
||||
|
||||
// Emit the page object and put it in the page list for later
|
||||
m_pageHandles.push_back( startPdfObject() );
|
||||
int pageHandle = startPdfObject();
|
||||
m_pageHandles.push_back( pageHandle );
|
||||
|
||||
fprintf( m_outputFile,
|
||||
"<<\n"
|
||||
|
@ -809,15 +810,44 @@ void PDF_PLOTTER::ClosePage()
|
|||
// Mark the page stream as idle
|
||||
m_pageStreamHandle = 0;
|
||||
|
||||
OUTLINE_NODE* pageOutlineNode = addOutlineNode(
|
||||
m_outlineRoot.get(), -1, wxString::Format( _( "Page %s" ), m_pageNumbers.back() ) );
|
||||
|
||||
|
||||
OUTLINE_NODE* componentOutlineNode = addOutlineNode( pageOutlineNode, -1, _( "Components" ) );
|
||||
|
||||
// let's reorg the symbol bookmarks under a page handle
|
||||
for( const std::pair<BOX2I, wxString>& bookmarkPair : m_componentBookmarksInPage )
|
||||
{
|
||||
const BOX2I& box = bookmarkPair.first;
|
||||
const wxString& ref = bookmarkPair.second;
|
||||
|
||||
VECTOR2I bottomLeft = iuToPdfUserSpace( box.GetPosition() );
|
||||
VECTOR2I topRight = iuToPdfUserSpace( box.GetEnd() );
|
||||
|
||||
int actionHandle = emitGoToAction( pageHandle, bottomLeft, topRight );
|
||||
|
||||
addOutlineNode( componentOutlineNode, actionHandle, ref );
|
||||
}
|
||||
|
||||
|
||||
std::sort( componentOutlineNode->children.begin(), componentOutlineNode->children.end(),
|
||||
[]( const OUTLINE_NODE* a, const OUTLINE_NODE* b ) -> bool
|
||||
{
|
||||
return a->title < b->title;
|
||||
} );
|
||||
|
||||
|
||||
// Clean up
|
||||
m_hyperlinksInPage.clear();
|
||||
m_hyperlinkMenusInPage.clear();
|
||||
m_componentBookmarksInPage.clear();
|
||||
}
|
||||
|
||||
|
||||
bool PDF_PLOTTER::StartPlot( const wxString& aPageNumber )
|
||||
bool PDF_PLOTTER::StartPlot(const wxString& aPageNumber)
|
||||
{
|
||||
wxASSERT( m_outputFile );
|
||||
wxASSERT(m_outputFile);
|
||||
|
||||
// First things first: the customary null object
|
||||
m_xrefTable.clear();
|
||||
|
@ -826,11 +856,16 @@ bool PDF_PLOTTER::StartPlot( const wxString& aPageNumber )
|
|||
m_hyperlinkMenusInPage.clear();
|
||||
m_hyperlinkHandles.clear();
|
||||
m_hyperlinkMenuHandles.clear();
|
||||
m_componentBookmarksInPage.clear();
|
||||
m_outlineRoot.release();
|
||||
m_totalOutlineNodes = 0;
|
||||
|
||||
m_outlineRoot = std::make_unique<OUTLINE_NODE>();
|
||||
|
||||
/* The header (that's easy!). The second line is binary junk required
|
||||
to make the file binary from the beginning (the important thing is
|
||||
that they must have the bit 7 set) */
|
||||
fputs( "%PDF-1.5\n%\200\201\202\203\n", m_outputFile );
|
||||
fputs("%PDF-1.5\n%\200\201\202\203\n", m_outputFile);
|
||||
|
||||
/* Allocate an entry for the page tree root, it will go in every page parent entry */
|
||||
m_pageTreeHandle = allocPdfObject();
|
||||
|
@ -842,11 +877,130 @@ bool PDF_PLOTTER::StartPlot( const wxString& aPageNumber )
|
|||
/* Now, the PDF is read from the end, (more or less)... so we start
|
||||
with the page stream for page 1. Other more important stuff is written
|
||||
at the end */
|
||||
StartPage( aPageNumber );
|
||||
StartPage(aPageNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int PDF_PLOTTER::emitGoToAction( int aPageHandle, const VECTOR2I& aBottomLeft,
|
||||
const VECTOR2I& aTopRight )
|
||||
{
|
||||
int actionHandle = allocPdfObject();
|
||||
startPdfObject( actionHandle );
|
||||
|
||||
fprintf( m_outputFile,
|
||||
"<</S /GoTo /D [%d 0 R /FitR %d %d %d %d]\n"
|
||||
">>\n",
|
||||
aPageHandle, aBottomLeft.x, aBottomLeft.y, aTopRight.x, aTopRight.y );
|
||||
|
||||
closePdfObject();
|
||||
|
||||
return actionHandle;
|
||||
}
|
||||
|
||||
|
||||
void PDF_PLOTTER::emitOutlineNode( OUTLINE_NODE* node, int parentHandle, int nextNode,
|
||||
int prevNode )
|
||||
{
|
||||
int nodeHandle = node->entryHandle;
|
||||
|
||||
int prevHandle = -1;
|
||||
int nextHandle = -1;
|
||||
for( std::vector<OUTLINE_NODE*>::iterator it = node->children.begin();
|
||||
it != node->children.end(); it++ )
|
||||
{
|
||||
if( it >= node->children.end() - 1 )
|
||||
{
|
||||
nextHandle = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = ( *( it + 1 ) )->entryHandle;
|
||||
}
|
||||
|
||||
emitOutlineNode( *it, nodeHandle, nextHandle, prevHandle );
|
||||
|
||||
prevHandle = ( *it )->entryHandle;
|
||||
}
|
||||
|
||||
if( parentHandle != -1 ) // -1 for parentHandle is the outline root itself which is handed elsewhere
|
||||
{
|
||||
startPdfObject( nodeHandle );
|
||||
|
||||
fprintf( m_outputFile,
|
||||
"<< /Title %s\n"
|
||||
" /Parent %d 0 R\n",
|
||||
encodeStringForPlotter(node->title ).c_str(),
|
||||
parentHandle);
|
||||
|
||||
if( nextNode > 0 )
|
||||
{
|
||||
fprintf( m_outputFile, " /Next %d 0 R\n", nextNode );
|
||||
}
|
||||
|
||||
if( prevNode > 0 )
|
||||
{
|
||||
fprintf( m_outputFile, " /Prev %d 0 R\n", prevNode );
|
||||
}
|
||||
|
||||
if( node->children.size() > 0 )
|
||||
{
|
||||
fprintf( m_outputFile, " /Count %lld\n", -1*node->children.size() );
|
||||
fprintf( m_outputFile, " /First %d 0 R\n", node->children.front()->entryHandle );
|
||||
fprintf( m_outputFile, " /Last %d 0 R\n", node->children.back()->entryHandle );
|
||||
}
|
||||
|
||||
if( node->actionHandle != -1 )
|
||||
{
|
||||
fprintf( m_outputFile, " /A %d 0 R\n", node->actionHandle );
|
||||
}
|
||||
|
||||
fputs( ">>\n", m_outputFile );
|
||||
closePdfObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PDF_PLOTTER::OUTLINE_NODE* PDF_PLOTTER::addOutlineNode( OUTLINE_NODE* aParent, int aActionHandle,
|
||||
const wxString& aTitle )
|
||||
{
|
||||
OUTLINE_NODE *node = aParent->AddChild( aActionHandle, aTitle, allocPdfObject() );
|
||||
m_totalOutlineNodes++;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
int PDF_PLOTTER::emitOutline()
|
||||
{
|
||||
if( m_outlineRoot->children.size() > 0 )
|
||||
{
|
||||
// declare the outline object
|
||||
m_outlineRoot->entryHandle = allocPdfObject();
|
||||
|
||||
emitOutlineNode( m_outlineRoot.get(), -1, -1, -1 );
|
||||
|
||||
startPdfObject( m_outlineRoot->entryHandle );
|
||||
|
||||
fprintf( m_outputFile,
|
||||
"<< /Type /Outlines\n"
|
||||
" /Count %d\n"
|
||||
" /First %d 0 R\n"
|
||||
" /Last %d 0 R\n"
|
||||
">>\n",
|
||||
m_totalOutlineNodes,
|
||||
m_outlineRoot->children.front()->entryHandle,
|
||||
m_outlineRoot->children.back()->entryHandle
|
||||
);
|
||||
|
||||
closePdfObject();
|
||||
|
||||
return m_outlineRoot->entryHandle;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool PDF_PLOTTER::EndPlot()
|
||||
{
|
||||
wxASSERT( m_outputFile );
|
||||
|
@ -1054,6 +1208,7 @@ bool PDF_PLOTTER::EndPlot()
|
|||
">>\n", (long) m_pageHandles.size() );
|
||||
closePdfObject();
|
||||
|
||||
|
||||
// The info dictionary
|
||||
int infoDictHandle = startPdfObject();
|
||||
char date_buf[250];
|
||||
|
@ -1080,8 +1235,27 @@ bool PDF_PLOTTER::EndPlot()
|
|||
fputs( ">>\n", m_outputFile );
|
||||
closePdfObject();
|
||||
|
||||
// Let's dump in the outline
|
||||
int outlineHandle = emitOutline();
|
||||
|
||||
// The catalog, at last
|
||||
int catalogHandle = startPdfObject();
|
||||
if( outlineHandle > 0 )
|
||||
{
|
||||
fprintf( m_outputFile,
|
||||
"<<\n"
|
||||
"/Type /Catalog\n"
|
||||
"/Pages %d 0 R\n"
|
||||
"/Version /1.5\n"
|
||||
"/PageMode /UseOutlines\n"
|
||||
"/Outlines %d 0 R\n"
|
||||
"/PageLayout /SinglePage\n"
|
||||
">>\n",
|
||||
m_pageTreeHandle,
|
||||
outlineHandle );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( m_outputFile,
|
||||
"<<\n"
|
||||
"/Type /Catalog\n"
|
||||
|
@ -1089,7 +1263,9 @@ bool PDF_PLOTTER::EndPlot()
|
|||
"/Version /1.5\n"
|
||||
"/PageMode /UseNone\n"
|
||||
"/PageLayout /SinglePage\n"
|
||||
">>\n", m_pageTreeHandle );
|
||||
">>\n",
|
||||
m_pageTreeHandle );
|
||||
}
|
||||
closePdfObject();
|
||||
|
||||
/* Emit the xref table (format is crucial to the byte, each entry must
|
||||
|
@ -1189,3 +1365,9 @@ void PDF_PLOTTER::HyperlinkMenu( const BOX2I& aBox, const std::vector<wxString>&
|
|||
{
|
||||
m_hyperlinkMenusInPage.push_back( std::make_pair( aBox, aDestURLs ) );
|
||||
}
|
||||
|
||||
|
||||
void PDF_PLOTTER::ComponentBookmark( const BOX2I& aLocation, const wxString& aSymbolReference )
|
||||
{
|
||||
m_componentBookmarksInPage.push_back( std::make_pair( aLocation, aSymbolReference ) );
|
||||
}
|
||||
|
|
|
@ -2065,6 +2065,9 @@ void SCH_SYMBOL::Plot( PLOTTER* aPlotter, bool aBackground ) const
|
|||
aPlotter->HyperlinkMenu( GetBoundingBox(), properties );
|
||||
|
||||
aPlotter->EndBlock( nullptr );
|
||||
|
||||
aPlotter->ComponentBookmark( GetBoundingBox(),
|
||||
GetField( REFERENCE_FIELD )->GetShownText() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -448,6 +448,17 @@ public:
|
|||
// NOP for most plotters.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bookmark to a symbol
|
||||
*
|
||||
* @aBox is the bounding box of the symbol
|
||||
* @aSymbolReference is the symbol schematic ref
|
||||
*/
|
||||
virtual void ComponentBookmark( const BOX2I& aBox, const wxString& aSymbolReference )
|
||||
{
|
||||
// NOP for most plotters.
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a marker (used for the drill map).
|
||||
*/
|
||||
|
|
|
@ -352,6 +352,8 @@ public:
|
|||
|
||||
void HyperlinkMenu( const BOX2I& aBox, const std::vector<wxString>& aDestURLs ) override;
|
||||
|
||||
void ComponentBookmark( const BOX2I& aLocation, const wxString& aSymbolReference ) override;
|
||||
|
||||
/**
|
||||
* PDF images are handles as inline, not XObject streams...
|
||||
*/
|
||||
|
@ -359,6 +361,38 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
struct OUTLINE_NODE
|
||||
{
|
||||
int actionHandle; //< Handle to action
|
||||
wxString title; //< Title of outline node
|
||||
int entryHandle; //< Allocated handle for this outline entry
|
||||
|
||||
std::vector<OUTLINE_NODE*> children;
|
||||
|
||||
OUTLINE_NODE* AddChild( int aActionHandle, const wxString& aTitle, int aEntryHandle )
|
||||
{
|
||||
OUTLINE_NODE* child = new OUTLINE_NODE
|
||||
{
|
||||
aActionHandle, aTitle, aEntryHandle
|
||||
};
|
||||
|
||||
children.push_back( child );
|
||||
|
||||
return child;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a new outline node entry
|
||||
*
|
||||
* The PDF object handle is automacially allocated
|
||||
*
|
||||
* @param aParent Parent node to append the new node to
|
||||
* @param aActionHandle The handle of an action that may be performed on click, set to -1 for no action
|
||||
* @param aTitle Title of node to display
|
||||
*/
|
||||
OUTLINE_NODE* addOutlineNode( OUTLINE_NODE* aParent, int aActionHandle,
|
||||
const wxString& aTitle );
|
||||
|
||||
virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle,
|
||||
const EDA_ANGLE& aEndAngle, int aRadius,
|
||||
|
@ -410,6 +444,23 @@ protected:
|
|||
*/
|
||||
void closePdfStream();
|
||||
|
||||
/**
|
||||
* Starts emitting the outline object
|
||||
*/
|
||||
int emitOutline();
|
||||
|
||||
/**
|
||||
* Emits a outline item object and recurses into any children
|
||||
*/
|
||||
void emitOutlineNode( OUTLINE_NODE* aNode, int aParentHandle, int aNextNode, int aPrevNode );
|
||||
|
||||
/**
|
||||
* Emits an action object that instructs a goto coordinates on a page
|
||||
*
|
||||
* @return Generated action handle
|
||||
*/
|
||||
int emitGoToAction( int aPageHandle, const VECTOR2I& aBottomLeft, const VECTOR2I& aTopRight );
|
||||
|
||||
int m_pageTreeHandle; ///< Handle to the root of the page tree object
|
||||
int m_fontResDictHandle; ///< Font resource dictionary
|
||||
std::vector<int> m_pageHandles; ///< Handles to the page objects
|
||||
|
@ -429,6 +480,11 @@ protected:
|
|||
///< Handles for all the hyperlink objects that will be deferred
|
||||
std::map<int, std::pair<BOX2D, wxString>> m_hyperlinkHandles;
|
||||
std::map<int, std::pair<BOX2D, std::vector<wxString>>> m_hyperlinkMenuHandles;
|
||||
|
||||
std::vector<std::pair<BOX2I, wxString>> m_componentBookmarksInPage;
|
||||
|
||||
std::unique_ptr<OUTLINE_NODE> m_outlineRoot; ///< Root outline node
|
||||
int m_totalOutlineNodes; ///< Total number of outline nodes
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -524,6 +524,7 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
|||
}
|
||||
|
||||
aPlotter->EndBlock( nullptr );
|
||||
aPlotter->ComponentBookmark( footprint->GetBoundingBox(), footprint->GetReference() );
|
||||
}
|
||||
|
||||
// Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
|
||||
|
|
Loading…
Reference in New Issue