From afe176abe2c7e65e1b1e91904753e93268756460 Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop <dudesuchamazing@gmail.com> Date: Tue, 4 Jul 2023 08:57:26 +0300 Subject: [PATCH] Optimize PDF plot data size for property popups; fix unicode escape. Adds a document-level action JSInit, which defines ShM function, allowing more compact data representation. Also reduces whitespaces. --- common/plotters/PDF_plotter.cpp | 104 +++++++++++++++++++---------- common/string_utils.cpp | 2 +- include/plotters/plotters_pslike.h | 3 +- 3 files changed, 70 insertions(+), 39 deletions(-) diff --git a/common/plotters/PDF_plotter.cpp b/common/plotters/PDF_plotter.cpp index 4519b11653..79a0854ad2 100644 --- a/common/plotters/PDF_plotter.cpp +++ b/common/plotters/PDF_plotter.cpp @@ -909,6 +909,8 @@ bool PDF_PLOTTER::StartPlot( const wxString& aPageNumber, const wxString& aPageN (it *could* be inherited via the Pages tree */ m_fontResDictHandle = allocPdfObject(); + m_jsNamesHandle = allocPdfObject(); + /* 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 */ @@ -980,31 +982,32 @@ void PDF_PLOTTER::emitOutlineNode( OUTLINE_NODE* node, int parentHandle, int nex startPdfObject( nodeHandle ); fprintf( m_outputFile, - "<< /Title %s\n" - " /Parent %d 0 R\n", + "<<\n" + "/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 ); + fprintf( m_outputFile, "/Next %d 0 R\n", nextNode ); } if( prevNode > 0 ) { - fprintf( m_outputFile, " /Prev %d 0 R\n", prevNode ); + fprintf( m_outputFile, "/Prev %d 0 R\n", prevNode ); } if( node->children.size() > 0 ) { - fprintf( m_outputFile, " /Count %zd\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 ); + fprintf( m_outputFile, "/Count %zd\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 ); + fprintf( m_outputFile, "/A %d 0 R\n", node->actionHandle ); } fputs( ">>\n", m_outputFile ); @@ -1114,10 +1117,11 @@ bool PDF_PLOTTER::EndPlot() startPdfObject( linkHandle ); fprintf( m_outputFile, - "<< /Type /Annot\n" - " /Subtype /Link\n" - " /Rect [%g %g %g %g]\n" - " /Border [16 16 0]\n", + "<<\n" + "/Type /Annot\n" + "/Subtype /Link\n" + "/Rect [%g %g %g %g]\n" + "/Border [16 16 0]\n", box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() ); wxString pageNumber; @@ -1130,7 +1134,7 @@ bool PDF_PLOTTER::EndPlot() if( m_pageNumbers[ii] == pageNumber ) { fprintf( m_outputFile, - " /Dest [%d 0 R /FitB]\n" + "/Dest [%d 0 R /FitB]\n" ">>\n", m_pageHandles[ii] ); @@ -1142,15 +1146,14 @@ bool PDF_PLOTTER::EndPlot() if( !pageFound ) { // destination page is not being plotted, assign the NOP action to the link - fprintf( m_outputFile, - " /A << /Type /Action /S /NOP >>\n" - ">>\n" ); + fprintf( m_outputFile, "/A << /Type /Action /S /NOP >>\n" + ">>\n" ); } } else { fprintf( m_outputFile, - " /A << /Type /Action /S /URI /URI %s >>\n" + "/A << /Type /Action /S /URI /URI %s >>\n" ">>\n", encodeStringForPlotter( url ).c_str() ); } @@ -1162,7 +1165,7 @@ bool PDF_PLOTTER::EndPlot() { const BOX2D& box = menuPair.first; const std::vector<wxString>& urls = menuPair.second; - wxString js = wxT( "var aParams = [ " ); + wxString js = wxT( "ShM([\n" ); for( const wxString& url : urls ) { @@ -1174,7 +1177,7 @@ bool PDF_PLOTTER::EndPlot() { wxString href = property.substr( property.Find( "http:" ) ); - js += wxString::Format( wxT( "{ cName: '%s', cReturn: '%s' }, " ), + js += wxString::Format( wxT( "[\"%s\", \"%s\"],\n" ), EscapeString( property, CTX_JS_STR ), EscapeString( href, CTX_JS_STR ) ); } @@ -1182,13 +1185,13 @@ bool PDF_PLOTTER::EndPlot() { wxString href = property.substr( property.Find( "https:" ) ); - js += wxString::Format( wxT( "{ cName: '%s', cReturn: '%s' }, " ), + js += wxString::Format( wxT( "[\"%s\", \"%s\"],\n" ), EscapeString( property, CTX_JS_STR ), EscapeString( href, CTX_JS_STR ) ); } else { - js += wxString::Format( wxT( "{ cName: '%s', cReturn: null }, " ), + js += wxString::Format( wxT( "[\"%s\"],\n" ), EscapeString( property, CTX_JS_STR ) ); } } @@ -1202,7 +1205,7 @@ bool PDF_PLOTTER::EndPlot() { wxString menuText = wxString::Format( _( "Show Page %s" ), pageNumber ); - js += wxString::Format( wxT( "{ cName: '%s', cReturn: '#%d' }, " ), + js += wxString::Format( wxT( "[\"%s\", \"#%d\"],\n" ), EscapeString( menuText, CTX_JS_STR ), static_cast<int>( ii ) ); break; @@ -1213,33 +1216,58 @@ bool PDF_PLOTTER::EndPlot() { wxString menuText = wxString::Format( _( "Open %s" ), url ); - js += wxString::Format( wxT( "{ cName: '%s', cReturn: '%s' }, " ), + js += wxString::Format( wxT( "[\"%s\", \"%s\"],\n" ), EscapeString( menuText, CTX_JS_STR ), EscapeString( url, CTX_JS_STR ) ); } } - js += wxT( "]; " ); - - js += wxT( "var cChoice = app.popUpMenuEx.apply\\( app, aParams \\); " ); - js += wxT( "if\\( cChoice != null && cChoice.substring\\( 0, 1 \\) == '#' \\)" - " this.pageNum = parseInt\\( cChoice.slice\\( 1 \\) \\); " ); - js += wxT( "else if\\( cChoice != null && cChoice.substring\\( 0, 4 \\) == 'http' \\)" - " app.launchURL\\( cChoice \\);" ); + js += wxT( "]);" ); startPdfObject( menuHandle ); fprintf( m_outputFile, - "<< /Type /Annot\n" - " /Subtype /Link\n" - " /Rect [%g %g %g %g]\n" - " /Border [16 16 0]\n", + "<<\n" + "/Type /Annot\n" + "/Subtype /Link\n" + "/Rect [%g %g %g %g]\n" + "/Border [16 16 0]\n", box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() ); fprintf( m_outputFile, - " /A << /Type /Action /S /JavaScript /JS (%s) >>\n" + "/A << /Type /Action /S /JavaScript /JS %s >>\n" ">>\n", - js.ToStdString().c_str() ); + encodeStringForPlotter( js ).c_str() ); + + closePdfObject(); + } + + { + startPdfObject( m_jsNamesHandle ); + + wxString js = R"JS( +function ShM(aEntries) { + var aParams = []; + for (var i in aEntries) { + aParams.push({ + cName: aEntries[i][0], + cReturn: aEntries[i][1] + }) + } + + var cChoice = app.popUpMenuEx.apply(app, aParams); + if (cChoice != null && cChoice.substring(0, 1) == '#') this.pageNum = parseInt(cChoice.slice(1)); + else if (cChoice != null && cChoice.substring(0, 4) == 'http') app.launchURL(cChoice); +} +)JS"; + + fprintf( m_outputFile, + "<< /JavaScript\n" + " << /Names\n" + " [ (JSInit) << /Type /Action /S /JavaScript /JS %s >> ]\n" + " >>\n" + ">>\n", + encodeStringForPlotter( js ).c_str() ); closePdfObject(); } @@ -1303,10 +1331,12 @@ bool PDF_PLOTTER::EndPlot() "/Version /1.5\n" "/PageMode /UseOutlines\n" "/Outlines %d 0 R\n" + "/Names %d 0 R\n" "/PageLayout /SinglePage\n" ">>\n", m_pageTreeHandle, - outlineHandle ); + outlineHandle, + m_jsNamesHandle ); } else { diff --git a/common/string_utils.cpp b/common/string_utils.cpp index dda51e0de7..7588ee0a9f 100644 --- a/common/string_utils.cpp +++ b/common/string_utils.cpp @@ -203,7 +203,7 @@ wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext ) { unsigned int code = c; char buffer[16]; - snprintf( buffer, sizeof(buffer), "\\\\u%4.4X", code ); + snprintf( buffer, sizeof(buffer), "\\u%4.4X", code ); converted += buffer; } else diff --git a/include/plotters/plotters_pslike.h b/include/plotters/plotters_pslike.h index 5d8639cba2..89fadab94a 100644 --- a/include/plotters/plotters_pslike.h +++ b/include/plotters/plotters_pslike.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -493,6 +493,7 @@ protected: int m_pageTreeHandle; ///< Handle to the root of the page tree object int m_fontResDictHandle; ///< Font resource dictionary + int m_jsNamesHandle; ///< Handle for Names dictionary with JS 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