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-2020 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 <trigo.h>
31 #include <algorithm>
32 #include <wx/zstream.h>
33 #include <wx/mstream.h>
34 #include <render_settings.h>
35 #include <advanced_config.h>
36 
37 #include "plotters_pslike.h"
38 
39 
40 
41 std::string PDF_PLOTTER::encodeStringForPlotter( const wxString& aText )
42 {
43  // returns a string compatible with PDF string convention from a unicode string.
44  // if the initial text is only ASCII7, return the text between ( and ) for a good readability
45  // if the initial text is no ASCII7, return the text between < and >
46  // and encoded using 16 bits hexa (4 digits) by wide char (unicode 16)
47  std::string result;
48 
49  // Is aText only ASCII7 ?
50  bool is_ascii7 = true;
51 
52  for( size_t ii = 0; ii < aText.Len(); ii++ )
53  {
54  if( aText[ii] >= 0x7F )
55  {
56  is_ascii7 = false;
57  break;
58  }
59  }
60 
61  if( is_ascii7 )
62  {
63  result = '(';
64 
65  for( unsigned ii = 0; ii < aText.Len(); ii++ )
66  {
67  unsigned int code = aText[ii];
68 
69  // These characters must be escaped
70  switch( code )
71  {
72  case '(':
73  case ')':
74  case '\\':
75  result += '\\';
77 
78  default:
79  result += code;
80  break;
81  }
82  }
83 
84  result += ')';
85  }
86  else
87  {
88  result = "<FEFF";
89 
90  for( size_t ii = 0; ii < aText.Len(); ii++ )
91  {
92  unsigned int code = aText[ii];
93  char buffer[16];
94  sprintf( buffer, "%4.4X", code );
95  result += buffer;
96 
97  }
98 
99  result += '>';
100  }
101 
102  return result;
103 }
104 
105 
106 bool PDF_PLOTTER::OpenFile( const wxString& aFullFilename )
107 {
108  m_filename = aFullFilename;
109 
110  wxASSERT( !m_outputFile );
111 
112  // Open the PDF file in binary mode
113  m_outputFile = wxFopen( m_filename, wxT( "wb" ) );
114 
115  if( m_outputFile == NULL )
116  return false ;
117 
118  return true;
119 }
120 
121 
122 void PDF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
123  double aScale, bool aMirror )
124 {
125  m_plotMirror = aMirror;
126  m_plotOffset = aOffset;
127  m_plotScale = aScale;
128  m_IUsPerDecimil = aIusPerDecimil;
129 
130  // The CTM is set to 1 user unit per decimal
131  m_iuPerDeviceUnit = 1.0 / aIusPerDecimil;
132 
133  /* The paper size in this engine is handled page by page
134  Look in the StartPage function */
135 }
136 
137 
138 void PDF_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
139 {
140  wxASSERT( workFile );
141 
142  if( aWidth == DO_NOT_SET_LINE_WIDTH )
143  return;
144  else if( aWidth == USE_DEFAULT_LINE_WIDTH )
146 
147  if( aWidth == 0 )
148  aWidth = 1;
149 
150  wxASSERT_MSG( aWidth > 0, "Plotter called to set negative pen width" );
151 
152  if( aWidth != m_currentPenWidth )
153  fprintf( workFile, "%g w\n", userToDeviceSize( aWidth ) );
154 
155  m_currentPenWidth = aWidth;
156 }
157 
158 
159 void PDF_PLOTTER::emitSetRGBColor( double r, double g, double b )
160 {
161  wxASSERT( workFile );
162  fprintf( workFile, "%g %g %g rg %g %g %g RG\n",
163  r, g, b, r, g, b );
164 }
165 
166 
168 {
169  wxASSERT( workFile );
170  switch( dashed )
171  {
173  fprintf( workFile, "[%d %d] 0 d\n",
174  (int) GetDashMarkLenIU(), (int) GetDashGapLenIU() );
175  break;
176  case PLOT_DASH_TYPE::DOT:
177  fprintf( workFile, "[%d %d] 0 d\n",
178  (int) GetDotMarkLenIU(), (int) GetDashGapLenIU() );
179  break;
181  fprintf( workFile, "[%d %d %d %d] 0 d\n",
182  (int) GetDashMarkLenIU(), (int) GetDashGapLenIU(),
183  (int) GetDotMarkLenIU(), (int) GetDashGapLenIU() );
184  break;
185  default:
186  fputs( "[] 0 d\n", workFile );
187  }
188 }
189 
190 
191 void PDF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_TYPE fill, int width )
192 {
193  wxASSERT( workFile );
194  DPOINT p1_dev = userToDeviceCoordinates( p1 );
195  DPOINT p2_dev = userToDeviceCoordinates( p2 );
196 
197  SetCurrentLineWidth( width );
198  fprintf( workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y,
199  p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y, fill == FILL_TYPE::NO_FILL ? 'S' : 'B' );
200 }
201 
202 
203 void PDF_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_TYPE aFill, int width )
204 {
205  wxASSERT( workFile );
206  DPOINT pos_dev = userToDeviceCoordinates( pos );
207  double radius = userToDeviceSize( diametre / 2.0 );
208 
209  /* OK. Here's a trick. PDF doesn't support circles or circular angles, that's
210  a fact. You'll have to do with cubic beziers. These *can't* represent
211  circular arcs (NURBS can, beziers don't). But there is a widely known
212  approximation which is really good
213  */
214 
215  SetCurrentLineWidth( width );
216 
217  // If diameter is less than width, switch to filled mode
218  if( aFill == FILL_TYPE::NO_FILL && diametre < width )
219  {
220  aFill = FILL_TYPE::FILLED_SHAPE;
221  SetCurrentLineWidth( 0 );
222 
223  radius = userToDeviceSize( ( diametre / 2.0 ) + ( width / 2.0 ) );
224  }
225 
226  double magic = radius * 0.551784; // You don't want to know where this come from
227 
228  // This is the convex hull for the bezier approximated circle
229  fprintf( workFile, "%g %g m "
230  "%g %g %g %g %g %g c "
231  "%g %g %g %g %g %g c "
232  "%g %g %g %g %g %g c "
233  "%g %g %g %g %g %g c %c\n",
234  pos_dev.x - radius, pos_dev.y,
235 
236  pos_dev.x - radius, pos_dev.y + magic,
237  pos_dev.x - magic, pos_dev.y + radius,
238  pos_dev.x, pos_dev.y + radius,
239 
240  pos_dev.x + magic, pos_dev.y + radius,
241  pos_dev.x + radius, pos_dev.y + magic,
242  pos_dev.x + radius, pos_dev.y,
243 
244  pos_dev.x + radius, pos_dev.y - magic,
245  pos_dev.x + magic, pos_dev.y - radius,
246  pos_dev.x, pos_dev.y - radius,
247 
248  pos_dev.x - magic, pos_dev.y - radius,
249  pos_dev.x - radius, pos_dev.y - magic,
250  pos_dev.x - radius, pos_dev.y,
251 
252  aFill == FILL_TYPE::NO_FILL ? 's' : 'b' );
253 }
254 
255 
256 void PDF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
257  FILL_TYPE fill, int width )
258 {
259  wxASSERT( workFile );
260 
261  if( radius <= 0 )
262  {
263  Circle( centre, width, FILL_TYPE::FILLED_SHAPE, 0 );
264  return;
265  }
266 
267  /* Arcs are not so easily approximated by beziers (in the general case),
268  so we approximate them in the old way */
269  wxPoint start, end;
270  const int delta = 50; // increment (in 0.1 degrees) to draw circles
271 
272  if( StAngle > EndAngle )
273  std::swap( StAngle, EndAngle );
274 
275  SetCurrentLineWidth( width );
276 
277  // Usual trig arc plotting routine...
278  start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) );
279  start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) );
280  DPOINT pos_dev = userToDeviceCoordinates( start );
281  fprintf( workFile, "%g %g m ", pos_dev.x, pos_dev.y );
282  for( int ii = StAngle + delta; ii < EndAngle; ii += delta )
283  {
284  end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) );
285  end.y = centre.y + KiROUND( sindecideg( radius, -ii ) );
286  pos_dev = userToDeviceCoordinates( end );
287  fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y );
288  }
289 
290  end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) );
291  end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) );
292  pos_dev = userToDeviceCoordinates( end );
293  fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y );
294 
295  // The arc is drawn... if not filled we stroke it, otherwise we finish
296  // closing the pie at the center
297  if( fill == FILL_TYPE::NO_FILL )
298  {
299  fputs( "S\n", workFile );
300  }
301  else
302  {
303  pos_dev = userToDeviceCoordinates( centre );
304  fprintf( workFile, "%g %g l b\n", pos_dev.x, pos_dev.y );
305  }
306 }
307 
308 
309 void PDF_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
310  FILL_TYPE aFill, int aWidth, void * aData )
311 {
312  wxASSERT( workFile );
313 
314  if( aCornerList.size() <= 1 )
315  return;
316 
317  SetCurrentLineWidth( aWidth );
318 
319  DPOINT pos = userToDeviceCoordinates( aCornerList[0] );
320  fprintf( workFile, "%g %g m\n", pos.x, pos.y );
321 
322  for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
323  {
324  pos = userToDeviceCoordinates( aCornerList[ii] );
325  fprintf( workFile, "%g %g l\n", pos.x, pos.y );
326  }
327 
328  // Close path and stroke(/fill)
329  fprintf( workFile, "%c\n", aFill == FILL_TYPE::NO_FILL ? 'S' : 'b' );
330 }
331 
332 
333 void PDF_PLOTTER::PenTo( const wxPoint& pos, char plume )
334 {
335  wxASSERT( workFile );
336 
337  if( plume == 'Z' )
338  {
339  if( m_penState != 'Z' )
340  {
341  fputs( "S\n", workFile );
342  m_penState = 'Z';
343  m_penLastpos.x = -1;
344  m_penLastpos.y = -1;
345  }
346 
347  return;
348  }
349 
350  if( m_penState != plume || pos != m_penLastpos )
351  {
352  DPOINT pos_dev = userToDeviceCoordinates( pos );
353  fprintf( workFile, "%g %g %c\n",
354  pos_dev.x, pos_dev.y,
355  ( plume=='D' ) ? 'l' : 'm' );
356  }
357 
358  m_penState = plume;
359  m_penLastpos = pos;
360 }
361 
362 
363 void PDF_PLOTTER::PlotImage( const wxImage & aImage, const wxPoint& aPos,
364  double aScaleFactor )
365 {
366  wxASSERT( workFile );
367  wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() );
368 
369  // Requested size (in IUs)
370  DPOINT drawsize( aScaleFactor * pix_size.x,
371  aScaleFactor * pix_size.y );
372 
373  // calculate the bitmap start position
374  wxPoint start( aPos.x - drawsize.x / 2,
375  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 = 0;
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( NULL, 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 
753  // The info dictionary
754  int infoDictHandle = startPdfObject();
755  char date_buf[250];
756  time_t ltime = time( NULL );
757  strftime( date_buf, 250, "D:%Y%m%d%H%M%S",
758  localtime( &ltime ) );
759 
760  if( m_title.IsEmpty() )
761  {
762  // Windows uses '\' and other platforms ue '/' as separator
763  m_title = m_filename.AfterLast( '\\');
764  m_title = m_title.AfterLast( '/');
765  }
766 
767  fprintf( m_outputFile,
768  "<<\n"
769  "/Producer (KiCad PDF)\n"
770  "/CreationDate (%s)\n"
771  "/Creator %s\n"
772  "/Title %s\n"
773  "/Trapped False\n",
774  date_buf,
776  encodeStringForPlotter( m_title ).c_str() );
777 
778  fputs( ">>\n", m_outputFile );
779  closePdfObject();
780 
781  // The catalog, at last
782  int catalogHandle = startPdfObject();
783  fprintf( m_outputFile,
784  "<<\n"
785  "/Type /Catalog\n"
786  "/Pages %d 0 R\n"
787  "/Version /1.5\n"
788  "/PageMode /UseNone\n"
789  "/PageLayout /SinglePage\n"
790  ">>\n", pageTreeHandle );
791  closePdfObject();
792 
793  /* Emit the xref table (format is crucial to the byte, each entry must
794  be 20 bytes long, and object zero must be done in that way). Also
795  the offset must be kept along for the trailer */
796  long xref_start = ftell( m_outputFile );
797  fprintf( m_outputFile,
798  "xref\n"
799  "0 %ld\n"
800  "0000000000 65535 f \n", (long) xrefTable.size() );
801 
802  for( unsigned i = 1; i < xrefTable.size(); i++ )
803  {
804  fprintf( m_outputFile, "%010ld 00000 n \n", xrefTable[i] );
805  }
806 
807  // Done the xref, go for the trailer
808  fprintf( m_outputFile,
809  "trailer\n"
810  "<< /Size %lu /Root %d 0 R /Info %d 0 R >>\n"
811  "startxref\n"
812  "%ld\n" // The offset we saved before
813  "%%%%EOF\n",
814  (unsigned long) xrefTable.size(), catalogHandle, infoDictHandle, xref_start );
815 
816  fclose( m_outputFile );
817  m_outputFile = NULL;
818 
819  return true;
820 }
821 
822 
823 void PDF_PLOTTER::Text( const wxPoint& aPos,
824  const COLOR4D aColor,
825  const wxString& aText,
826  double aOrient,
827  const wxSize& aSize,
828  enum EDA_TEXT_HJUSTIFY_T aH_justify,
829  enum EDA_TEXT_VJUSTIFY_T aV_justify,
830  int aWidth,
831  bool aItalic,
832  bool aBold,
833  bool aMultilineAllowed,
834  void* aData )
835 {
836  // PDF files do not like 0 sized texts which create broken files.
837  if( aSize.x == 0 || aSize.y == 0 )
838  return;
839 
840  // Render phantom text (which will be searchable) behind the stroke font. This won't
841  // be pixel-accurate, but it doesn't matter for searching.
842  int render_mode = 3; // invisible
843 
844  const char *fontname = aItalic ? ( aBold ? "/KicadFontBI" : "/KicadFontI" )
845  : ( aBold ? "/KicadFontB" : "/KicadFont" );
846 
847  // Compute the copious transformation parameters of the Current Transform Matrix
848  double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f;
849  double wideningFactor, heightFactor;
850 
851  computeTextParameters( aPos, aText, aOrient, aSize, m_plotMirror, aH_justify,
852  aV_justify, aWidth, aItalic, aBold,
853  &wideningFactor, &ctm_a, &ctm_b, &ctm_c,
854  &ctm_d, &ctm_e, &ctm_f, &heightFactor );
855 
856  SetColor( aColor );
857  SetCurrentLineWidth( aWidth, aData );
858 
859  /* We use the full CTM instead of the text matrix because the same
860  coordinate system will be used for the overlining. Also the %f
861  for the trig part of the matrix to avoid %g going in exponential
862  format (which is not supported) */
863  fprintf( workFile, "q %f %f %f %f %g %g cm BT %s %g Tf %d Tr %g Tz ",
864  ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f,
865  fontname, heightFactor, render_mode, wideningFactor * 100 );
866 
867  // The text must be escaped correctly
868  std:: string txt_pdf = encodeStringForPlotter( aText );
869  fprintf( workFile, "%s Tj ET\n", txt_pdf.c_str() );
870 
871  // Restore the CTM
872  fputs( "Q\n", workFile );
873 
874  // Plot the stroked text (if requested)
875  PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth,
876  aItalic, aBold, aMultilineAllowed );
877 }
878 
double GetDotMarkLenIU() const
Definition: plotter.cpp:139
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:590
virtual void StartPage()
Starts a new page in the PDF document.
FILE * m_outputFile
Output file.
Definition: plotter.h:580
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:571
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.
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:569
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:219
wxString workFilename
Handle to the deferred stream length.
double GetDashGapLenIU() const
Definition: plotter.cpp:151
double m_plotScale
Plot scale - chosen by the user (even implicitly with 'fit in a4')
Definition: plotter.h:563
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
bool m_plotMirror
Definition: plotter.h:574
bool m_colorMode
Definition: plotter.h:583
int m_currentPenWidth
Definition: plotter.h:585
#define NULL
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:591
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:65
wxPoint m_penLastpos
Definition: plotter.h:587
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:41
RENDER_SETTINGS * m_renderSettings
Definition: plotter.h:597
const wxSize & GetSizeMils() const
Definition: page_info.h:135
virtual DPOINT userToDeviceSize(const wxSize &size)
Modifies size according to the plotter scale factors (wxSize version, returns a DPOINT)
Definition: plotter.cpp:124
virtual void PlotPoly(const std::vector< wxPoint > &aCornerList, FILL_TYPE aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=NULL) override
Polygon plotting for PDF.
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)
Modifies 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:573
PAGE_INFO m_pageInfo
Definition: plotter.h:592
double GetDashMarkLenIU() const
Definition: plotter.cpp:145
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:68
char m_penState
Definition: plotter.h:586
int GetDefaultPenWidth() const
virtual bool OpenFile(const wxString &aFullFilename) override
Open or create the plot file aFullFilename.
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 It computes the transformation matrix to generate ...
Definition: PS_plotter.cpp:442
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:593
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.
virtual void SetCurrentLineWidth(int width, void *aData=NULL) override
Pen width setting for PDF.
wxString m_creator
Definition: plotter.h:589
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:98
virtual void PlotImage(const wxImage &aImage, const wxPoint &aPos, double aScaleFactor) override
PDF images are handles as inline, not XObject streams...