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.
This commit is contained in:
Alex Shvartzkop 2023-07-04 08:57:26 +03:00
parent b09ab2daab
commit afe176abe2
3 changed files with 70 additions and 39 deletions

View File

@ -909,6 +909,8 @@ bool PDF_PLOTTER::StartPlot( const wxString& aPageNumber, const wxString& aPageN
(it *could* be inherited via the Pages tree */ (it *could* be inherited via the Pages tree */
m_fontResDictHandle = allocPdfObject(); m_fontResDictHandle = allocPdfObject();
m_jsNamesHandle = allocPdfObject();
/* Now, the PDF is read from the end, (more or less)... so we start /* 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 with the page stream for page 1. Other more important stuff is written
at the end */ at the end */
@ -980,31 +982,32 @@ void PDF_PLOTTER::emitOutlineNode( OUTLINE_NODE* node, int parentHandle, int nex
startPdfObject( nodeHandle ); startPdfObject( nodeHandle );
fprintf( m_outputFile, fprintf( m_outputFile,
"<< /Title %s\n" "<<\n"
" /Parent %d 0 R\n", "/Title %s\n"
"/Parent %d 0 R\n",
encodeStringForPlotter(node->title ).c_str(), encodeStringForPlotter(node->title ).c_str(),
parentHandle); parentHandle);
if( nextNode > 0 ) if( nextNode > 0 )
{ {
fprintf( m_outputFile, " /Next %d 0 R\n", nextNode ); fprintf( m_outputFile, "/Next %d 0 R\n", nextNode );
} }
if( prevNode > 0 ) 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 ) if( node->children.size() > 0 )
{ {
fprintf( m_outputFile, " /Count %zd\n", -1 * node->children.size() ); 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, "/First %d 0 R\n", node->children.front()->entryHandle );
fprintf( m_outputFile, " /Last %d 0 R\n", node->children.back()->entryHandle ); fprintf( m_outputFile, "/Last %d 0 R\n", node->children.back()->entryHandle );
} }
if( node->actionHandle != -1 ) 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 ); fputs( ">>\n", m_outputFile );
@ -1114,10 +1117,11 @@ bool PDF_PLOTTER::EndPlot()
startPdfObject( linkHandle ); startPdfObject( linkHandle );
fprintf( m_outputFile, fprintf( m_outputFile,
"<< /Type /Annot\n" "<<\n"
" /Subtype /Link\n" "/Type /Annot\n"
" /Rect [%g %g %g %g]\n" "/Subtype /Link\n"
" /Border [16 16 0]\n", "/Rect [%g %g %g %g]\n"
"/Border [16 16 0]\n",
box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() ); box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() );
wxString pageNumber; wxString pageNumber;
@ -1130,7 +1134,7 @@ bool PDF_PLOTTER::EndPlot()
if( m_pageNumbers[ii] == pageNumber ) if( m_pageNumbers[ii] == pageNumber )
{ {
fprintf( m_outputFile, fprintf( m_outputFile,
" /Dest [%d 0 R /FitB]\n" "/Dest [%d 0 R /FitB]\n"
">>\n", ">>\n",
m_pageHandles[ii] ); m_pageHandles[ii] );
@ -1142,15 +1146,14 @@ bool PDF_PLOTTER::EndPlot()
if( !pageFound ) if( !pageFound )
{ {
// destination page is not being plotted, assign the NOP action to the link // destination page is not being plotted, assign the NOP action to the link
fprintf( m_outputFile, fprintf( m_outputFile, "/A << /Type /Action /S /NOP >>\n"
" /A << /Type /Action /S /NOP >>\n" ">>\n" );
">>\n" );
} }
} }
else else
{ {
fprintf( m_outputFile, fprintf( m_outputFile,
" /A << /Type /Action /S /URI /URI %s >>\n" "/A << /Type /Action /S /URI /URI %s >>\n"
">>\n", ">>\n",
encodeStringForPlotter( url ).c_str() ); encodeStringForPlotter( url ).c_str() );
} }
@ -1162,7 +1165,7 @@ bool PDF_PLOTTER::EndPlot()
{ {
const BOX2D& box = menuPair.first; const BOX2D& box = menuPair.first;
const std::vector<wxString>& urls = menuPair.second; const std::vector<wxString>& urls = menuPair.second;
wxString js = wxT( "var aParams = [ " ); wxString js = wxT( "ShM([\n" );
for( const wxString& url : urls ) for( const wxString& url : urls )
{ {
@ -1174,7 +1177,7 @@ bool PDF_PLOTTER::EndPlot()
{ {
wxString href = property.substr( property.Find( "http:" ) ); 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( property, CTX_JS_STR ),
EscapeString( href, CTX_JS_STR ) ); EscapeString( href, CTX_JS_STR ) );
} }
@ -1182,13 +1185,13 @@ bool PDF_PLOTTER::EndPlot()
{ {
wxString href = property.substr( property.Find( "https:" ) ); 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( property, CTX_JS_STR ),
EscapeString( href, CTX_JS_STR ) ); EscapeString( href, CTX_JS_STR ) );
} }
else else
{ {
js += wxString::Format( wxT( "{ cName: '%s', cReturn: null }, " ), js += wxString::Format( wxT( "[\"%s\"],\n" ),
EscapeString( property, CTX_JS_STR ) ); EscapeString( property, CTX_JS_STR ) );
} }
} }
@ -1202,7 +1205,7 @@ bool PDF_PLOTTER::EndPlot()
{ {
wxString menuText = wxString::Format( _( "Show Page %s" ), pageNumber ); 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 ), EscapeString( menuText, CTX_JS_STR ),
static_cast<int>( ii ) ); static_cast<int>( ii ) );
break; break;
@ -1213,33 +1216,58 @@ bool PDF_PLOTTER::EndPlot()
{ {
wxString menuText = wxString::Format( _( "Open %s" ), url ); 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( menuText, CTX_JS_STR ),
EscapeString( url, CTX_JS_STR ) ); EscapeString( url, CTX_JS_STR ) );
} }
} }
js += wxT( "]; " ); 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 \\);" );
startPdfObject( menuHandle ); startPdfObject( menuHandle );
fprintf( m_outputFile, fprintf( m_outputFile,
"<< /Type /Annot\n" "<<\n"
" /Subtype /Link\n" "/Type /Annot\n"
" /Rect [%g %g %g %g]\n" "/Subtype /Link\n"
" /Border [16 16 0]\n", "/Rect [%g %g %g %g]\n"
"/Border [16 16 0]\n",
box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() ); box.GetLeft(), box.GetBottom(), box.GetRight(), box.GetTop() );
fprintf( m_outputFile, fprintf( m_outputFile,
" /A << /Type /Action /S /JavaScript /JS (%s) >>\n" "/A << /Type /Action /S /JavaScript /JS %s >>\n"
">>\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(); closePdfObject();
} }
@ -1303,10 +1331,12 @@ bool PDF_PLOTTER::EndPlot()
"/Version /1.5\n" "/Version /1.5\n"
"/PageMode /UseOutlines\n" "/PageMode /UseOutlines\n"
"/Outlines %d 0 R\n" "/Outlines %d 0 R\n"
"/Names %d 0 R\n"
"/PageLayout /SinglePage\n" "/PageLayout /SinglePage\n"
">>\n", ">>\n",
m_pageTreeHandle, m_pageTreeHandle,
outlineHandle ); outlineHandle,
m_jsNamesHandle );
} }
else else
{ {

View File

@ -203,7 +203,7 @@ wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext )
{ {
unsigned int code = c; unsigned int code = c;
char buffer[16]; char buffer[16];
snprintf( buffer, sizeof(buffer), "\\\\u%4.4X", code ); snprintf( buffer, sizeof(buffer), "\\u%4.4X", code );
converted += buffer; converted += buffer;
} }
else else

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 * 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 * 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_pageTreeHandle; ///< Handle to the root of the page tree object
int m_fontResDictHandle; ///< Font resource dictionary 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 std::vector<int> m_pageHandles; ///< Handles to the page objects
int m_pageStreamHandle; ///< Handle of the page content object int m_pageStreamHandle; ///< Handle of the page content object
int m_streamLengthHandle; ///< Handle to the deferred stream length int m_streamLengthHandle; ///< Handle to the deferred stream length