From ed79ef8febb114f405e40dacb8ce8b848bbfe268 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 26 Jul 2020 15:57:07 +0200 Subject: [PATCH] PDF plotter: better handling of non ASCII7 (unicode 16) chars. Note also the PS plotter handle poorly these non ASCII chars. Fixes #4957 https://gitlab.com/kicad/code/kicad/issues/4957 --- common/plotters/PDF_plotter.cpp | 78 ++++++++++++++++++++++++++++++--- common/plotters/PS_plotter.cpp | 29 ++++++------ include/plotter.h | 9 +++- 3 files changed, 95 insertions(+), 21 deletions(-) diff --git a/common/plotters/PDF_plotter.cpp b/common/plotters/PDF_plotter.cpp index 7f865f509c..cd73078532 100644 --- a/common/plotters/PDF_plotter.cpp +++ b/common/plotters/PDF_plotter.cpp @@ -41,6 +41,70 @@ #include +std::string PDF_PLOTTER::encodeStringForPlotter( const wxString& aText ) +{ +// returns a string compatible with PDF string convention from a unicode string. +// if the initial text is only ASCII7, return the text between ( and ) for a good readability +// if the initial text is no ASCII7, return the text between < and > +// and encoded using 16 bits hexa (4 digits) by wide char (unicode 16) + std::string result; + + // Is aText only ASCII7 ? + bool is_ascii7 = true; + + for( size_t ii = 0; ii < aText.Len(); ii++ ) + { + if( aText[ii] >= 0x7F ) + { + is_ascii7 = false; + break; + } + } + + if( is_ascii7 ) + { + result = '('; + + for( unsigned ii = 0; ii < aText.Len(); ii++ ) + { + unsigned int code = aText[ii]; + + // These characters must be escaped + switch( code ) + { + // se if '(' and ')' must be escaped. + case '\\': + result += '\\'; + KI_FALLTHROUGH; + + default: + result += code; + break; + } + } + + result += ')'; + } + else + { + result = ">\n", outputFile ); closePdfObject(); @@ -846,8 +910,8 @@ void PDF_PLOTTER::Text( const wxPoint& aPos, fontname, heightFactor, render_mode, wideningFactor * 100 ); // The text must be escaped correctly - fputsPostscriptString( workFile, aText ); - fputs( " Tj ET\n", workFile ); + std:: string txt_pdf = encodeStringForPlotter( aText ); + fprintf( workFile, "%s Tj ET\n", txt_pdf.c_str() ); // Restore the CTM fputs( "Q\n", workFile ); diff --git a/common/plotters/PS_plotter.cpp b/common/plotters/PS_plotter.cpp index 81f6146ee4..f21e6ad4e8 100644 --- a/common/plotters/PS_plotter.cpp +++ b/common/plotters/PS_plotter.cpp @@ -300,16 +300,17 @@ void PSLIKE_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, } -/** - * Write on a stream a string escaped for postscript/PDF - */ -void PSLIKE_PLOTTER::fputsPostscriptString(FILE *fout, const wxString& txt) +std::string PSLIKE_PLOTTER::encodeStringForPlotter( const wxString& aUnicode ) { - putc( '(', fout ); - for( unsigned i = 0; i < txt.length(); i++ ) + // Write on a std::string a string escaped for postscript/PDF + std::string converted; + + converted += '('; + + for( unsigned i = 0; i < aUnicode.Len(); i++ ) { // Lazyness made me use stdio buffering yet another time... - wchar_t ch = txt[i]; + wchar_t ch = aUnicode[i]; if( ch < 256 ) { @@ -322,17 +323,19 @@ void PSLIKE_PLOTTER::fputsPostscriptString(FILE *fout, const wxString& txt) case '(': case ')': case '\\': - putc( '\\', fout ); + converted += '\\'; KI_FALLTHROUGH; default: - putc( ch, fout ); + converted += ch; break; } } } - putc( ')', fout ); + converted += ')'; + + return converted; } @@ -871,7 +874,7 @@ bool PS_PLOTTER::StartPlot() /* A "newline" character ("\n") is not included in the following string, because it is provided by the ctime() function. */ fprintf( outputFile, "%%%%CreationDate: %s", ctime( &time1970 ) ); - fprintf( outputFile, "%%%%Title: %s\n", TO_UTF8( filename ) ); + fprintf( outputFile, "%%%%Title: %s\n", encodeStringForPlotter( title ).c_str() ); fprintf( outputFile, "%%%%Pages: 1\n" ); fprintf( outputFile, "%%%%PageOrder: Ascend\n" ); @@ -991,9 +994,9 @@ void PS_PLOTTER::Text( const wxPoint& aPos, // Draw the hidden postscript text (if requested) if( m_textMode == PLOT_TEXT_MODE::PHANTOM ) { - fputsPostscriptString( outputFile, aText ); + std::string ps_test = encodeStringForPlotter( aText ); DPOINT pos_dev = userToDeviceCoordinates( aPos ); - fprintf( outputFile, " %g %g phantomshow\n", pos_dev.x, pos_dev.y ); + fprintf( outputFile, "%s %g %g phantomshow\n", ps_test.c_str(), pos_dev.x, pos_dev.y ); } PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, diff --git a/include/plotter.h b/include/plotter.h index 7f21a09ba7..ae902d9c32 100644 --- a/include/plotter.h +++ b/include/plotter.h @@ -766,7 +766,10 @@ protected: void postscriptOverlinePositions( const wxString& aText, int aXSize, bool aItalic, bool aBold, std::vector *pos_pairs ); - void fputsPostscriptString(FILE *fout, const wxString& txt); + + /// convert a wxString unicode string to a char string compatible with the accepted + /// string plotter format (convert special chars and non ascii7 chars) + virtual std::string encodeStringForPlotter( const wxString& aUnicode ); /// Virtual primitive for emitting the setrgbcolor operator virtual void emitSetRGBColor( double r, double g, double b ) = 0; @@ -917,6 +920,10 @@ public: protected: + /// convert a wxString unicode string to a char string compatible with the accepted + /// string PDF format (convert special chars and non ascii7 chars) + std::string encodeStringForPlotter( const wxString& aUnicode ) override; + virtual void emitSetRGBColor( double r, double g, double b ) override; int allocPdfObject(); int startPdfObject(int handle = -1);