diff --git a/common/plotters/PDF_plotter.cpp b/common/plotters/PDF_plotter.cpp index c9dc194b3f..bfd27bf3b1 100644 --- a/common/plotters/PDF_plotter.cpp +++ b/common/plotters/PDF_plotter.cpp @@ -38,10 +38,10 @@ #include #include #include +#include #include - std::string PDF_PLOTTER::encodeStringForPlotter( const wxString& aText ) { // returns a string compatible with PDF string convention from a unicode string. @@ -709,13 +709,14 @@ void PDF_PLOTTER::ClosePage() const double PTsPERMIL = 0.072; VECTOR2D psPaperSize = VECTOR2D( m_pageInfo.GetSizeMils() ) * PTsPERMIL; - auto iuToPdfUserSpace = [&]( const VECTOR2I& aCoord ) -> VECTOR2D - { - VECTOR2D retval = VECTOR2D( aCoord ) * PTsPERMIL / SCH_IU_PER_MILS; - // PDF y=0 is at bottom of page, invert coordinate - retval.y = psPaperSize.y - retval.y; - return retval; - }; + auto iuToPdfUserSpace = + [&]( const VECTOR2I& aCoord ) -> VECTOR2D + { + VECTOR2D retval = VECTOR2D( aCoord ) * PTsPERMIL / ( m_IUsPerDecimil * 10 ); + // PDF y=0 is at bottom of page, invert coordinate + retval.y = psPaperSize.y - retval.y; + return retval; + }; // Handle annotations (at the moment only "link" type objects) std::vector hyperlinkHandles; @@ -955,31 +956,45 @@ bool PDF_PLOTTER::EndPlot() const BOX2D& box = menuPair.first; const std::vector& urls = menuPair.second; - // We currently only support menu links for internal pages. This vector holds the - // page names and numbers. - std::vector> pages; + // We currently only support menu links for internal pages and property lists. + // This vector holds the menu titles and (optional) page numbers. + std::vector> menuItems; for( const wxString& url : urls ) { wxString pageNumber; - if( EDA_TEXT::IsGotoPageHref( url, &pageNumber ) ) + if( url.StartsWith( "!" ) ) + { + menuItems.push_back( { url.AfterFirst( '!' ), -1 } ); + } + else if( EDA_TEXT::IsGotoPageHref( url, &pageNumber ) ) { for( size_t ii = 0; ii < m_pageNumbers.size(); ++ii ) { if( m_pageNumbers[ii] == pageNumber ) - pages.push_back( { pageNumber, ii } ); + menuItems.push_back( { pageNumber, ii } ); } } } wxString js = wxT( "var aParams = [ " ); - for( const std::pair& page : pages ) + for( const std::pair& menuItem : menuItems ) { - js += wxString::Format( wxT( "{ cName: '%s', cReturn: '%d' }, " ), - wxString::Format( _( "Show Page %s" ), page.first ), - page.second ); + if( menuItem.second < 0 ) + { + js += wxString::Format( wxT( "{ cName: '%s', cReturn: null }, " ), + EscapeString( menuItem.first, CTX_JS_STR ) ); + } + else + { + wxString menuText = wxString::Format( _( "Show Page %s" ), menuItem.first ); + + js += wxString::Format( wxT( "{ cName: '%s', cReturn: '%d' }, " ), + EscapeString( menuText, CTX_JS_STR ), + menuItem.second ); + } } js += wxT( "]; " ); diff --git a/common/string_utils.cpp b/common/string_utils.cpp index 29ee625cc1..fb17369297 100644 --- a/common/string_utils.cpp +++ b/common/string_utils.cpp @@ -195,6 +195,13 @@ wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext ) else converted += c; } + else if( aContext == CTX_JS_STR ) + { + if( c == '\'' ) + converted += wxT( "{quote}" ); + else + converted += c; + } else if( aContext == CTX_LINE ) { if( c == '\n' || c == '\r' ) diff --git a/eeschema/sch_label.cpp b/eeschema/sch_label.cpp index 70f48e630d..17f52da36d 100644 --- a/eeschema/sch_label.cpp +++ b/eeschema/sch_label.cpp @@ -912,6 +912,20 @@ void SCH_LABEL_BASE::Plot( PLOTTER* aPlotter, bool aBackground ) const for( const SCH_FIELD& field : m_fields ) field.Plot( aPlotter, aBackground ); + + if( !m_fields.empty() ) + { + std::vector properties; + + for( const SCH_FIELD& field : GetFields() ) + { + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + field.GetName(), + field.GetShownText() ) ); + } + + aPlotter->HyperlinkMenu( GetBoundingBox(), properties ); + } } diff --git a/eeschema/sch_sheet.cpp b/eeschema/sch_sheet.cpp index cdc2013362..4a06ae4e22 100644 --- a/eeschema/sch_sheet.cpp +++ b/eeschema/sch_sheet.cpp @@ -1075,14 +1075,19 @@ void SCH_SHEET::Plot( PLOTTER* aPlotter, bool aBackground ) const } // Make the sheet object a clickable hyperlink (e.g. for PDF plotter) - if( !aBackground ) - { - BOX2I rect( m_pos, m_size ); - wxString pageNum = GetPageNumber( getSheetPath() ); + std::vector properties; + + properties.emplace_back( EDA_TEXT::GotoPageHref( GetPageNumber( getSheetPath() ) ) ); - aPlotter->HyperlinkBox( rect, EDA_TEXT::GotoPageHref( pageNum ) ); + for( const SCH_FIELD& field : GetFields() ) + { + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + field.GetName(), + field.GetShownText() ) ); } + aPlotter->HyperlinkMenu( GetBoundingBox(), properties ); + // Plot sheet pins for( SCH_SHEET_PIN* sheetPin : m_pins ) sheetPin->Plot( aPlotter, aBackground ); diff --git a/eeschema/sch_symbol.cpp b/eeschema/sch_symbol.cpp index 7c2e29cee2..35180b9d0b 100644 --- a/eeschema/sch_symbol.cpp +++ b/eeschema/sch_symbol.cpp @@ -2006,6 +2006,20 @@ void SCH_SYMBOL::Plot( PLOTTER* aPlotter, bool aBackground ) const field.Plot( aPlotter, local_background ); } + std::vector properties; + + for( const SCH_FIELD& field : GetFields() ) + { + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + field.GetName(), + field.GetShownText() ) ); + } + + properties.emplace_back( _( "!Description = " ) + m_part->GetDescription() ); + properties.emplace_back( _( "!Keywords = " ) + m_part->GetKeyWords() ); + + aPlotter->HyperlinkMenu( GetBoundingBox(), properties ); + aPlotter->EndBlock( nullptr ); } } diff --git a/include/string_utils.h b/include/string_utils.h index f134928f5d..71823618dc 100644 --- a/include/string_utils.h +++ b/include/string_utils.h @@ -55,6 +55,7 @@ enum ESCAPE_CONTEXT CTX_LIBID, CTX_IPC, CTX_QUOTED_STR, + CTX_JS_STR, CTX_LINE, CTX_CSV, CTX_FILENAME, diff --git a/pcbnew/dialogs/dialog_plot.cpp b/pcbnew/dialogs/dialog_plot.cpp index ddcda91b2c..debe7a908c 100644 --- a/pcbnew/dialogs/dialog_plot.cpp +++ b/pcbnew/dialogs/dialog_plot.cpp @@ -1075,6 +1075,7 @@ void DIALOG_PLOT::Plot( wxCommandEvent& event ) if( plotter ) { PlotBoardLayers( board, plotter, plotSequence, m_plotOpts ); + PlotInteractiveLayer( board, plotter ); plotter->EndPlot(); delete plotter->RenderSettings(); delete plotter; diff --git a/pcbnew/pcbplot.h b/pcbnew/pcbplot.h index 9696960a6b..d01ed6ad9c 100644 --- a/pcbnew/pcbplot.h +++ b/pcbnew/pcbplot.h @@ -158,6 +158,11 @@ PLOTTER* StartPlotBoard( BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts, int aL void PlotBoardLayers( BOARD* aBoard, PLOTTER* aPlotter, const LSEQ& aLayerSequence, const PCB_PLOT_PARAMS& aPlotOptions ); +/** + * Plot interactive items (hypertext links, properties, etc.). + */ +void PlotInteractiveLayer( BOARD* aBoard, PLOTTER* aPlotter ); + /** * Plot one copper or technical layer. * diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index 60398b30aa..abd16c58ca 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -76,6 +76,40 @@ void PlotBoardLayers( BOARD* aBoard, PLOTTER* aPlotter, const LSEQ& aLayers, } +void PlotInteractiveLayer( BOARD* aBoard, PLOTTER* aPlotter ) +{ + for( const FOOTPRINT* fp : aBoard->Footprints() ) + { + std::vector properties; + + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + _( "Reference designator" ), + fp->Reference().GetShownText() ) ); + + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + _( "Value" ), + fp->Value().GetShownText() ) ); + + for( const auto& [ name, value ] : fp->GetProperties() ) + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), name, value ) ); + + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + _( "Footprint" ), + fp->GetFPIDAsString() ) ); + + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + _( "Description" ), + fp->GetDescription() ) ); + + properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), + _( "Keywords" ), + fp->GetKeywords() ) ); + + aPlotter->HyperlinkMenu( fp->GetBoundingBox(), properties ); + } +} + + void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer, const PCB_PLOT_PARAMS& aPlotOpt ) {