diff --git a/common/eda_text.cpp b/common/eda_text.cpp index 50e7024246..f08c56d07c 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -983,7 +983,33 @@ bool EDA_TEXT::ValidateHyperlink( const wxString& aURL ) { wxURL url; - return aURL.IsEmpty() || url.SetURL( aURL ) == wxURL_NOERR; + return aURL.IsEmpty() || url.SetURL( aURL ) == wxURL_NOERR || IsGotoPageHyperlink( aURL ); +} + + +bool EDA_TEXT::IsGotoPageHyperlink( const wxString& aURL, int* aDestination ) +{ + wxString dest; + + if( !aURL.StartsWith( "goto:", &dest ) ) + return false; + + long num; + bool retval = dest.ToLong( &num ); + + if( !retval || num < 0 ) + return false; + + if( aDestination ) + *aDestination = static_cast( num ); + + return true; +} + + +wxString EDA_TEXT::GotoPageHyperlinkString( const int& aDestination ) +{ + return wxString::Format( "goto:%d", aDestination ); } diff --git a/common/plotters/PDF_plotter.cpp b/common/plotters/PDF_plotter.cpp index e738a1eb27..0a2456ea35 100644 --- a/common/plotters/PDF_plotter.cpp +++ b/common/plotters/PDF_plotter.cpp @@ -34,6 +34,7 @@ #include #include +#include // for IsGotoPageHyperlink #include #include #include @@ -741,8 +742,8 @@ void PDF_PLOTTER::ClosePage() closePdfObject(); }*/ - // Write out all "goto" hyperlinks for the page as link annotations (compatible with pdf 1.0) - for( const std::pair& linkPair : m_urlHyperlinksInPage ) + // Allocate all hyperlink objects for the page and calculate their position in user space coordinates + for( const std::pair& linkPair : m_hyperlinksInPage ) { const BOX2I& box = linkPair.first; const wxString& url = linkPair.second; @@ -754,12 +755,11 @@ void PDF_PLOTTER::ClosePage() userSpaceBox.SetOrigin( bottomLeft ); userSpaceBox.SetEnd( topRight ); - hyperlinkHandles.push_back( startPdfObject() ); + hyperlinkHandles.push_back( allocPdfObject() ); - m_urlHyperlinksHandles.insert( { hyperlinkHandles.back(), { userSpaceBox, url } } ); + m_hyperlinkHandles.insert( { hyperlinkHandles.back(), { userSpaceBox, url } } ); } - // - // TODO use allocPdfObject() !! + int hyperLinkArrayHandle = -1; // If we have added any annotation links, create an array containing all the objects @@ -807,8 +807,8 @@ void PDF_PLOTTER::ClosePage() // Mark the page stream as idle m_pageStreamHandle = 0; - // - m_urlHyperlinksInPage.clear(); + // Clean up + m_hyperlinksInPage.clear(); } @@ -819,8 +819,8 @@ bool PDF_PLOTTER::StartPlot() // First things first: the customary null object m_xrefTable.clear(); m_xrefTable.push_back( 0 ); - m_urlHyperlinksInPage.clear(); - m_urlHyperlinksHandles.clear(); + m_hyperlinksInPage.clear(); + m_hyperlinkHandles.clear(); /* The header (that's easy!). The second line is binary junk required to make the file binary from the beginning (the important thing is @@ -895,7 +895,7 @@ bool PDF_PLOTTER::EndPlot() fputs( ">>\n", m_outputFile ); closePdfObject(); - for( const std::pair>& handlePair : m_urlHyperlinksHandles ) + for( const std::pair>& handlePair : m_hyperlinkHandles ) { const int& linkhandle = handlePair.first; const std::pair& linkpair = handlePair.second; @@ -907,10 +907,37 @@ bool PDF_PLOTTER::EndPlot() fprintf( m_outputFile, "<< /Type /Annot\n" " /Subtype /Link\n" - " /Rect[%g %g %g %g] /Border[16 16 1]\n" - " /Dest [%d 0 R] >>\n" // /D [3 0 R /FitR –4 399 199 533] - ">>\n", - box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop(), m_pageHandles[0] ); + " /Rect[%g %g %g %g] /Border[16 16 1]\n", + box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() ); + + int pageIdx = -1; + + if( EDA_TEXT::IsGotoPageHyperlink( url, &pageIdx ) ) + { + if( pageIdx <= m_pageHandles.size() && pageIdx > 0 ) + { + fprintf( m_outputFile, + " /Dest [%d 0 R] >>\n" + ">>\n", + m_pageHandles[pageIdx - 1] ); + //todo: do we want to support specifying zoom factor/ position? e.g. /FitR + } + else + { + // destination page is not being plotted, assign the NOP action to the link + fprintf( m_outputFile, + " /A << /Type /Action /S /NOP >>\n" + ">>\n" ); + } + } + else + { + fprintf( m_outputFile, + " /A << /Type /Action /S /URI /URI %s >>\n" + ">>\n", + encodeStringForPlotter( url ).c_str() ); + } + closePdfObject(); } @@ -1056,8 +1083,8 @@ void PDF_PLOTTER::Text( const VECTOR2I& aPos, } -void PDF_PLOTTER::HyperlinkBoxURL( const BOX2I& aBox, const wxString& aDestinationURL ) +void PDF_PLOTTER::HyperlinkBox( const BOX2I& aBox, const wxString& aDestinationURL ) { - m_urlHyperlinksInPage.push_back( std::make_pair( aBox, aDestinationURL ) ); + m_hyperlinksInPage.push_back( std::make_pair( aBox, aDestinationURL ) ); } diff --git a/eeschema/sch_text.cpp b/eeschema/sch_text.cpp index 44ca96282f..e826981611 100644 --- a/eeschema/sch_text.cpp +++ b/eeschema/sch_text.cpp @@ -485,7 +485,7 @@ void SCH_TEXT::Plot( PLOTTER* aPlotter, bool aBackground ) const } if( HasHyperlink() ) - aPlotter->HyperlinkBoxURL( GetBoundingBox(), GetHyperlink() ); + aPlotter->HyperlinkBox( GetBoundingBox(), GetHyperlink() ); } diff --git a/eeschema/sch_textbox.cpp b/eeschema/sch_textbox.cpp index b89f8f8d67..999da78cb4 100644 --- a/eeschema/sch_textbox.cpp +++ b/eeschema/sch_textbox.cpp @@ -385,7 +385,7 @@ void SCH_TEXTBOX::Plot( PLOTTER* aPlotter, bool aBackground ) const } if( HasHyperlink() ) - aPlotter->HyperlinkBoxURL( GetBoundingBox(), GetHyperlink() ); + aPlotter->HyperlinkBox( GetBoundingBox(), GetHyperlink() ); } diff --git a/include/eda_text.h b/include/eda_text.h index 0bf9a3b88b..a859c83e04 100644 --- a/include/eda_text.h +++ b/include/eda_text.h @@ -335,6 +335,22 @@ public: */ static bool ValidateHyperlink( const wxString& aURL ); + /** + * Check if aURL is a valid "goto" hyperlink. + * + * @param aURL String to validate + * @param aDestinationIdx optional. pointer to populate with the page index destination + * @return true if aURL is a valid hyperlink + */ + static bool IsGotoPageHyperlink( const wxString& aURL, int* aDestination = nullptr ); + + /** + * Generate a hyperlink string that goes to the page number specified + * @param aDestination Virtual page number to go to. Note that the root sheet is 1. + * @return A hyperlink String that goes to the page number specified + */ + static wxString GotoPageHyperlinkString( const int& aDestination ); + protected: /** * A hyperlink to a URL or file in the system. If empty, this text object is not a hyperlink diff --git a/include/plotters/plotter.h b/include/plotters/plotter.h index 393e4768af..b805bd3c94 100644 --- a/include/plotters/plotter.h +++ b/include/plotters/plotter.h @@ -440,9 +440,9 @@ public: * Create a clickable hyperlink with a rectangular click area * * @aBox is the rectangular click target - * @aDestinationURL is the target + * @aDestinationURL is the target URL */ - virtual void HyperlinkBoxURL( const BOX2I& aBox, const wxString& aDestinationURL ) + virtual void HyperlinkBox( const BOX2I& aBox, const wxString& aDestinationURL ) { // NOP for most plotters. } diff --git a/include/plotters/plotters_pslike.h b/include/plotters/plotters_pslike.h index 1c784bb46b..74d3d64ae9 100644 --- a/include/plotters/plotters_pslike.h +++ b/include/plotters/plotters_pslike.h @@ -352,7 +352,7 @@ public: KIFONT::FONT* aFont = nullptr, void* aData = nullptr ) override; - virtual void HyperlinkBoxURL( const BOX2I& aBox, const wxString& aDestinationURL ) override; + virtual void HyperlinkBox( const BOX2I& aBox, const wxString& aDestinationURL ) override; /** * PDF images are handles as inline, not XObject streams... @@ -417,11 +417,11 @@ protected: FILE* m_workFile; ///< Temporary file to construct the stream before zipping std::vector m_xrefTable; ///< The PDF xref offset table - ///< List of loaded URLs in current page - std::vector> m_urlHyperlinksInPage; + ///< List of loaded hyperlinks in current page + std::vector> m_hyperlinksInPage; ///< Handles for all the hyperlink objects that will be deferred - std::map> m_urlHyperlinksHandles; + std::map> m_hyperlinkHandles; };