KiCad PCB EDA Suite
PDF_plotter.cpp
Go to the documentation of this file.
1 
6 /*
7  * This program source code file is part of KiCad, a free EDA CAD application.
8  *
9  * Copyright (C) 1992-2012 Lorenzo Marcantonio, l.marcantonio@logossrl.com
10  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, you may find one here:
24  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
25  * or you may search the http://www.gnu.org website for the version 2 license,
26  * or you may write to the Free Software Foundation, Inc.,
27  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29 
30 #include <algorithm>
31 
32 #include <wx/filename.h>
33 #include <wx/mstream.h>
34 #include <wx/zstream.h>
35 
36 #include <advanced_config.h>
37 #include <macros.h>
38 #include <render_settings.h>
39 #include <trigo.h>
40 
41 #include "plotters_pslike.h"
42 
43 
44 std::string PDF_PLOTTER::encodeStringForPlotter( const wxString& aText )
45 {
46  // returns a string compatible with PDF string convention from a unicode string.
47  // if the initial text is only ASCII7, return the text between ( and ) for a good readability
48  // if the initial text is no ASCII7, return the text between < and >
49  // and encoded using 16 bits hexa (4 digits) by wide char (unicode 16)
50  std::string result;
51 
52  // Is aText only ASCII7 ?
53  bool is_ascii7 = true;
54 
55  for( size_t ii = 0; ii < aText.Len(); ii++ )
56  {
57  if( aText[ii] >= 0x7F )
58  {
59  is_ascii7 = false;
60  break;
61  }
62  }
63 
64  if( is_ascii7 )
65  {
66  result = '(';
67 
68  for( unsigned ii = 0; ii < aText.Len(); ii++ )
69  {
70  unsigned int code = aText[ii];
71 
72  // These characters must be escaped
73  switch( code )
74  {
75  case '(':
76  case ')':
77  case '\\':
78  result += '\\';
80 
81  default:
82  result += code;
83  break;
84  }
85  }
86 
87  result += ')';
88  }
89  else
90  {
91  result = "<FEFF";
92 
93  for( size_t ii = 0; ii < aText.Len(); ii++ )
94  {
95  unsigned int code = aText[ii];
96  char buffer[16];
97  sprintf( buffer, "%4.4X", code );
98  result += buffer;
99 
100  }
101 
102  result += '>';
103  }
104 
105  return result;
106 }
107 
108 
109 bool PDF_PLOTTER::OpenFile( const wxString& aFullFilename )
110 {
111  m_filename = aFullFilename;
112 
113  wxASSERT( !m_outputFile );
114 
115  // Open the PDF file in binary mode
116  m_outputFile = wxFopen( m_filename, wxT( "wb" ) );
117 
118  if( m_outputFile == nullptr )
119  return false ;
120 
121  return true;
122 }
123 
124 
125 void PDF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
126  double aScale, bool aMirror )
127 {
128  m_plotMirror = aMirror;
129  m_plotOffset = aOffset;
130  m_plotScale = aScale;
131  m_IUsPerDecimil = aIusPerDecimil;
132 
133  // The CTM is set to 1 user unit per decimal
134  m_iuPerDeviceUnit = 1.0 / aIusPerDecimil;
135 
136  /* The paper size in this engine is handled page by page
137  Look in the StartPage function */
138 }
139 
140 
141 void PDF_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
142 {
143  wxASSERT( workFile );
144 
145  if( aWidth == DO_NOT_SET_LINE_WIDTH )
146  return;
147  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
149 
150  if( aWidth == 0 )
151  aWidth = 1;
152 
153  wxASSERT_MSG( aWidth > 0, "Plotter called to set negative pen width" );
154 
155  if( aWidth != m_currentPenWidth )
156  fprintf( workFile, "%g w\n", userToDeviceSize( aWidth ) );
157 
158  m_currentPenWidth = aWidth;
159 }
160 
161 
162 void PDF_PLOTTER::emitSetRGBColor( double r, double g, double b )
163 {
164  wxASSERT( workFile );
165  fprintf( workFile, "%g %g %g rg %g %g %g RG\n", r, g, b, r, g, b );
166 }
167 
168 
170 {
171  wxASSERT( workFile );
172  switch( dashed )
173  {
175  fprintf( workFile, "[%d %d] 0 d\n",
176  (int) GetDashMarkLenIU(), (int) GetDashGapLenIU() );
177  break;
178  case PLOT_DASH_TYPE::DOT:
179  fprintf( workFile, "[%d %d] 0 d\n",
180  (int) GetDotMarkLenIU(), (int) GetDashGapLenIU() );
181  break;
183  fprintf( workFile, "[%d %d %d %d] 0 d\n",
184  (int) GetDashMarkLenIU(), (int) GetDashGapLenIU(),
185  (int) GetDotMarkLenIU(), (int) GetDashGapLenIU() );
186  break;
187  default:
188  fputs( "[] 0 d\n", workFile );
189  }
190 }
191 
192 
193 void PDF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_TYPE fill, int width )
194 {
195  wxASSERT( workFile );
196  DPOINT p1_dev = userToDeviceCoordinates( p1 );
197  DPOINT p2_dev = userToDeviceCoordinates( p2 );
198 
199  SetCurrentLineWidth( width );
200  fprintf( workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y,
201  p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y, fill == FILL_TYPE::NO_FILL ? 'S' : 'B' );
202 }
203 
204 
205 void PDF_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_TYPE aFill, int width )
206 {
207  wxASSERT( workFile );
208  DPOINT pos_dev = userToDeviceCoordinates( pos );
209  double radius = userToDeviceSize( diametre / 2.0 );
210 
211  /* OK. Here's a trick. PDF doesn't support circles or circular angles, that's
212  a fact. You'll have to do with cubic beziers. These *can't* represent
213  circular arcs (NURBS can, beziers don't). But there is a widely known
214  approximation which is really good
215  */
216 
217  SetCurrentLineWidth( width );
218 
219  // If diameter is less than width, switch to filled mode
220  if( aFill == FILL_TYPE::NO_FILL && diametre < width )
221  {
222  aFill = FILL_TYPE::FILLED_SHAPE;
223  SetCurrentLineWidth( 0 );
224 
225  radius = userToDeviceSize( ( diametre / 2.0 ) + ( width / 2.0 ) );
226  }
227 
228  double magic = radius * 0.551784; // You don't want to know where this come from
229 
230  // This is the convex hull for the bezier approximated circle
231  fprintf( workFile, "%g %g m "
232  "%g %g %g %g %g %g c "
233  "%g %g %g %g %g %g c "
234  "%g %g %g %g %g %g c "
235  "%g %g %g %g %g %g c %c\n",
236  pos_dev.x - radius, pos_dev.y,
237 
238  pos_dev.x - radius, pos_dev.y + magic,
239  pos_dev.x - magic, pos_dev.y + radius,
240  pos_dev.x, pos_dev.y + radius,
241 
242  pos_dev.x + magic, pos_dev.y + radius,
243  pos_dev.x + radius, pos_dev.y + magic,
244  pos_dev.x + radius, pos_dev.y,
245 
246  pos_dev.x + radius, pos_dev.y - magic,
247  pos_dev.x + magic, pos_dev.y - radius,
248  pos_dev.x, pos_dev.y - radius,
249 
250  pos_dev.x - magic, pos_dev.y - radius,
251  pos_dev.x - radius, pos_dev.y - magic,
252  pos_dev.x - radius, pos_dev.y,
253 
254  aFill == FILL_TYPE::NO_FILL ? 's' : 'b' );
255 }
256 
257 
258 void PDF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
259  FILL_TYPE fill, int width )
260 {
261  wxASSERT( workFile );
262 
263  if( radius <= 0 )
264  {
265  Circle( centre, width, FILL_TYPE::FILLED_SHAPE, 0 );
266  return;
267  }
268 
269  /* Arcs are not so easily approximated by beziers (in the general case),
270  so we approximate them in the old way */
271  wxPoint start, end;
272  const int delta = 50; // increment (in 0.1 degrees) to draw circles
273 
274  if( StAngle > EndAngle )
275  std::swap( StAngle, EndAngle );
276 
277  SetCurrentLineWidth( width );
278 
279  // Usual trig arc plotting routine...
280  start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) );
281  start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) );
282  DPOINT pos_dev = userToDeviceCoordinates( start );
283  fprintf( workFile, "%g %g m ", pos_dev.x, pos_dev.y );
284 
285  for( int ii = StAngle + delta; ii < EndAngle; ii += delta )
286  {
287  end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) );
288  end.y = centre.y + KiROUND( sindecideg( radius, -ii ) );
289  pos_dev = userToDeviceCoordinates( end );
290  fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y );
291  }
292 
293  end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) );
294  end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) );
295  pos_dev = userToDeviceCoordinates( end );
296  fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y );
297 
298  // The arc is drawn... if not filled we stroke it, otherwise we finish
299  // closing the pie at the center
300  if( fill == FILL_TYPE::NO_FILL )
301  {
302  fputs( "S\n", workFile );
303  }
304  else
305  {
306  pos_dev = userToDeviceCoordinates( centre );
307  fprintf( workFile, "%g %g l b\n", pos_dev.x, pos_dev.y );
308  }
309 }
310 
311 
312 void PDF_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
313  FILL_TYPE aFill, int aWidth, void* aData )
314 {
315  wxASSERT( workFile );
316 
317  if( aCornerList.size() <= 1 )
318  return;
319 
320  SetCurrentLineWidth( aWidth );
321 
322  DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
323  fprintf( workFile, "%g %g m\n", pos.x, pos.y );
324 
325  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
326  {
327  pos = userToDeviceCoordinates( aCornerList[ii] );
328  fprintf( workFile, "%g %g l\n", pos.x, pos.y );
329  }
330 
331  // Close path and stroke(/fill)
332  fprintf( workFile, "%c\n", aFill == FILL_TYPE::NO_FILL ? 'S' : 'b' );
333 }
334 
335 
336 void PDF_PLOTTER::PenTo( const wxPoint& pos, char plume )
337 {
338  wxASSERT( workFile );
339 
340  if( plume == 'Z' )
341  {
342  if( m_penState != 'Z' )
343  {
344  fputs( "S\n", workFile );
345  m_penState = 'Z';
346  m_penLastpos.x = -1;
347  m_penLastpos.y = -1;
348  }
349 
350  return;
351  }
352 
353  if( m_penState != plume || pos != m_penLastpos )
354  {
355  DPOINT pos_dev = userToDeviceCoordinates( pos );
356  fprintf( workFile, "%g %g %c\n",
357  pos_dev.x, pos_dev.y,
358  ( plume=='D' ) ? 'l' : 'm' );
359  }
360 
361  m_penState = plume;
362  m_penLastpos = pos;
363 }
364 
365 
366 void PDF_PLOTTER::PlotImage( const wxImage & aImage, const wxPoint& aPos, double aScaleFactor )
367 {
368  wxASSERT( workFile );
369  wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() );
370 
371  // Requested size (in IUs)
372  DPOINT drawsize( aScaleFactor * pix_size.x, aScaleFactor * pix_size.y );
373 
374  // calculate the bitmap start position
375  wxPoint start( aPos.x - drawsize.x / 2, aPos.y + drawsize.y / 2);
376 
377  DPOINT dev_start = userToDeviceCoordinates( start );
378 
379  /* PDF has an uhm... simplified coordinate system handling. There is
380  *one* operator to do everything (the PS concat equivalent). At least
381  they kept the matrix stack to save restore environments. Also images
382  are always emitted at the origin with a size of 1x1 user units.
383  What we need to do is:
384  1) save the CTM end establish the new one
385  2) plot the image
386  3) restore the CTM
387  4) profit
388  */
389  fprintf( workFile, "q %g 0 0 %g %g %g cm\n", // Step 1
390  userToDeviceSize( drawsize.x ),
391  userToDeviceSize( drawsize.y ),
392  dev_start.x, dev_start.y );
393 
394  /* An inline image is a cross between a dictionary and a stream.
395  A real ugly construct (compared with the elegance of the PDF
396  format). Also it accepts some 'abbreviations', which is stupid
397  since the content stream is usually compressed anyway... */
398  fprintf( workFile,
399  "BI\n"
400  " /BPC 8\n"
401  " /CS %s\n"
402  " /W %d\n"
403  " /H %d\n"
404  "ID\n", m_colorMode ? "/RGB" : "/G", pix_size.x, pix_size.y );
405 
406  /* Here comes the stream (in binary!). I *could* have hex or ascii84
407  encoded it, but who cares? I'll go through zlib anyway */
408  for( int y = 0; y < pix_size.y; y++ )
409  {
410  for( int x = 0; x < pix_size.x; x++ )
411  {
412  unsigned char r = aImage.GetRed( x, y ) & 0xFF;
413  unsigned char g = aImage.GetGreen( x, y ) & 0xFF;
414  unsigned char b = aImage.GetBlue( x, y ) & 0xFF;
415 
416  // PDF inline images don't support alpha, so premultiply against white background
417  if( aImage.HasAlpha() )
418  {
419  unsigned char alpha = aImage.GetAlpha( x, y ) & 0xFF;
420 
421  if( alpha < 0xFF )
422  {
423  float a = 1.0 - ( (float) alpha / 255.0 );
424  r = ( int )( r + ( a * 0xFF ) ) & 0xFF;
425  g = ( int )( g + ( a * 0xFF ) ) & 0xFF;
426  b = ( int )( b + ( a * 0xFF ) ) & 0xFF;
427  }
428  }
429 
430  if( aImage.HasMask() )
431  {
432  if( r == aImage.GetMaskRed() && g == aImage.GetMaskGreen()
433  && b == aImage.GetMaskBlue() )
434  {
435  r = 0xFF;
436  g = 0xFF;
437  b = 0xFF;
438  }
439  }
440 
441  // As usual these days, stdio buffering has to suffeeeeerrrr
442  if( m_colorMode )
443  {
444  putc( r, workFile );
445  putc( g, workFile );
446  putc( b, workFile );
447  }
448  else
449  {
450  // Greyscale conversion (CIE 1931)
451  unsigned char grey = KiROUND( r * 0.2126 + g * 0.7152 + b * 0.0722 );
452  putc( grey, workFile );
453  }
454  }
455  }
456 
457  fputs( "EI Q\n", workFile ); // Finish step 2 and do step 3
458 }
459 
460 
462 {
463  xrefTable.push_back( 0 );
464  return xrefTable.size() - 1;
465 }
466 
467 
469 {
470  wxASSERT( m_outputFile );
471  wxASSERT( !workFile );
472 
473  if( handle < 0)
474  handle = allocPdfObject();
475 
476  xrefTable[handle] = ftell( m_outputFile );
477  fprintf( m_outputFile, "%d 0 obj\n", handle );
478  return handle;
479 }
480 
481 
483 {
484  wxASSERT( m_outputFile );
485  wxASSERT( !workFile );
486  fputs( "endobj\n", m_outputFile );
487 }
488 
489 
491 {
492  wxASSERT( m_outputFile );
493  wxASSERT( !workFile );
494  handle = startPdfObject( handle );
495 
496  // This is guaranteed to be handle+1 but needs to be allocated since
497  // you could allocate more object during stream preparation
499 
500  if( ADVANCED_CFG::GetCfg().m_DebugPDFWriter )
501  {
502  fprintf( m_outputFile,
503  "<< /Length %d 0 R >>\n" // Length is deferred
504  "stream\n", handle + 1 );
505  }
506  else
507  {
508  fprintf( m_outputFile,
509  "<< /Length %d 0 R /Filter /FlateDecode >>\n" // Length is deferred
510  "stream\n", handle + 1 );
511  }
512 
513  // Open a temporary file to accumulate the stream
514  workFilename = wxFileName::CreateTempFileName( "" );
515  workFile = wxFopen( workFilename, wxT( "w+b" ) );
516  wxASSERT( workFile );
517  return handle;
518 }
519 
520 
522 {
523  wxASSERT( workFile );
524 
525  long stream_len = ftell( workFile );
526 
527  if( stream_len < 0 )
528  {
529  wxASSERT( false );
530  return;
531  }
532 
533  // Rewind the file, read in the page stream and DEFLATE it
534  fseek( workFile, 0, SEEK_SET );
535  unsigned char *inbuf = new unsigned char[stream_len];
536 
537  int rc = fread( inbuf, 1, stream_len, workFile );
538  wxASSERT( rc == stream_len );
539  (void) rc;
540 
541  // We are done with the temporary file, junk it
542  fclose( workFile );
543  workFile = nullptr;
544  ::wxRemoveFile( workFilename );
545 
546  unsigned out_count;
547 
548  if( ADVANCED_CFG::GetCfg().m_DebugPDFWriter )
549  {
550  out_count = stream_len;
551  fwrite( inbuf, out_count, 1, m_outputFile );
552  }
553  else
554  {
555  // NULL means memos owns the memory, but provide a hint on optimum size needed.
556  wxMemoryOutputStream memos( nullptr, std::max( 2000l, stream_len ) ) ;
557 
558  {
559  /* Somewhat standard parameters to compress in DEFLATE. The PDF spec is
560  * misleading, it says it wants a DEFLATE stream but it really want a ZLIB
561  * stream! (a DEFLATE stream would be generated with -15 instead of 15)
562  * rc = deflateInit2( &zstrm, Z_BEST_COMPRESSION, Z_DEFLATED, 15,
563  * 8, Z_DEFAULT_STRATEGY );
564  */
565 
566  wxZlibOutputStream zos( memos, wxZ_BEST_COMPRESSION, wxZLIB_ZLIB );
567 
568  zos.Write( inbuf, stream_len );
569  } // flush the zip stream using zos destructor
570 
571  wxStreamBuffer* sb = memos.GetOutputStreamBuffer();
572 
573  out_count = sb->Tell();
574  fwrite( sb->GetBufferStart(), 1, out_count, m_outputFile );
575  }
576 
577  delete[] inbuf;
578  fputs( "endstream\n", m_outputFile );
579  closePdfObject();
580 
581  // Writing the deferred length as an indirect object
583  fprintf( m_outputFile, "%u\n", out_count );
584  closePdfObject();
585 }
586 
587 
589 {
590  wxASSERT( m_outputFile );
591  wxASSERT( !workFile );
592 
593  // Compute the paper size in IUs
595  m_paperSize.x *= 10.0 / m_iuPerDeviceUnit;
596  m_paperSize.y *= 10.0 / m_iuPerDeviceUnit;
597 
598  // Open the content stream; the page object will go later
600 
601  /* Now, until ClosePage *everything* must be wrote in workFile, to be
602  compressed later in closePdfStream */
603 
604  // Default graphic settings (coordinate system, default color and line style)
605  fprintf( workFile,
606  "%g 0 0 %g 0 0 cm 1 J 1 j 0 0 0 rg 0 0 0 RG %g w\n",
607  0.0072 * plotScaleAdjX, 0.0072 * plotScaleAdjY,
609 }
610 
611 
613 {
614  wxASSERT( workFile );
615 
616  // Close the page stream (and compress it)
617  closePdfStream();
618 
619  // Emit the page object and put it in the page list for later
620  pageHandles.push_back( startPdfObject() );
621 
622  /* Page size is in 1/72 of inch (default user space units)
623  Works like the bbox in postscript but there is no need for
624  swapping the sizes, since PDF doesn't require a portrait page.
625  We use the MediaBox but PDF has lots of other less used boxes
626  to use */
627 
628  const double BIGPTsPERMIL = 0.072;
629  wxSize psPaperSize = m_pageInfo.GetSizeMils();
630 
631  fprintf( m_outputFile,
632  "<<\n"
633  "/Type /Page\n"
634  "/Parent %d 0 R\n"
635  "/Resources <<\n"
636  " /ProcSet [/PDF /Text /ImageC /ImageB]\n"
637  " /Font %d 0 R >>\n"
638  "/MediaBox [0 0 %d %d]\n"
639  "/Contents %d 0 R\n"
640  ">>\n",
643  int( ceil( psPaperSize.x * BIGPTsPERMIL ) ),
644  int( ceil( psPaperSize.y * BIGPTsPERMIL ) ),
646  closePdfObject();
647 
648  // Mark the page stream as idle
649  pageStreamHandle = 0;
650 }
651 
652 
654 {
655  wxASSERT( m_outputFile );
656 
657  // First things first: the customary null object
658  xrefTable.clear();
659  xrefTable.push_back( 0 );
660 
661  /* The header (that's easy!). The second line is binary junk required
662  to make the file binary from the beginning (the important thing is
663  that they must have the bit 7 set) */
664  fputs( "%PDF-1.5\n%\200\201\202\203\n", m_outputFile );
665 
666  /* Allocate an entry for the page tree root, it will go in every page
667  parent entry */
669 
670  /* In the same way, the font resource dictionary is used by every page
671  (it *could* be inherited via the Pages tree */
673 
674  /* Now, the PDF is read from the end, (more or less)... so we start
675  with the page stream for page 1. Other more important stuff is written
676  at the end */
677  StartPage();
678  return true;
679 }
680 
681 
683 {
684  wxASSERT( m_outputFile );
685 
686  // Close the current page (often the only one)
687  ClosePage();
688 
689  /* We need to declare the resources we're using (fonts in particular)
690  The useful standard one is the Helvetica family. Adding external fonts
691  is *very* involved! */
692  struct {
693  const char *psname;
694  const char *rsname;
695  int font_handle;
696  } fontdefs[4] = {
697  { "/Helvetica", "/KicadFont", 0 },
698  { "/Helvetica-Oblique", "/KicadFontI", 0 },
699  { "/Helvetica-Bold", "/KicadFontB", 0 },
700  { "/Helvetica-BoldOblique", "/KicadFontBI", 0 }
701  };
702 
703  /* Declare the font resources. Since they're builtin fonts, no descriptors (yay!)
704  We'll need metrics anyway to do any alignment (these are in the shared with
705  the postscript engine) */
706  for( int i = 0; i < 4; i++ )
707  {
708  fontdefs[i].font_handle = startPdfObject();
709  fprintf( m_outputFile,
710  "<< /BaseFont %s\n"
711  " /Type /Font\n"
712  " /Subtype /Type1\n"
713 
714  /* Adobe is so Mac-based that the nearest thing to Latin1 is
715  the Windows ANSI encoding! */
716  " /Encoding /WinAnsiEncoding\n"
717  ">>\n",
718  fontdefs[i].psname );
719  closePdfObject();
720  }
721 
722  // Named font dictionary (was allocated, now we emit it)
724  fputs( "<<\n", m_outputFile );
725 
726  for( int i = 0; i < 4; i++ )
727  {
728  fprintf( m_outputFile, " %s %d 0 R\n",
729  fontdefs[i].rsname, fontdefs[i].font_handle );
730  }
731 
732  fputs( ">>\n", m_outputFile );
733  closePdfObject();
734 
735  /* The page tree: it's a B-tree but luckily we only have few pages!
736  So we use just an array... The handle was allocated at the beginning,
737  now we instantiate the corresponding object */
739  fputs( "<<\n"
740  "/Type /Pages\n"
741  "/Kids [\n", m_outputFile );
742 
743  for( unsigned i = 0; i < pageHandles.size(); i++ )
744  fprintf( m_outputFile, "%d 0 R\n", pageHandles[i] );
745 
746  fprintf( m_outputFile,
747  "]\n"
748  "/Count %ld\n"
749  ">>\n", (long) pageHandles.size() );
750  closePdfObject();
751 
752  // The info dictionary
753  int infoDictHandle = startPdfObject();
754  char date_buf[250];
755  time_t ltime = time( nullptr );
756  strftime( date_buf, 250, "D:%Y%m%d%H%M%S", localtime( &ltime ) );
757 
758  if( m_title.IsEmpty() )
759  {
760  // Windows uses '\' and other platforms use '/' as separator
761  m_title = m_filename.AfterLast( '\\');
762  m_title = m_title.AfterLast( '/');
763  }
764 
765  fprintf( m_outputFile,
766  "<<\n"
767  "/Producer (KiCad PDF)\n"
768  "/CreationDate (%s)\n"
769  "/Creator %s\n"
770  "/Title %s\n",
771  date_buf,
773  encodeStringForPlotter( m_title ).c_str() );
774 
775  fputs( ">>\n", m_outputFile );
776  closePdfObject();
777 
778  // The catalog, at last
779  int catalogHandle = startPdfObject();
780  fprintf( m_outputFile,
781  "<<\n"
782  "/Type /Catalog\n"
783  "/Pages %d 0 R\n"
784  "/Version /1.5\n"
785  "/PageMode /UseNone\n"
786  "/PageLayout /SinglePage\n"
787  ">>\n", pageTreeHandle );
788  closePdfObject();
789 
790  /* Emit the xref table (format is crucial to the byte, each entry must
791  be 20 bytes long, and object zero must be done in that way). Also
792  the offset must be kept along for the trailer */
793  long xref_start = ftell( m_outputFile );
794  fprintf( m_outputFile,
795  "xref\n"
796  "0 %ld\n"
797  "0000000000 65535 f \n", (long) xrefTable.size() );
798 
799  for( unsigned i = 1; i < xrefTable.size(); i++ )
800  {
801  fprintf( m_outputFile, "%010ld 00000 n \n", xrefTable[i] );
802  }
803 
804  // Done the xref, go for the trailer
805  fprintf( m_outputFile,
806  "trailer\n"
807  "<< /Size %lu /Root %d 0 R /Info %d 0 R >>\n"
808  "startxref\n"
809  "%ld\n" // The offset we saved before
810  "%%%%EOF\n",
811  (unsigned long) xrefTable.size(), catalogHandle, infoDictHandle, xref_start );
812 
813  fclose( m_outputFile );
814  m_outputFile = nullptr;
815 
816  return true;
817 }
818 
819 
820 void PDF_PLOTTER::Text( const wxPoint& aPos,
821  const COLOR4D& aColor,
822  const wxString& aText,
823  double aOrient,
824  const wxSize& aSize,
825  enum EDA_TEXT_HJUSTIFY_T aH_justify,
826  enum EDA_TEXT_VJUSTIFY_T aV_justify,
827  int aWidth,
828  bool aItalic,
829  bool aBold,
830  bool aMultilineAllowed,
831  void* aData )
832 {
833  // PDF files do not like 0 sized texts which create broken files.
834  if( aSize.x == 0 || aSize.y == 0 )
835  return;
836 
837  // Render phantom text (which will be searchable) behind the stroke font. This won't
838  // be pixel-accurate, but it doesn't matter for searching.
839  int render_mode = 3; // invisible
840 
841  const char *fontname = aItalic ? ( aBold ? "/KicadFontBI" : "/KicadFontI" )
842  : ( aBold ? "/KicadFontB" : "/KicadFont" );
843 
844  // Compute the copious transformation parameters of the Current Transform Matrix
845  double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f;
846  double wideningFactor, heightFactor;
847 
848  computeTextParameters( aPos, aText, aOrient, aSize, m_plotMirror, aH_justify,
849  aV_justify, aWidth, aItalic, aBold,
850  &wideningFactor, &ctm_a, &ctm_b, &ctm_c,
851  &ctm_d, &ctm_e, &ctm_f, &heightFactor );
852 
853  SetColor( aColor );
854  SetCurrentLineWidth( aWidth, aData );
855 
856  /* We use the full CTM instead of the text matrix because the same
857  coordinate system will be used for the overlining. Also the %f
858  for the trig part of the matrix to avoid %g going in exponential
859  format (which is not supported) */
860  fprintf( workFile, "q %f %f %f %f %g %g cm BT %s %g Tf %d Tr %g Tz ",
861  ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f,
862  fontname, heightFactor, render_mode, wideningFactor * 100 );
863 
864  // The text must be escaped correctly
865  std:: string txt_pdf = encodeStringForPlotter( aText );
866  fprintf( workFile, "%s Tj ET\n", txt_pdf.c_str() );
867 
868  // Restore the CTM
869  fputs( "Q\n", workFile );
870 
871  // Plot the stroked text (if requested)
872  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
873  aItalic, aBold, aMultilineAllowed );
874 }
875 
double GetDotMarkLenIU() const
Definition: plotter.cpp:140
void closePdfStream()
Finish the current PDF stream (writes the deferred length, too)
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:61
FILL_TYPE
The set of fill types used in plotting or drawing enclosed areas.
Definition: fill_type.h:28
virtual bool EndPlot() override
wxString m_filename
Definition: plotter.h:600
virtual void StartPage()
Start a new page in the PDF document.
virtual void Text(const wxPoint &aPos, const COLOR4D &aColor, const wxString &aText, double aOrient, const wxSize &aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed=false, void *aData=nullptr) override
Draw text with the plotter.
FILE * m_outputFile
Output file.
Definition: plotter.h:590
virtual void PenTo(const wxPoint &pos, char plume) override
Moveto/lineto primitive, moves the 'pen' to the specified direction.
double m_iuPerDeviceUnit
Definition: plotter.h:581
std::vector< int > pageHandles
Font resource dictionary.
virtual void SetDash(PLOT_DASH_TYPE dashed) override
PDF supports dashed lines.
virtual void Arc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
The PDF engine can't directly plot arcs, it uses the base emulation.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
virtual void Rect(const wxPoint &p1, const wxPoint &p2, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
Rectangles in PDF.
double m_IUsPerDecimil
Definition: plotter.h:579
This file contains miscellaneous commonly used macros and functions.
wxString workFilename
Handle to the deferred stream length.
double GetDashGapLenIU() const
Definition: plotter.cpp:152
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:573
virtual void ClosePage()
Close the current page in the PDF document (and emit its compressed stream).
int streamLengthHandle
Handle of the page content object.
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:126
virtual void SetColor(const COLOR4D &color) override
The SetColor implementation is split with the subclasses: The PSLIKE computes the rgb values,...
Definition: PS_plotter.cpp:64
bool m_plotMirror
Definition: plotter.h:584
bool m_colorMode
Definition: plotter.h:593
int m_currentPenWidth
Definition: plotter.h:595
int startPdfStream(int handle=-1)
Starts a PDF stream (for the page).
int startPdfObject(int handle=-1)
Open a new PDF object and returns the handle if the parameter is -1.
wxString m_title
Definition: plotter.h:601
virtual void SetViewport(const wxPoint &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
PDF can have multiple pages, so SetPageSettings can be called with the outputFile open (but not insid...
wxPoint m_penLastpos
Definition: plotter.h:597
std::string encodeStringForPlotter(const wxString &aUnicode) override
convert a wxString unicode string to a char string compatible with the accepted string PDF format (co...
Definition: PDF_plotter.cpp:44
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:607
const wxSize & GetSizeMils() const
Definition: page_info.h:135
virtual DPOINT userToDeviceSize(const wxSize &size)
Modify size according to the plotter scale factors (wxSize version, returns a DPOINT).
Definition: plotter.cpp:124
void closePdfObject()
Close the current PDF object.
PLOT_DASH_TYPE
Dashed line types.
Definition: plotter.h:104
Plotting engines similar to ps (PostScript, Gerber, svg)
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:68
virtual DPOINT userToDeviceCoordinates(const wxPoint &aCoordinate)
Modify coordinates according to the orientation, scale factor, and offsets trace.
Definition: plotter.cpp:93
int fontResDictHandle
Handle to the root of the page tree object.
double cosdecideg(double r, double a)
Circle generation utility: computes r * cos(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:439
double sindecideg(double r, double a)
Circle generation utility: computes r * sin(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:430
virtual void emitSetRGBColor(double r, double g, double b) override
PDF supports colors fully.
std::vector< long > xrefTable
Temporary file to construct the stream before zipping.
wxPoint m_plotOffset
Definition: plotter.h:583
PAGE_INFO m_pageInfo
Definition: plotter.h:602
double GetDashMarkLenIU() const
Definition: plotter.cpp:146
virtual void SetCurrentLineWidth(int width, void *aData=nullptr) override
Pen width setting for PDF.
double plotScaleAdjX
Fine user scale adjust ( = 1.0 if no correction)
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
char m_penState
Definition: plotter.h:596
int GetDefaultPenWidth() const
virtual bool OpenFile(const wxString &aFullFilename) override
Open or create the plot file aFullFilename.
virtual void Text(const wxPoint &aPos, const COLOR4D &aColor, const wxString &aText, double aOrient, const wxSize &aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed=false, void *aData=nullptr)
Draw text with the plotter.
Definition: gr_text.cpp:217
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void computeTextParameters(const wxPoint &aPos, const wxString &aText, int aOrient, const wxSize &aSize, bool aMirror, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, double *wideningFactor, double *ctm_a, double *ctm_b, double *ctm_c, double *ctm_d, double *ctm_e, double *ctm_f, double *heightFactor)
This is the core for postscript/PDF text alignment.
Definition: PS_plotter.cpp:429
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_TYPE aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=nullptr) override
Polygon plotting for PDF.
virtual bool StartPlot() override
The PDF engine supports multiple pages; the first one is opened 'for free' the following are to be cl...
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:125
wxSize m_paperSize
Definition: plotter.h:603
int allocPdfObject()
Allocate a new handle in the table of the PDF object.
virtual void Circle(const wxPoint &pos, int diametre, FILL_TYPE fill, int width=USE_DEFAULT_LINE_WIDTH) override
Circle drawing for PDF.
int pageStreamHandle
Handles to the page objects.
wxString m_creator
Definition: plotter.h:599
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
virtual void PlotImage(const wxImage &aImage, const wxPoint &aPos, double aScaleFactor) override
PDF images are handles as inline, not XObject streams...