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-2017 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 <fctsys.h>
31 #include <trigo.h>
32 #include <eda_base_frame.h>
33 #include <base_struct.h>
34 #include <common.h>
35 #include <plotter.h>
36 #include <macros.h>
37 #include <wx/zstream.h>
38 #include <wx/mstream.h>
39 #include <math/util.h> // for KiROUND
40 
41 #include <algorithm>
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  // se if '(' and ')' must be escaped.
76  case '\\':
77  result += '\\';
79 
80  default:
81  result += code;
82  break;
83  }
84  }
85 
86  result += ')';
87  }
88  else
89  {
90  result = "<FEFF";
91 
92  for( size_t ii = 0; ii < aText.Len(); ii++ )
93  {
94  unsigned int code = aText[ii];
95  char buffer[16];
96  sprintf( buffer, "%4.4X", code );
97  result += buffer;
98 
99  }
100 
101  result += '>';
102  }
103 
104  return result;
105 }
106 
107 
108 /*
109  * Open or create the plot file aFullFilename
110  * return true if success, false if the file cannot be created/opened
111  *
112  * Opens the PDF file in binary mode
113  */
114 bool PDF_PLOTTER::OpenFile( const wxString& aFullFilename )
115 {
116  filename = aFullFilename;
117 
118  wxASSERT( !outputFile );
119 
120  // Open the PDF file in binary mode
121  outputFile = wxFopen( filename, wxT( "wb" ) );
122 
123  if( outputFile == NULL )
124  return false ;
125 
126  return true;
127 }
128 
129 
130 void PDF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
131  double aScale, bool aMirror )
132 {
133  m_plotMirror = aMirror;
134  plotOffset = aOffset;
135  plotScale = aScale;
136  m_IUsPerDecimil = aIusPerDecimil;
137 
138  // The CTM is set to 1 user unit per decimil
139  iuPerDeviceUnit = 1.0 / aIusPerDecimil;
140 
141  /* The paper size in this engined is handled page by page
142  Look in the StartPage function */
143 }
144 
145 
154 void PDF_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
155 {
156  wxASSERT( workFile );
157 
158  if( aWidth == DO_NOT_SET_LINE_WIDTH )
159  return;
160  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
162 
163  if( aWidth == 0 )
164  aWidth = 1;
165 
166  wxASSERT_MSG( aWidth > 0, "Plotter called to set negative pen width" );
167 
168  if( aWidth != currentPenWidth )
169  fprintf( workFile, "%g w\n", userToDeviceSize( aWidth ) );
170 
171  currentPenWidth = aWidth;
172 }
173 
174 
184 void PDF_PLOTTER::emitSetRGBColor( double r, double g, double b )
185 {
186  wxASSERT( workFile );
187  fprintf( workFile, "%g %g %g rg %g %g %g RG\n",
188  r, g, b, r, g, b );
189 }
190 
195 {
196  wxASSERT( workFile );
197  switch( dashed )
198  {
200  fprintf( workFile, "[%d %d] 0 d\n",
201  (int) GetDashMarkLenIU(), (int) GetDashGapLenIU() );
202  break;
203  case PLOT_DASH_TYPE::DOT:
204  fprintf( workFile, "[%d %d] 0 d\n",
205  (int) GetDotMarkLenIU(), (int) GetDashGapLenIU() );
206  break;
208  fprintf( workFile, "[%d %d %d %d] 0 d\n",
209  (int) GetDashMarkLenIU(), (int) GetDashGapLenIU(),
210  (int) GetDotMarkLenIU(), (int) GetDashGapLenIU() );
211  break;
212  default:
213  fputs( "[] 0 d\n", workFile );
214  }
215 }
216 
217 
221 void PDF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
222 {
223  wxASSERT( workFile );
224  DPOINT p1_dev = userToDeviceCoordinates( p1 );
225  DPOINT p2_dev = userToDeviceCoordinates( p2 );
226 
227  SetCurrentLineWidth( width );
228  fprintf( workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y,
229  p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y,
230  fill == NO_FILL ? 'S' : 'B' );
231 }
232 
233 
237 void PDF_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T aFill, int width )
238 {
239  wxASSERT( workFile );
240  DPOINT pos_dev = userToDeviceCoordinates( pos );
241  double radius = userToDeviceSize( diametre / 2.0 );
242 
243  /* OK. Here's a trick. PDF doesn't support circles or circular angles, that's
244  a fact. You'll have to do with cubic beziers. These *can't* represent
245  circular arcs (NURBS can, beziers don't). But there is a widely known
246  approximation which is really good
247  */
248 
249  SetCurrentLineWidth( width );
250 
251  // If diameter is less than width, switch to filled mode
252  if( aFill == NO_FILL && diametre < width )
253  {
254  aFill = FILLED_SHAPE;
255  SetCurrentLineWidth( 0 );
256 
257  radius = userToDeviceSize( ( diametre / 2.0 ) + ( width / 2.0 ) );
258  }
259 
260  double magic = radius * 0.551784; // You don't want to know where this come from
261 
262  // This is the convex hull for the bezier approximated circle
263  fprintf( workFile, "%g %g m "
264  "%g %g %g %g %g %g c "
265  "%g %g %g %g %g %g c "
266  "%g %g %g %g %g %g c "
267  "%g %g %g %g %g %g c %c\n",
268  pos_dev.x - radius, pos_dev.y,
269 
270  pos_dev.x - radius, pos_dev.y + magic,
271  pos_dev.x - magic, pos_dev.y + radius,
272  pos_dev.x, pos_dev.y + radius,
273 
274  pos_dev.x + magic, pos_dev.y + radius,
275  pos_dev.x + radius, pos_dev.y + magic,
276  pos_dev.x + radius, pos_dev.y,
277 
278  pos_dev.x + radius, pos_dev.y - magic,
279  pos_dev.x + magic, pos_dev.y - radius,
280  pos_dev.x, pos_dev.y - radius,
281 
282  pos_dev.x - magic, pos_dev.y - radius,
283  pos_dev.x - radius, pos_dev.y - magic,
284  pos_dev.x - radius, pos_dev.y,
285 
286  aFill == NO_FILL ? 's' : 'b' );
287 }
288 
289 
294 void PDF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
295  FILL_T fill, int width )
296 {
297  wxASSERT( workFile );
298  if( radius <= 0 )
299  {
300  Circle( centre, width, FILLED_SHAPE, 0 );
301  return;
302  }
303 
304  /* Arcs are not so easily approximated by beziers (in the general case),
305  so we approximate them in the old way */
306  wxPoint start, end;
307  const int delta = 50; // increment (in 0.1 degrees) to draw circles
308 
309  if( StAngle > EndAngle )
310  std::swap( StAngle, EndAngle );
311 
312  SetCurrentLineWidth( width );
313 
314  // Usual trig arc plotting routine...
315  start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) );
316  start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) );
317  DPOINT pos_dev = userToDeviceCoordinates( start );
318  fprintf( workFile, "%g %g m ", pos_dev.x, pos_dev.y );
319  for( int ii = StAngle + delta; ii < EndAngle; ii += delta )
320  {
321  end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) );
322  end.y = centre.y + KiROUND( sindecideg( radius, -ii ) );
323  pos_dev = userToDeviceCoordinates( end );
324  fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y );
325  }
326 
327  end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) );
328  end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) );
329  pos_dev = userToDeviceCoordinates( end );
330  fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y );
331 
332  // The arc is drawn... if not filled we stroke it, otherwise we finish
333  // closing the pie at the center
334  if( fill == NO_FILL )
335  {
336  fputs( "S\n", workFile );
337  }
338  else
339  {
340  pos_dev = userToDeviceCoordinates( centre );
341  fprintf( workFile, "%g %g l b\n", pos_dev.x, pos_dev.y );
342  }
343 }
344 
345 
349 void PDF_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
350  FILL_T aFill, int aWidth, void * aData )
351 {
352  wxASSERT( workFile );
353  if( aCornerList.size() <= 1 )
354  return;
355 
356  SetCurrentLineWidth( aWidth );
357 
358  DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
359  fprintf( workFile, "%g %g m\n", pos.x, pos.y );
360 
361  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
362  {
363  pos = userToDeviceCoordinates( aCornerList[ii] );
364  fprintf( workFile, "%g %g l\n", pos.x, pos.y );
365  }
366 
367  // Close path and stroke(/fill)
368  fprintf( workFile, "%c\n", aFill == NO_FILL ? 'S' : 'b' );
369 }
370 
371 
372 void PDF_PLOTTER::PenTo( const wxPoint& pos, char plume )
373 {
374  wxASSERT( workFile );
375  if( plume == 'Z' )
376  {
377  if( penState != 'Z' )
378  {
379  fputs( "S\n", workFile );
380  penState = 'Z';
381  penLastpos.x = -1;
382  penLastpos.y = -1;
383  }
384  return;
385  }
386 
387  if( penState != plume || pos != penLastpos )
388  {
389  DPOINT pos_dev = userToDeviceCoordinates( pos );
390  fprintf( workFile, "%g %g %c\n",
391  pos_dev.x, pos_dev.y,
392  ( plume=='D' ) ? 'l' : 'm' );
393  }
394  penState = plume;
395  penLastpos = pos;
396 }
397 
401 void PDF_PLOTTER::PlotImage( const wxImage & aImage, const wxPoint& aPos,
402  double aScaleFactor )
403 {
404  wxASSERT( workFile );
405  wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() );
406 
407  // Requested size (in IUs)
408  DPOINT drawsize( aScaleFactor * pix_size.x,
409  aScaleFactor * pix_size.y );
410 
411  // calculate the bitmap start position
412  wxPoint start( aPos.x - drawsize.x / 2,
413  aPos.y + drawsize.y / 2);
414 
415  DPOINT dev_start = userToDeviceCoordinates( start );
416 
417  /* PDF has an uhm... simplified coordinate system handling. There is
418  *one* operator to do everything (the PS concat equivalent). At least
419  they kept the matrix stack to save restore environments. Also images
420  are always emitted at the origin with a size of 1x1 user units.
421  What we need to do is:
422  1) save the CTM end establish the new one
423  2) plot the image
424  3) restore the CTM
425  4) profit
426  */
427  fprintf( workFile, "q %g 0 0 %g %g %g cm\n", // Step 1
428  userToDeviceSize( drawsize.x ),
429  userToDeviceSize( drawsize.y ),
430  dev_start.x, dev_start.y );
431 
432  /* An inline image is a cross between a dictionary and a stream.
433  A real ugly construct (compared with the elegance of the PDF
434  format). Also it accepts some 'abbreviations', which is stupid
435  since the content stream is usually compressed anyway... */
436  fprintf( workFile,
437  "BI\n"
438  " /BPC 8\n"
439  " /CS %s\n"
440  " /W %d\n"
441  " /H %d\n"
442  "ID\n", colorMode ? "/RGB" : "/G", pix_size.x, pix_size.y );
443 
444  /* Here comes the stream (in binary!). I *could* have hex or ascii84
445  encoded it, but who cares? I'll go through zlib anyway */
446  for( int y = 0; y < pix_size.y; y++ )
447  {
448  for( int x = 0; x < pix_size.x; x++ )
449  {
450  unsigned char r = aImage.GetRed( x, y ) & 0xFF;
451  unsigned char g = aImage.GetGreen( x, y ) & 0xFF;
452  unsigned char b = aImage.GetBlue( x, y ) & 0xFF;
453 
454  // PDF inline images don't support alpha, so premultiply against white background
455  if( aImage.HasAlpha() )
456  {
457  unsigned char alpha = aImage.GetAlpha( x, y ) & 0xFF;
458 
459  if( alpha < 0xFF )
460  {
461  float a = 1.0 - ( (float) alpha / 255.0 );
462  r = ( int )( r + ( a * 0xFF ) ) & 0xFF;
463  g = ( int )( g + ( a * 0xFF ) ) & 0xFF;
464  b = ( int )( b + ( a * 0xFF ) ) & 0xFF;
465  }
466  }
467 
468  if( aImage.HasMask() )
469  {
470  if( r == aImage.GetMaskRed() && g == aImage.GetMaskGreen() && b == aImage.GetMaskBlue() )
471  {
472  r = 0xFF;
473  g = 0xFF;
474  b = 0xFF;
475  }
476  }
477 
478  // As usual these days, stdio buffering has to suffeeeeerrrr
479  if( colorMode )
480  {
481  putc( r, workFile );
482  putc( g, workFile );
483  putc( b, workFile );
484  }
485  else
486  {
487  // Greyscale conversion (CIE 1931)
488  unsigned char grey = KiROUND( r * 0.2126 + g * 0.7152 + b * 0.0722 );
489  putc( grey, workFile );
490  }
491  }
492  }
493 
494  fputs( "EI Q\n", workFile ); // Finish step 2 and do step 3
495 }
496 
497 
504 {
505  xrefTable.push_back( 0 );
506  return xrefTable.size() - 1;
507 }
508 
509 
515 {
516  wxASSERT( outputFile );
517  wxASSERT( !workFile );
518 
519  if( handle < 0)
520  handle = allocPdfObject();
521 
522  xrefTable[handle] = ftell( outputFile );
523  fprintf( outputFile, "%d 0 obj\n", handle );
524  return handle;
525 }
526 
527 
532 {
533  wxASSERT( outputFile );
534  wxASSERT( !workFile );
535  fputs( "endobj\n", outputFile );
536 }
537 
538 
546 {
547  wxASSERT( outputFile );
548  wxASSERT( !workFile );
549  handle = startPdfObject( handle );
550 
551  // This is guaranteed to be handle+1 but needs to be allocated since
552  // you could allocate more object during stream preparation
554  fprintf( outputFile,
555  "<< /Length %d 0 R /Filter /FlateDecode >>\n" // Length is deferred
556  "stream\n", handle + 1 );
557 
558  // Open a temporary file to accumulate the stream
559  workFilename = wxFileName::CreateTempFileName( "" );
560  workFile = wxFopen( workFilename, wxT( "w+b" ));
561  wxASSERT( workFile );
562  return handle;
563 }
564 
565 
570 {
571  wxASSERT( workFile );
572 
573  long stream_len = ftell( workFile );
574 
575  if( stream_len < 0 )
576  {
577  wxASSERT( false );
578  return;
579  }
580 
581  // Rewind the file, read in the page stream and DEFLATE it
582  fseek( workFile, 0, SEEK_SET );
583  unsigned char *inbuf = new unsigned char[stream_len];
584 
585  int rc = fread( inbuf, 1, stream_len, workFile );
586  wxASSERT( rc == stream_len );
587  (void) rc;
588 
589  // We are done with the temporary file, junk it
590  fclose( workFile );
591  workFile = 0;
592  ::wxRemoveFile( workFilename );
593 
594  // NULL means memos owns the memory, but provide a hint on optimum size needed.
595  wxMemoryOutputStream memos( NULL, std::max( 2000l, stream_len ) ) ;
596 
597  {
598  /* Somewhat standard parameters to compress in DEFLATE. The PDF spec is
599  * misleading, it says it wants a DEFLATE stream but it really want a ZLIB
600  * stream! (a DEFLATE stream would be generated with -15 instead of 15)
601  * rc = deflateInit2( &zstrm, Z_BEST_COMPRESSION, Z_DEFLATED, 15,
602  * 8, Z_DEFAULT_STRATEGY );
603  */
604 
605  wxZlibOutputStream zos( memos, wxZ_BEST_COMPRESSION, wxZLIB_ZLIB );
606 
607  zos.Write( inbuf, stream_len );
608 
609  delete[] inbuf;
610 
611  } // flush the zip stream using zos destructor
612 
613  wxStreamBuffer* sb = memos.GetOutputStreamBuffer();
614 
615  unsigned out_count = sb->Tell();
616 
617  fwrite( sb->GetBufferStart(), 1, out_count, outputFile );
618 
619  fputs( "endstream\n", outputFile );
620  closePdfObject();
621 
622  // Writing the deferred length as an indirect object
624  fprintf( outputFile, "%u\n", out_count );
625  closePdfObject();
626 }
627 
632 {
633  wxASSERT( outputFile );
634  wxASSERT( !workFile );
635 
636  // Compute the paper size in IUs
638  paperSize.x *= 10.0 / iuPerDeviceUnit;
639  paperSize.y *= 10.0 / iuPerDeviceUnit;
640 
641  // Open the content stream; the page object will go later
643 
644  /* Now, until ClosePage *everything* must be wrote in workFile, to be
645  compressed later in closePdfStream */
646 
647  // Default graphic settings (coordinate system, default color and line style)
648  fprintf( workFile,
649  "%g 0 0 %g 0 0 cm 1 J 1 j 0 0 0 rg 0 0 0 RG %g w\n",
650  0.0072 * plotScaleAdjX, 0.0072 * plotScaleAdjY,
652 }
653 
658 {
659  wxASSERT( workFile );
660 
661  // Close the page stream (and compress it)
662  closePdfStream();
663 
664  // Emit the page object and put it in the page list for later
665  pageHandles.push_back( startPdfObject() );
666 
667  /* Page size is in 1/72 of inch (default user space units)
668  Works like the bbox in postscript but there is no need for
669  swapping the sizes, since PDF doesn't require a portrait page.
670  We use the MediaBox but PDF has lots of other less used boxes
671  to use */
672 
673  const double BIGPTsPERMIL = 0.072;
674  wxSize psPaperSize = pageInfo.GetSizeMils();
675 
676  fprintf( outputFile,
677  "<<\n"
678  "/Type /Page\n"
679  "/Parent %d 0 R\n"
680  "/Resources <<\n"
681  " /ProcSet [/PDF /Text /ImageC /ImageB]\n"
682  " /Font %d 0 R >>\n"
683  "/MediaBox [0 0 %d %d]\n"
684  "/Contents %d 0 R\n"
685  ">>\n",
688  int( ceil( psPaperSize.x * BIGPTsPERMIL ) ),
689  int( ceil( psPaperSize.y * BIGPTsPERMIL ) ),
691  closePdfObject();
692 
693  // Mark the page stream as idle
694  pageStreamHandle = 0;
695 }
696 
703 {
704  wxASSERT( outputFile );
705 
706  // First things first: the customary null object
707  xrefTable.clear();
708  xrefTable.push_back( 0 );
709 
710  /* The header (that's easy!). The second line is binary junk required
711  to make the file binary from the beginning (the important thing is
712  that they must have the bit 7 set) */
713  fputs( "%PDF-1.5\n%\200\201\202\203\n", outputFile );
714 
715  /* Allocate an entry for the page tree root, it will go in every page
716  parent entry */
718 
719  /* In the same way, the font resource dictionary is used by every page
720  (it *could* be inherited via the Pages tree */
722 
723  /* Now, the PDF is read from the end, (more or less)... so we start
724  with the page stream for page 1. Other more important stuff is written
725  at the end */
726  StartPage();
727  return true;
728 }
729 
730 
732 {
733  wxASSERT( outputFile );
734 
735  // Close the current page (often the only one)
736  ClosePage();
737 
738  /* We need to declare the resources we're using (fonts in particular)
739  The useful standard one is the Helvetica family. Adding external fonts
740  is *very* involved! */
741  struct {
742  const char *psname;
743  const char *rsname;
744  int font_handle;
745  } fontdefs[4] = {
746  { "/Helvetica", "/KicadFont", 0 },
747  { "/Helvetica-Oblique", "/KicadFontI", 0 },
748  { "/Helvetica-Bold", "/KicadFontB", 0 },
749  { "/Helvetica-BoldOblique", "/KicadFontBI", 0 }
750  };
751 
752  /* Declare the font resources. Since they're builtin fonts, no descriptors (yay!)
753  We'll need metrics anyway to do any alignment (these are in the shared with
754  the postscript engine) */
755  for( int i = 0; i < 4; i++ )
756  {
757  fontdefs[i].font_handle = startPdfObject();
758  fprintf( outputFile,
759  "<< /BaseFont %s\n"
760  " /Type /Font\n"
761  " /Subtype /Type1\n"
762 
763  /* Adobe is so Mac-based that the nearest thing to Latin1 is
764  the Windows ANSI encoding! */
765  " /Encoding /WinAnsiEncoding\n"
766  ">>\n",
767  fontdefs[i].psname );
768  closePdfObject();
769  }
770 
771  // Named font dictionary (was allocated, now we emit it)
773  fputs( "<<\n", outputFile );
774  for( int i = 0; i < 4; i++ )
775  {
776  fprintf( outputFile, " %s %d 0 R\n",
777  fontdefs[i].rsname, fontdefs[i].font_handle );
778  }
779  fputs( ">>\n", outputFile );
780  closePdfObject();
781 
782  /* The page tree: it's a B-tree but luckily we only have few pages!
783  So we use just an array... The handle was allocated at the beginning,
784  now we instantiate the corresponding object */
786  fputs( "<<\n"
787  "/Type /Pages\n"
788  "/Kids [\n", outputFile );
789 
790  for( unsigned i = 0; i < pageHandles.size(); i++ )
791  fprintf( outputFile, "%d 0 R\n", pageHandles[i] );
792 
793  fprintf( outputFile,
794  "]\n"
795  "/Count %ld\n"
796  ">>\n", (long) pageHandles.size() );
797  closePdfObject();
798 
799 
800  // The info dictionary
801  int infoDictHandle = startPdfObject();
802  char date_buf[250];
803  time_t ltime = time( NULL );
804  strftime( date_buf, 250, "D:%Y%m%d%H%M%S",
805  localtime( &ltime ) );
806 
807  if( title.IsEmpty() )
808  {
809  // Windows uses '\' and other platforms ue '/' as sepatator
810  title = filename.AfterLast('\\');
811  title = title.AfterLast('/');
812  }
813 
814  fprintf( outputFile,
815  "<<\n"
816  "/Producer (KiCad PDF)\n"
817  "/CreationDate (%s)\n"
818  "/Creator %s\n"
819  "/Title %s\n"
820  "/Trapped False\n",
821  date_buf,
822  encodeStringForPlotter( creator ).c_str(),
823  encodeStringForPlotter( title ).c_str() );
824 
825  fputs( ">>\n", outputFile );
826  closePdfObject();
827 
828  // The catalog, at last
829  int catalogHandle = startPdfObject();
830  fprintf( outputFile,
831  "<<\n"
832  "/Type /Catalog\n"
833  "/Pages %d 0 R\n"
834  "/Version /1.5\n"
835  "/PageMode /UseNone\n"
836  "/PageLayout /SinglePage\n"
837  ">>\n", pageTreeHandle );
838  closePdfObject();
839 
840  /* Emit the xref table (format is crucial to the byte, each entry must
841  be 20 bytes long, and object zero must be done in that way). Also
842  the offset must be kept along for the trailer */
843  long xref_start = ftell( outputFile );
844  fprintf( outputFile,
845  "xref\n"
846  "0 %ld\n"
847  "0000000000 65535 f \n", (long) xrefTable.size() );
848  for( unsigned i = 1; i < xrefTable.size(); i++ )
849  {
850  fprintf( outputFile, "%010ld 00000 n \n", xrefTable[i] );
851  }
852 
853  // Done the xref, go for the trailer
854  fprintf( outputFile,
855  "trailer\n"
856  "<< /Size %lu /Root %d 0 R /Info %d 0 R >>\n"
857  "startxref\n"
858  "%ld\n" // The offset we saved before
859  "%%%%EOF\n",
860  (unsigned long) xrefTable.size(), catalogHandle, infoDictHandle, xref_start );
861 
862  fclose( outputFile );
863  outputFile = NULL;
864 
865  return true;
866 }
867 
868 void PDF_PLOTTER::Text( const wxPoint& aPos,
869  const COLOR4D aColor,
870  const wxString& aText,
871  double aOrient,
872  const wxSize& aSize,
873  enum EDA_TEXT_HJUSTIFY_T aH_justify,
874  enum EDA_TEXT_VJUSTIFY_T aV_justify,
875  int aWidth,
876  bool aItalic,
877  bool aBold,
878  bool aMultilineAllowed,
879  void* aData )
880 {
881  // PDF files do not like 0 sized texts which create broken files.
882  if( aSize.x == 0 || aSize.y == 0 )
883  return;
884 
885  // Render phantom text (which will be searchable) behind the stroke font. This won't
886  // be pixel-accurate, but it doesn't matter for searching.
887  int render_mode = 3; // invisible
888 
889  const char *fontname = aItalic ? ( aBold ? "/KicadFontBI" : "/KicadFontI" )
890  : ( aBold ? "/KicadFontB" : "/KicadFont" );
891 
892  // Compute the copious transformation parameters of the Curent Transform Matrix
893  double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f;
894  double wideningFactor, heightFactor;
895 
896  computeTextParameters( aPos, aText, aOrient, aSize, m_plotMirror, aH_justify,
897  aV_justify, aWidth, aItalic, aBold,
898  &wideningFactor, &ctm_a, &ctm_b, &ctm_c,
899  &ctm_d, &ctm_e, &ctm_f, &heightFactor );
900 
901  SetColor( aColor );
902  SetCurrentLineWidth( aWidth, aData );
903 
904  /* We use the full CTM instead of the text matrix because the same
905  coordinate system will be used for the overlining. Also the %f
906  for the trig part of the matrix to avoid %g going in exponential
907  format (which is not supported) */
908  fprintf( workFile, "q %f %f %f %f %g %g cm BT %s %g Tf %d Tr %g Tz ",
909  ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f,
910  fontname, heightFactor, render_mode, wideningFactor * 100 );
911 
912  // The text must be escaped correctly
913  std:: string txt_pdf = encodeStringForPlotter( aText );
914  fprintf( workFile, "%s Tj ET\n", txt_pdf.c_str() );
915 
916  // Restore the CTM
917  fputs( "Q\n", workFile );
918 
919  // Plot the stroked text (if requested)
920  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
921  aItalic, aBold, aMultilineAllowed );
922 }
923 
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:48
virtual bool EndPlot() override
virtual void StartPage()
Starts a new page in the PDF document.
int pageTreeHandle
Definition: plotter.h:933
virtual void PenTo(const wxPoint &pos, char plume) override
moveto/lineto primitive, moves the 'pen' to the specified direction
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=NULL) override
Draws text with the plotter.
std::vector< int > pageHandles
Font resource dictionary.
Definition: plotter.h:935
virtual void SetDash(PLOT_DASH_TYPE dashed) override
PDF supports dashed lines.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:88
bool colorMode
Definition: plotter.h:589
char penState
Definition: plotter.h:592
double m_IUsPerDecimil
Definition: plotter.h:571
wxPoint plotOffset
Plot offset (in IUs)
Definition: plotter.h:577
wxPoint penLastpos
Definition: plotter.h:593
PAGE_INFO pageInfo
Definition: plotter.h:598
This file contains miscellaneous commonly used macros and functions.
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=NULL)
Draws text with the plotter.
Definition: gr_text.cpp:222
wxString workFilename
Handle to the deferred stream length.
Definition: plotter.h:938
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=NULL) override
Polygon plotting for PDF.
double GetDashGapLenIU() const
Definition: plotter.cpp:152
virtual void ClosePage()
Close the current page in the PDF document (and emit its compressed stream)
int streamLengthHandle
Handle of the page content object.
Definition: plotter.h:937
static const int USE_DEFAULT_LINE_WIDTH
Definition: plotter.h:119
bool m_plotMirror
X axis orientation (SVG) and plot mirrored (only for PS, PDF HPGL and SVG)
Definition: plotter.h:581
#define NULL
virtual void Arc(const wxPoint &centre, double StAngle, double EndAngle, int rayon, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
The PDF engine can't directly plot arcs, it uses the base emulation.
double plotScaleAdjY
Definition: plotter.h:784
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.
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...
virtual void SetColor(COLOR4D color) override
The SetColor implementation is split with the subclasses: The PSLIKE computes the rgb values,...
Definition: PS_plotter.cpp:52
Base window classes and related definitions.
double plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:565
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:603
const wxSize & GetSizeMils() const
Definition: page_info.h:143
FILE * workFile
Definition: plotter.h:939
virtual DPOINT userToDeviceSize(const wxSize &size)
Modifies size according to the plotter scale factors (wxSize version, returns a DPOINT)
Definition: plotter.cpp:125
void closePdfObject()
Close the current PDF object.
PLOT_DASH_TYPE
Enum for choosing dashed line type.
Definition: plotter.h:87
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:55
wxString title
Definition: plotter.h:597
virtual DPOINT userToDeviceCoordinates(const wxPoint &aCoordinate)
Modifies coordinates according to the orientation, scale factor, and offsets trace.
Definition: plotter.cpp:94
virtual void Rect(const wxPoint &p1, const wxPoint &p2, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
Rectangles in PDF.
int fontResDictHandle
Handle to the root of the page tree object.
Definition: plotter.h:934
double cosdecideg(double r, double a)
Circle generation utility: computes r * cos(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:427
double sindecideg(double r, double a)
Circle generation utility: computes r * sin(a) Where a is in decidegrees, not in radians.
Definition: trigo.h:418
virtual void emitSetRGBColor(double r, double g, double b) override
PDF supports colors fully.
std::vector< long > xrefTable
Temporary file to costruct the stream before zipping.
Definition: plotter.h:940
int currentPenWidth
Definition: plotter.h:591
double GetDashMarkLenIU() const
Definition: plotter.cpp:146
virtual void Circle(const wxPoint &pos, int diametre, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH) override
Circle drawing for PDF.
double plotScaleAdjX
Fine user scale adjust ( = 1.0 if no correction)
Definition: plotter.h:784
FILE * outputFile
true if the Y axis is top to bottom (SVG)
Definition: plotter.h:586
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:68
The common library.
FILL_T
Enum FILL_T is the set of fill types used in plotting or drawing enclosed areas.
Definition: base_struct.h:42
int GetDefaultPenWidth() const
virtual bool OpenFile(const wxString &aFullFilename) override
Open or create the plot file aFullFilename.
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 It computes the transformation matrix to generate ...
Definition: PS_plotter.cpp:429
virtual bool StartPlot() override
The PDF engine supports multiple pages; the first one is opened 'for free' the following are to be cl...
double iuPerDeviceUnit
Device scale (from IUs to plotter device units - usually decimils)
Definition: plotter.h:574
static const int DO_NOT_SET_LINE_WIDTH
Definition: plotter.h:118
wxString filename
Definition: plotter.h:596
int allocPdfObject()
Allocate a new handle in the table of the PDF object.
int pageStreamHandle
Handles to the page objects.
Definition: plotter.h:936
wxString creator
Definition: plotter.h:595
wxSize paperSize
Definition: plotter.h:599
virtual void SetCurrentLineWidth(int width, void *aData=NULL) override
Pen width setting for PDF.
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:99
virtual void PlotImage(const wxImage &aImage, const wxPoint &aPos, double aScaleFactor) override
PDF images are handles as inline, not XObject streams...