w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

drvpptx.cpp
Go to the documentation of this file.
1 /*
2  drvPPTX.cpp : This file is part of pstoedit
3  Backend for Office Open XML files
4  Contributed by: Scott Pakin <scott+ps2ed_AT_pakin.org>
5 
6  Copyright (C) 1993 - 2020 Wolfgang Glunz, wglunz35_AT_pstoedit.net
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22 */
23 #if 1
24 
25 #ifdef _MSC_VER
26 // avoid this warning
27 // this macro needs to be define before all the includes
28 // warning C4996: 'sscanf': This function or variable may be unsafe. Consider using sscanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
29 #define _CRT_SECURE_NO_WARNINGS 1
30 #endif
31 
32 #include "drvpptx.h"
33 #include I_fstream
34 #include I_stdio
35 #include I_stdlib
36 #include I_iomanip
37 #include <cfloat>
38 #include <time.h>
39 
40 #include <errno.h>
41 #include <algorithm>
42 
43 #ifdef _MSC_VER
44 // MS VC++ Windows
45 // _USE_MATH_DEFINES needed on Windows to enable the define M_PI
46 #define _USE_MATH_DEFINES
47 #define srandom srand
48 #define random rand
49 #include <process.h>
50 
51 #if _MSC_VER < 1900
52 // work-around - missing on WIndows. before VC2015
53 long lroundf(float f) {
54  return (long)(floor(f +0.5f));
55 }
56 #endif
57 
58 #else
59 #include <unistd.h>
60 #endif
61 
62 #include <math.h>
63 
64 // when linking against static library - otherwise it means declspec(dllextern)
65 #define ZIP_EXTERN extern
66 
67 #include <zip.h>
68 
69 #ifdef _MSC_VER
70 // MS VC++ Windows
71 // handle this warning:
72 // 'xxxx': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _xxxx. See online help for details.
73 #define getpid _getpid
74 #define strdup _strdup
75 #define unlink _unlink
76 
77 #endif
78 using std::stringstream;
79 using std::ostringstream;
80 using std::istringstream;
81 using std::hex;
82 using std::setw;
83 using std::setfill;
84 
85 
86 
87 /*
88  The following are some things to know about the Office Open XML
89  (OOXML) DrawingML format (a.k.a. PowerPoint or pptx) that should
90  help understand the code in this file:
91 
92  - A pptx file is really a zip archive that contains a bunch of XML
93  files (plus embedded-image files).
94 
95  - These XML files are cross-linked to each other via
96  "relationships" (indirections through ID-to-filename mappings).
97 
98  - Describing graphics -- or anything else -- requires lots of XML
99  verbosity and a substantial amount of boilerplate text.
100 
101  - The DrawingML coordinate system places the origin in the
102  upper-left corner, in contrast to PostScript, which puts it in
103  the lower-left corner.
104 
105  - The main unit of measurement is the English Metric Unit (EMU),
106  of which there are exactly 914,400 per inch, 360,000 per
107  centimeter, and 12,700 per PostScript point. Note that all of
108  those are integers; DrawingML works exclusively with integers.
109  As an example, a distance of 10cm (approximately 3.9" or 283
110  pt.) is written as "3600000" in OOXML.
111 
112  - Angles are specified in 60,000ths of a degree. Positive numbers
113  represent clockwise rotations, and negative numbers represent
114  counterclockwise rotations. For example a 30-degree clockwise
115  rotation is written as "1800000" in OOXML.
116 
117  - Percentages are expressed in 1000ths and with no trailing "%"
118  character. For example, 75% is written as "75000" in OOXML.
119 
120  - Positions are specified as a distance from the upper-left corner
121  of the object's bounding box.
122 
123  - Coordinates within a shape are specified with the origin in the
124  upper-left corner of their bounding box.
125 
126  - Scaling is performed relative to the upper-left corner of the
127  object's bounding box. That is, the upper-left corner is
128  invariant with respect to scaling.
129 
130  - Rotation is performed relative to the center of the object's
131  bounding box.
132 
133  - The order of object transformations is (1) translate the
134  upper-left corner, (2) scale from the upper-left corner, (3)
135  flip horizontally/vertically around the image's center, (4)
136  rotate around the image's center.
137 
138  - Although DrawingML supports horizontal and vertical flipping for
139  text, PowerPoint flips only the shape in which the text is
140  embedded. (This box is always an invisible rectangle in the
141  case of drvpptx-generated DrawingML.) The text itself is in
142  fact *rotated*, not flipped. Specifically, horizontally flipped
143  text negates the sign of the rotation angle, and vertically
144  flipped text subtracts the rotation angle from 180 degrees. For
145  example, if a piece of text is rotated clockwise by 30 degrees
146  (angle="1800000"), flipping it horizontally (flipH="1") is
147  equivalent to rotating it instead by -30 degrees; flipping it
148  vertically (flipV="1") is equivalent to rotating it instead by
149  150 degrees.
150 
151  - Coordinates are always specified in an unrotated coordinate
152  space.
153 
154  - DrawingML supports a fairly large subset of the PostScript that
155  pstoedit understands. Omissions include winding-number fills
156  (only even-odd fills are supported), dash patterns with nonzero
157  offsets, arbitrarily transformed (e.g., skewed) text and images,
158  and vertical text kerning produced by PostScript's awidthshow.
159  I believe that vertical kerning can probably be faked using sub-
160  and superscripts, but that's probably not worth the effort.
161 
162  Office Open XML has been ratified as an Ecma International standard
163  (ECMA-376). See
164  http://www.ecma-international.org/publications/standards/Ecma-376.htm
165  for the complete specification. Note that Microsoft PowerPoint is
166  not always consistent with the ECMA-376 specification and that not
167  all of the information listed above appears in the
168  specification.
169 */
170 
171 const char * const drvPPTX::xml_rels =
172  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
173  "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"
174  " <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"ppt/presentation.xml\"/>\n"
175  "</Relationships>\n";
176 
177 const char * const drvPPTX::xml_slideLayout1_xml =
178  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
179  "<p:sldLayout preserve=\"1\" type=\"blank\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\n"
180  " <p:cSld name=\"Blank Slide\">\n"
181  " <p:spTree>\n"
182  " <p:nvGrpSpPr>\n"
183  " <p:cNvPr id=\"1\" name=\"\"/>\n"
184  " <p:cNvGrpSpPr/>\n"
185  " <p:nvPr/>\n"
186  " </p:nvGrpSpPr>\n"
187  " <p:grpSpPr>\n"
188  " <a:xfrm>\n"
189  " <a:off x=\"0\" y=\"0\"/>\n"
190  " <a:ext cx=\"0\" cy=\"0\"/>\n"
191  " <a:chOff x=\"0\" y=\"0\"/>\n"
192  " <a:chExt cx=\"0\" cy=\"0\"/>\n"
193  " </a:xfrm>\n"
194  " </p:grpSpPr>\n"
195  " </p:spTree>\n"
196  " </p:cSld>\n"
197  "</p:sldLayout>\n";
198 
199 const char * const drvPPTX::xml_slideLayout1_xml_rels =
200  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
201  "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"
202  " <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster\" Target=\"../slideMasters/slideMaster1.xml\"/>\n"
203  "</Relationships>\n";
204 
205 const char * const drvPPTX::xml_slideMaster1_xml =
206  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
207  "<p:sldMaster xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\n"
208  " <p:cSld>\n"
209  " <p:spTree>\n"
210  " <p:nvGrpSpPr>\n"
211  " <p:cNvPr id=\"1\" name=\"\"/>\n"
212  " <p:cNvGrpSpPr/>\n"
213  " <p:nvPr/>\n"
214  " </p:nvGrpSpPr>\n"
215  " <p:grpSpPr>\n"
216  " <a:xfrm>\n"
217  " <a:off x=\"0\" y=\"0\"/>\n"
218  " <a:ext cx=\"0\" cy=\"0\"/>\n"
219  " <a:chOff x=\"0\" y=\"0\"/>\n"
220  " <a:chExt cx=\"0\" cy=\"0\"/>\n"
221  " </a:xfrm>\n"
222  " </p:grpSpPr>\n"
223  " </p:spTree>\n"
224  " </p:cSld>\n"
225  " <p:clrMap accent1=\"accent1\" accent2=\"accent2\" accent3=\"accent3\" accent4=\"accent4\" accent5=\"accent5\" accent6=\"accent6\" bg1=\"lt1\" bg2=\"lt2\" folHlink=\"folHlink\" hlink=\"hlink\" tx1=\"dk1\" tx2=\"dk2\"/>\n"
226  " <p:sldLayoutIdLst>\n"
227  " <p:sldLayoutId id=\"2147483649\" r:id=\"rId2\"/>\n"
228  " </p:sldLayoutIdLst>\n"
229  "</p:sldMaster>\n";
230 
231 const char * const drvPPTX::xml_slideMaster1_xml_rels =
232  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
233  "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"
234  " <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme\" Target=\"../theme/theme1.xml\"/>\n"
235  " <Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout\" Target=\"../slideLayouts/slideLayout1.xml\"/>\n"
236  "</Relationships>\n";
237 
238 const char * const drvPPTX::xml_theme1_xml =
239  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
240  "<a:theme name=\"Office Theme\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">\n"
241  " <a:themeElements>\n"
242  " <a:clrScheme name=\"Office\">\n"
243  " <a:dk1>\n"
244  " <a:sysClr val=\"windowText\" lastClr=\"000000\"/>\n"
245  " </a:dk1>\n"
246  " <a:lt1>\n"
247  " <a:sysClr val=\"window\" lastClr=\"FFFFFF\"/>\n"
248  " </a:lt1>\n"
249  " <a:dk2>\n"
250  " <a:srgbClr val=\"1F497D\"/>\n"
251  " </a:dk2>\n"
252  " <a:lt2>\n"
253  " <a:srgbClr val=\"EEECE1\"/>\n"
254  " </a:lt2>\n"
255  " <a:accent1>\n"
256  " <a:srgbClr val=\"4F81BD\"/>\n"
257  " </a:accent1>\n"
258  " <a:accent2>\n"
259  " <a:srgbClr val=\"C0504D\"/>\n"
260  " </a:accent2>\n"
261  " <a:accent3>\n"
262  " <a:srgbClr val=\"9BBB59\"/>\n"
263  " </a:accent3>\n"
264  " <a:accent4>\n"
265  " <a:srgbClr val=\"8064A2\"/>\n"
266  " </a:accent4>\n"
267  " <a:accent5>\n"
268  " <a:srgbClr val=\"4BACC6\"/>\n"
269  " </a:accent5>\n"
270  " <a:accent6>\n"
271  " <a:srgbClr val=\"F79646\"/>\n"
272  " </a:accent6>\n"
273  " <a:hlink>\n"
274  " <a:srgbClr val=\"0000FF\"/>\n"
275  " </a:hlink>\n"
276  " <a:folHlink>\n"
277  " <a:srgbClr val=\"800080\"/>\n"
278  " </a:folHlink>\n"
279  " </a:clrScheme>\n"
280  " <a:fontScheme name=\"Office\">\n"
281  " <a:majorFont>\n"
282  " <a:latin typeface=\"Arial\"/>\n"
283  " <a:ea typeface=\"DejaVu Sans\"/>\n"
284  " <a:cs typeface=\"DejaVu Sans\"/>\n"
285  " </a:majorFont>\n"
286  " <a:minorFont>\n"
287  " <a:latin typeface=\"Arial\"/>\n"
288  " <a:ea typeface=\"DejaVu Sans\"/>\n"
289  " <a:cs typeface=\"DejaVu Sans\"/>\n"
290  " </a:minorFont>\n"
291  " </a:fontScheme>\n"
292  " <a:fmtScheme name=\"Office\">\n"
293  " <a:fillStyleLst>\n"
294  " <a:solidFill>\n"
295  " <a:schemeClr val=\"phClr\"/>\n"
296  " </a:solidFill>\n"
297  " <a:gradFill rotWithShape=\"1\">\n"
298  " <a:gsLst>\n"
299  " <a:gs pos=\"0\">\n"
300  " <a:schemeClr val=\"phClr\">\n"
301  " <a:tint val=\"50000\"/>\n"
302  " <a:satMod val=\"300000\"/>\n"
303  " </a:schemeClr>\n"
304  " </a:gs>\n"
305  " <a:gs pos=\"35000\">\n"
306  " <a:schemeClr val=\"phClr\">\n"
307  " <a:tint val=\"37000\"/>\n"
308  " <a:satMod val=\"300000\"/>\n"
309  " </a:schemeClr>\n"
310  " </a:gs>\n"
311  " <a:gs pos=\"100000\">\n"
312  " <a:schemeClr val=\"phClr\">\n"
313  " <a:tint val=\"15000\"/>\n"
314  " <a:satMod val=\"350000\"/>\n"
315  " </a:schemeClr>\n"
316  " </a:gs>\n"
317  " </a:gsLst>\n"
318  " <a:lin ang=\"16200000\" scaled=\"1\"/>\n"
319  " </a:gradFill>\n"
320  " <a:gradFill rotWithShape=\"1\">\n"
321  " <a:gsLst>\n"
322  " <a:gs pos=\"0\">\n"
323  " <a:schemeClr val=\"phClr\">\n"
324  " <a:shade val=\"51000\"/>\n"
325  " <a:satMod val=\"130000\"/>\n"
326  " </a:schemeClr>\n"
327  " </a:gs>\n"
328  " <a:gs pos=\"80000\">\n"
329  " <a:schemeClr val=\"phClr\">\n"
330  " <a:shade val=\"93000\"/>\n"
331  " <a:satMod val=\"130000\"/>\n"
332  " </a:schemeClr>\n"
333  " </a:gs>\n"
334  " <a:gs pos=\"100000\">\n"
335  " <a:schemeClr val=\"phClr\">\n"
336  " <a:shade val=\"94000\"/>\n"
337  " <a:satMod val=\"135000\"/>\n"
338  " </a:schemeClr>\n"
339  " </a:gs>\n"
340  " </a:gsLst>\n"
341  " <a:lin ang=\"16200000\" scaled=\"0\"/>\n"
342  " </a:gradFill>\n"
343  " </a:fillStyleLst>\n"
344  " <a:lnStyleLst>\n"
345  " <a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\n"
346  " <a:solidFill>\n"
347  " <a:schemeClr val=\"phClr\">\n"
348  " <a:shade val=\"95000\"/>\n"
349  " <a:satMod val=\"105000\"/>\n"
350  " </a:schemeClr>\n"
351  " </a:solidFill>\n"
352  " <a:prstDash val=\"solid\"/>\n"
353  " </a:ln>\n"
354  " <a:ln w=\"25400\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\n"
355  " <a:solidFill>\n"
356  " <a:schemeClr val=\"phClr\"/>\n"
357  " </a:solidFill>\n"
358  " <a:prstDash val=\"solid\"/>\n"
359  " </a:ln>\n"
360  " <a:ln w=\"38100\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\n"
361  " <a:solidFill>\n"
362  " <a:schemeClr val=\"phClr\"/>\n"
363  " </a:solidFill>\n"
364  " <a:prstDash val=\"solid\"/>\n"
365  " </a:ln>\n"
366  " </a:lnStyleLst>\n"
367  " <a:effectStyleLst>\n"
368  " <a:effectStyle>\n"
369  " <a:effectLst>\n"
370  " <a:outerShdw blurRad=\"40000\" dist=\"20000\" dir=\"5400000\" rotWithShape=\"0\">\n"
371  " <a:srgbClr val=\"000000\">\n"
372  " <a:alpha val=\"38000\"/>\n"
373  " </a:srgbClr>\n"
374  " </a:outerShdw>\n"
375  " </a:effectLst>\n"
376  " </a:effectStyle>\n"
377  " <a:effectStyle>\n"
378  " <a:effectLst>\n"
379  " <a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">\n"
380  " <a:srgbClr val=\"000000\">\n"
381  " <a:alpha val=\"35000\"/>\n"
382  " </a:srgbClr>\n"
383  " </a:outerShdw>\n"
384  " </a:effectLst>\n"
385  " </a:effectStyle>\n"
386  " <a:effectStyle>\n"
387  " <a:effectLst>\n"
388  " <a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">\n"
389  " <a:srgbClr val=\"000000\">\n"
390  " <a:alpha val=\"35000\"/>\n"
391  " </a:srgbClr>\n"
392  " </a:outerShdw>\n"
393  " </a:effectLst>\n"
394  " <a:scene3d>\n"
395  " <a:camera prst=\"orthographicFront\">\n"
396  " <a:rot lat=\"0\" lon=\"0\" rev=\"0\"/>\n"
397  " </a:camera>\n"
398  " <a:lightRig rig=\"threePt\" dir=\"t\">\n"
399  " <a:rot lat=\"0\" lon=\"0\" rev=\"1200000\"/>\n"
400  " </a:lightRig>\n"
401  " </a:scene3d>\n"
402  " <a:sp3d>\n"
403  " <a:bevelT w=\"63500\" h=\"25400\"/>\n"
404  " </a:sp3d>\n"
405  " </a:effectStyle>\n"
406  " </a:effectStyleLst>\n"
407  " <a:bgFillStyleLst>\n"
408  " <a:solidFill>\n"
409  " <a:schemeClr val=\"phClr\"/>\n"
410  " </a:solidFill>\n"
411  " <a:gradFill rotWithShape=\"1\">\n"
412  " <a:gsLst>\n"
413  " <a:gs pos=\"0\">\n"
414  " <a:schemeClr val=\"phClr\">\n"
415  " <a:tint val=\"40000\"/>\n"
416  " <a:satMod val=\"350000\"/>\n"
417  " </a:schemeClr>\n"
418  " </a:gs>\n"
419  " <a:gs pos=\"40000\">\n"
420  " <a:schemeClr val=\"phClr\">\n"
421  " <a:tint val=\"45000\"/>\n"
422  " <a:shade val=\"99000\"/>\n"
423  " <a:satMod val=\"350000\"/>\n"
424  " </a:schemeClr>\n"
425  " </a:gs>\n"
426  " <a:gs pos=\"100000\">\n"
427  " <a:schemeClr val=\"phClr\">\n"
428  " <a:shade val=\"20000\"/>\n"
429  " <a:satMod val=\"255000\"/>\n"
430  " </a:schemeClr>\n"
431  " </a:gs>\n"
432  " </a:gsLst>\n"
433  " <a:path path=\"circle\">\n"
434  " <a:fillToRect l=\"50000\" t=\"-80000\" r=\"50000\" b=\"180000\"/>\n"
435  " </a:path>\n"
436  " </a:gradFill>\n"
437  " <a:gradFill rotWithShape=\"1\">\n"
438  " <a:gsLst>\n"
439  " <a:gs pos=\"0\">\n"
440  " <a:schemeClr val=\"phClr\">\n"
441  " <a:tint val=\"80000\"/>\n"
442  " <a:satMod val=\"300000\"/>\n"
443  " </a:schemeClr>\n"
444  " </a:gs>\n"
445  " <a:gs pos=\"100000\">\n"
446  " <a:schemeClr val=\"phClr\">\n"
447  " <a:shade val=\"30000\"/>\n"
448  " <a:satMod val=\"200000\"/>\n"
449  " </a:schemeClr>\n"
450  " </a:gs>\n"
451  " </a:gsLst>\n"
452  " <a:path path=\"circle\">\n"
453  " <a:fillToRect l=\"50000\" t=\"50000\" r=\"50000\" b=\"50000\"/>\n"
454  " </a:path>\n"
455  " </a:gradFill>\n"
456  " </a:bgFillStyleLst>\n"
457  " </a:fmtScheme>\n"
458  " </a:themeElements>\n"
459  "</a:theme>\n";
460 
461 
462 
465 outzip(nullptr)
466 {
467  // Parse our command-line options.
468  if (options->colortype == "original")
470  else if (options->colortype == "theme")
472  else if (options->colortype == "theme-pure")
474  else {
475  errorMessage("ERROR: -colors must be either \"original\", \"theme\", or \"theme-pure\"");
476  abort();
477  }
478  if (options->fonttype == "windows")
480  else if (options->fonttype == "native")
482  else if (options->fonttype == "theme")
483  font_type = F_THEME;
484  else {
485  errorMessage("ERROR: -fonts must be one of \"windows\", \"native\", or \"theme\"");
486  abort();
487  }
488  if (options->embeddedfonts != "") {
489  // Loop over each EOT filename.
490  stringstream embed_stream(RSString(options->embeddedfonts).c_str());
491  string efile;
492  while (getline(embed_stream, efile, ',')) {
493  // Ensure the file exists, then add it to the list.
494  const char * const efile_cstr = efile.c_str();
495  if (!fileExists(efile_cstr)) {
496  RSString errmess("ERROR: Cannot open file ");
497  errmess += efile_cstr;
498  errorMessage(errmess.c_str());
499  abort();
500  }
501  eotlist.insert(efile);
502  }
503  }
504 
505  // Map PostScript core font names to their Windows + PANOSE replacements.
506  if (font_type == F_WINDOWS) {
507  ps2win.insert("Courier", "Courier New,Courier New,02070309020205020404");
508  ps2win.insert("Courier-Bold", "Courier New Bold,Courier New,02070609020205020404");
509  ps2win.insert("Courier-BoldOblique", "Courier New Bold Italic,Courier New,02070609020205090404");
510  ps2win.insert("Courier-Oblique", "Courier New Italic,Courier New,02070409020205090404");
511  ps2win.insert("Helvetica", "Arial,Arial,020b0604020202020204");
512  ps2win.insert("Helvetica-Bold", "Arial Bold,Arial,020b0704020202020204");
513  ps2win.insert("Helvetica-BoldOblique", "Arial Bold Italic,Arial,020b0704020202090204");
514  ps2win.insert("Helvetica-Oblique", "Arial Italic,Arial,020b0604020202090204");
515  ps2win.insert("Times-Bold", "Times New Roman Bold,Times New Roman,02020803070505020304");
516  ps2win.insert("Times-BoldItalic", "Times New Roman Bold Italic,Times New Roman,02020703060505090304");
517  ps2win.insert("Times-Italic", "Times New Roman Italic,Times New Roman,02020503050405090304");
518  ps2win.insert("Times-Roman", "Times New Roman,Times New Roman,02020603050405020304");
519  }
520 
521  // Output all floating-point numbers as integers.
522  slidef << std::fixed << std::setprecision(0);
523 
524  // Seed the random-number generator.
525  srandom((unsigned int) time(nullptr)*getpid());
526 
527  // Create a zip archive for holding PresentationML data.
528  create_pptx();
529 
530  // Specify the slide dimensions (must match ppt/presentation.xml's
531  // <p:sldSz> tag).
532  slideBBox.ll = Point(0, 0);
533  slideBBox.ur = Point(10080625/12700.0, 7559675/12700.0);
534 
535  // Number IDs from 1 and images from 0.
536  next_id = 1;
537  total_images = 0;
538  page_images = 0;
539 }
540 
542 {
543  // Embed fonts in the PPTX file if asked to do so.
544  if (!eotlist.empty()) {
545  unsigned int fontNum = 1;
546  for (auto iter = eotlist.begin();
547  iter != eotlist.end();
548  ++iter) {
549  const char * const eotFileName = iter->c_str();
550  struct zip_source *font_file = zip_source_file(outzip, eotFileName, 0, -1);
551  if (font_file == nullptr) {
552  RSString errmessage("ERROR: Failed to embed font file ");
553  errmessage += eotFileName ;
554  errmessage += " (" ;
555  errmessage += zip_strerror(outzip) ;
556  errmessage += ")" ;
557 
558  errorMessage(errmessage.c_str());
559  abort();
560  }
561  ostringstream full_eot_filename;
562  full_eot_filename << "ppt/fonts/font" << fontNum << ".fntdata";
563  if (zip_add(outzip, full_eot_filename.str().c_str(), font_file) == -1) {
564  RSString errmessage("ERROR: Failed to embed font file ");
565  errmessage += eotFileName ;
566  errmessage += " as " ;
567  errmessage += full_eot_filename.str().c_str() ;
568  errmessage += " (" ;
569  errmessage += zip_strerror(outzip) ;
570  errmessage += ")" ;
571 
572  errorMessage(errmessage.c_str());
573  abort();
574  }
575  fontNum++;
576  }
577  }
578 
579  // Create the presentation file.
580  ostringstream xml_presentation_xml;
581  xml_presentation_xml <<
582  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
583  "<p:presentation xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\""
584  << (eotlist.empty() ? ">\n" : " embedTrueTypeFonts=\"1\">\n");
585  xml_presentation_xml <<
586  " <p:sldMasterIdLst>\n"
587  " <p:sldMasterId id=\"2147483648\" r:id=\"rId2\"/>\n"
588  " </p:sldMasterIdLst>\n"
589  " <p:sldIdLst>\n";
590  for (unsigned int p = 0; p < totalNumberOfPages(); p++)
591  xml_presentation_xml << " <p:sldId id=\"" << p+256
592  << "\" r:id=\"rId" << p+3 << "\"/>\n";
593  xml_presentation_xml << " </p:sldIdLst>\n"
594  " <p:sldSz cx=\"10080625\" cy=\"7559675\"/>\n"
595  " <p:notesSz cx=\"7772400\" cy=\"10058400\"/>\n";
596  if (!eotlist.empty()) {
597  unsigned int rId = totalNumberOfPages() + 3;
598  xml_presentation_xml << " <p:embeddedFontLst>\n";
599  for (auto iter = eotlist.begin();
600  iter != eotlist.end();
601  ++iter) {
602  // Get information about the font.
603 
604  TextInfo textinfo;
605  eot2texinfo(*iter,textinfo);
607  RSString panose;
608  bool isBold;
609  bool isItalic;
610  unsigned char pitchFamily;
611  get_font_props(textinfo, &typeface, &panose, &isBold, &isItalic, &pitchFamily);
612 
613  // Describe the font to be embedded.
614  xml_presentation_xml << " <p:embeddedFont>\n"
615  << " <p:font typeface=\"" << typeface
616  << "\" pitchFamily=\"" << (unsigned int)pitchFamily
617  << "\" charset=\"0\"/>\n";
618  switch (int(isBold)*2 + int(isItalic)) {
619  case 0:
620  xml_presentation_xml << " <p:regular r:id=\"rId" << rId << "\"/>\n";
621  break;
622 
623  case 1:
624  xml_presentation_xml << " <p:italic r:id=\"rId" << rId << "\"/>\n";
625  break;
626 
627  case 2:
628  xml_presentation_xml << " <p:bold r:id=\"rId" << rId << "\"/>\n";
629  break;
630 
631  case 3:
632  xml_presentation_xml << " <p:boldItalic r:id=\"rId" << rId << "\"/>\n";
633  break;
634 
635  default:
636  errf << "\t\tERROR: unexpected case in drvpptx " << endl;
637  abort();
638  break;
639  }
640  xml_presentation_xml << " </p:embeddedFont>\n";
641  rId++;
642  }
643  xml_presentation_xml << " </p:embeddedFontLst>\n";
644  }
645  xml_presentation_xml << "</p:presentation>\n";
646  create_pptx_file("ppt/presentation.xml", xml_presentation_xml.str().c_str());
647 
648  // Create the presentation relationships file.
649  ostringstream xml_presentation_xml_rels;
650  xml_presentation_xml_rels <<
651  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
652  "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"
653  " <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme\" Target=\"theme/theme1.xml\"/>\n"
654  " <Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster\" Target=\"slideMasters/slideMaster1.xml\"/>\n";
655  for (unsigned int p = 0; p < totalNumberOfPages(); p++)
656  xml_presentation_xml_rels << " <Relationship Id=\"rId" << p+3
657  << "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide\" Target=\"slides/slide" << p+1
658  << ".xml\"/>\n";
659  if (!eotlist.empty()) {
660  unsigned int fontNum = 1;
661  unsigned int rId = totalNumberOfPages() + 3;
662  for (auto iter = eotlist.begin();
663  iter != eotlist.end();
664  ++iter) {
665  xml_presentation_xml_rels << " <Relationship Id=\"rId" << rId
666  << "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/font\""
667  << " Target=\"fonts/font" << fontNum << ".fntdata\"/>\n";
668  fontNum++;
669  rId++;
670  }
671  }
672  xml_presentation_xml_rels << "</Relationships>\n";
673  create_pptx_file("ppt/_rels/presentation.xml.rels", xml_presentation_xml_rels.str().c_str());
674 
675  // Create the content-types file.
676  ostringstream xml_content_types;
677  xml_content_types <<
678  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
679  "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n"
680  " <Override PartName=\"/_rels/.rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n"
681  " <Override PartName=\"/ppt/_rels/presentation.xml.rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n"
682  " <Override PartName=\"/ppt/slideLayouts/_rels/slideLayout1.xml.rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n"
683  " <Override PartName=\"/ppt/slideLayouts/slideLayout1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml\"/>\n"
684  " <Override PartName=\"/ppt/theme/theme1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/>\n"
685  " <Override PartName=\"/ppt/slideMasters/_rels/slideMaster1.xml.rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n"
686  " <Override PartName=\"/ppt/slideMasters/slideMaster1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml\"/>\n"
687  " <Override PartName=\"/ppt/presentation.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml\"/>\n";
688  for (unsigned int p = 0; p < totalNumberOfPages(); p++)
689  xml_content_types << " <Override PartName=\"/ppt/slides/_rels/slide" << p+1
690  << ".xml.rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n"
691  << " <Override PartName=\"/ppt/slides/slide" << p+1
692  << ".xml\" ContentType=\"application/vnd.openxmlformats-officedocument.presentationml.slide+xml\"/>\n";
693  if (total_images > 0)
694  xml_content_types << " <Default Extension=\"png\" ContentType=\"image/png\"/>\n";
695  if (!eotlist.empty())
696  xml_content_types << " <Default Extension=\"fntdata\" ContentType=\"application/x-fontdata\"/>\n";
697  xml_content_types << "</Types>\n";
698  create_pptx_file("[Content_Types].xml", xml_content_types.str().c_str());
699 
700  // Write the PPTX file to disk.
701  if (zip_close(outzip) == -1) {
702  RSString errmessage("ERROR: Failed to generate ");
703  errmessage += outFileName ;
704  errmessage += " (" ;
705  errmessage += zip_strerror(outzip) ;
706  errmessage += ")" ;
707 
708  errorMessage(errmessage.c_str());
709  abort();
710  }
711 }
712 
713 // Create a file in a PPTX package from given contents.
714 void drvPPTX::create_pptx_file(const char * relname, const char * contents)
715 {
716  // Convert the file contents into a data source.
717  struct zip_source * file_source = zip_source_buffer(outzip, strdup(contents), strlen(contents), 1);
718  if (file_source == nullptr) {
719  RSString errmessage("ERROR: Failed to create data for ");
720  errmessage += relname ;
721  errmessage += " (" ;
722  errmessage += zip_strerror(outzip) ;
723  errmessage += ")" ;
724  errorMessage(errmessage.c_str());
725  abort();
726  }
727 
728  // Add the data source to the PPTX file.
729  if (zip_add(outzip, relname, file_source) == -1) {
730  RSString errmessage("ERROR: Failed to insert ");
731  errmessage += relname ;
732  errmessage += " into " ;
733  errmessage += outFileName ;
734  errmessage += " (" ;
735  errmessage += zip_strerror(outzip) ;
736  errmessage += ")" ;
737  errorMessage( errmessage.c_str());
738  abort();
739  }
740 }
741 
742 // Create a PPTX file from scratch and add some of the boilerplate.
744 {
745  // Create a PPTX file for writing.
747  int ziperr;
748  outzip = zip_open(outFileName.c_str(), ZIP_CREATE, &ziperr);
749  if (outzip == nullptr) {
750  char reason[101];
751  zip_error_to_str(reason, 100, ziperr, errno);
752  RSString errmessage("ERROR: Failed to create ");
753  errmessage += outFileName ;
754  errmessage += " (" ;
755  errmessage += reason ;
756  errmessage += ")" ;
757 
758  errorMessage(errmessage.c_str());
759  abort();
760  }
761  RSString comment("Created by pstoedit's pptx driver from PostScript input ");
762  comment += inFileName;
763  zip_set_archive_comment(outzip, comment.c_str(), (zip_uint16_t)comment.length());
764 
765  // Insert boilerplate files into the PPTX archive.
766  create_pptx_file("_rels/.rels", xml_rels);
767  create_pptx_file("ppt/slideLayouts/slideLayout1.xml", xml_slideLayout1_xml);
768  create_pptx_file("ppt/slideLayouts/_rels/slideLayout1.xml.rels", xml_slideLayout1_xml_rels);
769  create_pptx_file("ppt/slideMasters/slideMaster1.xml", xml_slideMaster1_xml);
770  create_pptx_file("ppt/slideMasters/_rels/slideMaster1.xml.rels", xml_slideMaster1_xml_rels);
771  create_pptx_file("ppt/theme/theme1.xml", xml_theme1_xml);
772 }
773 
774 void drvPPTX::print_coords(const BBox & pathBBox)
775 {
776  // Output a list of coordinates in the shape's coordinate system.
777  const long int xshift_emu = -xtrans(pathBBox.ll.x_);
778  const long int yshift_emu = -ytrans(pathBBox.ur.y_);
779  for (unsigned int n = 0; n < numberOfElementsInPath(); n++) {
780  const basedrawingelement & elem = pathElement(n);
781  switch (elem.getType()) {
782  case moveto:
783  {
784  const Point & p = elem.getPoint(0);
785  slidef << " <a:moveTo>\n"
786  << " <a:pt "
787  << pt2emu(p.x_, p.y_, xshift_emu, yshift_emu) << "/>\n"
788  << " </a:moveTo>\n";
789  }
790  break;
791  case lineto:
792  {
793  const Point & p = elem.getPoint(0);
794  slidef << " <a:lnTo>\n"
795  << " <a:pt "
796  << pt2emu(p.x_, p.y_, xshift_emu, yshift_emu) << "/>\n"
797  << " </a:lnTo>\n";
798  }
799  break;
800  case curveto:
801  {
802  slidef << " <a:cubicBezTo>\n";
803  for (unsigned int cp = 0; cp < 3; cp++) {
804  const Point & p = elem.getPoint(cp);
805  slidef << " <a:pt "
806  << pt2emu(p.x_, p.y_, xshift_emu, yshift_emu) << "/>\n";
807  }
808  slidef << " </a:cubicBezTo>\n";
809  }
810  break;
811  case closepath:
812  slidef << " <a:close/>\n";
813  break;
814  default:
815  errf << "\t\tERROR: unexpected case in drvpptx " << endl;
816  abort();
817  break;
818  }
819  }
820 }
821 
822 void drvPPTX::open_page()
823 {
824  // Determine how much to offset the current page to center its
825  // graphics within the slide.
826  const BBox pageBBox = getCurrentBBox();
827  center_offset.x_ = (slideBBox.ur.x_ - slideBBox.ll.x_ - (pageBBox.ur.x_ - pageBBox.ll.x_)) / 2.0f;
828  center_offset.y_ = (slideBBox.ur.y_ - slideBBox.ll.y_ - (pageBBox.ur.y_ - pageBBox.ll.y_)) / 2.0f;
829 
830  // Output OOXML header boilerplate.
831  slidef << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
832  << "<p:sld xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\"\n"
833  << " xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\"\n"
834  << " xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\n"
835  << " <p:cSld>\n"
836  << " <p:spTree>\n"
837  << " <p:nvGrpSpPr>\n"
838  << " <p:cNvPr id=\"1\" name=\"\"/>\n"
839  << " <p:cNvGrpSpPr/>\n"
840  << " <p:nvPr/>\n"
841  << " </p:nvGrpSpPr>\n"
842  << " <p:grpSpPr>\n"
843  << " <a:xfrm>\n"
844  << " <a:off x=\"0\" y=\"0\"/>\n"
845  << " <a:ext cx=\"0\" cy=\"0\"/>\n"
846  << " <a:chOff x=\"0\" y=\"0\"/>\n"
847  << " <a:chExt cx=\"0\" cy=\"0\"/>\n"
848  << " </a:xfrm>\n"
849  << " </p:grpSpPr>\n";
850 
851  // Reset the image count.
852  page_images = 0;
853 }
854 
855 void drvPPTX::close_page()
856 {
857  // Output OOXML trailer boilerplate.
858  slidef << " </p:spTree>\n"
859  << " </p:cSld>\n"
860  << "</p:sld>\n";
861 
862  // Add the current slide to the PPTX file.
863  const char * const slideContents_c = strdup(slidef.str().c_str());
864  struct zip_source * slideContents = zip_source_buffer(outzip, slideContents_c, strlen(slideContents_c), 1);
865  ostringstream slideFileName;
866  slideFileName << "ppt/slides/slide" << currentPageNumber << ".xml";
867  char * const slideFileName_c = strdup(slideFileName.str().c_str()); // libzip seems to store a pointer to this.
868  if (zip_add(outzip, slideFileName_c, slideContents) == -1) {
869  RSString errmessage("ERROR: Failed to store ");
870  errmessage += slideFileName_c ;
871  errmessage += " in " ;
872  errmessage += outFileName ;
873  errmessage += " (" ;
874  errmessage += zip_strerror(outzip) ;
875  errmessage += ")" ;
876  errorMessage( errmessage.c_str());
877  free(slideFileName_c); // make leak checkers happier
878  abort();
879  }
880 
881  // Clear the slide contents in preparation for the next page.
882  slidef.str("");
883  slidef.clear();
884 
885  // Create a relationships file for the current slide.
886  ostringstream slideRelName;
887  slideRelName << "ppt/slides/_rels/slide" << currentPageNumber << ".xml.rels";
888  ostringstream slideRelContents;
889  slideRelContents <<
890  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
891  "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"
892  " <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout\" Target=\"../slideLayouts/slideLayout1.xml\"/>\n";
893  for (unsigned int i = 0; i < page_images; i++)
894  slideRelContents << " <Relationship Id=\"rId" << i + 2
895  << "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\" Target=\"../media/image"
896  << total_images - page_images + i + 1 << ".png\"/>\n";
897  slideRelContents << "</Relationships>\n";
898  create_pptx_file(slideRelName.str().c_str(), slideRelContents.str().c_str());
899 }
900 
901 unsigned char drvPPTX::panose2pitch (const unsigned int * panose_vals)
902 {
903  // Convert a PANOSE characterization to an OOXML pitch family.
904  unsigned char pitchFamily = 0;
905  switch (panose_vals[0]) {
906  case 3:
907  // "Latin script"
908  pitchFamily = 0x40; // "Script"
909  break;
910  case 4:
911  // "Latin decorative"
912  pitchFamily = 0x50; // "Decorative"
913  break;
914  default:
915  // "Latin text" or other
916  pitchFamily = (panose_vals[1] >= 11 && panose_vals[1] <= 13 ? 0x20 : 0x10); // "Swiss" or "Roman"
917  break;
918  }
919  pitchFamily |= (panose_vals[3] == 9 ? 0x01 : 0x02); // "Fixed" or "Variable"
920  return pitchFamily;
921 }
922 
923 void drvPPTX::get_font_props(const TextInfo & textinfo,
924  RSString * typeface, RSString * panose,
925  bool * isBold, bool * isItalic,
926  unsigned char * pitchFamily) const
927 {
928  // Replace PostScript core fonts with Windows fonts.
929  RSString currentFontName(textinfo.currentFontName);
930  if (font_type == F_WINDOWS) {
931  const RSString * winFont = ps2win.getValue(currentFontName);
932  if (winFont != nullptr)
933  currentFontName = *winFont;
934  }
935 
936  // Determine properties of the given font.
937  unsigned int panose_vals[10];
938  if (string_contains(currentFontName,",")) {
939  // Split the font name at commas into <full name>,<family name>,<panose>.
940  stringstream fontname_stream(currentFontName.c_str());
941  string fullname;
942  string familyname;
943  string panose_str;
944  if (getline(fontname_stream, fullname, ',') &&
945  getline(fontname_stream, familyname, ',') &&
946  getline(fontname_stream, panose_str, ',') &&
947  sscanf(panose_str.c_str(),
948  "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
949  &panose_vals[0], &panose_vals[1], &panose_vals[2],
950  &panose_vals[3], &panose_vals[4], &panose_vals[5],
951  &panose_vals[6], &panose_vals[7], &panose_vals[8],
952  &panose_vals[9]) == 10) {
953  // Compute everything from the given PANOSE categorization.
954  typeface->assign(familyname.c_str());
955  panose->assign(panose_str.c_str());
956  *isBold = panose_vals[2] >= 7; // "Demi" and up
957  *isItalic = panose_vals[7] >= 9; // "Oblique" letterform
958  *pitchFamily = panose2pitch(panose_vals);
959  return;
960  }
961  }
962 
963  // If we're here, then either the user didn't specify a font map
964  // that includes PANOSE data or the current font was not found in
965  // the map. Do the best we can heuristically.
966  *typeface = textinfo.currentFontFamilyName;
967  if (*typeface == "Courier" || *typeface == "unknown"
968  || typeface->length() < currentFontName.length())
969  *typeface = currentFontName;
970  for (int i = 0; i < 10; i++)
971  panose_vals[i] = 0;
972  static const RSString Sans("Sans");
973  if (string_contains(currentFontName,Sans) || string_contains(textinfo.currentFontFullName,Sans)) {
974  panose_vals[0] = 2; // "Latin text"
975  panose_vals[1] = 11; // "Normal sans"
976  }
977  else {
978  static const RSString Script("Script");
979  static const RSString Hand("Hand");
980  if (string_contains(currentFontName,Script) || string_contains(textinfo.currentFontFullName,Script)
981  || string_contains(currentFontName,Hand) || string_contains(textinfo.currentFontFullName,Hand))
982  panose_vals[0] = 3; // "Latin script"
983  else {
984  panose_vals[0] = 2; // "Latin text"
985  panose_vals[1] = 2; // "Cove"
986  }
987  }
988  static const RSString Bold("Bold");
989  if (string_contains(currentFontName,Bold) || string_contains(textinfo.currentFontFullName,Bold)) {
990  *isBold = true;
991  panose_vals[2] = 8; // "Bold"
992  }
993  else {
994  *isBold = false;
995  panose_vals[2] = 5; // "Book"
996  }
997  static const RSString Italic("Italic");
998  static const RSString Oblique("Oblique");
999  if (string_contains(currentFontName,Italic) || string_contains(textinfo.currentFontFullName,Italic)
1000  || string_contains(currentFontName,Oblique) || string_contains(textinfo.currentFontFullName,Oblique)) {
1001  *isItalic = true;
1002  panose_vals[9] = 9; // "Contact/oblique"
1003  }
1004  else {
1005  *isItalic = false;
1006  panose_vals[9] = 2; // "Contact/normal"
1007  }
1008  static const RSString Mono("Mono");
1009  if (string_contains(currentFontName,Mono) || string_contains(textinfo.currentFontFullName,Mono))
1010  panose_vals[4] = 9; // "Monospaced"
1011  else
1012  panose_vals[4] = 3; // "Modern"
1013  char panose_str[21];
1014  sprintf(panose_str, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
1015  panose_vals[0], panose_vals[1], panose_vals[2], panose_vals[3],
1016  panose_vals[4], panose_vals[5], panose_vals[6], panose_vals[7],
1017  panose_vals[8], panose_vals[9]);
1018  panose->assign(panose_str);
1019  *pitchFamily = panose2pitch(panose_vals);
1020 }
1021 
1022 // Fabricate a TextInfo structure from an EOT file header.
1023 void drvPPTX::eot2texinfo(const string& eotfilename, TextInfo & textinfo)
1024 {
1025  unsigned char panose_vals[10];
1026  unsigned char charvals[4];
1027 
1028  // Parse the EOT header.
1029  ifstream eotfile(eotfilename.c_str());
1030  eotfile.ignore(4+4+4+4); // Size, font data size, version, flags
1031  eotfile.read((char *)panose_vals, 10); // PANOSE values
1032  eotfile.ignore(1+1+4); // Character set, italic, weight
1033  eotfile.read((char *)charvals, 2); // Embedding restrictions
1034  const short fstype = charvals[1]<<8 | charvals[0];
1035  eotfile.read((char *)charvals, 2); // Magic number
1036  const unsigned short magicnum = charvals[1]<<8 | charvals[0];
1037  if (magicnum != 0x504c) {
1038  RSString errmessage("ERROR: ");
1039  errmessage += eotfilename.c_str() ;
1040  errmessage += " is not a valid Embedded OpenType (EOT) font file" ;
1041  errorMessage(errmessage.c_str());
1042  abort();
1043  }
1044  eotfile.ignore(4+4+4+4+4+4); // Unicode ranges 1-4 and code page ranges 1-2
1045  eotfile.ignore(4+4+4+4+4+2); // Checksum adjustment, reserved 1-4, padding
1046  eotfile.read((char *)charvals, 2); // Family-name length
1047  unsigned short namesize = charvals[1]<<8 | charvals[0];
1048  auto familyname = new char[namesize];
1049  eotfile.read(familyname, namesize); // Family name
1050  for (unsigned short i = 0; i < namesize/2; i++)
1051  // Cheesy conversion from Unicode to ASCII
1052  familyname[i] = familyname[i*2];
1053  textinfo.currentFontFamilyName = RSString(familyname, namesize/2);
1054  delete[] familyname;
1055  eotfile.ignore(2); // Padding
1056  eotfile.read((char *)charvals, 2); // Style-name length
1057  namesize = charvals[1]<<8 | charvals[0];
1058  eotfile.ignore(namesize); // Style name
1059  eotfile.ignore(2); // Padding
1060  eotfile.read((char *)charvals, 2); // Version-name length
1061  namesize = charvals[1]<<8 | charvals[0];
1062  eotfile.ignore(namesize); // Version name
1063  eotfile.ignore(2); // Padding
1064  eotfile.read((char *)charvals, 2); // Full-name length
1065  namesize = charvals[1]<<8 | charvals[0];
1066  auto fullname = new char[namesize];
1067  eotfile.read(fullname, namesize); // Full name
1068  for (unsigned short i = 0; i < namesize/2; i++)
1069  // Cheesy conversion from Unicode to ASCII
1070  fullname[i] = fullname[i*2];
1071  textinfo.currentFontFullName = RSString(fullname, namesize/2);
1072  delete[] fullname;
1073  eotfile.close();
1074 
1075  // Warn the user if the font has embedding restrictions.
1076  if (fstype == 0x0002)
1077  errf << "WARNING: Font " << textinfo.currentFontFullName << " ("
1078  << eotfilename << ") indicates that it must not be modified,"
1079  << " embedded, or exchanged in any manner without first obtaining"
1080  << " permission from the legal owner. Do not embed this font"
1081  << " unless you have obtained such permission.\n";
1082 
1083  // Concatenate the PANOSE data to the font name so get_font_props()
1084  // can extract and process it.
1085  char panose_str[22];
1086  sprintf(panose_str, ",%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
1087  panose_vals[0], panose_vals[1], panose_vals[2],
1088  panose_vals[3], panose_vals[4], panose_vals[5],
1089  panose_vals[6], panose_vals[7], panose_vals[8],
1090  panose_vals[9]);
1091  textinfo.currentFontName = textinfo.currentFontFullName;
1092  textinfo.currentFontName += ',';
1093  textinfo.currentFontName += textinfo.currentFontFamilyName;
1094  textinfo.currentFontName += panose_str;
1095 }
1096 
1097 // Given two 2-D vectors (represented as Points), return the angle
1098 // from the first to the second.
1099 // http://stackoverflow.com/questions/5188561/signed-angle-between-two-3d-vectors-with-same-origin-within-the-same-plane-reci
1100 // was useful here.
1102 {
1103  // Normalize each vector.
1104  float scale = pythagoras(first.x_, first.y_);
1105  first.x_ /= scale;
1106  first.y_ /= scale;
1107  scale = pythagoras(second.x_, second.y_);
1108  second.x_ /= scale;
1109  second.y_ /= scale;
1110 
1111  // Determine the direction of the rotation.
1112  const float direction = first.x_*second.y_ - first.y_*second.x_;
1113 
1114  // Determine the rotation itself.
1115  float angle = acos(first.x_*second.x_ + first.y_*second.y_) * 180.0f/(float)M_PI;
1116  if (direction < 0)
1117  angle = -angle;
1118  return angle;
1119 }
1120 
1121 // Given a six-element PostScript transformation matrix, determine the
1122 // components of the transformation it represents.
1123 void drvPPTX::parse_xform_matrix(const float * origMatrix,
1124  bool * mirrored,
1125  float * xscale, float * yscale,
1126  float * rotation,
1127  float * x_trans, float * y_trans)
1128 {
1129  // Return the translation then remove it from the matrix.
1130  float matrix[6];
1131  for (int i = 0; i < 6; i++)
1132  matrix[i] = origMatrix[i];
1133  *x_trans = matrix[4];
1134  *y_trans = matrix[5];
1135  matrix[4] = 0.0f;
1136  matrix[5] = 0.0f;
1137 
1138  // Determine whether the transformation includes mirroring.
1139  const Point xunit(1.0f, 0.0f);
1140  const Point xunit_xform = xunit.transform(matrix);
1141  const Point yunit(0.0f, 1.0f);
1142  const Point yunit_xform = yunit.transform(matrix);
1143  float rot90 = angle_between(xunit_xform, yunit_xform);
1144  *mirrored = rot90 < 0;
1145 
1146  // Compute the rotation angle.
1147  *rotation = angle_between(xunit, xunit_xform);
1148  if (*mirrored)
1149  *rotation = fmodf(*rotation + 180.0f, 360.0f);
1150 
1151  // Compute the scaling.
1152  *xscale = pythagoras(xunit_xform.x_, xunit_xform.y_);
1153  *yscale = pythagoras(yunit_xform.x_, yunit_xform.y_);
1154 }
1155 
1156 void drvPPTX::show_text(const TextInfo & textinfo)
1157 {
1158  // Output the non-visual shape properties.
1159  slidef << " <p:sp>\n"
1160  << " <p:nvSpPr>\n"
1161  << " <p:cNvPr id=\"" << next_id << "\" name=\"pstoedit " << next_id << "\"/>\n"
1162  << " <p:cNvSpPr/>\n"
1163  << " <p:nvPr/>\n"
1164  << " </p:nvSpPr>\n";
1165  next_id++;
1166 
1167  // Compute the unrotated text width and height.
1168  const float text_width = // Unrotated width
1169  pythagoras(textinfo.x_end() - textinfo.x(), textinfo.y_end() - textinfo.y());
1170  const float text_height = textinfo.currentFontSize; // Unrotated height
1171 
1172  // Determine if the text is flipped horizontally. We don't test for
1173  // vertical flipping because this is isomorphic to a horizontal flip
1174  // plus a rotation.
1175  bool flipH;
1176  float xscale, yscale, angle, x_trans, y_trans;
1177  parse_xform_matrix(textinfo.FontMatrix, &flipH, &xscale, &yscale,
1178  &angle, &x_trans, &y_trans);
1179  if (flipH)
1180  angle = -angle;
1181 
1182  // Compute the upper-left corner of the rotated text.
1183  const Point text_pivot(textinfo.x(), textinfo.y()); // Unrotated lower left
1184  Point text_ul(textinfo.x(), textinfo.y() + text_height); // Unrotated upper left
1185  Point text_c = text_pivot + Point(text_width/2.0f, text_height/2.0f); // Unrotated center
1186  if (flipH) {
1187  text_ul.x_ -= text_width;
1188  text_c.x_ -= text_width;
1189  }
1190 
1191  // Rotate the upper-left corner and center around the original
1192  // lower-left corner, then unrotate the upper-left corner around the
1193  // new center.
1194  const Point text_ul_rot = rotate_pt_around(text_ul, angle, text_pivot);
1195  const Point text_c_rot = rotate_pt_around(text_c, angle, text_pivot);
1196  const Point text_ofs = rotate_pt_around(text_ul_rot, -angle, text_c_rot);
1197 
1198  // Output the visual shape properties.
1199  slidef << " <p:spPr>\n";
1200  slidef << " <a:xfrm";
1201  if (angle != 0.0f)
1202  slidef << " rot=\"" << -angle*60000 << '"';
1203  if (flipH)
1204  slidef << " flipH=\"1\"";
1205  slidef << ">\n";
1206  slidef << " <a:off " << pt2emu(text_ofs.x_, text_ofs.y_) << "/>\n";
1207  slidef << " <a:ext " << pt2emu(text_width, text_height,
1208  0, 0, "cx", "cy", true) << "/>\n"
1209  << " </a:xfrm>\n"
1210  << " <a:prstGeom prst=\"rect\"/>\n"
1211  << " </p:spPr>\n";
1212 
1213  // Get information about the current font.
1215  RSString panose;
1216  bool isBold;
1217  bool isItalic;
1218  unsigned char pitchFamily;
1219  get_font_props(textinfo, &typeface, &panose, &isBold, &isItalic, &pitchFamily);
1220 
1221  // Output the text itself.
1222  slidef << " <p:txBody>\n"
1223  << " <a:bodyPr wrap=\"none\" lIns=\"0\" tIns=\"0\" rIns=\"0\" bIns=\"0\" rtlCol=\"0\">\n"
1224  << " <a:spAutoFit/>\n"
1225  << " </a:bodyPr>\n"
1226  << " <a:p>\n"
1227  << " <a:r>\n"
1228  << " <a:rPr dirty=\"1\" smtClean=\"0\" sz=\""
1229  << textinfo.currentFontSize*100.0 << '"'
1230  << (isBold ? " b=\"1\"" : "")
1231  << (isItalic ? " i=\"1\"" : "");
1232  if (textinfo.ax != 0)
1233  slidef << " spc=\"" << textinfo.ax*100.0 << '"';
1234  slidef << ">\n";
1235  print_color(16, textinfo.currentR, textinfo.currentG, textinfo.currentB);
1236  switch (font_type) {
1237  case F_WINDOWS:
1238  case F_NATIVE:
1239  slidef << " <a:latin typeface=\"" << typeface
1240  << "\" pitchFamily=\"" << (unsigned int)pitchFamily
1241  << "\" panose=\"" << panose
1242  << "\" charset=\"0\"/>\n";
1243  break;
1244  case F_THEME:
1245  // Use the theme's default font.
1246  break;
1247  default:
1248  errorMessage("ERROR: Unknown font type");
1249  abort();
1250  break;
1251  }
1252  slidef << " </a:rPr>\n"
1253  << " <a:t>";
1254  static bool warned_invalid_char = false; // true=already issued an invalid-character warning
1255  for (size_t c = 0; c < textinfo.thetext.length(); c++) {
1256  const unsigned char onechar = textinfo.thetext[c];
1257  if (onechar < 32 || (onechar >= 128 && onechar < 192)) {
1258  if (!warned_invalid_char) {
1259  errf << "Warning: Character " << (unsigned int)onechar << " is not allowed in OOXML text; ignoring\n";
1260  warned_invalid_char = true;
1261  }
1262  }
1263  else
1264  switch (onechar) {
1265  case '<':
1266  slidef << "&lt;";
1267  break;
1268  case '>':
1269  slidef << "&gt;";
1270  break;
1271  case '&':
1272  slidef << "&amp;";
1273  break;
1274  default:
1275  if (onechar < 128)
1276  slidef << onechar;
1277  else
1278  slidef << char(0xC0 | (onechar>>6)) << char(0x80 | (onechar&0x3F));
1279  break;
1280  }
1281  }
1282  slidef << "</a:t>\n"
1283  << " </a:r>\n"
1284  << " <a:endParaRPr dirty=\"1\">\n";
1285  print_color(14, textinfo.currentR, textinfo.currentG, textinfo.currentB);
1286  slidef << " </a:endParaRPr>\n"
1287  << " </a:p>\n"
1288  << " </p:txBody>\n"
1289  << " </p:sp>\n";
1290 }
1291 
1292 
1293 
1294 long int drvPPTX::bp2emu (float bp) {
1295  return lroundf(bp * 12700.0f);
1296 }
1297 
1298 
1299 const char * drvPPTX::pt2emu(float x_bp, float y_bp,
1300  long int xshift_emu, long int yshift_emu,
1301  RSString x_name, RSString y_name,
1302  bool scaleOnly) const
1303 {
1304  // Convert a PostScript (x, y) coordinate (in big points with its
1305  // origin in the lower left) to an XML string (in EMUs with its
1306  // origin in the upper left). If scaleOnly is true, then scale the
1307  // units without translating the coordinates (useful for widths and
1308  // heights). Note that the result will get overwritten on the
1309  // subsequent call.
1310  static char emuString[100]; // Should be more than enough in practice
1311 
1312  if (scaleOnly)
1313  sprintf(emuString, "%s=\"%ld\" %s=\"%ld\"",
1314  x_name.c_str(), bp2emu(x_bp),
1315  y_name.c_str(), bp2emu(y_bp));
1316  else
1317  sprintf(emuString, "%s=\"%ld\" %s=\"%ld\"",
1318  x_name.c_str(), xtrans(x_bp) + xshift_emu,
1319  y_name.c_str(), ytrans(y_bp) + yshift_emu);
1320  return emuString;
1321 }
1322 
1323 
1324 
1325 // Compute the centroid of a polygon. Note that we might not have
1326 // been given a polygon, but we'll at least return *something*.
1328 {
1329  // We start by finding a cycle of knots.
1330  const unsigned int numElts = numberOfElementsInPath();
1331  std::unique_ptr<Point[]> allKnots ( new Point[numElts + 1]);
1332  unsigned int numKnots = 0;
1333  unsigned int movetos = 0;
1334  for (unsigned int n = 0; n < numElts; n++) {
1335  const basedrawingelement & elem = pathElement(n);
1336  if (elem.getType() == moveto)
1337  movetos++;
1338  if (elem.getNrOfPoints() == 0)
1339  continue;
1340  allKnots[numKnots++] = elem.getPoint(elem.getNrOfPoints() - 1);
1341  }
1342  if (allKnots[numKnots - 1] == allKnots[0])
1343  numKnots--;
1344  else
1345  allKnots[numKnots] = allKnots[0];
1346 
1347  // Otherwise, we compute the area bounded by the knots.
1348  float area = 0.0f;
1349  for (unsigned int n = 0; n < numKnots; n++)
1350  area += allKnots[n].x_*allKnots[n+1].y_ - allKnots[n+1].x_*allKnots[n].y_;
1351  area /= 2.0f;
1352 
1353  Point result;
1354  // If we were given a disjoint path or the area is zero, simply
1355  // average all of the knot coordinates and return that.
1356  if ((numKnots > 0) && (movetos > 1 || area == 0.0f)) {
1357  Point centroid;
1358  for (unsigned int n = 0; n < numKnots; n++)
1359  centroid += allKnots[n];
1360  centroid.x_ /= numKnots;
1361  centroid.y_ /= numKnots;
1362  result = centroid;
1363  } else if (area > 0.0f) {
1364 
1365  // Finally, we compute the centroid of the polygon.
1366  Point p;
1367  for (unsigned int n = 0; n < numKnots; n++) {
1368  const float partial = allKnots[n].x_*allKnots[n+1].y_ - allKnots[n+1].x_*allKnots[n].y_;
1369  p.x_ += (allKnots[n].x_ + allKnots[n+1].x_)*partial;
1370  p.y_ += (allKnots[n].y_ + allKnots[n+1].y_)*partial;
1371  }
1372  p.x_ /= area*6.0f;
1373  p.y_ /= area*6.0f;
1374  result = p;
1375  }
1376  return result;
1377 }
1378 
1379 // Rotate point PT by ANGLE degrees around point PIVOT.
1380 Point drvPPTX::rotate_pt_around (const Point & pt, float angle, const Point & pivot)
1381 {
1382  Point shiftedPt = pt;
1383  shiftedPt.x_ -= pivot.x_; // Shift the pivot to the origin.
1384  shiftedPt.y_ -= pivot.y_;
1385  const float angle_rad = angle * (float)M_PI / 180.0f;
1386  const Point rotatedPt(shiftedPt.x_*cosf(angle_rad) - shiftedPt.y_*sinf(angle_rad), // Rotate the shifted point.
1387  shiftedPt.x_*sinf(angle_rad) + shiftedPt.y_*cosf(angle_rad));
1388  return rotatedPt + pivot; // Shift back to the original pivot.
1389 }
1390 
1391 void drvPPTX::print_connections(const BBox & pathBBox)
1392 {
1393  // Output shape connection sites (knots and centroid).
1394  const Point centroid = pathCentroid();
1395  const long int xshift_emu = -xtrans(pathBBox.ll.x_);
1396  const long int yshift_emu = -ytrans(pathBBox.ur.y_);
1397  slidef << " <a:cxnLst>\n"
1398  << " <a:cxn ang=\"0\">\n"
1399  << " <a:pos " << pt2emu(centroid.x_, centroid.y_,
1400  xshift_emu, yshift_emu) << "/>\n"
1401  << " </a:cxn>\n";
1402  for (unsigned int n = 0; n < numberOfElementsInPath(); n++) {
1403  const basedrawingelement & elem = pathElement(n);
1404  if (elem.getNrOfPoints() == 0)
1405  continue;
1406  const Point & p = elem.getPoint(elem.getNrOfPoints() - 1);
1407  const float angle = atan2f(centroid.y_ - p.y_, p.x_ - centroid.x_);
1408  slidef << " <a:cxn ang=\"" << angle*60000.0*180.0/M_PI << "\">\n"
1409  << " <a:pos " << pt2emu(p.x_, p.y_,
1410  xshift_emu, yshift_emu) << "/>\n"
1411  << " </a:cxn>\n";
1412  }
1413  slidef << " </a:cxnLst>\n";
1414 }
1415 
1416 void drvPPTX::print_color(int baseIndent, float redF, float greenF, float blueF)
1417 {
1418  // Output an OOXML solid color.
1419  string indentStr = string(baseIndent, ' ');
1420  const unsigned int red = (unsigned int)lroundf(redF * 255);
1421  const unsigned int green = (unsigned int)lroundf(greenF * 255);
1422  const unsigned int blue = (unsigned int)lroundf(blueF * 255);
1423  const unsigned int rgb = blue + 256*(green + 256*red);
1424  slidef << indentStr << "<a:solidFill>\n";
1425  switch (color_type) {
1426  case C_ORIGINAL:
1427  // With -colors=original, output the color exactly as specified.
1428  slidef << indentStr << " <a:srgbClr val=\""
1429  << hex << setw(6) << setfill('0') << rgb << std::dec << "\"/>\n";
1430  break;
1431 
1432  case C_THEME:
1433  case C_THEME_PURE:
1434  // With -colors=theme, randomly select a theme color.
1435  if (rgb == 0)
1436  // Black -> dark 1
1437  slidef << indentStr << " <a:schemeClr val=\"dk1\"/>\n";
1438  else if (rgb == 0xffffff)
1439  // White -> light 1
1440  slidef << indentStr << " <a:schemeClr val=\"lt1\"/>\n";
1441  else {
1442  // Randomly select a theme color, but remember it for next time.
1443  const ThemeColor * colorInfo = rgb2theme.getValue(rgb);
1444  ThemeColor newColorInfo;
1445  if (colorInfo == nullptr) {
1446  // This is the first time we've seen this RGB color.
1447  static const char *colorList[] = {
1448  "dk2", "lt2", "accent1", "accent2", "accent3",
1449  "accent4", "accent5", "accent6"
1450  };
1451  newColorInfo.name = colorList[random() % (sizeof(colorList)/sizeof(colorList[0]))];
1452  if (color_type == C_THEME) {
1453  // Randomly alter the luminosity with the constraint that
1454  // light colors map to light colors and dark colors map to
1455  // dark colors.
1456  const float origLum = sqrtf(0.241f*redF*redF + 0.691f*greenF*greenF + 0.068f*blueF*blueF);
1457  if (origLum >= 0.5)
1458  newColorInfo.lum = 50000 + random()%40000; // Map to [50%, 90%].
1459  else
1460  newColorInfo.lum = 30000 + random()%20000; // Map to [30%, 50%].
1461  }
1462  rgb2theme.insert(rgb, newColorInfo);
1463  colorInfo = &newColorInfo;
1464  }
1465 
1466  // Output the, possibly altered, theme color.
1467  if (colorInfo->lum == ~0U)
1468  slidef << indentStr << " <a:schemeClr val=\"" << colorInfo->name << "\"/>\n";
1469  else {
1470  slidef << indentStr << " <a:schemeClr val=\"" << colorInfo->name << "\">\n"
1471  << indentStr << " <a:lum val=\"" << colorInfo->lum << "\"/>\n"
1472  << indentStr << " </a:schemeClr>\n";
1473  }
1474  }
1475  break;
1476 
1477  default:
1478  errorMessage("ERROR: Unexpected color type");
1479  abort();
1480  break;
1481  }
1482  slidef << indentStr << "</a:solidFill>\n";
1483 }
1484 
1486 {
1487  // Output the current line join in OOXML format.
1488  switch (currentLineJoin()) {
1489  case 0:
1490  slidef << " <a:miter/>\n";
1491  break;
1492  case 1:
1493  slidef << " <a:round/>\n";
1494  break;
1495  case 2:
1496  slidef << " <a:bevel/>\n";
1497  break;
1498  default:
1499  errorMessage("ERROR: unknown linejoin");
1500  abort();
1501  break;
1502  }
1503 }
1504 
1506 {
1507  // Parse a PostScript dash pattern.
1508  istringstream dashStr(dashPattern());
1509  float *pattern = new float[2*string(dashPattern()).length()]; // Very generous allocation but expected to be short
1510  size_t patternLen = 0; // Number of floats in the above
1511  string oneToken;
1512  dashStr >> oneToken; // "["
1513  while (dashStr) {
1514  // Read floats until we reach the "]". Ignore that and anything
1515  // that follows it.
1516  dashStr >> pattern[patternLen];
1517  if (dashStr.fail())
1518  break;
1519  patternLen++;
1520  }
1521 
1522  // Output an OOXML custom dash.
1523  if (patternLen > 0) {
1524  // Repeat odd patterns to make them even.
1525  size_t p;
1526  if (patternLen % 2 == 1) {
1527  for (p = 0; p < patternLen; p++)
1528  pattern[p + patternLen] = pattern[p];
1529  patternLen *= 2;
1530  }
1531 
1532  // Output {dash, space} pairs.
1533  const float lineWidth = currentLineWidth();
1534  slidef << " <a:custDash>\n";
1535  for (p = 0; p < patternLen; p += 2)
1536  slidef << " <a:ds d=\"" << 100000.0*pattern[p]/lineWidth
1537  << "\" sp=\"" << 100000.0*pattern[p+1]/lineWidth << "\"/>\n";
1538  slidef << " </a:custDash>\n";
1539  }
1540  delete[] pattern;
1541 }
1542 
1543 void drvPPTX::show_path()
1544 {
1545  // Output the non-visible shape properties.
1546  slidef << " <p:sp>\n"
1547  << " <p:nvSpPr>\n"
1548  << " <p:cNvPr id=\"" << next_id << "\" name=\"pstoedit " << next_id << "\"/>\n"
1549  << " <p:cNvSpPr/>\n"
1550  << " <p:nvPr/>\n"
1551  << " </p:nvSpPr>\n";
1552  next_id++;
1553 
1554  // Compute the path's bounding box. This might not be perfectly
1555  // tight due to the way we process curves, but that's not a show
1556  // stopper.
1557  BBox pathBBox;
1558  pathBBox.ll.x_ = FLT_MAX;
1559  pathBBox.ll.y_ = FLT_MAX;
1560  pathBBox.ur.x_ = -FLT_MAX;
1561  pathBBox.ur.y_ = -FLT_MAX;
1562  Point prevPoint;
1563  for (unsigned int e = 0; e < numberOfElementsInPath(); e++) {
1564  // Non-curves are handled by considering each knot in the
1565  // bounding-box calcuation.
1566  const basedrawingelement & elem = pathElement(e);
1567  const unsigned int numPoints = elem.getNrOfPoints();
1568  if (elem.getType() != curveto)
1569  for (unsigned int p = 0; p < numPoints; p++) {
1570  const Point thisPt = elem.getPoint(p);
1571  pathBBox.ll.x_ = std::min(pathBBox.ll.x_, thisPt.x_);
1572  pathBBox.ll.y_ = std::min(pathBBox.ll.y_, thisPt.y_);
1573  pathBBox.ur.x_ = std::max(pathBBox.ur.x_, thisPt.x_);
1574  pathBBox.ur.y_ = std::max(pathBBox.ur.y_, thisPt.y_);
1575  }
1576 
1577  // Rather than attempt to compute the true bounding box of a
1578  // curve, we simply sample a large number of evenly spaced points
1579  // along the curve for our bounding-box calcuation. This is a lot
1580  // easier and probably works in virtually all cases.
1581  if (elem.getType() == curveto) {
1582  const float numSamples = 100.0f;
1583  for (float t = 0.0f; t <= 1.0f; t += 1.0f/numSamples) {
1584  const Point bPoint = PointOnBezier(t, prevPoint, elem.getPoint(0),
1585  elem.getPoint(1), elem.getPoint(2));
1586  pathBBox.ll.x_ = std::min(pathBBox.ll.x_, bPoint.x_);
1587  pathBBox.ll.y_ = std::min(pathBBox.ll.y_, bPoint.y_);
1588  pathBBox.ur.x_ = std::max(pathBBox.ur.x_, bPoint.x_);
1589  pathBBox.ur.y_ = std::max(pathBBox.ur.y_, bPoint.y_);
1590  }
1591  }
1592 
1593  // Keep track of the current point.
1594  if (numPoints > 0)
1595  prevPoint = elem.getPoint(numPoints - 1);
1596  }
1597 
1598  // Output the 2-D transform for the graphic frame (i.e., the shape's
1599  // offset and size).
1600  slidef << " <p:spPr>\n"
1601  << " <a:xfrm>\n";
1602  slidef << " <a:off " << pt2emu(pathBBox.ll.x_, pathBBox.ur.y_) << "/>\n";
1603  slidef << " <a:ext " << pt2emu(pathBBox.ur.x_ - pathBBox.ll.x_,
1604  pathBBox.ur.y_ - pathBBox.ll.y_,
1605  0, 0, "cx", "cy", true) << "/>\n"
1606  << " </a:xfrm>\n";
1607 
1608  // For the user's convenience, make each knot a connection site, and
1609  // specify that any text the user adds should fill the shape's bounding box.
1610  slidef << " <a:custGeom>\n";
1611  print_connections(pathBBox);
1612  slidef << " <a:rect l=\"l\" t=\"t\" r=\"r\" b=\"b\"/>\n";
1613 
1614  // Define the coordinate system for the shape within its frame.
1615  slidef << " <a:pathLst>\n"
1616  << " <a:path " << pt2emu(pathBBox.ur.x_ - pathBBox.ll.x_,
1617  pathBBox.ur.y_ - pathBBox.ll.y_,
1618  0, 0, "w", "h", true) << ">\n";
1619  // Output all of the shape's lines and curves.
1620  print_coords(pathBBox);
1621  slidef << " </a:path>\n"
1622  << " </a:pathLst>\n"
1623  << " </a:custGeom>\n";
1624 
1625  // Output a stroke and/or fill.
1627  // Filled region
1628  print_color(10, fillR(), fillG(), fillB());
1630  // Stroked line
1631  slidef << " <a:ln w=\"" << currentLineWidth()*12700.0
1632  << "\" cap=\"";
1633  switch (currentLineCap()) {
1634  case 0:
1635  slidef << "flat";
1636  break;
1637  case 1:
1638  slidef << "rnd";
1639  break;
1640  case 2:
1641  slidef << "sq";
1642  break;
1643  default:
1644  errorMessage("ERROR: unknown linecap");
1645  abort();
1646  break;
1647  }
1648  slidef << "\">\n";
1649  print_color(12, edgeR(), edgeG(), edgeB());
1650  print_dash();
1651  print_join();
1652  slidef << " </a:ln>\n";
1653  }
1654 
1655  // Indicate that if the shape is to be contain a text box, then the
1656  // text box should be middle-centered within the shape, and the text
1657  // itself should be centered horizontally within the text box.
1658  slidef << " </p:spPr>\n"
1659  << " <p:txBody>\n"
1660  << " <a:bodyPr wrap=\"none\" lIns=\"0\" tIns=\"0\" rIns=\"0\" bIns=\"0\" rtlCol=\"0\" anchor=\"ctr\" anchorCtr=\"1\"/>\n"
1661  << " <a:lstStyle/>\n"
1662  << " <a:p>\n"
1663  << " <a:pPr algn=\"ctr\"/>\n"
1664  << " <a:endParaRPr dirty=\"1\"/>\n"
1665  << " </a:p>\n"
1666  << " </p:txBody>\n"
1667  << " </p:sp>\n";
1668 }
1669 
1670 void drvPPTX::show_rectangle(const float llx, const float lly, const float urx, const float ury)
1671 {
1672  // We could probably generate a rectangle preset if we felt like it.
1673  unused(&llx); unused(&lly); unused(&urx); unused(&ury); // avoid the compiler warning
1674  show_path();
1675 }
1676 
1677 void drvPPTX::show_image(const PSImage & imageinfo)
1678 {
1679  // One might think that imageinfo.imageMatrix, which the PostScript
1680  // Language Reference Manual suggests using to map the unit square
1681  // to the bounds of the source image, would be useful here.
1682  // However, experiments suggest that
1683  // imageinfo.normalizedImageCurrentMatrix in fact provides all the
1684  // information we need to place an image on a slide.
1685  const float * ctm = imageinfo.normalizedImageCurrentMatrix;
1686 
1687  // Determine the image's orientation, scale, rotation, and position
1688  // on the slide.
1689  // http://stackoverflow.com/questions/4361242/extract-rotation-scale-values-from-2d-transformation-matrix
1690  // and
1691  // http://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix
1692  // were helpful here.
1693  const bool flipH = ctm[0] < 0;
1694  const bool flipV = ctm[3] > 0; // Reversed sense because we're already flipping the coordinate system
1695  const float xscale = pythagoras(ctm[0], ctm[2]);
1696  const float yscale = pythagoras(ctm[1], ctm[3]);
1697  float angle = atan2f(ctm[2], ctm[0]) * (float)(180.0f/M_PI);
1698  if (flipH)
1699  angle = 180.0f - angle;
1700  if (flipV)
1701  angle = -angle;
1702  const long int angle_int = lroundf(-60000.0f*angle);
1703 
1704  // DrawingML rotates the image *after* the location for the image's
1705  // upper-left corner is specified. Hence, we determine the image's
1706  // unrotated upper-left corner by applying the CTM to the image's
1707  // center and taking an offset from that.
1708  const Point center_orig(imageinfo.width/2.0f, imageinfo.height/2.0f);
1709  const Point center_xform = center_orig.transform(ctm);
1710  const Point ofs = center_xform + Point(-xscale*imageinfo.width/2.0f, yscale*imageinfo.height/2.0f);
1711 
1712  // Place the image on the slide.
1713  total_images++;
1714  page_images++;
1715  slidef << " <p:pic>\n"
1716  << " <p:nvPicPr>\n"
1717  << " <p:cNvPr id=\"" << next_id << "\" name=\"pstoedit " << next_id << "\"/>\n"
1718  << " <p:cNvPicPr/>\n"
1719  << " <p:nvPr/>\n"
1720  << " </p:nvPicPr>\n";
1721  next_id++;
1722  slidef << " <p:blipFill>\n"
1723  << " <a:blip r:embed=\"rId" << page_images + 1 << "\"/>\n"
1724  << " <a:srcRect/>\n"
1725  << " <a:stretch>\n"
1726  << " <a:fillRect/>\n"
1727  << " </a:stretch>\n"
1728  << " </p:blipFill>\n";
1729  slidef << " <p:spPr bwMode=\"auto\">\n"
1730  << " <a:xfrm";
1731  if (angle_int != 0)
1732  slidef << " rot=\"" << angle_int << '"';
1733  if (flipH)
1734  slidef << " flipH=\"1\"";
1735  if (flipV)
1736  slidef << " flipV=\"1\"";
1737  const float cx = imageinfo.width*xscale;
1738  const float cy = imageinfo.height*yscale;
1739  slidef << ">\n"
1740  << " <a:off " << pt2emu(ofs.x_, ofs.y_) << "/>\n";
1741  slidef << " <a:ext " << pt2emu(cx, cy, 0, 0, "cx", "cy", true) << "/>\n"
1742  << " </a:xfrm>\n"
1743  << " <a:prstGeom prst=\"rect\"/>\n"
1744  << " <a:noFill/>\n"
1745  << " </p:spPr>\n"
1746  << " </p:pic>\n";
1747 
1748  // Embed the image in the PPTX file.
1749  struct zip_source *img_file = zip_source_file(outzip, imageinfo.FileName.c_str(), 0, -1);
1750  if (img_file == nullptr) {
1751  RSString errmessage("ERROR: Failed to embed image file ");
1752  errmessage += imageinfo.FileName;
1753  errmessage += " (";
1754  errmessage += zip_strerror(outzip);
1755  errmessage += ")";
1756  errorMessage(errmessage.c_str());
1757  abort();
1758  }
1759  ostringstream img_filename;
1760  img_filename << "ppt/media/image" << total_images << ".png";
1761  if (zip_add(outzip, img_filename.str().c_str(), img_file) == -1) {
1762  RSString errmessage("ERROR: Failed to embed image file ");
1763  errmessage += imageinfo.FileName;
1764  errmessage +=" as ";
1765  errmessage +=img_filename.str().c_str();
1766  errmessage += " (";
1767  errmessage +=zip_strerror(outzip);
1768  errmessage +=")";
1769  errorMessage(errmessage.c_str());
1770  abort();
1771  }
1772 }
1773 
1775 D_pptx("pptx",
1776  "PresentationML (PowerPoint) format",
1777  "This is the format used internally by Microsoft PowerPoint. LibreOffice can also read/write PowerPoint files albeit with some lack of functionality.",
1778  "pptx",
1779  true, // backend supports subpaths
1780  // if subpaths are supported, the backend must deal with
1781  // sequences of the following form
1782  // moveto (start of subpath)
1783  // lineto (a line segment)
1784  // lineto
1785  // moveto (start of a new subpath)
1786  // lineto (a line segment)
1787  // lineto
1788  //
1789  // If this argument is set to false each subpath is drawn
1790  // individually which might not necessarily represent
1791  // the original drawing.
1792  true, // backend supports curves
1793  true, // backend supports elements which are filled and have edges
1794  true, // backend supports text
1795  DriverDescription::png, // support for PNG images
1796  DriverDescription::noopen, // we create the output file ourself
1797  true, // if format supports multiple pages in one file
1798  false // clipping
1799  );
1800 
1801 #endif
__inline float __cdecl atan2f(float _Y, float _X)
Definition: CPAL.d:18913
__inline float __cdecl cosf(float _X)
Definition: CPAL.d:18928
__inline float __cdecl sinf(float _X)
Definition: CPAL.d:19029
__inline float __cdecl fmodf(float _X, float _Y)
Definition: CPAL.d:18970
__inline float __cdecl sqrtf(float _X)
Definition: CPAL.d:19039
double __cdecl acos(double _X)
long __cdecl lroundf(float _X)
struct WordList Italic
Definition: Resource.c:39
#define strdup
Definition: Utility.h:167
bp
Definition: action.c:1035
cp
Definition: action.c:1035
double ury
Definition: aftopl.c:56
double urx
Definition: aftopl.c:55
double lly
Definition: aftopl.c:54
double llx
Definition: aftopl.c:53
static String typeface
Definition: automatic.cc:66
#define blue
Definition: backend_eps.c:37
#define green
Definition: backend_eps.c:36
#define red
Definition: backend_eps.c:35
static void dec(struct edge *e, int h)
const T::V_Type * getValue(const T::K_Type &key) const
Definition: miscutil.h:267
void insert(const T::K_Type &key, const T::V_Type &value)
Definition: miscutil.h:264
RSString FileName
Definition: psimage.h:46
float normalizedImageCurrentMatrix[6]
Definition: psimage.h:41
unsigned int width
Definition: psimage.h:37
unsigned int height
Definition: psimage.h:36
void assign(const char *src, const size_t len)
Definition: miscutil.cpp:752
size_t length() const
Definition: miscutil.h:129
const char * c_str() const
Definition: miscutil.h:127
virtual const Point & getPoint(unsigned int i) const =0
virtual Dtype getType() const =0
virtual unsigned int getNrOfPoints() const =0
unsigned int lum
Definition: drvpptx.h:140
unsigned long page_images
Definition: drvpptx.h:75
BBox slideBBox
Definition: drvpptx.h:72
long int xtrans(float x_bp) const
Definition: drvpptx.h:111
Point pathCentroid()
Definition: drvpptx.cpp:1327
Mapper< FontMapping > ps2win
Definition: drvpptx.h:152
void create_pptx_file(const char *relname, const char *contents)
Definition: drvpptx.cpp:714
@ C_THEME_PURE
Definition: drvpptx.h:102
@ C_THEME
Definition: drvpptx.h:102
@ C_ORIGINAL
Definition: drvpptx.h:102
static const char *const xml_slideMaster1_xml
Definition: drvpptx.h:83
void show_rectangle(const float llx, const float lly, const float urx, const float ury) override
Definition: drvpptx.cpp:1670
void show_image(const PSImage &imageinfo) override
Definition: drvpptx.cpp:1677
static Point rotate_pt_around(const Point &pt, float angle, const Point &pivot)
Definition: drvpptx.cpp:1380
static const char *const xml_theme1_xml
Definition: drvpptx.h:85
Mapper< RGB2Theme > rgb2theme
Definition: drvpptx.h:149
std::ostringstream slidef
Definition: drvpptx.h:71
struct zip * outzip
Definition: drvpptx.h:70
~drvPPTX() override
Definition: drvpptx.cpp:541
void print_coords(const BBox &pathBBox)
Definition: drvpptx.cpp:774
Point center_offset
Definition: drvpptx.h:73
static const char *const xml_rels
Definition: drvpptx.h:80
enum drvPPTX::@1585 font_type
static void parse_xform_matrix(const float *origMatrix, bool *mirrored, float *xscale, float *yscale, float *rotation, float *x_trans, float *y_trans)
Definition: drvpptx.cpp:1123
void print_join()
Definition: drvpptx.cpp:1485
void print_color(int baseIndent, float redF, float greenF, float blueF)
Definition: drvpptx.cpp:1416
void create_pptx()
Definition: drvpptx.cpp:743
@ F_NATIVE
Definition: drvpptx.h:103
@ F_THEME
Definition: drvpptx.h:103
@ F_WINDOWS
Definition: drvpptx.h:103
void print_connections(const BBox &pathBBox)
Definition: drvpptx.cpp:1391
static unsigned char panose2pitch(const unsigned int *panose_vals)
Definition: drvpptx.cpp:901
static const char *const xml_slideLayout1_xml
Definition: drvpptx.h:81
unsigned long total_images
Definition: drvpptx.h:76
void get_font_props(const TextInfo &textinfo, RSString *typeface, RSString *panose, bool *isBold, bool *isItalic, unsigned char *pitchFamily) const
Definition: drvpptx.cpp:923
void print_dash()
Definition: drvpptx.cpp:1505
void show_text(const TextInfo &textinfo) override
Definition: drvpptx.cpp:1156
void eot2texinfo(const string &eotfilename, TextInfo &textinfo)
Definition: drvpptx.cpp:1023
static long int bp2emu(float bp)
Definition: drvpptx.cpp:1294
unsigned long next_id
Definition: drvpptx.h:74
static const char *const xml_slideMaster1_xml_rels
Definition: drvpptx.h:84
std::set< std::string > eotlist
Definition: drvpptx.h:77
static const char *const xml_slideLayout1_xml_rels
Definition: drvpptx.h:82
const char * pt2emu(float x_bp, float y_bp, long int xshift_emu=0, long int yshift_emu=0, RSString x_name="x", RSString y_name="y", bool scaleOnly=false) const
Definition: drvpptx.cpp:1299
enum drvPPTX::@1584 color_type
long int ytrans(float y_bp) const
Definition: drvpptx.h:119
static float angle_between(Point first, Point second)
Definition: drvpptx.cpp:1101
bool pathWasMerged() const
Definition: drvbase.h:640
float edgeG() const
Definition: drvbase.h:644
static unsigned int & totalNumberOfPages()
Definition: drvbase.cpp:1676
unsigned int currentPageNumber
Definition: drvbase.h:374
const basedrawingelement & pathElement(unsigned int index) const
Definition: drvbase.cpp:407
showtype currentShowType() const
Definition: drvbase.h:633
const RSString outFileName
Definition: drvbase.h:362
ostream & errf
Definition: drvbase.h:360
float edgeB() const
Definition: drvbase.h:645
const char * dashPattern() const
Definition: drvbase.h:650
float fillG() const
Definition: drvbase.h:647
float edgeR() const
Definition: drvbase.h:643
unsigned int currentLineCap() const
Definition: drvbase.h:635
const RSString inFileName
Definition: drvbase.h:361
float fillR() const
Definition: drvbase.h:646
unsigned int currentLineJoin() const
Definition: drvbase.h:636
const BBox & getCurrentBBox() const
Definition: drvbase.cpp:315
virtual void open_page()=0
float currentLineWidth() const
Definition: drvbase.h:641
@ fill
Definition: drvbase.h:162
@ stroke
Definition: drvbase.h:162
@ eofill
Definition: drvbase.h:162
virtual void close_page()=0
virtual void show_path()=0
float fillB() const
Definition: drvbase.h:648
unsigned int & numberOfElementsInPath()
Definition: drvbase.h:513
#define n
Definition: t4ht.c:1290
@ closepath
Definition: cscommands.h:4
#define free(a)
Definition: decNumber.cpp:310
@ lineto
Definition: drvbase.h:749
@ moveto
Definition: drvbase.h:749
@ curveto
Definition: drvbase.h:749
#define constructBase
Definition: drvbase.h:884
Point PointOnBezier(float t, const Point &p1, const Point &p2, const Point &p3, const Point &p4)
Definition: drvbase.h:1183
#define derivedConstructor(Class)
Definition: drvbase.h:871
static DriverDescriptionT< drvPPTX > D_pptx("pptx", "PresentationML (PowerPoint) format", "This is the format used internally by Microsoft PowerPoint. LibreOffice can also read/write PowerPoint files albeit with some lack of functionality.", "pptx", true, true, true, true, DriverDescription::png, DriverDescription::noopen, true, false)
#define hex(c)
Definition: kanji.c:63
int sscanf()
char pattern[8]
Definition: gemtopnm.c:50
mpz_t * f
Definition: gen-fib.c:34
#define c(n)
Definition: gpos-common.c:150
int rotation
Definition: hbf2gf.c:334
char comment[255+1]
Definition: hbf2gf.c:350
static Bool fileExists(Char *name)
Definition: bzip2.c:940
small capitals from c petite p
Definition: afcover.h:72
small capitals from c petite p scientific i
Definition: afcover.h:80
#define M_PI
Definition: gd.h:236
int int cy
Definition: gdfx.h:13
int cx
Definition: gdfx.h:12
int errno
#define unlink(file)
Definition: config.h:413
#define FLT_MAX
Definition: c-minmax.h:81
#define getpid
Definition: win32lib.h:76
#define floor(x)
Definition: cpascal.h:52
#define string
Definition: ctangleboot.c:111
#define unused
Definition: main.c:29
void abort()
#define sprintf
Definition: snprintf.c:44
hb_script_t Script
Definition: luaharfbuzz.h:18
static int zip_open(lua_State *L)
Definition: luazip.c:85
static int zip_close(lua_State *L)
Definition: luazip.c:101
void errorMessage(const char *errortext)
Definition: miscutil.cpp:603
float pythagoras(const float x, const float y)
Definition: miscutil.h:335
bool string_contains(const RSString &s, const RSString &substr)
Definition: miscutil.h:186
angle
Definition: cordic.py:17
#define min(a, b)
Definition: pbmplus.h:223
#define max(a, b)
Definition: pbmto4425.c:11
char * familyname
Definition: pbmtopk.c:65
static char reason[256]
Definition: pdfsig.cc:141
float ** matrix()
double scale
Definition: pnmhistmap.c:38
static int32_t first
Definition: ppagelist.c:29
static double yscale
Definition: ppmtopjxl.c:47
static double xscale
Definition: ppmtopjxl.c:46
time_t time()
hb_script_t
Definition: hb-common.h:448
le_int32 fixed
Definition: sfnt.h:144
int ofs
Definition: buildvm_libbc.h:45
#define img_filename(N)
Definition: image.h:166
#define img_file(N)
Definition: image.h:172
#define direction(p, c)
Definition: sh12.c:109
ShellFileEnvironment e
Definition: sh6.c:388
Definition: drvbase.h:119
Point ur
Definition: drvbase.h:122
Point ll
Definition: drvbase.h:121
float y_
Definition: drvbase.h:91
Point transform(const float matrix[6]) const
Definition: drvbase.cpp:1572
float x_
Definition: drvbase.h:89
int length() const
Return the string's length.
Definition: string.hh:153
void assign(const String &x) const
Definition: string.hh:696
RSString currentFontName
Definition: drvbase.h:176
float FontMatrix[6]
Definition: drvbase.h:169
RSString currentFontFullName
Definition: drvbase.h:179
RSString currentFontFamilyName
Definition: drvbase.h:178
RSString thetext
Definition: drvbase.h:173
float currentFontSize
Definition: drvbase.h:181
float y() const
Definition: drvbase.h:168
float x() const
Definition: drvbase.h:167
float x_end() const
Definition: drvbase.h:171
float y_end() const
Definition: drvbase.h:172
Definition: jpegtopnm.c:237
Definition: dvips.h:235
*job_name strlen((char *) job_name) - 4)
void getline(void)
second
Definition: tex4ht.c:4115
return() int(((double) *(font_tbl[cur_fnt].wtbl+(int)(*(font_tbl[cur_fnt].char_wi+(int)(ch - font_tbl[cur_fnt].char_f)% 256)))/(double)(1L<< 20)) *(double) font_tbl[cur_fnt].scale)
#define U(a)
Definition: twiddle.h:1
struct Point Point
PATTERN * pt
Definition: vlna.c:74
#define nullptr
Definition: xetex.h:75