From 0a588d044b721dfbe9cb560749b3cec0b3b8bb52 Mon Sep 17 00:00:00 2001 From: Ben Moores Date: Sun, 6 Apr 2008 01:26:14 +0000 Subject: [PATCH] Experimental pdf output using wxPdfDoc: - wxpdfdoc patches - mapnik patches - visual studio build instructions / project files see /msvc/readme.txt for build instructions The scons makefiles have not been updated, the new files are in /pdf, /include/pdf, /demo/pdf/, and /bindings/python --- .../mapnik_pdf_line_pattern_symbolizer.cpp | 42 + .../python/mapnik_pdf_point_symbolizer.cpp | 39 + .../mapnik_pdf_polygon_pattern_symbolizer.cpp | 38 + bindings/python/mapnik_pdf_renderer.cpp | 76 + .../python/mapnik_pdf_shield_symbolizer.cpp | 40 + bindings/python/mapnik_python.cpp | 13 + demo/pdf/images/a.PNG | Bin 0 -> 5210 bytes demo/pdf/images/b.PNG | Bin 0 -> 16241 bytes demo/pdf/images/c.pdf | 513 ++++ demo/pdf/main.cpp | 624 ++++ include/mapnik/attribute_collector.hpp | 6 + include/mapnik/image_data.hpp | 2 +- include/mapnik/octree.hpp | 11 +- include/mapnik/png_io.hpp | 4 +- include/mapnik/rule.hpp | 15 +- include/mapnik/symbolizer.hpp | 2 +- include/mapnik/unicode.hpp | 2 + include/pdf/font_engine_pdf.hpp | 172 ++ include/pdf/pdf_line_pattern_symbolizer.hpp | 105 + include/pdf/pdf_point_symbolizer.hpp | 89 + .../pdf/pdf_polygon_pattern_symbolizer.hpp | 89 + include/pdf/pdf_renderer.hpp | 249 ++ include/pdf/pdf_renderer_layout.hpp | 240 ++ include/pdf/pdf_renderer_utility.hpp | 50 + include/pdf/pdf_shield_symbolizer.hpp | 95 + msvc/agg/agg.vcproj | 758 +++++ msvc/binding_python/binding_python.vcproj | 356 +++ msvc/core/core.vcproj | 807 +++++ msvc/input_raster/input_raster.vcproj | 224 ++ msvc/input_shapefile/input_shapefile.vcproj | 262 ++ msvc/msvc.sln | 75 + msvc/pdf/pdf.vcproj | 200 ++ msvc/readme.txt | 281 ++ msvc/shapeindex/shapeindex.vcproj | 276 ++ pdf/font_engine_pdf.cpp | 200 ++ pdf/pdf_line_pattern_symbolizer.cpp | 73 + pdf/pdf_point_symbolizer.cpp | 80 + pdf/pdf_polygon_pattern_symbolizer.cpp | 69 + pdf/pdf_renderer.cpp | 1719 +++++++++++ pdf/pdf_renderer_layout.cpp | 111 + pdf/pdf_renderer_utility.cpp | 64 + pdf/pdf_shield_symbolizer.cpp | 76 + .../wxpdfdoc/include/wx/pdfdoc.h | 2630 +++++++++++++++++ .../wxpdfdoc/include/wx/pdfoc.h | 179 ++ .../wxpdfdoc/include/wx/pdfproperties.h | 882 ++++++ .../wxPdfDoc-patch/wxpdfdoc/src/pdfcolor.cpp | 574 ++++ .../wxPdfDoc-patch/wxpdfdoc/src/pdfdoc.cpp | 2306 +++++++++++++++ .../wxpdfdoc/src/pdfgraphics.cpp | 2243 ++++++++++++++ .../wxPdfDoc-patch/wxpdfdoc/src/pdfimage.cpp | 1221 ++++++++ .../wxPdfDoc-patch/wxpdfdoc/src/pdfkernel.cpp | 2472 ++++++++++++++++ .../wxPdfDoc-patch/wxpdfdoc/src/pdfoc.cpp | 122 + .../wxPdfDoc-patch/wxpdfdoc/src/pdfparser.cpp | 1893 ++++++++++++ .../wxpdfdoc_patch_20080112.patch | 1023 +++++++ 53 files changed, 23682 insertions(+), 10 deletions(-) create mode 100644 bindings/python/mapnik_pdf_line_pattern_symbolizer.cpp create mode 100644 bindings/python/mapnik_pdf_point_symbolizer.cpp create mode 100644 bindings/python/mapnik_pdf_polygon_pattern_symbolizer.cpp create mode 100644 bindings/python/mapnik_pdf_renderer.cpp create mode 100644 bindings/python/mapnik_pdf_shield_symbolizer.cpp create mode 100644 demo/pdf/images/a.PNG create mode 100644 demo/pdf/images/b.PNG create mode 100644 demo/pdf/images/c.pdf create mode 100644 demo/pdf/main.cpp create mode 100644 include/pdf/font_engine_pdf.hpp create mode 100644 include/pdf/pdf_line_pattern_symbolizer.hpp create mode 100644 include/pdf/pdf_point_symbolizer.hpp create mode 100644 include/pdf/pdf_polygon_pattern_symbolizer.hpp create mode 100644 include/pdf/pdf_renderer.hpp create mode 100644 include/pdf/pdf_renderer_layout.hpp create mode 100644 include/pdf/pdf_renderer_utility.hpp create mode 100644 include/pdf/pdf_shield_symbolizer.hpp create mode 100644 msvc/agg/agg.vcproj create mode 100644 msvc/binding_python/binding_python.vcproj create mode 100644 msvc/core/core.vcproj create mode 100644 msvc/input_raster/input_raster.vcproj create mode 100644 msvc/input_shapefile/input_shapefile.vcproj create mode 100644 msvc/msvc.sln create mode 100644 msvc/pdf/pdf.vcproj create mode 100644 msvc/readme.txt create mode 100644 msvc/shapeindex/shapeindex.vcproj create mode 100644 pdf/font_engine_pdf.cpp create mode 100644 pdf/pdf_line_pattern_symbolizer.cpp create mode 100644 pdf/pdf_point_symbolizer.cpp create mode 100644 pdf/pdf_polygon_pattern_symbolizer.cpp create mode 100644 pdf/pdf_renderer.cpp create mode 100644 pdf/pdf_renderer_layout.cpp create mode 100644 pdf/pdf_renderer_utility.cpp create mode 100644 pdf/pdf_shield_symbolizer.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfdoc.h create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfoc.h create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfproperties.h create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfcolor.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfdoc.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfgraphics.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfimage.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfkernel.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfoc.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfparser.cpp create mode 100644 thirdparty/wxPdfDoc-patch/wxpdfdoc_patch_20080112.patch diff --git a/bindings/python/mapnik_pdf_line_pattern_symbolizer.cpp b/bindings/python/mapnik_pdf_line_pattern_symbolizer.cpp new file mode 100644 index 000000000..bef429fe8 --- /dev/null +++ b/bindings/python/mapnik_pdf_line_pattern_symbolizer.cpp @@ -0,0 +1,42 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include +#include + +using mapnik::pdf_line_pattern_symbolizer; +using mapnik::stroke; + +void export_pdf_line_pattern_symbolizer() +{ + using namespace boost::python; + + class_>("PDFLinePatternSymbolizer", init("Create a new PDF Line Pattern Symbolizer")) + .def (init("Copies a PDF Line Pattern Symbolizer")) + .def (init("Creates a PDF Line Pattern Symbolizer from a Line Pattern Symbolizer")) + .def ("set_output_size", &mapnik::pdf_line_pattern_symbolizer::set_output_size, "Sets the output size of the tile in PDF units") + .def ("get_output_width", &mapnik::pdf_line_pattern_symbolizer::get_output_width, "Gets the output width of the tile in PDF units") + .def ("get_output_height", &mapnik::pdf_line_pattern_symbolizer::get_output_height, "Gets the output height of the tile in PDF units") + .add_property("stroke",make_function( &pdf_line_pattern_symbolizer::get_stroke, return_value_policy() ), &pdf_line_pattern_symbolizer::set_stroke ) + ; +} diff --git a/bindings/python/mapnik_pdf_point_symbolizer.cpp b/bindings/python/mapnik_pdf_point_symbolizer.cpp new file mode 100644 index 000000000..cdf32d4b6 --- /dev/null +++ b/bindings/python/mapnik_pdf_point_symbolizer.cpp @@ -0,0 +1,39 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include + +void export_pdf_point_symbolizer() +{ + using namespace boost::python; + using mapnik::pdf_point_symbolizer; + + class_>("PDFPointSymbolizer", init<>("Default PDF Point Symbolizer - 1x1 black square")) + .def (init("Create a new PDF Point Symbolizer")) + .def (init("Copies a PDF Point Symbolizer")) + .def (init("Creates a PDF point symbolizer from a point symbolizer")) + .def ("set_output_size", &mapnik::pdf_point_symbolizer::set_output_size, "Sets the output size of the symbol in PDF units") + .def ("get_output_width", &mapnik::pdf_point_symbolizer::get_output_width, "Gets the output width of the symbol in PDF units") + .def ("get_output_height", &mapnik::pdf_point_symbolizer::get_output_height, "Gets the output height of the symbol in PDF units") + ; +} diff --git a/bindings/python/mapnik_pdf_polygon_pattern_symbolizer.cpp b/bindings/python/mapnik_pdf_polygon_pattern_symbolizer.cpp new file mode 100644 index 000000000..193cf5a2f --- /dev/null +++ b/bindings/python/mapnik_pdf_polygon_pattern_symbolizer.cpp @@ -0,0 +1,38 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include + +void export_pdf_polygon_pattern_symbolizer() +{ + using namespace boost::python; + using mapnik::pdf_polygon_pattern_symbolizer; + + class_>("PDFPolygonPatternSymbolizer", init("Create a new PDF Polygon Pattern Symbolizer")) + .def (init("Copies a PDF Polygon Pattern Symbolizer")) + .def (init("Creates a PDF Polygon Pattern Symbolizer from a Polygon Pattern Symbolizer")) + .def ("set_output_size", &mapnik::pdf_polygon_pattern_symbolizer::set_output_size, "Sets the output size of the tile in PDF units") + .def ("get_output_width", &mapnik::pdf_polygon_pattern_symbolizer::get_output_width, "Gets the output width of the tile in PDF units") + .def ("get_output_height", &mapnik::pdf_polygon_pattern_symbolizer::get_output_height, "Gets the output height of the tile in PDF units") + ; +} diff --git a/bindings/python/mapnik_pdf_renderer.cpp b/bindings/python/mapnik_pdf_renderer.cpp new file mode 100644 index 000000000..c7ffee02a --- /dev/null +++ b/bindings/python/mapnik_pdf_renderer.cpp @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include +#include +#include + + +void export_pdf_renderer_layout() +{ + using namespace boost::python; + using mapnik::pdf_renderer_layout; + + class_("PDFRendererLayout", init("Default PDF Renderer Layout")) + .def ("set_font", &mapnik::pdf_renderer_layout::set_font, "Set the font to use for overlays") + .def ("set_background_colour", &mapnik::pdf_renderer_layout::set_background_colour, "set the page background colour") + .def ("set_map_area", &mapnik::pdf_renderer_layout::set_map_area, "Set the area of the page to render the map") + .def ("set_map_area_border", &mapnik::pdf_renderer_layout::set_map_area_border, "Set whether to draw a map border") + .def ("set_map_area_border_width", &mapnik::pdf_renderer_layout::set_map_area_border_width, "Set the map border width") + .def ("add_overlay_image", &mapnik::pdf_renderer_layout::add_overlay_image, "Add an overlay image") + .def ("set_map_grid", &mapnik::pdf_renderer_layout::set_map_grid, "Enable/Disable drawing map grid") + .def ("set_border_scales", &mapnik::pdf_renderer_layout::set_border_scales, "Enable/Disable drawing scales around edge of map") + .def ("set_map_grid_approx_spacing", &mapnik::pdf_renderer_layout::set_map_grid_approx_spacing, "Sets the approximate spacing of the grid lines") + .def ("set_border_scale_width", &mapnik::pdf_renderer_layout::set_border_scale_width, "Set width of scale bars around edge of map") + .def ("set_border_scale_linewidth", &mapnik::pdf_renderer_layout::set_border_scale_linewidth, "Set thickness of lines around the grid edge") + .def ("set_map_grid_colour", &mapnik::pdf_renderer_layout::set_map_grid_colour, "Set the colour of the grid across the map") + .def ("set_map_grid_linewidth", &mapnik::pdf_renderer_layout::set_map_grid_linewidth, "Set the map grid line width") + .def ("set_scale_bar", &mapnik::pdf_renderer_layout::set_scale_bar, "Enable/Disable the scale bar") + .def ("set_scale_bar_area", &mapnik::pdf_renderer_layout::set_scale_bar_area, "Set the scale bar size and location") + .def ("set_scale_bar_factor", &mapnik::pdf_renderer_layout::set_scale_bar_factor, "Sets the scale bar scale factor and label") + ; +} + + + +void render_to_pdf_1(const mapnik::Map& map, const mapnik::pdf_renderer_layout &layout, const std::string& filename) { + mapnik::render_to_pdf(map, layout, filename); +} + +void render_to_pdf_2(const mapnik::Map& map, const mapnik::pdf_renderer_layout &layout, const std::string& filename, const bool compress) { + mapnik::render_to_pdf(map, layout, filename, compress); +} + + + + +void export_pdf_renderer() +{ + + using namespace boost::python; + using mapnik::pdf_renderer; + + def("render_to_pdf",&render_to_pdf_1); + def("render_to_pdf",&render_to_pdf_2); + +} diff --git a/bindings/python/mapnik_pdf_shield_symbolizer.cpp b/bindings/python/mapnik_pdf_shield_symbolizer.cpp new file mode 100644 index 000000000..c1851984c --- /dev/null +++ b/bindings/python/mapnik_pdf_shield_symbolizer.cpp @@ -0,0 +1,40 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include + +void export_pdf_shield_symbolizer() +{ + using namespace boost::python; + using mapnik::pdf_shield_symbolizer; + using mapnik::text_symbolizer; + + class_< pdf_shield_symbolizer, bases >("PDFShieldSymbolizer", init< std::string const&, std::string const&, unsigned, mapnik::Color const&, std::string const&, std::string const&, unsigned, unsigned, double, double>("TODO")) + .def (init("Copies a PDF Shield Symbolizer")) + .def (init("Creates a PDF Shield Symbolizer from a Shield Symbolizer")) + .def ("set_output_size", &mapnik::pdf_shield_symbolizer::set_output_size, "Sets the output size of the shield in PDF units") + .def ("get_output_width", &mapnik::pdf_shield_symbolizer::get_output_width, "Gets the output width of the shield in PDF units") + .def ("get_output_height", &mapnik::pdf_shield_symbolizer::get_output_height, "Gets the output height of the shield in PDF units") + ; + +} diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 8785bea3e..38d11b84a 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -55,6 +55,12 @@ void export_text_symbolizer(); void export_shield_symbolizer(); void export_font_engine(); void export_projection(); +void export_pdf_point_symbolizer(); +void export_pdf_line_pattern_symbolizer(); +void export_pdf_polygon_pattern_symbolizer(); +void export_pdf_shield_symbolizer(); +void export_pdf_renderer_layout(); +void export_pdf_renderer(); #include #include @@ -178,6 +184,13 @@ BOOST_PYTHON_MODULE(_mapnik) export_projection(); export_coord(); export_map(); + export_pdf_point_symbolizer(); + export_pdf_line_pattern_symbolizer(); + export_pdf_polygon_pattern_symbolizer(); + export_pdf_shield_symbolizer(); + export_pdf_renderer_layout(); + export_pdf_renderer(); + def("render_to_file",&render_to_file1); def("render_to_file",&render_to_file2); diff --git a/demo/pdf/images/a.PNG b/demo/pdf/images/a.PNG new file mode 100644 index 0000000000000000000000000000000000000000..11586860427dd166be8f1feaf50b814aaa5f77c4 GIT binary patch literal 5210 zcmV-g6s7BlP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C33o|EK~#9!?45sXRc9H;KfSlRx3{*vP^tS%-z8CJg-rjr8dCz;^b8^4QP1E+C_dVx)&imte-sgQz$?WVb zMVX^e@fH+;6t#d#i?`t8x;nni>LIm26VRCTw;^UiD;D_P z1kei{2YQNKmU_j3XMoGV5Awjse19u&4)`O`Q3Rq+Ex_BrexSm}@om8GfrCJ}2*jB- z$|t;0L0L}$zXl>jAojEh_=U|@kxT1=pB8~wS2ge}Am&2ZUz6id5r_pHl+#+b^b}B6 z1X7q{Ka&jztW>u}R z>NUV~rnpY(rP0(Tn{GW&GiNYO0|P(`7)1DqF3L~Jg>!21V7&6wm(?y^3R|;h8F`xZ zs;px(N+EnjXMt{n??{DeG~bUJuf~urY4N@Z>@5&RzFEpf;6>nHz+nYtqG>RndmY+< zgb|+6%fKnwHMyam~2lx83kx`4C;mw=;kY|uGtxg_WKcC74S%|M3T1`?KT zfj?vhEDsbxcxK~ zgg4V9#eg?}9TtvxP1CuJy46Brh?TK6%v2cR@-EZ@KcQ(Xc`nmEss{2igJEPtIhAG8 zETj~$&*k$O1YXMgq7q1#H1hnQh-@j50=|1D`-ZtN)%{Agr$H5vgfz_jq*laQL?PNE zy#Xqf0v<%TfKUzOD0&@nsJ2)GS6V$r=a0Bi!*E;Ms<3M6LH*fLeuZIgX(oGa95 z{0^{0wwe=&kkNl+jSu~@OZq<0EL+9dd<8pozi+{vXbi0ig-X9XGc$d_#-USkTv0W) zP-1s~h+Hu|ityt4UYj@lglM#KP`XR{@WfSw8~@cBpP0*~+;WT?ddf@DK{!)o!ct_D*j3 z)SYftQ7@;%o&W){TJ|t{<6&)2b3ecQt2(OM=_L>lD`$_I3}exvI=?WAVua<5LI)f6BZ8)X-8S{h5WI`2f?t?k^o@*6t4 zsQJ`n5|E5~fMwEH+KaQje+&1)p;B(EyNhLw>xjp1HKDhRn+OC%4AXJ>>6BoUZR#m2 zi_=hl4~@5X5{*`xG?y?INVaSiJZ%0M~y8Kx0~fWF={FBpKX1NPwas=g3mrfhp;k1_-VO z{0O8kz(D%^2&5kv2_TFSETkGY2?z+MWYA>!noI(63?HneSWsGClW&2HAiTsuQMa9L z*t8^=2hg9V$}Q{M&K5c2fQ$mq1^~#j*bGEBF-`1`k(qIPs*Ca(2nvuiVhMQ6hcLzv z(KqQJ0Rge}yUAzc$&@^2N6Xgp`m%cNAJTReoiu#p_ugHXvzz`yh#j$}Z=FlYb9Edn z=)~Jwjfkk&ir9@?fmp80ybLRe$Yjvt;6%>=8xZLNV}`ek^C}9n1Ed9!+%i|fnF?+s z2kSuOT^t3TMg(SC4G%gGd=arr$+T_Q!-y11%65`j0aAh3fx6R1J++9o~;q9pynbnKn(Yn3CH5U~O0QMetW5Fo9H+`Dl_b%td(Rs(#?=9Afj*sb(E7D&!K zTaqSK{WnsJ{?1J4hpO&b74XeDe>*E5Fvo*>sDQ5KoI)7cdO~(#b_ddzfCg!^41Byr zyi2m-4F9|NPby*LQsxbAhjEzYAs>)axEtK|3>ITw=TSEz7u9OS#-ZNn@+;Gmb-g-3 z%+-@}Mi_RZ%i4)}$%xx}8Mp(7!Pyf{C;JhLLEZA_Vvh#<^|GbxQQboJYr23&)aGnF z>LHAbc4ISt@w^GldhQ=;lYOEt^#OMStpBaO7u!7cm#>4^+xMm+)8bxW9l$Ghyz01x z=$`AZK~1Ws9hjqg?OanXARU@dK8%_algcdLabxTE^^_Zo4RY2k%oP12t zQ|W8v0^-H~Kot=urseM%Qw8LT2PdzhPSXM~^=#3(LO;LRlzz+>+9Y<6Gxnh^F4W$ro98jefkZ(NmNK=Tn z<|I`D0pZ`*kp}jV3xn~)9zhvLye+}@x=1L`tjFyaVh!UUsH}jebdSa{r3bP0@95P? z`PM^@;a+Ix7$Ty?%~tpVVi9YXoPnuqiAjW;|BwW+K}Gq6Ea4tNILTC|$G9S!whp^t zLOxi!#zfbFAfK>{h_wc%E{+SbJ5g$UT5)lkEGwa;#}Uh6G z9g*ed5c%U+Q?l=VO!nnL+a={$mo-;|at5{!;bLw<1Vco);h5}~0H=z#xp4h|02#&V UfHm)LmH+?%07*qoM6N<$f=#g8oB#j- literal 0 HcmV?d00001 diff --git a/demo/pdf/images/b.PNG b/demo/pdf/images/b.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b989593380d5975a7290f151022ba95e8b8e40f7 GIT binary patch literal 16241 zcmV-%KaRkOP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-CG>%C`K~#9!Y`s~mZEJSl^&8{+X0z5_d!Oc>dv4uZw_Hui zWjnSjIE|B%V<$pFvGPD9l*A~CL??lS6oN%a1VY7q2#-iSAe1*mC*UCnB@d8*Lr_ox za!4fXikm7|UAyXb=iJlmW;Jup?;C@M@B7wTyQ=nGTH1G?)yz5OX#V3rnkhg2_kW)w z0zs?~z6yEM*~30P9P^_-tviq6$z+@L(|!14-#y#)pHBVF+#jq<9v6>BKstyJk%$CD zK>%>v@Pr#A$asbfCOD@b2XPeXkrm;J;Ss}QR!>>K#pRa>pNA)W;(X%;-}so9TXqZX z^pSPXgB9;w^7bW8?q9sN zy=x>O(w+qbg~f0P%A8p;=Et{$H~-Z)%9$tE<0HE#7elH%#2w6KF_}#l(`1s-Y+3~5 z$Mclq<Y);z5d*2RbB9EpNxLdmRW1WT`SK&|GpO>t*G#EO){3`EEX^d%$S_-#X%za%Iue8^ zRi?)u6@t)*dL9DXiR}d7ju9`r90U~?K?ssrnmu|BIPQ{NXG^^ru(%#_m@Q-v$;D7} zHN>mImTQ#3st9P$9-4d7;s^nxNe%FTXK>sRt@16OX^A|s+;QlT-{qb|+;fBPS4ovg_Ck zRbD|+sUGrp_yMAF(X;8y@26Io~?3+-y^ z33;6))giI|qM@3tCze~f*T@Bzk9m8A!u3oxqB1C*5&}{VBvVXd2}~Jt=ENZ}Ei8$v z6@6%ul9RlS_(zo(P(dUr1o3sBH3g9r#G`r?JLIrrO)mA+@SvDscrgp3oM#7Pi)&H^ z zMmo)SA~@^7Y&Gh4THRI@aFP?7W2#8iSTakls3IGe15xh-uJ)XaRLv@ai_ojPBN!-> zOAMV<7sFi2+k=?6WS36eklE%bbxsCBBRaOrUSyK+L|*V2S@7j7K1v1X6=w);O=LB% zz!GZB6-KnL*r%ic8j%hSC$|`+di0_YQRsyTBJ@J9^g<|b)EizkvU6DK72NcwVqSB@ zBdk7T=2YGilS%Jh;8>^bZWh!%3q@wUyEsJg80gegaQAg* zQ60z!-C4kTd;pL@7?4;qH8VV63wa{%vAIf(jN2I@VJB!>@*6MiEa6K76I#vCa(!j7 z6t5nX#n~qoqc`XX9Z_mRuJZafgcU z3wE82?hMv@^RZN6$)cJxC!uqM0<{PtLENN9xPsj}VF=y_57;VN7hD8){oufyj`E6x z4umk&cwZoUPUMNSW!aIpq&<16QnLl0u>~n^%Y>{LB=f~{s^QhWIg*I>#5Mf_3m73( zHMyS1#&#iPaAd%Rfs;MDV39nKt=gB)BtuH7XqeP%ERdTE6G0?qmqI7>j$4B#eXs5iGH6Nn7SS(jCi|w5y7i z0)%Q)ZbA-X0Rvyi$rAIK!P0ChtO#pZ(+9c$u%Fq@+#Z;h$~rvkA<#ARiZCdfNE+)4 z1PU@;YZfU*=oTCZ>b-iesH6f`GxE{F)8h4n zrG&^1Kzq6kVTA-d*OX;`&3warTg9h4@}3;X0+^e7R<$VBlV?kN&n$+WiA;phvl@{V zUC*Xt6+t*GWap5W3*~Tu4Xl-3DutoYDP!QGXU^3gvlX+d>2yJYd#ij_5!Rp{#8tdl zcMum5cY?VnWO0{lSW8$I^%QU?i1$hy3`yA!1(AI;t1Z$Xl{AqJaET7c1w?`|yh*xd zddcA>^9}P2>5k=&v_(4dmQ2FbEXLf6b(%xUJ2y+#K=1K_2i5}&TwU;J#irv0%!$RY zo60(5GVjuQNq~}=_ARQn|o446>%hBFz4#yApGoKUJvSH^fA~FybB)Hi`8Y5 zY_hl{lk8%+3nCJgsNN~PdJJ?G!FYC0iCuO-QL&Aj(w?|N*06>PxIj1bm-K7mK!}6^ z?UAm*0>M1v>K4U1@u;R)a&vR6F@l9h4N4q|YqV#Zc(rH0RFZQ^je6!pmgXZgH5Owo zI&0Osi+e6mqeym6Oo?RS=lV2TV0)xeLFON!WzAxzo1*!Xb^|`OUXCQlRIufEQ+f*cyf2I)Fo~e z07oUPSZ}b7+nJlGBFRDxTcd5wtE3iZ%1ntvV!v=O=1j6mD~BVGDD{ix%#y<&{dZQY z=$p(qUN6^)@$U&M7@&tz z5E@}b*Yp?kYr=}q6IaBsDw<1jlDpsy?lh!d^H2lF7W#l*u-T9z*Au(BGQtQsH=OJh zyUxZTbI28K>?RIN^Fgr-Mf6qhRq!s<{RZCnlDOYBKBNynnhhO;RF)E#UAAAfx24AaR;3GbW55c3N;s|Dv z8>Y2Z&XL9wmlDNd>`nJeIAon&7t?&?^G>A=17{duM7y(cUZY}8B3r{l!V|jBvH3EO zzQFj1aDnzvlJ>?97B;Oo4ux*R)kCafTNw0`%0Ub#{mPliI4n%*^f;s@uNEARAU<2{ zOWo+Kh6NJv6)v#CyUwFT)uiI8U66P?%WP`SoY^Ps)Kz6obq*0awGb@eL5|t-gasCb zfHoH4qK80S<5H7911(nB*q2;N?T&Bgp7P+Qc=&?p8@&2W_Rm=Eu&I>}8{*x6LjPUj zhA&=n^O!kvy|3~rF3d`HUCL`-@n<$>V_GO7e@fMb&6!n49}tPXdNE9IyhWpUeSiTv zJj4_v1W+LgQFS|eLV;2o>yoPwaCfBO+fq)i2hyz1XY!u;2JP5(#1%uyMFynFD{+i@ zEY88%W?t`kx#wmAcx%H?{0TnuN3d^l^P3!AvfN<_cO+Di&98|sxLorim)uU6vtKGe z7p=Tb1)rQu*o>Sj^;|TrXxZE|bOeokbZCR=V{-)~qJ(&_kX9UAc9z85#O=iOo|jv;6Ar)kl3)LjuYHlPeTA#f zv-%W&MY_WmG@uu-s!n0mF+`%0topSA4vSK)jcH*DwJK4*dNHywOD*3o#=J0eaTS=& ztre~f$&g#!J=EBPV1l?HBSEB7AA|0eP(Vox-V1#w7RPq5)s&-Jbz_CJlOE{_ePz%E zERlEE9lD1?QD~v5&k9pg>q}-ViT%v=fqmln9nbH0zU4PQ;pe}^Pkfg3XXtjYKn8^s z4D6l5!XcM@1*o-&sFkP2nR}*8pXdUi9GH`GEpU_qZR!{-UPM#Vl!t zD{EO)&s>Ee2hm=+i+;#jlIo~FC?br(4YrHEoVr(YA~(uS2jYkhm9Z(+zycSm`L$G5 zDrZF}^uiG6BO!p0VM^>KmdxG6cINiL%N^f*o1cD*cP_bv;2wL85gIk{6a(BR; zL&TgI=#(K4fR1GB7v@-#YF(<8B?GS=BH37S9D_SP3+|$4A*dCw3N&%W1$8j8Df9$k zP}%3w8s(k%5D4NK8Pv1O-8@~-{WdSD_HM+QPKS}Wt^mE;psVT_O-N^ESX#I`WsGck zhDa9(0| zQy;{;fZ|utdi88Hw+1>F_>L*xq9cw{e3t<5g9dU_Csz^3QH z$cv6XR(YjNa#8_)#+*4U+#Y!Ug2yX{NXpzyyx#MA$Mv4wf;oL;H?t&i#+}Q7F48Hh zj*EdQap>76y1Ay%g`RJWKwJk)uE)yIY_dxXD(fHzSRIpKL!b++uvFreLewaEaVaM? zst@X&09*u*>dEDh^>E$o@<>?Y7XZ=`2I7kTiue#NVdMs$PuwjCjFDBW#jE0fhQQ^> zs%PEN$J!5+oCWey+2K-Z-p;(<(g$3a65ENJ12+eD6N?dr?Z9EFp8L%WS0hmg%Bo}R z7#GHJNfMe!w0!@P$O@0#+Q9PY!(NnwLAwxEOe};=#bWBSPsR z7z1O+s$|aJu%e2 z#V#18zaVXiOD%cc&Ai(4V#{Gcgt6o0o{vY~ShE=j%6=gmLFk&N z7Yb&M&hFqz7W6<#Q&UeDs$m{;y}6->(&b>8oLLf6Vl&iwha0MNfljNs74FF){-LoDlI65rk79yjfqWIXzqML(Rm}{pI(07Q} z9=2jirO}~2Pt~{ztvn}IJwdA;m1L;XitHV-VG{d_46HC1A!?L7o2rmqW@JJZBnHRf z(7;7tHTsNC_>7dskCLJ^BX z)hc|$v9q)=W#-_qukDK^i!)V%$ib|ndIob*rlQ^pOfUn4;OoxzXvA?TGawcN9z=#$i z(K0u?R2g)Y+Ui*PI8JOq%bDGgxjKPjq^q7TfH!{GPks5t!H1SGw}yXH(HL0VTXrq! zNSh_t!W}H2jE{Y-wna-TbaFwwmm)85TtVDb{9!47T*+bwcZq7>y370Bj4inF){3WJ z}UBWkNNH^UX)bU3D$b4tO~0Z zT8fI2q(EZnU_b>)NuuisIw0Ki2SZHT`h}= zK&STpU3xyv{^_1P^ZA}X`_sJtDIeX`>L$51QYB;6)$=za6^>j?3XFF_2Sf{J=Jx3> zd)J(OVMmK2Yx)%m_nF8zl1@z^siO;MV>_(j26K!koQmONu-548ftskr)~QE?4m!wi z1KUCgarp&%6z#R>wd<5+aR(oQtd6cXm*exxXbGl{!===WGWfVnpX}%9#h$d|M@N3~ zCwTuAFL$KlQMUA=G#5L^ks1>gC>>0Su3C^;%wBJo8vDoAlu%27=HkT(tHa`1K#Y(O zMI4K#uy_m(4o>)wzb=wnpASSS{UB9gi<9o4$AiNJTeyW=xPt>YtXN&yPUPD3c9DaL zfvOC`3h!{quz;gVPyw-68tg^IBi_lJ^X{;F!S(xm?PvLB;CDV{J8K zawgFm13n@HQd&++VR6nZr{N1ujyyS;V0B(h!Hr^XXyZ|j+-Yj?A{*Jm*T3E_M{9u! ztpe7jLyAgyEVy(JcW?_k*uxBpUeT}E2E9&lx9DtY;(hcH*5aWED^=#J0+(23G>Bqd z@Rcru?hpIt>DfQ!m;M5O=NTW|tQIyG}TlJHPtp z?0@**_{!h7nmoYX*v>wc{wxm!t!h#@+*Z>l<`Iz6g)-r@+sj?H;)jRUj~o;=lj^A} zMQluqyBL}(rtP>lrcxM?|M2S)9p(n0QbiCk)&_X3aGQX$zyWq}2ivAusT$+vvE?9J z*R9Ev#gQSh5x>Bt#LxqH>35TiOo0VSSsV%KtLP7h{F#gCD_1Wrp8buF`Aa|ZkDt9s z=n=<@0i6vb)?uZP>7Z}Q)rw+uu48eg?3Kq9IYD{z&T9jKHk-?(Q(qfB+nTK~E6_it zqB2K$$;l+U(I_D6IBr&2-;YBA1=DBfVS`@sg?E<2D2vAzlOD3XT;$cDZ)hI0VU7d9 z_}CsqdFXl_{I;`wCPS|{3~CR0`_rG>{=$F$Cx7(+{4f9RZ#?*4`1IGFH zhgeIh6=qtU<18>p26ThZ_<@`fyIpB*_2Yz`x7tW(u*ty|PfBg3FVyp~r&K{C{ME0^ zC|=Lxt|^Pvj$v&qeyud5GarA~Uf&W%I^qM?qkC7l9aI?uSK_Peyo8r<2MKzHwJ#z& z)oqr^LNW~L#4n;f9_Bn>#zx_AA^p}3^U-;X<)LD1Sav&Xy<=A@dU>>Ye9V0TJ z$IeU|QIhdwWVe%9*~!QER8L>0G3r$5k^VnM6(@W6D__?EbZKt;7Phrdvy@{rA|g@( zZH5VM;To>t7WQzcIbng)!$Th;1QGQh48bqOuf%u-AHj3j0mubmU@LNCa<}L{OEO%f zQ!YDybFj~^Jji+disp%j^*IK6qPFB-0F+ilUIZ9WDkG!>pEb;V0T{xOY0~Mwe($Ud3(5pSMUm6x3D<3 zgfPO$`k{B~8VXPE7xe@CppX8b%by9hNye`s z|MS8B`rrP{Km7Tx?Y{gG`PgCu+8@BlVY?yl+GjPonHTL&C zT{cWk*B5@^;Sm8&xgd0!uo4p z*8-!Mn;^=gV9xne;W=s6EBFLHhG+1yq4~qf*)bp&tkxcci1#oSVZDN9@El&j4P^8Q z!xf(ro*nen6mL^FWL;dM_@lLbq0jQ`@DGp~J~#W9e?7kcqd&j<@fR$wnJ3Jt{NI4A zkqzO3uqLcdV|Q|_SLG(Jx*))+cW?5dOln!viW(Lx;RH_jYhTw6I&l&ln)BW^a4cyZmTF)nTI4BS^U(Q1DJ>Z;OiRkC`ggT9<~jg7w0{-DsS=bckmp(2k*o8 z;Q5)^7B-^Dx17O>*b~Inkp;GJefIjIj3ZCJXm^8rw2z7#4bsthi4;yCY8(BQg3d4C5?(hm+&3{5omxSHm+%6fH@CGk!#5{#c$dR2$NY+4S>-?eGY1y@v+zX7FlS%=E*IZg zrPW_dZwve?x6eq9UlKNi4f+Q04Z0N(Y=>RcUIEF~NU}GFp{J=b8r98EM1$hk#R?QvalHdlu0k_R06MPm%YNL5qdv58P zi+msc2@l@PzwmT=^`_pXu)opUxqG%B{^2M7|9y4$)j07p_!Iab^6HXHFgAd?2^!Q4AC5$kN4?-^p@+o`(ch!GUj&9eY?M(w5 zc8UoP-?ee$cd%V#pLI4pWW9!8{9kwcd6;{C`>A~R7B8R5{ta2K@bQX^fjHsdBjFM8 zDg8SPPw5`hKcKs$yCjZ;4w+A|t9AsR)m9|b3C8bRqf-EEZ8nRc%0X%z9$;15`cLSz z2=UO6L~f3e7LC?;C#NatBph6Boxjt9z#oDqjg_98X_vg}4nBh4f~Wk{SC`!r1%}`s zZu{BvcW*lWTX=?i@K|o%^4q8EA2N^bE|S!zFS343?vNYOitvbdMSMhmMHom z`X=JR!c}f8gDDc|&^To7egE6js6h z?=O#k?5`jE&&YYnei9zR(&9id^RA`h58+KNF72lu%<|YTyZp(%6Z};cE@1tVFMcBL zzd>pH5_8WYu96>me1ragEv>z`Ou~9F;^KmuK1y-StCBe?HwwxUNqaow5j|V;QWtOl zMovh=%{Yih7PNDhn)g;q9GkF8?vz>wl{ew9;VHZeFOe-i6kz9WB1y^!FOxHAenaGE zvi|ePw_W}bJcHF~vjGY{?7?7!D|p1?pUPk9iI=?5+hn>5&VP!03IET&?Z4M?J<8z< zo006EC8Tv4F3~mNmXxsrcEFq{xQnCanmwj5#V<_baXzS=>oxg+gEy#E_EZ#ZN*&Kw zF5{t@@ZP^yhjeLpqT1bbhO>^sk~4534W>S?Ud)5mT2K;ZVL!_4mArgYK6+c8zr&kr z|8sYI%?ntQL&Lzu9?Q3sXz+mbLyt-qSp_!YPgWLQBLAKHpL)To%lh zOP;V)DH>U!1A&+jt0iDHjdqn*7sS5$`_i;qJg}bAt)D8Kr1^5GZ#r?&nlUh}9; z_6ah>b&HP*7sw-4o6_&gD6H_|UFW}sPu%~|OK$hvjyG2Zjz?r5j2?nQ zur|Qrh)c{@X)N+et(hpGJ<=m%b9v1$>k<@0G9KRh*IIU@XQSt-Y1Xzut-1NEbMULJ z8GDP|lzA6Cib#={k)rrU?6IV`Wd5vd-{skt`OQBpzx8Ft8~e%&ejNvVn6nZ!!>uE%554J}LJi`q#wH+YZMytt~ zimXK*ia&-+@r^83a{CS+{FMIQXFT27lWTwIY=kIGF24siFu-HtV`5iTc_>xOPmo`M z3;gr9ZlB6slzatiR97}0ume70g^TNvfzWv%80u|5MUj_n(l`)0q-1)1-8ZNsVU3ni zzUEq`u4#Jj&$J%yct*u|BN%pPdEC_KcE7haruD4D>$*Uo)NDn{jP``NFrZiBm&gdc zNH4zO@Q%Lvf;`_a?R<0RPn-uLAioPA)EE*U){gH`IN=3;8~Gg=@way-AIqIeeu!U0 zjh*!eY{JU!o09RZJgNw4g~DvRbr4~d`%*yb(SY>ms^(1bl2D**Y?+}!c<)c0`RwD& zkT)p5Z4B!6zA>K5>lhOb$Zca|wdO!B8Q+U#q=!rKH5{jHdbpDGw%k3HmuvP1zueh7 z$Rp%?@K0d_?-CwVW6+Xvy@Y=T*T@J@4%U4nTbJ}0)+-srW9b^RdQRFSgGCA6R>#S4 z8;H8}79-LjJz+#I2v@`h#4F+z@q*BxnHV14`+4eQdN{Mhqr6ITb92VnEX~==*;D$|B7^v&$%hYQ>yn-z>y58LXT0nIib!9U*SJ>YJW|GpD4hj$ZO0K-=q2%z za79;e|GK(1cL?wOL7;XvR!h|TBDlL>f>8W)5mK5{dhx5p#}8y@GCxID zm%au`xZttPjrAT+dkLj>bcidi!PMJsr5-K%wIN;-FNqiENazWMhxdN6#kj5lvU_em zot2>Pnx|jxRj{jA=N3Ts$Wjca?#3(IP6^q$k$iAfxHgL0H2MK7sGThj3fTCh76b;^T+1b6MU}88*Id6Mh8~XhBD)pEd&& znVG1C*7Pr00hg``?V%ThRVf<1_xsLw-8GXuSFU5petYKWj|sM&aTSL~8P3%JTUn`9 zNu~s{Mtpq{8$%kr)g6qIO=4D9UxLKbA{ZeXoNLK@6tow*>OjlLYZY8TdXZ7MBt4SX z@5(>=IllQ9c=f0K-}s;8--7SL9c)TkR(1jlL0C`dOF4`zPgT2>j{!Zp`5*MARMeelfnIv%4hpfWM7wC{jD^CKXiOqj+$7$=!4}Jpbl}|MCCha}Rg^U;Gt* z>o@pS{94Gk)Kwq?{`k31|Bs|cOn*p&T;}!Sv2*}d94~&pi^tVa(;PcwMk3PoRh=+p zI1U)U_oT5)cIpH@!3b&ZJ9d*ET7N_4G=6Glpnuoe0OE1OkkJ0CjH()J#SR5hx0Z^U zk+hLC6%>ggL9E|7-(~tuw!d|-Cx7$LUi`CPyl7yFhUwLV~R`b0XRxR!r5YNMZ$Xz?+Z&BMm=^;wzZ?RZ)`b34}PXZM|l-f zRUzW>B&>qJDjsxPGJX)7TNYv^*R8@TNKo2FCl6-vhV+d2yG-9^H|`!k{l+iIul*6O zg|zcDv%oA&J@Z=9T9!-FBcVsMs1M=v`7Vw$NUw%z@Ny7!HC4;&PLfgx?_D)DvHSA+ zKFoD=Ug>@)l>0KBnp0Eg+ODr~e9hLc$K=x=m6vbG?E`KvW&23?Z^+>d(qqXF#K$`BE-e(6O$+MH)4SBx z7!4NA)@oEd*0@s@3C;bwLHK?WgZY!LnJ{g8q$Qt1SuFh&n z-KpwTKjF|yJ4Yo|@m6edlS+BiE+&In{I)a^8?Q#`ta;ZpnM>8S_h) z-((wHet&+@Nr!Dj-+{feh%44tbW&T_!5qnTfItS_w`} zBsm9U+tz$7uuunwO5AmE@m}7ri!a(ZV;OE(UbDQL&uyVU%e_g;j@gnxFH-u2#tJb-MamVea;(9z0Da{$trRhAX&Zcsu?#j0-qppN zrN-<%d6+HS;akjHhujkkDR%`Z-%=yv(cOEnXxeg%vNFOObcmed*H>K zB(nDahy@gp6+g0HDKc`nCM&*Rx8x3W7)+Fq@ris#e#P>Fv?T#AS@w+YOnu)1A#zOI z6wPH!OG5Eqy=D#dz7NI0>%E=i_4mVBX}WaWZs9SkfVy7dgiDRH-Eg%(tjc9Z-lrnZ zveGfPb!P}fFWvHTfv=H`FnZZOdB)yKxf(KixTdWl2`k>+uOsrDc~36Y`30K_JVOjW z;5+P&bi?wJG?AYZZpjn&Bh$F@nwGjI>J3dq#A=uh*f7i7^*NZA{T;;Wxx@Y2h7`TH zIV^KD<_zD_K=L; zdWkzOVa40~^?+QHXS_QVW6F?aX`8VGGrlEXvpCBwX~r`C`FXsMq3n3|kj@C?_6X+v zLNd((Q+J8=BsuG5INvP1Zyu^NalFwYV?Og--fYlM&0&o2sKKro`P32gLadoSi(xz0 zwMc3VQK^Qngbt5CaJL+%)&X~TEZy+B;?2W)^sp!AdYO*f_K{XSEiGCTe!zC*GK=Q$ z`DwK=WspY1Q+G0e<03{VlTA(zk_x|lE-&uOtBmlxYI)2JT-G&V)dusK!%AnrM_9q* z6Q-9tN03#$eSje>ljLjS2$$4l8D;J8O76AAo`)Di%)tO zj;)c}y!8S%aQMDv=(F5t-OTnV>%QsN+pvT0B70Y`5l-@Y4MOPf!DS_~!q;uFOCc`q zt!=8=uH>=|-U;I3aVBI;i?AHQWyq=)9ZA@pbVu6O^~CJR&smPQFOWQ3?nfJ^`)t?! zANBCwc>hn~OzY2XHFWAmO=rO3(4NozeR276AJCrn(3dQ_Kq$pU$Oo7k89%@vQl{^s z)Y0K%Mjph=lA_?^5Tp*=N^PoDBbbPx*`o!dw@`Y#6TT-OYO`cnd2NlZ(%!?c^nm2H zTu7Sc!9Q#l)IX_XlVegn~%L}hk0)y#K%+Tu;7O}Hp`xr@jd>G zWlb1sakAf}c>ItfxIdcP4-=N2t+XB634OcHM}1d6u>;2)oN~JPLqQR0E-vtR>N3NE zwNO}Ar`ryD!Ln{^m(;tV_T<&cJ2|t;A237i-#+W?hFaww7LCwo*Sq#Tv@VYzsMp5i z=?v~4p+-v#j#OYNn-M@o@#_u-)}nPqD0K?%pS_>L*x`OH{#;l`Z)K<5Js$4e1FTL=>FLH4V&W0WaN{TR zf7#HPHfleCx)jpXMLV~E56FD*+=i-0Uhl>rW%fbJ zFr6O%UL8ld&f<=h%Nz!IYwA9aF zfh6$<-I*bw-3_J=nIWxP+FP#*>V%Tyxy> endobj 2 0 obj<> endobj 3 0 obj<> endobj 5 0 obj null endobj 6 0 obj<>/ProcSet[/PDF/Text]/ExtGState<>/Shading<>/Properties<>>>>>>> endobj 7 0 obj<> endobj 48 0 obj 25578 endobj 49 0 obj<>stream +8;U<2GrScM"32ed'"Z4D@iJptR5%H,=*Sd89?KteHb#!-;7;s`p=,)#>Ub@B-/%-O +2cNrlGp4hnArqqjfOm9eAf/sJl904[o/uL5SA3>kTC:reT'uDt#l;#PYTZW)d?8\7 +l&d#fU#E@m^#5EOXU.GrNS+3Y,BD!/@Dq^F1(V(mL9Qi+m>+$XJ4ZYhd1PO2?H6-&H +<@*ZGrW.;XZ:a[3]E)_>;Nj*'3[="@DR@ok%7gP7E!Z$g',VXN>XWbenNc-&H-jgN +e3r*EdnitNWp'.QaT'A4:^bar"fV=i7Ok*O'CC_s,-6qc"b(n^PC +dGfk6N*c/jkh.i(f&uQ7VB.t4LE`On5Z7GOf#Ib-![A>>4e&-)kW+I#`O1`"no +,+ERa7>_/hpn],AeTro:$-d9%Oi!hacZA.KZeqYhJlXi$f5IcIoFFY.UlnW8BSY#i +l$&'1c5ZV:U+-XTHSlf[>=U#h!3A0T\?] +]fZd2p''D-2\ba)+L2`oKc:Ihf&hUUa4eu+Tihe +$:7!+"DQ5:3Vaqi9?SjiZF=7#e5`Z'4Wg)hNcOPQXLqi:E%&0$8kDgL4FOUN0.k\O:n2hfD=.WGsGmP#DQ*U$7RLoL=p:Po;i$O.4 +;$n0@!Dj=/6>=kL^4Ko7RC&k@48E5&B5%*Q>'sYpAWfNX?,'Q/[C57-,>NsR]#rL] +7#jWaNG:_"$V,60ULl=W[XH"3>PRsXm#FH7%A_%>TDNsfCQSI/WVsZ2C"b_4HL8=Z +1N3TSU`a]M63][T'SM!5>/L4FrY*PQZktS_^[/gMg9);TrZcV[ioT.d]X0;kQ4FbR +^(A:]IR1N[2CW"+bhdT&D-15K6;Le1NYc/.j[6`J:MsUZV_-);(&Ql#ZW'YDVda,C +r/D5mdj$2Z#M^Rhr1H@\XG/`Ak`0g6Nl!r,J&u`K,P)RdIb5t*(9&M&e)$5oRi:A\ +lYDkWpfFuKKe#/WO>@h2e^-LdkkW*AXU_Fo3M(1.7@g43Sp6!#SUV[89n"a(?!P/b +Lb)K;T3Kc5#d5r`5QDLOJ5E&(Q>8Za"nU;5F47"+,$4'jO*U`9>Jo(nA6>`?bft%orp9BKJaX +6s'BA"?.@MQON5#KH&LU_c3OuD$.[J`3#LkE=Xu)Zm:no_%iaQXnM'Q.DVt@!,k2N4&+$6pkMriVSdObkDqT +0!2P%@M05uK9;9E3iNF0^eOAd'CZnR!!GPQ#REl=g>et]:R3-oI?mbQ?[H<-S1+n< +15>C%ojW.5&>=X]A`;I4Sb%O?X.*<:*==Y9MTe->)cn^EbKC\SUrfk\.L+7dVS@g< +_B!V$guNe>)8*rl!!,n-!^`$kUN.gLd5PUA5"llYIM\C\FI!taH49!/[:=(*P+d4i +9u:`!'!,nuF`T'dSrpGfZhJ+L\a`1o)Z=^O]%7AaSFn7/TophJ3;se>^eJhNdd]U8 +^a-`a_.;`fC>fO((0Lsn@D0.p[GD#'r*H%/%K;m21:po`V)N."L/<37(C-kU0XrO&cF]^Ytc +-![2f.TS9@!!,n-!^`kLpXDSNXq,[7RoDKf3_@f+GEj`LS0E,51&14(f'6@^bmsMk +4]^77s'^FA=:_ +E8frjOVRUa<:#Tmg]+tZjreCs!HLJCHE6nOX-d]dI7U29.;3P:aZcg4p?+01@&;_a +D6dtgc`"[+1QG/IX"t)C\jL'HEn6o,L8=V7/Qr%[IDgc*WuK.Tn)/HAKLV5B^jqoo +*c7rXQNX]\#O.b:3%K&Fo?pF-)U"@JQP[UT[gJ2)?MT&rPb,A!FjbmB+94GE1)EQa +?::%"1=RlJZ`V5rMLJ>1qV8go7km_.e+.+nJat.8Bb>duM/9d^n,W]LI8c5iW +^qZ9"6ZR%;L^0c/VJb`^1:j/#=gthCG&kaInp1%i,c+uimXUe^53%TO*!5=.-mC&d +-n7"GhHjAE^'tZ1XV<-L#,7VuhQ.2H5#4b=Q1GZrMc>_Kfs/Sh( +L=7Vdq>NKGD8%*hV:fUP]8&WGbB-+'5s_e8kf]>?6qC2'KJWi?"X!47*uQQW7qLG! +*(1n90pf:%2(]2[R,JpseRiU^4>=Gr"uMO<0'RlNf$S;:'lUdOn7[aYOsRR/;)8[' +Oggp'$lIRh?J%o+:!2p9FO +XpZ3,GZjmkK(77cI[3Hc7^CjZ"3_;qI6@I>(Vr@u>WlXoFI5BY_Q%@:"k]Lo?PS>j +6L<"oi5ra)?sp9ca/>t."_$f%G%_S];8Y#-k#6gb&^,V`:1^7kA+$2@RO`%%@?V)P +R^K%#V4t*3QWD0J*)Mu2c\ +7R\dS.W=EXU`S_Brg%&UNa9lp0BZ1jZ@`^-&69O;UOll'mK +_sY+\Nu?W%1Su^qac6Gt^9^6Bie:b-c_<7:=ST)>-R\"]$c&Uf%J$XR\u8^Dn! +'>HG?:HoQQ,Wk+TZ8MPM>;p1j%h#a$rO-/E3pcp,H0fMSW1H +@ct1Q0Xf/cZ7?ET@ncU"?urpf)>n&sof:&COH3A#VL-A#cpbO/7_D*Kkfp[:j;<(9 +s"6oA"OS-[_`g7C"UU1V@K5K$)JK[6@;cg>+X>e5R\kkp;9.%X\dM$Gd1i"QJWX`- +g@`^+E/]iF>5`MmAbdA;/'Lm*0'J(2+pQP?H@"U3[qlQfo +f4c&L*?_C5(&Wb"7G'`ZH_*GfP%eq]gPA1ROJ5>\dSu=RJ'R&>!9RbVAlZV!TVR#Y +BA_V\i%od?-`hY"=JiN`oA[n;AR[URcF920O$k#=+r_Q9$rMM3o8DX.J=^q?Hl*#pk#$QTR17BFbu:g_Qr=T3"di1^t"(SC*uF[alpaZIteeMEBFaQW_U +IsVk/@gE0q\"nhsrm$+MOU7`jSRlb<-8*ZoTmq^:>XBT&85Z2iBtAcI*QdC55@O%/ +>b]Wo%TAh5-/+E4@ERR>Z!Bj\oJ^)"W&SQ[-3e+S?ea>GO7=O,C)[<0;,me3Qgit" +B,3 +6JPReFs-h8:ri@1A&,db$?EOZF6iifck>#Sc?ft0ucds"a1\oQA +Y@9[_qjcBCWP>d4Am0]9$l_5E;iBn/V/63W]/@4!0uKZS>M1ad=9E#SW9l6=E5C3l +lJ(d;,N-kL=m^nNPCR&6[A.M,[A!jG6IR!-JS8snU@":X8ZE0YnL(@0$.8cA9*]TG&/QXn-K^p\f7_+ +EtNc[B>`miE1m.h+5XZ+2t/NNSRM/V).;W-R;8SM'P)0rfrPS["-4?R)0M3/+-M8#khEAZTXBrR^?-`),lR=7aG<;DmMC(H(TmDC.hUm0Q_ +RD,IFS*g/;.;b*W2#F-"C)nM?@SnW>0Ud+9UkgF.P#EdIf-[%W#JL\B\k4i^%Q`7D +XPYfiZ(7b*hbh_s?e8W9r&<5MhtPq#rO^s2XG2r,Nm;qD*_4k9pY_)':4a3Z,B--K +:&(tj%jt#n50bf'QM.^b!+DOZKTY\?CW5Dd\WEYs!OqhC6gg(<\49Hulfnsg9#!+= +]@Ib,Zuuh;0aF,A*\j/2ca4RcMsE&R/dc,4`ON);-$\0@N-#Ni2=O4q2n8D?L]i'C +:N8nhW>SrN9.&fS4OZJJn^8='khW]gmf]K'7"]f5HH6]R_@2OOF+KaVL03WO!pmM$ +@R8,rcc5]/1EPSHXgp'>-7nK<%]7Z.4?I4O_?@n4M!]1,g`E%]VOm=sJ9,/3/\I7Z&1PA@,2j?Clrb-,2srU*rGo\V +G^jEZ*MFu.-5g+=AB7=4jW-(iRrjS\Y_NOG1C.W7aE>=W]\lNii86XHHAV +3ocAppr5i!5k@TeN&es![;7J7qaO;u\i:]p`3p6'P)E:PEAs;&l&$"B_R-`SbAnD8 +iQVt>'BYnZY/9o,#$fo^5'2luP]f(OUmj4qeF3tH-7'8j6XEZWQR39VGn6Ip*=`") +9pET]K3)/%'plrKf>f4g9nIi22Ui-k!:ehQC@#sYCu1W-$.a:8rEMqkVM=Sa]f0>/ +m_Th?VF1Ik"(`a:1gte`p$L?:ANuO0^rU&"BZm30VWU3mG6;t,*X"@t#?.[T+[W8H +!n\bd8$&E(2bG7EE?;M0jpD?n4K(Z_)>4j=U?6blGn%ELSG@/P=WQ=#:c[afKI7KZ3GYbG%m]AW$_:'9BBo!\$[ZFs.V +HkJjVKRjIUN:c(`7[f`"&t76hl2I"3ckQ;7*ZEi8nl\^EJ&EdtXI%:Ej8X1k51?p6 +\^'I_\od+DXu#nX%6X.],h!HLD>IV$/jl(lqRFSVh+u0YngorAR5g>\^amOt8B^_\ +.2)"7P$b+3gJWjf^2@]ZP=-haenHW!&oL@*>DCQKVl(e'lkj"mgRHF])alUfNZuBu +a_90HbKn-tqW1jX]7tZC=[-5GG@]rI1!)/XsnE`-=9$rM%V/&:#p +:-$HGL9Jb?_IUtc&hsq%.Zc@4lasSTY8oS-Q^0_4`4DUiVPlV",@F)?PKNEg1CP`. +T?@)4Ej!h6:Xq]-#$3ag;cu?P!iN!I<@.hd_PC,f.JI$`_'\>YAS(NELha%gG5&Cs +Z"C\6/`'dG#$sm(hcB_$&jUKShQ#RR[;s^N.b0<3+O7CW^3Xqf$81I[RsLRd/+.XL +XZGe4!j3j-L&.H2$g8$q]3XXTDsc"OM1L`Dne*_n6XqRNKL2u6R:hpGg\X+%ht*J`Fj8h;/3`5!2! +hjdAOk]mB?QKp&R#09[?dF$)pZi9k=WbV@_1A![i6abe')0H\$.MXTu\.%/Oni&,Y +_[%GS<+@,>Eqb@SpSKnC?Bmqg1O4o=GKD8>X=8C\^4GR"o4a`oVFEH4:9[!"j*Oj= +m#YU;\j[3cke\i'&"O%SrtYlOJPBi=9t5Zl:\G6B<0'e\lAJPuBdI +8djfY('t#;nA[`jl-&4[5c<'0 +S(Yj56G!.s"]T5>0EtD;i0h@nqQj]bN:Zi]rCXL-3]/IdDZ_$Do@TO-X7Lnc1)aj+ +c&5uTGV;)tH`W[`p%![m +2>nB8:T:SVXGh/iab![A-8JdT`,ME7Ala_P>QufPBr[PQ,8+T_Al$!'aEUa'TL1'u +k5SZL(fNM<-Gl5.FoNO/CDYC3K2'`lm49_AdZ&(8E!"*Pi1?E;aN5/F1a.Tg2;3ZU +%lco'pFO^K#rej)5nCK*E+FiM:#pIi,`!"/@R[]0\RC>N#S@eFM!87E +C?@c)h]i+DJ?\:9Qha:X0(NYq9*67 +UuVE9:YfDJB&\[Vg,t`0Vtk^Kp=LGDCWY*roYY[mF^NDcF"#)CoR+[QVk:6g#otAY +m4>>g-B:[a+2U5qRIDYhK^aX;+o6S0'A]th"ub2BJn@3%j2#[%0b[;$TLYdJ(j"*H +Sf&QpYi6j`";-VA0bI)lY5KI6<1\d!V]s^'-5O4)B!P9l%.R-L9A"blSU=jKQe1s8 +@Y>jIHE0Z!WRuA'%nJP=M==lTXFB^A<-N^!dqP8fA'(r)b;A8;LkOf#PT7JDLn$$a +Z;n662jNCRCesbr]+im#>[c;`%hNIAU)nD6(nCUGV1u+AJ36#Uc(8GI?8l>:d(8m\ +[E3D&Nb)&[;!('K'6&E8Q48S`KE(lGOkgZ=!:o76N&l=hGO`0gglGfZ5suqIQtT0% +Y7!tqXD(kb,l;Yc';u'P;K\kf`d)k$rF^_ +]oKtrSu^],)?,Z#(L,P\(Ah%KDKa'eL\t@\Xnr\SZ^8LCS![IA]X377Z[9K!kR#.f +8J!&>`.XML#i++9IIS3?EiNptornfcJ$Ed5ECe$.gK1D9]JIM_*1!N.%N59%0%G-7 +Yn*c2Kusd^eB>B1)4kM*`$ZcQ%q.snkGQV%cia07o?6$$77L[U)$t&!k;R)oLQ=KiS/SDHqI4'"n%mYqbI+ +07;b]f'$mP%28T>^YR-+KdQ!7a\tO($K2:W6M+d?(\tV3!#VO!;Qj)h%jO-%he$J0 +3&'ogr0Xr/LSZiPQ4(T!&eP^".[UdrG<%"0Y.jV6F'/)7$*X&I.(;&racT1$>uZ^L +&bCED81;#JP2)6lPfSM_]:_KJ/jdEuhd&dbp1\`80'ku)-J+AW-G+!g)T.MP,+"+l +gLnbflhgGSj8JeuRcetYH/A;oXjr7a'QM*nf$5S`*:-_k6?Z,U/o8-B]@h/u&EOaL +;!t9(R[PILg9>\$"En9@2ca<(_>[sA-tX@4BQehermR2("D@Lr7bN^d7eY$A0m.ng +enM2fe)`'ap81Vp`aS6cgP("=P"2R@Ilcri5X%&U>ADBN7-9YnFhZhPJeme<,GDfii:r]W5/[C`T#Qg9T ++B$G_UO]b`iYf1;%uW:ma:!ps%:uNr>(FGWB;QC/@L<:9Fb?EHoWdDA\lF*ldWsE] +F_c,YkV&4Tl`f.NH@+)/H:@6J9L6pU=d5S2bu\5n4R_T+(G3-o2d4%L)PE8aP<3;a +O99O8Oo6["G+]e&1MM@XQKle +)7[ZY*6suAc.(sN(o_oiJ[P/V%qNp)KQK?pR +PMm'(M/g2-??["$FaCm4:92>tANsgsXo03Ni2>F2oa;^RJ4csbAsAp.Uer6?<)/$9 +nK!)sE&n[LiOd;ZSCXAnh#CV:V`EuuY'_IGXg_%IX5m8>(#@K;iX6)6@qgnu@QmPX +XSJ$)=RccF;jm>pD#!Tgp?LBQf\`lJfuoOGYmRZZ9r1K_Ta%IV)C#Y;77/;YWuS[i +D3$i>LT4,0Gs%]PI/stPRG[MRR4ekB,qZcfj%jn?;o=Y*CeSL_U&Z2* +Nq\Da[t"o6cFnfl9jeNUZ'X?=J;&jjZ3j#.9fVf*mn8GOoD:h$J/ArE=U3\U-jn]H(48KODAqRVO<$9M8 +8HbXMnst,a;a@Gd[#`R_$G/:?/\SM.Dpl6?)iT!IBsOtMF!V(-E +BbUgSQ'3=gYX(((LfLkcRrq:H2L?b&p;C,15:8:&cW14!Q!Z0Jc6qmOu +YWHH]L^l$=!VW'PUnoEbF>2]7#_VZf$mk>H]*kV:KD#o2:M$(9\0pGMK.2e\Grr.f +^^TlN]H(>UV3`cOSBbBKp]l(iR\'2u^@8J>ldIfLcp8]%492c'TU +;Ws5H-./ZRP`7/odciL?-_d,VV!C_BrhF\.NRl,K')m]$f#e#5ppli9(G%cE?dUY0 +aY-0*b"Qqs@(M2rqGc.1(K]gQ>uN,lIE0*RUQ1&6RbRLREj&E?nNMRpEiqhlLjpBh +4U3>QascgU.m4_BUHmn5L]ijCkF;K$g:0&[@ed/sDr)H'-7(=I*m*l/f=)]('qLtZ +!t#t;,YO>U5sg[%A8+CRLsD\eVCYLJN)MjEHf>u(h[qHB-4e!Eqc/nfS([WaMOhU' +^_K(#H4F\?8d[^;MLAE:PQO_<:^%'>a[7S +B(_S]8"Y'2D0,cn(hpm4Ae_a\Kji@W&32-_6)5!=6\V6@-;2\O[2f;rj0'qlKfVE>kS?`9-P1,akAd>]7p,]1qrU]UjQM+5pcP:K@CL +nO(dBcq'mC1fG$LC4+@@fEg[mV!NpPK,Ki*IYQ:ncldUB7[@gs +YIHO7h9JSu<.=!?htq?lS3NY/H+cGgkB"p_l)e*1E3ll,@]6@;E\g7Y@I/K673Ast +*B\#D$&43<+]40#PaKjE+&dKketGB^5&I2&4bgYR+%4Q&fR=^p^\H7bQ>E&JLaN6m +TI;XJVB2h=Vk'fhMOko1-/S@)k'PBt?[FjRX6_jOB_hA>`cS-6!p;bT=65M\,RB5f +f\!@qGHjj9kggU8]G"oBaa=p!Q5TD9.46R>:I.lB[2bP-V8RJ/1U>9LYtIST,-.Hb +"I`L."knuL3$10YLr3^,WmJt.K^)7$IW@\\F^=_<8BY%1iu>dnGAdN8h+?DFp3n.1 +eV[![VZZqV/9Bt=*eQN&%$!TeaT/_TAfo7/X8h1N1:uH9$`Mn5cIIu%6"D+$j>n[N +\],d:OnAC-4R'"U6+kER?FE]GLl?97n&dBt(7\/]>u&g)l/E^&qF!nB-b,>e\$QdV +W6\2KRSmWn&'b)Zp78Web1',PKJJs=adj[5o,*_jgs:t>kSCmrG['>!K=q+#?;\*e +.D*3EHfL&2=mQA/afP%2i +n7gG73YT38Dcl\o/S9dO[S48T@ZZt6lb8U_@&9dt.M:"`LR0LVM&@%%fTEl6c-IC7?j4_b7Iq?6nR_65!__m?Q!tG`O]3?J- +o)+u>6=mS=n+4K5KjodmQu@n-0?&FWV.KR.hlA!6*"n]d#"KKlC^/KBNV6Q'l.t+2 +7`tb%F^gLc#,9+,M$ue8I.(GE%O0Vq+L+Id3S>7>Cs*Mh`TD^:hHpGGL)D*bH!2nF +4rop9;\50@"-bei0!M0rOn,B$q6icdj?'f\]_t9_YjcVti"gp?r?_ArXYqU +$MJj*$IC7KYcMOX5SlCioF6\ndGc](WETkXs,LK6AF0E0+:aSJHk5'Zgr+U$X2$_! +@k:H'@0K;,g:i=[HOu;F'OgJ1k[? +DKUh3A.U)uV>@s]]9'4UK3THZb9+C&SQ%c+.U;_9VR+fF1Po5i6C(ZR>OU)Y, +2%c7JcD0D,;Y-jJc?jf],99a`:BmF`ZN@Il0mjsV!c=okG`P;,L_qBCM1/?Da-C3n +;1Ma,46>kF1q+@;j/NGiF&5!`9I5feR,l6f!Y-D(hJ*X20d.F:0u%h%@iUu\d:@59 +?5I>-U$>n:d!GX!':IOh)dOeUlEAk-d@9)DJG0?+-\FDDN>_:dS'H'k->-i\1He7C +W4C`NhAX)6G(/=5-@?C:AF;t9L]p;A#S_t>cr;C;8VPVHnC:OaFQ$IB4f<)JB>sCf +MA\>7GKm'mF#.SZ:J<@rg'd?[]LVHT*P"d,Y8&T!Xr7tk6:<"o +FO`ci\h9Zb@nDo[h?>'I7d<*^VKi^pnQUcPLE+1a57:;I!@)u\FW>Qre1K=\Ho!B- +]d=]cnLVYFMQK_H`GdrcQAMWp7H=1V1a9he8Z5$oOKXXrqDmshDj+F[QJsJ0IQ4Ni +r(g[Y+>m#d>bQO<:c._p3@MaGd%Ws]i?:KXPStD(bObP>6lic,ii,Pj;O17kr0o?# +-kFSQq"!Fq2?\-[O2V<@4;Ml +#>JHNL3J!1Lkr/GKLACr5Kg:VZa(j;C+Ar*r!?P/165f7Y7[p$Sm\?dA0@&">h::[ ++JN@WY3YUDHHT?^GLbkDrlbj[oS#,?X;gb\]$!2Wb'f?2'1\?\778VrDI#hjk@`!m +``OUIXi8kK/1o!n'XpGqdpbDlAVj?SKLjlZ:?6@F=irW7K/$uXbSWoojr)s3qmcIH +J+rV^rr.<%YP7egI9t6PKXfN)h#20t]m5=m>jj%NOD#kqTo:50K.#'O$jU;c4!202 +XT2e2)u8BJe"]gZ:%+Yr=+%["71T.7jd!GT1RUAd_H[uTghT(DS&[.g6W/q,r-]$# +>f=Vkc]U6g=JteYuELZr7dW'AOg54u(-1J?7(:B-!#L%V6BY%@c +W4qAJ5%\DX`(('[L1s`IGGb`63HYH!)!.GGqS(m4q$;lUZ44`Lnkn6BGu2("*E==* +#>^TE;/^n.\/@&[L7,fs(pFmC,pQ%j/.I0(YQU8Pr>C9or<`P.=\GK-ScgCR$RJ"R +*J@D+Bm]jbO\_fM?Xc,HJIV-sd.drbK1 +K?6Cls%$n'TZeQ^$:c"e+#tQq%+I3.>e>U^s$p_FZ%,JlVZbTuNSu`3_o!m+E=?sr +#)5Z9@EEPh.B@XcbuT0aEqZ3,*e0^l$WlG>1Vc2.DeH5,L+.FsbH!ShRAOK-F`#7N?VQr;gl?0OC@["XoWF1ioAg_@#-Y_# +k[=l:8]`R4&e$U);#EAb@obbK@.k#8Rp^,M8CQd5PePNK&,rZt>85pL.p"7(t[MX?[8,S\'OD8M=XjU%h1k#?u,bD%Y%Un312De](=Dg5<>%XS]D$1 +Ea!Epk+iuojkdPSpOiDu,b.gq%#$"NV5+ia@07)[[cCfNlNWQD(+eOA4IC5&]7j:c +b+KOKM1oA#YJ9_)%Q"A!6DLD_FD9'Nb!.eJVj* +(^W@b0I@#0oh'?orQF0N$>hd*ki'"ZNDfs,A[S,cq'Op[C_+6c]s>e"BQAn,#1r$2 ++6<-^A%RM&":1t#*2`uV:Dd8"5P:+h#V)p*$;=uXCq$i"QK\&2T70(ki=la,R2sb;Ah`-?>ENL_8(u&;m +FC<_EBpOhE:Bq5dbXUjar@Y-AGa>ed%7aSrogi3S?.ruU\P^88-+!sN2O;4>?K[mF +09'NjST<#;pop.\8rV^jaV#"=@,"4jAWeQ@RaE9A2C#%0QFaG@RqAQG;-[?q1rl0% +17a3+W>qa?Z47h-@]h&XCV/^RPr0]Xq1%G2ND@,FSEHm8d&SfYSoI'8+Z(p:j'd2a +QJ-_8'PV^g^:MsL9aV+AeZ_:Q1:U>te50&OEhlik+?#HD2IE?(.'-j.X\gQ%Xto1F +=&!NXMW[jFmt)I?Z+bTrn<+FS0rd2mm]QY8$naSGHn[Pqq_$h$Vab(GeB!Mi*94!t +30!,dOfT)HMhMB(IND*0AHR*nRV +jUc8@b'62Jh-K_GKJi/ZEF(22Img/Vj]+!\6.nCuYD +Dtb!7:a;#ql?ofi0rj1=[>')V')3n-j-i)i7KF">/=ZKFN6! +!iKhA`Y3@li_G*UQko$q1>aT'6c-YN(b.+?/gYcObm57nSB:d-F+gfb_^V;<77&A* +4!f+>Aq$M?)WfiiH[,A6^'CM9l +0J1q9YV/c\#q+756^LGRl2Hueg&bX=G'gs*S3YA@USI+d48]JWpDjo*T:;cCWo/R% +U$uY_kFjVD&/qt"e8UebDkgs#&2X,RJenp;ieR=&Jk1[KNb^#*;E?K9N+CTM]G_o" +=Yf+5.gmc;/P\1cUjro#/)G3cS\UH$8uG4]RtRQn4%-X#8!B#EJk2[&Mock7ggokH +/X(fQ]p%'H-#hi%e\NcSlc@&%DI*dh'BY3A_GPu*Lor.H<)"[+'/V(LL&cBhH&#>i +4VP%W`^rr@L;K,8;Gr[5lt#h8%!#e4kt-8$f?uX4UNn>R_Aohbo"R;RGs'UP+3-n8 +<`CE2D!P2g,TUn^1qDdg%I5'F#!Z1,7>N8OLjn`KIVtWk*`4t_MBlb4.ruR4UG1hL +MiMY4rVGookOJ&/5@:VGj7R`45Ml_&2N+)WrSico(ml&@%^+/0.$Z77&5d@h[R2H_ +&%[eCa7tRZ1;#<=$g5J^C8aZ-,Y:F44>I[EG,cYl-;me/CSa%2k!"@XYf1mm&a#aG +g^f\phSE1WiLQH,FN#EhY(mCZk3-iQ("`T=Y[s.g`$]$&@2mkT@dJ_H4HAmX[CVl03L&; +3=GBtb=tm$1r_n_4())s$WoK"'o/5+c5stP+-m^")H<9c`CpTt+(u`*0N8m]O,cMV +.<[=KIY9k):bj/iU0n&"`6Te)hi[dPLb"IhmqX^<&_%#%7cY0eXlo'jQ`n6"CEHTO +>^R5t"h&q=Z]ZpS$1do$HM-[;TN\(%m;&UW[X%6^R![M1,dF2r8-U>2"Z6l0$RGmQ +.LIM1MUtMAO*X:Fi3:F,JuA3c31WF%A%*(0g$+`3$G1dPDkkeP75S9f"g\87^`$8K +Q6i[eH>Sk*[hZ&6:YKRK/.-I[WiT72-](E5s0IY:2=Z!EgG1Q-!hs'(VRY/N#D?ZH +7CYk8/CE&#H\n\&+i::HT+\XcK>Pgo?%ff$Nl(.>6!t#O!nQ`)[Gs[.0o>V9Vd2\p +/44E2O,QJA`Fj11=sVM(;!'Mh$#WXQJ.;8(UW7\QrLung3,cd/)f]P`39M0C);7A@ +L7*21FuBMm<&,?OXY6Vkc@0D_*B-fe1Unu^gGb2GNH'BkBcg_`?jg*.P[pV2],mRM +deJ.$qb;*%i8LR9%(^)CBA8F,?GDnD#Vc:"-n-JX+Ch*1"@U'/9I[]Q;&l:"MGJ2: +6E^Yjnjr[YV>k=OLBiggeN\A`_Sr&7_pBjt%*T&p!Gh,WVo&>?D8K2@'sfokHc(6d +09l*P*?T=8XmJ&\Z'9iN5a%@1;NS.[+9J9F!iE+\R5]ncK'?$qm/b+ZT.W+USZWtr +AWZ#H:$Wf/Bf$srW9=W[f':FjQ*LRp3Hi@O]s$bkGE(C3idVDI3Me%0%r8mq-i/0! +N-qR/NFY;;<1ikL`^54^Lk5r#ib.pr`ZAohnU"6*6lN+T-Dh^\,.t#r96AWc&H;2^ +S[eFc/Rdbt?,.)W-"BYC:DN.YC<(Nf>T[_u=2HO<[YrKE$b/KsXpFtB(Wh1QOYR#T +"T#OE1[ODf"-[bFN'nar^r&fIVHK*g5^QKV1F6m\J0oOF94tu1]!OqX]u/e_Xgn0i +/)aqZNM>ITni-"1T2KiBYMU3h_rq`#T-ITTIlk.oNLN9;"1D_p$H@%9),+8S1P/L) +7mDYEgT2GmqrfZ]=[$[?(oOFR/m"-APY=Ip$790G5B,;,U\lI)@^Pl4%r(tbflUEu +\$M,\5$RYdXrdsC?>2X?NVY'dF#s1mYCZO"o&K/hT%jGiV3G3%g%Au1'n*9B*K:T( +qX:CkVjIrlb$NocYSGY:k'@/*O$V%*8P7el/d!Xm:5p4TQ@6[F]Ch,)qrB_t`RBUFBoCe[^Q1#I+6H]C +&Au,BqU"Q1D; +]^VN[6N3H`r%sH*Z+5]X\/!nr^M1uSDqNEXVs0#p3Xg]]'rjQf;bl7ED7E99D1'ER +l@sm;@8>Lgo$KlWe,(Yo1q,X7m)HsB,F^&@gD,lc;.1]N52mrcR?F369AOZOIUjWF +$79.Q2R-?Jh9R\Fu%LP%M'XQZ#qP0o6bct7Err^Nr+:.mK(N:F2GRrQ:7sh\UtaF1.RQ- +&n\YC3r^Luq'=&k5E4Ym8"N"%+A&TM6]e-@YVg5-/:]\iB::=.^\_L5Ac"[]Psm(% +>WcjI/N)=8W0KR2Hn?0KT"8%(kS(=; +jLK'4(Hg$3k#Cs8euY5mG,NE03-]"mOnK9UH!gF#U!>@G_Tgn6LRL0:=A\sQDtIRM +qqV-uhb7)4\'7B@m[,G5oOic0ai9X+;0+T>:.AIB1I'787[Ce`B:NbDb\AeuTqDFb +>?8L\%`4ifbmK]&D0?#Q4[S+'B%F<'fh8]0oW;pa9Z\Zhhhah6ebF"370jD56DJe_ +YW>'lFZMfOZW2NQBXB@nckj[NP&,`+VP:V!,#SfS;,%AuM1bpB&-W_$[8`ZQT:e+dgo10*c%IEB&;c46"A7a1hr_?FdrfRFSkjgUZTZd +bZFBGIc3+92Z?g8U&%G:H4Q/>8Q)$pJs:n>'..WWH%:5DDrMe]$LMV2=mXNOB@[#e +\^TS+Soaq0L+=>3Zhk!Kr?33g7KW`kDj.if<=O4\?FgUU9Z8RL(K-pYj"L=n= +,55ACN-AcDb>W0M+u,,ZVUPpHe` +1W^mD$&Jcsc>_!oJ%_:$=+3?ZIGO@>h`DE]N;gqYaru9H&.2#p.F5QfGl,3&ii)3B +^NOiDODpif"E[h$QLVDi(%(KgfB=M:T"MgA4pXn*\,sI2+@tkdpoCRR/NTPCq#.ah +I"(?6R^RmVQSJ#9#N(dd*Pr\5Ot-i#n2!K5#.QJV?/(i;8Z]%tV4E%!&1EVX05XB0 +6+:#]8V``_X;k#OZm^A0&pMeaI@DiG[Sm7PUj9&h(aAU +1[BbeB??[@D6+epD938k2cj*TmYEgG`O1r+,a>+Ej%':a!^]0Z.#$9US07^1GD8Jj +h;g$&rg&7:6N@"@bDDV_iLQDW\3#lc7G0Y\M`B +%SkG2F:V,D;F0K%@ggS4PE_rH_rVI/l#MD'GoW*KDr$$mms?[:Z!o=r]mFACc%S_? +\+l;qM?=m0gofZO?HX-]DTWrs(Fcc$Z<5mZ,2nO/$V.X.Q +][H2a;&BiG69to+Kt]$Ad!]Lq'm!bG=clRn&or%>.RQiU6C3*&'TQ46Yl,?80%>UK +GK/:R%4>\H:$uugjpZO9^C][Am-^I$?%pZnoB#/lQJi638br>k>Rgcs)t7+\?a:u_ +$fIroRq02IgH[`>T_rDG9#XS"f6&O">#H=2lb$NG1JdTZPF-tpLW6G.+8#:BPu#Q? +F3[9+"AS^up/KB+d-iS@C`?CF9(?AfCY#44/*0?7Q.+Bg12SQemE@_ICS3ZXbi[.; +U0CRc#6r5IaJ/V0!0(@m0o(>n9<#?X'1m`WR0qUG%[DpFCRAFIS>B7lb[iXTmM-N[4SF'b<8'6dtBE-\:]b$\g\h` +QVKet]X0X#:1A\$!sX9nT\hMbW-/Rm*c$DnRsL2eGI6j6C(n04?maC'\:t]'G(L"G +on]0(1OuqO1`M,sSEUBq%2]fD0VVh9kR<.Rk9:QIJs[!?(+H#kh/Ft@N345na,pWM +"GD@>:s(hKFpQ@!WSre/*/i\#';BjUa.nJr18hoN)4fm9_YWJgNAMH!Z5>A:"@f(" +acDQ#\X26mSh/C)6H@4fe=[-$(i1jC7U=lhis[o(fo5%Bn4@]^QS70+rc-jcpYFuV +^^ThC[2.H%k%CRkN"#>^Od>71.o_eI9FpUQP#WCaSBr>*7?uJY7'Jkj%KedEZIEc? +[u'%f?kcWiPMVrEXi,rk#eJTQO21("s*lali*-h$$3UnA69to+AXHe(WNQ_j0YPp! +=Mi-@L^:]4T$6(ef+6*V0^9M)Q9Z6\Vst5K=^[#9SJKY_Ahc3SeV1/NOJJ1%;X*(g +FWSHC>c7?s[_UJ4*UH(9>fP]q^.eRgeI'Tk.$4EOL8@fd!pF*`Rp9.ekWs&Je?1o.6*%N"A`=Z_6!LGg((qbcpY +P!S,'k%9d'8rS45J!eQ/cSi]*Dbg*bf8hcl+11cVHqEWGn?sKO/#%1'XbKiV=$P%L +VL,JG-D'Y/ed+gZNZR$*.0pE9&#`CmdQV2?q%)5GC*MVZuUmD='S +#B/HQ=)4((:E]9R#/b+4]Mg'MLK]HF8")N?J2(FC(_'5:ED?1`9;moM +@A_(bVNB;u\C#2M8j"(Pe1jZX3hUjrZ^H:u&:TEe1fU@^0KUBAoJY#jDutfEls6gO +a3L22j29=p!nP8^19I$a!,Gis'eD:4(3r#IW#8r=)oChFBcY^R>?rjjplmjgLgBuKMjE\,QJfL\&->0a#q4mOMqYNqIi)ZCSFaOneudR9 +s6V3,M +endstream endobj 50 0 obj<> endobj 51 0 obj[52 0 R] endobj 52 0 obj<> endobj 53 0 obj<> endobj 54 0 obj<> endobj 55 0 obj 2428 endobj 56 0 obj<>stream +H‰|—Í®I…÷ýõ]ÎÌȌȔ¾cØ0‹Ñ\‰Ù!tÅØ  {àéùNVW[ÂYxÑ·u*2~Nœx÷Ç¿üû¯_¶w?¾¤íý/Û-í–òðíÛ_/Q}ûòñ–¶¿m·wø9m¿ÞÞ¿ÞÞ½¤?§-o¯¿òÓëÛÆÇ¿øØ^¿n9éïdúÂöžuï-íî{­Ùû–ën6Ì·Þâ£q´ì£4¯ÛëçÛoR*ù·¯¿}xýŸ×îu€8 +@ ¶šsbµÄÖù’Ó¨ ¶½÷šŸˆå‘ ûÖö”Z-‚ôÚZžÕ“m#íÉ{—“}onÝNH»„lÖtíÑêˆéeôvx‘ú6|/ œœó^ÃG>ëb¯)„ËhÅÊôÑFD4Ù^­W¾a‹ó„lW£àAÝ[#¢‚Lu¹ii¤1oa•PæØs±þ„ô5dÞS³éf© ïîmwó2ïíÔR›Î¥®39ÝZ”~bÆf1oc3ÒTJÛî +jËÓÏ‘;ÎŽ( KÙs6¦§_AR{Ôœñ\J.È-ã]ãšAV +yn‘õ­îdð[~Æ$.¥²•=ªuiQ)Ce~Ɇ›ƒ‹ä"S¦œ¿»‚Ä<؉Ѱí^ô¸’Ñð-›ùVöÊ…zpOqB¾¿‚ìFgÜÓž‰NüÆDŒÜCiâDÄØ”û‰ør™ñ:H°BØZªtÝY|fÜ©È•§vëÄôîýéåK̾Ww»ZE P»‚øUªw¡‘ @´½{¤g(?\ æÞmB¶V9* )n´UÌPºBãU Ù•°o-þû%ä€ÈhíNcXÀ?¢©{ +9)/{=í5¥8‰ÈÒUCB•[‚˜·N +ƒDÖ}pß= +,§Þ$áÔÕó’.«×èêÄf·Hk³paRU.L\Cš êáÌ]æ°>»¦Ý 8}6yÃ7Ôé#X™ŽLŠt/gÙaB‚)˜9´Mµ$å”zÄìȪ¦ƒ›º|ƒ× y ^&mC?Ò6yçY%voZ¢Ê/ÅÁ©CEƒLÕú$L» L ÇÀ'îÑ'twÒëH¸W#`3Uì¬"»$Ìjy’¤HSFÃH÷Ž€?ÊL!!”—mOÈ ¾Ä;J¯‹×û1ÍR÷£!ë°±Ð÷Œº¡P?»Ç.è2iĵ¶FÍQó­û¬¡1ÄóEüç‡8ŸcÜ.ø’Z§Rµ39‘ΤL4)u²m´éžÂsÌ9NUÖgzÖ|95…ãƒIF܇jp6dôN˃~rWpÓЬ³yêºyè ¦j¥e(hèÄV‰» ʺ• +)·2lö¸êã„\÷âT:™ˆô´OÖ`rá¤Ø’·Šî`9g×uçpëÞ¨7nMNCˆ\§N{cÊm¥Q·mŽ[·ôíΗB£0’Û>)A¦É¸(×Èê€(K hÄŠ{ëP=/eFÔ6;zÀæ“Ýœ1Ú¦¢°©'v>ï\tçüdµzÙ6„š~cȦSDÏÈi‚¨ˆá̉©3ì››—2#ÿ ‰š%yhîÞ&1X+nFÞ1¨ß C¢ ^êŒ>E®æ-×sÀE¨¿càÒA%˜6©Édèm4ÂÙ8õRhÄI+&oêhê¬ÙÐÚAB…ü–.‚j"M0Ez'eÔK¥ÁäkU#WÅUµc‡x©t:EÔElI$&ÚÉÎ!Q/¥F[MFŸ£ ŠÙíÐXÍyª¡ üŒ'«F<1ÿØ"˜bcÐu\¹©ú§Ø\crO‘pH8ï*Ùæ)62«I"|óÏÛg­=‡´î0òsýaí)EÊ’7Y³‚nü¼´¢F”Øõˤ9¢W¶·Ûò$ƒ·nãVheÈ+Ï~o„IžÖ´»IÀsÑ¥íí¶<08ÄÓ×6ž^ì81ŠöºF–»šleãéåI©I®{hu–6=ý½EýHôÝ JÎmmSÐ'0Ý} hhiãéÕI‚Oþ‹¦ÅÖ„¸°)Ù‹“«òYØÞnŸn¿Rˆ¼tT%RUšØ$U¦¥òÈ3%Ub™ê\Ya[–(©ÕÚyX˜pwq.`äßß™uð“¸uaãáÕÉ^4Ã*4àÒaèÅ¥§W'¾Ì¡ªUÓV&½ùûsÈEvXøêRèS÷,lºó÷V€8-Æ)k'r¡.mo·õIðÊT¿ÇœK±¶ééÅIF`GWH¹ %)±²ñôê¤Æí(Z"Ôì ÄbeãéåÉïkge;JóŸ*åtŸÈ|£"©Ã=A¢'÷ÒYy·i'=!íŽÎˆ†>ÁJ±9Ó«ä‡Kã¢Ý™bÍ d¬H87}Nûqëiçi–™iõQûiE7ÕêQck aN Š&=NKêè4¹l‘¯l5<8ìa-ŽØ9¬Þ¼(|ÜX³­Š2o:³ïI'vЪÂ(•Xm^yï3}˜É€7ˆtšcÊp5 ÕêX%aÙ®½ƒúp/‡•u 7ÑHÔÆa'\&/øZ3ñÀ0%·³w"¶Eûó}½Ô&äÓ§üêb»Z~I¶%W +KÞ)WªªTGrNòϤ˜AÇ£”={éƒ`t›¬–iÆåÉúA]Ö¨÷L{=2!;ÎQsÓŠx÷imŒ-- XÛc«›V(>?üêc8ö\D©ÓZ»Öç¬oýxr. 2éòØ‹ÖÒ™WW%±X`5ÄjNUçz‡Öž.gÂZŒ +mJr5çT˜¬š¬&3Xv›Ó_P_f¤8¼ ¤r3+‡U ÁDP¶k›=A˜ ú‘1Žd#Óý{˜óƒÎf‘7…n›®A•Gy¡Ñhå)ĭÍÒwù¨P§L’G}˜§žžV?šýÚµeM«QégRíÄ`­ýaOˆÀãôY09=Vdtðp##î~°» ˆ¼Q]4ËU¾©ëA #Z-d¤ærŽ¶Knµ„†§Ð¥£3Ùê"¸—A?ÑTåcTWk.è"ŽfÃOÕò°ŸU®m$úãôÐ>(kÒÖA{V6ÐÃìldcÚ}×Î'¯M´d‡•÷ É¿ÍÈÈ +ÃôÃÉzwå‚uîv]…£–YŒîH³z–T,þþÓö>stream +H‰|TkTSWÎM¸'ˆ4cÍC¢7|`U”€¨ciÅ¢*Öѱ:’`‚@ à¨ø@Q$>Zª0õÁ£¢øZŒUk…@ ©€ÏúZî‹×ÌÖêÌü™ûãÜ»öÞgïï|çû.Åqár(ŠúcHÄÜ•Y4>ÜbÐ)Õ#õñÊ„¾¸‚•²Ã³ÜvÅŠ]XÆ]´[?|èi¢Gp8E¼OZݽàÖÀñ)4ºîþ‹û(MQHPU/X­ÖÌWkL:“e¶>‘t^«5É}Tãä~Ó§+&ô­òþ*ùR‹Ñ¤‰7Êç'¨ô†D½AiÒ¨}åòà¸8ydß.£¼4Þ¹‹ÕåJ«è\ÚŽf£ƒ¨½çkùvW‘ë6׶AòA%ƒ^»y»¥ºq»:ø üLpµÐ Úz°ýB±7xìç 5a-ÝpZO í…°åc ýÙ@Kç׋nÃòCgV~[áiEÔT9QwðŸÅ‚õS¦;C[:Àôkh—ð]K¶hÇñš?IÛRp®FVVr´êšä·™µ s+_ÈÚŠª®Þ–Ô&ŸÑ3¥ÑKs¤‹"2¶hdKDÖœ£Ö3RçõuA –­œ,û-piBÂwOêÉã¦ÅúÅw[-2AQz3[c§Ê»xlq§(M»,}™Oðþsß½Åç7­?%û®‘þ»iYþ4)Ö`oì…µØ^Ø"Ôåå” €—acÏ5Pwìà.ÞÝlQFõå7¥0ìméÍ[²Ÿ.{ \ PZGÄæÊbÅ÷øSiHXÆÖe2çBÑÞ£¥{ª¥öØOÙ%à ÆxáˆÝŒn]Öžd™ ,Ë;š@g'­ ­¬X„s@'ì½ÔÇ@vˆWÃÈA‚y„ÿkM`꯻ëá„k°¿ ï|oÇÿB‚ÎmM ·Á~{\ÊÐò.Öý‘ð9ÛïEØ0†ãÕhsŠzk¤+F¿‚/!èýk˜V_½)­\&¼OŽ.|ž“~8HŠÍX†ÇâÕ^å~×–ÊÂn·%½–v¶~—[!<'óZ ,…ÌÐ(úè„pàgO ?.Ã(ã pC¡¹'0¥æ@MO ‰†“k€ +tN9^š‹ÿª ŠOÚ·ßÂlšCo)¬ØV"Wç˜'c—ûóº´tðZ<:àªk†$ÜF¿A8C&TИƒp4Þ—–l1Z’<3¡5 ¦eÙÙ³=Xw;>˾ðGÈ.4³mÔµž9¼GHI$‹y»cG| 1Ø2Ïs'²Ø’›õ1b’ZEÔüåCå¾×{Á%»Í3™Q<åÔxàâfñmƒá^i÷?Úµ’Ì´}íÈÞdò5³·Þg¥ myOWÂÎò…qà²ëþövƺ»dVõçgzB,~Jf¦ãr$<ì=ý?&sWœMe[éðÄëà =þc A.Iod/7Råìœnt§‹Èí„!°VáÁ0‡ãP,Ær‹×ÀÌö¬"<"†Œi)Œ†Ñ6R Áƒ°32‰Üäao äB†³Ÿß£h{Á±]ÅÒö»E‰Ëò/ÜüdºsŠ)W…L‘Îý*c«RÖ.²æßsZúìæ¿ÉaËGê Ù{SeÿAÙÅÎéâõŒNE˜•zØu\ÎÜ.Kàk‚× ‡áxYŽ'²tƒ)Ì¡CÛ·çÊZMì4ºâJðïÇ/ï>üÃa,xÛÈéïøÓþêÛ°¯þ¿ðW?@ŽÌ´¾ùSs—Lu}Œ6¯¯K~’f‹»°"ßÕÊ·»zÝ)©M©‰=ÎœVFäûJ/ÌØ%ëˆí!Gª”vÝÔ|±x…¯LáévöBõ£â¼Ê‘±¼1¥N +þ-o_Fþ£j™oyôÉ›’š²+]7ªS72Çéà +òæIñ°€ ¯‰õK€«’uêÊcÂ%!ËW+-=R¤fKˆ Ÿ7S°†x`‡“}Ñͽ;‚A‰]„ÁýÞjðMïH>Žy Œª T Ëø÷ŽâcŸó}üBoÁg ;Š/°f=éóÉP+{E˜vÈ ÒöEáG´ð‡8ÿÄÀÔ ×(µÕðPßéíâ ’ˆ¶P•õj†™­C´šÛsK¸Nx<ƒ·´P[Ô‘Ww°Þ5};/g~a@;~#îD°”ôU ‡ÌSSgn&m77Z©÷§ÃSñd䇻Mæé¡ÄK›ê,SÚgÀñ$_öË°s›ÏcÏyàx J¸OA“±Â°&-.U噉,å©¥†*(Äc¬Äˆ½ŸÐ¿¡ûàSz½ üH±Z¡º0¦,² 6 ´3aóŽœáC2;Œ‡>ÃI+ŒQ–(Ä|Þ|ÖpyDˆÇ"ˆíÓ¸‚/ØI~Evê›Î;áÁ¦Û{?±"Ö•­¤k{¿žO3ù½n½U´`Vz]¬²n€ nh™¢œÂ‹¬.‰ž¯}…ÇE,N3¨˜‚ÅôÑÒó‡j¤/wœ¥’læãE»½âA’¹—B^4Ô–ž.f¬Hø·o"hÚ^²ól¥dÊy‘ðâ–]›3·0Ñ¢U ¥Qšs]µ1MQ˜®Ü°«‘ëåG/¹×ÈGœ.[W1 g]Õ¸L£e€#¶ÒÂ(¥Õd®íZŠDJ 5 g¢å£°Ñ1QL¡|¬î d6E2öémçvoö³?KÞ¿çä<ÏyÎyÎÛ9¶<çÏFVìÐÏWs»f ´:Ö€ž%ÒムY—oRJ˜o¤§qž°ö_»Q>KC<æ‡ 1õw$Ê8¬Ì.cpS*„vSKKƒ™rÞ§W>Û‘‚øÙ™Ûä LÔ²Æf“Í&o‚ãÍò\K|.i‘ÒW–èré-{f òáø"ð‡»uú¶ÝKØOäÙs¶x(¥ü” ‰“#NÛV|V?Wô_±®%¸¦')¨ P¿QèRçödÒ(:1m7cGYN†èïÇúG/1L‡5Óaè6yÜîžP²RZ“ΠcH\“]ø2½U±ðÛŸ£óËSÃyYv¶AgÖê$âì“3«é#V#ž×G@Âmžë>4ßçs ¦,ÍfÇ9ÉR°èYôÄsHˆ=lóƒdHþvÔéèb4_õ¥Eú´4Ó댬ù65Ñäº÷€þõCi–‰#,NNæ…´9ÞÖRF'ƒ(/ŠîÞmŸºÛé<ýžƒ±MMµM¥tá±úÒ¶ Hsp¯¤jß>oH&[ÑñxÒ Oë׸¸Àæ«?(rá soÃ&C@þbØC}ª£îTEnzí@´3ˆ¾¾~u¨“µ¼*@ë_zý™C¹ç:JÕ^¢¢÷ŽvŒž˜nu³Ÿ¸/x!R"…xJc¬=­e^yW¡ÌÂ4ͯ1>÷ÃäÀ9m¬Ug®×JÄ`,i>ÜV>À÷áSaHà4Û[˜aç`ß-zñ²|'‹ºØ— =¡O^RvJSÁ”kUUÚÊ(#ÜÀ†‰sÔTˆØq ’ä ‹ãj¤¡!ùÐÍÍR=ç;»ºT•ÊªÊ²²óݘÃIT§þ`[ðo +endstream endobj 59 0 obj 399 endobj 60 0 obj<>stream +H‰\“Íjã0ï~ +ÛC±cý´`Hrض4í8ö—¬a#Å9äíWÑ„j°a3âs^oVßO*C»•Ií{ß9 çЊÚÉ¡÷Ù¬T]ßN7JßöØŒY<»½œ&9nü~È •ĵÓ.ênÙ ;¹Ïò·ÐIèýAÝ}ÕÛ{•oÏãøOŽâ'U¨ªRì³¼þÓŒ¯ÍQTžŽ=lº¸ÞO—‡xægÇçeU&žáҜƦ•Ðøƒd‹">•Z¬ãSeâ»_ëzαݾýÛ„ëör·…™U‰J¨„4¤!ÈBrƒ¡Gè z‚æÐZBKèz†j¨†VÐ +z^ 5´N¤‹D¶€è³ôiú,}š>KŸ¦ÏÒ§é³ôiú,}š>KŸ¦ÏÒ§é³ôiú,}‡‹ÁÅábpq¸\.‡‹ÁÅábpq¸\.‡‹ÁÅÝ\¸kÇ]îÚq׆»v«4B·Y¹Syõ=©í9„8¤é¿HÓyËÞË÷¯3£Š§®oö_€ÎÖî +endstream endobj 61 0 obj<>stream + + + + + +JPEG256104/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAaAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYqoXmoWVmnO6nSEEOyhiAWEaF34r1biikmnbJRiTyVLb3zdoVpBNM85kEFtDeuI0Yn6tc OUjlUkBWBKmtDUZOOGRPxr4otQtvONrd3mnw2ttK8F/dXdpHcsVVf9DRy8qAFuSM8ZQdPHpkjgIB s8gD81tHP5h0tNfGhGQ/pD6q1860+FIFcR8mbtVjtkPClw8XS6W0dbXVtdW8dzaypPbyqGimiYOj KehVlqCMgQQaKVTArsVdirsVdirsVdirsVdirG/8e6FFLqa3jm2XTrpbOhrLNLIVDVSCIPLTrT4d wCegzI/LS2rqL/BRaTf8rEkh19nWJ73y9eWunXdvcp6afV47yV7fmykCR+UvCo/Z3+m38rce6QJH vrdHEh/M/nu706PU0t78rcwm/S3WS3iWNHiFtHEGkMjGiSXQIYp8W4IHHeWLTiVWO7r7/wBSkp3c efdOhk0p2YR2F5HcXFzeXAMQS3g4xrMFPRZZZUCluoOUjTE33ivmm2S2l3aXlvHc2k0dxbSjlFPE wdGHirKSDmOYkGilVwK7FXYq7FXYq7FXYq7FXYq7FXYqxzzB53sNKN7b28E2o6jYQNc3FrAj8Y0C GQGWbiUTkq7Dqewy/FpzKidgUEpOPOGo3t5Db/pGzs47O1bUNWlsAdQJX1QqW8fwggiNg0h4ctxQ DLvAAF0TZoXt8UWxy9u9Ru3vbnT5YbnSNLmi8y2UUkcrXU1rd8vrESVYKqD/AEhWHE9hQZfGIFA/ UfT5WOX6EK0Ogapd2yaIlpcFotI1PSJLiSN0geBZF/RrrMwVCSB0rUb1AGA5Ig8Vj6on/ilpNtI8 r+cNMbSYreC2ntNHLS2aXVw0TKlzAUkt2eOO5JMMjHi1DVdq7ZVPNjld3cvLuPPpzSAWNeafLHm9 9T1PUJI/Rl1i2+rXEkTc97m6EFvZxMN+IWOJ5TQDjy/m2yMOXHQH80/cLJ++mJBZBb+d7ew1TS9G 0oLH5X022f61qLqWNwlsogRLZRu1Z3VQwrzaoXpvQdOZAyl9ZPL377/BlbP7O+guowUPGUKrTW7F fViLioWRVLcWzClEhkiMirsVdirsVdirsVUb2+srG2e6vbiO1to6epPM6xxrU0HJmIA3wxiSaG5V 5h5u8y+ZIbnX/wBHeoNOa4t7GpuSksV2gik/d0AMEN3E3pKwb7fxfDXNjgxQIjfOr5dP0kc/cwJQ 2k+V9aadNUtfXSaDjeXd9d2c8bG7tgzRgQyEXdwrQXElu7AcmIVtz1lPNGuE+7Y9D9g3FqAnnk3y X9b0Wc6v9YQyGW1gBRrUm2W6N1G4ikBljpKzcAxrxpUZTnz1L019+9UkBV1H8totU8y3c12XXR5K SujMj/WJZZ4ZpU4hQVjH1RBuanfBHVcMBX1f2/rXhan/AC+1b6xYzzXqajFpltbQxwOnptOLP1ZY 0epZRzn9Esa78MRqY0RVWT9v7L+a0q/l/wCUrjRtTvJZYPQK2tvbzyqKLdXTlri5n26hXmEaeFCM GpzCYHvPwHIKAznMNk7FXYq7FXYq7FXYq7FXYqx7zF5207QrwWtxbXM7i3+tzSQrHwigEgiLsZHQ mjsKhAxA3pl+LTmYsEdyCWKXM2q6l5c0bzHrWpSTaLeTRy6lp9oZLRIbe7X01BlgZJnEErKX5N/N 7ZkgRjOUIj1DkTvy/WxQQsdUh8zXEkKX+s2+m3f6Nu1tZuMlzEYRdWguZCyCQQPK8MpkbcEcq7jJ 8UTDpGxfu6GvfzCsl8veQzb6Hp8MzNpOo6fJM9ncWDR+tHDO3L0Zi8ckUtF4q9VIPEHrmPl1FyP8 QPekBkOheXdN0W0jt7VS7xh1N1NRpmEkjStycBdjI5biAF8BlGTKZmykBM8rS7FWnRJEaORQ6OCr owqCDsQQcVed+dfJyQXn6fhM7Rg2liIbSNibCwRiJ5YI4gzF+LsAVWqBiRmfgz2OH3nfqfNiQxW+ 826TpXmj655YtLXSrTRbKRJoLtDbTaiZnjYRLGSkxegDK0gJFeTChFciOGUoVMmRkem9MbetaJ5o 0XWi6WEzNKiJK0UkckLem9QrqJVTmhIIDrVffNbkwyhzZgprlSXYq7FVO4uLe2ge4uJUhgiBaSWR giKo6lmNABhAJNBWJ3v5jeWLiHUbW2e7uhDEKy2KMDIsvqIXtpaoCI/SesgIUEbNXMmOlmKJoe/9 LHiYHdazruoQ2SDXfr10L2ym0O3eBTDeWquiR3AIX1PWjk5/WVLUG+w+E5miEY36aFG/I93u7mNv RbbyjHqVw+o+Z7O0k1BnWkNpJObcxxAemJ1b00uCrcmBkj+GtB0rmAc3CKgTXnX4DOmT5jpdirsV dirsVdirsVdirsVdirsVdirsVSfzlp36S8p6xY0q09nOsff4/TJT/hqZbglwzB80Hk86ttJ1rzB5 e0mO+0k3z2axX2ktNJyjudOmKq9pcXAHFZVQq4r9rip6hszzOMJGjV7HyPeAx5sl8vfllFZ6RBY6 lqN3cW5Dtd6Uk1bJnl5chugmZfi3q9GPxEb5j5dXcrAHv6/qSIsu0zStO0uzSy062jtbWP7MUShR U9SfEnuTucxZzMjZNlkisirsVdirsVdirsVYd5z8vWtv5aC2Nj69pa3qahqNon7yaeES+rchWkNW d+4LfEPhzKwZSZ7ncih+hiQw/wAw67fXOrx6uYrqC1vILZWTTp4RdQ6a89LcmVZFRZLy6kUfBIeM anrvmXixgR4drF8+V9fkPtQSm2jebvO1l5XfU9RFnqL6fL9S1CwcyW15FOsohRTKFljleXmjfYQf FscqyYMZnQsXuDzH4+aglkmkeftPv08vLNbS2lz5jjuJbKJijqotgGPNw37amq0HzpmPPTEcW98N fayBSC7/ADbt5orQ2EXorevd2vrTfE0MyIjWchQEAxzeqvUjqB45fHREXfSv2/JHEx63v9e85Wut Wn6Sa11qfT4PQ0p3LWdzBPZJI7RoaBHYypJVfiSoDVXLzGOIxNXG+fUbseae6r9d197ebS9OlvbT X9Eawlk5C3ht3WX4vWejFDHzccFUkkUp1ymFQ+o0Yyv3p5sw8seWLfRLRlMjXV7NJLPcXT0BMk7B pOCqFSNTxXZFFaVO+YuXMZnuDIBOspS7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXEBgVYVB2IPQj FVlvbwW8EdvbxrDBEoSKKNQqKqigVVFAAMJJJsqvwK7FXYq7FXYq7FXYq7FXYqlOteWNK1aze2mV rdmaKRbm2IimV4DWFgwG/pk1XkCB4ZbjyyibQQlNz5GLJbwx3kk6y6iupatd3JUzytDEVhCiJI4x xdI9qAAL45YNR5dKH4+aKYpP5C83y6VaOIYbfUvLNjHa6H6Uof6xPHKjvKeQQRrLHFwozdWPQZkj U4+I90zv5IopzL+VlrOdJrIIYYNKOl6nEBvLSH04pFptzjcluXyyoawi/wCtYTwp35f8i6RpNtAk g+u3EJtnS4lVQUktbWO0V4gN0rHCK/Eep7bZVl1EpHu5/abSAyPMdLsVdirsVdirsVdirsVdirsV dirsVdiqW+YdfsdD01727NafDFEPtSOeij+OY2q1UcMOKX9rOEDI0Hmg/OLW0vA0lrbtbE7wgOG4 +z8jv70+jNJi7XzS9VDhcz8pGnpmga/p+uael7ZPVTtJGftxt3Vhm90+ojljxR/scOcDE0Uxy9g7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUNqWo2em2E9/eSCK1tkMkrnsB 4eJPQDDGJJoMoxMjQeT/AJd/mFeax+Yd79cYpBqsRS0grUR/V6vEn/AF6+LHM7PgEcYro7DUacRx iuj2HMB1rsVS6y1/TbzU7zTYZD9bsSBMjClQQDyXxAJoff6Mx8ephOcoD6oszAgA96Y5kMHEgCp6 Yq8O8+eZTretP6T1sLWsVqOx/mk/2RH3Uziu09Z42Tb6Y8v1uyw4+EebD7xuMq/6v8cyuzYcWM+9 yYp75L82XWgaotwhL2slEuoOzp4j/KXqDlwnLBPiHLqO9qzYhIPfrK8tr20iu7ZxJBMoeNx3Bzos cxOIkORdUQQaKtk0OxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVRtry1uldraVJljcxuyEMA69VqO4 rgBB5IEgeSthS7FXYq7FWMeavzB0XyxdRW+pwXX79S8M0UatG1DRgGLDde4y7HgMxs34tPLILFJH /wArz8lfyXn/ACKT/mvLPyc/Jt/Iz8mBfmf+ZkXmWODT9LEsWmJ+8n9UBWkl/ZBALfCo/H5DMnT6 fg3PNy9LpuDc82E6PqU2l6rZ6jD/AHlpMkyjx4MDQ/PpmTKNghypx4gR3vq3TtTsNStUurGdLiCR QyvGwbqK0NOhzSSiQaLoJRMTRROBi8X853F/oXn24v7RykvJJ4j2ZXQclI7gmoOcpq5yw6oyHvdj hAlCi9T8teYbPXtLjvrY0b7M8JPxRyDqp/gfDOk0+eOWPEHCyYzE0WO/mX5sj06wOk2sg/SF4v72 h3jhOxPzfoP9rNf2xqjjx8Mecvs/tbdPis2eTyHOQc9AaoaGP6f4Z0PYUbE/h+lsgoQSkEZsNTgs MiHoP5e+ezosv1O9Zn0uY123MTn9oD+U/tD6fnr9JrDp5cMvoP2OHnwcW45vYrLUbC/gWeyuI7mF hVZInVxQ+4JzpiCHXSiRzRGBDsVdirsVdirsVdirsVdirGPzIhum8pXVxaSPDdWTJcwyRsVZeDUY gjf7DNlGpB4CR0cfVA8BI5h595d/OTWLPjDrEQ1CAbestEmH/GrfcPnmHj1kh9W7g4tdIfVuzz/l aPk39GNfC8NRt9U4kTlvDh/GvH3zL/NQq7c383jq7eZebPzP1vW+dtak6fpzVHpRn946/wDFjinX +UbfPMHLqpS2Gwdfm1cp7DYPTPyqt/R8kWLHZpmmkP0yso/BRmdpRWMOfoxWMMtzIcp2KuxV2KpN 5s8r2HmXRZtNuxxLfHbz0q0Uo+y4/UfEZZjyGBsNuLKYSsPmTWtGv9G1OfTb+P07m3biw7EdmU91 YbjNvCYkLDvITEhYQOTZuxVWtL29s5hNZ3EltMOkkLsjD6VIOAgHmgxB5vVfy28y/mlrF0scMyXO mREC4u76PkqjuFdODu9O3I++YWfHjiPN1+pxYojz8k3/ADjsKXOn34H94jwOf9Qhl/4mc4ztzHUo z+H4+bTpJbEMF0fzFrOhyTSaZcGBpl4SbKwNNweLAio7HMPSamWP6TzcqWMS5pRfXN3dXUl1cSvL cyktJKxJYk++bDHkE/q3vvbIgAU3BfKTwmorU+2dgf6Zg6vsogcWL1Du6j9f4581MO5R1jZYm9yP vpmZ7ObymPd+lONL0m4+JPgM6TJp+LyDbStxuJ14seMZ/ZHf55hcWDBLiiLn3/q7vv8ANjsHuP5X r5U0fQkhi1G1bUrukt5WRUflT4YwGoSEG3zrk46qMtzIX73V6kzlLlsz5WV1DKQynoQajLgbcRvC rsVdirsVdirsVdirsVUNQtI72wubOT+7uYnhf5SKVP68EhYpjKNgh8uzRPDM8MgpJGxRx4FTQ5oS KeeIpZgQ7FX0j5IiSPyho6oQwNrExINd3XkR9BObvAPQPc77Tj92PcneWtzsVdirsVdirxD8/byz fWtNtI41+twwM88wHxFJGpGhPtxY/Tmx0QNEu00APCT0eWZmuwdir0D8rPy7s/MzzX+ozf6BZyCN rSM0eRqcvib9lPlufbMXUZzDYc3D1WoMNhzL3yzs7Syto7W0hSC3iHGOKMBVUewGawkk2XUGRJss N/MDSfMGvRwWVjprCK3kZzcyywqG24jgA5ahrXcDNJ2nhy56jGOwPMkfrcjBKMdyWEn8r/OB/wCP VP8AkbH/AM1ZrY9l6gdPtDk/mYN/8qm82soJihUnsZVr+FcyYaDOOg+a/moJPrv5d+ZdLj9a4tC8 IFWmhPqKv+tx3X6RmQJ5cH1A13tsNRE9WLy2t04WEmsamoJqSNqUzK0+u08Ccg+uQrpvvd+/zbxI K0FhGnUV+eYWp7WnPkgzRSxqNqZqZ5pHqwtUVRmPLIUM18heUNX1R/ri3U1hpyNQzRMySSMOojp+ LfrzP7O0WTKeKzGHlzPucbPlEduZevWdolrAsKPJIB1eaRpXPzZyTnVY4CIrc+824BNq2TQ7FXYq 7FXYq7FXYqwf8xvzAj0K3bTtPcPrEw3PUQIw+23+Uf2R9J98XU6jgFD6nD1Wp4BQ+p4a7u7s7sWd iWZmNSSdySTmqdOtwK7FWVeSPP2oeWrgRNW40qRqz2td1J6vHXo3t0P45kYNQYHycnT6k4z5PdtJ 1fT9WsY76wmE1vINmHUHurDsw7g5toTEhYdzCYkLCMyTJ2KuxV2KvmXzTLqfmzzpqVxptvLemSYp AkKM/wC6i/dofh6Aqlc2+MCEBbvMQGOAvZP9G/I3zReRGXUJodNqpKRMfVkLU25BDxUH/Wr7ZXPW RHLdqnroDluwzzD5Z1ny9fGz1S3MUm5jkG8ci/zI3Qj8fHL4ZBIWHJx5YzFh6x/zj+6nS9XT9pZ4 mI9ihA/VmFreYdf2hzD1bMJ17sVdirsVdiqR6p5I8r6nMJ7qxT1Qas8ZaIt/rcCtf15h5NBhmbMd /Lb7m2OaUeRYT5p/Kh4Ua60EtKi1L2Tmrgf8Vt+18jv881Ws7KlH1Y9/L9TkY9T0k87eGSN2jkUo 6EqyMKEEdQQc0MpVsXLtlfkzyJe63OlxdI8GlLu0xFDLQ/Zjr1927Zn6Hs6Wc3LaH3+5oy5hHlze z21vBbQR29ugihiULHGooAB0AzroQEQANgHXk2qZJDsVdirsVdirsVdiqE1UaodPmXSzEL9lpA05 YRqx25Hirk060pkZ3W3NjO69PN43qH5TeeZriW4meC8nlYvJKJjyZietXVOua2WkyE3zdVLR5Cb5 pLqf5eecNNtpbq608i2hUvJKkkTgKOporFqfRlUtPOIshpnpskRZDHMoaE68r+UtW8yXUtvp/pr6 Kh5ZZSVRQTQAlQxqflluLCZmg24cMshoM7sPyObZtQ1QDxjt46/8O5/41zLjoe8ubHs/vLNvK3kf SfLZkawluHaYUk9aSqmnfgoVa+9MysWAQ5OXh08cfK2Q5c3uxV2KtOiujI26sCCOmxxVRsdPsbC3 W3sreO2gX7MUKKi/coGEyJ5plIk2VfAhA6zoela1YvY6nbrcW7/st1U/zIw3Vh4jJQmYmwzhMxNh IPInkUeU59UjhuPrFlePE9sWFJFCBwVemxpyG46+GW5s3HTbnz+JXeGW5Q47sVdirsVdirsVdiqE uNI0m4n+sXFlbzXA2E0kSM9B/lEE5VLBjkbMQT7mQkR1ReWsXYq7FXYq7FXYq7FXYq7FXYq7FVsk aSRtHIoeNwVdGFQQRQgjFSHzr548sv5e1+ezAP1ST97Zue8THYV8V+yc0ufFwSro6LUYuCVdHrf5 V6B+ivK8U8i0utRIuJD3CEUiX/gfi+nNjpcfDD3uz0ePhhfUsxzJcp2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ksf84eT7LzJb2kc54SWsyyB+5iJ Hqx/7JfxplObCJ00Z8AyAX0T9VVVCqAqqKKo2AA7DLm9vFXYq7FXYq7FXYq7FXYq7FXYq7FX/9k= + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj xref +0 62 +0000000004 65535 f +0000000016 00000 n +0000000076 00000 n +0000000126 00000 n +0000000008 00001 f +0000000270 00000 n +0000000290 00000 n +0000000714 00000 n +0000000009 00001 f +0000000010 00001 f +0000000011 00001 f +0000000012 00001 f +0000000013 00001 f +0000000014 00001 f +0000000015 00001 f +0000000016 00001 f +0000000017 00001 f +0000000018 00001 f +0000000019 00001 f +0000000020 00001 f +0000000021 00001 f +0000000022 00001 f +0000000023 00001 f +0000000024 00001 f +0000000025 00001 f +0000000026 00001 f +0000000027 00001 f +0000000028 00001 f +0000000029 00001 f +0000000030 00001 f +0000000031 00001 f +0000000032 00001 f +0000000033 00001 f +0000000034 00001 f +0000000035 00001 f +0000000036 00001 f +0000000037 00001 f +0000000038 00001 f +0000000039 00001 f +0000000040 00001 f +0000000041 00001 f +0000000042 00001 f +0000000043 00001 f +0000000044 00001 f +0000000045 00001 f +0000000046 00001 f +0000000047 00001 f +0000000000 00001 f +0000000825 00000 n +0000000847 00000 n +0000026743 00000 n +0000026876 00000 n +0000026900 00000 n +0000027197 00000 n +0000027265 00000 n +0000027460 00000 n +0000027481 00000 n +0000029981 00000 n +0000030002 00000 n +0000033227 00000 n +0000033247 00000 n +0000033718 00000 n +trailer +<<9f8f33a8150b0f4ab7115312b3461624>]>> +startxref +46290 +%%EOF diff --git a/demo/pdf/main.cpp b/demo/pdf/main.cpp new file mode 100644 index 000000000..ca4c98d19 --- /dev/null +++ b/demo/pdf/main.cpp @@ -0,0 +1,624 @@ +#include +#include + +#define DATASOURCEDIR "D:/otm/mapnik/trunk/msvc/Debug/" +#define REGISTEREDFONT "D:/otm/mapnik/trunk/fonts/dejavu-ttf-2.14/DejaVuSans.ttf" +#define DEMODIR "D:/otm/mapnik/trunk/demo/" +#define DEMODATA DEMODIR"data/" + + +//mapnik +#include +#include +#include +#include +#include +#include +#include + +//pdf renderer +#include "pdf/pdf_renderer.hpp" +#include "pdf/pdf_renderer_layout.hpp" +#include "pdf/pdf_renderer_utility.hpp" +#include "pdf/pdf_point_symbolizer.hpp" +#include "pdf/pdf_polygon_pattern_symbolizer.hpp" +#include "pdf/pdf_line_pattern_symbolizer.hpp" +#include "pdf/pdf_shield_symbolizer.hpp" + +// WX +#include + +using namespace mapnik; + +void demo(void); +void fancy_demo(void); + + +int main(void) { + demo(); + fancy_demo(); + + return 0; +} + + + +void demo(void) { + std::cout << "demo\n==============================================================\n"; + + //--------------------------------- + //Setup + + //register datasources + datasource_cache::instance()->register_datasources(DATASOURCEDIR); + + //register the font + // note: pdf output uses its own fonts, which are either the standard + // pdf fonts (arial, courier, times, symbol, zapfDingbats) or external + // fonts (see pdf_text_renderer::set_external_font_options). + freetype_engine::register_font(REGISTEREDFONT); + + + //--------------------------------- + //Map creation + + //create an empty map. + // - The dimensions given here are in pixels for the normal renderer, + // and 'pdf-units' for the pdf renderer. See pdf_renderer_layout + // below for more information on the pdf-units. + Map m(150,200); + + //background colour + m.set_background(color_factory::from_string("cyan")); + + + //--------------------------------- + //Create styles + // Note: dimensions given here will be interpreted in pdf-units rather + // than pixels for the pdf output. E.g. a line width of '5' cound be + // 5mm, 5in, or 5pt depending on the pdf-units. + + // Provinces (polygon) + feature_type_style provpoly_style; + + rule_type provpoly_rule_on; + provpoly_rule_on.set_filter(create_filter("[NAME_EN] = 'Ontario'")); + provpoly_rule_on.append(polygon_symbolizer(Color(250, 190, 183))); + provpoly_style.add_rule(provpoly_rule_on); + + rule_type provpoly_rule_qc; + provpoly_rule_qc.set_filter(create_filter("[NAME_EN] = 'Quebec'")); + provpoly_rule_qc.append(polygon_symbolizer(Color(217, 235, 203))); + provpoly_style.add_rule(provpoly_rule_qc); + + m.insert_style("provinces",provpoly_style); + + + // Provinces (polyline) + feature_type_style provlines_style; + + stroke provlines_stk (Color(0,0,0),0.75); + provlines_stk.add_dash(8, 4); + provlines_stk.add_dash(2, 2); + provlines_stk.add_dash(2, 2); + + rule_type provlines_rule; + provlines_rule.append(line_symbolizer(provlines_stk)); + provlines_style.add_rule(provlines_rule); + + m.insert_style("provlines",provlines_style); + + + // Drainage + feature_type_style qcdrain_style; + + rule_type qcdrain_rule; + qcdrain_rule.set_filter(create_filter("[HYC] = 8")); + qcdrain_rule.append(polygon_symbolizer(Color(153, 204, 255))); + qcdrain_style.add_rule(qcdrain_rule); + + m.insert_style("drainage",qcdrain_style); + + + // Roads 3 and 4 (The "grey" roads) + feature_type_style roads34_style; + rule_type roads34_rule; + roads34_rule.set_filter(create_filter("[CLASS] = 3 or [CLASS] = 4")); + stroke roads34_rule_stk(Color(171,158,137),1.0); + roads34_rule_stk.set_line_cap(ROUND_CAP); + roads34_rule_stk.set_line_join(ROUND_JOIN); + roads34_rule.append(line_symbolizer(roads34_rule_stk)); + roads34_style.add_rule(roads34_rule); + + m.insert_style("smallroads",roads34_style); + + + // Roads 2 (The thin yellow ones) + feature_type_style roads2_style_1; + rule_type roads2_rule_1; + roads2_rule_1.set_filter(create_filter("[CLASS] = 2")); + stroke roads2_rule_stk_1(Color(171,158,137),1.8); + roads2_rule_stk_1.set_line_cap(ROUND_CAP); + roads2_rule_stk_1.set_line_join(ROUND_JOIN); + roads2_rule_1.append(line_symbolizer(roads2_rule_stk_1)); + roads2_style_1.add_rule(roads2_rule_1); + + m.insert_style("road-border", roads2_style_1); + + feature_type_style roads2_style_2; + rule_type roads2_rule_2; + roads2_rule_2.set_filter(create_filter("[CLASS] = 2")); + stroke roads2_rule_stk_2(Color(255,250,115),1.5); + roads2_rule_stk_2.set_line_cap(ROUND_CAP); + roads2_rule_stk_2.set_line_join(ROUND_JOIN); + roads2_rule_2.append(line_symbolizer(roads2_rule_stk_2)); + roads2_style_2.add_rule(roads2_rule_2); + + m.insert_style("road-fill", roads2_style_2); + + + // Roads 1 (The big orange ones, the highways) + feature_type_style roads1_style_1; + rule_type roads1_rule_1; + roads1_rule_1.set_filter(create_filter("[CLASS] = 1")); + stroke roads1_rule_stk_1(Color(188,149,28),2.2); + roads1_rule_stk_1.set_line_cap(ROUND_CAP); + roads1_rule_stk_1.set_line_join(ROUND_JOIN); + roads1_rule_1.append(line_symbolizer(roads1_rule_stk_1)); + roads1_style_1.add_rule(roads1_rule_1); + m.insert_style("highway-border", roads1_style_1); + + feature_type_style roads1_style_2; + rule_type roads1_rule_2; + roads1_rule_2.set_filter(create_filter("[CLASS] = 1")); + stroke roads1_rule_stk_2(Color(242,191,36),2.0); + roads1_rule_stk_2.set_line_cap(ROUND_CAP); + roads1_rule_stk_2.set_line_join(ROUND_JOIN); + roads1_rule_2.append(line_symbolizer(roads1_rule_stk_2)); + + roads1_style_2.add_rule(roads1_rule_2); + m.insert_style("highway-fill", roads1_style_2); + + + // Populated Places + + feature_type_style popplaces_style; + rule_type popplaces_rule; + text_symbolizer popplaces_text_symbolizer("GEONAME","arial",3,Color(50,50,100)); + popplaces_text_symbolizer.set_halo_fill(Color(255,255,200)); + popplaces_text_symbolizer.set_halo_radius(1); + popplaces_rule.append(popplaces_text_symbolizer); + popplaces_style.add_rule(popplaces_rule); + + m.insert_style("popplaces",popplaces_style ); + + //--------------------------------- + //Create Layers and assign styles + + // Layers + // Provincial polygons + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"boundaries"; + + Layer lyr("Provinces"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("provinces"); + m.addLayer(lyr); + } + + // Drainage + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"qcdrainage"; + Layer lyr("Quebec Hydrography"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("drainage"); + m.addLayer(lyr); + } + + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"ontdrainage"; + + Layer lyr("Ontario Hydrography"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("drainage"); + m.addLayer(lyr); + } + + + // Provincial boundaries + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"boundaries_l"; + Layer lyr("Provincial borders"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("provlines"); + m.addLayer(lyr); + } + + // Roads + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"roads"; + Layer lyr("Roads"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("smallroads"); + lyr.add_style("road-border"); + lyr.add_style("road-fill"); + lyr.add_style("highway-border"); + lyr.add_style("highway-fill"); + + m.addLayer(lyr); + } + + // popplaces + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"popplaces"; + p["encoding"] = "latin1"; + Layer lyr("Populated Places"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("popplaces"); + m.addLayer(lyr); + } + + + //--------------------------------- + //Set map visible area + +// m.zoom_all(); + m.zoomToBox(Envelope(1405120.04127408,-247003.813399447,1706357.31328276,-25098.593149577)); + + + //--------------------------------- + //Render to a png (you'll need to change the font in the popplaces style for this to work + +/* Image32 buf(m.getWidth(),m.getHeight()); + agg_renderer ren(m,buf,0,0); + ren.apply(); + save_to_file(buf.data(),"canada2.png","png"); +*/ + + //================================= + //Render to a pdf + + //--------------------- + //create the page layout for the pdf + + //this specifies the units used on the page, and dimensions of the page in those units. + pdf_renderer_layout page_layout("mm", 210, 297); + + //set the location of the map within the page + page_layout.set_map_area(25, 40, m.getWidth(), m.getHeight()); + + page_layout.set_map_area_border(true); //Draw a border around the map + page_layout.set_map_area_border_width(0.75); // - width of the border + page_layout.set_background_colour(Color(255, 255, 255)); // - colour of the border + + page_layout.set_map_grid(true); //Draw a grid across the map (on its own layer so it can be turned on/off) + page_layout.set_map_grid_colour(Color(0,128,255,200)); // - Grid colour + page_layout.set_map_grid_linewidth(0.33); // - Grid line width + + page_layout.set_border_scales(true); //Draw scales around the map border + page_layout.set_map_grid_approx_spacing(20); // - approximate length of each grid segment, this will be tweaked to get nice units (e.g. 20mm may equate to 5832.78 meters, so it'll be changed to something like 22 to give 6000 meters) + page_layout.set_border_scale_width(3); // - width of the scales around the border + page_layout.set_border_scale_linewidth(0.25); // - line width of the border scales + + page_layout.set_scale_bar(true); //Draw a scale bar + page_layout.set_scale_bar_area(Envelope(50,250, 170, 257)); // - position and size of the scale bar. The width will be tweaked to give proper units. + page_layout.set_scale_bar_factor(1,"Meters"); // - Give the scale factor and label. E.g. if the map projection outputs in meters, the scale factor is 1, but if you want kilometers, specify 0.001. +// page_layout.set_scale_bar_factor(0.001,_T("Kilometers")); +// page_layout.set_scale_bar_factor(39.3700787,_T("Inches")); +// page_layout.set_scale_bar_factor(0.000621371192,_T("Miles")); +// page_layout.set_scale_bar_factor(0.546806649,_T("Fathoms")); +// page_layout.set_scale_bar_factor(3.2808399,"Feet"); + + //--------------------- + //render the pdf to a file + render_to_pdf(m, page_layout, "demo.pdf", false); + render_to_pdf(m, page_layout, "demo-compressed.pdf", true); + +} + + + +void fancy_demo(void) { + std::cout << "fancy demo\n==============================================================\n"; + + //--------------------------------- + //Setup + + //register datasources + datasource_cache::instance()->register_datasources(DATASOURCEDIR); + + //register the font + // note: pdf output uses its own fonts, which are either the standard + // pdf fonts (arial, courier, times, symbol, zapfDingbats) or external + // fonts (see pdf_text_renderer::set_external_font_options). + freetype_engine::register_font(REGISTEREDFONT); + + + //--------------------------------- + //Map creation + + //create an empty map. + // - The dimensions given here are in pixels for the normal renderer, + // and 'pdf-units' for the pdf renderer. See pdf_renderer_layout + // below for more information on the pdf-units. + Map m(800,500); + + //background colour + m.set_background(color_factory::from_string("cyan")); + + + //--------------------------------- + //Create styles + // Note: dimensions given here will be interpreted in pdf-units rather + // than pixels for the pdf output. E.g. a line width of '5' cound be + // 5mm, 5in, or 5pt depending on the pdf-units. + + // Provinces (polygon) + feature_type_style provpoly_style; + + rule_type provpoly_rule_on; + provpoly_rule_on.set_filter(create_filter("[NAME_EN] = 'Ontario'")); + provpoly_rule_on.append(polygon_symbolizer(Color(250, 190, 183))); + provpoly_style.add_rule(provpoly_rule_on); + + rule_type provpoly_rule_qc; + provpoly_rule_qc.set_filter(create_filter("[NAME_EN] = 'Quebec'")); + provpoly_rule_qc.append(polygon_symbolizer(Color(217, 235, 203))); + provpoly_style.add_rule(provpoly_rule_qc); + + m.insert_style("provinces",provpoly_style); + + + // Provinces (polyline) + feature_type_style provlines_style; + + stroke provlines_stk (Color(0,0,0),0.75); + provlines_stk.add_dash(8, 4); + provlines_stk.add_dash(2, 2); + provlines_stk.add_dash(2, 2); + + rule_type provlines_rule; + provlines_rule.append(line_symbolizer(provlines_stk)); + provlines_style.add_rule(provlines_rule); + + m.insert_style("provlines",provlines_style); + + + // Drainage + feature_type_style qcdrain_style; + + rule_type qcdrain_rule; + qcdrain_rule.set_filter(create_filter("[HYC] = 8")); + pdf_polygon_pattern_symbolizer psym(DEMODIR"pdf/images/b.png","png",100,100, 10, 10); + qcdrain_rule.append(psym); + qcdrain_style.add_rule(qcdrain_rule); + + m.insert_style("drainage",qcdrain_style); + + + // Roads 3 and 4 (The "grey" roads) + feature_type_style roads34_style; + rule_type roads34_rule; + roads34_rule.set_filter(create_filter("[CLASS] = 3 or [CLASS] = 4")); + stroke roads34_rule_stk(Color(171,158,137),1.0); + roads34_rule_stk.set_line_cap(ROUND_CAP); + roads34_rule_stk.set_line_join(ROUND_JOIN); + roads34_rule.append(line_symbolizer(roads34_rule_stk)); + roads34_style.add_rule(roads34_rule); + + m.insert_style("smallroads",roads34_style); + + + // Roads 2 (The thin yellow ones) + feature_type_style roads2_style_1; + rule_type roads2_rule_1; + roads2_rule_1.set_filter(create_filter("[CLASS] = 2")); + stroke roads2_rule_stk_1(Color(171,158,137),1.8); + roads2_rule_stk_1.set_line_cap(ROUND_CAP); + roads2_rule_stk_1.set_line_join(ROUND_JOIN); + roads2_rule_1.append(line_symbolizer(roads2_rule_stk_1)); + roads2_style_1.add_rule(roads2_rule_1); + + m.insert_style("road-border", roads2_style_1); + + feature_type_style roads2_style_2; + rule_type roads2_rule_2; + roads2_rule_2.set_filter(create_filter("[CLASS] = 2")); + stroke roads2_rule_stk_2(Color(255,250,115),1.5); + roads2_rule_stk_2.set_line_cap(ROUND_CAP); + roads2_rule_stk_2.set_line_join(ROUND_JOIN); + roads2_rule_2.append(line_symbolizer(roads2_rule_stk_2)); + roads2_style_2.add_rule(roads2_rule_2); + + m.insert_style("road-fill", roads2_style_2); + + + // Roads 1 (The big orange ones, the highways) + feature_type_style roads1_style_1; + rule_type roads1_rule_1; + roads1_rule_1.set_filter(create_filter("[CLASS] = 1")); + stroke roads1_rule_stk_1(Color(188,149,28),4); + roads1_rule_stk_1.set_line_cap(ROUND_CAP); + roads1_rule_stk_1.set_line_join(ROUND_JOIN); + roads1_rule_1.append(line_symbolizer(roads1_rule_stk_1)); + roads1_style_1.add_rule(roads1_rule_1); + m.insert_style("highway-border", roads1_style_1); + + feature_type_style roads1_style_2; + rule_type roads1_rule_2; + roads1_rule_2.set_filter(create_filter("[CLASS] = 1")); + pdf_line_pattern_symbolizer lsym(DEMODIR"pdf/images/a.png","png",3,100,100,10,10); + roads1_rule_2.append(lsym); + + roads1_style_2.add_rule(roads1_rule_2); + m.insert_style("highway-fill", roads1_style_2); + + + // Populated Places + + feature_type_style popplaces_style; + rule_type popplaces_rule; + text_symbolizer popplaces_text_symbolizer("GEONAME","arial",3,Color(50,50,100)); + popplaces_text_symbolizer.set_halo_fill(Color(255,255,200)); + popplaces_text_symbolizer.set_halo_radius(1); + popplaces_rule.append(popplaces_text_symbolizer); + popplaces_style.add_rule(popplaces_rule); + + m.insert_style("popplaces",popplaces_style ); + + //--------------------------------- + //Create Layers and assign styles + + // Layers + // Provincial polygons + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"boundaries"; + + Layer lyr("Provinces"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("provinces"); + m.addLayer(lyr); + } + + // Drainage + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"qcdrainage"; + Layer lyr("Quebec Hydrography"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("drainage"); + m.addLayer(lyr); + } + + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"ontdrainage"; + + Layer lyr("Ontario Hydrography"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("drainage"); + m.addLayer(lyr); + } + + + // Provincial boundaries + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"boundaries_l"; + Layer lyr("Provincial borders"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("provlines"); + m.addLayer(lyr); + } + + // Roads + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"roads"; + Layer lyr("Roads"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("smallroads"); + lyr.add_style("road-border"); + lyr.add_style("road-fill"); +// lyr.add_style("highway-border"); + lyr.add_style("highway-fill"); + + m.addLayer(lyr); + } + + // popplaces + { + parameters p; + p["type"]="shape"; + p["file"]=DEMODATA"popplaces"; + p["encoding"] = "latin1"; + Layer lyr("Populated Places"); + lyr.set_datasource(datasource_cache::instance()->create(p)); + lyr.add_style("popplaces"); + m.addLayer(lyr); + } + + + //--------------------------------- + //Set map visible area + +// m.zoom_all(); + m.zoomToBox(Envelope(1405120.04127408,-247003.813399447,1706357.31328276,-25098.593149577)); + + + //--------------------------------- + //Render to a png (you'll need to change the font in the popplaces style for this to work + +/* Image32 buf(m.getWidth(),m.getHeight()); + agg_renderer ren(m,buf,0,0); + ren.apply(); + save_to_file(buf.data(),"canada2.png","png"); +*/ + + //================================= + //Render to a pdf + + //--------------------- + //create the page layout for the pdf + + //this specifies the units used on the page, and dimensions of the page in those units. + pdf_renderer_layout page_layout("mm", 841, 594); + + //set the location of the map within the page + page_layout.set_map_area(20, 40, m.getWidth(), m.getHeight()); + + page_layout.set_map_area_border(true); //Draw a border around the map + page_layout.set_map_area_border_width(0.75); // - width of the border + page_layout.set_background_colour(Color(200, 200, 225)); //Page background colour + + page_layout.set_map_grid(true); //Draw a grid across the map (on its own layer so it can be turned on/off) + page_layout.set_map_grid_colour(Color(0,128,255,200)); // - Grid colour + page_layout.set_map_grid_linewidth(0.33); // - Grid line width + + page_layout.set_border_scales(true); //Draw scales around the map border + page_layout.set_map_grid_approx_spacing(20); // - approximate length of each grid segment, this will be tweaked to get nice units (e.g. 20mm may equate to 5832.78 meters, so it'll be changed to something like 22 to give 6000 meters) + page_layout.set_border_scale_width(3); // - width of the scales around the border + page_layout.set_border_scale_linewidth(0.25); // - line width of the border scales + + page_layout.set_scale_bar(true); //Draw a scale bar + page_layout.set_scale_bar_area(Envelope(50,555, 150, 567)); // - position and size of the scale bar. The width will be tweaked to give proper units. +// page_layout.set_scale_bar_factor(1,"Meters"); // - Give the scale factor and label. E.g. if the map projection outputs in meters, the scale factor is 1, but if you want kilometers, specify 0.001. +// page_layout.set_scale_bar_factor(0.001,"Kilometers"); +// page_layout.set_scale_bar_factor(39.3700787,"Inches"); + page_layout.set_scale_bar_factor(0.000621371192,"Miles"); +// page_layout.set_scale_bar_factor(0.546806649,"Fathoms"); +// page_layout.set_scale_bar_factor(3.2808399,"Feet"); + + + page_layout.add_overlay_image(DEMODIR"pdf/images/c.pdf", 5, 5, 200, 40, 1.2); + page_layout.add_overlay_image(DEMODIR"pdf/images/a.png", 500, 460, 100, 100, -25); + page_layout.add_overlay_image(DEMODIR"pdf/images/b.png", 650, 400, 100, 100, 13.25); + + + //--------------------- + //render the pdf to a file + render_to_pdf(m, page_layout, "fancy_demo.pdf", false); +// render_to_pdf(m, page_layout, "fancy_demo-compressed.pdf", true); + +} + + + diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index b5e83c05f..638fda627 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -47,6 +47,12 @@ namespace mapnik { { names_.insert(sym.get_name()); } +#if ENABLE_PDF + void operator () (pdf_shield_symbolizer const &sym) + { + names_.insert(sym.get_name()); + } +#endif //ENABLE_PDF void operator () (shield_symbolizer const& sym) { names_.insert(sym.get_name()); diff --git a/include/mapnik/image_data.hpp b/include/mapnik/image_data.hpp index 9fd57a8a9..4641ee23e 100644 --- a/include/mapnik/image_data.hpp +++ b/include/mapnik/image_data.hpp @@ -142,7 +142,7 @@ namespace mapnik }; typedef ImageData ImageData32; - typedef ImageData ImageData8; + typedef ImageData ImageData8; } #endif //IMAGE_DATA_HPP diff --git a/include/mapnik/octree.hpp b/include/mapnik/octree.hpp index 221baedb1..725aa3dc9 100644 --- a/include/mapnik/octree.hpp +++ b/include/mapnik/octree.hpp @@ -24,6 +24,7 @@ #ifndef _OCTREE_HPP_ #define _OCTREE_HPP_ +#include #include #include #include @@ -32,7 +33,7 @@ namespace mapnik { - typedef uint8_t byte ; + typedef boost::uint8_t byte; struct rgb { byte r; @@ -82,7 +83,7 @@ namespace mapnik { unsigned greens; unsigned blues; unsigned count; - uint8_t index; + boost::uint8_t index; }; struct node_cmp { @@ -214,9 +215,9 @@ namespace mapnik { if (itr->count != 0) { unsigned count = itr->count; - palette.push_back(rgb(uint8_t(itr->reds/float(count)), - uint8_t(itr->greens/float(count)), - uint8_t(itr->blues/float(count)))); + palette.push_back(rgb(boost::uint8_t(itr->reds/float(count)), + boost::uint8_t(itr->greens/float(count)), + boost::uint8_t(itr->blues/float(count)))); itr->index = palette.size() - 1; } for (unsigned i=0; i < 8 ;++i) diff --git a/include/mapnik/png_io.hpp b/include/mapnik/png_io.hpp index 1fc02fcbd..3b345c0b1 100644 --- a/include/mapnik/png_io.hpp +++ b/include/mapnik/png_io.hpp @@ -102,7 +102,7 @@ namespace mapnik { { unsigned val = row[x]; mapnik::rgb c((val)&0xff, (val>>8)&0xff, (val>>16) & 0xff); - uint8_t index = tree.quantize(c); + boost::uint8_t index = tree.quantize(c); row_out[x] = index; } } @@ -123,7 +123,7 @@ namespace mapnik { { unsigned val = row[x]; mapnik::rgb c((val)&0xff, (val>>8)&0xff, (val>>16) & 0xff); - uint8_t index = tree.quantize(c); + boost::uint8_t index = tree.quantize(c); if (x%2 > 0) index = index<<4; row_out[x>>1] |= index; } diff --git a/include/mapnik/rule.hpp b/include/mapnik/rule.hpp index 181475b63..31ae7c0d4 100644 --- a/include/mapnik/rule.hpp +++ b/include/mapnik/rule.hpp @@ -32,6 +32,12 @@ #include #include #include +#ifdef ENABLE_PDF + #include "pdf/pdf_point_symbolizer.hpp" + #include "pdf/pdf_polygon_pattern_symbolizer.hpp" + #include "pdf/pdf_line_pattern_symbolizer.hpp" + #include "pdf/pdf_shield_symbolizer.hpp" +#endif #include #include @@ -110,7 +116,14 @@ namespace mapnik shield_symbolizer, text_symbolizer, building_symbolizer, - markers_symbolizer> symbolizer; + markers_symbolizer +#ifdef ENABLE_PDF + ,pdf_point_symbolizer, + pdf_polygon_pattern_symbolizer, + pdf_line_pattern_symbolizer, + pdf_shield_symbolizer +#endif + > symbolizer; typedef std::vector symbolizers; diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index fa2b2b8d3..344b60a94 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -30,7 +30,7 @@ namespace mapnik { - class symbolizer_with_image { + class MAPNIK_DECL symbolizer_with_image { public: boost::shared_ptr get_image() const; const std::string & get_filename() const; diff --git a/include/mapnik/unicode.hpp b/include/mapnik/unicode.hpp index 7ed17a5c6..67e5170b5 100644 --- a/include/mapnik/unicode.hpp +++ b/include/mapnik/unicode.hpp @@ -29,6 +29,8 @@ #include +#include + namespace mapnik { class transcoder : private boost::noncopyable { diff --git a/include/pdf/font_engine_pdf.hpp b/include/pdf/font_engine_pdf.hpp new file mode 100644 index 000000000..ddf6ac5d6 --- /dev/null +++ b/include/pdf/font_engine_pdf.hpp @@ -0,0 +1,172 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef FONT_ENGINE_PDF_HPP +#define FONT_ENGINE_PDF_HPP + +// mapnik +#include +#include +#include +#include +#include + +// boost +#include +#include +#include +#include +#include + +// stl +#include +#include +#include +#include + +// WX +#include + +namespace mapnik +{ + + class pdf_text_renderer : private boost::noncopyable + { + private: + //! \brief Records width and height of a character + typedef std::pair char_dimension_t; + + public: + //! \brief Constructor + //! + //! \param[in] _pdf The PDF document + pdf_text_renderer(wxPdfDocument &_pdf); + + + //! \brief Sets the options for using external fonts + //! + //! \param[in] externalFontPath Path to the fonts, and the generated xml files. + //! \param[in] enableSubsetting Enables/Disables font subsetting to reduce file size. + void set_external_font_options(const std::wstring& externalFontPath, const bool enableSubsetting); + + + //! \brief Embeds a new font + //! + //! \param[in] fontName The name to use for the font internally, does not need to be related + //! to the real font name. + //! \param[in] fontXMLFile The xml file generated with the wxpdfdoc makefont utility + //! \return true if successfull, false otherwise + bool add_external_font(const std::wstring& fontName, const std::wstring& fontXMLFile); + + + //! \brief Set the font to use + //! + //! The font must either be one of the pdf internal fonts (arial, courier, times, symbol, zapfDingbats) + //! or have been loaded with add_external_font(...). + //! + //! \param[in] fontThe name of the font + //! \return True if successfully set, false otherwise + bool set_font(const std::string& font); + + + //! \brief Set the font to use + //! + //! The font must either be one of the pdf internal fonts (arial, courier, times, symbol, zapfDingbats) + //! or have been loaded with add_external_font(...). + //! + //! \param[in] fontThe name of the font + //! \return True if successfully set, false otherwise + bool set_font(const std::wstring& font); + + + //! \brief Set the font size + //! + //! \param[in] size The size in pdf units + void set_size(const double size); + + + //! \brief Set the font colour + //! + //! \param[in] _fill The colour + void set_fill(mapnik::Color const& _fill); + + + //! \brief Set the halo colour + //! + //! \param[in] _halo The colour + void set_halo_fill(mapnik::Color const& _halo); + + + //! \brief Set the halo radius + //! + //! \param[in] radius The radius in pdf units + void set_halo_radius( const double radius ); + + + //! \brief Get the dimensions of a character in mm + //! + //! Note: The height of the character is taken to be the + //! height of a capital letter plus the descent height of the font. This + //! should give reasonable results. + //! + //! \param[in] c The character + //! \return The characters dimensions. + pdf_text_renderer::char_dimension_t character_dimensions(const unsigned c); + + + //! \brief Calculates the dimensions of all the characters and the whole string + //! + //! \param[in] info The string_info to populate + void get_string_info(string_info *info); + + + //! \brief Prepares to render a whole lot of text + //! + //! This sets the line style and colour in case we are rendering + //! text halos. + void prepare_render(void); + + + //! \brief Renders a text path + //! + //! This renders the text path calculated by the placement_finder. + //! + //! \param[in] path The path to render + //! \param[in] x0 x offset + //! \param[in] y0 y offset + void render(text_path *path, double x0, double y0); + + + private: + wxPdfDocument *pdf; //!< The PDF Document + const std::wstring default_font_name; //!< The default font. This must be an internal pdf font (arial, courier, times, symbol, zapfDingbats) + std::wstring font_name; //!< Name of the font + double font_size; //!< Font size in pdf units + const wxPdfFontDescription *font_description; //!< Internal information about a font + mapnik::Color fill; //!< Fill colour + mapnik::Color halo_fill; //!< Halo colour + double halo_radius; //!< Halo radius in pdf units + }; + +} + +#endif //FONT_ENGINE_PDF_HPP diff --git a/include/pdf/pdf_line_pattern_symbolizer.hpp b/include/pdf/pdf_line_pattern_symbolizer.hpp new file mode 100644 index 000000000..281836972 --- /dev/null +++ b/include/pdf/pdf_line_pattern_symbolizer.hpp @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef PDF_LINE_PATTERN_SYMBOLIZER_HPP +#define PDF_LINE_PATTERN_SYMBOLIZER_HPP + +// mapnik +#include +#include +#include +#include + +//boost +#include + +namespace mapnik +{ + class MAPNIK_DECL pdf_line_pattern_symbolizer + : public line_pattern_symbolizer + { + public: + //! \brief constructor + //! + //! \param[in] file The image file + //! \param[in] type The type of the file, e.g. "png" + //! \param[in] line_width The width of the line (in pdf units) + //! \param[in] image_width The width of the image in pixels + //! \param[in] image_height The height of the image in pixels + //! \param[in] _output_width The width of the image tile (in pdf units) + //! \param[in] _output_height The height of the image tile (in pdf units) + pdf_line_pattern_symbolizer(std::string const& file, + std::string const& type, + const double line_width, + const unsigned image_width, const unsigned image_height, + const double _output_width, const double _output_height); + + //! \brief constructor + //! + //! Copies the supplied pdf_line_pattern_symbolizer + pdf_line_pattern_symbolizer(pdf_line_pattern_symbolizer const& rhs); + + //! \brief constructor + //! + //! Takes a line_pattern_symbolizer and turns it into a pdf_line_pattern_symbolizer + //! + //! \param[in] rhs The line_pattern_symbolizer to start with + //! \param[in] line_width The width of the line (in pdf units) + //! \param[in] _output_width The width of the image tile (in pdf units) + //! \param[in] _output_height The height of the image tile (in pdf units) + pdf_line_pattern_symbolizer(line_pattern_symbolizer const& rhs, const double line_width, const double _output_width, const double _output_height); + + //! \brief Sets the output size of the tile + //! + //! \param[in] width Width in pdf units + //! \param[in] height Height in pdf units + void set_output_size(const double width, const double height); + + //! \brief Gets the output width of the tile + //! + //! \return The output width of the image in pdf units + double get_output_width(void) const { return output_width; }; + + //! \brief Gets the output height of the tile + //! + //! \return The output height of the image in pdf units + double get_output_height(void) const { return output_height; }; + + //! \brief Gets the line stroke + //! + //! \return the line stroke + stroke const& get_stroke() const { return line_stroke; }; + + //! \brief Sets the line stroke + //! + //! \param[in] stroke The stroke to use + void set_stroke(stroke const& stroke) { line_stroke = stroke; }; + + private: + double output_width; //!< output width of pattern in pdf units + double output_height; //!< output height of pattern in pdf units + + stroke line_stroke; //!< the line stroke + }; +} + +#endif //PDF_LINE_PATTERN_SYMBOLIZER_HPP diff --git a/include/pdf/pdf_point_symbolizer.hpp b/include/pdf/pdf_point_symbolizer.hpp new file mode 100644 index 000000000..e54849f95 --- /dev/null +++ b/include/pdf/pdf_point_symbolizer.hpp @@ -0,0 +1,89 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef PDF_POINT_SYMBOLIZER_HPP +#define PDF_POINT_SYMBOLIZER_HPP + +#include +#include +#include +#include + + +namespace mapnik +{ + class MAPNIK_DECL pdf_point_symbolizer + : public point_symbolizer + { + public: + explicit pdf_point_symbolizer(); + + //! \brief constructor + //! + //! \param[in] file The image file + //! \param[in] type The type of the file, e.g. "png" + //! \param[in] image_width The width of the image in pixels + //! \param[in] image_height The height of the image in pixels + //! \param[in] _output_width The scaled (output) width of the image (in pdf units) + //! \param[in] _output_height The scaled (output) height of the image (in pdf units) + pdf_point_symbolizer( std::string const& file, + std::string const& type, + unsigned image_width, unsigned image_height, + double _output_width, double _output_height); + + //! \brief constructor + //! + //! Copies the supplied pdf_point_symbolizer + pdf_point_symbolizer(pdf_point_symbolizer const& rhs); + + //! \brief constructor + //! + //! Takes a point_symbolizer and turns it into a pdf_point_symbolizer + //! + //! \param[in] rhs The point_symbolizer to start with + //! \param[in] _output_width The scaled (output) width of the image (in pdf units) + //! \param[in] _output_height The scaled (output) height of the image (in pdf units) + pdf_point_symbolizer(point_symbolizer const& rhs, const double _output_width, const double _output_height); + + //! \brief Sets the output size of the image + //! + //! \param[in] width Width in pdf units + //! \param[in] height Height in pdf units + void set_output_size(const double width, const double height); + + //! \brief Gets the output width of an image + //! + //! \return The output width of the image in pdf units + double get_output_width(void) const { return output_width; }; + + //! \brief Gets the output height of an image + //! + //! \return The output height of the image in pdf units + double get_output_height(void) const { return output_height; }; + + private: + double output_width; //!< output width of image in pdf units + double output_height; //!< output height of image in pdf units + }; +} + +#endif // PDF_POINT_SYMBOLIZER_HPP diff --git a/include/pdf/pdf_polygon_pattern_symbolizer.hpp b/include/pdf/pdf_polygon_pattern_symbolizer.hpp new file mode 100644 index 000000000..7a04dd241 --- /dev/null +++ b/include/pdf/pdf_polygon_pattern_symbolizer.hpp @@ -0,0 +1,89 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef PDF_POLYGON_PATTERN_SYMBOLIZER_HPP +#define PDF_POLYGON_PATTERN_SYMBOLIZER_HPP + +// mapnik +#include +#include +#include + +//boost +#include + +namespace mapnik +{ + class MAPNIK_DECL pdf_polygon_pattern_symbolizer + : public polygon_pattern_symbolizer + { + public: + //! \brief constructor + //! + //! \param[in] file The image file + //! \param[in] type The type of the file, e.g. "png" + //! \param[in] image_width The width of the image in pixels + //! \param[in] image_height The height of the image in pixels + //! \param[in] _output_width The scaled (output) width of the image tile (in pdf units) + //! \param[in] _output_height The scaled (output) height of the image tile (in pdf units) + pdf_polygon_pattern_symbolizer(std::string const& file, + std::string const& type, + unsigned image_width, unsigned image_height, + double _output_width, double _output_height); + + //! \brief constructor + //! + //! Copies the supplied pdf_polygon_pattern_symbolizer + pdf_polygon_pattern_symbolizer(pdf_polygon_pattern_symbolizer const& rhs); + + //! \brief constructor + //! + //! Takes a polygon_pattern_symbolizer and turns it into a pdf_polygon_pattern_symbolizer + //! + //! \param[in] rhs The polygon_pattern_symbolizer to start with + //! \param[in] _output_width The width of the image tile (in pdf units) + //! \param[in] _output_height The height of the image tile (in pdf units) + pdf_polygon_pattern_symbolizer(polygon_pattern_symbolizer const& rhs, const double _output_width, const double _output_height); + + //! \brief Sets the output size of the tile + //! + //! \param[in] width Width in pdf units + //! \param[in] height Height in pdf units + void set_output_size(const double width, const double height); + + //! \brief Gets the output width of the tile + //! + //! \return The output width of the image in pdf units + double get_output_width(void) const { return output_width; }; + + //! \brief Gets the output height of the tile + //! + //! \return The output height of the image in pdf units + double get_output_height(void) const { return output_height; }; + + private: + double output_width; //!< output width of pattern tile in pdf units + double output_height; //!< output height of pattern tile in pdf units + }; +} + +#endif //PDF_POLYGON_PATTERN_SYMBOLIZER_HPP diff --git a/include/pdf/pdf_renderer.hpp b/include/pdf/pdf_renderer.hpp new file mode 100644 index 000000000..17606180f --- /dev/null +++ b/include/pdf/pdf_renderer.hpp @@ -0,0 +1,249 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//! \file pdf_renderer.hpp +//! \brief PDF renderer for mapnik data +//! + +#ifndef PDF_RENDERER_H +#define PDF_RENDERER_H + +// pdf renderer +#include "font_engine_pdf.hpp" +namespace mapnik { class pdf_renderer_layout; } + + +// mapnik +#include +#include +#include +#include +#include +#include +#include + +// boost +#include +#include + +// WX +#include +class wxImage; + +namespace mapnik { + + //! \brief round a number to nearest integer + //! + //! \param[in] x number to round + //! \return number rounded to nearest integer + #define round(x) ( (x)>=0 ? (long)((x)+0.5) : (long)((x)-0.5) ) + + //class prototypes + class pdf_point_symbolizer; + class pdf_line_pattern_symbolizer; + class pdf_polygon_pattern_symbolizer; + class pdf_shield_symbolizer; + + + //! \brief Renderer for PDFs + class MAPNIK_DECL pdf_renderer + : public feature_style_processor, private boost::noncopyable + { + public: + //! \brief Constructor + //! + //! \param[in] m The map data + //! \param[in] _pdf The PDF document to populate + //! \param[in] _page_layout Describes the layout of the map page + //! \param[in] offset_x Number of pdf units to offset the map data in x direction + //! \param[in] offset_y Number of pdf units to offset the map data in y direction + pdf_renderer(Map const& m, wxPdfDocument &_pdf, const pdf_renderer_layout &_page_layout, const double offset_x=0.0, const double offset_y=0.0); + + + //! \brief Sets the options for using external fonts + //! + //! See http://wxcode.sourceforge.net/docs/wxpdfdoc/makefont.html for how + //! to generate the required xml files for external fonts. + //! + //! For example: + //! - ttf2ufm -a trebuc.ttf trebuc + //! - makefont -a trebuc.afm -f trebuc.ttf -e cp1252 + //! + //! \param[in] externalFontPath Path to the fonts, and the generated xml files. + //! \param[in] enableSubsetting Enables/Disables font subsetting to reduce file size. + void set_external_font_options(const std::wstring& externalFontPath, const bool enableSubsetting); + + + //! \brief Embeds a new font + //! + //! See set_external_font_options for how to generate the xml file. + //! + //! \param[in] fontName The name to use for the font internally, does not need to be related + //! to the real font name. + //! \param[in] fontXMLFile The xml file generated with the wxpdfdoc makefont utility + //! \return true if successfull, false otherwise + bool add_external_font(const std::wstring& fontName, const std::wstring& fontXMLFile); + + + //! \brief Called at the start of map processing + //! + //! \param[in] map The map data + void start_map_processing(Map const& map); + + //! \brief Called at the end of map processing + //! + //! \param[in] map The map data + void end_map_processing(Map const& map); + + //! \brief Called before each layer is processed + //! + //! \param[in] lay The layer data + void start_layer_processing(Layer const& lay); + + //! \brief Called after each layer has been proecssed + //! + //! \param[in] lay The layer data + void end_layer_processing(Layer const& lay); + + //! \brief Process point symbolizer + void process(point_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process pdf point symbolizer + void process(pdf_point_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process line symbolizer + void process(line_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process line pattern symbolizer + void process(line_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process pdf line pattern symbolizer + void process(pdf_line_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process polygon symbolizer + void process(polygon_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process polygon pattern symbolizer + void process(polygon_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process pdf polygon pattern symbolizer + void process(pdf_polygon_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process raster symbolizer + void process(raster_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process shield symbolizer + void process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process pdf shield symbolizer + void process(pdf_shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process text symbolizer + void process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process building symbolizer + void process(building_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + //! \brief Process marker symbolizer + //! + //! This is not currently supported by the PDF renderer + void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); + + + //! \brief convert string to wstring + //! + //! \param[in] str string to convert + //! \return the wide string + static std::wstring convert_string_to_wstring(const std::string str); + + private: + //! \brief Creates a wxImage from an ImageData32 image + //! + //! This performs all the pixel format conversions. dst is + //! created by the function, and must be deleted by the + //! caller when no longer required. + //! + //! \param[in] src The source image + //! \param[in] dst Pointer to the wx image pointer that will be created and returned + void create_wximage_from_imagedata(boost::shared_ptr src, wxImage **dst); + + //! \brief Creates a wxImage from an ImageData32 image + //! + //! This performs all the pixel format conversions. dst is + //! created by the function, and must be deleted by the + //! caller when no longer required. + //! + //! \param[in] src The source image + //! \param[in] dst Pointer to the wx image pointer that will be created and returned + void create_wximage_from_imagedata(const ImageData32* src, wxImage **dst); + + //! \brief Generates a pdf shape from a transformed path + //! + //! This takes a path (wrapped with a coordinate transform) and converts + //! it into a pdf shape. + //! + //! \param[in] transformed_path The transformed path + //! \param[in] shape The shape to fill + void create_shape_from_path(const coord_transform2 &transformed_path, wxPdfShape &shape); + + //! \brief Get a unique image name + //! + //! This uses unnamed_image_count to generate a unique name + //! + //! \return The name + std::wstring generate_unique_image_name(void); + + + //! \brief Do all the overlay rendering + //! + //! This parses the page_layout to render all the additional + //! content. + void render_overlays(void); + + //! \brief Render overlay images + void render_overlay_images(void); + + //! \brief Render the grid + void render_overlay_grid(void); + + //! \brief Render the scale bar + void render_overlay_scale_bar(void); + + + private: + Map const& map; //!< The map being rendered + + CoordTransform coord_trans; //!< Converter for moving between map cartesian coordinates and pdf coordinates + pdf_text_renderer text_renderer; //!< Utility for rendering pdf text + label_collision_detector4 detector; //!< Collision detector + + wxPdfDocument *pdf; //!< The pdf document being filled + const pdf_renderer_layout &page_layout; //!< The layout of the map page + double line_miter_limit; //!< Line miter limit + unsigned int unnamed_image_count; //!< Counter used for generating unique name for unnamed images (e.g. rasters) + }; +} + + + + +#endif //PDF_RENDERER_H diff --git a/include/pdf/pdf_renderer_layout.hpp b/include/pdf/pdf_renderer_layout.hpp new file mode 100644 index 000000000..a46520025 --- /dev/null +++ b/include/pdf/pdf_renderer_layout.hpp @@ -0,0 +1,240 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//! \file pdf_renderer_layout.hpp +//! \brief Describes the paper page layout for the pdf map +//! + +#ifndef PDF_RENDERER_LAYOUT_H +#define PDF_RENDERER_LAYOUT_H + +// WX +#include + +// STD +#include + +// mapnik +#include +#include +#include + + +namespace mapnik { + + //! \brief Describes the layout of the page containing the map + class MAPNIK_DECL pdf_renderer_layout { + friend class pdf_renderer; + public: + //! \brief Structure describing overlay images + typedef struct overlay_image_data { + std::string file; //!< The image file + double x; //!< Top left corner x coordinate + double y; //!< Top left corner y coordinate + double width; //!< Width of the image (0 means scale proportional to height) + double height; //!< Height of the image (0 means scale proportional to width) + double angle; //!< Rotation angle (degrees) + } overlay_image_data; + + public: + //! \brief Constructor + //! + //! \param[in] units The units of all the measurements, e.g. "mm","pt","in" + //! \param[in] width The width of the page (in pdf units) + //! \param[in] height The height of the page (in pdf units) + //! \param[in] portrait The orientation of the page, true for portrait, false for landscape + pdf_renderer_layout(const std::string &units, const double width, const double height, const bool portrait = true); + + //! \brief Destructor + ~pdf_renderer_layout(); + + + //! Get the page width + //! \return The page width + const double get_page_width(void) const {return page_area.width(); }; + + //! Get the page height + //! \return The page height + const double get_page_height(void) const {return page_area.height(); }; + + //! Get the page orientation + //! \return The page orientation + const bool get_page_portrait(void) const {return page_portrait; }; + + //! Get the page units + //! \return The page units + const std::string &get_page_units(void) const {return page_units; }; + + + + //---------------------------------------------------------------------------------------------------- + // Page layout + + //! \brief Set the font to use for overlays + //! + //! \param[in] name The name of the font + void set_font(const std::string font) { font_name = font; }; + + //! \brief set the page background colour + //! + //! This is separate to the map background colour in the Map class + //! + //! \param[in] colour The background colour + void set_background_colour(const Color &colour) { page_background_colour = colour; }; + + //! \brief Set the area of the page to render the map + //! + //! \param[in] x Top left corner x coordinate + //! \param[in] y Top left corner y coordinate + //! \param[in] width Width of the area + //! \param[in] height Height of the area + void set_map_area(const double x, const double y, const double width, const double height); + + //! \brief Set whether to draw a map border + //! + //! \param[in] drawBorder True for yes, false for no + void set_map_area_border(const bool drawBorder) { map_area_border = drawBorder; }; + + //! \brief Set the map border width + //! + //! \param[in] width Width of the border + void set_map_area_border_width(const double width) { map_area_border_width = width; }; + + //---------------------------------------------------------------------------------------------------- + // Overlay images + + //! \brief Add an overlay image + //! + //! Images added here will be overlaid on the map. You can add + //! png and pdf files. + //! + //! \param[in] image Image filename + //! \param[in] x Top left corner x coordinate + //! \param[in] y Top left corner y coordinate + //! \param[in] width Width of the area + //! \param[in] height Height of the area + //! \param[in] angle Rotation angle (degrees) + void add_overlay_image(const std::string &image, const double x, const double y, const double width, const double height, const double angle); + + //---------------------------------------------------------------------------------------------------- + // Grids + + //! \brief Enable/Disable drawing map grid + //! + //! \param[in] enable True to enable, false to disable + void set_map_grid(const bool enable) { map_grid = enable; }; + + //! \brief Enable/Disable drawing scales around edge of map + //! + //! \param[in] enable True to enable, false to disable + void set_border_scales(const bool enable) { border_scales = enable; }; + + //! \brief Sets the approximate spacing of the grid lines + //! + //! This will be tweaked to give nice round numbers to the grid lines. + //! + //! \param[in] spacing The approx spacing of the grid lines + void set_map_grid_approx_spacing(const double spacing) { map_grid_approx_spacing = spacing; }; + + //! \brief Set width of scale bars around edge of map + //! + //! \param[in] width The thickness of the bars + void set_border_scale_width(const double width) { border_scale_width = width; }; + + //! \brief Set thickness of lines around the grid edge + //! + //! \param[in] width The thickness of the lines + void set_border_scale_linewidth(const double width) { border_scale_linewidth = width; }; + + //! \brief Set the colour of the grid across the map + //! + //! \param[in] colour The colour of the grid (with alpha) + void set_map_grid_colour(const Color &colour) { map_grid_colour = colour; }; + + //! \brief Set the map grid line width + //! + //! \param[in] width The width of the map grid lines + void set_map_grid_linewidth(const double width) { map_grid_linewidth = width; }; + + //---------------------------------------------------------------------------------------------------- + // Scale bar + + //! \brief Enable/Disable the scale bar + //! + //! \param[in] enable True to draw the scale bar, false to disable + void set_scale_bar(const bool enable) { scale_bar = enable; }; + + //! \brief Set the scale bar size and location + //! + //! The width of the scale bar will be adjusted to make sense, but it will be + //! centered on the provided area. + //! + //! \param[in] area The area + void set_scale_bar_area(const Envelope area) { scale_bar_area = area; }; + + //! \brief Sets the scale bar scale factor and label + //! + //! See scale_bar_factor and scale_bar_unit for details. + //! + //! \param[in] scale_factor The scale factor + void set_scale_bar_factor(const double scale_factor, const std::string &unit) { scale_bar_factor = scale_factor; scale_bar_unit = unit; }; + + private: + //common parameters + std::string font_name; //!< The name of the font to use for text in the overlays + + //page layout + const Envelope page_area; //!< Page area, specified in the constructors pdf document + const std::string page_units; //!< PDF units of measure + bool page_portrait; //!< True if page is portrait, false for landscape + Color page_background_colour; //!< Background page colour (default white) + + Envelope map_area; //!< Area of the page that the map will occupy + bool map_area_border; //!< Whether to draw border around map + double map_area_border_width; //!< Thickness of map border (only drawn if no grid/border scales) + + //overlay images + std::vector overlay_images; //!< List of overlay images + typedef std::vector::iterator OIDitr; //!< overlay image vector iterator + + + + //grids + bool map_grid; //!< True for draw grid across map + bool border_scales; //!< True to draw edge scales, false otherwise + double map_grid_approx_spacing; //!< Approximate spacing between grid. This will be tweaked to give round coordinates + double border_scale_width; //!< Thickness of grid bars bordering map + double border_scale_linewidth; //!< Thickness of the lines around the grid edge (also used in the scale bar) + Color map_grid_colour; //!< Colour to draw the grid + double map_grid_linewidth; //!< Thickness of the map grid lines + + //scale bar + bool scale_bar; //!< True to draw scale bar + Envelope scale_bar_area; //!< Where to draw the scale bar. The width will be adjusted to give good units, but it will be centered on the given area. + double scale_bar_factor; //!< The scale factor to convert 1 projection unit into 1 'scale_bar_unit'. + std::string scale_bar_unit; //!< The units of the scale bar, e.g. 'meters' 'kilometers' 'miles' + }; + +} //namespace mapnik + + +#endif //PDF_RENDERER_LAYOUT_H diff --git a/include/pdf/pdf_renderer_utility.hpp b/include/pdf/pdf_renderer_utility.hpp new file mode 100644 index 000000000..9e381dac8 --- /dev/null +++ b/include/pdf/pdf_renderer_utility.hpp @@ -0,0 +1,50 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//! \file pdf_renderer_utility.hpp +//! \brief PDF renderer utilities +//! + +#ifndef PDF_RENDERER_UTILITY_H +#define PDF_RENDERER_UTILITY_H + +namespace mapnik { + +#include + +//class prototypes +class Map; +class pdf_renderer_layout; + + +//! \brief Render a map to pdf using a layout +//! +//! \param[in] map The map to render +//! \param[in] layout The page layouit +//! \param[in] filename The output file name +//! \param[in] compress Enable/Disable pdf compression +MAPNIK_DECL void render_to_pdf(const mapnik::Map& map, const mapnik::pdf_renderer_layout &layout, const std::string& filename, const bool compress = true); + + +} //namespace mapnik + +#endif //PDF_RENDERER_UTILITY_H diff --git a/include/pdf/pdf_shield_symbolizer.hpp b/include/pdf/pdf_shield_symbolizer.hpp new file mode 100644 index 000000000..592e6b51e --- /dev/null +++ b/include/pdf/pdf_shield_symbolizer.hpp @@ -0,0 +1,95 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef PDF_SHIELD_SYMBOLIZER_HPP +#define PDF_SHIELD_SYMBOLIZER_HPP + +#include +#include +#include +#include + + +namespace mapnik +{ + class MAPNIK_DECL pdf_shield_symbolizer + : public shield_symbolizer + { + public: + //! \brief constructor + //! + //! \param[in] name The name of the attribute to use as the label + //! \param[in] face_name The name of the font + //! \param[in] size The size of the font + //! \param[in] fill The font colour + //! \param[in] file The image file + //! \param[in] type The type of the file, e.g. "png" + //! \param[in] image_width The width of the image in pixels + //! \param[in] image_height The height of the image in pixels + //! \param[in] _output_width The scaled (output) width of the image (in pdf units) + //! \param[in] _output_height The scaled (output) height of the image (in pdf units) + pdf_shield_symbolizer(std::string const& name, + std::string const& face_name, + unsigned size, + Color const& fill, + std::string const& file, + std::string const& type, + unsigned image_width, unsigned image_height, + double _output_width, double _output_height); + + //! \brief constructor + //! + //! Copies the supplied pdf_shield_symbolizer + pdf_shield_symbolizer(pdf_shield_symbolizer const& rhs); + + //! \brief constructor + //! + //! Takes a pdf_shield_symbolizer and turns it into a pdf_shield_symbolizer + //! + //! \param[in] rhs The pdf_shield_symbolizer to start with + //! \param[in] _output_width The width of the image in pdf units + //! \param[in] _output_height The height of the image in pdf units + pdf_shield_symbolizer(shield_symbolizer const& rhs, const double _output_width, const double _output_height); + + //! \brief Sets the output size of the image + //! + //! \param[in] width Width in pdf units + //! \param[in] height Height in pdf units + void set_output_size(const double width, const double height); + + //! \brief Gets the output width of an image + //! + //! \return The output width of the image in pdf units + double get_output_width(void) const { return output_width; }; + + //! \brief Gets the output height of an image + //! + //! \return The output height of the image in pdf units + double get_output_height(void) const { return output_height; }; + + private: + double output_width; //!< output width of image in pdf units + double output_height; //!< output height of image in pdf units + }; +} + +#endif // PDF_SHIELD_SYMBOLIZER_HPP diff --git a/msvc/agg/agg.vcproj b/msvc/agg/agg.vcproj new file mode 100644 index 000000000..714ef9df4 --- /dev/null +++ b/msvc/agg/agg.vcproj @@ -0,0 +1,758 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/binding_python/binding_python.vcproj b/msvc/binding_python/binding_python.vcproj new file mode 100644 index 000000000..5fd079857 --- /dev/null +++ b/msvc/binding_python/binding_python.vcproj @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/core/core.vcproj b/msvc/core/core.vcproj new file mode 100644 index 000000000..09d7d3203 --- /dev/null +++ b/msvc/core/core.vcproj @@ -0,0 +1,807 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/input_raster/input_raster.vcproj b/msvc/input_raster/input_raster.vcproj new file mode 100644 index 000000000..1b33ac6a7 --- /dev/null +++ b/msvc/input_raster/input_raster.vcproj @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/input_shapefile/input_shapefile.vcproj b/msvc/input_shapefile/input_shapefile.vcproj new file mode 100644 index 000000000..a3dd8807a --- /dev/null +++ b/msvc/input_shapefile/input_shapefile.vcproj @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/msvc.sln b/msvc/msvc.sln new file mode 100644 index 000000000..bbe3f0262 --- /dev/null +++ b/msvc/msvc.sln @@ -0,0 +1,75 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "agg", "agg\agg.vcproj", "{3BFF8124-56D1-4722-81B9-8B1810D1825C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mapnik", "core\core.vcproj", "{AFB929AA-7EE5-44CC-AC30-F08D120233F2}" + ProjectSection(ProjectDependencies) = postProject + {3BFF8124-56D1-4722-81B9-8B1810D1825C} = {3BFF8124-56D1-4722-81B9-8B1810D1825C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapeindex", "shapeindex\shapeindex.vcproj", "{FCF9FDD7-0B23-4FDA-A965-5275D1682362}" + ProjectSection(ProjectDependencies) = postProject + {AFB929AA-7EE5-44CC-AC30-F08D120233F2} = {AFB929AA-7EE5-44CC-AC30-F08D120233F2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "input_shapefile", "input_shapefile\input_shapefile.vcproj", "{9AF4294B-E941-4FF5-BC3F-A47C47C577F7}" + ProjectSection(ProjectDependencies) = postProject + {AFB929AA-7EE5-44CC-AC30-F08D120233F2} = {AFB929AA-7EE5-44CC-AC30-F08D120233F2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "input_raster", "input_raster\input_raster.vcproj", "{7F0D48DE-482F-42A0-8D19-2C52DCEAFEDD}" + ProjectSection(ProjectDependencies) = postProject + {AFB929AA-7EE5-44CC-AC30-F08D120233F2} = {AFB929AA-7EE5-44CC-AC30-F08D120233F2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "binding_python", "binding_python\binding_python.vcproj", "{D4904853-B226-4142-B5AD-871BEE3C497B}" + ProjectSection(ProjectDependencies) = postProject + {AFB929AA-7EE5-44CC-AC30-F08D120233F2} = {AFB929AA-7EE5-44CC-AC30-F08D120233F2} + {3BFF8124-56D1-4722-81B9-8B1810D1825C} = {3BFF8124-56D1-4722-81B9-8B1810D1825C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pdf", "pdf\pdf.vcproj", "{9853DAB2-975F-454B-9B62-00C2718C5F70}" + ProjectSection(ProjectDependencies) = postProject + {AFB929AA-7EE5-44CC-AC30-F08D120233F2} = {AFB929AA-7EE5-44CC-AC30-F08D120233F2} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3BFF8124-56D1-4722-81B9-8B1810D1825C}.Debug|Win32.ActiveCfg = Debug|Win32 + {3BFF8124-56D1-4722-81B9-8B1810D1825C}.Debug|Win32.Build.0 = Debug|Win32 + {3BFF8124-56D1-4722-81B9-8B1810D1825C}.Release|Win32.ActiveCfg = Release|Win32 + {3BFF8124-56D1-4722-81B9-8B1810D1825C}.Release|Win32.Build.0 = Release|Win32 + {AFB929AA-7EE5-44CC-AC30-F08D120233F2}.Debug|Win32.ActiveCfg = Debug|Win32 + {AFB929AA-7EE5-44CC-AC30-F08D120233F2}.Debug|Win32.Build.0 = Debug|Win32 + {AFB929AA-7EE5-44CC-AC30-F08D120233F2}.Release|Win32.ActiveCfg = Release|Win32 + {AFB929AA-7EE5-44CC-AC30-F08D120233F2}.Release|Win32.Build.0 = Release|Win32 + {FCF9FDD7-0B23-4FDA-A965-5275D1682362}.Debug|Win32.ActiveCfg = Debug|Win32 + {FCF9FDD7-0B23-4FDA-A965-5275D1682362}.Debug|Win32.Build.0 = Debug|Win32 + {FCF9FDD7-0B23-4FDA-A965-5275D1682362}.Release|Win32.ActiveCfg = Release|Win32 + {FCF9FDD7-0B23-4FDA-A965-5275D1682362}.Release|Win32.Build.0 = Release|Win32 + {9AF4294B-E941-4FF5-BC3F-A47C47C577F7}.Debug|Win32.ActiveCfg = Debug|Win32 + {9AF4294B-E941-4FF5-BC3F-A47C47C577F7}.Debug|Win32.Build.0 = Debug|Win32 + {9AF4294B-E941-4FF5-BC3F-A47C47C577F7}.Release|Win32.ActiveCfg = Release|Win32 + {9AF4294B-E941-4FF5-BC3F-A47C47C577F7}.Release|Win32.Build.0 = Release|Win32 + {7F0D48DE-482F-42A0-8D19-2C52DCEAFEDD}.Debug|Win32.ActiveCfg = Debug|Win32 + {7F0D48DE-482F-42A0-8D19-2C52DCEAFEDD}.Debug|Win32.Build.0 = Debug|Win32 + {7F0D48DE-482F-42A0-8D19-2C52DCEAFEDD}.Release|Win32.ActiveCfg = Release|Win32 + {7F0D48DE-482F-42A0-8D19-2C52DCEAFEDD}.Release|Win32.Build.0 = Release|Win32 + {D4904853-B226-4142-B5AD-871BEE3C497B}.Debug|Win32.ActiveCfg = Debug|Win32 + {D4904853-B226-4142-B5AD-871BEE3C497B}.Debug|Win32.Build.0 = Debug|Win32 + {D4904853-B226-4142-B5AD-871BEE3C497B}.Release|Win32.ActiveCfg = Release|Win32 + {D4904853-B226-4142-B5AD-871BEE3C497B}.Release|Win32.Build.0 = Release|Win32 + {9853DAB2-975F-454B-9B62-00C2718C5F70}.Debug|Win32.ActiveCfg = Debug|Win32 + {9853DAB2-975F-454B-9B62-00C2718C5F70}.Debug|Win32.Build.0 = Debug|Win32 + {9853DAB2-975F-454B-9B62-00C2718C5F70}.Release|Win32.ActiveCfg = Release|Win32 + {9853DAB2-975F-454B-9B62-00C2718C5F70}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msvc/pdf/pdf.vcproj b/msvc/pdf/pdf.vcproj new file mode 100644 index 000000000..006d6c8f9 --- /dev/null +++ b/msvc/pdf/pdf.vcproj @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/readme.txt b/msvc/readme.txt new file mode 100644 index 000000000..2cc2ca168 --- /dev/null +++ b/msvc/readme.txt @@ -0,0 +1,281 @@ +-------------------------------------------------------------------------------- +General: + Built using visual studio c++ 2005 express edition + - with the platform SDK + + +-------------------------------------------------------------------------------- +Boost + +http://www.boost.org/users/download/ + +1) download and extract to "c:/program files/boost/" or equivalent + it will make a directory like "c:/program files/boost/boost_1_35_0/" +2) read /index.htm and follow the getting started instructions on compiling the + libraries for your environment + + +-------------------------------------------------------------------------------- +wxWidgets + +http://www.wxwidgets.org/downloads/#latest_stable + + +1) download and extract the wxAll source archive to /thirdparty/wxWidgets/ + (e.g. there will now be a directory /thirdparty/wxWidgets/wxWidgets-x.x.x/ ) +2) open the workspace "wxWidgets/wxWidgets-x.x.x/build/msw/wx.dsw" + - let visual studio perform conversion if required +3) Select the 'Unicode Debug' configuration and build the complete solution +4) if you want, build the complete solution with the 'Unicode Release' configuration +5) create an environment variable WXDIR with value "path/to/mapnik/thirdparty/wxWidgets/wxWidgets-x.x.x/" + + +-------------------------------------------------------------------------------- +wxPdfDoc + +http://wxcode.sourceforge.net/components/wxpdfdoc/ + +1) download wxPdfDocument 0.8.0 +2) create a directory /thirdparty/wxWidgets/wxCode/components +3) extract wxPdfDocument into /thirdparty/wxWidgets/wxCode/components +4) open the workspace "wxpdfdoc/build/wxpdfdoc.dsw" + - let visual studio perform conversion if required +5) choose the 'Unicode Debug Monolithic' configuration +6) right click the 'wxpdfdoc' project, Properties and go to C/C++ / General properties + change WXWIN to WXDIR in the 'Additional include directories' line. It should look like: + $(WXDIR)\lib\vc_lib\mswud,$(WXDIR)\include,..\include + +7) apply the patch wxpdfdoc_patch_20080112.patch or copy the files manually +8) add the files pdfoc.h and pdfoc.cpp to the project + +9) build wxpdfdoc +10) repeat step 6 for the 'makefont' project + It should look like: "$(WXDIR)\lib\vc_lib\mswud";"$(WXDIR)\include";..\include;..\..\include + - and also change WXWIN to WXDIR in the resources / General properties 'Additional include directories' + - and also change WXWIN to WXDIR in the linker / general properties 'additional library directories' +11) In the Linker/input properties, change wxmsw26ud.lib to wxbase28ud.lib and add wxbase28ud_xml.lib +12) build makefont +13) if you want, repeat for the 'Unicode Release Monolithic' configuration +14) create and environment variable WXPDFDIR with value "path/to/mapnik/thirdparty/wxWidgets/wxCode/components/wxpdfdoc" + + +If this doesnt work, compare the command lines and work out whats different + +Example command line for wxpdfdoc project compiler: + /Od /I "D:\OTM\mapnik\trunk\thirdparty\wxWidgets\wxWidgets-2.8.7\lib\vc_lib\mswud" /I "D:\OTM\mapnik\trunk\thirdparty\wxWidgets\wxWidgets-2.8.7\include" /I "..\include" /D "WIN32" /D "_LIB" /D "_DEBUG" /D "__WXDEBUG__" /D "__WXMSW__" /D "_VC80_UPGRADE=0x0600" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /MDd /Fp".\msvc6prjud_lib\wxpdfdoc/wxpdfdoc_wxpdfdoc.pch" /Fo".\msvc6prjud_lib\wxpdfdoc/" /Fd"..\lib\wxpdfdocud.pdb" /W4 /nologo /c /Zi /TP /errorReport:prompt + +Example command line for makefont project compiler: + /Od /I "D:\OTM\mapnik\trunk\thirdparty\wxWidgets\wxWidgets-2.8.7\lib\vc_lib\mswud" /I "D:\OTM\mapnik\trunk\thirdparty\wxWidgets\wxWidgets-2.8.7\include" /I "..\include" /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "__WXDEBUG__" /D "__WXMSW__" /D "_CONSOLE" /D "_VC80_UPGRADE=0x0600" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /MDd /Fp".\msvc6prjud_lib\makefont/wxpdfdoc_makefont.pch" /Fo".\msvc6prjud_lib\makefont/" /Fd"..\makefont\makefont.pdb" /W4 /nologo /c /Zi /TP /errorReport:prompt + +Example command line for makefont project linker: + /OUT:"..\makefont\makefont.exe" /INCREMENTAL /NOLOGO /LIBPATH:"D:\OTM\mapnik\trunk\thirdparty\wxWidgets\wxWidgets-2.8.7\lib\vc_lib" /LIBPATH:"..\lib" /MANIFEST /MANIFESTFILE:".\msvc6prjud_lib\makefont\makefont.exe.intermediate.manifest" /DEBUG /PDB:".\..\makefont/makefont.pdb" /SUBSYSTEM:CONSOLE /MACHINE:X86 /ERRORREPORT:PROMPT ..\lib\wxpdfdocud.lib wxbase28ud.lib wxbase28ud_xml.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib winmm.lib comctl32.lib rpcrt4.lib wsock32.lib odbc32.lib oleacc.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib + + + +-------------------------------------------------------------------------------- +ICU + +http://icu-project.org/download/ + +1) download and extract to /thirdparty/icu-x.x.x +2) open the solution /icu-x.x.x/source/allinone/allinone.sln +3) Select the 'Debug' configuration and build the complete solution +4) if you want, build the complete solution with the 'Release' configuration +5) create an environment variable ICUDIR with value "path/to/mapnik/thirdparty/icu-x.x.x/" + + +-------------------------------------------------------------------------------- +freetype + +http://freetype.sourceforge.net/download.html#stable +(e.g. http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz) + +1) download and extract to /thirdparty/freetype-x.x.x +2) open the solution /freetype-x.x.x/builds/win32/visualc/freetype.sln +3) Select the 'Debug' configuration +4) build the solution +5) if you want, build the solution with the 'Release multithreaded' configuration +6) create an environment variable FREETYPEDIR with value "path/to/mapnik/thirdparty/freetype-x.x.x/" + + + + +-------------------------------------------------------------------------------- +zlib + +http://www.zlib.net/ +(http://www.zlib.net/zlib-1.2.3.tar.gz) + +1) download and extract to /thirdparty/zlib-x.x.x +2) open the workspace in "/zlib-x.x.x/projects/visualc6/zlib.dsw" + - let visual studio perform conversion if required +3) select the 'lib debug' configuration and build the zlib project +4) if you want, try the asm optimised versions and release builds +5) create an environment variable ZLIBDIR with value "path/to/mapnik/thirdparty/zlib-x.x.x/" + + +-------------------------------------------------------------------------------- +libpng + +http://www.libpng.org/pub/png/libpng.html +(ftp://ftp.simplesystems.org/pub/libpng/png/src/lpng1225.zip) + +1) download and extract to /thirdparty/lpngxxxx +2) open the solution /lpng1225/projects/visualc71/libpng.sln + - let visual studio perform conversion if required +3) add zlib to the 'additional include directories' of libpng + e.g. it should look like: ..\..;"..\..\..\zlib-1.2.3" +4) remove the project dependancy on zlib (right click project, project dependancies ...) +5) select the 'lib debug' configuration and build the libpng project +6) if you want, try the asm optimised versions and release builds +2) create an environment variable LIBPNGDIR with value "path/to/mapnik/thirdparty/lpngxxxx/" + +-------------------------------------------------------------------------------- +libjpeg + +http://www.ijg.org/ +ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz + +1) download and extract to /thirdparty/jpeg-6b/ +2) copy jconfig.vc to jconfig.h +3) open a visual studio command prompt and change to the /thirdparty/jpeg-x directory +4) nmake /f makefile.vc all + +-------------------------------------------------------------------------------- +libtiff + +http://www.libtiff.org/ +(ftp://ftp.remotesensing.org/pub/libtiff/tiff-3.8.2.tar.gz) + + +1) download and extract to /thirdparty/libtiff-x.x.x +2) open the nmake.opt file and uncomment the JPEG_SUPPORT and ZIP_SUPPORT sections (also update ZLIBDIR and JPEGDIR) +2) open a visual studio command prompt and change to the /thirdparty/libtiff-x.x.x directory +3) nmake /f makefile.vc +4) change to tools directory +5) nmake /f makefile.vc + +-------------------------------------------------------------------------------- +libtool (ltdl) + +http://gnuwin32.sourceforge.net/packages/libtool.htm +(http://gnuwin32.sourceforge.net/downlinks/libtool-bin-zip.php) + +1) download and extract the binaries package to /thirdparty/libtool-x.x.x +2) create an environment variable LIBTOOLDIR with value "path/to/mapnik/thirdparty/libtool-x.x.x/" + + +-------------------------------------------------------------------------------- +proj.4 + +http://www.remotesensing.org/proj/ +(ftp://ftp.remotesensing.org/proj/proj-4.6.0.tar.gz) +(ftp://ftp.remotesensing.org/proj/proj-datumgrid-1.3.zip) + + +1) download and extract to /thirdparty/proj-x.x.x +2) if you have more datum definition files, extract them to the /proj-x.x.x/nad directory +3) edit /proj-x.x.x/src/makefile.vc + - change PROJ_LIB_DIR to point to the proper place e.g: PROJ_LIB_DIR=D:\OTM\mapnik\trunk\thirdparty\proj-4.6.0\nad +4) open a visual studio command prompt and change the the /proj-x.x.x/src/ directory +5) nmake /f makefile.vc all +6) create an environment variable PROJ4DIR with value "path/to/mapnik/thirdparty/proj-x.x.x/" + + +-------------------------------------------------------------------------------- +python bindings + +1) create an environment variable PYTHONDIR with the value "path/to/python" e.g. "c:\program files\python25" +2) copy "/mapnik/trunk/bindings/python/mapnik" directory to "/python/Lib/site-packages/mapnik" +3) create a file called paths.py in "/python/Lib/site-packages/mapnik/" + - add the lines: + +mapniklibpath = 'path/to/mapnik/msvc/Debug' +inputpluginspath = 'path/to/mapnik/msvc/Debug' +fontscollectionpath = 'path/to/mapnik/fonts/dejavu-ttf-2.14' + +e.g. + +mapniklibpath = 'D:/otm/mapnik/trunk/msvc/Debug' +inputpluginspath = 'D:/otm/mapnik/trunk/msvc/Debug' +fontscollectionpath = 'D:/otm/mapnik/trunk/fonts/dejavu-ttf-2.14' + +4) open __init__.py + - comment out lines 21-29 and 99, where it is using the get/set dlopenflags things +5) copy libltdl3.dll from "mapnik/thirdparth/libtool-x.x.x/bin" to "/python/Lib/site-packages/mapnik/" +6) copy boost_python-vc80-mt-gd-x_xx_x.dll from "boost/stage/lib" to "/python/Lib/site-packages/mapnik/" + +7) when you build mapnik and the python-bindings they will copy other files to "/python/Lib/site-packages/mapnik/" + +-- after you've build mapnik and the python-bindings (later) + +8) start python +>>> import mapnik +>>> dir(mapnik) +and you shouldn't get any errors + + + +-------------------------------------------------------------------------------- +mapnik + +1) create an environment variable MAPNIKDIR with value "path/to/mapnik" e.g. "D:\OTM\mapnik\trunk\" +2) In visual studio, do Tools menu, Options, projects and solutions, vc++ directories + - choose include files + - add: + $(LIBTIFFDIR)\libtiff\ + $(ZLIBDIR) + $(PROJ4DIR)\src + $(LIBJPEGDIR) + $(LIBPNGDIR) + $(LIBTOOLDIR)\include + $(FREETYPEDIR)\include + $(ICUDIR)\include\ + + + +-------------------------------------------------------------------------------- +building + +1) just hit build all and it should all work. +2) to test things, try the pdf project. + - You'll need to update the four defines at the top of main.cpp in the pdf project. + Point them to the proper locations on your machine. + + + + + +-------------------------------------------------------------------------------- +alternative libpng + +http://gnuwin32.sourceforge.net/packages/libpng.htm +(http://gnuwin32.sourceforge.net/downlinks/libpng-lib-zip.php) + +1) download and extract the 'developer files' package to /thirdparty/libpng-x.x.x +2) create an environment variable LIBPNGDIR with value "path/to/mapnik/thirdparty/libpng-x.x.x/" + + +-------------------------------------------------------------------------------- +alternative libtiff + +http://gnuwin32.sourceforge.net/packages/tiff.htm +(http://gnuwin32.sourceforge.net/downlinks/tiff-lib-zip.php) + +1) download and extract the 'developer files' package to /thirdparty/libtiff-x.x.x-x +2) create an environment variable LIBTIFFDIR with value "path/to/mapnik/thirdparty/libtiff-x.x.x-x/" + + +-------------------------------------------------------------------------------- +alternative libjpeg + +http://gnuwin32.sourceforge.net/packages/libjpeg.htm +(http://gnuwin32.sourceforge.net/downlinks/jpeg-lib-zip.php) + +1) download and extract the 'developer files' package to /thirdparty/libjpeg-6b +2) create an environment variable LIBJPEGDIR with value "path/to/mapnik/thirdparty/libjpeg-6b/" + + + + + diff --git a/msvc/shapeindex/shapeindex.vcproj b/msvc/shapeindex/shapeindex.vcproj new file mode 100644 index 000000000..24487ec09 --- /dev/null +++ b/msvc/shapeindex/shapeindex.vcproj @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pdf/font_engine_pdf.cpp b/pdf/font_engine_pdf.cpp new file mode 100644 index 000000000..e82bd2cd3 --- /dev/null +++ b/pdf/font_engine_pdf.cpp @@ -0,0 +1,200 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#if ENABLE_PDF + +// pdf +#include + +namespace mapnik { + + pdf_text_renderer::pdf_text_renderer(wxPdfDocument &_pdf) + : fill(0,0,0), + halo_fill(255,255,255), + halo_radius(0), + default_font_name(_T("arial")) + { + pdf = &_pdf; + font_description = NULL; + + //set the other defaults + set_size(10); + set_font(default_font_name); + + } + + + void pdf_text_renderer::set_external_font_options(const std::wstring& externalFontPath, const bool enableSubsetting) { + pdf->SetFontPath(externalFontPath); + pdf->SetFontSubsetting(enableSubsetting); + } + + + bool pdf_text_renderer::add_external_font(const std::wstring& fontName, const std::wstring& fontXMLFile) { + return pdf->AddFont(fontName, _T(""), fontXMLFile); + } + + + + bool pdf_text_renderer::set_font(const std::string& font) { + std::wstring wFont; + + //convert to wide name + std::string::const_iterator itr; + for(itr = font.begin();itr!=font.end();itr++) { + wFont.push_back(*itr); + } + + return set_font(wFont); + } + + bool pdf_text_renderer::set_font(const std::wstring& font) { + + //try to set font + if(pdf->SetFont(font, _T(""), pdf->GetFontSize())) + { + font_name = font; + + font_description = &pdf->GetFontDescription(); + + return true; + } + + //if we get here, give up and use the default which always exists (internal pdf font) + std::wclog << __FILE__ << ":" << __LINE__ << ": failed to set font: " << font << ". Using default: " << default_font_name << "\n"; + pdf->SetFont(default_font_name, _T(""), pdf->GetFontSize()); + font_description = &pdf->GetFontDescription(); + + return false; + } + + + void pdf_text_renderer::set_size(const double size) { + font_size = size; + pdf->SetFontSize(font_size * pdf->GetScaleFactor()); + } + + + void pdf_text_renderer::set_fill(mapnik::Color const& _fill) + { + fill = _fill; + } + + + void pdf_text_renderer::set_halo_fill(mapnik::Color const& _halo) + { + halo_fill = _halo; + } + + + void pdf_text_renderer::set_halo_radius( const double radius ) + { + halo_radius = radius; + } + + + pdf_text_renderer::char_dimension_t pdf_text_renderer::character_dimensions(const unsigned c) + { + std::wstring str; + str.push_back(c); + + char_dimension_t dim; + dim.first = pdf->GetStringWidth(str); //gets it in pdf units, not points. no conversion needed. + dim.second = (font_description->GetCapHeight() - font_description->GetDescent()) * font_size / 1000.0; // The cap-height and descent are scaled by 1000, and are relative to a font size of 1. + + return dim; + } + + + void pdf_text_renderer::get_string_info(string_info *info) + { + double width = 0; + double height = 0; + + const UnicodeString &text = info->get_string(); + + for(int i=0;iadd_info(c, char_dim.first, char_dim.second); + + width += char_dim.first; + height = char_dim.second > height ? char_dim.second : height; + } + info->set_dimensions(width, height); + } + + void pdf_text_renderer::prepare_render(void) + { + pdf->SetTextColor(fill.red(), fill.green(), fill.blue()); + wxPdfLineStyle lineStyle; + lineStyle.SetWidth(halo_radius); + lineStyle.SetColour(wxPdfColour(halo_fill.red(), halo_fill.green(), halo_fill.blue())); + lineStyle.SetLineJoin(wxPDF_LINEJOIN_ROUND); + lineStyle.SetLineCap(wxPDF_LINECAP_ROUND); + pdf->SetLineStyle(lineStyle); + + } + + + + void pdf_text_renderer::render(text_path *path, double x0, double y0) + { + int c; + double x, y, angle; + std::wstring character; + + //render the halos if they're used + if(halo_radius > 0) { + for (int i = 0; i < path->num_nodes(); i++) + { + path->vertex(&c, &x, &y, &angle); + + character.clear(); + character.push_back(c); + + //note that angle has to be converted from radians to degrees, and y direction is opposite to what you expect + pdf->RotatedText(x0 + x, y0 - y, character, (180.0/M_PI)*angle, 1); //stroke, no fill + } + } + + //rewind the iterator + path->itr_ = 0; + + //render the text + for (int i = 0; i < path->num_nodes(); i++) + { + path->vertex(&c, &x, &y, &angle); + + character.clear(); + character.push_back(c); + + //note that angle has to be converted from radians to degrees, and y direction is opposite to what you expect + pdf->RotatedText(x0 + x, y0 - y, character, (180.0/M_PI)*angle, 0); //fill, no stroke + } + } + +} + +#endif //ENABLE_PDF diff --git a/pdf/pdf_line_pattern_symbolizer.cpp b/pdf/pdf_line_pattern_symbolizer.cpp new file mode 100644 index 000000000..4ffbc74f5 --- /dev/null +++ b/pdf/pdf_line_pattern_symbolizer.cpp @@ -0,0 +1,73 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + + +#if ENABLE_PDF + +// pdf +#include + +// mapnik +#include +#include + +// stl +#include + +namespace mapnik +{ + + pdf_line_pattern_symbolizer::pdf_line_pattern_symbolizer(std::string const& file, std::string const& type, const double line_width, const unsigned image_width, const unsigned image_height, const double _output_width, const double _output_height) + : line_pattern_symbolizer(file, type, image_width, image_height), + line_stroke(Color(), line_width) + { + output_width = _output_width; + output_height = _output_height; + } + + pdf_line_pattern_symbolizer::pdf_line_pattern_symbolizer(pdf_line_pattern_symbolizer const& rhs) + : line_pattern_symbolizer(rhs) + { + output_width = rhs.output_width; + output_height = rhs.output_height; + line_stroke = rhs.line_stroke; + } + + pdf_line_pattern_symbolizer::pdf_line_pattern_symbolizer(line_pattern_symbolizer const& rhs, const double line_width, const double _output_width, const double _output_height) + : line_pattern_symbolizer(rhs), + line_stroke(Color(), line_width) + { + output_width = _output_width; + output_height = _output_height; + } + + + void pdf_line_pattern_symbolizer::set_output_size(const double width, const double height) + { + output_width = width; + output_height = height; + } + + +} //namespace mapnik + +#endif //ENABLE_PDF diff --git a/pdf/pdf_point_symbolizer.cpp b/pdf/pdf_point_symbolizer.cpp new file mode 100644 index 000000000..eacc9e14d --- /dev/null +++ b/pdf/pdf_point_symbolizer.cpp @@ -0,0 +1,80 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#if ENABLE_PDF + +// pdf +#include + +// mapnik +#include +#include +// boost +#include +// stl +#include + +namespace mapnik +{ + pdf_point_symbolizer::pdf_point_symbolizer() + : point_symbolizer() + { + output_width = 1; + output_height = 1; + } + + pdf_point_symbolizer::pdf_point_symbolizer( std::string const& file, + std::string const& type, + unsigned image_width, unsigned image_height, + double _output_width, double _output_height) + : point_symbolizer(file, type, image_width, image_height) + { + output_width = _output_width; + output_height = _output_height; + } + + + pdf_point_symbolizer::pdf_point_symbolizer(pdf_point_symbolizer const& rhs) + : point_symbolizer(rhs) + { + output_width = rhs.get_output_width(); + output_height = rhs.get_output_height(); + } + + pdf_point_symbolizer::pdf_point_symbolizer(point_symbolizer const& rhs, const double _output_width, const double _output_height) + : point_symbolizer(rhs) + { + output_width = _output_width; + output_height = _output_height; + } + + + + void pdf_point_symbolizer::set_output_size(const double width, const double height) + { + output_width = width; + output_height = height; + } + +} + +#endif //ENABLE_PDF diff --git a/pdf/pdf_polygon_pattern_symbolizer.cpp b/pdf/pdf_polygon_pattern_symbolizer.cpp new file mode 100644 index 000000000..1a08bb746 --- /dev/null +++ b/pdf/pdf_polygon_pattern_symbolizer.cpp @@ -0,0 +1,69 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#if ENABLE_PDF + +// pdf +#include + +// mapnik +#include +#include + +// stl +#include + +namespace mapnik +{ + + pdf_polygon_pattern_symbolizer::pdf_polygon_pattern_symbolizer(std::string const& file, std::string const& type, unsigned image_width, unsigned image_height, double _output_width, double _output_height) + : polygon_pattern_symbolizer(file, type, image_width, image_height) + { + output_width = _output_width; + output_height = _output_height; + } + + pdf_polygon_pattern_symbolizer::pdf_polygon_pattern_symbolizer(pdf_polygon_pattern_symbolizer const& rhs) + : polygon_pattern_symbolizer(rhs) + { + output_width = rhs.output_width; + output_height = rhs.output_height; + } + + pdf_polygon_pattern_symbolizer::pdf_polygon_pattern_symbolizer(polygon_pattern_symbolizer const& rhs, const double _output_width, const double _output_height) + : polygon_pattern_symbolizer(rhs) + { + output_width = _output_width; + output_height = _output_height; + } + + + void pdf_polygon_pattern_symbolizer::set_output_size(const double width, const double height) + { + output_width = width; + output_height = height; + } + + +} //namespace mapnik + +#endif //ENABLE_PDF diff --git a/pdf/pdf_renderer.cpp b/pdf/pdf_renderer.cpp new file mode 100644 index 000000000..32245f215 --- /dev/null +++ b/pdf/pdf_renderer.cpp @@ -0,0 +1,1719 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//! \file pdf_renderer.cpp +//! \brief Implementation of pdf_renderer.hpp +//! + +#if ENABLE_PDF + +// pdf +#include +#include +#include + +// mapnik +#include +#include +#include +#include +#include + +// boost +#include +#include +#include + +// stl +#include + +// WX +#include +#include +#include + + +namespace mapnik +{ + + //--------------------------------------------------------------------------- + typedef boost::tuple segment_t; + + //--------------------------------------------------------------------------- + bool pdf_y_order(segment_t const& first,segment_t const& second) + { + double miny0 = std::min(first.get<1>(),first.get<3>()); + double miny1 = std::min(second.get<1>(),second.get<3>()); + return miny0 > miny1; + } + + //--------------------------------------------------------------------------- + pdf_renderer::pdf_renderer(Map const& m, wxPdfDocument &_pdf, const pdf_renderer_layout &_page_layout, const double offset_x, const double offset_y) + : feature_style_processor(m), + coord_trans(_page_layout.map_area.width(), + _page_layout.map_area.height(), + m.getCurrentExtent(), + offset_x - _page_layout.map_area.minx(), + offset_y - _page_layout.map_area.miny()), + detector(Envelope(-64 ,-64, m.getWidth() + 64 ,m.getHeight() + 64)), + text_renderer(_pdf), + page_layout(_page_layout), + map(m) + { + line_miter_limit = 4.0; //The value of 4.0 is hard-coded by the agg_renderer, whether this value for pdf documents means the same thing is unknown, but it doesnt look too bad + unnamed_image_count = 0; + + pdf = &_pdf; + pdf->AddPage(); + + pdf->SetFont(_T("arial"), _T(""), 10); + + //I dont see why I should have to do this, but apparently I do. + wxInitAllImageHandlers(); + } + + + //--------------------------------------------------------------------------- + void pdf_renderer::set_external_font_options(const std::wstring& externalFontPath, const bool enableSubsetting) { + text_renderer.set_external_font_options(externalFontPath, enableSubsetting); + } + + + //--------------------------------------------------------------------------- + bool pdf_renderer::add_external_font(const std::wstring& fontName, const std::wstring& fontXMLFile) { + return text_renderer.add_external_font(fontName, fontXMLFile); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::start_map_processing(Map const& map) + { + //save graphics state + pdf->StartTransform(); + + //render the map background colour + if(map.background()) { + wxPdfColour mapBackgroundColour(map.background()->red(), map.background()->green(), map.background()->blue()); + pdf->SetFillColor(mapBackgroundColour); + pdf->Rect(page_layout.map_area.minx(), page_layout.page_area.miny(), page_layout.map_area.maxx(), page_layout.page_area.maxy(), wxPDF_STYLE_FILL); + } + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::end_map_processing(Map const& map) + { + //save graphics state + pdf->StartTransform(); + + //blank off the area of the map outside the page_layout.map_area + pdf->SetFillColor(wxPdfColour(page_layout.page_background_colour.red(), page_layout.page_background_colour.green(), page_layout.page_background_colour.blue())); + pdf->Rect(0,0, page_layout.map_area.minx(), page_layout.page_area.maxy(), wxPDF_STYLE_FILL); + pdf->Rect(page_layout.map_area.maxx(), 0, page_layout.page_area.maxx() - page_layout.map_area.maxx(), page_layout.page_area.maxy(), wxPDF_STYLE_FILL); + pdf->Rect(0,0, page_layout.page_area.maxx(), page_layout.map_area.miny(), wxPDF_STYLE_FILL); + pdf->Rect(0, page_layout.map_area.maxy(), page_layout.page_area.maxx(), page_layout.page_area.maxy() - page_layout.map_area.maxy(), wxPDF_STYLE_FILL); + + //restore graphics state + pdf->StopTransform(); + + + //render the overlays + render_overlays(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::start_layer_processing(Layer const& lay) + { + wxPdfOcg *l = new wxPdfOcg(convert_string_to_wstring(lay.name())); + pdf->AddOcg(l); + + pdf->EnterOcg(l); + + //clear the detector if the layer has requested it. If this isn't + // done, then symbolizers which use the detector won't find a place + // if there is something already placed in a lower layer in an + // intersecting envelope. + if (lay.clear_label_cache()) + { + detector.clear(); + } + } + + //--------------------------------------------------------------------------- + void pdf_renderer::end_layer_processing(Layer const& lay) + { + pdf->ExitOcg(); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(point_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + std::clog << "WARNING: point_symbolizer should not be used for pdf rendering, use pdf_point_symbolizer\n\tThe symbol will be incorrectly scaled with 1:1 pixel:pdf-unit scaling"; + + //convert it to a pdf_point_symbolizer and process it + pdf_point_symbolizer tmp_sym(sym, sym.get_image()->width(), sym.get_image()->height()); + process(tmp_sym, feature, prj_trans); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(pdf_point_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + double x; + double y; + double z=0; + + //save graphics state + pdf->StartTransform(); + + //try to get the image for the point + boost::shared_ptr const& data = sym.get_image(); + if ( data ) //if there is an image + { + unsigned int geometry_index; + for( geometry_index=0; geometry_index < feature.num_geometries(); ++geometry_index ) + { + geometry2d const& geom = feature.get_geometry(geometry_index); + + //get the position (center of image) in the correct coordinate system + geom.label_position(&x,&y); + prj_trans.backward(x,y,z); + coord_trans.forward(&x,&y); + + //calculate the origin of the image (x,y are center of image) + double w; + double h; + + //since this is a pdf_point_symbolizer, use the proper output width/height + w = sym.get_output_width(); + h = sym.get_output_height(); + + double px= x - (0.5 * w); + double py= y - (0.5 * h); + + //calculate the envelope of the image to pass to the detector + Envelope label_ext (floor(x - (0.5 * w)), + floor(y - (0.5 * h)), + ceil (x + (0.5 * w)), + ceil (y + (0.5 * h))); + + //if the detector found somewhere to put it, or if the symbol is + // allowed to overlap with other things, then draw the image + if (sym.get_allow_overlap() || detector.has_placement(label_ext)) + { + //convert the image + wxImage *image = NULL; + create_wximage_from_imagedata(data, &image); + + //convert image name to wide name + std::wstring imageName = convert_string_to_wstring(sym.get_filename());; + + //draw the image + if(!pdf->Image(imageName, *image, px, py, w, h)) { + std::wclog << __FILE__ << ":" << __LINE__ << ": failed to draw image " << imageName << "\n"; + } + + //finished with the image + delete image; + image = NULL; + + //tell the detector to remember where we put something + detector.insert(label_ext); + } + } + } + + //restore graphics state + pdf->StopTransform(); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(line_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + mapnik::stroke const& mapnik_stroke = sym.get_stroke(); + Color const& mapnik_col = mapnik_stroke.get_color(); + + // set the alpha to use (colour is set in the lineStyle) + pdf->SetAlpha(mapnik_stroke.get_opacity(), mapnik_stroke.get_opacity(), wxPDF_BLENDMODE_NORMAL); + + //generate the line style style + wxPdfLineStyle pdf_lineStyle; + pdf_lineStyle.SetColour(wxPdfColour(mapnik_col.red(),mapnik_col.green(),mapnik_col.blue())); + switch(mapnik_stroke.get_line_cap()) { + case BUTT_CAP: + pdf_lineStyle.SetLineCap(wxPDF_LINECAP_BUTT); + break; + case SQUARE_CAP: + pdf_lineStyle.SetLineCap(wxPDF_LINECAP_SQUARE); + break; + case ROUND_CAP: + pdf_lineStyle.SetLineCap(wxPDF_LINECAP_ROUND); + break; + } + switch(mapnik_stroke.get_line_join()) { + case MITER_JOIN: + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_MITER); + pdf_lineStyle.SetMiterLimit(line_miter_limit); + break; + case MITER_REVERT_JOIN: + std::clog << __FILE__ << ":" << __LINE__ << ": MITER_REVERT_JOIN not supported, using MITER_JOIN.\n"; + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_MITER); + pdf_lineStyle.SetMiterLimit(line_miter_limit); + break; + case ROUND_JOIN: + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_ROUND); + break; + case BEVEL_JOIN: + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_BEVEL); + break; + } + pdf_lineStyle.SetWidth(mapnik_stroke.get_width()); + + wxPdfArrayDouble pdf_dash; + mapnik::dash_array const& mapnik_dash = mapnik_stroke.get_dash_array(); + dash_array::const_iterator itr = mapnik_dash.begin(); + dash_array::const_iterator end = mapnik_dash.end(); + for (;itr != end;++itr) + { + pdf_dash.Add( itr->first ); + pdf_dash.Add( itr->second ); + } + pdf_lineStyle.SetDash(pdf_dash); + + pdf->SetLineStyle(pdf_lineStyle); + + + //now parse the geometries + + unsigned int geometry_index; + //for each of the geometries contained in the feature + for (geometry_index=0; geometry_index < feature.num_geometries(); ++geometry_index) + { + //get the current geometry, and only continue if it is valid (more than 1 point) + geometry2d const& geom = feature.get_geometry(geometry_index); + + //make sure there are enough points + if(geom.num_points() < 2) { + std::clog << __FILE__ << ":" << __LINE__ << ": not enough points to make line\n"; + continue; + } + + + //wrap the geometry so we get the transformed vertexes + path_transform transformed_path(coord_trans, geom, prj_trans); + + wxPdfShape shape; + double x,y; + unsigned int cmd; + bool nextIsMove = true; // set to true if the next vertex should be done with a MoveTo rather than LineTo + + //until we get the SEG_END marker, keep adding vertexes + while((cmd = transformed_path.vertex(&x, &y)) != SEG_END) { + if(nextIsMove) { + shape.MoveTo(x, y); + nextIsMove = false; + } + else { + shape.LineTo(x, y); + } + } + + //render the path + pdf->Shape(shape, wxPDF_STYLE_DRAW); + + } + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(line_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + std::clog << "WARNING: line_pattern_symbolizer should not be used for pdf rendering, use pdf_line_pattern_symbolizer\n\tThe tile pattern will be incorrectly scaled with 1:1 pixel:pdf-unit scaling"; + + //convert it to a pdf_line_pattern_symbolizer and process it + double line_width = (sym.get_image()->width() + sym.get_image()->height()) / 2.0; + pdf_line_pattern_symbolizer tmp_sym(sym, line_width, sym.get_image()->width(), sym.get_image()->height()); + process(tmp_sym, feature, prj_trans); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(pdf_line_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + mapnik::stroke const& mapnik_stroke = sym.get_stroke(); + Color const& mapnik_col = mapnik_stroke.get_color(); + + // set the alpha to use (colour is set in the lineStyle) + pdf->SetAlpha(mapnik_stroke.get_opacity(), mapnik_stroke.get_opacity(), wxPDF_BLENDMODE_NORMAL); + + //generate the line style style + wxPdfLineStyle pdf_lineStyle; + pdf_lineStyle.SetColour(wxPdfColour(mapnik_col.red(),mapnik_col.green(),mapnik_col.blue())); + switch(mapnik_stroke.get_line_cap()) { + case BUTT_CAP: + pdf_lineStyle.SetLineCap(wxPDF_LINECAP_BUTT); + break; + case SQUARE_CAP: + pdf_lineStyle.SetLineCap(wxPDF_LINECAP_SQUARE); + break; + case ROUND_CAP: + pdf_lineStyle.SetLineCap(wxPDF_LINECAP_ROUND); + break; + } + switch(mapnik_stroke.get_line_join()) { + case MITER_JOIN: + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_MITER); + pdf_lineStyle.SetMiterLimit(line_miter_limit); + break; + case MITER_REVERT_JOIN: + std::clog << __FILE__ << ":" << __LINE__ << ": MITER_REVERT_JOIN not supported, using MITER_JOIN.\n"; + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_MITER); + pdf_lineStyle.SetMiterLimit(line_miter_limit); + break; + case ROUND_JOIN: + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_ROUND); + break; + case BEVEL_JOIN: + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_BEVEL); + break; + } + pdf_lineStyle.SetWidth(mapnik_stroke.get_width()); + + wxPdfArrayDouble pdf_dash; + mapnik::dash_array const& mapnik_dash = mapnik_stroke.get_dash_array(); + dash_array::const_iterator itr = mapnik_dash.begin(); + dash_array::const_iterator end = mapnik_dash.end(); + for (;itr != end;++itr) + { + pdf_dash.Add( itr->first ); + pdf_dash.Add( itr->second ); + } + pdf_lineStyle.SetDash(pdf_dash); + + pdf->SetLineStyle(pdf_lineStyle); + + //convert the image + boost::shared_ptr const& data = sym.get_image(); + wxImage *image = NULL; + create_wximage_from_imagedata(data, &image); + + std::wstring imageName = convert_string_to_wstring(sym.get_filename()); + + // create and set the draw pattern and color space + pdf->AddPattern(imageName, *image, imageName, sym.get_output_width(), sym.get_output_height()); + pdf->SetDrawColorSpace(1); + pdf->SetDrawPattern(imageName); + + //finished with image + delete image; + image = NULL; + + //now parse the geometries + + unsigned int geometry_index; + //for each of the geometries contained in the feature + for (geometry_index=0; geometry_index < feature.num_geometries(); ++geometry_index) + { + //get the current geometry, and only continue if it is valid (more than 1 point) + geometry2d const& geom = feature.get_geometry(geometry_index); + + //make sure there are enough points + if(geom.num_points() < 2) { + std::clog << __FILE__ << ":" << __LINE__ << ": not enough points to make line\n"; + continue; + } + + + //wrap the geometry so we get the transformed vertexes + path_transform transformed_path(coord_trans, geom, prj_trans); + + wxPdfShape shape; + double x,y; + unsigned int cmd; + bool nextIsMove = true; // set to true if the next vertex should be done with a MoveTo rather than LineTo + + //until we get the SEG_END marker, keep adding vertexes + while((cmd = transformed_path.vertex(&x, &y)) != SEG_END) { + if(nextIsMove) { + shape.MoveTo(x, y); + nextIsMove = false; + } + else { + shape.LineTo(x, y); + } + } + + //render the path + pdf->Shape(shape, wxPDF_STYLE_DRAW); + + } + + //go back to normal color space + pdf->SetDrawColorSpace(0); + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(polygon_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + // set the fill color and alpha to use + pdf->SetFillColor(sym.get_fill().red(), sym.get_fill().green(), sym.get_fill().blue()); + pdf->SetAlpha(sym.get_opacity(), sym.get_opacity(), wxPDF_BLENDMODE_NORMAL); + + unsigned int geometry_index; + //for each of the geometries contained in the feature + for (geometry_index=0; geometry_index < feature.num_geometries(); ++geometry_index) + { + //get the current geometry, and only continue if it is valid (more than 2 points) + geometry2d const& geom = feature.get_geometry(geometry_index); + + //make sure there are enough points + if(geom.num_points() < 3) { + std::clog << __FILE__ << ":" << __LINE__ << ": not enough points to make polygon\n"; + continue; + } + + + //wrap the geometry so we get the transformed vertexes + path_transform transformed_path(coord_trans, geom, prj_trans); + + //create the pdf shape + wxPdfShape shape; + create_shape_from_path(transformed_path, shape); + + //render it with even-odd fill rule (without border) + pdf->Shape(shape, wxPDF_STYLE_FILL, true); + + } + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(polygon_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + std::clog << "WARNING: polygon_pattern_symbolizer should not be used for pdf rendering, use pdf_polygon_pattern_symbolizer\n\tThe fill pattern will be incorrectly scaled with 1:1 pixel:pdf-unit scaling"; + + //convert it to a pdf_polygon_pattern_symbolizer and process it + pdf_polygon_pattern_symbolizer tmp_sym(sym, sym.get_image()->width(), sym.get_image()->height()); + process(tmp_sym, feature, prj_trans); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(pdf_polygon_pattern_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + //convert the image + boost::shared_ptr const& data = sym.get_image(); + wxImage *image = NULL; + create_wximage_from_imagedata(data, &image); + + std::wstring imageName = convert_string_to_wstring(sym.get_filename()); + + // create and set the fill pattern and color space + pdf->AddPattern(imageName, *image, imageName, sym.get_output_width(), sym.get_output_height()); + pdf->SetFillColorSpace(1); + pdf->SetFillPattern(imageName); + + //finished with iamge + delete image; + image = NULL; + + + unsigned int geometry_index; + //for each of the geometries contained in the feature + for (geometry_index=0; geometry_index < feature.num_geometries(); ++geometry_index) + { + //get the current geometry, and only continue if it is valid (more than 2 points) + geometry2d const& geom = feature.get_geometry(geometry_index); + + //make sure there are enough points + if(geom.num_points() < 3) { + std::clog << __FILE__ << ":" << __LINE__ << ": not enough points to make polygon\n"; + continue; + } + + + //wrap the geometry so we get the transformed vertexes + path_transform transformed_path(coord_trans, geom, prj_trans); + + wxPdfShape shape; + double x,y; + unsigned int cmd; + bool nextIsMove = true; // set to true if the next vertex should be done with a MoveTo rather than LineTo + geometry2d::vertex_type startPoint; + + //until we get the SEG_END marker, keep adding vertexes + while((cmd = transformed_path.vertex(&x, &y)) != SEG_END) { + //if moving to vertex, record the start point and mark that next vertex is not a 'move'. + if(nextIsMove) { + startPoint.x = x; + startPoint.y = y; + shape.MoveTo(x, y); + nextIsMove = false; + } + else { + //if the vertex matches the starting point for the subpath + // draw the line, close the path, and mark that the next point + // should be done with moveto + if((startPoint.x == x) && (startPoint.y == y)) { + shape.LineTo(x, y); + shape.ClosePath(); + nextIsMove = true; + } + else { + shape.LineTo(x, y); + } + } + } + + //close the path and render it with even-odd fill rule (without border) + shape.ClosePath(); + pdf->Shape(shape, wxPDF_STYLE_FILL, true); + + } + + //back to normal color space + pdf->SetFillColorSpace(0); + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(raster_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + //save graphics state + pdf->StartTransform(); + + raster_ptr const& raster=feature.get_raster(); + if (raster) + { + Envelope ext=coord_trans.forward(raster->ext_); + + //convert the image + ImageData32 *data = &raster->data_; + wxImage *image = NULL; + create_wximage_from_imagedata(data, &image); + + std::wstring imageName = generate_unique_image_name(); + + //draw the image + if(!pdf->Image(imageName, *image, ext.minx(), ext.miny(), ext.width(), ext.height())) { + std::wclog << __FILE__ << ":" << __LINE__ << ": failed to draw raster " << imageName << "\n"; + } + + //finished with image + delete image; + image = NULL; + } + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + std::clog << "WARNING: shield_symbolizer should not be used for pdf rendering, use pdf_shield_symbolizer\n\tThe symbol will be incorrectly scaled with 1:1 pixel:pdf-unit scaling"; + + //convert it to a pdf_shield_symbolizer and process it + pdf_shield_symbolizer tmp_sym(sym, sym.get_image()->width(), sym.get_image()->height()); + process(tmp_sym, feature, prj_trans); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(pdf_shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + // get shield text and image + UnicodeString text = feature[sym.get_name()].to_unicode(); + boost::shared_ptr const& data = sym.get_image(); + + //convert the image + wxImage *image = NULL; + create_wximage_from_imagedata(data, &image); + std::wstring imageName = convert_string_to_wstring(sym.get_filename()); + + // get the text colour, halo colour, halo radius, text size + Color const& textColour = sym.get_fill(); + Color const& textHaloColour = sym.get_halo_fill(); + unsigned int textHaloRadius = sym.get_halo_radius(); + unsigned int textSize = sym.get_text_size(); + std::string fontName = sym.get_face_name(); + + //only continue if there is text and a shield + if (text.length() > 0 && data) + { + //set the text properties + text_renderer.set_font(fontName); + text_renderer.set_fill(textColour); + text_renderer.set_size(textSize); + text_renderer.set_halo_fill(textHaloColour); + text_renderer.set_halo_radius(textHaloRadius); + text_renderer.prepare_render(); + + //create the placement finder + placement_finder finder(detector); + + //get the string information + string_info info(text); + text_renderer.get_string_info(&info); + + unsigned num_geom = feature.num_geometries(); + for (unsigned i=0;i 0) // don't bother with empty geometries + { + path_transform path(coord_trans, geom,prj_trans); + + //the text_placement information + placement text_placement(info, sym); + + //calculate the placement using the finders + double label_x, label_y, z=0.0; + geom.label_position(&label_x, &label_y); + prj_trans.backward(label_x,label_y, z); + coord_trans.forward(&label_x,&label_y); + finder.find_point_placement(text_placement,label_x,label_y); + + //render the calculated placements and shield + for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii) + { + double x = text_placement.placements[ii].starting_x; + double y = text_placement.placements[ii].starting_y; + double imagew = sym.get_output_width(); + double imageh = sym.get_output_height(); + + //draw the image + if(!pdf->Image(imageName, *image, x-(imagew/2), y-(imageh/2), imagew, imageh )) { + std::wclog << __FILE__ << ":" << __LINE__ << ": failed to draw shield " << imageName << "\n"; + } + + //draw the text on top + text_renderer.render(&text_placement.placements[ii], x, y); + + } + } + } + } + + //finished with the image + delete image; + image = NULL; + + //restore graphics state + pdf->StopTransform(); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + // get the text to render + UnicodeString text = feature[sym.get_name()].to_unicode(); + + // get the text colour, halo colour, halo radius, text size + Color const& textColour = sym.get_fill(); + Color const& textHaloColour = sym.get_halo_fill(); + unsigned int textHaloRadius = sym.get_halo_radius(); + unsigned int textSize = sym.get_text_size(); + std::string fontName = sym.get_face_name(); + + // if there is text to render + if ( text.length() > 0 ) + { + //set the properties + text_renderer.set_font(fontName); + text_renderer.set_fill(textColour); + text_renderer.set_size(textSize); + text_renderer.set_halo_fill(textHaloColour); + text_renderer.set_halo_radius(textHaloRadius); + text_renderer.prepare_render(); + + //create the placement finder + placement_finder finder(detector); + + //get the string information + string_info info(text); + text_renderer.get_string_info(&info); + + + unsigned num_geom = feature.num_geometries(); + for (unsigned i=0;i 0) // don't bother with empty geometries + { + path_transform path(coord_trans, geom,prj_trans); + + //the text_placement information + placement text_placement(info, sym); + + //calculate the placement using the finders + if (sym.get_label_placement() == POINT_PLACEMENT) + { + double label_x, label_y, z=0.0; + geom.label_position(&label_x, &label_y); + prj_trans.backward(label_x,label_y, z); + coord_trans.forward(&label_x,&label_y); + finder.find_point_placement(text_placement,label_x,label_y); + } + else + { + finder.find_line_placements(text_placement,path); + } + + //render the calculated placements + for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii) + { + double x = text_placement.placements[ii].starting_x; + double y = text_placement.placements[ii].starting_y; + text_renderer.render(&text_placement.placements[ii], x, y); + } + } + } + } + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(building_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_frame_transform; + typedef coord_transform2 path_roof_transform; + + //save graphics state + pdf->StartTransform(); + + //get the common parameters + Color const& roof_colour = sym.get_fill(); + Color frame_colour = Color(roof_colour.red()*0.8, roof_colour.green()*0.8, roof_colour.blue()*0.8); + double opacity = sym.get_opacity(); + double height = 0.7071 * sym.height(); // height in meters + + //set common draw parameters + wxPdfLineStyle pdf_lineStyle; + pdf_lineStyle.SetLineJoin(wxPDF_LINEJOIN_ROUND); + pdf_lineStyle.SetWidth(0.25); + pdf->SetLineStyle(pdf_lineStyle); + + //for each building + for( unsigned bidx = 0; bidx < feature.num_geometries(); bidx++) + { + geometry2d const& geom = feature.get_geometry(bidx); + + //its only a building if it has more than 2 points + if( geom.num_points() > 2) + { + boost::scoped_ptr frame(new line_string_impl); + boost::scoped_ptr roof(new polygon_impl); + std::deque frame_segments; + + //add all the frame vertices, and face_segments + double x0, y0; + unsigned command; + + command = geom.vertex(&x0, &y0); + for(unsigned j = 1; j < geom.num_points(); j++) + { + double x, y; + command = geom.vertex(&x, &y); + if(command == SEG_MOVETO) + { + frame->move_to(x, y); + } + else if (command == SEG_LINETO) + { + frame->line_to(x, y); + } + + frame_segments.push_back(segment_t(x0, y0, x, y)); + x0 = x; + y0 = y; + } + + //draw the walls of the building in sorted order + std::sort(frame_segments.begin(), frame_segments.end(), pdf_y_order); + std::deque::const_iterator itr; + for( itr = frame_segments.begin(); itr != frame_segments.end(); ++itr) + { + //create polygon for face + boost::scoped_ptr faces(new polygon_impl); + faces->move_to(itr->get<0>(),itr->get<1>()); + faces->line_to(itr->get<2>(),itr->get<3>()); + faces->line_to(itr->get<2>(),itr->get<3>() + height); + faces->line_to(itr->get<0>(),itr->get<1>() + height); + + //transform to pdf coordinates + path_frame_transform frame_path(coord_trans, *faces, prj_trans); + + //set the fill colour + pdf->SetFillColor(frame_colour.red(), frame_colour.green(), frame_colour.blue()); + pdf->SetDrawColor(128,128,128); + pdf->SetAlpha(opacity, opacity, wxPDF_BLENDMODE_NORMAL); + + //create the shape + wxPdfShape shape; + create_shape_from_path(frame_path, shape); + + //render it with even-odd fill rule (with border) + pdf->Shape(shape, wxPDF_STYLE_FILLDRAW, true); + } + + //draw the roof of the building + geom.rewind(0); + for(unsigned j = 1; j < geom.num_points(); j++) + { + double x, y; + command = geom.vertex(&x, &y); + if(command == SEG_MOVETO) + { + roof->move_to(x, y+height); + } + else if (command == SEG_LINETO) + { + roof->line_to(x, y+height); + } + } + + //transform to pdf coordinates + path_roof_transform roof_path(coord_trans, *roof, prj_trans); + + //create the shape + wxPdfShape roof_shape; + create_shape_from_path(roof_path, roof_shape); + + //set the fill colour + pdf->SetFillColor(roof_colour.red(), roof_colour.green(), roof_colour.blue()); + pdf->SetDrawColor(128,128,128); + pdf->SetAlpha(opacity, opacity, wxPDF_BLENDMODE_NORMAL); + + //render it with even-odd fill rule (with border) + pdf->Shape(roof_shape, wxPDF_STYLE_FILLDRAW, true); + } + } + + //restore graphics state + pdf->StopTransform(); + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) + { + std::clog << "WARNING: markers_symbolizer is not supported in PDF output\n"; + +/* // generate a wrapper for applying map->image coordinate transforms to geometry2d's + typedef coord_transform2 path_transform; + + //save graphics state + pdf->StartTransform(); + + arrow arrow_; + + unsigned int geometry_index; + //for each of the geometries contained in the feature + for (geometry_index=0; geometry_index < feature.num_geometries(); ++geometry_index) + { + //get the current geometry, and only continue if it is valid (more than 2 points) + geometry2d const& geom = feature.get_geometry(geometry_index); + + //needs to be more than 1 vertex + if(geom.num_points() > 1) { + path_transform path(coord_trans, geom, prj_trans); + + agg::conv_dash dash(path); + dash.add_dash(2.0, 20.0); + markers_converter, arrow, label_collision_detector4> marker(dash, arrow_, detector); + + //set the fill colour + pdf->SetFillColor(0, 0, 255); + pdf->SetAlpha(1, 1, wxPDF_BLENDMODE_NORMAL); + + //create the shape + wxPdfShape shape; + double x,y; + unsigned int cmd; + bool nextIsMove = true; // set to true if the next vertex should be done with a MoveTo rather than LineTo + geometry2d::vertex_type startPoint; + + //until we get the SEG_END marker, keep adding vertexes + while((cmd = marker.vertex(&x, &y)) != SEG_END) { + //if moving to vertex, record the start point and mark that next vertex is not a 'move'. + if(nextIsMove) { + startPoint.x = x; + startPoint.y = y; + shape.MoveTo(x, y); + nextIsMove = false; + } + else { + //if the vertex matches the starting point for the subpath + // draw the line, close the path, and mark that the next point + // should be done with moveto + if((startPoint.x == x) && (startPoint.y == y)) { + shape.LineTo(x, y); + shape.ClosePath(); + nextIsMove = true; + } + else { + shape.LineTo(x, y); + } + } + } + + //close the path so it is ready to be rendered + shape.ClosePath(); + + + + //render it with even-odd fill rule (with border) + pdf->Shape(shape, wxPDF_STYLE_FILL, true); + } + } + + //restore graphics state + pdf->StopTransform(); + + */ + } + + + //--------------------------------------------------------------------------- + void pdf_renderer::create_wximage_from_imagedata(boost::shared_ptr src, wxImage **dst) + { + unsigned int x,y; + unsigned int height = src->height(); + unsigned int width = src->width(); + const unsigned int *srcRow = NULL; + unsigned char r,g,b,a; + + //create the new image + (*dst) = new wxImage(width, height); + (*dst)->SetAlpha(); + + for( y = 0; y < height; y++ ) + { + srcRow = src->getRow(y); + for( x = 0; x < width; x++ ) + { + a = (srcRow[x] & 0xFF000000) >> 24; + b = (srcRow[x] & 0x00FF0000) >> 16; + g = (srcRow[x] & 0x0000FF00) >> 8; + r = (srcRow[x] & 0x000000FF) >> 0; + + (*dst)->SetRGB(x,y,r,g,b); + (*dst)->SetAlpha(x,y,a); + } + } + } + + //--------------------------------------------------------------------------- + void pdf_renderer::create_wximage_from_imagedata(const ImageData32* src, wxImage **dst) + { + unsigned int x,y; + unsigned int height = src->height(); + unsigned int width = src->width(); + const unsigned int *srcRow = NULL; + unsigned char r,g,b,a; + + //create the new image + (*dst) = new wxImage(width, height); + (*dst)->SetAlpha(); + + for( y = 0; y < height; y++ ) + { + srcRow = src->getRow(y); + for( x = 0; x < width; x++ ) + { + a = (srcRow[x] & 0xFF000000) >> 24; + b = (srcRow[x] & 0x00FF0000) >> 16; + g = (srcRow[x] & 0x0000FF00) >> 8; + r = (srcRow[x] & 0x000000FF) >> 0; + + (*dst)->SetRGB(x,y,r,g,b); + (*dst)->SetAlpha(x,y,a); + } + } + } + + + //--------------------------------------------------------------------------- + void pdf_renderer::create_shape_from_path(const coord_transform2 &transformed_path, wxPdfShape &shape) + { + double x,y; + unsigned int cmd; + bool nextIsMove = true; // set to true if the next vertex should be done with a MoveTo rather than LineTo + geometry2d::vertex_type startPoint; + + //until we get the SEG_END marker, keep adding vertexes + while((cmd = transformed_path.vertex(&x, &y)) != SEG_END) { + //if moving to vertex, record the start point and mark that next vertex is not a 'move'. + if(nextIsMove) { + startPoint.x = x; + startPoint.y = y; + shape.MoveTo(x, y); + nextIsMove = false; + } + else { + //if the vertex matches the starting point for the subpath + // draw the line, close the path, and mark that the next point + // should be done with moveto + if((startPoint.x == x) && (startPoint.y == y)) { + shape.LineTo(x, y); + shape.ClosePath(); + nextIsMove = true; + } + else { + shape.LineTo(x, y); + } + } + } + + //close the path so it is ready to be rendered + shape.ClosePath(); + } + + + //--------------------------------------------------------------------------- + std::wstring pdf_renderer::generate_unique_image_name(void) + { + std::wstringstream name; + name << _T("UNNAMED_IMAGE_") ; + name << unnamed_image_count; + + unnamed_image_count++; + return name.str(); + } + + + //--------------------------------------------------------------------------- + std::wstring pdf_renderer::convert_string_to_wstring(const std::string str) + { + std::wstring ostring; + std::string::const_iterator itr; + for(itr = str.begin();itr!=str.end();itr++) { + ostring.push_back(*itr); + } + + return ostring; + } + + //--------------------------------------------------------------------------- + void pdf_renderer::render_overlays(void) + { + //save graphics state + pdf->StartTransform(); + + //map border + if(page_layout.map_area_border && !(page_layout.border_scales || page_layout.map_grid)) { + wxPdfLineStyle borderStyle; + borderStyle.SetWidth(page_layout.map_area_border_width); + borderStyle.SetLineCap(wxPDF_LINECAP_SQUARE); + borderStyle.SetLineJoin(wxPDF_LINEJOIN_NONE); + borderStyle.SetColour(wxPdfColour(0,0,0)); + + pdf->SetLineStyle(borderStyle); + + double thickness = page_layout.map_area_border_width * 0.75; // shouldn't need to scale, but adobe doesnt render this nicely, can see through non-existant gaps until you zoom in. + + pdf->Rect(page_layout.map_area.minx() - (thickness/2), page_layout.map_area.miny() - (thickness/2), page_layout.map_area.width() + thickness, page_layout.map_area.height() + thickness, wxPDF_STYLE_DRAW); + + } + + //render the overlay images + render_overlay_images(); + + //render the grid + render_overlay_grid(); + + //render the scale bar + render_overlay_scale_bar(); + + //restore graphics state + pdf->StopTransform(); + } + + //--------------------------------------------------------------------------- + void pdf_renderer::render_overlay_images(void) { + std::vector::const_iterator itr; + pdf_renderer_layout::overlay_image_data *data; + bool isPDF; + + //for each overlay image + for(itr = page_layout.overlay_images.begin(); itr != page_layout.overlay_images.end(); itr++) { + //save graphics state + pdf->StartTransform(); + + data = *itr; + + wxString file = convert_string_to_wstring(data->file); + file = file.Lower(); + + //if its a pdf file, use the import page functions + if(file.find(_T(".pdf"), true) == (file.Len() - 4)) { + isPDF = true; + } + else { + isPDF = false; + } + + double tw, th; + double width, height; + int tnum = 0; //for pdf + wxImage *image; //for image + + //Load the pdf/image + if(isPDF) { //if PDF + //try to load + if(!pdf->SetSourceFile(file)) { + std::clog << "Failed to load PDF file: " << file.c_str() << "\n"; + continue; + } + + //try to import page + tnum = pdf->ImportPage(1); + if(tnum == 0) { + std::clog << "Failed to import PDF page\n"; + continue; + } + + //get template size + pdf->GetTemplateSize(tnum, tw, th); + } + else { //if Image + //load the image + image = new wxImage(file); + if(!image->IsOk()) { + std::clog << "Failed to load image file: " << file.c_str() << "\n"; + delete image; + image = NULL; + continue; + } + + //get image size + tw = image->GetWidth(); + th = image->GetHeight(); + } + + + //work out the proper scaling if one of the supplied arguments is 0 + if(data->width == 0 && data->height == 0) { + width = tw; + height = th; + } + else { + if(data->width == 0) { + width = (tw/th) * data->height; + } + else { + width = data->width; + } + + if(data->height == 0) { + height = (th/tw) * data->width; + } + else { + height = data->height; + } + } + + //rotate image (it rotates anticlockwise for some reason, so reverse it, and rotate around the center of the image) + pdf->Rotate( 0 - data->angle, data->x + (width / 2), data->y + (height / 2)); + + + //draw the pdf/image + if(isPDF) { + pdf->UseTemplate(tnum, data->x, data->y, width, height); + } + else { + pdf->Image(generate_unique_image_name(), *image, data->x, data->y, width, height); + delete image; + image = NULL; + } + + + //restore graphics state + pdf->StopTransform(); + } + } + + //--------------------------------------------------------------------------- + void pdf_renderer::render_overlay_grid(void) { + Envelope dataExtent = map.getCurrentExtent(); + Envelope mapExtent = page_layout.map_area; + + double dataWidth = dataExtent.width(); + double dataHeight = dataExtent.height(); + + double mapWidth = mapExtent.width(); + double mapHeight = mapExtent.height(); + + double dataunitsperspace = dataWidth / (mapWidth/page_layout.map_grid_approx_spacing); // number of data units per grid space + + //make data units per space 'round' + double mag = pow(10,floor(log10(dataunitsperspace))); // magnitude of mapunitsperspace + double rdataunitsperspace = round(dataunitsperspace/mag)*mag; // 'round' number of data units per grid space + + //work out the size and number of grid spaces + double spaces = dataWidth / rdataunitsperspace; // number of spaces across page + double grid_round_spacing = mapWidth/spaces; // page units per space + + //add the grid lines ocg if enabled + wxPdfOcg *map_grid_optional = NULL; + if(page_layout.map_grid) { + //this gets managed by wxpdf, dont delete ourself + map_grid_optional = new wxPdfOcg(_T("Grid Lines")); + pdf->AddOcg(map_grid_optional); + } + + //if drawing border scales or map grids + if(page_layout.border_scales || page_layout.map_grid) { + + double hbslw = page_layout.border_scale_linewidth / 2.0; + double bslw = page_layout.border_scale_linewidth; + double bsw = page_layout.border_scale_width; + double hmglw = page_layout.map_grid_linewidth / 2.0; + + pdf->StartTransform(); + + //--------------------- + //Draw border scales + + wxPdfLineStyle borderScaleLineStyle; + borderScaleLineStyle.SetColour(wxPdfColour(0,0,0)); + borderScaleLineStyle.SetWidth(page_layout.border_scale_linewidth); + borderScaleLineStyle.SetLineJoin(wxPDF_LINEJOIN_NONE); + borderScaleLineStyle.SetLineCap(wxPDF_LINECAP_NONE); + pdf->SetLineStyle(borderScaleLineStyle); + + wxPdfLineStyle mapGridLineStyle; + mapGridLineStyle.SetColour(wxPdfColour(page_layout.map_grid_colour.red(), page_layout.map_grid_colour.green(), page_layout.map_grid_colour.blue())); + mapGridLineStyle.SetWidth(page_layout.map_grid_linewidth); + mapGridLineStyle.SetLineJoin(wxPDF_LINEJOIN_NONE); + mapGridLineStyle.SetLineCap(wxPDF_LINECAP_NONE); + + + //border around scales + + wxPdfShape tbord; + tbord.MoveTo(mapExtent.minx()+hbslw, mapExtent.miny()-hbslw); + tbord.LineTo(mapExtent.minx()+hbslw, mapExtent.miny()+hbslw-bsw); + tbord.LineTo(mapExtent.maxx()-hbslw, mapExtent.miny()+hbslw-bsw); + tbord.LineTo(mapExtent.maxx()-hbslw, mapExtent.miny()-hbslw); + tbord.ClosePath(); + + wxPdfShape rbord; + rbord.MoveTo(mapExtent.maxx()+hbslw, mapExtent.miny()+hbslw); + rbord.LineTo(mapExtent.maxx()-hbslw+bsw, mapExtent.miny()+hbslw); + rbord.LineTo(mapExtent.maxx()-hbslw+bsw, mapExtent.maxy()-hbslw); + rbord.LineTo(mapExtent.maxx()+hbslw, mapExtent.maxy()-hbslw); + rbord.ClosePath(); + + wxPdfShape bbord; + bbord.MoveTo(mapExtent.maxx()-hbslw, mapExtent.maxy()+hbslw); + bbord.LineTo(mapExtent.maxx()-hbslw, mapExtent.maxy()-hbslw+bsw); + bbord.LineTo(mapExtent.minx()+hbslw, mapExtent.maxy()-hbslw+bsw); + bbord.LineTo(mapExtent.minx()+hbslw, mapExtent.maxy()+hbslw); + bbord.ClosePath(); + + wxPdfShape lbord; + lbord.MoveTo(mapExtent.minx()-hbslw, mapExtent.maxy()-hbslw); + lbord.LineTo(mapExtent.minx()+hbslw-bsw, mapExtent.maxy()-hbslw); + lbord.LineTo(mapExtent.minx()+hbslw-bsw, mapExtent.miny()+hbslw); + lbord.LineTo(mapExtent.minx()-hbslw, mapExtent.miny()+hbslw); + lbord.ClosePath(); + + pdf->SetDrawColor(0,0,0); + pdf->Shape(tbord, wxPDF_STYLE_DRAW); + pdf->Shape(rbord, wxPDF_STYLE_DRAW); + pdf->Shape(bbord, wxPDF_STYLE_DRAW); + pdf->Shape(lbord, wxPDF_STYLE_DRAW); + + pdf->StopTransform(); + + + //--------------------- + //work out the text size + + double textscale = 0.8; //text will never be bigger than this scale factor of border_scale_width + double textsize = page_layout.border_scale_width * textscale; //first guess, wont get any larger + double textoffseth = textsize / 4.0; //(horizontal text offset) this gets refined below + double textoffsetv = 0; //(vertical text offset) calculated later + + double middleValue = dataExtent.minx() + (dataExtent.width() / 2.0); + wxString testText = testText.Format(_T("%d"), (long)middleValue); + double testWidth; + + //set initial font size, and the font + text_renderer.set_font(page_layout.font_name); + text_renderer.set_size(textsize); + + //get the width (in page units) of a grid bar + double availableTextWidth = grid_round_spacing; + + + //keep making the text smaller until it fits + testWidth = pdf->GetStringWidth(testText); + while(testWidth > (availableTextWidth - (4*textoffseth))) { //4*textoffseth so that text will always be closest to correct side (1space on left, 3 on right) + textsize *= 0.9; + textoffseth = textsize / 4.0; + text_renderer.set_size(textsize); + testWidth = pdf->GetStringWidth(testText); + } + + textoffsetv = (page_layout.border_scale_width - (0.667 * textsize)) / 2; //0.667 scale factor since text height includes funny symbols and marks + + + //--------------------- + //Draw border scales + + + //draw border scales across/down the map, and the map grids if enabled + pdf->StartTransform(); + + //the only lines being drawn at the moment are the map grid lines. + // The edge scale rectangles are only filled, no lines. + pdf->SetLineStyle(mapGridLineStyle); + + + double x0, x1, x2, x3; + double y0, y1, y2, y3; + double dx0, dy0, dx1, dy1; + bool done; + bool filled; + wxString txt; + double stringWidth; + bool drawmapgrid; + + done = false; + filled = true; + drawmapgrid = false; //dont draw first line + x0 = dataExtent.minx(); + x1 = ceil(x0 / rdataunitsperspace) * rdataunitsperspace; + y0 = mapExtent.miny() - page_layout.border_scale_width; + y1 = mapExtent.miny(); + y2 = mapExtent.maxy(); + y3 = mapExtent.maxy() + page_layout.border_scale_width; + + while(!done) { + dx0 = x0; + dx1 = x1; + dy0 = y0; + dy1 = y1; + coord_trans.forward(&dx0, &dy0); + coord_trans.forward(&dx1, &dy1); + + //draw the bar + if(filled) { + pdf->Rect(dx0, y0+hbslw, dx1-dx0, y1-y0-bslw, wxPDF_STYLE_FILL); + pdf->Rect(dx0, y2+hbslw, dx1-dx0, y3-y2-bslw, wxPDF_STYLE_FILL); + } + + //draw the map grid if enabled + if(page_layout.map_grid && drawmapgrid) { + pdf->EnterOcg(map_grid_optional); + pdf->SetAlpha(page_layout.map_grid_colour.alpha()/256.0); + pdf->Line(dx0, y1 + hmglw, dx0, y2 - hmglw); + pdf->SetAlpha(1); + pdf->ExitOcg(); + } + drawmapgrid = true; //draw it next time (if enabled) + + //set the text color + if(filled) { + pdf->SetTextColor(255,255,255); + } + else { + pdf->SetTextColor(0,0,0); + } + + //render the text + txt = txt.Format(_T("%d"), (long)x0); //x0 is round, so it has nothing after the decimal + stringWidth = pdf->GetStringWidth(txt); + if(stringWidth <= (abs(dx1-dx0) - (2 * textoffseth))) { + pdf->Text(dx0 + textoffseth, y0+page_layout.border_scale_width-textoffsetv, txt); + pdf->Text(dx0 + textoffseth, y2+page_layout.border_scale_width-textoffsetv, txt); + } + //next colour next time + filled = !filled; + + //work out if we're done, and get the next coordintes + x0 = x1; + if(x0 >= dataExtent.maxx()) { + done = true; + } + x1 += rdataunitsperspace; + if(x1 >= dataExtent.maxx()) { + x1 = dataExtent.maxx(); + } + } + + + done = false; + filled = true; + drawmapgrid = false; + y0 = dataExtent.miny(); + y1 = ceil(y0 / rdataunitsperspace) * rdataunitsperspace; + x0 = mapExtent.minx() - page_layout.border_scale_width; + x1 = mapExtent.minx(); + x2 = mapExtent.maxx(); + x3 = mapExtent.maxx() + page_layout.border_scale_width; + + while(!done) { + dy0 = y0; + dy1 = y1; + dx0 = x0; + dx1 = x1; + coord_trans.forward(&dx0, &dy0); + coord_trans.forward(&dx1, &dy1); + + //draw the bar + if(filled) { + pdf->Rect(x0+hbslw, dy0, x1-x0-bslw, dy1-dy0, wxPDF_STYLE_FILL); + pdf->Rect(x2+hbslw, dy0, x3-x2-bslw, dy1-dy0, wxPDF_STYLE_FILL); + } + + //draw the map grid if enabled + if(page_layout.map_grid && drawmapgrid) { + pdf->EnterOcg(map_grid_optional); + pdf->SetAlpha(page_layout.map_grid_colour.alpha()/256.0); + pdf->Line(x1 + hmglw, dy0, x2 - hmglw, dy0); + pdf->SetAlpha(1); + pdf->ExitOcg(); + } + drawmapgrid = true; //draw it next time + + //set the text color + if(filled) { + pdf->SetTextColor(255,255,255); + } + else { + pdf->SetTextColor(0,0,0); + } + + //render the text + txt = txt.Format(_T("%d"), (long)y0); //y0 is round, so it has nothing after the decimal + stringWidth = pdf->GetStringWidth(txt); + if(stringWidth <= (abs(dy0-dy1) - (2 * textoffseth))) { + pdf->RotatedText(x0+page_layout.border_scale_width-textoffsetv, dy0 - textoffseth, txt, 90); + pdf->RotatedText(x2+page_layout.border_scale_width-textoffsetv, dy0 - textoffseth, txt, 90); + } + + //next colour next time + filled = !filled; + + //work out if we're done, and get the next coordintes + y0 = y1; + if(y0 >= dataExtent.maxy()) { + done = true; + } + y1 += rdataunitsperspace; + if(y1 >= dataExtent.maxy()) { + y1 = dataExtent.maxy(); + } + } + + pdf->StopTransform(); + } + + } + + //--------------------------------------------------------------------------- + void pdf_renderer::render_overlay_scale_bar(void) { + //give up if not supposed to draw it + if(!page_layout.scale_bar) { + return; + } + + //------------------------------ + //scale the scale bar to give us + // nice units (e.g. 500 instead + // of 487.326) + Envelope dataExtent = map.getCurrentExtent(); + Envelope mapExtent = page_layout.map_area; + + double dataWidth = dataExtent.width(); + double mapWidth = mapExtent.width(); + + double dataToMapScale = page_layout.scale_bar_factor * (dataWidth/mapWidth); // e.g. 43554.345 meters = 1mm on page + + + //scale bar looks like: + // <------width-------> + // .----.----.--------. + // | | | | + // .----.----.--------. + // |||< 2q >| + // + // where there q = width/4. + + double dataUnitsAlongQ = dataToMapScale * (page_layout.scale_bar_area.width() / 4.0); + + //make dataUnitsAlongQ 'round' + double mag = pow(10,floor(log10(dataUnitsAlongQ))); // magnitude of dataUnitsAlongQ + double rdataUnitsAlongQ = round(dataUnitsAlongQ/mag)*mag; // 'round' number of data units along Q + + //work out how wide Q is now (in map units) + double scale_bar_q_width = rdataUnitsAlongQ / dataToMapScale; + + + //------------------------------ + //draw the scale bar + + pdf->StartTransform(); + + wxPdfLineStyle scaleLineStyle; + scaleLineStyle.SetColour(wxPdfColour(0,0,0)); + scaleLineStyle.SetWidth(page_layout.border_scale_linewidth); + scaleLineStyle.SetLineJoin(wxPDF_LINEJOIN_NONE); + scaleLineStyle.SetLineCap(wxPDF_LINECAP_NONE); + pdf->SetLineStyle(scaleLineStyle); + + + double tlx = page_layout.scale_bar_area.minx() + (page_layout.scale_bar_area.width() / 2.0) - (2 * scale_bar_q_width); + double tly = page_layout.scale_bar_area.miny(); + double brx = tlx + (4.0 * scale_bar_q_width); + double h = page_layout.scale_bar_area.height(); + double bry = tly + h; + double hlw = page_layout.border_scale_linewidth / 2.0; + + //------------------- + //the bars + pdf->SetFillColor(0,0,0); + pdf->Rect(tlx + (0.0 * scale_bar_q_width), tly, scale_bar_q_width, h, wxPDF_STYLE_FILL); + pdf->SetFillColor(255,255,255); + pdf->Rect(tlx + (1.0 * scale_bar_q_width), tly, scale_bar_q_width, h, wxPDF_STYLE_FILL); + pdf->SetFillColor(0,0,0); + pdf->Rect(tlx + (2.0 * scale_bar_q_width), tly, (2.0 * scale_bar_q_width), h, wxPDF_STYLE_FILL); + + //------------------- + //border + wxPdfShape border; + border.MoveTo(tlx + hlw, tly + hlw); + border.LineTo(brx - hlw, tly + hlw); + border.LineTo(brx - hlw, bry - hlw); + border.LineTo(tlx + hlw, bry - hlw); + border.ClosePath(); + + pdf->SetDrawColor(0,0,0); + pdf->Shape(border, wxPDF_STYLE_DRAW); + + + //------------------- + //the labels + + //set initial font size, and the font + double textsize = h; + text_renderer.set_font(page_layout.font_name); + text_renderer.set_size(textsize); + + //figure out the text size iteratively + wxString label; + label = wxString::Format(_T("%d"), (long)(rdataUnitsAlongQ * 4.0)); //this is round, so can be cast to integer + + double textWidth = pdf->GetStringWidth(label); + while(textWidth > (scale_bar_q_width * 0.8)) { // 0.8 scale so it doesnt fill it completely + textsize *= 0.9; + text_renderer.set_size(textsize); + textWidth = pdf->GetStringWidth(label); + } + + //render the text + pdf->SetTextColor(0,0,0); + + label = wxString::Format(_T("%d"), 0); + textWidth = pdf->GetStringWidth(label); + pdf->Text(tlx + (2.0 * scale_bar_q_width) - (textWidth / 2.0), bry + textsize, label); + + label = wxString::Format(_T("%d"), (long)rdataUnitsAlongQ); + textWidth = pdf->GetStringWidth(label); + pdf->Text(tlx + (1.0 * scale_bar_q_width) - (textWidth / 2.0), bry + textsize, label); + + label = wxString::Format(_T("%d"), (long)(rdataUnitsAlongQ * 2.0)); + textWidth = pdf->GetStringWidth(label); + pdf->Text(tlx + (0.0 * scale_bar_q_width) - (textWidth / 2.0), bry + textsize, label); + pdf->Text(tlx + (4.0 * scale_bar_q_width) - (textWidth / 2.0), bry + textsize, label); + + label = convert_string_to_wstring(page_layout.scale_bar_unit); + textWidth = pdf->GetStringWidth(label); + pdf->Text(tlx + (2.0 * scale_bar_q_width) - (textWidth / 2.0), bry + (2 * textsize), label); + + + pdf->StopTransform(); + } + + + + +} //namespace mapnik + +#endif //ENABLE_PDF diff --git a/pdf/pdf_renderer_layout.cpp b/pdf/pdf_renderer_layout.cpp new file mode 100644 index 000000000..320fa6093 --- /dev/null +++ b/pdf/pdf_renderer_layout.cpp @@ -0,0 +1,111 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//! \file pdf_renderer_layout.cpp +//! \brief Implementation of pdf_renderer_layout.hpp +//! + +#if ENABLE_PDF + +// mapnik +#include + +// pdf +#include +#include +#include + +namespace mapnik +{ + + //--------------------------------------------------------------------------- + pdf_renderer_layout::pdf_renderer_layout(const std::string &units, const double width, const double height, const bool portrait) + : page_area(Envelope(0,0, width, height)), + page_units(units) + { + page_background_colour = Color(255, 255, 255); + page_portrait = portrait; + + map_area = page_area; + map_area_border = false; + map_area_border_width = 0.5; + + font_name = "Arial"; + + border_scales = true; + map_grid = false; + map_grid_approx_spacing = 35; + border_scale_width = 4.5; + border_scale_linewidth = 0.1; + map_grid_colour = Color(0,0,0,64); + map_grid_linewidth = 0.25; + + scale_bar = true; + scale_bar_area = Envelope(20,20,100,27.5); + scale_bar_factor = 0.001; + scale_bar_unit = "Kilometers"; + } + + //--------------------------------------------------------------------------- + pdf_renderer_layout::~pdf_renderer_layout() + { + //remove all data in overlay images vector + OIDitr itr; + for(itr = overlay_images.begin(); itr != overlay_images.end(); itr++) { + delete *itr; + *itr = NULL; + } + + } + + //--------------------------------------------------------------------------- + void pdf_renderer_layout::set_map_area(const double x, const double y, const double width, const double height) + { + map_area = Envelope(x, y, x+width, y+height); + + if(!page_area.contains(map_area)) + { + map_area = page_area; + std::clog << "WARNING: specified map area is not within the page area. Setting map area to page area."; + } + } + + void pdf_renderer_layout::add_overlay_image(const std::string &image, const double x, const double y, const double width, const double height, const double angle) + { + overlay_image_data *data = new overlay_image_data; + data->file = image; + data->x = x; + data->y = y; + data->width = width; + data->height = height; + data->angle = angle; + + overlay_images.push_back(data); + } + + + +} //namespace mapnik + + +#endif // #if ENABLE_PDF + diff --git a/pdf/pdf_renderer_utility.cpp b/pdf/pdf_renderer_utility.cpp new file mode 100644 index 000000000..8f933fa33 --- /dev/null +++ b/pdf/pdf_renderer_utility.cpp @@ -0,0 +1,64 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +//! \file pdf_renderer_utility.cpp +//! \brief Implementation of pdf_renderer_utility.hpp +//! + +#if ENABLE_PDF + +// mapnik +#include +#include +#include + +// WX +#include + + +namespace mapnik { + +MAPNIK_DECL void render_to_pdf(const mapnik::Map& map, const mapnik::pdf_renderer_layout &layout, const std::string& filename, const bool compress) { + using namespace mapnik; + + //create the pdf + wxPdfDocument pdf(layout.get_page_width(), layout.get_page_height(), layout.get_page_portrait()?wxPORTRAIT:wxLANDSCAPE, pdf_renderer::convert_string_to_wstring(layout.get_page_units())); + + //set pdf options + pdf.SetMargins(0,0,0); + pdf.SetCompression(compress); + + //create the renderer + pdf_renderer pr(map, pdf, layout, 0, 0); + + //render + pr.apply(); + + //save to file + pdf.SaveAsFile(pdf_renderer::convert_string_to_wstring(filename)); +} + + +} //namespace mapnik + + +#endif //#if ENABLE_PDF \ No newline at end of file diff --git a/pdf/pdf_shield_symbolizer.cpp b/pdf/pdf_shield_symbolizer.cpp new file mode 100644 index 000000000..182227750 --- /dev/null +++ b/pdf/pdf_shield_symbolizer.cpp @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * This file is part of the wxPdfDoc modifications for mapnik + * + * Copyright (C) 2007 Ben Moores + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#if ENABLE_PDF + +// pdf +#include + +// mapnik +#include +#include +// boost +#include +// stl +#include + +namespace mapnik +{ + + pdf_shield_symbolizer::pdf_shield_symbolizer(std::string const& name, + std::string const& face_name, + unsigned size, + Color const& fill, + std::string const& file, + std::string const& type, + unsigned image_width, unsigned image_height, + double _output_width, double _output_height) + : shield_symbolizer(name, face_name, size, fill, file, type, image_width, image_height) + { + output_width = _output_width; + output_height = _output_height; + } + + + pdf_shield_symbolizer::pdf_shield_symbolizer(pdf_shield_symbolizer const& rhs) + : shield_symbolizer(rhs) + { + output_width = rhs.get_output_width(); + output_height = rhs.get_output_height(); + } + + pdf_shield_symbolizer::pdf_shield_symbolizer(shield_symbolizer const& rhs, const double _output_width, const double _output_height) + : shield_symbolizer(rhs) + { + output_width = _output_width; + output_height = _output_height; + } + + void pdf_shield_symbolizer::set_output_size(const double width, const double height) + { + output_width = width; + output_height = height; + } + +} + +#endif //ENABLE_PDF diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfdoc.h b/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfdoc.h new file mode 100644 index 000000000..13042b41b --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfdoc.h @@ -0,0 +1,2630 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfdoc.h +// Purpose: +// Author: Ulrich Telle +// Modified by: +// Created: 2005-08-04 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfdoc.h Interface of the wxPdfDocument class + +#ifndef _PDFDOC_H_ +#define _PDFDOC_H_ + +#include +#include + +#include "wx/pdfdocdef.h" +#include "wx/pdfencrypt.h" +#include "wx/pdffont.h" +#include "wx/pdfimage.h" +#include "wx/pdfproperties.h" +#include "wx/pdfoc.h" + +#define wxPDF_PRODUCER _T("wxPdfDocument 0.8.0") + +#define wxPDF_EPSILON 1e-6 + +class wxPdfExtGState; +class wxPdfGradient; + +class wxPdfCellContext; +class wxPdfTable; +class wxPdfIndirectObject; +class wxPdfAnnotationWidget; +class wxPdfTemplate; +class wxPdfParser; +class wxPdfObject; + +/// Hashmap class for offset values +WX_DECLARE_HASH_MAP(long, int, wxIntegerHash, wxIntegerEqual, wxPdfOffsetHashMap); + +/// Hashmap class for document pages +WX_DECLARE_HASH_MAP(long, wxMemoryOutputStream*, wxIntegerHash, wxIntegerEqual, wxPdfPageHashMap); + +/// Hashmap class for boolean values +WX_DECLARE_HASH_MAP(long, bool, wxIntegerHash, wxIntegerEqual, wxPdfBoolHashMap); + +/// Hashmap class for double values +WX_DECLARE_HASH_MAP(long, double, wxIntegerHash, wxIntegerEqual, wxPdfDoubleHashMap); + +/// Hashmap class for document links +WX_DECLARE_HASH_MAP(long, wxPdfLink*, wxIntegerHash, wxIntegerEqual, wxPdfLinkHashMap); + +/// Hashmap class for page links +WX_DECLARE_HASH_MAP(long, wxArrayPtrVoid*, wxIntegerHash, wxIntegerEqual, wxPdfPageLinksMap); + +/// Hashmap class for document annotations +WX_DECLARE_HASH_MAP(long, wxArrayPtrVoid*, wxIntegerHash, wxIntegerEqual, wxPdfAnnotationsMap); + +/// Hashmap class for document annotations +WX_DECLARE_HASH_MAP(long, wxArrayPtrVoid*, wxIntegerHash, wxIntegerEqual, wxPdfFormAnnotsMap); + +/// Hashmap class for form fields +WX_DECLARE_HASH_MAP(long, wxPdfIndirectObject*, wxIntegerHash, wxIntegerEqual, wxPdfFormFieldsMap); + +/// Hashmap class for templates +WX_DECLARE_HASH_MAP(long, wxPdfTemplate*, wxIntegerHash, wxIntegerEqual, wxPdfTemplatesMap); + +/// Hashmap class for font encoding differences +WX_DECLARE_HASH_MAP(long, wxString*, wxIntegerHash, wxIntegerEqual, wxPdfDiffHashMap); + +/// Hashmap class for extended graphics states +WX_DECLARE_HASH_MAP(long, wxPdfExtGState*, wxIntegerHash, wxIntegerEqual, wxPdfExtGStateMap); +WX_DECLARE_HASH_MAP(long, int, wxIntegerHash, wxIntegerEqual, wxPdfExtGSLookupMap); + +/// Hashmap class for gradients +WX_DECLARE_HASH_MAP(long, wxPdfGradient*, wxIntegerHash, wxIntegerEqual, wxPdfGradientMap); + +/// Hashmap class for core fonts +WX_DECLARE_STRING_HASH_MAP(int, wxPdfCoreFontMap); + +/// Hashmap class for core fonts +WX_DECLARE_STRING_HASH_MAP(int, wxPdfNamedLinksMap); + +/// Hash map class for used/embedded fonts +WX_DECLARE_STRING_HASH_MAP(wxPdfFont*, wxPdfFontHashMap); + +/// Hash map class for embedded images +WX_DECLARE_STRING_HASH_MAP(wxPdfImage*, wxPdfImageHashMap); + +/// Hash map class for spot colors +WX_DECLARE_STRING_HASH_MAP(wxPdfSpotColour*, wxPdfSpotColourMap); + +/// Hash map class for patterns +WX_DECLARE_STRING_HASH_MAP(wxPdfPattern*, wxPdfPatternMap); + +/// Hash map class for spot colors +WX_DECLARE_STRING_HASH_MAP(wxPdfIndirectObject*, wxPdfRadioGroupMap); + +/// Hash map class for parsers +WX_DECLARE_STRING_HASH_MAP(wxPdfParser*, wxPdfParserMap); + +/// Class representing a PDF document. +class WXDLLIMPEXP_PDFDOC wxPdfDocument +{ +public: + /// Constructor + /** + * \param orientation Defines the default page orientation. Possible values are: + * \li wxPORTRAIT portrait layout (default) + * \li wxLANDSCAPE landscape layout + * + * \param unit Defines the user units. Possible values are: + * \li "mm" millimeter (1 mm = 0.0394 in = 2.833 pt = 0.1 cm) (default) + * \li "cm" centimeter (1 cm = 0.394 in = 28.33 pt = 10 mm) + * \li "pt" points (1 pt = 1/72 in = 0.0353 cm = 0.353 mm) + * \li "in" inch (1 in = 72 pt = 2.54 cm = 25.4 mm) + * \param format Defines the page format. All known wxWidgets paper types are allowed. (Default: wxPAPER_A4) + */ + wxPdfDocument(int orientation = wxPORTRAIT, + const wxString& unit = wxString(_T("mm")), + wxPaperSize format = wxPAPER_A4); + + /// Constructor + /** + * \param orientation Defines the default page orientation. Possible values are: + * \li wxPORTRAIT portrait layout (default) + * \li wxLANDSCAPE landscape layout + * + * \param unit Defines the user units. Possible values are: + * \li "mm" millimeter (1 mm = 0.0394 in = 2.833 pt = 0.1 cm) (default) + * \li "cm" centimeter (1 cm = 0.394 in = 28.33 pt = 10 mm) + * \li "pt" points (1 pt = 1/72 in = 0.0353 cm = 0.353 mm) + * \li "in" inch (1 in = 72 pt = 2.54 cm = 25.4 mm) + * + * \param paperWidth Defines the width of the paper in user units. + * \param paperHeight Defines the height of the paper in user units. + */ + wxPdfDocument(const double paperWidth, + const double paperHeight, + int orientation = wxPORTRAIT, + const wxString& unit = wxString(_T("mm"))); + + + virtual ~wxPdfDocument(); + + /// Set permissions as well as user and owner passwords. + /** + * \param permissions flag indicating permissions. + * Flags from the following list may be combined as needed + * If a value is present it means that the permission is granted + * \param userPassword user password if applicable. + * If a user password is set, user will be prompted before document is opened + * \param ownerPassword owner password.if applicable + * If an owner password is set, the document can be opened + * in privilege mode with no restriction if that password is entered + * \param encryptionMethod selects the encryption method. Possible values are: + * \li wxPDF_ENCRYPTION_RC4V1 RC4 method, version 1, with 40 bit encryption key (default) + * \li wxPDF_ENCRYPTION_RC4V2 RC4 method, version 2, with 40..128 bit encryption key + * \li wxPDF_ENCRYPTION_AESV2 AES method, with 128 bit encryption key + * \param keyLength Length of the key used for encryption (Default: 0) + * The default value selects the standard encryption method revision 2 with a key length of 40 bits. + * Specifying a value > 0 selects the standard encryption method revision 3 with the given key length, + * the key length has to be in the range 40..128 and has to be dividable by 8. + * The key length is adjusted accordingly if these conditions are not met. + * NOTE: Adobe Reader supports only 40- and 128-bit encryption keys. + */ + virtual void SetProtection(int permissions, + const wxString& userPassword = wxEmptyString, + const wxString& ownerPassword = wxEmptyString, + wxPdfEncryptionMethod encryptionMethod = wxPDF_ENCRYPTION_RC4V1, + int keyLength = 0); + + /// Set the image scale. + /** + * \param[in] scale image scale. + */ + virtual void SetImageScale(double scale); + + /// Returns the image scale. + /** + * \return image scale. + */ + virtual double GetImageScale(); + + /// Returns the page width in units. + /** + * \return int page width. + */ + virtual double GetPageWidth(); + + /// Returns the page height in units. + /** + * \return int page height. + */ + virtual double GetPageHeight(); + + /// Returns the page break margin. + /** + * \return int page break margin. + */ + virtual double GetBreakMargin(); + + /// Returns the scale factor (number of points in user unit). + /** + * \return int scale factor. + */ + virtual double GetScaleFactor(); + + /// Defines the left, top and right margins. + /** + * By default, they equal 1 cm. + * Call this method to change them. + * \param left Left margin. + * \param top Top margin. + * \param right Right margin. Default value is the left one. + * \see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() + */ + virtual void SetMargins(double left, double top, double right = -1); + + /// Defines the left margin. + /** + * The method can be called before creating the first page. + * If the current abscissa gets out of page, it is brought back to the margin. + * \param margin The margin. + * \see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() + */ + virtual void SetLeftMargin(double margin); + + /// Returns the left margin. + /** + * \return double left margin. + */ + virtual double GetLeftMargin(); + + /// Defines the top margin. + /** + * The method can be called before creating the first page. + * \param margin The margin. + * \see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() + */ + virtual void SetTopMargin(double margin); + + /// Returns the top margin. + /** + * \return double top margin. + */ + virtual double GetTopMargin(); + + /// Defines the right margin. + /** + * The method can be called before creating the first page. + * \param margin The margin. + * \see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() + */ + virtual void SetRightMargin(double margin); + + /// Returns the right margin. + /** + * \return double right margin. + */ + virtual double GetRightMargin(); + + /// Defines the cell margin. + /** + * The method can be called before creating the first page. + * \param margin The margin. + */ + virtual void SetCellMargin(double margin); + + /// Returns the cell margin. + /** + * \return double cell margin. + */ + virtual double GetCellMargin(); + + /// Sets the height of a text line + /** + * \param height The line height. + */ + virtual void SetLineHeight(double height); + + /// Returns the height of a text line + /** + * \return double line height + */ + virtual double GetLineHeight(); + + /// Enables or disables the automatic page breaking mode. + /** + * When enabling, the second parameter + * is the distance from the bottom of the page that defines the triggering limit. + * By default, the mode is on and the margin is 2 cm. + * \param autoPageBreak Boolean indicating if mode should be on or off. + * \param margin Distance from the bottom of the page. + * \see Cell(), MultiCell(), AcceptPageBreak() + */ + virtual void SetAutoPageBreak(bool autoPageBreak, double margin = 0); + + /// Defines the way the document is to be displayed by the viewer. + /** + * The zoom level can be set:pages can be displayed entirely on screen, occupy the full width + * of the window, use real size, be scaled by a specific zooming factor or use viewer default + * (configured in the Preferences menu of Acrobat). The page layout can be specified too: + * single at once, continuous display, two columns or viewer default. By default, documents + * use the full width mode with continuous display. + * \param zoom The zoom to use. It can be one of the following string values or a number + * indicating the zooming factor to use. + * \li wxPDF_ZOOM_FULLPAGE: displays the entire page on screen + * \li wxPDF_ZOOM_FULLWIDTH: uses maximum width of window + * \li wxPDF_ZOOM_REAL: uses real size (equivalent to 100% zoom) + * \li wxPDF_ZOOM_DEFAULT: uses viewer default mode + * \li wxPDF_ZOOM_FACTOR: uses viewer default mode + * \param layout The page layout. Possible values are: + * \li wxPDF_LAYOUT_SINGLE: displays one page at once + * \li wxPDF_LAYOUT_CONTINUOUS: displays pages continuously (default) + * \li wxPDF_LAYOUT_TWO: displays two pages on two columns + * \li wxPDF_LAYOUT_DEFAULT: uses viewer default mode + * \param zoomFactor specifies the zoom factor in percent if layout is wxPDF_ZOOM_FACTOR + */ + virtual void SetDisplayMode(wxPdfZoom zoom, + wxPdfLayout layout = wxPDF_LAYOUT_CONTINUOUS, + double zoomFactor = 100.); + + /// Activates or deactivates page compression. + /** + * When activated, the internal representation of each + * page is compressed, which leads to a compression ratio of about 2 for the resulting document. + * Compression is on by default. + * \param compress Boolean indicating if compression must be enabled. + */ + virtual void SetCompression(bool compress); + + /// Defines the viewer preferences. + /** + * \param preferences A set of viewer preferences options. + * \li wxPDF_VIEWER_HIDETOOLBAR: Hide tool bar + * \li wxPDF_VIEWER_HIDEMENUBAR: Hide menu bar + * \li wxPDF_VIEWER_HIDEWINDOWUI: Hide user interface + * \li wxPDF_VIEWER_FITWINDOW: Fit window to page size + * \li wxPDF_VIEWER_CENTERWINDOW: Center window on screen + * \li wxPDF_VIEWER_DISPLAYDOCTITLE: Display document title in title bar + */ + virtual void SetViewerPreferences(int preferences = 0); + + /// Defines the title of the document. + /** + * \param title The title. + * \see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() + */ + virtual void SetTitle(const wxString& title); + + /// Defines the subject of the document. + /** + * \param subject The subject. + * \see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() + */ + virtual void SetSubject(const wxString& subject); + + /// Defines the author of the document. + /** + * \param author The name of the author. + * \see SetCreator(), SetKeywords(), SetSubject(), SetTitle() + */ + virtual void SetAuthor(const wxString& author); + + /// Associates keywords with the document, + /** + * Generally keywords are in the form 'keyword1 keyword2 ...'. + * \param keywords The list of keywords. + * \see SetAuthor(), SetCreator(), SetSubject(), SetTitle() + */ + virtual void SetKeywords(const wxString& keywords); + + /// Defines the creator of the document. + /** + * This is typically the name of the application that generates the PDF. + * \param creator The name of the creator. + * \see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() + */ + virtual void SetCreator(const wxString& creator); + + /// Defines an alias for the total number of pages. + /** + * It will be substituted as the document is closed. + * \param alias The alias. Default value: {nb}. + * \see PageNo(), Footer() + */ + virtual void AliasNbPages(const wxString& alias = wxString(_T("{nb}"))); + + /// This method begins the generation of the PDF document. + /** + * It is not necessary to call it explicitly + * because AddPage() does it automatically. + * Note: no page is created by this method + * \see AddPage(), Close() + */ + virtual void Open(); + + /// Terminates the PDF document. + /** + * It is not necessary to call this method explicitly because SaveAsFile() + * does it automatically. If the document contains no page, AddPage() is called to prevent from getting + * an invalid document. + * \see Open(), SaveAsFile() + */ + virtual void Close(); + + /// Adds a new page to the document. + /** + * If a page is already present, the Footer() method is called first + * to output the footer. Then the page is added, the current position set to the top-left corner according + * to the left and top margins, and Header() is called to display the header. + * The font which was set before calling is automatically restored. There is no need to call SetFont() + * again if you want to continue with the same font. The same is true for colors and line width. + * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. + * \param orientation Page orientation. Possible values are: + * \li wxPORTRAIT + * \li wxLANDSCAPE + * The default value is the one passed to the constructor. + * \see FPDF(), Header(), Footer(), SetMargins() + */ + virtual void AddPage(int orientation = -1); + + /// This method is used to render the page header. + /** + * It is automatically called by AddPage() and should not be called directly by the application. + * The implementation in wxPdfDocument is empty, so you have to subclass it and override the method + * if you want a specific processing. + * \see Footer() + */ + virtual void Header(); + + /// This method is used to render the page footer. + /** + * It is automatically called by AddPage() and Close() and should not be called directly by + * the application. The implementation in wxPdfDocument is empty, so you have to subclass it + * and override the method if you want a specific processing. + * \see Header() + */ + virtual void Footer(); + + /// Returns whether footer output is in progress + /** + * \return true if footer output is in progress, false otherwise + * \see Header() + */ + virtual bool IsInFooter(); + + /// Returns the current page number. + /** + * \return page number + * \see AliasNbPages() + */ + virtual int PageNo(); + + /// Add spot color + /** + * Add a spot color which can be referenced in color setting methods + * \param name the name of the spot color (case sensitive) + * \param cyan indicates the cyan level. Value between 0 and 100 + * \param magenta indicates the magenta level. Value between 0 and 100 + * \param yellow indicates the yellow level. Value between 0 and 100 + * \param black indicates the black level. Value between 0 and 100 + * \see SetDrawColor(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void AddSpotColor(const wxString& name, double cyan, double magenta, double yellow, double black); + + + /// Defines the colorspace for drawing operations + /** + * \param space 0 = normal, 1 = pattern + */ + virtual void SetDrawColorSpace(const int space); + + + /// Defines the colorspace for fill operations + /** + * \param space 0 = normal, 1 = pattern + */ + virtual void SetFillColorSpace(const int space); + + + /// Defines the color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value + * is retained from page to page. + * \param grayscale indicates the gray level. Value between 0 and 255 + * \see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetDrawColor(const unsigned char grayscale); + + /// Defines the color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value + * is retained from page to page. + * \param color defines a color composed of a red, green and blue component + * \see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetDrawColor(const wxColour& color); + + /// Defines the color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value + * is retained from page to page. + * \param color defines a color using the class wxPdfColour + * \see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetDrawColor(const wxPdfColour& color); + + /// Defines the color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value + * is retained from page to page. + * \param red indicates the red level. Value between 0 and 255 + * \param green indicates the green level. Value between 0 and 255 + * \param blue indicates the blue level. Value between 0 and 255 + * \see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetDrawColor(const unsigned char red, const unsigned char green, const unsigned char blue); + + /// Defines the color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value + * is retained from page to page. + * \param cyan indicates the cyan level. Value between 0 and 100 + * \param magenta indicates the magenta level. Value between 0 and 100 + * \param yellow indicates the yellow level. Value between 0 and 100 + * \param black indicates the black level. Value between 0 and 100 + * \see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetDrawColor(double cyan, double magenta, double yellow, double black); + + /// Defines the spot color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value + * is retained from page to page. + * \param name the name of the spot color + * \param tint indicates the tint level. Value between 0 and 100. Default: 100. + * \see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetDrawColor(const wxString& name, double tint = 100); + + /// Defines the pattern used for all drawing operations. + /** + * N.B. You muse be in the pattern color space before calling, see SetDrawColorSpace. + * + * \param name is the name of the pattern + */ + virtual void SetDrawPattern(const wxString& name); + + /// Gets the color used for all drawing operations. + /** + * \see SetDrawColor() + */ + virtual const wxPdfColour GetDrawColor(); + + /// Defines the color used for all filling operations. + /** + * Affected filling operations are: filled rectangles and cell backgrounds. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value is + * retained from page to page. + * \param grayscale indicates the gray level. Value between 0 and 255 + * \see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + virtual void SetFillColor(const unsigned char grayscale); + + /// Defines the color used for all filling operations. + /** + * Affected filling operations are: filled rectangles and cell backgrounds. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value is + * retained from page to page. + * \param color defines a color composed of a red, green and blue component + * \see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + virtual void SetFillColor(const wxColour& color); + + /// Defines the color used for all filling operations. + /** + * Affected filling operations are: filled rectangles and cell backgrounds. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value is + * retained from page to page. + * \param color defines a color using the class wxPdfColour + * \see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + virtual void SetFillColor(const wxPdfColour& color); + + /// Defines the color used for all filling operations. + /** + * Affected filling operations are: filled rectangles and cell backgrounds. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value is + * retained from page to page. + * \param red indicates the red level. Value between 0 and 255 + * \param green indicates the green level. Value between 0 and 255 + * \param blue indicates the blue level. Value between 0 and 255 + * \see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + virtual void SetFillColor(const unsigned char red, const unsigned char green, const unsigned char blue); + + /// Defines the color used for all filling operations. + /** + * Affected filling operations are: filled rectangles and cell backgrounds. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value is + * retained from page to page. + * \param cyan indicates the cyan level. Value between 0 and 100 + * \param magenta indicates the magenta level. Value between 0 and 100 + * \param yellow indicates the yellow level. Value between 0 and 100 + * \param black indicates the black level. Value between 0 and 100 + * \see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + virtual void SetFillColor(double cyan, double magenta, double yellow, double black); + + /// Defines the spot color used for all filling operations. + /** + * Affected filling operations are: filled rectangles and cell backgrounds. It can be expressed in RGB + * components or gray scale. The method can be called before the first page is created and the value is + * retained from page to page. + * \param name is the name of the spot color + * \param tint indicates the tint level. Value between 0 and 100. Default: 100. + * \see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + virtual void SetFillColor(const wxString& name, double tint = 100); + + + /// Defines the pattern used for all drawing operations. + /** + * N.B. You must be in the pattern color space before calling, see SetFillColorSpace. + * + * \param name is the name of the pattern + */ + virtual void SetFillPattern(const wxString& name); + + + /// Gets the color used for all filling operations. + /** + * \see SetFillColor() + */ + virtual const wxPdfColour GetFillColor(); + + /// Defines the color used for text. + /** + * It can be expressed in RGB components or gray scale. The method can be called before the first page is + * created and the value is retained from page to page. + * \param grayscale indicates the gray level. Value between 0 and 255 + * \see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + virtual void SetTextColor(const unsigned char grayscale); + + /// Defines the color used for text. + /** + * It can be expressed in RGB components or gray scale. The method can be called before the first page is + * created and the value is retained from page to page. + * \param color defines a color composed of a red, green and blue component + * \see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + virtual void SetTextColor(const wxColour& color); + + /// Defines the color used for text. + /** + * It can be expressed in RGB components or gray scale. The method can be called before the first page is + * created and the value is retained from page to page. + * \param color defines a color using the class wxPdfColour + * \see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + virtual void SetTextColor(const wxPdfColour& color); + + /// Defines the color used for text. + /** + * It can be expressed in RGB components or gray scale. The method can be called before the first page is + * created and the value is retained from page to page. + * \param red indicates the red level. Value between 0 and 255 + * \param green indicates the green level. Value between 0 and 255 + * \param blue indicates the blue level. Value between 0 and 255 + * \see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + virtual void SetTextColor(const unsigned char red, const unsigned char green, const unsigned char blue); + + /// Defines the color used for text. + /** + * It can be expressed in RGB components or gray scale. The method can be called before the first page is + * created and the value is retained from page to page. + * \param cyan indicates the cyan level. Value between 0 and 100 + * \param magenta indicates the magenta level. Value between 0 and 100 + * \param yellow indicates the yellow level. Value between 0 and 100 + * \param black indicates the black level. Value between 0 and 100 + * \see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + virtual void SetTextColor(double cyan, double magenta, double yellow, double black); + + /// Defines the spot color used for text. + /** + * It can be expressed in RGB components or gray scale. The method can be called before the first page is + * created and the value is retained from page to page. + * \param name the name of the spot color + * \param tint indicates the tint level. Value between 0 and 100. Default: 100. + * \see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + virtual void SetTextColor(const wxString& name, double tint = 100); + + /// Gets the color used for text output. + /** + * \see SetTextColor() + */ + virtual const wxPdfColour GetTextColor(); + + /// Returns the length of a string in user unit. + /** + * A font must be selected. + * \param s The string whose length is to be computed + * \return int + */ + virtual double GetStringWidth(const wxString& s); + + /// Defines the line width. + /** + * By default, the value equals 0.2 mm. The method can be called before the first page is created + * and the value is retained from page to page. + * \param width The width. + * \see Line(), Rect(), Cell(), MultiCell() + */ + virtual void SetLineWidth(double width); + + /// Gets the current line width. + /** + * \return current line width + * \see Line(), Rect(), Cell(), MultiCell() + */ + virtual double GetLineWidth(); + + /// Sets line style + /** + * \param linestyle: Line style. \see wxPdfLineStale + */ + virtual void SetLineStyle(const wxPdfLineStyle& linestyle); + + /// Get current line style + /** + * \return current line style. + */ + virtual const wxPdfLineStyle& GetLineStyle(); + + /// Draws a line between two points. + /** + * \param x1 Abscissa of first point + * \param y1 Ordinate of first point + * \param x2 Abscissa of second point + * \param y2 Ordinate of second point + * \see SetLineWidth(), SetDrawColor() + */ + virtual void Line(double x1, double y1, double x2, double y2); + + /// Draws an arrow line between two points. + /** + * \param x1 Abscissa of first point + * \param y1 Ordinate of first point + * \param x2 Abscissa of second point + * \param y2 Ordinate of second point + * \param linewidth line width + * \param height height of the arrow head + * \param width width of the arrow head + * \see SetLineWidth(), SetDrawColor(), SetFillColor() + */ + virtual void Arrow(double x1, double y1, double x2, double y2, double linewidth, double height, double width); + + /// Outputs a rectangle. + /** + * It can be drawn (border only), filled (with no border) or both. + * \param x Abscissa of upper-left corner + * \param y Ordinate of upper-left corner + * \param w Width + * \param h Height + * \param style Style of rendering. Possible values are: + * \li wxPDF_STYLE_DRAW (default) + * \li wxPDF_STYLE_FILL: fill + * \li wxPDF_STYLE_FILLDRAW: draw and fill + * \see SetLineWidth(), SetDrawColor(), SetFillColor() + */ + virtual void Rect(double x, double y, double w, double h, int style = wxPDF_STYLE_DRAW); + + /// Draws a rounded rectangle + /** + * \param x Abscissa of upper-left corner + * \param y Ordinate of upper-left corner + * \param w Width + * \param h Height + * \param r: Radius of the rounded corners + * \param roundCorner: Draws rounded corner or not. + * \li wxPDF_CORNER_NONE no rounded corners + * \li wxPDF_CORNER_TOP_LEFT top left corner + * \li wxPDF_CORNER_TOP_RIGHT top right corner + * \li wxPDF_CORNER_BOTTOM_LEFT bottom left corner + * \li wxPDF_CORNER_BOTTOM_RIGHT bottom right corner + * \li wxPDF_CORNER_ALL all corners + * \param style: Style of rectangle (draw and/or fill) + */ + virtual void RoundedRect(double x, double y, double w, double h, + double r, int roundCorner = wxPDF_CORNER_ALL, int style = wxPDF_STYLE_DRAW); + + /// Draws a Bezier curve + /** + * A Bezier curve is tangent to the line between the control points at either end of the curve. + * \param x0: Abscissa of start point + * \param y0: Ordinate of start point + * \param x1: Abscissa of control point 1 + * \param y1: Ordinate of control point 1 + * \param x2: Abscissa of control point 2 + * \param y2: Ordinate of control point 2 + * \param x3: Abscissa of end point + * \param y3: Ordinate of end point + * \param style: Style of rectangle (draw and/or fill) + */ + virtual void Curve(double x0, double y0, double x1, double y1, + double x2, double y2, double x3, double y3, + int style = wxPDF_STYLE_DRAW); + + /// Draws an ellipse + /** + * \param x0: Abscissa of Center point + * \param y0: Ordinate of Center point + * \param rx: Horizontal radius + * \param ry: Vertical radius (if ry = 0, draws a circle) + * \param angle: Orientation angle (anti-clockwise) + * \param astart: Start angle + * \param afinish: Finish angle + * \param style: Style of rectangle (draw and/or fill) + * \param nSeg: Ellipse is made up of nSeg Bezier curves + */ + virtual void Ellipse(double x0, double y0, double rx, double ry = 0, + double angle = 0, double astart = 0, double afinish = 360, + int style = wxPDF_STYLE_DRAW, int nSeg = 8); + + /// Draws a circle + /** + * \param x0: Abscissa of Center point + * \param y0: Ordinate of Center point + * \param r: Radius + * \param astart: Start angle + * \param afinish: Finish angle + * \param style: Style of rectangle (draw and/or fill) + * \param nSeg: Circle is made up of nSeg Bezier curves + */ + virtual void Circle(double x0, double y0, double r, + double astart = 0, double afinish = 360, + int style = wxPDF_STYLE_DRAW, int nSeg = 8); + + /// Draws a sector + /** + * \param x0: Abscissa of Center point + * \param y0: Ordinate of Center point + * \param r: Radius + * \param astart: Start angle + * \param afinish: Finish angle + * \param style: Style of rectangle (draw and/or fill, default: fill&draw) + * \param clockwise: indicates whether to go clockwise (default: true) + * \param origin: origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock; default: 90) + */ + virtual void Sector(double x0, double y0, double r, double astart, double afinish, + int style = wxPDF_STYLE_FILLDRAW, bool clockwise = true, double origin = 90.); + + /// Draws a polygon + /** + * \param x Array with abscissa values + * \param y Array with ordinate values + * \param style: Style of polygon (draw and/or fill) + */ + virtual void Polygon(const wxPdfArrayDouble& x, const wxPdfArrayDouble& y, + int style = wxPDF_STYLE_DRAW); + + /// Draws a regular polygon + /** + * \param x0: Abscissa of Center point + * \param y0: Ordinate of Center point + * \param r: Radius of circumscribed circle + * \param ns: Number of sides + * \param angle: Orientation angle (anti-clockwise) + * \param circle: Flag whether to draw circumscribed circle or not + * \param style: Style of polygon (draw and/or fill) + * \param circleStyle: Style of circumscribed circle (draw and/or fill) (if draw) + * \param circleLineStyle: Line style for circumscribed circle. (if draw) + * \param circleFillColor: Fill color for circumscribed circle. (if draw fill circle) + */ + virtual void RegularPolygon(double x0, double y0, double r, int ns, double angle = 0, bool circle = false, + int style = wxPDF_STYLE_DRAW, + int circleStyle = wxPDF_STYLE_DRAW, + const wxPdfLineStyle& circleLineStyle = wxPdfLineStyle(), + const wxPdfColour& circleFillColor = wxPdfColour()); + + /// Draws a star polygon + /** + * \param x0: Abscissa of Center point + * \param y0: Ordinate of Center point + * \param r: Radius of circumscribed circle + * \param nv: Number of vertices + * \param ng: Number of gaps (ng % nv = 1 => regular polygon) + * \param angle: Orientation angle (anti-clockwise) + * \param circle: Flag whether to draw circumscribed circle or not + * \param style: Style of polygon (draw and/or fill) + * \param circleStyle: Style of circumscribed circle (draw and/or fill) (if draw) + * \param circleLineStyle: Line style for circumscribed circle. (if draw) + * \param circleFillColor: Fill color for circumscribed circle. (if draw fill circle) + */ + virtual void StarPolygon(double x0, double y0, double r, int nv, int ng, double angle = 0, bool circle = false, + int style = wxPDF_STYLE_DRAW, + int circleStyle = wxPDF_STYLE_DRAW, + const wxPdfLineStyle& circleLineStyle = wxPdfLineStyle(), + const wxPdfColour& circleFillColor = wxPdfColour()); + + /// Draws a shape + /** + * \param shape: shape to be drawn + * \param style Style of rendering. Possible values are: + * \li wxPDF_STYLE_DRAW (default) + * \li wxPDF_STYLE_FILL: fill + * \li wxPDF_STYLE_FILLDRAW: draw and fill + * \li wxPDF_STYLE_DRAWCLOSE: close path and draw (can be combined with wxPDF_STYLE_FILL + * \param alternativeFillRule: If this is true, the even-odd rule will be used when filling, + * otherwise the default nonzero winding method will be used. + */ + virtual void Shape(const wxPdfShape& shape, int style = wxPDF_STYLE_DRAW, bool alternativeFillRule = false); + + /// Performs a rotation around a given center. + /** + * \param angle angle in degrees. + * \param x abscissa of the rotation center. Default value: current position. + * \param y ordinate of the rotation center. Default value: current position. + * + * The rotation affects all elements which are printed after the method call + * (with the exception of the clickable areas). + * + * Remarks: + * \li Only the display is altered. The GetX() and GetY() methods are not affected, + * nor the automatic page break mechanism. + * \li Rotation is not kept from page to page. Each page begins with a null rotation. + */ + virtual void Rotate(double angle, double x = -1, double y = -1); + + /// Sets the default path for font definition files + /** + * wxPdfDocument uses XML font definition files for embedding fonts. + * The definition file (and the font file itself when embedding) must be present + * in the path set by SetFontPath. + * + * \param fontPath the path to be used as the default font file path + * If an empty string is passed the default path is set to the path specified + * by the environment variable WXPDF_FONTPATH. If WXPDF_FONTPATH does not exist, + * the subdirectory 'fonts' of the current working directory is used instead. + */ + virtual void SetFontPath(const wxString& fontPath = wxEmptyString); + + /// Returns the current default path for font definition files + /** + * \return The default font path + */ + virtual wxString GetFontPath() const { return m_fontPath; }; + + /// Sets the font embedding mode + /** + * If other fonts than the 14 Adobe core fonts are used in a document, they are usually + * embedded into the PDF file, often resulting in rather large PDF files. This is + * especially true for Unicode fonts with thousands of glyphs. To reduce the size of + * the resulting PDF file fonts may be subsetted, that is, only those glyphs actually + * used in the document are embedded. + * + * Currently wxPdfDocument supports font subsetting for TrueType Unicode fonts only. + * + * \param fontSubsetting Boolean indicating whether font subsetting should be used or not. + */ + virtual void SetFontSubsetting(bool fontSubsetting = true) { m_fontSubsetting = fontSubsetting; } + + /// Returns the font embedding mode + /** + * \return true if font subsetting is enabled, false otherwise + */ + virtual bool GetFontSubsetting() const { return m_fontSubsetting; } + + /// Imports a TrueType, TrueTypeUnicode or Type1 font and makes it available. + /** + * It is necessary to generate a font definition file first with the makefont utility. + * The definition file (and the font file itself when embedding) must be present either + * in the subdirectory 'fonts' of the current working directory or in the one indicated + * by WXPDF_FONTPATH if this environment variable is defined. + * \param family Font family. The name can be chosen arbitrarily. If it is a standard family name, + * it will override the corresponding font. + * \param style Font style. Possible values are (case insensitive): + * \li empty string: regular (default) + * \li B: bold + * \li I: italic + * \li BI or IB: bold italic + * \param file The font definition file. By default, the name is built from the family and style, + * in lower case with no space. + * \see SetFont(), SetFontPath() + */ + virtual bool AddFont(const wxString& family, + const wxString& style = wxEmptyString, + const wxString& file = wxEmptyString); + + /// Imports a CJK (Chinese, Japanese or Korean) font and makes it available. + /** + * It is necessary to generate a font definition file first with the makefont utility. + * The definition file (and the font file itself when embedding) must be present either + * in the current directory or in the one indicated by WXPDF_FONTPATH if the constant is + * defined. + * \param family Font family. The name can be chosen arbitrarily. If it is a standard family name, + * it will override the corresponding font. + * + * All font styles (regular, bold, italic and bold-italic) are made available + * The font definition file name is built from the family in lower case with no space. + * There are several predefined font definition files available: + * \li BIG5 Chinese (traditional) + * \li BIG5-HW Chinese (traditional) half-width ASCII characters + * \li GB Chinese (simplified) + * \li GB-HW Chinese (simplified) half-width ASCII characters + * \li SJIS Japanese + * \li SJIS-HW Japanese, half-width ASCII characters + * \li UHC Korean + * \li UHC-HW Korean, half-width ASCII characters + * + * These fonts require that the Adobe CJK font support is installed + * \see SetFont() + */ +#if wxUSE_UNICODE + virtual bool AddFontCJK(const wxString& family); +#else + virtual bool AddFontCJK(const wxString& WXUNUSED(family)) { return false; } +#endif + + /// Sets the font used to print character strings. + /** + * It is mandatory to call this method at least once before printing text or the + * resulting document would not be valid. + * The font can be either a standard one or a font added via the AddFont() method. + * Standard fonts use Windows encoding cp1252 (Western Europe). + * The method can be called before the first page is created and the font is retained from page to page. + * If you just wish to change the current font size, it is simpler to call SetFontSize(). + * + * \param family Family font. It can be either a name defined by AddFont() or one of the standard + * families (case insensitive): + * \li Courier (fixed-width) + * \li Helvetica or Arial (synonymous; sans serif) + * \li Times (serif) + * \li Symbol (symbolic) + * \li ZapfDingbats (symbolic) + * + * It is also possible to pass an empty string. In that case, the current family is retained. + * \param style Font style. Possible values are (case insensitive): + * \li empty string: regular (default) + * \li B: bold + * \li I: italic + * \li BI or IB: bold italic + * \li U: underline + * or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats + * \param size Font size in points. The default value is the current size. If no size has been + * specified since the beginning of the document, the value taken is 12 + * \see AddFont(), SetFontSize(), Cell(), MultiCell(), Write() + */ + virtual bool SetFont(const wxString& family, + const wxString& style = wxEmptyString, + double size = 0); + + /// Defines the size of the current font. + /** + * \param size The size (in points) + * \see SetFont() + */ + virtual void SetFontSize(double size); + + /// Returns the current font description instance. + /** + * \return The current font description. + * \see SetFont() + */ + virtual const wxPdfFontDescription& GetFontDescription() const; + + /// Gets the font family of the current font. + /** + * \return The font family of the current font + * \see SetFont() + */ + virtual const wxString GetFontFamily(); + + /// Gets the style of the current font. + /** + * \return The style of the current font + * \see SetFont() + */ + virtual const wxString GetFontStyle(); + + /// Gets the size of the current font. + /** + * \return The size (in points) of the current font + * \see SetFont() + */ + virtual double GetFontSize(); + + /// Creates a new internal link and returns its identifier. + /** + * An internal link is a clickable area which directs to another place within the document. + * The identifier can then be passed to Cell(), Write(), Image() or Link(). + * The destination is defined with SetLink(). + * \see Cell(), Write(), Image(), Link(), SetLink() + */ + virtual int AddLink(); + + /// Defines the page and position a link points to. + /** + * \param link The link identifier returned by AddLink() + * \param y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) + * \param page Number of target page; -1 indicates the current page. This is the default value + * \see AddLink() + */ + virtual bool SetLink(int link, double y = 0., int page = -1); + + /// Puts a link on a rectangular area of the page. + /** + * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful + * for instance to define a clickable area inside an image. + * \param x Abscissa of the upper-left corner of the rectangle + * \param y Ordinate of the upper-left corner of the rectangle + * \param w Width of the rectangle + * \param h Height of the rectangle + * \param link URL or identifier returned by AddLink() + * \see AddLink(), Cell(), Write(), Image() + */ + virtual void Link(double x, double y, double w, double h, const wxPdfLink& link); + + /// Adds a bookmark to the document outline + /** + * \param txt: the bookmark title. + * \param level: the bookmark level (0 is top level, 1 is just below, and so on). + * \param y: the y position of the bookmark destination in the current page. + * -1 means the current position. Default value: 0. + */ + virtual void Bookmark(const wxString& txt, int level = 0, double y = 0); + + /// Prints a character string. + /** + * The origin is on the left of the first charcter, on the baseline. + * This method allows to place a string precisely on the page, but it is usually easier to use Cell(), + * MultiCell() or Write() which are the standard methods to print text. + * \param x Abscissa of the origin + * \param y Ordinate of the origin + * \param txt String to print + * \param mode The rendering mode (0=normal, 1 = stroked, 2 = filled then stroked) + * \see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() + */ + virtual void Text(double x, double y, const wxString& txt, const int mode = 0); + + /// Prints a rotated text string + /** + * \param x: abscissa of the rotation center. + * \param y: ordinate of the rotation center. + * \param txt String to print + * \param mode The rendering mode (0=normal, 1 = stroked, 2 = filled then stroked) + * \param angle: angle in degrees. + */ + virtual void RotatedText(double x, double y, const wxString& txt, double angle, const int mode = 0); + + /// Whenever a page break condition is met, + /** + * Whenever a page break condition is met, the method is called, and the break is issued or not + * depending on the returned value. The default implementation returns a value according to the + * mode selected by SetAutoPageBreak() + * + * This method is called automatically and should not be called directly by the application. + * \return boolean + * \see SetAutoPageBreak() + */ + virtual bool AcceptPageBreak(); + + /// Prints a cell (rectangular area) with optional borders, background color and character string. + /** + * The upper-left corner of the cell corresponds to the current position. The text can be aligned + * or centered. After the call, the current position moves to the right or to the next line. + * It is possible to put a link on the text. + * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done + * before outputting. + * \param w Cell width. If 0, the cell extends up to the right margin. + * \param h Cell height. Default value: 0. + * \param txt String to print. Default value: empty string. + * \param border Indicates if borders must be drawn around the cell. The value can be + * \li wxPDF_BORDER_NONE no border + * \li wxPDF_BORDER_LEFT left border + * \li wxPDF_BORDER_RIGHT right border + * \li wxPDF_BORDER_TOP top border + * \li wxPDF_BORDER_BOTTOM bottom border + * \li wxPDF_BORDER_FRAME border on all sides + * + * or a combination of them. + * \param ln Indicates where the current position should go after the call. Possible values are: + * \li 0: to the right + * \li 1: to the beginning of the next line + * \li 2: below + * + * Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. + * \param align Allows to center or align the text. Possible values are:
  • L or empty string: left align (default value)
  • C: center
  • R: right align
+ * \li wxPDF_ALIGN_LEFT align the text at the left margin + * \li wxPDF_ALIGN_CENTER center the text + * \li wxPDF_ALIGN_RIGHT align the text at the right margin + * \li wxPDF_ALIGN_JUSTIFY justify the text + * + * \param fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * \param link URL or identifier returned by AddLink(). + * \see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() + */ + virtual void Cell(double w, double h = 0., const wxString& txt = wxEmptyString, + int border = wxPDF_BORDER_NONE, int ln = 0, + int align = wxPDF_ALIGN_LEFT, int fill = 0, + const wxPdfLink& link = wxPdfLink(-1)); + + /// This method allows printing text with line breaks. + /** + * They can be automatic (as soon as the text reaches the right border of the cell) or explicit + * (via the \n character). As many cells as necessary are output, one below the other. + * Text can be aligned, centered or justified. The cell block can be framed and the background painted. + * \param w Width of cells. If 0, they extend up to the right margin of the page. + * \param h Height of cells. + * \param txt String to print + * \param border Indicates if borders must be drawn around the cell. The value can be + * \li wxPDF_BORDER_NONE no border + * \li wxPDF_BORDER_LEFT left border + * \li wxPDF_BORDER_RIGHT right border + * \li wxPDF_BORDER_TOP top border + * \li wxPDF_BORDER_BOTTOM bottom border + * \li wxPDF_BORDER_FRAME border on all sides + * + * or a combination of them. + * \param align Allows to center or align the text. Possible values are: + * \li wxPDF_ALIGN_LEFT align the text at the left margin + * \li wxPDF_ALIGN_CENTER center the text + * \li wxPDF_ALIGN_RIGHT align the text at the right margin + * \li wxPDF_ALIGN_JUSTIFY justify the text (default) + * + * \param fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * \param maxline Defines the maximum number of lines which should be printed. + * If maxline is 0 then the number of lines is not restricted. Default value: 0. + * \return position in text string txt where output ended due to reaching the maximum number of lines + * \see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() + */ + virtual int MultiCell(double w, double h, const wxString& txt, + int border = 0, int align = wxPDF_ALIGN_JUSTIFY, + int fill = 0, int maxline = 0); + + /// This method counts the number of lines a text would occupy in respect to a given maximal width + /** + * \param w Width of cells. If 0, they extend up to the right margin of the page. + * \param txt String for which the number of lines is to be counted + * \return Number of lines this text would occupy + */ + virtual int LineCount(double w, const wxString& txt); + + /// This method counts the number of lines a text will occupy in respect to a given maximal width + /** + * \param w Width of cells. If 0, they extend up to the right margin of the page. + * \param h Height of cells. + * \param txt String to print + * \param halign Allows to center or align the text. Possible values are: + * \li wxPDF_ALIGN_LEFT align the text at the left margin + * \li wxPDF_ALIGN_CENTER center the text + * \li wxPDF_ALIGN_RIGHT align the text at the right margin + * \li wxPDF_ALIGN_JUSTIFY justify the text (default) + * + * \param valign Allows to vertical align the text. Possible values are: + * \li wxPDF_ALIGN_TOP align the text at the top of the box + * \li wxPDF_ALIGN_MIDDLE center the text vertically in the box + * \li wxPDF_ALIGN_BOTTOM align the text at the bottom of the box + * + * \param border Indicates if borders must be drawn around the text box. The value can be + * \li wxPDF_BORDER_NONE no border + * \li wxPDF_BORDER_LEFT left border + * \li wxPDF_BORDER_RIGHT right border + * \li wxPDF_BORDER_TOP top border + * \li wxPDF_BORDER_BOTTOM bottom border + * \li wxPDF_BORDER_FRAME border on all sides + * + * or a combination of them. + * \param fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + */ + virtual int TextBox(double w, double h, const wxString& txt, + int halign = wxPDF_ALIGN_JUSTIFY, int valign = wxPDF_ALIGN_TOP, + int border = 0, int fill = 0); + + /// This method prints text from the current position. + /** + * When the right margin is reached (or the \n character is met) a line break occurs and text continues + * from the left margin. Upon method exit, the current position is left just at the end of the text. + * It is possible to put a link on the text. + * \param h Line height + * \param txt String to print + * \param link URL or identifier returned by AddLink() + * \see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() + */ + virtual void Write(double h, const wxString& txt, const wxPdfLink& link = wxPdfLink(-1)); + + /// This method prints text with cell attributes from the current position. + /** + * When the right margin is reached (or the \n character is met) a line break occurs and text continues + * from the left margin. Upon method exit, the current position is left just at the end of the text. + * It is possible to put a link on the text. + * \param h Line height + * \param txt String to print + * \param border Indicates if borders must be drawn around the cell. The value can be + * \li wxPDF_BORDER_NONE no border + * \li wxPDF_BORDER_LEFT left border + * \li wxPDF_BORDER_RIGHT right border + * \li wxPDF_BORDER_TOP top border + * \li wxPDF_BORDER_BOTTOM bottom border + * \li wxPDF_BORDER_FRAME border on all sides + * + * or a combination of them. + * \param fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * \param link URL or identifier returned by AddLink() + * \see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() + */ + virtual void WriteCell(double h, const wxString& txt, int border = wxPDF_BORDER_NONE, int fill = 0, const wxPdfLink& link = wxPdfLink(-1)); + + /// Puts an image in the page. + /** + * The upper-left corner must be given. The dimensions can be specified in different ways: + * \li explicit width and height (expressed in user unit) + * \li one explicit dimension, the other being calculated automatically in order to keep the original proportions + * \li no explicit dimension, in which case the image is put at 72 dpi + * + * Supported formats are JPEG, PNG, GIF and WMF. + * For JPEG, all flavors are allowed: + * \li gray scales, + * \li true colors (24 bits), + * \li CMYK (32 bits) + * + * For PNG, the following flavors are allowed: + * \li gray scales on at most 8 bits (256 levels) + * \li indexed colors + * \li true colors (24 bits) + * + * but the following options are not supported: + * \li Interlacing + * \li Alpha channel + * + * If a transparent color is defined, it will be taken into account (but will be only interpreted + * by Acrobat 4 and above). + * + * For GIF, all flavors the wsWidgets GIF decoder is able to handle are supported + * + * For WMF: WMF files contain vector data described in terms of Windows Graphics Device Interface + * (GDI) commands. There are approximately 80 different GDI commands allowed for in the WMF standard. + * This method interprets only a small subset of these, but is sufficient to display most WMF images. + * Please feel free to add further functionality. + * + * The format can be specified explicitly or inferred from the file extension. + * + * It is possible to put a link on the image. + * + * Remark: if an image is used several times, only one copy will be embedded in the file. + * + * \param file Name of the file containing the image. + * \param x Abscissa of the upper-left corner. + * \param y Ordinate of the upper-left corner. + * \param w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param mimeType Image format. Possible values are: image/jpeg, image/png, image/gif, image/wmf. + * If not specified, the type is inferred from the file extension. + * \param link URL or identifier returned by AddLink(). + * \param maskImage Id of an image mask created previously by ImageMask(). + * \see AddLink() + */ + virtual bool Image(const wxString& file, double x, double y, double w = 0, double h = 0, + const wxString& mimeType = wxEmptyString, + const wxPdfLink& link = wxPdfLink(-1), + int maskImage = 0); + + /** + * Puts an image in the page + * The image is given by an wxImage-Object + * \param name Name of the image to be used as an identifier for this image object. + * \param image wxImage object which will be embedded as PNG + * \param x Abscissa of the upper-left corner. + * \param y Ordinate of the upper-left corner. + * \param w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param link URL or identifier returned by AddLink(). + * \param maskImage Id of an image mask created previously by ImageMask(). + */ + virtual bool Image(const wxString& name, const wxImage& image, + double x, double y, double w = 0, double h = 0, + const wxPdfLink& link = wxPdfLink(-1), + int maskImage = 0); + + /** + * Puts an image in the page + * The image is given by an wxInputStream-Object containing the raw image data. + * \param name Name of the image to be used as an identifier for this image object. + * \param stream wxInputStream object containing the raw image data + * \param mimeType Image format. Possible values are: image/jpeg, image/png, image/gif, image/wmf. + * \param x Abscissa of the upper-left corner. + * \param y Ordinate of the upper-left corner. + * \param w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param link URL or identifier returned by AddLink(). + * \param maskImage Id of an image mask created previously by ImageMask(). + */ + virtual bool Image(const wxString& name, wxInputStream& stream, + const wxString& mimeType, + double x, double y, double w = 0, double h = 0, + const wxPdfLink& link = wxPdfLink(-1), + int maskImage = 0); + + /** + * Prepares an image for use as an image mask + * The image is given as the name of the file conatining the image + * \param file Name of the file containing the image. + * \param mimeType Image format. Possible values are: image/jpeg, image/png, image/gif, image/wmf. + * \returns id of the image mask, or 0 in case of an error + */ + virtual int ImageMask(const wxString& file, const wxString& mimeType = wxEmptyString); + + /** + * Prepares an image for use as an image mask + * The image is given by an wxImage-Object + * \param name Name of the image. + * \param image wxImage object. + * \returns id of the image mask, or 0 in case of an error + */ + virtual int ImageMask(const wxString& name, const wxImage& image); + + /** + * Prepares an image for use as an image mask + * The image is given by an wxInputStream-Object containing the raw image data. + * \param name Name of the image. + * \param stream wxInputStream object containing the raw image data + * \param mimeType Image format. Possible values are: image/jpeg, image/png, image/gif, image/wmf. + * \returns id of the image mask, or 0 in case of an error + */ + virtual int ImageMask(const wxString& name, wxInputStream& stream, const wxString& mimeType); + + /// Puts a rotated image in the page. + /** + * The upper-left corner must be given. + * + * The format can be specified explicitly or inferred from the file extension. + * + * It is possible to put a link on the image. + * + * Remark: if an image is used several times, only one copy will be embedded in the file. + * + * \param file Name of the file containing the image. + * \param x Abscissa of the upper-left corner. + * \param y Ordinate of the upper-left corner. + * \param w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. + * \param angle Angle of rotation + * \param type Image format. Possible values are (case insensitive): JPG, JPEG, PNG, GIF, WMF. + * If not specified, the type is inferred from the file extension. + * \param link URL or identifier returned by AddLink(). + * \param maskImage Id of an image mask created previously by ImageMask(). + * \see Image(), AddLink() + */ + virtual void RotatedImage(const wxString& file, double x, double y, double w, double h, + double angle, + const wxString& type = wxEmptyString, + const wxPdfLink& link = wxPdfLink(-1), + int maskImage = 0); + + /// Performs a line break. + /** + * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. + * \param h The height of the break. By default, the value equals the height of the last printed cell. + * \see Cell() + */ + virtual void Ln(double h = -1); + + /// Returns the abscissa of the current position. + /** + * \return float + * \see SetX(), GetY(), SetY() + */ + virtual double GetX(); + + /// Defines the abscissa of the current position. + /** + * If the passed value is negative, it is relative to the right of the page. + * \param x The value of the abscissa. + * \see GetX(), GetY(), SetY(), SetXY() + */ + virtual void SetX(double x); + + /// Returns the ordinate of the current position. + /** + * \return float + * \see SetY(), GetX(), SetX() + */ + virtual double GetY(); + + /// Moves the current abscissa back to the left margin and sets the ordinate. + /** + * If the passed value is negative, it is relative to the bottom of the page. + * \param y The value of the ordinate. + * \see GetX(), GetY(), SetY(), SetXY() + */ + virtual void SetY(double y); + + /// Defines the abscissa and ordinate of the current position. + /** + * If the passed values are negative, they are relative respectively to the right and bottom of the page. + * \param x The value of the abscissa + * \param y The value of the ordinate + * \see SetX(), SetY() + */ + virtual void SetXY(double x, double y); + + /// Saves the document to a file on disk + /** + * The method first calls Close() if necessary to terminate the document. + * \param name The name of the file. If not given, the document will be named 'doc.pdf' + * \see Close() + */ + virtual void SaveAsFile(const wxString& name = wxEmptyString); + + /// Closes the document and returns the memory buffer containing the document + /** + * The method first calls Close() if necessary to terminate the document. + * \return const wxMemoryOutputStream reference to the buffer containing the PDF document. + * \see Close() + */ + virtual const wxMemoryOutputStream& CloseAndGetBuffer(); + + /// Define text as clipping area + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * \param x Abscissa of the origin + * \param y Ordinate of the origin + * \param txt String to print + * \param outline Draw the outline or not. + */ + virtual void ClippingText(double x, double y, const wxString& txt, bool outline = false); + + /// Define rectangle as clipping area + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * \param x Abscissa of the upper-left corner + * \param y Ordinate of the upper-left corner + * \param w Width of the rectangle + * \param h Height of the rectangle + * \param outline Draw the outline or not. + */ + virtual void ClippingRect(double x, double y, double w, double h, bool outline = false); + + /// Define ellipse as clipping area + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * \param x Abscissa of the Center point + * \param y Ordinate of the Center point + * \param rx: Horizontal radius + * \param ry: Vertical radius (if ry = 0, draws a circle) + * \param outline Draw the outline or not. (Default false) + */ + virtual void ClippingEllipse(double x, double y, double rx, double ry = 0, bool outline = false); + + /// Define polygon as clipping area + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * \param x Array with abscissa values + * \param y Array with ordinate values + * \param outline Draw the outline or not. (Default false) + */ + virtual void ClippingPolygon(const wxPdfArrayDouble& x, const wxPdfArrayDouble& y, bool outline = false); + + /// Start defining a clipping path + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * The clipping path may consist of one or more subpaths. + */ + virtual void ClippingPath(); + + /// Begin a new subpath + /** + * Move to the starting point of a new (sub)path. + * The new current point is (x, y). + * \param x abscissa value + * \param y ordinate value + * \remark This must be the first operation after ClippingPath(). + */ + virtual void MoveTo(double x, double y); + + /// Append a straight line segment to the current (sub)path + /** + * Append a straight line segment from the current point to the point (x, y). + * The new current point is (x, y). + * \param x abscissa value + * \param y ordinate value + */ + virtual void LineTo(double x, double y); + + /// Append a cubic Bezier curve to the current (sub)path + /** + * Append a cubic Bezier curve to the current path. The curve extends + * from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) + * as the Bézier control points. The new current point is (x3, y3). + * \param x1: Abscissa of control point 1 + * \param y1: Ordinate of control point 1 + * \param x2: Abscissa of control point 2 + * \param y2: Ordinate of control point 2 + * \param x3: Abscissa of end point + * \param y3: Ordinate of end point + */ + virtual void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3); + + /// Close the clipping path + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * \param style Style of rendering. Possible values are: + * \li wxPDF_STYLE_NOOP (default) + * \li wxPDF_STYLE_DRAW: draw the outline of the clipping path + * \li wxPDF_STYLE_FILL: fill the area enclosed by the clipping path + * \li wxPDF_STYLE_FILLDRAW: draw and fill + */ + virtual void ClosePath(int style = wxPDF_STYLE_NOOP); + + /// Define clipping area using a shape + /** + * A clipping area restricts the display and prevents any elements from showing outside of it. + * \param shape shape defining the clipping path + * \param style Style of rendering. Possible values are: + * \li wxPDF_STYLE_NOOP (default) + * \li wxPDF_STYLE_DRAW: draw the outline of the clipping path + * \li wxPDF_STYLE_FILL: fill the area enclosed by the clipping path + * \li wxPDF_STYLE_FILLDRAW: draw and fill + */ + virtual void ClippingPath(const wxPdfShape& shape, int style = wxPDF_STYLE_NOOP); + + /// Remove clipping area + /** + * Once you have finished using the clipping, you must remove it with UnsetClipping(). + */ + virtual void UnsetClipping(); + + /// Prints a cell clipped to a rectangular area + /** + * The upper-left corner of the cell corresponds to the current position. The text can be aligned + * or centered. After the call, the current position moves to the right or to the next line. + * It is possible to put a link on the text. + * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done + * before outputting. + * \param w Cell width. + * \param h Cell height. + * \param txt String to print. Default value: empty string. + * \param border Indicates if borders must be drawn around the cell. The value can be + * \li wxPDF_BORDER_NONE no border + * \li wxPDF_BORDER_LEFT left border + * \li wxPDF_BORDER_RIGHT right border + * \li wxPDF_BORDER_TOP top border + * \li wxPDF_BORDER_BOTTOM bottom border + * \li wxPDF_BORDER_FRAME border on all sides + * + * or a combination of them. + * \param ln Indicates where the current position should go after the call. Possible values are: + * \li 0: to the right + * \li 1: to the beginning of the next line + * \li 2: below + * + * Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. + * \param align Allows to center or align the text. Possible values are:
  • L or empty string: left align (default value)
  • C: center
  • R: right align
+ * \li wxPDF_ALIGN_LEFT align the text at the left margin + * \li wxPDF_ALIGN_CENTER center the text + * \li wxPDF_ALIGN_RIGHT align the text at the right margin + * \li wxPDF_ALIGN_JUSTIFY justify the text + * + * \param fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * \param link URL or identifier returned by AddLink(). + * \see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() + */ + virtual void ClippedCell(double w, double h = 0., const wxString& txt = wxEmptyString, + int border = wxPDF_BORDER_NONE, int ln = 0, + int align = wxPDF_ALIGN_LEFT, int fill = 0, + const wxPdfLink& link = wxPdfLink(-1)); + + /// Enters a transformation environment + /** + * Before applying any transformation this method should be invoked. + * All transformation method invoke it implicitly if necessary. + * All open transformation environments are closed implicitly on page end. + */ + virtual void StartTransform(); + + /// Performs scaling in X direction only + /** + * A scaling transformation is applied for the X direction. + * \param sx: scaling factor for width as percent. 0 is not allowed. + * \param x: abscissa of the scaling center. Default is current x position + * \param y: ordinate of the scaling center. Default is current y position + */ + virtual bool ScaleX(double sx, double x = -1, double y = -1); + + /// Performs scaling in Y direction only + /** + * A scaling transformation is applied for the Y direction. + * \param sy: scaling factor for height as percent. 0 is not allowed. + * \param x: abscissa of the scaling center. Default is current x position + * \param y: ordinate of the scaling center. Default is current y position + */ + virtual bool ScaleY(double sy, double x = -1, double y = -1); + + /// Performs equal scaling in X and Y direction + /** + * A scaling transformation is applied for both - X and Y - directions. + * \param s: scaling factor for width and height as percent. 0 is not allowed. + * \param x: abscissa of the scaling center. Default is current x position + * \param y: ordinate of the scaling center. Default is current y position + */ + virtual bool ScaleXY(double s, double x = -1, double y = -1); + + /// Performs scaling in X and Y direction + /** + * A scaling transformation is applied independently for X and Y direction. + * \param sx: scaling factor for width in percent. 0 is not allowed. + * \param sy: scaling factor for height in percent. 0 is not allowed. + * \param x: abscissa of the scaling center. Default is current x position + * \param y: ordinate of the scaling center. Default is current y position + */ + virtual bool Scale(double sx, double sy, double x = -1, double y = -1); + + /// Performs a horizontal mirroring transformation + /** + * Alias for scaling -100% in x-direction + * \param x: abscissa of the axis of reflection + */ + virtual void MirrorH(double x = -1); + + /// Performs a vertical mirroring transformation + /** + * Alias for scaling -100% in y-direction + * \param y: abscissa of the axis of reflection + */ + virtual void MirrorV(double y = -1); + + /// Moves the X origin + /** + * \param tx: movement to the right + */ + virtual void TranslateX(double tx); + + /// Moves the Y origin + /** + * \param ty: movement to the bottom + */ + virtual void TranslateY(double ty); + + /// Moves the origin + /** + * \param tx: movement to the right + * \param ty: movement to the bottom + */ + virtual void Translate(double tx, double ty); + +// virtual void Rotate(double angle, double x = -1, double y = -1); + + /// Performs a skewing in both X direction only + /** + * \param xAngle: angle in degrees between -90 (skew to the left) and 90 (skew to the right) + * \param x: abscissa of the skewing center. default is current x position + * \param y: ordinate of the skewing center. default is current y position + */ + virtual bool SkewX(double xAngle, double x = -1, double y = -1); + + /// Performs a skewing in Y direction only + /** + * \param yAngle: angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) + * \param x: abscissa of the skewing center. default is current x position + * \param y: ordinate of the skewing center. default is current y position + */ + virtual bool SkewY(double yAngle, double x = -1, double y = -1); + + /// Performs a skewing in both X and Y directions + /** + * \param xAngle: angle in degrees between -90 (skew to the left) and 90 (skew to the right) + * \param yAngle: angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) + * \param x: abscissa of the skewing center. default is current x position + * \param y: ordinate of the skewing center. default is current y position + */ + virtual bool Skew(double xAngle, double yAngle, double x = -1, double y = -1); + + /// Leaves a transformation environment + /** + * This method should be invoked to cancel a transformation environment + * opened by StartTransform. + * All open transformation environments are closed implicitly on page end. + */ + virtual void StopTransform(); + + /// Sets alpha values and blend mode + /** + * \param lineAlpha alpha value for stroking operations, from 0 (transparent) to 1 (opaque) + * \param fillAlpha alpha value for non-stroking operations, from 0 (transparent) to 1 (opaque) + * \param blendMode one of the following: + * Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, + * HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity + */ + virtual int SetAlpha(double lineAlpha = 1, double fillAlpha = 1, wxPdfBlendMode blendMode = wxPDF_BLENDMODE_NORMAL); + + /// Sets a previously defined alpha state + /** + * \param alphaState id of alpha state + */ + virtual void SetAlphaState(int alphaState); + + + /// Defines an image pattern + /** + * Add a pattern which can be reference in fill pattern methods + * \param name the name of the pattern (case sensitive) + * \param image the image to use for the pattern + * \param imagename the name of the image (case sensitive) + * \param width the display width + * \param height the display height + */ + virtual void AddPattern(const wxString& name, const wxImage& image, const wxString& imageName, const double width, const double height); + + + /// Defines a linear gradient shading + /** + * \param col1 first color (RGB or CMYK). + * \param col2 second color (RGB or CMYK). + * \param gradientType Type of the gradient + */ + virtual int LinearGradient(const wxPdfColour& col1, const wxPdfColour& col2, + wxPdfLinearGradientType gradientType = wxPDF_LINEAR_GRADIENT_HORIZONTAL); + + + /// Defines a axial gradient shading + /** + * \param col1 first color (RGB or CMYK). + * \param col2 second color (RGB or CMYK). + * \param x1 start point of gradient vector, default: 0 (range 0 .. 1) + * \param y1 start point of gradient vector, default: 0 (range 0 .. 1) + * \param x2 end point of gradient vector, default: 1 (range 0 .. 1) + * \param y2 end point of gradient vector, default: 0 (range 0 .. 1) + * \param intexp interpolation exponent, default: 1 + */ + virtual int AxialGradient(const wxPdfColour& col1, const wxPdfColour& col2, + double x1 = 0, double y1 = 0, + double x2 = 1, double y2 = 0, + double intexp = 1); + + /// Defines a axial gradient shading + /** + * \param col1 first color (RGB or CMYK). + * \param col2 second color (RGB or CMYK). + * \param x1 start point of gradient vector, default: 0 (range 0 .. 1) + * \param y1 start point of gradient vector, default: 0 (range 0 .. 1) + * \param x2 end point of gradient vector, default: 1 (range 0 .. 1) + * \param y2 end point of gradient vector, default: 0 (range 0 .. 1) + * \param midpoint position of the mirror point, default: 0.5 (range 0 .. 1) + * \param intexp interpolation exponent, default: 1 + */ + virtual int MidAxialGradient(const wxPdfColour& col1, const wxPdfColour& col2, + double x1 = 0, double y1 = 0, + double x2 = 1, double y2 = 0, + double midpoint = 0.5, double intexp = 1); + + /// Defines a radial gradient shading + /** + * \param col1 first color (RGB or CMYK). + * \param col2 second color (RGB or CMYK). + * \param x1 center point of circle 1, default: 0.5 (range 0 .. 1) + * \param y1 center point of circle 1, default: 0.5 (range 0 .. 1) + * \param r1 radius of circle 1, default: 0 + * \param x2 center point of circle 2, default: 0.5 (range 0 .. 1) + * \param y2 center point of circle 2, default: 0.5 (range 0 .. 1) + * \param r2 radius of circle 2, default: 1 + * \param intexp interpolation exponent, default: 1 + */ + virtual int RadialGradient(const wxPdfColour& col1, const wxPdfColour& col2, + double x1 = 0.5, double y1 = 0.5, double r1 = 0, + double x2 = 0.5, double y2 = 0.5, double r2 = 1, + double intexp = 1); + + /// Defines a coons patch mesh gradient shading + /** + * \param mesh coons patch mesh to be used for the gradient + * \param minCoord minimal coordinate of the mesh + * \param maxCoord maximal coordinate of the mesh + */ + virtual int CoonsPatchGradient(const wxPdfCoonsPatchMesh& mesh, double minCoord = 0, double maxCoord = 1); + + /// Paints a gradient shading to rectangular area + /** + * \param x abscissa of the top left corner of the rectangle. + * \param y ordinate of the top left corner of the rectangle. + * \param w width of the rectangle. + * \param h height of the rectangle. + * \param gradient id of the gradient. + */ + virtual void SetFillGradient(double x, double y, double w, double h, int gradient); + + /// Add an optional content group + /** + * The OCG will be managed by this class after its added, so you dont need to destroy it yourself. + * This means the OCG must have been created dynamically, not statically. + * \param[in] ocg The OCG to add + */ + virtual void AddOcg(wxPdfOcg *ocg) { m_pdfOc.AddOcg(ocg); }; + + /// Start marking content as optional + /** + * This starts marking content as belonging to the specified ocg. Call + * ExitOcg to stop marking content. + * \param[in] ocg The OCG to mark content as belonging to + */ + virtual void EnterOcg(const wxPdfOcg *ocg); + + /// Stop marking content as optional + /** + * This stops marking content as belonging to the previously entered ocg. + */ + virtual void ExitOcg(void); + + + /// Draws a graphical marker symbol + /** + * \param x abscissa of the marker's center + * \param y ordinate of the marker's center + * \param markerType type of the marker + * \param size size of the marker + */ + virtual void Marker(double x, double y, wxPdfMarker markerType, double size); + + /// Adds a text annotation + /** + * \param x abscissa of the annotation symbol + * \param y ordinate of the annotation symbol + * \param text annotation text + */ + virtual void Annotate(double x, double y, const wxString& text); + + /// Appends Javascript + /** + * Allows to append Javascript code to a Javascript object at the document level. + * \param javascript Javascript code to be appended + */ + virtual void AppendJavascript(const wxString& javascript); + + /// Prints a string containing simple XML markup + /** + * Output starts at the current position. + * \param str string containing text with simple XML markup + * \see \ref writexml + */ + void WriteXml(const wxString& str); + + /// Adds a check box field at the current position + /** + * Adds a check box to the list of form fields at the current position + * \param name field name of the check box + * \param width width of the check box + * \param checked default value of the check box + */ + void CheckBox(const wxString& name, double width, bool checked = false); + + /// Adds a check box field + /** + * Adds a check box to the list of form fields + * \param name field name of the check box + * \param x abscissa of the check box position + * \param y ordinate of the check box position + * \param width width of the check box + * \param checked default value of the check box + */ + void CheckBox(const wxString& name, double x, double y, double width, bool checked = false); + + /// Adds a combo box field at the current position + /** + * Adds a combo box to the list of form fields at the current position + * \param name field name of the combo box + * \param width width of the combo box + * \param height height of the combo box + * \param values array of option values of the combo box + */ + void ComboBox(const wxString& name, double width, double height, const wxArrayString& values); + + /// Adds a combo box field + /** + * Adds a combo box to the list of form fields + * \param name field name of the combo box + * \param x abscissa of the combo box position + * \param y ordinate of the combo box position + * \param width width of the combo box + * \param height height of the combo box + * \param values array of option values of the combo box + */ + void ComboBox(const wxString& name, + double x, double y, double width, double height, + const wxArrayString& values); + + /// Adds a push button at the current position + /** + * Adds a push button to the list of form fields at the current position + * \param name field name of the push button + * \param width width of the push button + * \param height height of the push button + * \param caption caption of the push button + * \param action associated Javascript action + */ + void PushButton(const wxString& name, double width, double height, + const wxString& caption, const wxString& action); + + /// Adds a push button + /** + * Adds a push button to the list of form fields + * \param name field name of the push button + * \param x abscissa of the push button position + * \param y ordinate of the push button position + * \param width width of the push button + * \param height height of the push button + * \param caption caption of the push button + * \param action associated Javascript action + */ + void PushButton(const wxString& name, double x, double y, double width, double height, + const wxString& caption, const wxString& action); + + /// Adds a radio button at the current position + /** + * Adds a radio button to the list of form fields at the current position + * \param group name of the radio button group this radio button belongs to + * \param name field name of the radio button + * \param width width of the radio button + */ + void RadioButton(const wxString& group, const wxString& name, double width); + + /// Adds a radio button + /** + * Adds a radio button to the list of form fields + * \param group name of the radio button group this radio button belongs to + * \param name field name of the radio button + * \param x abscissa of the radio button position + * \param y ordinate of the radio button position + * \param width width of the radio button + */ + void RadioButton(const wxString& group, const wxString& name, + double x, double y, double width); + + /// Adds a text field at the current position + /** + * Adds a text field to the list of form fields at the current position + * \param name field name of the text field + * \param width width of the text field + * \param height height of the text field + * \param value default value of the text field + * \param multiline flag whether the text field is a multiline field or not + */ + void TextField(const wxString& name, double width, double height, + const wxString& value = wxEmptyString, bool multiline = false); + + /// Adds a text field + /** + * Adds a text field to the list of form fields + * \param name field name of the text field + * \param x abscissa of the text field position + * \param y ordinate of the text field position + * \param width width of the text field + * \param height height of the text field + * \param value default value of the text field + * \param multiline flag whether the text field is a multiline field or not + */ + void TextField(const wxString& name, + double x, double y, double width, double height, + const wxString& value = wxEmptyString, bool multiline = false); + + /// Sets colors for form fields + /** + * Sets the border, background and text color to be used + * for all subsequent form field additions until this method is called again + * with different values. + * \param borderColor color of the form field's border + * \param backgroundColor color of the form field's background + * \param textColor color of the form field's font + */ + void SetFormColors(const wxPdfColour& borderColor = wxPdfColour(), + const wxPdfColour& backgroundColor = wxPdfColour(250), + const wxPdfColour& textColor = wxPdfColour()); + + /// Sets the border style for form fields + /** + * Sets the border width and style to be used + * for all subsequent form field additions until this method is called again + * with different values. + * \param borderStyle style of the form field's border + * \li wxPDF_BORDER_SOLID - solid border + * \li wxPDF_BORDER_DASHED - dashed border + * \li wxPDF_BORDER_BEVELED - beveled border + * \li wxPDF_BORDER_INSET - inset border + * \li wxPDF_BORDER_UNDERLINE - border on the bottom side only + * \param borderWidth width of the form field's border + */ + void SetFormBorderStyle(wxPdfBorderStyle borderStyle = wxPDF_BORDER_SOLID, + double borderWidth = -1); + + /// Starts a new Template + /** + * Starts a new template, optionally with own dimensions. + * The margins have to adapted to the new template size. + * For writing outside the template, for example to build a clipped template, + * the margins and "cursor" position have to be set manually after + * the call to BeginTemplate(). + * + * If no dimensions are given, the template uses the current page size. + * The method returns the ID of the current template. + * The ID is used to reference a template in the UseTemplate() method. + * Warning: A template once created is embedded in the resulting PDF document + * at all events, even if it is not used. + * + * \param x The x-coordinate given in user units + * \param y The y-coordinate given in user units + * \param width The width given in user units + * \param height The height given in user units + * \return int The ID of the created template + * \see EndTemplate(), UseTemplate() + * + * Attention: Calls to BeginTemplate can not be nested! + */ + int BeginTemplate(double x = 0, double y = 0, double width = 0, double height = 0); + + /// Terminates a template + /** + * Terminates the creation of a template and reset initiated variables on beginTemplate. + * + * \return If a template was under construction, its ID is returned, otherwise a 0 is returned. + * \see BeginTemplate(), UseTemplate() + */ + int EndTemplate(); + + /// Get the calculated size of a template + /** + * Retrieves the size of a template. + * + * \param templateId A valid template ID + * \param width The width of the template + * \param height The height of the template + * \see BeginTemplate(), EndTemplate(), UseTemplate(), ImportPage() + * + * Attention: The width and/or height parameters have to be set to a value <= 0 + * prior to calling this method, otherwise they will not be calculated. + * If one dimension, i.e. width, is passed with a value > 0, + * the other one, i.e. height, is calculated accordingly. + */ + void GetTemplateSize(int templateId, double& width, double& height); + + /// Uses a template in current page or in another template + /** + * Uses the specified template just like an image in the current page or + * in another template. + * + * All parameters are optional. The width or height is calculated using + * GetTemplateSize internally. + * By default the size as defined by BeginTemplate is used. + * + * \param templateId A valid template ID + * \param x The x coordinate + * \param y The y coordinate + * \param width The new width of the template + * \param height The new height of the template + * \see BeginTemplate(), EndTemplate(), ImportPage() + * + * Attention: The template may be displayed distorted, if both width and height + * are given with values > 0 and do not correspond to the dimensions of the template. + */ + void UseTemplate(int templateId, double x = -1, double y = -1, double width = 0, double height = 0); + + /// Sets a source file for the external template feature. + /** + * Selects the source for the external template feature. + * A parser is setup for importing pages from the PDF document. + * Although wxPdfDocument usually creates PDF documents conforming to version 1.3 + * of the PDF standard, parsing of documents conforming to versions up to 1.6 is + * supported. If pages are aimported from documents conforming to a higher version + * than 1.3 the version used by wxPdDocument is updated accordingly. + * + * \param filename a valid filename + * \param password a valid user or owner password if the PDF document is encrypted + * \return the number of available pages, or 0 if the document could not be opened + * \see ImportPage(), UseTemplate() + * + * Attention: Access permissions for printing, copying and extracting text or graphics + * are required. If a PDF document does not have these access permissions, it cannot + * be used as a source for the external template feature. + */ + int SetSourceFile(const wxString& filename, const wxString& password = wxEmptyString); + + /// Gets the document information dictionary of the current external PDF document. + /** + * Gets the values of the Info dictionary of the current external document, if available. + * + * \param info the info dictionary object receiving the document information + * \return true if the info dictionary was available, false otherwise + * \see SetSourceFile() + */ + bool GetSourceInfo(wxPdfInfo& info); + + /// Imports a page from an external PDF document + /** + * Imports a page from the current external PDF document. As the bounding box of the + * template the ArtBox of the imported page is used. If the page does not have an + * explicit ArtBox, the CropBox will be used instead; if there is no explicit CropBox + * then the MediaBox will be used. + * + * \param pageno page number of the page to be imported + * \return Index of imported page - for use in UseTemplate(). + * A value of 0 is returned if the page number is out of range or no source was selected. + * \see SetSourceFile() + */ + int ImportPage(int pageno); + + /// Gets the bounding box of a template + /** + * Especially for pages imported from an external PDF document the size of the bounding + * box might be of interest. The values returned correspond to the coordinates of the + * lower left corner and the width and height of the template. + * + * \param templateId A valid template ID + * \param x The x coordinate of the lower left corner + * \param y The y coordinate of the lower left corner + * \param width The width of the template + * \param height The height of the template + * \see SetTemplateBBox(), BeginTemplate(), ImportPage() + */ + void GetTemplateBBox(int templateId, double& x, double& y, double& width, double& height); + + /// Sets the bounding box of a template + /** + * As long as a template hasn't been used it is possible to change the bounding box of + * the template. This may be useful for pages imported from an external PDF document + * allowing to set the visible portion of the template. + * \b Note: Setting the bounding box influences only the visible area of the template, + * not the real size it occupies. + * + * \param templateId A valid template ID + * \param x The x coordinate of the lower left corner + * \param y The y coordinate of the lower left corner + * \param width The width of the template + * \param height The height of the template + * \see GetTemplateBBox(), BeginTemplate(), ImportPage() + */ + void SetTemplateBBox(int templateId, double x, double y, double width, double height); + + /// Prints a text string along a path defined by a shape + /** + * \param shape shape defining a path along which the text is printed + * \param text text string to be printed + * \param mode flag how to handle the text string + * \li wxPDF_SHAPEDTEXTMODE_ONETIME: the text should be printed at most one time depending on the path length + * \li wxPDF_SHAPEDTEXTMODE_STRETCHTOFIT: the text should be stretched to fit exactly along the given path (default) + * \li wxPDF_SHAPEDTEXTMODE_REPEAT: the text should be repeated if the text length is shorter than the path length + */ + void ShapedText(const wxPdfShape& shape, const wxString& text, wxPdfShapedTextMode mode = wxPDF_SHAPEDTEXTMODE_STRETCHTOFIT); + + /// Converts a wxColour to the corresponding PDF specification + /** + * \param color color to be converted to a hexadecimal string representation + * \return the hexadecimal string representation of the color + */ + static wxString RGB2String(const wxColour& color); + + /// Formats a floating point number with a fixed precision + /** + * \param value the value to be formatted + * \param precision the number of decimal places + * \return the string representation of the number + */ + static wxString Double2String(double value, int precision = 0); + + /// Parses a floating point number + /** + * \param str the string to be parsed + * \return the value of floating point number given by the string representation, + * 0 if the string could not be parsed. + */ + static double String2Double(const wxString& str); + + /// Converts an integer number to a roman number + /** + * \param value integer value to be converted + * \return the string representation of the integer value as a roman number + */ + static wxString Convert2Roman(int value); + + /// Forces a floating point number into a fixed range + /** + * \param value value to be forced into range + * \param minValue lower limit + * \param maxValue upper limit + * \return value conforming to the given range: + * \li the minValue if the value falls below the lower limit + * \li the value itself if it is within range + * \li the maxValue if the value exceeds the upper limit + */ + static double ForceRange(double value, double minValue, double maxValue); + + /// Create a unique ID + static wxString GetUniqueId(const wxString& prefix = wxEmptyString); + +protected: + + /// Select font + virtual bool SelectFont(const wxString& family, + const wxString& style = wxEmptyString, + double size = 0, bool setFont = true); + + /// Start document + virtual void BeginPage(int orientation); + + /// End of page contents + virtual void EndPage(); + + /// End dociment + virtual void EndDoc(); + + /// Add header + virtual void PutHeader(); + + /// Add pages. + virtual void PutPages(); + + /// Replace page number aliases + virtual void ReplaceNbPagesAlias(); + + /// Add resources + virtual void PutResources(); + + /// Add bookmarks + virtual void PutBookmarks(); + + /// Add extended graphics states + virtual void PutExtGStates(); + + /// Add shaders + virtual void PutShaders(); + + /// Adds fonts + virtual void PutFonts(); + + /// Add images + virtual void PutImages(); + + /// Add templates + virtual void PutTemplates(); + + /// Add imported objects + virtual void PutImportedObjects(); + + virtual void WriteObjectValue(wxPdfObject* value, bool newline = true); + + /// Add spot colors + virtual void PutSpotColors(); + + /// Add Patterns + virtual void PutPatterns(); + + /// Add OC + virtual void PutOc(); + + /// Add Javascript (document level) + virtual void PutJavaScript(); + + /// Add resource dictionary + virtual void PutResourceDict(); + + /// Add encryption info. + virtual void PutEncryption(); + + /// Add form fields + virtual void PutFormFields(); + + /// Add info. + virtual void PutInfo(); + + /// Addcatalog + virtual void PutCatalog(); + + /// Add object dictionary + virtual void PutXObjectDict(); + + /// Add trailer + virtual void PutTrailer(); + + /// Calculate stream size + int CalculateStreamLength(int len); + + /// Calculate stream offset + int CalculateStreamOffset(); + + /// Get new object id + int GetNewObjId(); + + /// Begin a new object + void NewObj(int objId = 0); + + /// Decorate text + wxString DoDecoration(double x, double y, const wxString& txt); + + /// Format a text string + void TextEscape(const wxString& s, bool newline = true); + + /// Add byte stream + void PutStream(wxMemoryOutputStream& s); + + /// Add a text string to the document + void OutTextstring(const wxString& s, bool newline = true); + + /// Add a raw text string to the document (without charset conversion) + void OutRawTextstring(const wxString& s, bool newline = true); + + /// Add a hex text string to the document (without charset conversion) + void OutHexTextstring(const wxString& s, bool newline = true); + + /// Add an ASCII text string to the document + void OutAsciiTextstring(const wxString& s, bool newline = true); + + /// Add \ before \, ( and ) + void OutEscape(const char* s, int len); + + /// Add ASCII string + void OutAscii(const wxString& s, bool newline = true); + + /// Add character string + void Out(const char* s, bool newline = true); + + /// Add len characters + void Out(const char* s, int len, bool newline = true); + + /// Sets a draw point + void OutPoint(double x, double y); + + /// Sets a draw point relative to current position + void OutPointRelative(double dx, double dy); + + /// Draws a line from last draw point + void OutLine(double x, double y); + + /// Draws a line relative from last draw point + void OutLineRelative(double dx, double dy); + + /// Draws a Bézier curve from last draw point + void OutCurve(double x1, double y1, double x2, double y2, double x3, double y3); + + /// Perform transformation + void Transform(double tm[6]); + + /// Adds a form field to the document + void AddFormField(wxPdfAnnotationWidget* field, bool setFormField = true); + + /// Add an indirect object to the document + void OutIndirectObject(wxPdfIndirectObject* object); + + /// Add an image object to the document + void OutImage(wxPdfImage* currentImage, + double x, double y, double w, double h, const wxPdfLink& link); + + /// Prepare an XML cell for output + void PrepareXmlCell(wxXmlNode* node, wxPdfCellContext& context); + + /// Output a prepared XML cell + void WriteXmlCell(wxXmlNode* node, wxPdfCellContext& context); + + /// Take alignment of an XML cell into account + void DoXmlAlign(wxPdfCellContext& context); + + /// Prepare an XML table for output + void PrepareXmlTable(wxXmlNode* node, wxPdfCellContext& context); + + /// Output a prepared XML table + void WriteXmlTable(wxPdfCellContext& context); + + /// Initialize the core fonts + void InitializeCoreFonts(); + +private: + /// Set the page format + void SetPageFormat(const int orientation, const wxPaperSize format); + + /// Set the page format + void SetPageFormat(const int orientation, const double paperWidth, const double paperHeight); + +private: + int m_page; ///< current page number + int m_n; ///< current object number + int m_firstPageId; ///< object id of the first page + + wxPdfOffsetHashMap* m_offsets; ///< array of object offsets + + wxMemoryOutputStream m_buffer; ///< buffer holding in-memory PDF + wxPdfPageHashMap* m_pages; ///< array containing pages + int m_state; ///< current document state + + bool m_compress; ///< compression flag + int m_defOrientation; ///< default orientation + int m_curOrientation; ///< current orientation + wxPdfBoolHashMap* m_orientationChanges; ///< array indicating orientation changes + + double m_k; ///< scale factor (number of points in user unit) + double m_fwPt; ///< width of page format in points + double m_fhPt; ///< height of page format in points + double m_fw; ///< width of page format in user unit + double m_fh; ///< height of page format in user unit + double m_wPt; ///< current width of page in points + double m_hPt; ///< current height of page in points + double m_w; ///< current width of page in user unit + double m_h; ///< current height of page in user unit + double m_imgscale; ///< image scale factor + + double m_tMargin; ///< top margin + double m_bMargin; ///< page break margin + double m_lMargin; ///< left margin + double m_rMargin; ///< right margin + double m_cMargin; ///< cell margin + + double m_x; ///< current x position in user unit for cell positioning + double m_y; ///< current y position in user unit for cell positioning + double m_angle; ///< current rotation angle + double m_lasth; ///< height of last cell printed + double m_lineWidth; ///< line width in user units + wxPdfLineStyle m_lineStyle; ///< current line style + + int m_inTransform; ///< flag for transformation state + + wxPdfCoreFontMap* m_coreFonts; ///< array of standard font names (and character widths) + wxPdfFontHashMap* m_fonts; ///< array of used fonts + wxPdfDiffHashMap* m_diffs; ///> array of encoding differences + wxPdfImageHashMap* m_images; ///< array of used images + wxPdfPageLinksMap* m_pageLinks; ///< array of links in pages + wxPdfLinkHashMap* m_links; ///< array of internal links + wxPdfNamedLinksMap* m_namedLinks; ///< array of named internal links + + wxPdfExtGStateMap* m_extGStates; ///< array of extended graphics states + wxPdfExtGSLookupMap* m_extGSLookup; ///< array for fast lookup of extended graphics states + int m_currentExtGState; ///< current extended graphics state + + wxPdfGradientMap* m_gradients; ///< array of gradients + wxPdfSpotColourMap* m_spotColors; ///< array of spot colors + wxPdfPatternMap* m_patterns; ///< array of patterns + + wxPdfAnnotationsMap* m_annotations; ///< array of text annotations + + wxArrayPtrVoid m_outlines; ///< array of bookmarks + int m_outlineRoot; ///< number of root node + int m_maxOutlineLevel; ///< max. occuring outline level + + wxString m_fontPath; ///< current default path for font files + wxString m_fontFamily; ///< current font family + wxString m_fontStyle; ///< current font style + int m_decoration; ///< font decoration flags + bool m_fontSubsetting; ///< flag whether to use font subsetting + + wxPdfFont* m_currentFont; ///< current font info + + double m_fontSizePt; ///< current font size in points + double m_fontSize; ///< current font size in user unit + wxPdfColour m_drawColor; ///< commands for drawing color + wxPdfColour m_fillColor; ///< commands for filling color + wxPdfColour m_textColor; ///< commands for text color + bool m_colorFlag; ///< indicates whether fill and text colors are different + double m_ws; ///< word spacing + + bool m_autoPageBreak; ///< automatic page breaking + double m_pageBreakTrigger; ///< threshold used to trigger page breaks + bool m_inFooter; ///< flag set when processing footer + wxPdfZoom m_zoomMode; ///< zoom display mode + double m_zoomFactor; ///< zoom factor + wxPdfLayout m_layoutMode; ///< layout display mode + int m_viewerPrefs; ///< viewer preferences + + wxString m_title; ///< title + wxString m_subject; ///< subject + wxString m_author; ///< author + wxString m_keywords; ///< keywords + wxString m_creator; ///< creator + wxString m_aliasNbPages; ///< alias for total number of pages + wxString m_PDFVersion; ///< PDF version number + + double m_img_rb_x; ///< right-bottom corner X coordinate of inserted image + double m_img_rb_y; ///< right-bottom corner Y coordinate of inserted image + + // Encryption + bool m_encrypted; ///< flag whether document is protected + wxPdfEncrypt* m_encryptor; ///< encryptor instance + int m_encObjId; ///< encrypted object id + + // Javascript + int m_nJS; ///< Javascript object number + wxString m_javascript; ///< Javascript string + + // Forms + int m_zapfdingbats; ///< index of font ZapfDingBats + wxPdfFormFieldsMap* m_formFields; ///< array of form fields + wxPdfFormAnnotsMap* m_formAnnotations; ///< array of form field annotations + wxPdfRadioGroupMap* m_radioGroups; ///< array of radio button groups + wxString m_formBorderColor; ///< form field border color + wxString m_formBackgroundColor; ///< form field background color + wxString m_formTextColor; ///< form field text color + wxString m_formBorderStyle; ///< form field border style + double m_formBorderWidth; ///< form field border width + + // Templates + bool m_inTemplate; ///< flag whether template mode is on + wxPdfTemplatesMap* m_templates; ///< array of templates + wxString m_templatePrefix; ///< prefix used for template object names + int m_templateId; ///< Id of current template + wxPdfTemplate* m_currentTemplate; ///< current template + + wxPdfParserMap* m_parsers; ///< array of parsers + wxPdfParser* m_currentParser; ///< current parser + wxString m_currentSource; ///< current import source file name + wxString m_importVersion; ///< highest PDF version of imported files + + static bool ms_seeded; ///< flag whether random number generator is seeded + static int ms_s1; ///< Random number generator seed 1 + static int ms_s2; ///< Random number generator seed 2 + + wxPdfOc m_pdfOc; ///< Optional content manager + + + friend class wxPdfImage; + friend class wxPdfTable; +}; + +#endif diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfoc.h b/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfoc.h new file mode 100644 index 000000000..395a8cd5a --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfoc.h @@ -0,0 +1,179 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfoc.h +// Purpose: +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfoc.h PDF Optional Content Management Classes + +#ifndef _PDFOC_H_ +#define _PDFOC_H_ + +#include +#include "pdfdocdef.h" + +/// OCG Intent options +#define wxPDF_OCG_INTENT_VIEW 0x0001 +#define wxPDF_OCG_INTENT_DESIGN 0x0002 + + +class wxPdfOcg; // Predefine for hashmap + +/// Hashmap class for document links +WX_DECLARE_HASH_MAP(unsigned int, wxPdfOcg*, wxIntegerHash, wxIntegerEqual, wxPdfOcgHashMap); + + +/// Class representing the optional content in a document. +class WXDLLIMPEXP_PDFDOC wxPdfOc { +public: + /// Constructor + wxPdfOc(); + + /// Destructor + ~wxPdfOc(); + + /// Add an optional content group + /** + * The OCG will be managed by this class after its added, so you dont need to destroy it yourself. + * This means the OCG must have been created dynamically, not statically. + * \param[in] ocg The OCG to add + */ + void AddOcg(wxPdfOcg *ocg); + + /// Get the OCG map + /** + * \return The ocg hash map + */ + wxPdfOcgHashMap& GetOcgMap(void) { return m_ocgs; }; + + +private: + + unsigned int m_nextOcgId; ///< Next Id to use for an Ocg. Incremented after adding new Ocg; + wxPdfOcgHashMap m_ocgs; ///< Hash map of ocgs + + +}; + + +/// Class representing an optional content group +class WXDLLIMPEXP_PDFDOC wxPdfOcg { +public: + /// Construct a new OCG + /** + * \param [in] name The label shown in the view application (does not need to be unique) + */ + wxPdfOcg(const wxString& name); + + /// Destructor + ~wxPdfOcg(); + + /// Set OCG Intent + /** + * \param[in] intent Combination of the defined wxPDF_OCG_INTENT_???? values to set + */ + void SetIntent(const unsigned int intent) { m_intent |= intent; }; + + /// Clear OCG Intent + /** + * \param[in] intent Combination of the defined wxPDF_OCG_INTENT_???? values to clear + */ + void ClearIntent(const unsigned int intent) { m_intent &= ~intent; }; + + /// Get OCG Index + /** + * \return The OCG index + */ + unsigned int GetOcgIndex(void) const { return m_index; }; + + /// Set OCG Index + /** + * \param[in] index The value to set index to + */ + void SetOcgIndex(const unsigned int index) { m_index = index; }; + + /// Get Object Index + /** + * \return The object index + */ + unsigned int GetObjectIndex(void) const { return m_objIndex; }; + + /// Set Object Index + /** + * \param[in] index The value to set index to + */ + void SetObjectIndex(const unsigned int index) { m_objIndex = index; }; + + /// Set the default visibility state + /** + * \param[in] state true = on, false = off + */ + void SetDefaultVisibilityState(const bool state) { m_defaultState = state; }; + + /// Get the default visibility state + /** + * \return The default visibility state, true = on, false = off + */ + bool GetDefaultVisibilityState(void) const { return m_defaultState; }; + + /// Get ocg name + /** + * \return OCG name + */ + const wxString& GetName(void) const {return m_name; }; + + /// Get stringised Intent + /** + * \return String representing intent + */ + wxString& GetIntentString(void); + +private: + unsigned int m_objIndex; ///< Object index + unsigned int m_index; ///< OCG Index + wxString m_name; ///< OCG Name + unsigned int m_intent; ///< OCG Intent (combinations of wxPDF_OCG_INTENT_VIEW | wxPDF_OCG_INTENT_DESIGN) + wxString m_intentStr; ///< OCG Intent string + bool m_defaultState; ///!< true = default on, false = default off +}; + + +/// Class representing an optional content membership dictionary +/*class WXDLLIMPEXP_PDFDOC wxPdfOcmd { +public: + typedef enum VisiblityPolicy { + AllOn = 0, + AnyOn, + AnyOff, + AllOff + } VisibilityPolicy; + +public: + /// Construct a new OCMD + wxPdfOcmd(); + + + /// Destructor + ~wxPdfOcmd(); + + + /// Set visibility policy + void SetVisibilityPolicy(const VisibilityPolicy policy); + + /// Get visibility policy + const VisibilityPolicy GetVisibilityPolicy(void) const { return m_policy; }; + + /// Get visibility policy as PdfName + const wxPdfName& GetNameVisibilityPolicy(void) const { return m_strPolicy; }; + +private: + + wxPdfArray m_ocg; ///< Array of OCGs whose states determine the visibility of content controlled by this OCMD + VisibilityPolicy m_policy; ///< Visiblity policy + wxPdfName m_strPolicy; ///< Stringised visibility policy +}; +*/ + + + +#endif //_PDFOC_H_ diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfproperties.h b/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfproperties.h new file mode 100644 index 000000000..33aa61653 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/include/wx/pdfproperties.h @@ -0,0 +1,882 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfproperties.h +// Purpose: +// Author: Ulrich Telle +// Modified by: +// Created: 2006-07-13 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfproperties.h Interface of the several wxPdfDocument property classes + +#ifndef _PDFPROPERTIES_H_ +#define _PDFPROPERTIES_H_ + +// wxWidgets headers +#include "wx/pdfdocdef.h" + +/// Border options +#define wxPDF_BORDER_NONE 0x0000 +#define wxPDF_BORDER_LEFT 0x0001 +#define wxPDF_BORDER_RIGHT 0x0002 +#define wxPDF_BORDER_TOP 0x0004 +#define wxPDF_BORDER_BOTTOM 0x0008 +#define wxPDF_BORDER_FRAME 0x000F + +/// Corner options +#define wxPDF_CORNER_NONE 0x0000 +#define wxPDF_CORNER_TOP_LEFT 0x0001 +#define wxPDF_CORNER_TOP_RIGHT 0x0002 +#define wxPDF_CORNER_BOTTOM_LEFT 0x0004 +#define wxPDF_CORNER_BOTTOM_RIGHT 0x0008 +#define wxPDF_CORNER_ALL 0x000F + +/// Style options +#define wxPDF_STYLE_NOOP 0x0000 +#define wxPDF_STYLE_DRAW 0x0001 +#define wxPDF_STYLE_FILL 0x0002 +#define wxPDF_STYLE_FILLDRAW 0x0003 +#define wxPDF_STYLE_DRAWCLOSE 0x0004 +#define wxPDF_STYLE_MASK 0x0007 + +/// Font decoration options +#define wxPDF_FONT_NORMAL 0x0000 +#define wxPDF_FONT_UNDERLINE 0x0001 +#define wxPDF_FONT_OVERLINE 0x0002 +#define wxPDF_FONT_STRIKEOUT 0x0004 +#define wxPDF_FONT_DECORATION 0x0007 // Mask all possible decorations + +/// Permission options +#define wxPDF_PERMISSION_NONE 0x0000 ///< Allow nothing +#define wxPDF_PERMISSION_PRINT 0x0004 ///< Allow printing +#define wxPDF_PERMISSION_MODIFY 0x0008 ///< Allow modifying +#define wxPDF_PERMISSION_COPY 0x0010 ///< Allow text copying +#define wxPDF_PERMISSION_ANNOT 0x0020 ///< Allow annotations +#define wxPDF_PERMISSION_ALL 0x003C ///< Allow anything + +/// Encryption methods +enum wxPdfEncryptionMethod +{ + wxPDF_ENCRYPTION_RC4V1, + wxPDF_ENCRYPTION_RC4V2, + wxPDF_ENCRYPTION_AESV2 +}; + +/// Color types +enum wxPdfColourType +{ + wxPDF_COLOURTYPE_UNKNOWN, + wxPDF_COLOURTYPE_GRAY, + wxPDF_COLOURTYPE_RGB, + wxPDF_COLOURTYPE_CMYK, + wxPDF_COLOURTYPE_SPOT +}; + +/// Form field border styles +enum wxPdfBorderStyle +{ + wxPDF_BORDER_SOLID, + wxPDF_BORDER_DASHED, + wxPDF_BORDER_BEVELED, + wxPDF_BORDER_INSET, + wxPDF_BORDER_UNDERLINE +}; + +/// Alignment options +enum wxPdfAlignment +{ + wxPDF_ALIGN_LEFT, + wxPDF_ALIGN_CENTER, + wxPDF_ALIGN_RIGHT, + wxPDF_ALIGN_JUSTIFY, + wxPDF_ALIGN_TOP = wxPDF_ALIGN_LEFT, + wxPDF_ALIGN_MIDDLE = wxPDF_ALIGN_CENTER, + wxPDF_ALIGN_BOTTOM = wxPDF_ALIGN_RIGHT +}; + +/// Zoom options +enum wxPdfZoom +{ + wxPDF_ZOOM_FULLPAGE, + wxPDF_ZOOM_FULLWIDTH, + wxPDF_ZOOM_REAL, + wxPDF_ZOOM_DEFAULT, + wxPDF_ZOOM_FACTOR +}; + +/// Layout options +enum wxPdfLayout +{ + wxPDF_LAYOUT_CONTINUOUS, + wxPDF_LAYOUT_SINGLE, + wxPDF_LAYOUT_TWO, + wxPDF_LAYOUT_DEFAULT +}; + +/// Viewer preferences +#define wxPDF_VIEWER_HIDETOOLBAR 0x0001 +#define wxPDF_VIEWER_HIDEMENUBAR 0x0002 +#define wxPDF_VIEWER_HIDEWINDOWUI 0x0004 +#define wxPDF_VIEWER_FITWINDOW 0x0008 +#define wxPDF_VIEWER_CENTERWINDOW 0x0010 +#define wxPDF_VIEWER_DISPLAYDOCTITLE 0x0020 + +/// Line Cap options +enum wxPdfLineCap +{ + wxPDF_LINECAP_NONE = -1, + wxPDF_LINECAP_BUTT = 0, + wxPDF_LINECAP_ROUND = 1, + wxPDF_LINECAP_SQUARE = 2 +}; + +/// Line join options +enum wxPdfLineJoin +{ + wxPDF_LINEJOIN_NONE = -1, + wxPDF_LINEJOIN_MITER = 0, + wxPDF_LINEJOIN_ROUND = 1, + wxPDF_LINEJOIN_BEVEL = 2 +}; + +/// Marker symbols +enum wxPdfMarker +{ + wxPDF_MARKER_CIRCLE, + wxPDF_MARKER_SQUARE, + wxPDF_MARKER_TRIANGLE_UP, + wxPDF_MARKER_TRIANGLE_DOWN, + wxPDF_MARKER_TRIANGLE_LEFT, + wxPDF_MARKER_TRIANGLE_RIGHT, + wxPDF_MARKER_DIAMOND, + wxPDF_MARKER_PENTAGON_UP, + wxPDF_MARKER_PENTAGON_DOWN, + wxPDF_MARKER_PENTAGON_LEFT, + wxPDF_MARKER_PENTAGON_RIGHT, + wxPDF_MARKER_STAR, + wxPDF_MARKER_STAR4, + wxPDF_MARKER_PLUS, + wxPDF_MARKER_CROSS, + wxPDF_MARKER_SUN, + wxPDF_MARKER_BOWTIE_HORIZONTAL, + wxPDF_MARKER_BOWTIE_VERTICAL, + wxPDF_MARKER_ASTERISK, + wxPDF_MARKER_LAST // Marks the last available marker symbol; do not use! +}; + +/// Linear gradient types +enum wxPdfLinearGradientType +{ + wxPDF_LINEAR_GRADIENT_HORIZONTAL, + wxPDF_LINEAR_GRADIENT_VERTICAL, + wxPDF_LINEAR_GRADIENT_MIDHORIZONTAL, + wxPDF_LINEAR_GRADIENT_MIDVERTICAL, + wxPDF_LINEAR_GRADIENT_REFLECTION_LEFT, + wxPDF_LINEAR_GRADIENT_REFLECTION_RIGHT, + wxPDF_LINEAR_GRADIENT_REFLECTION_TOP, + wxPDF_LINEAR_GRADIENT_REFLECTION_BOTTOM +}; + +enum wxPdfBlendMode +{ + wxPDF_BLENDMODE_NORMAL, + wxPDF_BLENDMODE_MULTIPLY, + wxPDF_BLENDMODE_SCREEN, + wxPDF_BLENDMODE_OVERLAY, + wxPDF_BLENDMODE_DARKEN, + wxPDF_BLENDMODE_LIGHTEN, + wxPDF_BLENDMODE_COLORDODGE, + wxPDF_BLENDMODE_COLORBURN, + wxPDF_BLENDMODE_HARDLIGHT, + wxPDF_BLENDMODE_SOFTLIGHT, + wxPDF_BLENDMODE_DIFFERENCE, + wxPDF_BLENDMODE_EXCLUSION, + wxPDF_BLENDMODE_HUE, + wxPDF_BLENDMODE_SATURATION, + wxPDF_BLENDMODE_COLOR, + wxPDF_BLENDMODE_LUMINOSITY +}; + +enum wxPdfShapedTextMode +{ + wxPDF_SHAPEDTEXTMODE_ONETIME, + wxPDF_SHAPEDTEXTMODE_STRETCHTOFIT, + wxPDF_SHAPEDTEXTMODE_REPEAT +}; + +/// Class representing a PDF document information dictionary. +class WXDLLIMPEXP_PDFDOC wxPdfInfo +{ +public: + wxPdfInfo() {} + virtual~wxPdfInfo() {} + + void SetTitle(const wxString& title) { m_title = title; } + void SetAuthor(const wxString& author) { m_author = author; } + void SetSubject(const wxString& subject) { m_subject = subject; } + void SetKeywords(const wxString& keywords) { m_keywords = keywords; } + void SetCreator(const wxString& creator) { m_creator = creator; } + void SetProducer(const wxString& producer) { m_producer = producer; } + void SetCreationDate(const wxString& creationDate) { m_creationDate = creationDate; } + void SetModDate(const wxString& modDate) { m_modDate = modDate; } + + const wxString GetTitle() const { return m_title; } + const wxString GetAuthor() const { return m_author; } + const wxString GetSubject() const { return m_subject; } + const wxString GetKeywords() const { return m_keywords; } + const wxString GetCreator() const { return m_creator; } + const wxString GetProducer() const { return m_producer; } + const wxString GetCreationDate() const { return m_creationDate; } + const wxString GetModDate() const { return m_modDate; } + +private: + wxString m_title; ///< The document’s title. + wxString m_author; ///< The name of the person who created the document. + wxString m_subject; ///< The subject of the document. + wxString m_keywords; ///< Keywords associated with the document. + wxString m_creator; ///< The name of the application that created the original document. + wxString m_producer; ///< The name of the application that produced the document. + wxString m_creationDate; ///< The date and time the document was created. + wxString m_modDate; ///< The date and time the document was modified. +}; + +/// Class representing internal or external links. +class WXDLLIMPEXP_PDFDOC wxPdfLink +{ +public: + /// Constructor for internal link + /** + * Use this constructor to create an \b internal link reference. + * \see wxPdfDocument::Link(), wxPdfDocument::Write(), wxPdfDocument::Cell(), wxPdfDocument::ClippedCell(), wxPdfDocument::Image(), wxPdfDocument::RotatedImage() + */ + wxPdfLink(int linkRef); + + /// Constructor for external link + /** + * Use this constructor to create an \b external link reference. + * \see wxPdfDocument::Link(), wxPdfDocument::Write(), wxPdfDocument::Cell(), wxPdfDocument::ClippedCell(), wxPdfDocument::Image(), wxPdfDocument::RotatedImage() + */ + wxPdfLink(const wxString& linkURL); + + /// Copy constructor + wxPdfLink(const wxPdfLink& pdfLink); + + /// Destructor + virtual ~wxPdfLink(); + + /// Check whether this instance is a valid link reference + bool IsValid() const { return m_isValid; } + + /// Check whether this instance is an internal reference + bool IsLinkRef() const { return m_isRef; } + + /// Get the internal link reference + int GetLinkRef() const { return m_linkRef; } + + /// Get the external link reference + const wxString GetLinkURL() const { return m_linkURL; } + + /// Set page number and position on page + void SetLink(int page, double ypos) { m_page = page; m_ypos = ypos; } + + /// Get the page this link refers to + int GetPage() { return m_page; } + + /// Get the page position this link refers to + double GetPosition() { return m_ypos; } + +private: + bool m_isValid; ///< Flag whether this instance is valid + bool m_isRef; ///< Flag whether this is an internal link reference + int m_linkRef; ///< Internal link reference + wxString m_linkURL; ///< External link reference + int m_page; ///< Page number this link refers to + double m_ypos; ///< Position on page this link refers to +}; + +/// Class representing the sensitive area of links referring to a page. (For internal use only) +class WXDLLIMPEXP_PDFDOC wxPdfPageLink : public wxPdfLink +{ +public: + /// Constructor + wxPdfPageLink(double x, double y, double w, double h, const wxPdfLink& pdfLink); + + /// Destructor + virtual ~wxPdfPageLink(); + + /// Get the X offset + double GetX() { return m_x; } + + /// Get the Y offset + double GetY() { return m_y; } + + /// Get the width + double GetWidth() { return m_w; } + + /// Get the height + double GetHeight() { return m_h; } + +private: + double m_x; ///< X offset of sensitive area + double m_y; ///< Y offset of sensitive area + double m_w; ///< Width of sensitive area + double m_h; ///< Height of sensitive area +}; + +/// Class representing text annotations. +class WXDLLIMPEXP_PDFDOC wxPdfAnnotation +{ +public: + /// Constructor for text annotation + /** + * Use this constructor to create a text annotation. + * \param x X offset of the annotation + * \param y Y offset of the annotation + * \param text annotation text + */ + wxPdfAnnotation(double x, double y, const wxString& text); + + /// Copy constructor + wxPdfAnnotation(const wxPdfAnnotation& annotation); + + /// Destructor + virtual ~wxPdfAnnotation() {} + + /// Get the X offset of the annotation + double GetX() const { return m_x; } + + /// Get the Y offset of the annotation + double GetY() const { return m_y; } + + /// Get the text of the annotation + wxString GetText() const { return m_text; } + +private: + double m_x; ///< X offset of the annotation + double m_y; ///< Y offset of the annotation + wxString m_text; ///< Annotation text +}; + +/// Class representing bookmarks for defining the document's outline. (For internal use only) +class WXDLLIMPEXP_PDFDOC wxPdfBookmark +{ +public: + /// Constructor + wxPdfBookmark(const wxString& txt, int level, double y, int page); + + /// Destructor + virtual ~wxPdfBookmark(); + + /// Get the bookmark text + wxString GetText() { return m_text; } + + /// Get the associated level + int GetLevel() { return m_level; } + + /// Get the Y offset of the bookmark + double GetY() { return m_y; } + + /// Get the page number of the bookmark + int GetPage() { return m_page; } + + /// Set the parent of the bookmark + void SetParent(int parent) { m_parent = parent; } + + /// Get the parent of the bookmark + int GetParent() { return m_parent; } + + /// Set previous bookmark + void SetPrev(int prev) { m_prev = prev; } + + /// Get previous bookmark + int GetPrev() { return m_prev; } + + /// Set next bookmark + void SetNext(int next) { m_next = next; } + + /// Get next bookmark + int GetNext() { return m_next; } + + /// Set first bookmark + void SetFirst(int first) { m_first = first; } + + /// Get first bookmark + int GetFirst() { return m_first; } + + /// Set last bookmark + void SetLast(int last) { m_last = last; } + + /// Get last bookmark + int GetLast() { return m_last; } + +private: + wxString m_text; ///< Text of bookmark + int m_level; ///< Associated level + double m_y; ///< Y offset + int m_page; ///< Page number + int m_parent; ///< Parent bookmark + int m_prev; ///< Previous bookmark + int m_next; ///< Next bookmark + int m_first; ///< First bookmark + int m_last; ///< Last bookmark +}; + +/// Class representing spot colors. +class WXDLLIMPEXP_PDFDOC wxPdfSpotColour +{ +public: + /// Constructor for spot color + wxPdfSpotColour(int index, double cyan, double magenta, double yellow, double black); + + /// Copy constructor + wxPdfSpotColour(const wxPdfSpotColour& color); + + /// Set object index + void SetObjIndex(int index) { m_objIndex = index; } + + /// Get object index + int GetObjIndex() const { return m_objIndex; } + + /// Get spot color index + int GetIndex() const { return m_index; } + + /// Get cyan level + double GetCyan() const { return m_cyan; } + + /// Get magenta level + double GetMagenta() const { return m_magenta; } + + /// Get yellow level + double GetYellow() const { return m_yellow; } + + /// Get black level + double GetBlack() const { return m_black; } + +private: + int m_objIndex; ///< object index + int m_index; ///< color index + double m_cyan; ///< cyan level + double m_magenta; ///< magenta level + double m_yellow; ///< yellow level + double m_black; ///< black level +}; + + +/// Class representing patterns. +// Implementation in pdfcolor.cpp +class WXDLLIMPEXP_PDFDOC wxPdfPattern +{ +public: + /// Constructor for pattern + /** + * \param index The pattern index + * \param width The image width + * \param height The image height + */ + wxPdfPattern(const int index, const double width, const double height); + + /// Copy constructor + wxPdfPattern(const wxPdfPattern& pattern); + + /// Set object index + void SetObjIndex(int index) { m_objIndex = index; }; + + /// Get object index + int GetObjIndex() const { return m_objIndex; }; + + /// Get pattern index + int GetIndex() const { return m_index; }; + + /// Set image + void SetImage(wxPdfImage *image) { m_image = image; }; + + /// Get image + wxPdfImage *GetImage() const {return m_image; }; + + /// Get image width + int GetImageWidth() const {return m_imageWidth; }; + + /// Get image height + int GetImageHeight() const {return m_imageHeight; }; + +private: + int m_objIndex; ///< object index + int m_index; ///< pattern index + wxPdfImage *m_image; ///< image + double m_imageWidth; ///< image width + double m_imageHeight;///< image height +}; + + +/// Class representing wxPdfDocument colors. +class WXDLLIMPEXP_PDFDOC wxPdfColour +{ +public: + /// Default constructor + /** + * Constructs a color object with an undefined color + */ + wxPdfColour(); + + /// Constructor for grayscale color + /** + * Defines a grayscale color + * \param grayscale indicates the gray level. Value between 0 and 255 + */ + wxPdfColour(const unsigned char grayscale); + + /// Constructor for wxColour color + /** + * Defines a wxColour color. + * \param color defines a wxColour color composed of a red, green and blue component + */ + wxPdfColour(const wxColour& color); + + /// Constructor for RGB color + /** + * Defines a RGB color. + * \param red indicates the red level. Value between 0 and 255 + * \param green indicates the green level. Value between 0 and 255 + * \param blue indicates the blue level. Value between 0 and 255 + */ + wxPdfColour(const unsigned char red, const unsigned char green, const unsigned char blue); + + /// Constructor for CMYK color + /** + * Defines a CMYK color. + * \param cyan indicates the cyan level. Value between 0 and 100 + * \param magenta indicates the magenta level. Value between 0 and 100 + * \param yellow indicates the yellow level. Value between 0 and 100 + * \param black indicates the black level. Value between 0 and 100 + */ + wxPdfColour(double cyan, double magenta, double yellow, double black); + + /// Constructor for named RGB color + /** + * Defines a named RGB color. + * \param name is the name of the requested color. Use of HTML notation \#rrggbb as color name is also supported. + */ + wxPdfColour(const wxString& name); + + /// Constructor for named RGB color + /** + * Defines a spot color. + * \param spotColor is the spot color to be used + * \param tint indicates the tint level. Value between 0 and 100. Default: 100. + */ + wxPdfColour(const wxPdfSpotColour& spotColor, double tint); + + /// Copy constructor + wxPdfColour(const wxPdfColour& color); + + /// Assignment operator + wxPdfColour& operator=(const wxPdfColour& color); + + /// Set grayscale color + /** + * \param grayscale indicates the gray level. Value between 0 and 255. Default: 0 (Black). + */ + void SetColor(const unsigned char grayscale = 0); + + /// Set wxColour color + /** + * \param color defines a wxColour color composed of a red, green and blue component + */ + void SetColor(const wxColour& color); + + /// Set RGB color + /** + * \param red indicates the red level. Value between 0 and 255 + * \param green indicates the green level. Value between 0 and 255 + * \param blue indicates the blue level. Value between 0 and 255 + */ + void SetColor(const unsigned char red, const unsigned char green, const unsigned char blue); + + /// Set CMYK color + /** + * \param cyan indicates the cyan level. Value between 0 and 100 + * \param magenta indicates the magenta level. Value between 0 and 100 + * \param yellow indicates the yellow level. Value between 0 and 100 + * \param black indicates the black level. Value between 0 and 100 + */ + void SetColor(double cyan, double magenta, double yellow, double black); + + /// Set a named RGB color + /** + * \param name is the name of the requested color + */ + void SetColor(const wxString& name); + + /// Set a spot color (internal use only) + /** + * \param spotColor is the spot color to be used + * \param tint indicates the tint level. Value between 0 and 100. Default: 100. + */ + void SetColor(const wxPdfSpotColour& spotColor, double tint); + + + /// Get internal color string representation (for internal use only) + /** + * \param drawing flag specifying whether the color is used for drawing operations + */ + const wxString GetColor(bool drawing) const; + + /// Get color type + /** + */ + wxPdfColourType GetColorType() const { return m_type; } + + /// Get internal color value string representation (for internal use only) + /** + */ + const wxString GetColorValue() const; + + /// Compare color + bool Equals(const wxPdfColour& color) const; + +protected: + /// Constructor for internal color string representation + wxPdfColour(const wxString& color, bool WXUNUSED(internal)); + + /// Get a color database + static wxColourDatabase* GetColorDatabase(); + +private: + wxPdfColourType m_type; ///< color type + wxString m_prefix; ///< internal color string prefix + wxString m_color; ///< internal color string + + static wxColourDatabase* ms_colorDatabase; +}; + +bool operator==(const wxPdfColour& a, const wxPdfColour& b); + +bool operator!=(const wxPdfColour& a, const wxPdfColour& b); + +/// Class representing double arrays (no standard class in wxWidgets unfortunately) +WX_DEFINE_USER_EXPORTED_ARRAY_DOUBLE(double, wxPdfArrayDouble, class WXDLLIMPEXP_PDFDOC); + +/// Class representing line styles. +class WXDLLIMPEXP_PDFDOC wxPdfLineStyle +{ +public: + /// Constructor + /** + * Creates a line style for use in graphics primitives. + * \param[in] width Width of the line in user units. + * \param[in] cap Type of cap to put on the line (butt, round, square). + * The difference between 'square' and 'butt' is that 'square' + * projects a flat end past the end of the line. + * \param[in] join form of line joining: miter, round or bevel + * \param[in] dash pattern for dashed lines.Is an empty array (without dash) or + * array with series of length values, which are the lengths of the on and off dashes. + * For example: (2) represents 2 on, 2 off, 2 on , 2 off ... + * (2,1) is 2 on, 1 off, 2 on, 1 off.. etc + * \param[in] phase Modifier of the dash pattern which is used to shift the point at which the pattern starts + * \param[in] color line color. + * \see SetLineStyle(), Curve(), Line(), Circle(), Ellipse(), Rect(), RoundedRect(), Polygon(), RegularPolygon(), StarPolygon() + */ + wxPdfLineStyle(double width = -1, + wxPdfLineCap cap = wxPDF_LINECAP_NONE, wxPdfLineJoin join = wxPDF_LINEJOIN_NONE, + const wxPdfArrayDouble& dash = wxPdfArrayDouble(), double phase = -1, + const wxPdfColour& color = wxPdfColour()); + + /// Copy constructor + wxPdfLineStyle(const wxPdfLineStyle& lineStyle); + + /// Assignment operator + wxPdfLineStyle& operator= (const wxPdfLineStyle& lineStyle); + + /// Destructor + virtual ~wxPdfLineStyle(); + + /// Check whether the style is initialized. + bool IsSet() const { return m_isSet; } + + /// Set the line width + void SetWidth(double width) { m_width = width; } + + /// Get the line width + double GetWidth() const { return m_width; } + + /// Set the line ending style + void SetLineCap(const wxPdfLineCap cap) { m_cap = cap; } + + /// Get the line ending style + wxPdfLineCap GetLineCap() const { return m_cap; } + + /// Set the line join style + void SetLineJoin(const wxPdfLineJoin join) { m_join = join; } + + /// Get the line join style + wxPdfLineJoin GetLineJoin() const { return m_join; } + + /// Set the dash pattern + void SetDash(const wxPdfArrayDouble& dash) { m_dash = dash; } + + /// Get the dash pattern + const wxPdfArrayDouble& GetDash() const { return m_dash; } + + /// Set the dash pattern phase + void SetPhase(double phase) { m_phase = phase; } + + /// Get the dash pattern phase + double GetPhase() const { return m_phase; } + + /// Set the line color + void SetColour(const wxPdfColour& color) { m_color = color; }; + + /// Get the line color + const wxPdfColour& GetColour() const { return m_color; }; + + /// Set the miter limit (0 for none) + void SetMiterLimit(const double miterLimit) { m_miterLimit = miterLimit; }; + + /// Get the miter limit + double GetMiterLimit() const { return m_miterLimit; }; + + +private: + bool m_isSet; ///< Flag whether the style is initialized + double m_width; ///< Line width + wxPdfLineCap m_cap; ///< Line ending style + wxPdfLineJoin m_join; ///< Line joining style + wxPdfArrayDouble m_dash; ///< Dash pattern + double m_phase; ///< Dash pattern phase + wxPdfColour m_color; ///< Line color + double m_miterLimit; ///< Miter limit (0 for none) +}; + +/// Class representing a coons patch mesh. + +class WXDLLIMPEXP_PDFDOC wxPdfCoonsPatchMesh +{ +public: + /// Constructor + wxPdfCoonsPatchMesh(); + + /// Destructor + virtual ~wxPdfCoonsPatchMesh(); + + /// Add patch to mesh + /** + * \param edgeFlag flag indicating the patch position relative to previous patches + * \li 0 - new patch, unrelated to previous patches (the first patch added must have this flag) + * \li 1 - above previous patch + * \li 2 - right to previous patch + * \li 3 - below previous patch + * \param colors array of colors of this patch (size: 4 if edge flag is 1, 2 otherwise) + * \param x array of x coordinates of patch mesh points (size: 12 if edge flag is 1, 8 otherwise) + * \param y array of y coordinates of patch mesh points (size: 12 if edge flag is 1, 8 otherwise) + * \returns true if the added patch is valid + */ + bool AddPatch(int edgeFlag, wxPdfColour colors[], double x[], double y[]); + + /// Checks whether the coons patch mesh is valid + bool Ok() const { return m_ok; } + + /// Get color type of the coons patch mesh + /** + * \returns the color type of the coons patch mesh (gray scale, RGB or CMYK) + */ + wxPdfColourType GetColorType() const { return m_colorType; } + + /// Get the number of patches + /** + * \returns the number of patches of the coons patch mesh + */ + size_t GetPatchCount() const { return m_patches.size(); } + + /// Get the array of patches + /** + * + * \returns array of patches + */ + const wxArrayPtrVoid* GetPatches() const { return &m_patches; } + +private: + bool m_ok; ///< flag whether the coons patch mesh is valid + wxPdfColourType m_colorType; ///< color type of the mesh + wxArrayPtrVoid m_patches; ///< array of patches +}; + +/// Shape segment types +enum wxPdfSegmentType +{ + wxPDF_SEG_UNDEFINED, + wxPDF_SEG_MOVETO, + wxPDF_SEG_LINETO, + wxPDF_SEG_CURVETO, + wxPDF_SEG_CLOSE +}; + +/// Class representing a shape consisting of line and curve segments +class WXDLLIMPEXP_PDFDOC wxPdfShape +{ +public: + /// Constructor + wxPdfShape(); + + /// Destructor + virtual ~wxPdfShape(); + + /// Begin a new subpath of the shape + /** + * Move to the starting point of a new (sub)path. + * The new current point is (x, y). + * \param x abscissa value + * \param y ordinate value + * \remark This must be the first operation in constructing the shape. + */ + void MoveTo(double x, double y); + + /// Add line segment to the shape + /** + * Append a straight line segment from the current point to the point (x, y). + * The new current point is (x, y). + * \param x abscissa value + * \param y ordinate value + */ + void LineTo(double x, double y); + + /// Add a cubic Bezier curve to the shape + /** + * Append a cubic Bezier curve to the current path. The curve extends + * from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) + * as the Bezier control points. The new current point is (x3, y3). + * \param x1: Abscissa of control point 1 + * \param y1: Ordinate of control point 1 + * \param x2: Abscissa of control point 2 + * \param y2: Ordinate of control point 2 + * \param x3: Abscissa of end point + * \param y3: Ordinate of end point + */ + void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3); + + /// Close (sub)path of the shape + void ClosePath(); + + /// Get the number of segments of the shape + size_t GetSegmentCount() const { return m_types.GetCount(); } + + /// Get a specific segment of the shape (for internal use only) + /** + * \param[in] iterType index of segment in segment type array + * \param[in] iterPoints index of segment in segment coordinate array + * \param[out] coords array of segment coordinates (size: >= 8) + * \returns the type of the segment + */ + wxPdfSegmentType GetSegment(int iterType, int iterPoints, double coords[]) const; + +private: + wxArrayInt m_types; ///< array of segment types + wxPdfArrayDouble m_x; ///< array of abscissa values + wxPdfArrayDouble m_y; ///< array of ordinate values + int m_subpath; ///< subpath index + int m_segment; ///< segment index + int m_index; ///< points index +}; + +#endif + diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfcolor.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfcolor.cpp new file mode 100644 index 000000000..80c368dfd --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfcolor.cpp @@ -0,0 +1,574 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfcolor.cpp +// Purpose: Implementation of wxPdfDocument color handling +// Author: Ulrich Telle +// Modified by: +// Created: 2006-01-27 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfcolor.cpp Implementation of the wxPdfDocument color handling + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#include "wx/pdfdoc.h" + +#include "pdfcolordata.inc" + +wxColourDatabase* wxPdfColour::ms_colorDatabase = NULL; + +wxColourDatabase* +wxPdfColour::GetColorDatabase() +{ + if (ms_colorDatabase == NULL) + { + if (wxTheColourDatabase != NULL) + { + ms_colorDatabase = wxTheColourDatabase; + } + else + { + static wxColourDatabase pdfColorDatabase; + ms_colorDatabase = &pdfColorDatabase; + } + size_t n; + for ( n = 0; n < WXSIZEOF(wxColourTable); n++ ) + { + const wxColourDesc& cc = wxColourTable[n]; + ms_colorDatabase->AddColour(cc.name, wxColour(cc.r, cc.g, cc.b)); + } + } + return ms_colorDatabase; +} + +wxPdfColour::wxPdfColour() +{ + m_type = wxPDF_COLOURTYPE_UNKNOWN; + m_prefix = wxEmptyString; + m_color = _T("0"); +} + +wxPdfColour::wxPdfColour(const unsigned char grayscale) +{ + SetColor(grayscale); +} + +wxPdfColour::wxPdfColour(const wxColour& color) +{ + SetColor(color); +} + +wxPdfColour::wxPdfColour(const unsigned char red, const unsigned char green, const unsigned char blue) +{ + SetColor(red, green, blue); +} + +wxPdfColour::wxPdfColour(double cyan, double magenta, double yellow, double black) +{ + SetColor(cyan, magenta, yellow, black); +} + +wxPdfColour::wxPdfColour(const wxPdfSpotColour& spot, double tint) +{ + SetColor(spot, tint); +} + +wxPdfColour::wxPdfColour(const wxString& name) +{ + SetColor(name); +} + +wxPdfColour::wxPdfColour(const wxPdfColour& color) +{ + m_type = color.m_type; + m_prefix = color.m_prefix; + m_color = color.m_color; +} + +wxPdfColour::wxPdfColour(const wxString& color, bool WXUNUSED(internal)) +{ + m_color = color; +} + +wxPdfColour& +wxPdfColour::operator=(const wxPdfColour& color) +{ + m_type = color.m_type; + m_prefix = color.m_prefix; + m_color = color.m_color; + return *this; +} + +bool +wxPdfColour::Equals(const wxPdfColour& color) const +{ + return (m_type == color.m_type) && (m_prefix == color.m_prefix) && (m_color == color.m_color); +} + +void +wxPdfColour::SetColor(const unsigned char grayscale) +{ + m_type = wxPDF_COLOURTYPE_GRAY; + m_prefix = wxEmptyString; + m_color = wxPdfDocument::Double2String(((double) grayscale)/255.,3); +} + +void +wxPdfColour::SetColor(const wxColour& color) +{ + m_type = wxPDF_COLOURTYPE_RGB; + m_prefix = wxEmptyString; + m_color = wxPdfDocument::RGB2String(color); +} + +void +wxPdfColour::SetColor(const unsigned char red, const unsigned char green, const unsigned char blue) +{ + SetColor(wxColour(red,green,blue)); +} + +void +wxPdfColour::SetColor(double cyan, double magenta, double yellow, double black) +{ + m_type = wxPDF_COLOURTYPE_CMYK; + m_prefix = wxEmptyString; + m_color = wxPdfDocument::Double2String(wxPdfDocument::ForceRange(cyan, 0., 100.)/100.,3) + _T(" ") + + wxPdfDocument::Double2String(wxPdfDocument::ForceRange(magenta, 0., 100.)/100.,3) + _T(" ") + + wxPdfDocument::Double2String(wxPdfDocument::ForceRange(yellow, 0., 100.)/100.,3) + _T(" ") + + wxPdfDocument::Double2String(wxPdfDocument::ForceRange(black, 0., 100.)/100.,3); +} + +void +wxPdfColour::SetColor(const wxString& name) +{ + if (name.Length() == 7 && name[0] == wxT('#')) + { + unsigned long r = 0, g = 0, b = 0; + if (name.Mid(1,2).ToULong(&r,16) && + name.Mid(3,2).ToULong(&g,16) && + name.Mid(5,2).ToULong(&b,16)) + { + SetColor((unsigned char) r, (unsigned char) g, (unsigned char) b); + } + else + { + SetColor(0); + } + } + else + { + wxColourDatabase* colorDatabase = GetColorDatabase(); + wxColour color = colorDatabase->Find(name); + if (color.Ok()) + { + SetColor(color); + } + else + { + SetColor(0); + } + } +} + +void +wxPdfColour::SetColor(const wxPdfSpotColour& spot, double tint) +{ + m_type = wxPDF_COLOURTYPE_SPOT; + m_prefix = wxString::Format(_T("/CS%d CS "), spot.GetIndex()); + m_color = wxPdfDocument::Double2String(wxPdfDocument::ForceRange(tint, 0., 100.)/100.,3); +} + +const wxString +wxPdfColour::GetColor(bool drawing) const +{ + wxString color = wxEmptyString; + switch (m_type) + { + case wxPDF_COLOURTYPE_GRAY: + color = m_color + wxString(_T(" g")); + break; + case wxPDF_COLOURTYPE_RGB: + color = m_color + wxString(_T(" rg")); + break; + case wxPDF_COLOURTYPE_CMYK: + color = m_color + wxString(_T(" k")); + break; + case wxPDF_COLOURTYPE_SPOT: + color = m_prefix + m_color + wxString(_T(" scn")); + break; + default: + color = wxString(_T("0 g")); + break; + } + if (drawing) + color.MakeUpper(); + else + color.MakeLower(); + color.Replace(_T("/cs"), _T("/CS")); + return color; +} + +const wxString +wxPdfColour::GetColorValue() const +{ + return m_color; +} + +wxPdfSpotColour::wxPdfSpotColour(int index, double cyan, double magenta, double yellow, double black) + : m_objIndex(0), m_index(index), m_cyan(cyan), m_magenta(magenta), m_yellow(yellow), m_black(black) +{ +} + +wxPdfSpotColour::wxPdfSpotColour(const wxPdfSpotColour& color) +{ + m_objIndex = color.m_objIndex; + m_index = color.m_index; + m_cyan = color.m_cyan; + m_magenta = color.m_magenta; + m_yellow = color.m_yellow; + m_black = color.m_black; +} + +wxPdfPattern::wxPdfPattern(const int index, const double width, const double height) + : m_objIndex(0), m_index(index), m_imageWidth(width), m_imageHeight(height) +{ +} + +wxPdfPattern::wxPdfPattern(const wxPdfPattern& pattern) +{ + m_objIndex = pattern.m_objIndex; + m_index = pattern.m_index; + m_imageWidth = pattern.m_imageWidth; + m_imageHeight = pattern.m_imageHeight; + m_image = pattern.m_image; +} + +// --- + +void +wxPdfDocument::AddPattern(const wxString& name, const wxImage& image, const wxString& imageName, const double width, const double height) +{ + wxPdfPatternMap::iterator pattern = (*m_patterns).find(name); + wxPdfPattern *newPattern; + if (pattern == (*m_patterns).end()) + { + int i = (*m_patterns).size() + 1; + newPattern = new wxPdfPattern(i, width, height); + (*m_patterns)[name] = newPattern; + + + //store the image + wxImage tempImage = image.Copy(); + wxPdfImage* currentImage = NULL; + wxPdfImageHashMap::iterator imageIter = (*m_images).find(imageName); + int maskImage; + if (imageIter == (*m_images).end()) + { + if (tempImage.HasAlpha()) + { + maskImage = ImageMask(imageName+wxString(_T(".mask")), tempImage); + tempImage.ConvertAlphaToMask(0); + + } + // First use of image, get info + tempImage.SetMask(false); + int i = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, i, imageName, tempImage); + currentImage->Parse(); + currentImage->SetMaskImage(maskImage); + (*m_images)[imageName] = currentImage; + } + + imageIter = (*m_images).find(imageName); + newPattern->SetImage( (*imageIter).second ); + + } +} + + +void +wxPdfDocument::AddSpotColor(const wxString& name, double cyan, double magenta, double yellow, double black) +{ + wxPdfSpotColourMap::iterator spotColor = (*m_spotColors).find(name); + if (spotColor == (*m_spotColors).end()) + { + int i = (*m_spotColors).size() + 1; + (*m_spotColors)[name] = new wxPdfSpotColour(i, cyan, magenta, yellow, black); + } +} + +void +wxPdfDocument::SetDrawColorSpace(const int space) +{ + switch(space) { + case 1: + OutAscii(_T("/Pattern CS")); + break; + case 0: + default: + OutAscii(_T("/DeviceRGB CS")); + break; + } +} + +void +wxPdfDocument::SetFillColorSpace(const int space) +{ + switch(space) { + case 1: + OutAscii(_T("/Pattern cs")); + break; + case 0: + default: + OutAscii(_T("/DeviceRGB cs")); + break; + } +} + +void +wxPdfDocument::SetDrawColor(const wxColour& color) +{ + wxPdfColour tempColor(color); + m_drawColor = tempColor; + if (m_page > 0) + { + OutAscii(m_drawColor.GetColor(true)); + } +} + +void +wxPdfDocument::SetDrawColor(const unsigned char grayscale) +{ + wxPdfColour tempColor(grayscale); + m_drawColor = tempColor; + if (m_page > 0) + { + OutAscii(m_drawColor.GetColor(true)); + } +} + +void +wxPdfDocument::SetDrawColor(const unsigned char red, const unsigned char green, const unsigned char blue) +{ + SetDrawColor(wxColour(red, green, blue)); +} + +void +wxPdfDocument::SetDrawColor(double cyan, double magenta, double yellow, double black) +{ + SetDrawColor(wxPdfColour(cyan, magenta, yellow, black)); +} + +void +wxPdfDocument::SetDrawColor(const wxPdfColour& color) +{ + m_drawColor = color; + if (m_page > 0) + { + OutAscii(m_drawColor.GetColor(true)); + } +} + +void +wxPdfDocument::SetDrawColor(const wxString& name, double tint) +{ + wxPdfSpotColourMap::iterator spotColor = (*m_spotColors).find(name); + if (spotColor != (*m_spotColors).end()) + { + wxPdfColour tempColor(*(spotColor->second), tint); + m_drawColor = tempColor; + if (m_page > 0) + { + OutAscii(m_drawColor.GetColor(true)); + } + } + else + { + wxLogError(_("SetDrawColor: Undefined spot color: ") + name); + } +} + +void +wxPdfDocument::SetDrawPattern(const wxString& name) { + wxPdfPatternMap::iterator patternIter = (*m_patterns).find(name); + wxPdfPattern *pattern; + if( patternIter != (*m_patterns).end()) + { + pattern = patternIter->second; + OutAscii(wxString::Format(_T("/P%d SCN"), pattern->GetIndex())); + } + else { + wxLogError(_("SetFillPattern: Undefined pattern: ") + name); + } +} + +const wxPdfColour +wxPdfDocument::GetDrawColor() +{ + return wxPdfColour(m_drawColor); +} + +void +wxPdfDocument::SetFillColor(const wxColour& color) +{ + wxPdfColour tempColor(color); + m_fillColor = tempColor; + m_colorFlag = (m_fillColor != m_textColor); + if (m_page > 0) + { + OutAscii(m_fillColor.GetColor(false)); + } +} + +void +wxPdfDocument::SetFillColor(const unsigned char grayscale) +{ + wxPdfColour tempColor(grayscale); + m_fillColor = tempColor; + m_colorFlag = (m_fillColor != m_textColor); + if (m_page > 0) + { + OutAscii(m_fillColor.GetColor(false)); + } +} + +void +wxPdfDocument::SetFillColor(const wxPdfColour& color) +{ + m_fillColor = color; + m_colorFlag = (m_fillColor != m_textColor); + if (m_page > 0) + { + OutAscii(m_fillColor.GetColor(false)); + } +} + +void +wxPdfDocument::SetFillColor(const unsigned char red, const unsigned char green, const unsigned char blue) +{ + SetFillColor(wxColour(red, green, blue)); +} + +void +wxPdfDocument::SetFillColor(double cyan, double magenta, double yellow, double black) +{ + SetFillColor(wxPdfColour(cyan, magenta, yellow, black)); +} + +void +wxPdfDocument::SetFillColor(const wxString& name, double tint) +{ + wxPdfSpotColourMap::iterator spotColor = (*m_spotColors).find(name); + if (spotColor != (*m_spotColors).end()) + { + wxPdfColour tempColor(*(spotColor->second), tint); + m_fillColor = tempColor; + m_colorFlag = (m_fillColor != m_textColor); + if (m_page > 0) + { + OutAscii(m_fillColor.GetColor(false)); + } + } + else + { + wxLogError(_("SetFillColor: Undefined spot color: ") + name); + } +} + +void +wxPdfDocument::SetFillPattern(const wxString& name) { + wxPdfPatternMap::iterator patternIter = (*m_patterns).find(name); + wxPdfPattern *pattern; + if( patternIter != (*m_patterns).end()) + { + pattern = patternIter->second; + OutAscii(wxString::Format(_T("/P%d scn"), pattern->GetIndex())); + } + else { + wxLogError(_("SetFillPattern: Undefined pattern: ") + name); + } +} + + +const wxPdfColour +wxPdfDocument::GetFillColor() +{ + return wxPdfColour(m_fillColor); +} + +void +wxPdfDocument::SetTextColor(const wxColour& color) +{ + wxPdfColour tempColor(color); + m_textColor = tempColor; + m_colorFlag = (m_fillColor != m_textColor); +} + +void +wxPdfDocument::SetTextColor(const unsigned char grayscale) +{ + wxPdfColour tempColor(grayscale); + m_textColor = tempColor; + m_colorFlag = (m_fillColor != m_textColor); +} + +void +wxPdfDocument::SetTextColor(const wxPdfColour& color) +{ + m_textColor = color; + m_colorFlag = (m_fillColor != m_textColor); +} + +void +wxPdfDocument::SetTextColor(const unsigned char red, const unsigned char green, const unsigned char blue) +{ + SetTextColor(wxColour(red, green, blue)); +} + +void +wxPdfDocument::SetTextColor(double cyan, double magenta, double yellow, double black) +{ + SetTextColor(wxPdfColour(cyan, magenta, yellow, black)); +} + +void +wxPdfDocument::SetTextColor(const wxString& name, double tint) +{ + wxPdfSpotColourMap::iterator spotColor = (*m_spotColors).find(name); + if (spotColor != (*m_spotColors).end()) + { + wxPdfColour tempColor(*(spotColor->second), tint); + m_textColor = tempColor; + m_colorFlag = (m_fillColor != m_textColor); + } + else + { + wxLogError(_("SetTextColor: Undefined spot color: ") + name); + } +} + +const wxPdfColour +wxPdfDocument::GetTextColor() +{ + return wxPdfColour(m_textColor); +} + +bool operator==(const wxPdfColour& a, const wxPdfColour& b) +{ + return a.Equals(b); +} + +bool operator!=(const wxPdfColour& a, const wxPdfColour& b) +{ + return !a.Equals(b); +} diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfdoc.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfdoc.cpp new file mode 100644 index 000000000..75079a705 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfdoc.cpp @@ -0,0 +1,2306 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfdoc.cpp +// Purpose: Implementation of wxPdfDocument (public methods) +// Author: Ulrich Telle +// Modified by: +// Created: 2005-08-04 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfdoc.cpp Implementation of the wxPdfDoc class + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#include +#include + +#include "wx/pdfdoc.h" +#include "wx/pdfform.h" +#include "wx/pdfgraphics.h" +#include "wx/pdftemplate.h" + +// ---------------------------------------------------------------------------- +// wxPdfDocument: class representing a PDF document +// ---------------------------------------------------------------------------- + +wxPdfDocument::wxPdfDocument(int orientation, const wxString& unit, wxPaperSize format) +{ + double w, h; + double unitscale; + + // Scale factor + if (unit == _T("pt")) + { + unitscale = 1.; + } + else if (unit == _T("in")) + { + unitscale = 72.; + } + else if (unit == _T("cm")) + { + unitscale = 72. / 2.54; + } + else // if (unit == "mm") or unknown + { + unitscale = 72. / 25.4; + } + + + // get the page width/height and pass to other constructor + + wxPrintPaperDatabase* printPaperDatabase = new wxPrintPaperDatabase; + printPaperDatabase->CreateDatabase(); + wxPrintPaperType* paperType = printPaperDatabase->FindPaperType(format); + if (paperType == NULL) + { + paperType = printPaperDatabase->FindPaperType(wxPAPER_A4); + } + wxSize paperSize = paperType->GetSize(); + //get the width and height in user units. papersize is in tenths of a mm, convert to inches, then points, then to user units + w = (paperSize.GetWidth() / 254.0) * 72.0 * unitscale; + h = (paperSize.GetHeight() / 254.0) * 72.0 * unitscale; + delete printPaperDatabase; + + wxPdfDocument(w, h, orientation, unit); +} + +wxPdfDocument::wxPdfDocument(const double paperWidth, const double paperHeight, int orientation, const wxString& unit) { + // Allocate arrays + m_currentFont = NULL; + + m_page = 0; + m_n = 2; + m_offsets = new wxPdfOffsetHashMap(); + + m_pages = new wxPdfPageHashMap(); + m_orientationChanges = new wxPdfBoolHashMap(); + + m_state = 0; + + InitializeCoreFonts(); + m_fonts = new wxPdfFontHashMap(); + m_images = new wxPdfImageHashMap(); + m_pageLinks = new wxPdfPageLinksMap(); + m_links = new wxPdfLinkHashMap(); + m_namedLinks = new wxPdfNamedLinksMap(); + m_diffs = new wxPdfDiffHashMap(); + m_extGStates = new wxPdfExtGStateMap(); + m_extGSLookup = new wxPdfExtGSLookupMap(); + m_currentExtGState = 0; + m_gradients = new wxPdfGradientMap(); + m_annotations = new wxPdfAnnotationsMap(); + m_formAnnotations = new wxPdfFormAnnotsMap(); + m_formFields = new wxPdfFormFieldsMap(); + m_radioGroups = new wxPdfRadioGroupMap(); + m_templates = new wxPdfTemplatesMap(); + m_parsers = new wxPdfParserMap(); + m_spotColors = new wxPdfSpotColourMap(); + m_patterns = new wxPdfPatternMap(); + + m_outlineRoot = -1; + m_maxOutlineLevel = 0; + + m_inFooter = false; + m_lasth = 0; + m_fontFamily = _T(""); + m_fontStyle = _T(""); + m_fontSizePt = 12; + m_decoration = wxPDF_FONT_NORMAL; + m_fontSubsetting = true; + + m_drawColor = wxPdfColour(); + m_fillColor = wxPdfColour(); + m_textColor = wxPdfColour(); + m_colorFlag = false; + m_ws = 0; + + // Scale factor + if (unit == _T("pt")) + { + m_k = 1.; + } + else if (unit == _T("in")) + { + m_k = 72.; + } + else if (unit == _T("cm")) + { + m_k = 72. / 2.54; + } + else // if (unit == "mm") or unknown + { + m_k = 72. / 25.4; + } + + // Initialize image scale factor + m_imgscale = 1.; + + // Page format + m_fw = paperWidth; + m_fh = paperHeight; + m_fwPt = m_fw * m_k; + m_fhPt = m_fh * m_k; + + + /*wxPrintPaperDatabase* printPaperDatabase = new wxPrintPaperDatabase; + printPaperDatabase->CreateDatabase(); + wxPrintPaperType* paperType = printPaperDatabase->FindPaperType(format); + if (paperType == NULL) + { + paperType = printPaperDatabase->FindPaperType(wxPAPER_A4); + } + wxSize paperSize = paperType->GetSize(); + m_fwPt = paperSize.GetWidth() / 254. * 72.; + m_fhPt = paperSize.GetHeight() / 254. * 72.; + delete printPaperDatabase; + + m_fw = m_fwPt / m_k; + m_fh = m_fhPt / m_k; +*/ + + // Page orientation + if (orientation == wxLANDSCAPE) + { + m_defOrientation = wxLANDSCAPE; + m_wPt = m_fhPt; + m_hPt = m_fwPt; + } + else // orientation == wxPORTRAIT or unknown + { + m_defOrientation = wxPORTRAIT; + m_wPt = m_fwPt; + m_hPt = m_fhPt; + } + + m_curOrientation = m_defOrientation; + m_w = m_wPt / m_k; + m_h = m_hPt / m_k; + m_angle = 0; + m_inTransform = 0; + + // Page margins (1 cm) + double margin = 28.35 / m_k; + SetMargins(margin, margin); + + // Interior cell margin (1 mm) + m_cMargin = margin / 10; + + // Line width (0.2 mm) + m_lineWidth = .567 / m_k; + + // Automatic page break + SetAutoPageBreak(true, 2*margin); + + // Full width display mode + SetDisplayMode(wxPDF_ZOOM_FULLWIDTH); + m_zoomFactor = 100.; + + // Default viewer preferences + m_viewerPrefs = 0; + + // Enable compression + SetCompression(true); + + // Set default PDF version number + m_PDFVersion = _T("1.3"); + m_importVersion = m_PDFVersion; + + m_encrypted = false; + m_encryptor = NULL; + + m_javascript = wxEmptyString; + + m_inTemplate = false; + m_templateId = 0; + m_templatePrefix = _T("/TPL"); + + m_currentParser = NULL; + m_currentSource = wxEmptyString; + + SetFontPath(); + SetFont(_T("ZapfDingBats"), _T(""), 9); + m_zapfdingbats = m_currentFont->GetIndex(); +} + + + +wxPdfDocument::~wxPdfDocument() +{ + delete m_coreFonts; + + wxPdfFontHashMap::iterator font = m_fonts->begin(); + for (font = m_fonts->begin(); font != m_fonts->end(); font++) + { + if (font->second != NULL) + { + delete font->second; + } + } + delete m_fonts; + + wxPdfImageHashMap::iterator image = m_images->begin(); + for (image = m_images->begin(); image != m_images->end(); image++) + { + if (image->second != NULL) + { + delete image->second; + } + } + delete m_images; + + wxPdfPageHashMap::iterator page = m_pages->begin(); + for (page = m_pages->begin(); page != m_pages->end(); page++) + { + if (page->second != NULL) + { + delete page->second; + } + } + delete m_pages; + + wxPdfPageLinksMap::iterator pageLinks = m_pageLinks->begin(); + for (pageLinks = m_pageLinks->begin(); pageLinks != m_pageLinks->end(); pageLinks++) + { + if (pageLinks->second != NULL) + { + delete pageLinks->second; + } + } + delete m_pageLinks; + + wxPdfLinkHashMap::iterator link = m_links->begin(); + for (link = m_links->begin(); link != m_links->end(); link++) + { + if (link->second != NULL) + { + delete link->second; + } + } + delete m_links; + + delete m_namedLinks; + + size_t j; + for (j = 0; j < m_outlines.GetCount(); j++) + { + wxPdfBookmark* bookmark = (wxPdfBookmark*) m_outlines[j]; + delete bookmark; + } + + wxPdfDiffHashMap::iterator diff = m_diffs->begin(); + for (diff = m_diffs->begin(); diff != m_diffs->end(); diff++) + { + if (diff->second != NULL) + { + delete diff->second; + } + } + delete m_diffs; + + wxPdfExtGStateMap::iterator extGState = m_extGStates->begin(); + for (extGState = m_extGStates->begin(); extGState != m_extGStates->end(); extGState++) + { + if (extGState->second != NULL) + { + delete extGState->second; + } + } + delete m_extGStates; + + delete m_extGSLookup; + + wxPdfGradientMap::iterator gradient = m_gradients->begin(); + for (gradient = m_gradients->begin(); gradient != m_gradients->end(); gradient++) + { + if (gradient->second != NULL) + { + delete gradient->second; + } + } + delete m_gradients; + + wxPdfAnnotationsMap::iterator annotation = m_annotations->begin(); + for (annotation = m_annotations->begin(); annotation != m_annotations->end(); annotation++) + { + if (annotation->second != NULL) + { + delete annotation->second; + } + } + delete m_annotations; + + wxPdfFormAnnotsMap::iterator formAnnotation = m_formAnnotations->begin(); + for (formAnnotation = m_formAnnotations->begin(); formAnnotation != m_formAnnotations->end(); formAnnotation++) + { + if (formAnnotation->second != NULL) + { + delete formAnnotation->second; + } + } + delete m_formAnnotations; + + wxPdfFormFieldsMap::iterator formField = m_formFields->begin(); + for (formField = m_formFields->begin(); formField != m_formFields->end(); formField++) + { + if (formField->second != NULL) + { + delete formField->second; + } + } + delete m_formFields; + + wxPdfRadioGroupMap::iterator radioGroup = m_radioGroups->begin(); + for (radioGroup = m_radioGroups->begin(); radioGroup != m_radioGroups->end(); radioGroup++) + { + if (radioGroup->second != NULL) + { + delete radioGroup->second; + } + } + delete m_radioGroups; + + wxPdfTemplatesMap::iterator templateIter = m_templates->begin(); + for (templateIter = m_templates->begin(); templateIter != m_templates->end(); templateIter++) + { + if (templateIter->second != NULL) + { + delete templateIter->second; + } + } + delete m_templates; + + wxPdfParserMap::iterator parser = m_parsers->begin(); + for (parser = m_parsers->begin(); parser != m_parsers->end(); parser++) + { + if (parser->second != NULL) + { + delete parser->second; + } + } + delete m_parsers; + + wxPdfSpotColourMap::iterator spotColor = m_spotColors->begin(); + for (spotColor = m_spotColors->begin(); spotColor != m_spotColors->end(); spotColor++) + { + if (spotColor->second != NULL) + { + delete spotColor->second; + } + } + delete m_spotColors; + + wxPdfPatternMap::iterator pattern = m_patterns->begin(); + for(pattern = m_patterns->begin(); pattern != m_patterns->end(); pattern++) + { + if(pattern->second != NULL) + { + delete pattern->second; + } + } + delete m_patterns; + + delete m_orientationChanges; + + delete m_offsets; + + if (m_encryptor != NULL) + { + delete m_encryptor; + } +} + +// --- Public methods + +void +wxPdfDocument::SetProtection(int permissions, + const wxString& userPassword, + const wxString& ownerPassword, + wxPdfEncryptionMethod encryptionMethod, + int keyLength) +{ + if (m_encryptor == NULL) + { + int revision = (keyLength > 0) ? 3 : 2; + switch (encryptionMethod) + { + case wxPDF_ENCRYPTION_AESV2: + revision = 4; + if (m_PDFVersion < _T("1.6")) + { + m_PDFVersion = _T("1.6"); + } + break; + case wxPDF_ENCRYPTION_RC4V2: + revision = 3; + break; + case wxPDF_ENCRYPTION_RC4V1: + default: + revision = 2; + break; + } + m_encryptor = new wxPdfEncrypt(revision, keyLength); + m_encrypted = true; + int allowedFlags = wxPDF_PERMISSION_PRINT | wxPDF_PERMISSION_MODIFY | + wxPDF_PERMISSION_COPY | wxPDF_PERMISSION_ANNOT; + int protection = 192; + protection += (permissions & allowedFlags); + wxString ownerPswd = ownerPassword; + if (ownerPswd.Length() == 0) + { + ownerPswd = wxPdfDocument::GetUniqueId(_T("wxPdfDoc")); + } + m_encryptor->GenerateEncryptionKey(userPassword, ownerPswd, protection); + } +} + +void +wxPdfDocument::SetImageScale(double scale) +{ + m_imgscale = scale; +} + +double +wxPdfDocument::GetImageScale() +{ + return m_imgscale; +} + +double +wxPdfDocument::GetPageWidth() +{ + return m_w; +} + +double +wxPdfDocument::GetPageHeight() +{ + return m_h; +} + +double +wxPdfDocument::GetBreakMargin() +{ + return m_bMargin; +} + +double +wxPdfDocument::GetScaleFactor() +{ + return m_k; +} + +void +wxPdfDocument::AliasNbPages(const wxString& alias) +{ + // Define an alias for total number of pages + m_aliasNbPages = alias; +} + +void +wxPdfDocument::Open() +{ + // Begin document + m_state = 1; +} + +void +wxPdfDocument::AddPage(int orientation) +{ + if (m_inTemplate) + { + wxLogError(_("wxPdfDocument::AddPage: Adding pages in templates is impossible. Current template ID is %d."), m_templateId); + return; + } + + // Start a new page + if (m_state == 0) + { + Open(); + } + wxString family = m_fontFamily; + wxString style = m_fontStyle; + if (m_decoration & wxPDF_FONT_UNDERLINE) + { + style += wxString(_T("U")); + } + if (m_decoration & wxPDF_FONT_OVERLINE) + { + style += wxString(_T("O")); + } + if (m_decoration & wxPDF_FONT_STRIKEOUT) + { + style += wxString(_T("S")); + } + double size = m_fontSizePt; + double lw = m_lineWidth; + wxPdfColour dc = m_drawColor; + wxPdfColour fc = m_fillColor; + wxPdfColour tc = m_textColor; + bool cf = m_colorFlag; + + if (m_page > 0) + { + // Page footer + m_inFooter = true; + Footer(); + m_inFooter = false; + // Close page + EndPage(); + } + + // Start new page + BeginPage(orientation); + + // Set line cap style to square + Out("2 J"); + + // Set line width + m_lineWidth = lw; + OutAscii(Double2String(lw*m_k,2)+wxString(_T(" w"))); + + // Set font + if (family.Length() > 0) + { + SetFont(family, style, size); + } + + // Set colors + m_drawColor = dc; + if (dc != wxPdfColour(0)) + { + OutAscii(dc.GetColor(true)); + } + m_fillColor = fc; + if (fc != wxPdfColour(0)) + { + OutAscii(fc.GetColor(false)); + } + m_textColor = tc; + m_colorFlag = cf; + + // Page header + Header(); + + // Restore line width + if (m_lineWidth != lw) + { + m_lineWidth = lw; + OutAscii(Double2String(lw*m_k,2)+wxString(_T(" w"))); + } + + // Restore font + if(family.Length() > 0) + { + SetFont(family, style, size); + } + + // Restore colors + if (m_drawColor != dc) + { + m_drawColor = dc; + OutAscii(dc.GetColor(true)); + } + if (m_fillColor != fc) + { + m_fillColor = fc; + OutAscii(fc.GetColor(false)); + } + m_textColor = tc; + m_colorFlag = cf; +} + +void +wxPdfDocument::SetLineWidth(double width) +{ + // Set line width + m_lineWidth = width; + if (m_page > 0) + { + OutAscii(Double2String(width*m_k,2)+ wxString(_T(" w"))); + } +} + +double +wxPdfDocument::GetLineWidth() +{ + return m_lineWidth; +} + +void +wxPdfDocument::SetFontPath(const wxString& fontPath) +{ + if (fontPath != wxEmptyString) + { + m_fontPath = fontPath; + } + else + { + wxString localFontPath; + if (!wxGetEnv(_T("WXPDF_FONTPATH"), &localFontPath)) + { + localFontPath = wxGetCwd(); + if (!wxEndsWithPathSeparator(localFontPath)) + { + localFontPath += wxFILE_SEP_PATH; + } + localFontPath += _T("fonts"); + } + m_fontPath = localFontPath; + } +} + +bool +wxPdfDocument::AddFont(const wxString& family, const wxString& style, const wxString& file) +{ + if (family.Length() == 0) return false; + + // Add a TrueType or Type1 font + wxString lcFamily = family.Lower(); + wxString lcStyle = style.Lower(); + wxString ucStyle = style.Upper(); + + wxString fileName = file; + if (fileName.Length() == 0) + { + fileName = lcFamily + lcStyle + wxString(_T(".xml")); + fileName.Replace(_T(" "),_T("")); + } + + if (ucStyle == _T("IB")) + { + ucStyle = _T("BI"); + } + + // check if the font has been already added + wxString fontkey = lcFamily + ucStyle; + wxPdfFontHashMap::iterator font = (*m_fonts).find(fontkey); + if (font != (*m_fonts).end()) + { + // Font already loaded + return true; + } + + // Open font metrics XML file + wxFileName fontFileName(fileName); + fontFileName.MakeAbsolute(GetFontPath()); + //BM: wxFileSystem doesnt appear to have default handler for local files?, just let + // the wxXmlDocument load the file itself rather than generating a stream. +/* wxFileSystem fs; + wxFSFile* xmlFontMetrics = fs.OpenFile(fontFileName.GetFullPath()); + if (!xmlFontMetrics) + { + // Font metrics XML file not found + wxLogDebug(_T("wxPdfDocument::AddFont: Font metrics file '%s' not found."), fileName.c_str()); + return false; + } +*/ + // Load the XML file + wxXmlDocument fontMetrics; +// bool loaded = fontMetrics.Load(*xmlFontMetrics->GetStream()); +// delete xmlFontMetrics; + bool loaded = fontMetrics.Load(fontFileName.GetFullPath()); + if (!loaded) + { + // Font metrics file loading failed + wxLogDebug(_T("wxPdfDocument::AddFont: Loading of font metrics file '%s' failed."), fileName.c_str()); + return false; + } + if (!fontMetrics.IsOk() || fontMetrics.GetRoot()->GetName() != wxT("wxpdfdoc-font-metrics")) + { + // Not a font metrics file + wxLogDebug(_T("wxPdfDocument::AddFont: Font metrics file '%s' invalid."), fileName.c_str()); + return false; + } + + wxString fontType; + wxXmlNode* root = fontMetrics.GetRoot(); + if (!root->GetPropVal(_T("type"), &fontType)) + { + // Font type not specified + wxLogDebug(_T("wxPdfDocument::AddFont: Font type not specified for font '%s'."), family.c_str()); + return false; + } + + int i = (*m_fonts).size() + 1; + wxPdfFont* addedFont = NULL; + if (fontType == _T("TrueType")) + { + addedFont = new wxPdfFontTrueType(i); + } + else if (fontType == _T("Type1")) + { + addedFont = new wxPdfFontType1(i); + } +#if wxUSE_UNICODE + else if (fontType == _T("TrueTypeUnicode")) + { + addedFont = new wxPdfFontTrueTypeUnicode(i); + } + else if (fontType == _T("OpenTypeUnicode")) + { + addedFont = new wxPdfFontOpenTypeUnicode(i); + if (m_PDFVersion < _T("1.6")) + { + m_PDFVersion = _T("1.6"); + } + } + else if (fontType == _T("Type0")) + { + addedFont = new wxPdfFontType0(i); + } +#endif + else + { + // Unknown font type + wxLogDebug(_T("wxPdfDocument::AddFont: Unknown font type '%s'."), fontType.c_str()); + return false; + } + if (!addedFont->LoadFontMetrics(root)) + { + delete addedFont; + return false; + } + addedFont->SetFilePath(fontFileName.GetPath()); + (*m_fonts)[fontkey] = addedFont; + + if (addedFont->HasDiffs()) + { + // Search existing encodings + int d = 0; + int nb = (*m_diffs).size(); + for (i = 1; i <= nb; i++) + { + if (*(*m_diffs)[i] == addedFont->GetDiffs()) + { + d = i; + break; + } + } + if (d == 0) + { + d = nb + 1; + (*m_diffs)[d] = new wxString(addedFont->GetDiffs()); + } + addedFont->SetDiffIndex(d); + } + + return true; +} + +#if wxUSE_UNICODE + +bool +wxPdfDocument::AddFontCJK(const wxString& family) +{ + wxString lcFamily = family.Lower(); + wxString fontFile = lcFamily + wxString(_T(".xml")); + wxString fontkey = lcFamily; + wxString fontName; + bool valid; + + wxPdfFontHashMap::iterator font = (*m_fonts).find(fontkey); + if (font != (*m_fonts).end()) + { + return true; + } + + valid = AddFont(family, _T(""), fontFile); + if (valid) + { + // Add all available styles (bold, italic and bold-italic) + // For all styles the same font metric file is used, therefore + // the font name has to be changed afterwards to reflect the + // style. + AddFont(family, _T("B"), fontFile); + fontkey = lcFamily + wxString(_T("B")); + font = (*m_fonts).find(fontkey); + fontName = font->second->GetName(); + fontName += wxString(_T(",Bold")); + font->second->SetName(fontName); + + AddFont(family, _T("I"), fontFile); + fontkey = lcFamily + wxString(_T("I")); + font = (*m_fonts).find(fontkey); + fontName = font->second->GetName(); + fontName += wxString(_T(",Italic")); + font->second->SetName(fontName); + + AddFont(family, _T("BI"), fontFile); + fontkey = lcFamily + wxString(_T("BI")); + font = (*m_fonts).find(fontkey); + fontName = font->second->GetName(); + fontName += wxString(_T(",BoldItalic")); + font->second->SetName(fontName); + } + return valid; +} + +#endif // wxUSE_UNICODE + +bool +wxPdfDocument::SetFont(const wxString& family, const wxString& style, double size) +{ + return SelectFont(family, style, size, true); +} + +void +wxPdfDocument::SetFontSize(double size) +{ + // Set font size in points + if (m_fontSizePt == size) + { + return; + } + m_fontSizePt = size; + m_fontSize = size / m_k; + if ( m_page > 0) + { + OutAscii(wxString::Format(_T("BT /F%d "),m_currentFont->GetIndex()) + + Double2String(m_fontSizePt,2) + wxString(_T(" Tf ET"))); + } +} + +const wxPdfFontDescription& +wxPdfDocument::GetFontDescription() const +{ + return m_currentFont->GetDesc(); +} + +const wxString +wxPdfDocument::GetFontFamily() +{ + return m_fontFamily; +} + +const wxString +wxPdfDocument::GetFontStyle() +{ + wxString style = m_fontStyle; + if (m_decoration & wxPDF_FONT_UNDERLINE) + { + style += wxString(_T("U")); + } + if (m_decoration & wxPDF_FONT_OVERLINE) + { + style += wxString(_T("O")); + } + if (m_decoration & wxPDF_FONT_STRIKEOUT) + { + style += wxString(_T("S")); + } + return style; +} + +double +wxPdfDocument::GetFontSize() +{ + return m_fontSizePt; +} + +double +wxPdfDocument::GetStringWidth(const wxString& s) +{ + double w = 0; + if (m_currentFont != 0) + { + w = m_currentFont->GetStringWidth(s) * m_fontSize; + } + return w; +} + +void +wxPdfDocument::Text(double x, double y, const wxString& txt, const int mode) +{ + // Output a string + if (m_colorFlag) + { + Out("q ", false); + OutAscii(m_textColor.GetColor(false), false); + Out(" ", false); + } + OutAscii(wxString(_T("BT ")) + + Double2String(x*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" Td ")), false); + if(mode<3) + { + OutAscii(Double2String(mode) + wxString(_T(" Tr")), false); + } + + OutAscii(wxString(_T(" (")), false); + TextEscape(txt,false); + Out(") Tj ET", false); + + if ((m_decoration & wxPDF_FONT_DECORATION) && txt.Length() > 0) + { + Out(" ", false); + OutAscii(DoDecoration(x, y, txt), false); + } + + if (m_colorFlag) + { + Out(" Q", false); + } + Out("\n", false); +} + +void +wxPdfDocument::RotatedText(double x, double y, const wxString& txt, double angle, const int mode) +{ + // Text rotated around its origin + StartTransform(); + Rotate(angle, x, y); + Text(x, y, txt, mode); + StopTransform(); +} + +bool +wxPdfDocument::AcceptPageBreak() +{ + // Accept automatic page break or not + return m_autoPageBreak; +} + +void +wxPdfDocument::Cell(double w, double h, const wxString& txt, int border, int ln, int align, int fill, const wxPdfLink& link) +{ + // Output a cell + double x, y; + double k = m_k; + if (m_y + h > m_pageBreakTrigger && !m_inFooter && AcceptPageBreak()) + { + // Automatic page break + x = m_x; + double ws = m_ws; + if (ws > 0) + { + m_ws = 0; + Out("0 Tw"); + } + AddPage(m_curOrientation); + m_x = x; + if (ws > 0) + { + m_ws = ws; + OutAscii(Double2String(ws*k,3)+wxString(_T(" Tw"))); + } + } + if ( w == 0) + { + w = m_w - m_rMargin - m_x; + } + wxString s = wxEmptyString; + if (fill == 1 || border == wxPDF_BORDER_FRAME) + { + s = Double2String(m_x*k,2) + wxString(_T(" ")) + + Double2String((m_h-m_y)*k,2) + wxString(_T(" ")) + + Double2String(w*k,2) + wxString(_T(" ")) + + Double2String(-h*k,2); + if (fill == 1) + { + if (border == wxPDF_BORDER_FRAME) + { + s += wxString(_T(" re B ")); + } + else + { + s += wxString(_T(" re f ")); + } + } + else + { + s += wxString(_T(" re S ")); + } + } + if (border != wxPDF_BORDER_NONE && border != wxPDF_BORDER_FRAME) + { + x = m_x; + y = m_y; + if (border & wxPDF_BORDER_LEFT) + { + s += Double2String(x*k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*k,2) + wxString(_T(" m ")) + + Double2String(x*k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+h))*k,2) + wxString(_T(" l S ")); + } + if (border & wxPDF_BORDER_TOP) + { + s += Double2String(x*k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*k,2) + wxString(_T(" m ")) + + Double2String((x+w)*k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*k,2) + wxString(_T(" l S ")); + } + if (border & wxPDF_BORDER_RIGHT) + { + s += Double2String((x+w)*k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*k,2) + wxString(_T(" m ")) + + Double2String((x+w)*k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+h))*k,2) + wxString(_T(" l S ")); + } + if (border & wxPDF_BORDER_BOTTOM) + { + s += Double2String(x*k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+h))*k,2) + wxString(_T(" m ")) + + Double2String((x+w)*k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+h))*k,2) + wxString(_T(" l S ")); + } + } + if (s.Length() > 0) + { + bool newline = txt.Length() == 0; + OutAscii(s, newline); + s = _T(""); + } + + if (txt.Length() > 0) + { + double width = GetStringWidth(txt); + double dx; + if (align == wxPDF_ALIGN_RIGHT) + { + dx = w - m_cMargin - width; + } + else if (align == wxPDF_ALIGN_CENTER) + { + dx = (w - width) / 2; + } + else + { + dx = m_cMargin; + } + if (m_colorFlag) + { + s += wxString(_T("q ")) + m_textColor.GetColor(false) + wxString(_T(" ")); + } + s += wxString(_T("BT ")) + + Double2String((m_x+dx)*k,2) + wxString(_T(" ")) + + Double2String((m_h-(m_y+.5*h+.3*m_fontSize))*k,2) + wxString(_T(" Td (")); + OutAscii(s,false); + TextEscape(txt,false); + s = _T(") Tj ET"); + if (m_currentFont != 0) + { + m_currentFont->UpdateUsedChars(txt); + } + + if (m_decoration & wxPDF_FONT_DECORATION) + { + s += wxString(_T(" ")) + DoDecoration(m_x+dx,m_y+.5*h+.3*m_fontSize,txt); + } + if (m_colorFlag) + { + s += wxString(_T(" Q")); + } + if (link.IsValid()) + { + Link(m_x+dx,m_y+.5*h-.5*m_fontSize,width,m_fontSize,link); + } + OutAscii(s); + } + m_lasth = h; + if (ln > 0) + { + // Go to next line + m_y += h; + if ( ln == 1) + { + m_x = m_lMargin; + } + } + else + { + m_x += w; + } +} + +int +wxPdfDocument::MultiCell(double w, double h, const wxString& txt, int border, int align, int fill, int maxline) +{ + // Output text with automatic or explicit line breaks + if (w == 0) + { + w = m_w - m_rMargin - m_x; + } + + double wmax = (w - 2 * m_cMargin); + wxString s = txt; + s.Replace(_T("\r"),_T("")); // remove carriage returns + int nb = s.Length(); + if (nb > 0 && s[nb-1] == _T('\n')) + { + nb--; + } + + int b = wxPDF_BORDER_NONE; + int b2 = wxPDF_BORDER_NONE; + if (border != wxPDF_BORDER_NONE) + { + if (border == wxPDF_BORDER_FRAME) + { + b = wxPDF_BORDER_LEFT | wxPDF_BORDER_RIGHT | wxPDF_BORDER_TOP; + b2 = wxPDF_BORDER_LEFT | wxPDF_BORDER_RIGHT; + } + else + { + b2 = wxPDF_BORDER_NONE; + if (border & wxPDF_BORDER_LEFT) + { + b2 = b2 | wxPDF_BORDER_LEFT; + } + if (border & wxPDF_BORDER_RIGHT) + { + b2 = b2 | wxPDF_BORDER_RIGHT; + } + b = (border & wxPDF_BORDER_TOP) ? b2 | wxPDF_BORDER_TOP : b2; + } + } + int sep = -1; + int i = 0; + int j = 0; + double len = 0; + double ls = 0; + int ns = 0; + int nl = 1; + wxChar c; + while (i < nb) + { + // Get next character + c = s[i]; + if (c == _T('\n')) + { + // Explicit line break + if (m_ws > 0) + { + m_ws = 0; + Out("0 Tw"); + } + Cell(w,h,s.SubString(j,i-1),b,2,align,fill); + i++; + sep = -1; + j = i; + len = 0; + ns = 0; + nl++; + if (border != wxPDF_BORDER_NONE && nl == 2) + { + b = b2; + } + if (maxline > 0 && nl > maxline) + { + return j; + } + continue; + } + if (c == _T(' ')) + { + sep = i; + ls = len; + ns++; + } + len = GetStringWidth(s.SubString(j, i)); + + if (len > wmax) + { + // Automatic line break + if (sep == -1) + { + if (i == j) + { + i++; + } + if (m_ws > 0) + { + m_ws=0; + Out("0 Tw"); + } + Cell(w,h,s.SubString(j,i-1),b,2,align,fill); + } + else + { + if (align == wxPDF_ALIGN_JUSTIFY) + { + m_ws = (ns > 1) ? (wmax - ls)/(ns-1) : 0; + OutAscii(Double2String(m_ws*m_k,3)+wxString(_T(" Tw"))); + } + Cell(w,h,s.SubString(j,sep-1),b,2,align,fill); + i = sep + 1; + } + sep = -1; + j = i; + len = 0; + ns = 0; + nl++; + if (border != wxPDF_BORDER_NONE && nl == 2) + { + b = b2; + } + if (maxline > 0 && nl > maxline) + { + return j; + } + } + else + { + i++; + } + } + // Last chunk + if (m_ws > 0) + { + m_ws = 0; + Out("0 Tw"); + } + if ((border != wxPDF_BORDER_NONE) && (border & wxPDF_BORDER_BOTTOM)) + { + b = b | wxPDF_BORDER_BOTTOM; + } + Cell(w,h,s.SubString(j,i-1),b,2,align,fill); + m_x = m_lMargin; + return i; +} + +int +wxPdfDocument::LineCount(double w, const wxString& txt) +{ + // Output text with automatic or explicit line breaks + if (w == 0) + { + w = m_w - m_rMargin - m_x; + } + + double wmax = (w - 2 * m_cMargin); + wxString s = txt; + s.Replace(_T("\r"),_T("")); // remove carriage returns + int nb = s.Length(); + if (nb > 0 && s[nb-1] == _T('\n')) + { + nb--; + } + + int sep = -1; + int i = 0; + int j = 0; + double len = 0; + int nl = 1; + wxChar c; + while (i < nb) + { + // Get next character + c = s[i]; + if (c == _T('\n')) + { + // Explicit line break + i++; + sep = -1; + j = i; + len = 0; + nl++; + continue; + } + if (c == _T(' ')) + { + sep = i; + } + len = GetStringWidth(s.SubString(j, i)); + + if (len > wmax) + { + // Automatic line break + if (sep == -1) + { + if (i == j) + { + i++; + } + } + else + { + i = sep + 1; + } + sep = -1; + j = i; + len = 0; + nl++; + } + else + { + i++; + } + } + return nl; +} + +int +wxPdfDocument::TextBox(double w, double h, const wxString& txt, + int halign, int valign, int border, int fill) +{ + double xi = m_x; + double yi = m_y; + + double hrow = m_fontSize; + int textrows = LineCount(w, txt); + int maxrows = (int) floor(h / hrow); + int rows = (textrows < maxrows) ? textrows : maxrows; + + double dy = 0; + if (valign == wxPDF_ALIGN_MIDDLE) + { + dy = (h - rows * hrow) / 2; + } + else if (valign == wxPDF_ALIGN_BOTTOM) + { + dy = h - rows * hrow; + } + + SetY(yi+dy); + SetX(xi); + int trail = MultiCell(w, hrow, txt, 0, halign, fill, rows); + + if (border == wxPDF_BORDER_FRAME) + { + Rect(xi, yi, w, h); + } + else + { + if (border & wxPDF_BORDER_LEFT) Line(xi,yi,xi,yi+h); + if (border & wxPDF_BORDER_RIGHT) Line(xi+w,yi,xi+w,yi+h); + if (border & wxPDF_BORDER_TOP) Line(xi,yi,xi+w,yi); + if (border & wxPDF_BORDER_BOTTOM) Line(xi,yi+h,xi+w,yi+h); + } + + return trail; +} + +void +wxPdfDocument::Write(double h, const wxString& txt, const wxPdfLink& link) +{ + WriteCell(h, txt, wxPDF_BORDER_NONE, 0, link); +} + +void +wxPdfDocument::WriteCell(double h, const wxString& txt, int border, int fill, const wxPdfLink& link) +{ + // Output text in flowing mode + wxString s = txt; + s.Replace(_T("\r"),_T("")); // remove carriage returns + int nb = s.Length(); + + // handle single space character + if ((nb == 1) && s[0] == _T(' ')) + { + m_x += GetStringWidth(s); + return; + } + + double saveCellMargin = GetCellMargin(); + SetCellMargin(0); + + double w = m_w - m_rMargin - m_x; + double wmax = (w - 2 * m_cMargin) + wxPDF_EPSILON; + + int sep = -1; + int i = 0; + int j = 0; + double len=0; + int nl = 1; + wxChar c; + while (i < nb) + { + // Get next character + c = s[i]; + if (c == _T('\n')) + { + // Explicit line break + Cell(w, h, s.SubString(j,i-1), border, 2, wxPDF_ALIGN_LEFT, fill, link); + i++; + sep = -1; + j = i; + len = 0; + if (nl == 1) + { + m_x = m_lMargin; + w = m_w - m_rMargin - m_x; + wmax = (w - 2 * m_cMargin); + } + nl++; + continue; + } + if (c == _T(' ')) + { + sep = i; + } + len = GetStringWidth(s.SubString(j, i)); + if (len > wmax) + { + // Automatic line break + if (sep == -1) + { + if (m_x > m_lMargin) + { + // Move to next line + m_x = m_lMargin; + m_y += h; + w = m_w - m_rMargin -m_x; + wmax = (w - 2 * m_cMargin); + i++; + nl++; + continue; + } + if (i == j) + { + i++; + } + Cell(w, h,s.SubString(j, i-1), border, 2, wxPDF_ALIGN_LEFT, fill, link); + } + else + { + Cell(w, h, s.SubString(j, sep-1), border, 2, wxPDF_ALIGN_LEFT, fill, link); + i = sep + 1; + } + sep = -1; + j = i; + len = 0; + if (nl == 1) + { + m_x = m_lMargin; + w = m_w - m_rMargin - m_x; + wmax = (w - 2 * m_cMargin); + } + nl++; + } + else + { + i++; + } + } + // Last chunk + if (i != j) + { + Cell(len, h, s.SubString(j,i-1), border, 0, wxPDF_ALIGN_LEFT, fill, link); + } + + // Following statement was in PHP code, but seems to be in error. + // m_x += GetStringWidth(s.SubString(j, i-1)); + SetCellMargin(saveCellMargin); +} + +bool +wxPdfDocument::Image(const wxString& file, double x, double y, double w, double h, + const wxString& type, const wxPdfLink& link, int maskImage) +{ + wxPdfImage* currentImage = NULL; + // Put an image on the page + wxPdfImageHashMap::iterator image = (*m_images).find(file); + if (image == (*m_images).end()) + { + // First use of image, get info + int i = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, i, file, type); + if (!currentImage->Parse()) + { + bool isValid = false; + delete currentImage; + + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL) + { + wxImage::AddHandler(new wxPNGHandler()); + } + wxImage tempImage; + tempImage.LoadFile(file); + if (tempImage.Ok()) + { + isValid = Image(file, tempImage, x, y, w, h, link, maskImage); + } + return isValid; + } + if (maskImage > 0) + { + currentImage->SetMaskImage(maskImage); + } + (*m_images)[file] = currentImage; + } + else + { + currentImage = image->second; + if (maskImage > 0 && currentImage->GetMaskImage() != maskImage) + { + currentImage->SetMaskImage(maskImage); + } + } + OutImage(currentImage, x, y, w, h, link); + return true; +} + +bool +wxPdfDocument::Image(const wxString& name, const wxImage& img, double x, double y, double w, double h, + const wxPdfLink& link, int maskImage) +{ + bool isValid = false; + if (img.Ok()) + { + wxImage tempImage = img.Copy(); + wxPdfImage* currentImage = NULL; + // Put an image on the page + wxPdfImageHashMap::iterator image = (*m_images).find(name); + if (image == (*m_images).end()) + { + if (tempImage.HasAlpha()) + { + if (maskImage <= 0) + { + maskImage = ImageMask(name+wxString(_T(".mask")), tempImage); + } + if(!tempImage.ConvertAlphaToMask(0)) + { + return false; + } + } + // First use of image, get info + tempImage.SetMask(false); + int i = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, i, name, tempImage); + if (!currentImage->Parse()) + { + delete currentImage; + return false; + } + if (maskImage > 0) + { + currentImage->SetMaskImage(maskImage); + } + (*m_images)[name] = currentImage; + } + else + { + currentImage = image->second; + if (maskImage > 0 && currentImage->GetMaskImage() != maskImage) + { + currentImage->SetMaskImage(maskImage); + } + } + OutImage(currentImage, x, y, w, h, link); + isValid = true; + } + return isValid; +} + +bool +wxPdfDocument::Image(const wxString& name, wxInputStream& stream, + const wxString& mimeType, + double x, double y, double w, double h, + const wxPdfLink& link, int maskImage) +{ + bool isValid = false; + wxPdfImage* currentImage = NULL; + // Put an image on the page + wxPdfImageHashMap::iterator image = (*m_images).find(name); + if (image == (*m_images).end()) + { + // First use of image, get info + int i = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, i, name, stream, mimeType); + if (!currentImage->Parse()) + { + delete currentImage; + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL) + { + wxImage::AddHandler(new wxPNGHandler()); + } + wxImage tempImage; + tempImage.LoadFile(stream, mimeType); + if (tempImage.Ok()) + { + isValid = Image(name, tempImage, x, y, w, h, link, maskImage); + } + return isValid; + + } + if (maskImage > 0) + { + currentImage->SetMaskImage(maskImage); + } + (*m_images)[name] = currentImage; + } + else + { + currentImage = image->second; + if (maskImage > 0 && currentImage->GetMaskImage() != maskImage) + { + currentImage->SetMaskImage(maskImage); + } + } + OutImage(currentImage, x, y, w, h, link); + isValid = true; + return isValid; +} + +int +wxPdfDocument::ImageMask(const wxString& file, const wxString& type) +{ + int n = 0; + wxPdfImage* currentImage = NULL; + // Put an image on the page + wxPdfImageHashMap::iterator image = (*m_images).find(file); + if (image == (*m_images).end()) + { + // First use of image, get info + n = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, n, file, type); + if (!currentImage->Parse()) + { + delete currentImage; + return 0; + } + // Check whether this is a gray scale image (must be) + if (currentImage->GetColorSpace() != _T("DeviceGray")) + { + delete currentImage; + return 0; + } + (*m_images)[file] = currentImage; + } + else + { + currentImage = image->second; + n = currentImage->GetIndex(); + } + if (m_PDFVersion < _T("1.4")) + { + m_PDFVersion = _T("1.4"); + } + return n; +} + +int +wxPdfDocument::ImageMask(const wxString& name, const wxImage& img) +{ + int n = 0; + if (img.Ok()) + { + wxPdfImage* currentImage = NULL; + // Put an image on the page + wxPdfImageHashMap::iterator image = (*m_images).find(name); + if (image == (*m_images).end()) + { + wxImage tempImage; + if (img.HasAlpha()) + { + int x, y; + int w = img.GetWidth(); + int h = img.GetHeight(); + tempImage = wxImage(w, h); + unsigned char alpha; + for (x = 0; x < w; x++) + { + for (y = 0; y < h; y++) + { + alpha = img.GetAlpha(x, y); + tempImage.SetRGB(x, y, alpha, alpha, alpha); + } + } + tempImage.SetOption(wxIMAGE_OPTION_PNG_FORMAT, wxPNG_TYPE_GREY_RED); + } + else + { + tempImage = img.Copy(); + tempImage.SetOption(wxIMAGE_OPTION_PNG_FORMAT, wxPNG_TYPE_GREY); + } + tempImage.SetMask(false); + // First use of image, get info + n = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, n, name, tempImage); + if (!currentImage->Parse()) + { + delete currentImage; + return 0; + } + (*m_images)[name] = currentImage; + } + else + { + currentImage = image->second; + n = currentImage->GetIndex(); + } + if (m_PDFVersion < _T("1.4")) + { + m_PDFVersion = _T("1.4"); + } + } + return n; +} + +int +wxPdfDocument::ImageMask(const wxString& name, wxInputStream& stream, const wxString& mimeType) +{ + int n = 0; + wxPdfImage* currentImage = NULL; + // Put an image on the page + wxPdfImageHashMap::iterator image = (*m_images).find(name); + if (image == (*m_images).end()) + { + // First use of image, get info + n = (*m_images).size() + 1; + currentImage = new wxPdfImage(this, n, name, stream, mimeType); + if (!currentImage->Parse()) + { + delete currentImage; + return 0; + } + // Check whether this is a gray scale image (must be) + if (currentImage->GetColorSpace() != _T("DeviceGray")) + { + delete currentImage; + return 0; + } + (*m_images)[name] = currentImage; + } + else + { + currentImage = image->second; + n = currentImage->GetIndex(); + } + if (m_PDFVersion < _T("1.4")) + { + m_PDFVersion = _T("1.4"); + } + return n; +} + +void +wxPdfDocument::RotatedImage(const wxString& file, double x, double y, double w, double h, + double angle, const wxString& type, const wxPdfLink& link, int maskImage) +{ + // Image rotated around its upper-left corner + StartTransform(); + Rotate(angle, x, y); + Image(file, x, y, w, h, type, link, maskImage); + StopTransform(); +} + +void +wxPdfDocument::Ln(double h) +{ + // Line feed; default value is last cell height + m_x = m_lMargin; + if (h < 0) + { + m_y += m_lasth; + } + else + { + m_y += h; + } +} + +void +wxPdfDocument::SaveAsFile(const wxString& name) +{ + wxString fileName = name; + // Finish document if necessary + if (m_state < 3) + { + Close(); + } + // Normalize parameters + if(fileName.Length() == 0) + { + fileName = _T("doc.pdf"); + } + // Save to local file + wxFileOutputStream outfile(fileName); + wxMemoryInputStream tmp(m_buffer); + outfile.Write(tmp); + outfile.Close(); +} + +const wxMemoryOutputStream& +wxPdfDocument::CloseAndGetBuffer() +{ + if (m_state < 3) + { + Close(); + } + + return m_buffer; +} + +void +wxPdfDocument::SetViewerPreferences(int preferences) +{ + m_viewerPrefs = (preferences > 0) ? preferences : 0; + if (((m_viewerPrefs & wxPDF_VIEWER_DISPLAYDOCTITLE) != 0) && (m_PDFVersion < _T("1.4"))) + { + m_PDFVersion = _T("1.4"); + } +} + +void +wxPdfDocument::SetTitle(const wxString& title) +{ + // Title of document + m_title = title; +} + +void +wxPdfDocument::SetSubject(const wxString& subject) +{ + // Subject of document + m_subject = subject; +} + +void +wxPdfDocument::SetAuthor(const wxString& author) +{ + // Author of document + m_author = author; +} + +void +wxPdfDocument::SetKeywords(const wxString& keywords) +{ + // Keywords of document + m_keywords = keywords; +} + +void +wxPdfDocument::SetCreator(const wxString& creator) +{ + // Creator of document + m_creator = creator; +} + +void +wxPdfDocument::SetMargins(double left, double top, double right) +{ + // Set left, top and right margins + m_lMargin = left; + m_tMargin = top; + if (right == -1) + { + right = left; + } + m_rMargin = right; +} + +void +wxPdfDocument::SetLeftMargin(double margin) +{ + // Set left margin + m_lMargin = margin; + if (m_page > 0 && m_x < margin) + { + m_x = margin; + } +} + +double +wxPdfDocument::GetLeftMargin() +{ + return m_lMargin; +} + +void +wxPdfDocument::SetTopMargin(double margin) +{ + // Set top margin + m_tMargin = margin; +} + +double +wxPdfDocument::GetTopMargin() +{ + return m_tMargin; +} + +void +wxPdfDocument::SetRightMargin(double margin) +{ + // Set right margin + m_rMargin = margin; +} + +double +wxPdfDocument::GetRightMargin() +{ + return m_rMargin; +} + +void +wxPdfDocument::SetCellMargin(double margin) +{ + // Set cell margin + m_cMargin = margin; +} + +double +wxPdfDocument::GetCellMargin() +{ + return m_cMargin; +} + +void +wxPdfDocument::SetLineHeight(double height) +{ + m_lasth = height; +} + +double +wxPdfDocument::GetLineHeight() +{ + return m_lasth; +} + +void +wxPdfDocument::SetAutoPageBreak(bool autoPageBreak, double margin) +{ + // Set auto page break mode and triggering margin + m_autoPageBreak = autoPageBreak; + m_bMargin = margin; + m_pageBreakTrigger = m_h - margin; +} + +void +wxPdfDocument::SetDisplayMode(wxPdfZoom zoom, wxPdfLayout layout, double zoomFactor) +{ + // Set display mode in viewer + switch (zoom) + { + case wxPDF_ZOOM_FULLPAGE: + case wxPDF_ZOOM_FULLWIDTH: + case wxPDF_ZOOM_REAL: + case wxPDF_ZOOM_DEFAULT: + m_zoomMode = zoom; + break; + case wxPDF_ZOOM_FACTOR: + m_zoomMode = zoom; + m_zoomFactor = (zoomFactor > 0) ? zoomFactor : 100.; + break; + default: + m_zoomMode = wxPDF_ZOOM_FULLWIDTH; + break; + } + + switch (layout) + { + case wxPDF_LAYOUT_SINGLE: + case wxPDF_LAYOUT_TWO: + case wxPDF_LAYOUT_DEFAULT: + case wxPDF_LAYOUT_CONTINUOUS: + m_layoutMode = layout; + break; + default: + m_layoutMode = wxPDF_LAYOUT_CONTINUOUS; + break; + } +} + +void +wxPdfDocument::Close() +{ + // Terminate document + if (m_state == 3) + { + return; + } + if (m_page == 0) + { + AddPage(); + } + + // Page footer + m_inFooter = true; + Footer(); + m_inFooter = false; + + // Close page + EndPage(); + + // Close document + EndDoc(); +} + +void +wxPdfDocument::Header() +{ + // To be implemented in your own inherited class +} + +void +wxPdfDocument::Footer() +{ + // To be implemented in your own inherited class +} + +bool +wxPdfDocument::IsInFooter() +{ + return m_inFooter; +} + +int +wxPdfDocument::PageNo() +{ + // Get current page number + return m_page; +} + +double +wxPdfDocument::GetX() +{ + // Get x position + return m_x; +} + +void +wxPdfDocument::SetX(double x) +{ + // Set x position + if ( x >= 0.0) + { + m_x = x; + } + else + { + m_x = m_w + x; + } +} + +double +wxPdfDocument::GetY() +{ + // Get y position + return m_y; +} + +void +wxPdfDocument::SetY(double y) +{ + // Set y position and reset x + m_x = m_lMargin; + if ( y >= 0) + { + m_y = y; + } + else + { + m_y = m_h + y; + } +} + +void +wxPdfDocument::SetXY(double x, double y) +{ + // Set x and y positions + SetY(y); + SetX(x); +} + +void +wxPdfDocument::SetCompression(bool compress) +{ + m_compress = compress; +} + +void +wxPdfDocument::AppendJavascript(const wxString& javascript) +{ + m_javascript += javascript; +} + +// --- Static methods + +wxString +wxPdfDocument::RGB2String(const wxColour& color) +{ + double r = color.Red(); + double g = color.Green(); + double b = color.Blue(); + wxString rgb = Double2String(r/255.,3) + _T(" ") + + Double2String(g/255.,3) + _T(" ") + + Double2String(b/255.,3); + return rgb; +} + +wxString +wxPdfDocument::Double2String(double value, int precision) +{ + wxString number; + if (precision < 0) + { + precision = 0; + } + else if (precision > 16) + { + precision = 16; + } + + // Use absolute value locally + double localValue = fabs(value); + double localFraction = (localValue - floor(localValue)) +(5. * pow(10.0, -precision-1)); + if (localFraction >= 1) + { + localValue += 1.0; + localFraction -= 1.0; + } + localFraction *= pow(10.0, precision); + + if (value < 0) + { + number += wxString(_T("-")); + } + + number += wxString::Format(_T("%.0lf"), floor(localValue)); + + // generate fraction, padding with zero if necessary. + if (precision > 0) + { + number += wxString(_T(".")); + wxString fraction = wxString::Format(_T("%.0lf"), floor(localFraction)); + if (fraction.Length() < ((size_t) precision)) + { + number += wxString(_T('0'), precision-fraction.Length()); + } + number += fraction; + } + + return number; +} + +double +wxPdfDocument::String2Double(const wxString& str) +{ + wxString value = str.Strip(wxString::both); + double result = 0; + double sign = 1; + int scale = 0; + int exponent = 0; + int expsign = 1; + int j = 0; + int jMax = value.Length(); + if (jMax > 0) + { + if (value[j] == wxT('+')) + { + j++; + } + else if (value[j] == wxT('-')) + { + sign = -1; + j++; + } + while (j < jMax && wxIsdigit(value[j])) + { + result = result*10 + (value[j] - wxT('0')); + j++; + } + if (j < jMax && value[j] == wxT('.')) + { + j++; + while (j < jMax && wxIsdigit(value[j])) + { + result = result*10 + (value[j] - wxT('0')); + scale++; + j++; + } + } + if (j < jMax && (value[j] == wxT('E') || value[j] == wxT('e'))) + { + j++; + if (value[j] == wxT('+')) + { + j++; + } + else if (value[j] == wxT('-')) + { + expsign = -1; + j++; + } + while (j < jMax && wxIsdigit(value[j])) + { + exponent = exponent*10 + (value[j] - wxT('0')); + j++; + } + exponent *= expsign; + } + result = sign * result * pow(10.0, exponent-scale); + } + return result; +} + +wxString +wxPdfDocument::Convert2Roman(int value) +{ + wxString result = wxEmptyString; + + if (value > 0 && value < 4000) + { + static wxString romans = _T("MDCLXVI"); + int pos = 6; // Point to LAST character in 'romans' + int currentDigit; + + while (value > 0) + { + currentDigit = value % 10; + if (currentDigit == 4 || currentDigit == 9) + { + result.Prepend(romans.Mid(pos - currentDigit / 4, 1)); + result.Prepend(romans.Mid(pos, 1)); + } + else + { + int x = currentDigit % 5; + while (x-- > 0) + { + result.Prepend(romans.Mid(pos, 1)); + } + if (currentDigit >= 5) + { + result.Prepend(romans.Mid(pos - 1, 1)); + } + } + value /= 10; + pos -= 2; + } + } + else + { + result = _T("???"); + } + return result; +} + +double +wxPdfDocument::ForceRange(double value, double minValue, double maxValue) +{ + if (value < minValue) + { + value = minValue; + } + else if (value > maxValue) + { + value = maxValue; + } + return value; +} + +void +wxPdfDocument::EnterOcg(const wxPdfOcg *ocg) +{ + OutAscii(wxString::Format(_T("/OC /OC%d BDC"), ocg->GetOcgIndex())); +} + +void +wxPdfDocument::ExitOcg(void) +{ + OutAscii(_T("EMC")); +} diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfgraphics.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfgraphics.cpp new file mode 100644 index 000000000..08f2c7fde --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfgraphics.cpp @@ -0,0 +1,2243 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfgraphics.cpp +// Purpose: Implementation of wxPdfDocument graphics primitives +// Author: Ulrich Telle +// Modified by: +// Created: 2006-01-27 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfgraphics.cpp Implementation of the wxPdfDocument graphics primitives + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#include + +#include "wx/pdfdoc.h" +#include "wx/pdfgraphics.h" + +wxPdfExtGState::wxPdfExtGState(double lineAlpha, double fillAlpha, wxPdfBlendMode blendMode) +{ + m_lineAlpha = lineAlpha; + m_fillAlpha = fillAlpha; + m_blendMode = blendMode; +} + +wxPdfExtGState::~wxPdfExtGState() +{ +} + +int +wxPdfDocument::SetAlpha(double lineAlpha, double fillAlpha, wxPdfBlendMode blendMode) +{ + int n = 0; + + // Force alpha into range 0 .. 1 + if (lineAlpha < 0) lineAlpha = 0; + else if (lineAlpha > 1) lineAlpha = 1; + if (fillAlpha < 0) fillAlpha = 0; + else if (fillAlpha > 1) fillAlpha = 1; + + // Create state id for lookup map + int id = ((int) blendMode) * 100000000 + (int) (lineAlpha * 1000) * 10000 + (int) (fillAlpha * 1000); + + // Lookup state + wxPdfExtGSLookupMap::iterator extGState = (*m_extGSLookup).find(id); + if (extGState == (*m_extGSLookup).end()) + { + n = (*m_extGStates).size() + 1; + (*m_extGStates)[n] = new wxPdfExtGState(lineAlpha, fillAlpha, blendMode); + (*m_extGSLookup)[id] = n; + } + else + { + n = extGState->second; + } + + if (n != m_currentExtGState) + { + SetAlphaState(n); + } + + return n; +} + +void +wxPdfDocument::SetAlphaState(int alphaState) +{ + if (alphaState > 0 && (size_t) alphaState <= (*m_extGStates).size()) + { + OutAscii(wxString::Format(_T("/GS%d gs"), alphaState)); + } +} + +// ---------------------------------------------------------------------------- +// wxPdfLineStyle: class representing line style for drawing graphics +// ---------------------------------------------------------------------------- + +wxPdfLineStyle::wxPdfLineStyle(double width, + wxPdfLineCap cap, wxPdfLineJoin join, + const wxPdfArrayDouble& dash, double phase, + const wxPdfColour& color) +{ + m_isSet = (width > 0) || (cap >= 0) || (join >= 0) || (dash.GetCount() > 0); + m_width = width; + m_cap = cap; + m_join = join; + m_dash = dash; + m_phase = phase; + m_color = color; + m_miterLimit = 0; +} + +wxPdfLineStyle::~wxPdfLineStyle() +{ +} + + +wxPdfLineStyle::wxPdfLineStyle(const wxPdfLineStyle& lineStyle) +{ + m_isSet = lineStyle.m_isSet; + m_width = lineStyle.m_width; + m_cap = lineStyle.m_cap; + m_join = lineStyle.m_join; + m_dash = lineStyle.m_dash; + m_phase = lineStyle.m_phase; + m_color = lineStyle.m_color; + m_miterLimit = lineStyle.m_miterLimit; +} + +wxPdfLineStyle& +wxPdfLineStyle::operator= (const wxPdfLineStyle& lineStyle) +{ + m_isSet = lineStyle.m_isSet; + m_width = lineStyle.m_width; + m_cap = lineStyle.m_cap; + m_join = lineStyle.m_join; + m_dash = lineStyle.m_dash; + m_phase = lineStyle.m_phase; + m_color = lineStyle.m_color; + m_miterLimit = lineStyle.m_miterLimit; + return *this; +} + +// --- Gradients + +wxPdfGradient::wxPdfGradient(wxPdfGradientType type) +{ + m_type = type; +} + +wxPdfGradient::~wxPdfGradient() +{ +} + +wxPdfAxialGradient::wxPdfAxialGradient(const wxPdfColour& color1, const wxPdfColour& color2, double x1, double y1, double x2, double y2, double intexp) + : wxPdfGradient(wxPDF_GRADIENT_AXIAL) +{ + m_color1 = color1; + m_color2 = color2; + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; + m_intexp = intexp; +} + +wxPdfAxialGradient::~wxPdfAxialGradient() +{ +} + +wxPdfMidAxialGradient::wxPdfMidAxialGradient(const wxPdfColour& color1, const wxPdfColour& color2, double x1, double y1, double x2, double y2, double midpoint, double intexp) + : wxPdfAxialGradient(color1, color2, x1, y1, x2, y2, intexp) +{ + m_type = wxPDF_GRADIENT_MIDAXIAL; + m_midpoint = midpoint; +} + +wxPdfMidAxialGradient::~wxPdfMidAxialGradient() +{ +} + +wxPdfRadialGradient::wxPdfRadialGradient(const wxPdfColour& color1, const wxPdfColour& color2, + double x1, double y1, double r1, + double x2, double y2, double r2, double intexp) + : wxPdfAxialGradient(color1, color2, x1, y1, x2, y2, intexp) +{ + m_type = wxPDF_GRADIENT_RADIAL; + m_r1 = r1; + m_r2 = r2; +} + +wxPdfRadialGradient::~wxPdfRadialGradient() +{ +} + +wxPdfCoonsPatch::wxPdfCoonsPatch(int edgeFlag, wxPdfColour colors[], double x[], double y[]) +{ + m_edgeFlag = edgeFlag; + size_t n = (edgeFlag == 0) ? 4 : 2; + size_t j; + for (j = 0; j < n; j++) + { + m_colors[j] = colors[j]; + } + + n = (edgeFlag == 0) ? 12 : 8; + for (j = 0; j < n; j++) + { + m_x[j] = x[j]; + m_y[j] = y[j]; + } +} + +wxPdfCoonsPatch::~wxPdfCoonsPatch() +{ +} + +wxPdfCoonsPatchMesh::wxPdfCoonsPatchMesh() +{ + m_ok = false; + m_colorType = wxPDF_COLOURTYPE_UNKNOWN; +} + +wxPdfCoonsPatchMesh::~wxPdfCoonsPatchMesh() +{ + size_t n = m_patches.size(); + if (n > 0) + { + size_t j; + for (j = 0; j < n; j++) + { + delete ((wxPdfCoonsPatch*) m_patches[j]); + } + } +} + +bool +wxPdfCoonsPatchMesh::AddPatch(int edgeFlag, wxPdfColour colors[], double x[], double y[]) +{ + wxPdfColourType colorType = m_colorType; + if (m_patches.size() == 0 && edgeFlag != 0) return false; + int n = (edgeFlag == 0) ? 4 : 2; + int j; + for (j = 0; j < n; j++) + { + if (colorType == wxPDF_COLOURTYPE_UNKNOWN) + { + colorType = colors[j].GetColorType(); + } + if (colors[j].GetColorType() != colorType) return false; + } + m_colorType = colorType; + wxPdfCoonsPatch* patch = new wxPdfCoonsPatch(edgeFlag, colors, x, y); + m_patches.Add(patch); + m_ok = true; + return true; +} + +wxPdfCoonsPatchGradient::wxPdfCoonsPatchGradient(const wxPdfCoonsPatchMesh& mesh, double minCoord, double maxCoord) + : wxPdfGradient(wxPDF_GRADIENT_COONS) +{ + int edgeFlag; + double *x; + double *y; + const wxArrayPtrVoid* patches = mesh.GetPatches(); + size_t n = patches->size(); + size_t j, k, nc; + unsigned char ch; + int bpcd = 65535; //16 BitsPerCoordinate + int coord; + wxPdfColour *colors; + + m_colorType = mesh.GetColorType(); + // build the data stream + for (j = 0; j < n; j++) + { + wxPdfCoonsPatch* patch = (wxPdfCoonsPatch*) (*patches)[j]; + edgeFlag = patch->GetEdgeFlag(); + ch = edgeFlag; + m_buffer.Write(&ch,1); //start with the edge flag as 8 bit + x = patch->GetX(); + y = patch->GetY(); + nc = (edgeFlag == 0) ? 12 : 8; + for (k = 0; k < nc; k++) + { + // each point as 16 bit + coord = (int) (((x[k] - minCoord) / (maxCoord - minCoord)) * bpcd); + if (coord < 0) coord = 0; + if (coord > bpcd) coord = bpcd; + ch = (coord >> 8) & 0xFF; + m_buffer.Write(&ch,1); + ch = coord & 0xFF; + m_buffer.Write(&ch,1); + coord = (int) (((y[k] - minCoord) / (maxCoord - minCoord)) * bpcd); + if (coord < 0) coord = 0; + if (coord > bpcd) coord = bpcd; + ch = (coord >> 8) & 0xFF; + m_buffer.Write(&ch,1); + ch = coord & 0xFF; + m_buffer.Write(&ch,1); + } + colors = patch->GetColors(); + nc = (edgeFlag == 0) ? 4 : 2; + for (k = 0; k < nc; k++) + { + // each color component as 8 bit + wxStringTokenizer tkz(colors[k].GetColorValue(), wxT(" ")); + while ( tkz.HasMoreTokens() ) + { + ch = ((int) (wxPdfDocument::String2Double(tkz.GetNextToken()) * 255)) & 0xFF; + m_buffer.Write(&ch,1); + } + } + } +} + +wxPdfCoonsPatchGradient::~wxPdfCoonsPatchGradient() +{ +} + +// --- + +wxPdfShape::wxPdfShape() +{ + m_subpath = -1; + m_index = 0; +} + +wxPdfShape::~wxPdfShape() +{ +} + +void +wxPdfShape::MoveTo(double x, double y) +{ + m_subpath = m_x.GetCount(); + m_types.Add(wxPDF_SEG_MOVETO); + m_x.Add(x); + m_y.Add(y); +} + +void +wxPdfShape::LineTo(double x, double y) +{ + if (m_subpath >= 0) + { + m_types.Add(wxPDF_SEG_LINETO); + m_x.Add(x); + m_y.Add(y); + } + else + { + wxLogError(_T("wxPdfShape::LineTo: Invalid subpath.")); + } +} + +void +wxPdfShape::CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) +{ + if (m_subpath >= 0) + { + m_types.Add(wxPDF_SEG_CURVETO); + m_x.Add(x1); + m_y.Add(y1); + m_x.Add(x2); + m_y.Add(y2); + m_x.Add(x3); + m_y.Add(y3); + } + else + { + wxLogError(_T("wxPdfShape::LineTo: Invalid subpath.")); + } +} + +void +wxPdfShape::ClosePath() +{ + if (m_subpath >= 0 && m_types.GetCount() > 0 && m_types.Last() != wxPDF_SEG_CLOSE) + { + m_types.Add(wxPDF_SEG_CLOSE); + m_x.Add(m_x[m_subpath]); + m_y.Add(m_y[m_subpath]); + m_subpath = -1; + } +} + +wxPdfSegmentType +wxPdfShape::GetSegment(int iterType, int iterPoints, double coords[]) const +{ + wxPdfSegmentType segType = wxPDF_SEG_UNDEFINED; + if (iterType >= 0 && (size_t) iterType < m_types.GetCount()) + { + int pointCount = (m_types[iterType] == wxPDF_SEG_CURVETO) ? 2 : 0; + if (iterPoints >= 0 && (size_t) (iterPoints + pointCount) < m_x.GetCount()) + { + segType = (wxPdfSegmentType) m_types[iterType]; + switch (segType) + { + case wxPDF_SEG_CLOSE: + coords[0] = m_x[iterPoints]; + coords[1] = m_y[iterPoints]; + break; + + case wxPDF_SEG_MOVETO: + case wxPDF_SEG_LINETO: + coords[0] = m_x[iterPoints]; + coords[1] = m_y[iterPoints]; + break; + + case wxPDF_SEG_CURVETO: + coords[0] = m_x[iterPoints]; + coords[1] = m_y[iterPoints]; + iterPoints++; + coords[2] = m_x[iterPoints]; + coords[3] = m_y[iterPoints]; + iterPoints++; + coords[4] = m_x[iterPoints]; + coords[5] = m_y[iterPoints]; + break; + } + } + } + return segType; +} + +wxPdfFlatPath::wxPdfFlatPath(const wxPdfShape* shape, double flatness, int limit) +{ + m_shape = shape; + m_iterType = 0; + m_iterPoints = 0; + m_done = false; + m_flatnessSq = flatness * flatness; + m_recursionLimit = limit; + + m_stackMaxSize = 6 * m_recursionLimit + /* 6 + 2 */ 8; + m_stack = new double[m_stackMaxSize]; + m_recLevel = new int[m_recursionLimit + 1]; + + FetchSegment(); +} + +wxPdfFlatPath::~wxPdfFlatPath() +{ + delete [] m_stack; + delete [] m_recLevel; +} + +void +wxPdfFlatPath::InitIter() +{ + m_done = false; + m_iterType = 0; + m_iterPoints = 0; + m_stackSize = 0; + FetchSegment(); +} + + /** + * Fetches the next segment from the source iterator. + */ +void +wxPdfFlatPath::FetchSegment() +{ + int sp; + + if ((size_t) m_iterType >= m_shape->GetSegmentCount()) + { + m_done = true; + return; + } + + m_srcSegType = m_shape->GetSegment(m_iterType, m_iterPoints, m_scratch); + + switch (m_srcSegType) + { + case wxPDF_SEG_CLOSE: + return; + + case wxPDF_SEG_MOVETO: + case wxPDF_SEG_LINETO: + m_srcPosX = m_scratch[0]; + m_srcPosY = m_scratch[1]; + return; + + case wxPDF_SEG_CURVETO: + if (m_recursionLimit == 0) + { + m_srcPosX = m_scratch[4]; + m_srcPosY = m_scratch[5]; + m_stackSize = 0; + return; + } + sp = 6 * m_recursionLimit; + m_stackSize = 1; + m_recLevel[0] = 0; + m_stack[sp] = m_srcPosX; // P1.x + m_stack[sp + 1] = m_srcPosY; // P1.y + m_stack[sp + 2] = m_scratch[0]; // C1.x + m_stack[sp + 3] = m_scratch[1]; // C1.y + m_stack[sp + 4] = m_scratch[2]; // C2.x + m_stack[sp + 5] = m_scratch[3]; // C2.y + m_srcPosX = m_stack[sp + 6] = m_scratch[4]; // P2.x + m_srcPosY = m_stack[sp + 7] = m_scratch[5]; // P2.y + SubdivideCubic(); + return; + } +} + +void +wxPdfFlatPath::Next() +{ + if (m_stackSize > 0) + { + --m_stackSize; + if (m_stackSize > 0) + { + switch (m_srcSegType) + { + case wxPDF_SEG_CURVETO: + SubdivideCubic(); + return; + + default: + break; + //throw new IllegalStateException(); + } + } + } + + if ((size_t) m_iterType < m_shape->GetSegmentCount()) + { + switch (m_srcSegType) + { + case wxPDF_SEG_CLOSE: + case wxPDF_SEG_MOVETO: + case wxPDF_SEG_LINETO: + m_iterPoints++; + break; + + case wxPDF_SEG_CURVETO: + m_iterPoints += 3; + break; + } + m_iterType++; + } + + FetchSegment(); +} + +int +wxPdfFlatPath::CurrentSegment(double coords[]) +{ + //if (done) + //throw new NoSuchElementException(); + + switch (m_srcSegType) + { + case wxPDF_SEG_CLOSE: + return m_srcSegType; + + case wxPDF_SEG_MOVETO: + case wxPDF_SEG_LINETO: + coords[0] = m_srcPosX; + coords[1] = m_srcPosY; + return m_srcSegType; + + case wxPDF_SEG_CURVETO: + if (m_stackSize == 0) + { + coords[0] = m_srcPosX; + coords[1] = m_srcPosY; + } + else + { + int sp = m_stackMaxSize - 6 * m_stackSize; + coords[0] = m_stack[sp + 4]; + coords[1] = m_stack[sp + 5]; + } + return wxPDF_SEG_LINETO; + } + + //throw new IllegalStateException(); + return wxPDF_SEG_UNDEFINED; +} + +static void +SubdivideCubicCurve(double src[], int srcOff, + double left[], int leftOff, + double right[], int rightOff) +{ + // To understand this code, please have a look at the image + // "CubicCurve2D-3.png" in the sub-directory "doc-files". + double srcC1x; + double srcC1y; + double srcC2x; + double srcC2y; + double leftP1x; + double leftP1y; + double leftC1x; + double leftC1y; + double leftC2x; + double leftC2y; + double rightC1x; + double rightC1y; + double rightC2x; + double rightC2y; + double rightP2x; + double rightP2y; + double midx; // Mid = left.P2 = right.P1 + double midy; // Mid = left.P2 = right.P1 + + leftP1x = src[srcOff]; + leftP1y = src[srcOff + 1]; + srcC1x = src[srcOff + 2]; + srcC1y = src[srcOff + 3]; + srcC2x = src[srcOff + 4]; + srcC2y = src[srcOff + 5]; + rightP2x = src[srcOff + 6]; + rightP2y = src[srcOff + 7]; + + leftC1x = (leftP1x + srcC1x) / 2; + leftC1y = (leftP1y + srcC1y) / 2; + rightC2x = (rightP2x + srcC2x) / 2; + rightC2y = (rightP2y + srcC2y) / 2; + midx = (srcC1x + srcC2x) / 2; + midy = (srcC1y + srcC2y) / 2; + leftC2x = (leftC1x + midx) / 2; + leftC2y = (leftC1y + midy) / 2; + rightC1x = (midx + rightC2x) / 2; + rightC1y = (midy + rightC2y) / 2; + midx = (leftC2x + rightC1x) / 2; + midy = (leftC2y + rightC1y) / 2; + + if (left != NULL) + { + left[leftOff] = leftP1x; + left[leftOff + 1] = leftP1y; + left[leftOff + 2] = leftC1x; + left[leftOff + 3] = leftC1y; + left[leftOff + 4] = leftC2x; + left[leftOff + 5] = leftC2y; + left[leftOff + 6] = midx; + left[leftOff + 7] = midy; + } + + if (right != NULL) + { + right[rightOff] = midx; + right[rightOff + 1] = midy; + right[rightOff + 2] = rightC1x; + right[rightOff + 3] = rightC1y; + right[rightOff + 4] = rightC2x; + right[rightOff + 5] = rightC2y; + right[rightOff + 6] = rightP2x; + right[rightOff + 7] = rightP2y; + } +} + +static double +PointSegmentDistanceSq(double x1, double y1, double x2, double y2, double px, double py) +{ + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + double x, y; + if (pd2 == 0) + { + // Points are coincident. + x = x1; + y = y2; + } + else + { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + + if (u < 0) + { + // "Off the end" + x = x1; + y = y1; + } + else if (u > 1.0) + { + x = x2; + y = y2; + } + else + { + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); + } + } + + return (x - px) * (x - px) + (y - py) * (y - py); +} + +static double +GetFlatnessSq(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) +{ + + double d1 = PointSegmentDistanceSq(x1, y1, x2, y2, cx1, cy1); + double d2 = PointSegmentDistanceSq(x1, y1, x2, y2, cx2, cy2); + return (d1 > d2) ? d1 : d2; +// return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, cx1, cy1), +// Line2D.ptSegDistSq(x1, y1, x2, y2, cx2, cy2)); +} + +static double +GetFlatnessSq(double coords[], int offset) +{ + return GetFlatnessSq(coords[offset+0], coords[offset+1], coords[offset+2], + coords[offset+3], coords[offset+4], coords[offset+5], + coords[offset+6], coords[offset+7]); +} + + /** + * Repeatedly subdivides the cubic curve segment that is on top + * of the stack. The iteration terminates when the recursion limit + * has been reached, or when the resulting segment is flat enough. + */ +void +wxPdfFlatPath::SubdivideCubic() +{ + int sp; + int level; + + sp = m_stackMaxSize - 6 * m_stackSize - 2; + level = m_recLevel[m_stackSize - 1]; + while ((level < m_recursionLimit) + && (GetFlatnessSq(m_stack, sp) >= m_flatnessSq)) + { + m_recLevel[m_stackSize] = m_recLevel[m_stackSize - 1] = ++level; + + SubdivideCubicCurve(m_stack, sp, m_stack, sp - 6, m_stack, sp); + ++m_stackSize; + sp -= 6; + } +} + +double +wxPdfFlatPath::MeasurePathLength() +{ + double points[6]; + double moveX = 0, moveY = 0; + double lastX = 0, lastY = 0; + double thisX = 0, thisY = 0; + int type = 0; + double total = 0; + + // Save iterator state + bool saveDone = m_done; + int saveIterType = m_iterType; + int saveIterPoints = m_iterPoints; + int saveStackSize = m_stackSize; + + InitIter(); + while (!IsDone()) + { + type = CurrentSegment(points); + switch( type ) + { + case wxPDF_SEG_MOVETO: + moveX = lastX = points[0]; + moveY = lastY = points[1]; + break; + + case wxPDF_SEG_CLOSE: + points[0] = moveX; + points[1] = moveY; + // Fall into.... + + case wxPDF_SEG_LINETO: + thisX = points[0]; + thisY = points[1]; + double dx = thisX-lastX; + double dy = thisY-lastY; + total += sqrt(dx*dx + dy*dy); + lastX = thisX; + lastY = thisY; + break; + } + Next(); + } + + // Restore iterator state + m_done = saveDone; + m_iterType = saveIterType; + m_iterPoints = saveIterPoints; + m_stackSize = saveStackSize; + FetchSegment(); + + return total; +} + +void +wxPdfDocument::ShapedText(const wxPdfShape& shape, const wxString& text, wxPdfShapedTextMode mode) +{ + bool stretchToFit = (mode == wxPDF_SHAPEDTEXTMODE_STRETCHTOFIT); + bool repeat = (mode == wxPDF_SHAPEDTEXTMODE_REPEAT); + double flatness = 0.25 / GetScaleFactor(); + wxPdfFlatPath it(&shape, flatness); + double points[6]; + double moveX = 0, moveY = 0; + double lastX = 0, lastY = 0; + double thisX = 0, thisY = 0; + int type = 0; + bool first = false; + double next = 0; + int currentChar = 0; + int length = text.Length(); + double height = GetFontSize() / GetScaleFactor(); + + if ( length == 0 ) + return; + + double factor = stretchToFit ? it.MeasurePathLength() / GetStringWidth(text) : 1.0; + double nextAdvance = 0; + + while (currentChar < length && !it.IsDone()) + { + type = it.CurrentSegment(points); + switch (type) + { + case wxPDF_SEG_MOVETO: + moveX = lastX = points[0]; + moveY = lastY = points[1]; + SetXY(moveX, moveY); + first = true; + nextAdvance = GetStringWidth(text.Mid(currentChar,1)) * 0.5; + next = nextAdvance; + break; + + case wxPDF_SEG_CLOSE: + points[0] = moveX; + points[1] = moveY; + // Fall into.... + + case wxPDF_SEG_LINETO: + thisX = points[0]; + thisY = points[1]; + double dx = thisX-lastX; + double dy = thisY-lastY; + double distance = sqrt(dx*dx + dy*dy); + if (distance >= next) + { + double r = 1.0 / distance; + double angle = atan2(-dy, dx) * 45. / atan(1.); + while (currentChar < length && distance >= next) + { + wxString glyph = text.Mid(currentChar, 1); + double x = lastX + next*dx*r; + double y = lastY + next*dy*r; + double advance = nextAdvance; + nextAdvance = currentChar < length-1 ? GetStringWidth(text.Mid(currentChar+1,1)) * 0.5 : + (repeat) ? GetStringWidth(text.Mid(0,1)) * 0.5 : 0; + SetXY(x, y); + StartTransform(); + Rotate(angle); + SetXY(x-advance,y-height); + Write(height, glyph); + StopTransform(); + next += (advance+nextAdvance) * factor; + currentChar++; + if ( repeat ) + { + currentChar %= length; + } + } + } + next -= distance; + first = false; + lastX = thisX; + lastY = thisY; + break; + } + it.Next(); + } +} + +// --- + +void +wxPdfDocument::Line(double x1, double y1, double x2, double y2) +{ + // Draw a line + OutAscii(Double2String(x1*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y1)*m_k,2) + wxString(_T(" m ")) + + Double2String(x2*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y2)*m_k,2) + wxString(_T(" l S"))); +} + +void +wxPdfDocument::Rect(double x, double y, double w, double h, int style) +{ + wxString op; + // Draw a rectangle + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILLDRAW) + { + op = _T("B"); + } + else + { + op = _T("S"); + } + OutAscii(Double2String(x*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" ")) + + Double2String(w*m_k,2) + wxString(_T(" ")) + + Double2String(-h*m_k,2) + wxString(_T(" re ")) + op); +} + +void +wxPdfDocument::RoundedRect(double x, double y, double w, double h, + double r, int roundCorner, int style) +{ + if ((roundCorner & wxPDF_CORNER_ALL) == wxPDF_CORNER_NONE) + { + // Not rounded + Rect(x, y, w, h, style); + } + else + { + // Rounded + wxString op; + // Draw a rectangle + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else + { + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILLDRAW) + { + op = _T("B"); + } + else + { + op = _T("S"); + } + } + + double myArc = 4. / 3. * (sqrt(2.) - 1.); + + OutPoint(x + r, y); + double xc = x + w - r; + double yc = y + r; + OutLine(xc, y); + + if (roundCorner & wxPDF_CORNER_TOP_LEFT) + { + OutCurve(xc + (r * myArc), yc - r, xc + r, yc - (r * myArc), xc + r, yc); + } + else + { + OutLine(x + w, y); + } + + xc = x + w - r ; + yc = y + h - r; + OutLine(x + w, yc); + + if (roundCorner & wxPDF_CORNER_TOP_RIGHT) + { + OutCurve(xc + r, yc + (r * myArc), xc + (r * myArc), yc + r, xc, yc + r); + } + else + { + OutLine(x + w, y + h); + } + + xc = x + r; + yc = y + h - r; + OutLine(xc, y + h); + + if (roundCorner & wxPDF_CORNER_BOTTOM_LEFT) + { + OutCurve(xc - (r * myArc), yc + r, xc - r, yc + (r * myArc), xc - r, yc); + } + else + { + OutLine(x, y + h); + } + + xc = x + r; + yc = y + r; + OutLine(x, yc); + + if (roundCorner & wxPDF_CORNER_BOTTOM_RIGHT) + { + OutCurve(xc - r, yc - (r * myArc), xc - (r * myArc), yc - r, xc, yc - r); + } + else + { + OutLine(x, y); + OutLine(x + r, y); + } + OutAscii(op); + } +} + +void +wxPdfDocument::Curve(double x0, double y0, double x1, double y1, + double x2, double y2, double x3, double y3, + int style) +{ + wxString op; + // Draw a rectangle + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else + { + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILLDRAW) + { + op = _T("B"); + } + else + { + op = _T("S"); + } + } + + OutPoint(x0, y0); + OutCurve(x1, y1, x2, y2, x3, y3); + OutAscii(op); +} + +void +wxPdfDocument::Ellipse(double x0, double y0, double rx, double ry, + double angle, double astart, double afinish, + int style, int nSeg) +{ + if (rx <= 0) return; + + wxString op; + // Draw a rectangle + if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else + { + if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_FILLDRAW) + { + op = _T("B"); + } + else if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_DRAWCLOSE) + { + op = _T("s"); // small 's' means closing the path as well + } + else + { + op = _T("S"); + } + } + + if (ry <= 0) + { + ry = rx; + } + rx *= m_k; + ry *= m_k; + if (nSeg < 2) + { + nSeg = 2; + } + + static double pi = 4. * atan(1.0); + astart = pi * astart / 180.; + afinish = pi * afinish / 180.; + double totalAngle = afinish - astart; + + double dt = totalAngle / nSeg; + double dtm = dt / 3; + + x0 *= m_k; + y0 = (m_h - y0) * m_k; + if (angle != 0) + { + double a = -(pi * angle / 180.); + OutAscii(wxString(_T("q ")) + + Double2String(cos(a),2) + wxString(_T(" ")) + + Double2String(-1 * sin(a),2) + wxString(_T(" ")) + + Double2String(sin(a),2) + wxString(_T(" ")) + + Double2String(cos(a),2) + wxString(_T(" ")) + + Double2String(x0,2) + wxString(_T(" ")) + + Double2String(y0,2) + wxString(_T(" cm"))); + x0 = 0; + y0 = 0; + } + + double t1, a0, b0, c0, d0, a1, b1, c1, d1; + t1 = astart; + a0 = x0 + (rx * cos(t1)); + b0 = y0 + (ry * sin(t1)); + c0 = -rx * sin(t1); + d0 = ry * cos(t1); + OutPoint(a0 / m_k, m_h - (b0 / m_k)); + int i; + for (i = 1; i <= nSeg; i++) + { + // Draw this bit of the total curve + t1 = (i * dt) + astart; + a1 = x0 + (rx * cos(t1)); + b1 = y0 + (ry * sin(t1)); + c1 = -rx * sin(t1); + d1 = ry * cos(t1); + OutCurve((a0 + (c0 * dtm)) / m_k, + m_h - ((b0 + (d0 * dtm)) / m_k), + (a1 - (c1 * dtm)) / m_k, + m_h - ((b1 - (d1 * dtm)) / m_k), + a1 / m_k, + m_h - (b1 / m_k)); + a0 = a1; + b0 = b1; + c0 = c1; + d0 = d1; + } + OutAscii(op); + if (angle !=0) + { + Out("Q"); + } +} + +void +wxPdfDocument::Circle(double x0, double y0, double r, double astart, double afinish, + int style, int nSeg) +{ + Ellipse(x0, y0, r, 0, 0, astart, afinish, style, nSeg); +} + +void +wxPdfDocument::Sector(double xc, double yc, double r, double astart, double afinish, + int style, bool clockwise, double origin) +{ + static double pi = 4. * atan(1.); + static double pi2 = 0.5 * pi; + double d; + if (clockwise) + { + d = afinish; + afinish = origin - astart; + astart = origin - d; + } + else + { + afinish += origin; + astart += origin; + } + astart = fmod(astart, 360.) + 360; + afinish = fmod(afinish, 360.) + 360; + if (astart > afinish) + { + afinish += 360; + } + afinish = afinish / 180. * pi; + astart = astart / 180. * pi; + d = afinish - astart; + if (d == 0) + { + d = 2 * pi; + } + + wxString op; + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else + { + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILLDRAW) + { + op = _T("b"); + } + else + { + op = _T("s"); + } + } + + double myArc; + if (sin(d/2) != 0.0) + { + myArc = 4./3. * (1.-cos(d/2))/sin(d/2) * r; + } + else + { + myArc = 0.0; + } + // first put the center + OutPoint(xc,yc); + // put the first point + OutLine(xc+r*cos(astart),yc-r*sin(astart)); + // draw the arc + if (d < pi2) + { + OutCurve(xc+r*cos(astart)+myArc*cos(pi2+astart), + yc-r*sin(astart)-myArc*sin(pi2+astart), + xc+r*cos(afinish)+myArc*cos(afinish-pi2), + yc-r*sin(afinish)-myArc*sin(afinish-pi2), + xc+r*cos(afinish), + yc-r*sin(afinish)); + } + else + { + afinish = astart + d/4; + myArc = 4./3. * (1.-cos(d/8))/sin(d/8) * r; + OutCurve(xc+r*cos(astart)+myArc*cos(pi2+astart), + yc-r*sin(astart)-myArc*sin(pi2+astart), + xc+r*cos(afinish)+myArc*cos(afinish-pi2), + yc-r*sin(afinish)-myArc*sin(afinish-pi2), + xc+r*cos(afinish), + yc-r*sin(afinish)); + astart = afinish; + afinish = astart + d/4; + OutCurve(xc+r*cos(astart)+myArc*cos(pi2+astart), + yc-r*sin(astart)-myArc*sin(pi2+astart), + xc+r*cos(afinish)+myArc*cos(afinish-pi2), + yc-r*sin(afinish)-myArc*sin(afinish-pi2), + xc+r*cos(afinish), + yc-r*sin(afinish)); + astart = afinish; + afinish = astart + d/4; + OutCurve(xc+r*cos(astart)+myArc*cos(pi2+astart), + yc-r*sin(astart)-myArc*sin(pi2+astart), + xc+r*cos(afinish)+myArc*cos(afinish-pi2), + yc-r*sin(afinish)-myArc*sin(afinish-pi2), + xc+r*cos(afinish), + yc-r*sin(afinish)); + astart = afinish; + afinish = astart + d/4; + OutCurve(xc+r*cos(astart)+myArc*cos(pi2+astart), + yc-r*sin(astart)-myArc*sin(pi2+astart), + xc+r*cos(afinish)+myArc*cos(afinish-pi2), + yc-r*sin(afinish)-myArc*sin(afinish-pi2), + xc+r*cos(afinish), + yc-r*sin(afinish)); + } + // terminate drawing + OutAscii(op); +} + +void +wxPdfDocument::Polygon(const wxPdfArrayDouble& x, const wxPdfArrayDouble& y, int style) +{ + int np = (x.GetCount() < y.GetCount()) ? x.GetCount() : y.GetCount(); + + wxString op; + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else + { + if ((style & wxPDF_STYLE_FILLDRAW) == wxPDF_STYLE_FILLDRAW) + { + op = _T("B"); + } + else + { + op = _T("S"); + } + } + + OutPoint(x[0], y[0]); + int i; + for (i = 1; i < np; i++) + { + OutLine(x[i], y[i]); + } + OutLine(x[0], y[0]); + OutAscii(op); +} + +void +wxPdfDocument::RegularPolygon(double x0, double y0, double r, int ns, double angle, bool circle, int style, + int circleStyle, const wxPdfLineStyle& circleLineStyle, const wxPdfColour& circleFillColor) +{ + if (ns < 3) + { + ns = 3; + } + if (circle) + { + wxPdfLineStyle saveStyle = GetLineStyle(); + SetLineStyle(circleLineStyle); + wxPdfColour saveColor = GetFillColor(); + SetFillColor(circleFillColor); + Circle(x0, y0, r, 0, 360, circleStyle); + SetLineStyle(saveStyle); + SetFillColor(saveColor); + } + static double pi = 4. * atan(1.); + double a; + wxPdfArrayDouble x, y; + int i; + for (i = 0; i < ns; i++) + { + a = (angle + (i * 360 / ns)) / 180. * pi; + x.Add(x0 + (r * sin(a))); + y.Add(y0 + (r * cos(a))); + } + Polygon(x, y, style); +} + + +void +wxPdfDocument::StarPolygon(double x0, double y0, double r, int nv, int ng, double angle, bool circle, int style, + int circleStyle, const wxPdfLineStyle& circleLineStyle, const wxPdfColour& circleFillColor) +{ + if (nv < 2) + { + nv = 2; + } + if (circle) + { + wxPdfLineStyle saveStyle = GetLineStyle(); + SetLineStyle(circleLineStyle); + wxPdfColour saveColor = GetFillColor(); + SetFillColor(circleFillColor); + Circle(x0, y0, r, 0, 360, circleStyle); + SetLineStyle(saveStyle); + SetFillColor(saveColor); + } + wxArrayInt visited; + visited.SetCount(nv); + int i; + for (i = 0; i < nv; i++) + { + visited[i] = 0; + } + static double pi = 4. * atan(1.); + double a; + wxPdfArrayDouble x, y; + i = 0; + do + { + visited[i] = 1; + a = (angle + (i * 360 / nv)) / 180. * pi; + x.Add(x0 + (r * sin(a))); + y.Add(y0 + (r * cos(a))); + i = (i + ng) % nv; + } + while (visited[i] == 0); + Polygon(x, y, style); +} + +void +wxPdfDocument::Shape(const wxPdfShape& shape, int style, bool alternativeFillRule) +{ + wxString op; + if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_FILL) + { + op = _T("f"); + } + else + { + if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_FILLDRAW) + { + op = _T("B"); + } + else if ((style & wxPDF_STYLE_MASK) == (wxPDF_STYLE_DRAWCLOSE | wxPDF_STYLE_FILL)) + { + op = _T("b"); // small 'b' means closing the path as well + } + else if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_DRAWCLOSE) + { + op = _T("s"); // small 's' means closing the path as well + } + else + { + op = _T("S"); + } + } + + //if alternative fill rule requested, add the modifier to the command if its f, b or B + if (alternativeFillRule) + { + if(op == _T("f") || op == _T("b") || op == _T("B")) + { + op += _T("*"); + } + } + + Out("q"); + + double scratch[6]; + int iterType; + int iterPoints = 0; + int segCount = shape.GetSegmentCount(); + for (iterType = 0; iterType < segCount; iterType++) + { + int segType = shape.GetSegment(iterType, iterPoints, scratch); + switch (segType) + { + case wxPDF_SEG_CLOSE: + Out("h"); + iterPoints++; + break; + case wxPDF_SEG_MOVETO: + OutPoint(scratch[0], scratch[1]); + iterPoints++; + break; + case wxPDF_SEG_LINETO: + OutLine(scratch[0], scratch[1]); + iterPoints++; + break; + case wxPDF_SEG_CURVETO: + OutCurve(scratch[0], scratch[1], scratch[2], scratch[3],scratch[4], scratch[5]); + iterPoints += 3; + break; + } + } + OutAscii(op); + Out("Q"); + +// ClosePath(style); +} + +void +wxPdfDocument::ClippingText(double x, double y, const wxString& txt, bool outline) +{ + wxString op = outline ? _T("5") : _T("7"); + OutAscii(wxString(_T("q BT ")) + + Double2String(x*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" Td ")) + + op + wxString(_T(" Tr (")),false); + TextEscape(txt,false); + Out(") Tj 0 Tr ET"); +} + +void +wxPdfDocument::ClippingRect(double x, double y, double w, double h, bool outline) +{ + wxString op = outline ? _T("S") : _T("n"); + OutAscii(wxString(_T("q ")) + + Double2String(x*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" ")) + + Double2String(w*m_k,2) + wxString(_T(" ")) + + Double2String(-h*m_k,2) + wxString(_T(" re W ")) + op); +} + +void +wxPdfDocument::ClippingEllipse(double x, double y, double rx, double ry, bool outline) +{ + wxString op = outline ? _T("S") : _T("n"); + if (ry <= 0) + { + ry = rx; + } + double lx = 4./3. * (sqrt(2.)-1.) * rx; + double ly = 4./3. * (sqrt(2.)-1.) * ry; + + OutAscii(wxString(_T("q ")) + + Double2String((x+rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" m ")) + + Double2String((x+rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y-ly))*m_k,2) + wxString(_T(" ")) + + Double2String((x+lx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y-ry))*m_k,2) + wxString(_T(" ")) + + Double2String(x*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y-ry))*m_k,2) + wxString(_T(" c"))); + + OutAscii(Double2String((x-lx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y-ry))*m_k,2) + wxString(_T(" ")) + + Double2String((x-rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y-ly))*m_k,2) + wxString(_T(" ")) + + Double2String((x-rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" c"))); + + OutAscii(Double2String((x-rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+ly))*m_k,2) + wxString(_T(" ")) + + Double2String((x-lx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+ry))*m_k,2) + wxString(_T(" ")) + + Double2String(x*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+ry))*m_k,2) + wxString(_T(" c"))); + + OutAscii(Double2String((x+lx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+ry))*m_k,2) + wxString(_T(" ")) + + Double2String((x+rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-(y+ly))*m_k,2) + wxString(_T(" ")) + + Double2String((x+rx)*m_k,2) + wxString(_T(" ")) + + Double2String((m_h-y)*m_k,2) + wxString(_T(" c W ")) + op); +} + +void +wxPdfDocument::ClippingPolygon(const wxPdfArrayDouble& x, const wxPdfArrayDouble& y, bool outline) +{ + int np = (x.GetCount() < y.GetCount()) ? x.GetCount() : y.GetCount(); + + wxString op = outline ? _T("S") : _T("n"); + + Out("q"); + OutPoint(x[0], y[0]); + int i; + for (i = 1; i < np; i++) + { + OutLine(x[i], y[i]); + } + OutLine(x[0], y[0]); + OutAscii(wxString(_T("h W ")) + op); +} + +void +wxPdfDocument::ClippingPath() +{ + Out("q"); +} + +void +wxPdfDocument::MoveTo(double x, double y) +{ + OutPoint(x, y); +} + +void +wxPdfDocument::LineTo(double x, double y) +{ + OutLine(x, y); +} + +void +wxPdfDocument::CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) +{ + OutCurve(x1, y1, x2, y2, x3, y3); +} + +void +wxPdfDocument::ClosePath(int style) +{ + wxString op; + switch (style) + { + case wxPDF_STYLE_DRAW: op = _T("S"); break; + case wxPDF_STYLE_FILL: op = _T("F"); break; + case wxPDF_STYLE_FILLDRAW: op = _T("B"); break; + default: op = _T("n"); break; + } + OutAscii(wxString(_T("h W ")) + op); +} + +void +wxPdfDocument::ClippingPath(const wxPdfShape& shape, int style) +{ + ClippingPath(); + double scratch[6]; + int iterType; + int iterPoints = 0; + int segCount = shape.GetSegmentCount(); + for (iterType = 0; iterType < segCount; iterType++) + { + int segType = shape.GetSegment(iterType, iterPoints, scratch); + switch (segType) + { + case wxPDF_SEG_CLOSE: + iterPoints++; + break; + case wxPDF_SEG_MOVETO: + MoveTo(scratch[0], scratch[1]); + iterPoints++; + break; + case wxPDF_SEG_LINETO: + LineTo(scratch[0], scratch[1]); + iterPoints++; + break; + case wxPDF_SEG_CURVETO: + CurveTo(scratch[0], scratch[1], scratch[2], scratch[3],scratch[4], scratch[5]); + iterPoints += 3; + break; + } + } + ClosePath(style); +} + +void +wxPdfDocument::UnsetClipping() +{ + Out("Q"); +} + +void +wxPdfDocument::ClippedCell(double w, double h, const wxString& txt, + int border, int ln, int align, int fill, const wxPdfLink& link) +{ + if ((border != wxPDF_BORDER_NONE) || (fill != 0) || (m_y+h > m_pageBreakTrigger)) + { + Cell(w, h, _T(""), border, 0, wxPDF_ALIGN_LEFT, fill); + m_x -= w; + } + ClippingRect(m_x, m_y, w, h); + Cell(w, h, txt, wxPDF_BORDER_NONE, ln, align, 0, link); + UnsetClipping(); +} + +void +wxPdfDocument::SetLineStyle(const wxPdfLineStyle& linestyle) +{ + m_lineStyle = linestyle; + if (linestyle.GetWidth() >= 0) + { + double width_prev = m_lineWidth; + SetLineWidth(linestyle.GetWidth()); + m_lineWidth = width_prev; + } + switch (linestyle.GetLineCap()) + { + case wxPDF_LINECAP_BUTT: + case wxPDF_LINECAP_ROUND: + case wxPDF_LINECAP_SQUARE: + OutAscii(wxString::Format(_T("%d J"), linestyle.GetLineCap())); + break; + default: + break; + } + switch (linestyle.GetLineJoin()) + { + case wxPDF_LINEJOIN_MITER: + case wxPDF_LINEJOIN_ROUND: + case wxPDF_LINEJOIN_BEVEL: + OutAscii(wxString::Format(_T("%d j"), linestyle.GetLineJoin())); + break; + default: + break; + } + + if ( (linestyle.GetLineJoin() == wxPDF_LINEJOIN_MITER) && (linestyle.GetMiterLimit() != 0.0) ) + { + OutAscii(wxString::Format(_T("%f M"), linestyle.GetMiterLimit())); + } + + const wxPdfArrayDouble& dash = linestyle.GetDash(); + if (&dash != NULL) + { + wxString dashString = _T(""); + size_t j; + for (j = 0; j < dash.GetCount(); j++) + { + if (j > 0) + { + dashString += wxString(_T(" ")); + } + dashString += Double2String(dash[j],2); + } + double phase = linestyle.GetPhase(); + if (phase < 0 || dashString.Length() == 0) + { + phase = 0; + } + OutAscii(wxString(_T("[")) + dashString + wxString(_T("] ")) + + Double2String(phase,2) + wxString(_T(" d"))); + } + SetDrawColor(linestyle.GetColour()); +} + +const wxPdfLineStyle& +wxPdfDocument::GetLineStyle() +{ + return m_lineStyle; +} + +void +wxPdfDocument::StartTransform() +{ + //save the current graphic state + m_inTransform++; + Out("q"); +} + +bool +wxPdfDocument::ScaleX(double sx, double x, double y) +{ + return Scale(sx, 100, x, y); +} + +bool +wxPdfDocument::ScaleY(double sy, double x, double y) +{ + return Scale(100, sy, x, y); +} + +bool +wxPdfDocument::ScaleXY(double s, double x, double y) +{ + return Scale(s, s, x, y); +} + +bool +wxPdfDocument::Scale(double sx, double sy, double x, double y) +{ + if (x < 0) + { + x = m_x; + } + if (y < 0) + { + y = m_y; + } + if (sx == 0 || sy == 0) + { + wxLogError(_T("wxPdfDocument::Scale: Please use values unequal to zero for Scaling.")); + return false; + } + y = (m_h - y) * m_k; + x *= m_k; + //calculate elements of transformation matrix + sx /= 100; + sy /= 100; + double tm[6]; + tm[0] = sx; + tm[1] = 0; + tm[2] = 0; + tm[3] = sy; + tm[4] = x * (1 - sx); + tm[5] = y * (1 - sy); + //scale the coordinate system + if (m_inTransform == 0) + { + StartTransform(); + } + Transform(tm); + return true; +} + +void +wxPdfDocument::MirrorH(double x) +{ + Scale(-100, 100, x); +} + +void +wxPdfDocument::MirrorV(double y) +{ + Scale(100, -100, -1, y); +} + +void +wxPdfDocument::TranslateX(double tx) +{ + Translate(tx, 0); +} + +void +wxPdfDocument::TranslateY(double ty) +{ + Translate(0, ty); +} + +void +wxPdfDocument::Translate(double tx, double ty) +{ + if (m_inTransform == 0) + { + StartTransform(); + } + // calculate elements of transformation matrix + double tm[6]; + tm[0] = 1; + tm[1] = 0; + tm[2] = 0; + tm[3] = 1; + tm[4] = tx; + tm[5] = -ty; + // translate the coordinate system + Transform(tm); +} + +void +wxPdfDocument::Rotate(double angle, double x, double y) +{ + if (m_inTransform == 0) + { + StartTransform(); + } + if (x < 0) + { + x = m_x; + } + if (y < 0) + { + y = m_y; + } + y = (m_h - y) * m_k; + x *= m_k; + // calculate elements of transformation matrix + double tm[6]; + angle *= (atan(1.) / 45.); + tm[0] = cos(angle); + tm[1] = sin(angle); + tm[2] = -tm[1]; + tm[3] = tm[0]; + tm[4] = x + tm[1] * y - tm[0] * x; + tm[5] = y - tm[0] * y - tm[1] * x; + //rotate the coordinate system around ($x,$y) + Transform(tm); +} + +bool +wxPdfDocument::SkewX(double xAngle, double x, double y) +{ + return Skew(xAngle, 0, x, y); +} + +bool +wxPdfDocument::SkewY(double yAngle, double x, double y) +{ + return Skew(0, yAngle, x, y); +} + +bool +wxPdfDocument::Skew(double xAngle, double yAngle, double x, double y) +{ + if (x < 0) + { + x = m_x; + } + if (y < 0) + { + y = m_y; + } + if (xAngle <= -90 || xAngle >= 90 || yAngle <= -90 || yAngle >= 90) + { + wxLogError(_T("wxPdfDocument::Skew: Please use values between -90 and 90 degree for skewing.")); + return false; + } + x *= m_k; + y = (m_h - y) * m_k; + //calculate elements of transformation matrix + double tm[6]; + xAngle *= (atan(1.) / 45.); + yAngle *= (atan(1.) / 45.); + tm[0] = 1; + tm[1] = tan(yAngle); + tm[2] = tan(xAngle); + tm[3] = 1; + tm[4] = -tm[2] * y; + tm[5] = -tm[1] * x; + //skew the coordinate system + if (m_inTransform == 0) + { + StartTransform(); + } + Transform(tm); + return true; +} + +void +wxPdfDocument::StopTransform() +{ + //restore previous graphic state + if (m_inTransform > 0) + { + m_inTransform--; + Out("Q"); + + //hack to make next font change work, see https://sourceforge.net/tracker/?func=detail&atid=462816&aid=1861202&group_id=51305 + m_fontFamily = wxEmptyString; + //hack to make colour change work, related to same bug as above + m_fillColor = wxPdfColour(1,2,3); + } +} + +static bool +ColorSpaceOk(const wxPdfColour& col1, const wxPdfColour& col2) +{ + return (col1.GetColorType() != wxPDF_COLOURTYPE_SPOT && + col1.GetColorType() == col2.GetColorType()); +} + +int +wxPdfDocument::LinearGradient(const wxPdfColour& col1, const wxPdfColour& col2, + wxPdfLinearGradientType gradientType) +{ + static double h[] = { 0, 0, 1, 0 }; + static double v[] = { 0, 0, 0, 1 }; + wxPdfGradient* gradient; + + int n = 0; + if (ColorSpaceOk(col1, col2)) + { + switch (gradientType) + { + case wxPDF_LINEAR_GRADIENT_REFLECTION_TOP: + gradient = new wxPdfMidAxialGradient(col1, col2, v[0], v[1], v[2], v[3], 0.67, 0.7); + break; + case wxPDF_LINEAR_GRADIENT_REFLECTION_BOTTOM: + gradient = new wxPdfMidAxialGradient(col1, col2, v[0], v[1], v[2], v[3], 0.33, 0.7); + break; + case wxPDF_LINEAR_GRADIENT_REFLECTION_LEFT: + gradient = new wxPdfMidAxialGradient(col1, col2, h[0], h[1], h[2], h[3], 0.33, 0.7); + break; + case wxPDF_LINEAR_GRADIENT_REFLECTION_RIGHT: + gradient = new wxPdfMidAxialGradient(col1, col2, h[0], h[1], h[2], h[3], 0.67, 0.7); + break; + case wxPDF_LINEAR_GRADIENT_MIDVERTICAL: + gradient = new wxPdfMidAxialGradient(col1, col2, v[0], v[1], v[2], v[3], 0.5, 1); + break; + case wxPDF_LINEAR_GRADIENT_MIDHORIZONTAL: + gradient = new wxPdfMidAxialGradient(col1, col2, h[0], h[1], h[2], h[3], 0.5, 1); + break; + case wxPDF_LINEAR_GRADIENT_VERTICAL: + gradient = new wxPdfAxialGradient(col1, col2, v[0], v[1], v[2], v[3], 1); + break; + case wxPDF_LINEAR_GRADIENT_HORIZONTAL: + default: + gradient = new wxPdfAxialGradient(col1, col2, h[0], h[1], h[2], h[3], 1); + break; + } + n = (*m_gradients).size()+1; + (*m_gradients)[n] = gradient; + } + else + { + wxLogError(_("wxPdfDocument::LinearGradient: Color spaces do not match.")); + } + return n; +} + +int +wxPdfDocument::AxialGradient(const wxPdfColour& col1, const wxPdfColour& col2, + double x1, double y1, double x2, double y2, + double intexp) +{ + int n = 0; + if (ColorSpaceOk(col1, col2)) + { + n = (*m_gradients).size()+1; + (*m_gradients)[n] = new wxPdfAxialGradient(col1, col2, x1, y1, x2, y2, intexp); + } + else + { + wxLogError(_("wxPdfDocument::LinearGradient: Color spaces do not match.")); + } + return n; +} + +int +wxPdfDocument::MidAxialGradient(const wxPdfColour& col1, const wxPdfColour& col2, + double x1, double y1, double x2, double y2, + double midpoint, double intexp) +{ + int n = 0; + if (ColorSpaceOk(col1, col2)) + { + n = (*m_gradients).size()+1; + (*m_gradients)[n] = new wxPdfMidAxialGradient(col1, col2, x1, y1, x2, y2, midpoint, intexp); + } + else + { + wxLogError(_("wxPdfDocument::LinearGradient: Color spaces do not match.")); + } + return n; +} + +int +wxPdfDocument::RadialGradient(const wxPdfColour& col1, const wxPdfColour& col2, + double x1, double y1, double r1, + double x2, double y2, double r2, double intexp) +{ + int n = 0; + if (ColorSpaceOk(col1, col2)) + { + n = (*m_gradients).size()+1; + (*m_gradients)[n] = new wxPdfRadialGradient(col1, col2, x1, y1, r1, x2, y2, r2, intexp); + } + else + { + wxLogError(_("wxPdfDocument::RadialGradient: Color spaces do not match.")); + } + return n; +} + +int +wxPdfDocument::CoonsPatchGradient(const wxPdfCoonsPatchMesh& mesh, double minCoord, double maxCoord) +{ + int n = 0; + if (mesh.Ok()) + { + n = (*m_gradients).size()+1; + (*m_gradients)[n] = new wxPdfCoonsPatchGradient(mesh, minCoord, maxCoord); + } + else + { + wxLogError(_("wxPdfDocument::CoonsPatchGradient: Mesh is invalid.")); + } + return n; +} + +/* draw a marker at a raw point-based coordinate */ +void +wxPdfDocument::Marker(double x, double y, wxPdfMarker markerType, double size) +{ + double saveLineWidth = m_lineWidth; + double halfsize = size * 0.5; + static double b = 4. / 3.; + + Out("q"); + switch (markerType) + { + case wxPDF_MARKER_CIRCLE: + SetLineWidth(size * 0.15); + OutPoint(x - halfsize, y); + OutCurve(x - halfsize, y + b * halfsize, x + halfsize, y + b * halfsize, x + halfsize, y); + OutCurve(x + halfsize, y - b * halfsize, x - halfsize, y - b * halfsize, x - halfsize, y); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_TRIANGLE_UP: + SetLineWidth(size * 0.15); + OutPoint(x, y - size * 0.6667); + OutLineRelative(-size / 1.7321, size); + OutLineRelative(1.1546 * size, 0.0); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_TRIANGLE_DOWN: + SetLineWidth(size * 0.15); + OutPoint(x, y + size * 0.6667); + OutLineRelative(-size / 1.7321, -size); + OutLineRelative(1.1546 * size, 0.0); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_TRIANGLE_LEFT: + SetLineWidth(size * 0.15); + OutPoint(x - size * 0.6667, y); + OutLineRelative(size, -size / 1.7321); + OutLineRelative(0.0, 1.1546 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_TRIANGLE_RIGHT: + SetLineWidth(size * 0.15); + OutPoint(x + size * 0.6667, y); + OutLineRelative(-size, -size / 1.7321); + OutLineRelative(0.0, 1.1546 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_DIAMOND: + SetLineWidth(size * 0.15); + size *= 0.9; + OutPoint( x, y+size/1.38); + OutLineRelative( 0.546 * size, -size / 1.38); + OutLineRelative(-0.546 * size, -size / 1.38); + OutLineRelative(-0.546 * size, size / 1.38); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_SQUARE: + SetLineWidth(size * 0.15); + Rect(x - halfsize, y - halfsize, size, size, wxPDF_STYLE_FILLDRAW); + Out("B"); + break; + case wxPDF_MARKER_STAR: + size *= 1.2; + halfsize = 0.5 * size; + SetLineWidth(size * 0.09); + OutPoint(x, y + size * 0.5); + OutLine(x + 0.112255 * size, y + 0.15451 * size); + OutLine(x + 0.47552 * size, y + 0.15451 * size); + OutLine(x + 0.181635 * size, y - 0.05902 * size); + OutLine(x + 0.29389 * size, y - 0.40451 * size); + OutLine(x, y - 0.19098 * size); + OutLine(x - 0.29389 * size, y - 0.40451 * size); + OutLine(x - 0.181635 * size, y - 0.05902 * size); + OutLine(x - 0.47552 * size, y + 0.15451 * size); + OutLine(x - 0.112255 * size, y + 0.15451 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_STAR4: + size *= 1.2; + halfsize = 0.5 * size; + SetLineWidth(size * 0.09); + OutPoint(x, y + size * 0.5); + OutLine(x + 0.125 * size, y + 0.125 * size); + OutLine(x + size * 0.5, y); + OutLine(x + 0.125 * size, y - 0.125 * size); + OutLine(x, y - size * 0.5); + OutLine(x - 0.125 * size, y - 0.125 * size); + OutLine(x - size * 0.5, y); + OutLine(x - 0.125 * size, y + 0.125 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_PLUS: + size *= 1.2; + halfsize = 0.5 * size; + SetLineWidth(size * 0.1); + OutPoint(x + 0.125 * size, y + size * 0.5); + OutLine(x + 0.125 * size, y + 0.125 * size); + OutLine(x + size * 0.5, y + 0.125 * size); + OutLine(x + size * 0.5, y - 0.125 * size); + OutLine(x + 0.125 * size, y - 0.125 * size); + OutLine(x + 0.125 * size, y - size * 0.5); + OutLine(x - 0.125 * size, y - size * 0.5); + OutLine(x - 0.125 * size, y - 0.125 * size); + OutLine(x - size * 0.5, y - 0.125 * size); + OutLine(x - size * 0.5, y + 0.125 * size); + OutLine(x - 0.125 * size, y + 0.125 * size); + OutLine(x - 0.125 * size, y + size * 0.5); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_CROSS: + size *= 1.2; + halfsize = 0.5 * size; + SetLineWidth(size * 0.1); + OutPoint(x, y + 0.176777 * size); + OutLine(x + 0.265165 * size, y + 0.441941 * size); + OutLine(x + 0.441941 * size, y + 0.265165 * size); + OutLine(x + 0.176777 * size, y); + OutLine(x + 0.441941 * size, y - 0.265165 * size); + OutLine(x + 0.265165 * size, y - 0.441941 * size); + OutLine(x, y - 0.176777 * size); + OutLine(x - 0.265165 * size, y - 0.441941 * size); + OutLine(x - 0.441941 * size, y - 0.265165 * size); + OutLine(x - 0.176777 * size, y); + OutLine(x - 0.441941 * size, y + 0.265165 * size); + OutLine(x - 0.265165 * size, y + 0.441941 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_PENTAGON_UP: + SetLineWidth(size * 0.15); + OutPoint(x + 0.5257 * size, y - size * 0.1708); + OutLineRelative(-0.5257 * size, -0.382 * size); + OutLineRelative(-0.5257 * size, 0.382 * size); + OutLineRelative(0.2008 * size, 0.6181 * size); + OutLineRelative(0.6499 * size, 0.0); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_PENTAGON_DOWN: + SetLineWidth(size * 0.15); + OutPoint(x - 0.5257 * size, y + size * 0.1708); + OutLineRelative( 0.5257 * size, 0.382 * size); + OutLineRelative( 0.5257 * size, -0.382 * size); + OutLineRelative(-0.2008 * size, -0.6181 * size); + OutLineRelative(-0.6499 * size, 0.0); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_PENTAGON_LEFT: + SetLineWidth(size * 0.15); + OutPoint(x - size * 0.1708, y + 0.5257 * size); + OutLineRelative(-0.382 * size, -0.5257 * size); + OutLineRelative( 0.382 * size, -0.5257 * size); + OutLineRelative( 0.6181 * size, 0.2008 * size); + OutLineRelative( 0.0, 0.6499 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_PENTAGON_RIGHT: + SetLineWidth(size * 0.15); + OutPoint(x + size * 0.1708, y - 0.5257 * size); + OutLineRelative( 0.382 * size, 0.5257 * size); + OutLineRelative(-0.382 * size, 0.5257 * size); + OutLineRelative(-0.6181 * size, -0.2008 * size); + OutLineRelative( 0.0, -0.6499 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_BOWTIE_HORIZONTAL: + SetLineWidth(size * 0.13); + OutPoint(x - 0.5 * size, y - 0.5 * size); + OutLine(x + 0.5 * size, y + 0.5 * size); + OutLine(x + 0.5 * size, y - 0.5 * size); + OutLine(x - 0.5 * size, y + 0.5 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_BOWTIE_VERTICAL: + SetLineWidth(size * 0.13); + OutPoint(x - 0.5 * size, y - 0.5 * size); + OutLine(x + 0.5 * size, y + 0.5 * size); + OutLine(x - 0.5 * size, y + 0.5 * size); + OutLine(x + 0.5 * size, y - 0.5 * size); + Out("h"); + Out("B"); + break; + case wxPDF_MARKER_ASTERISK: + size *= 1.05; + SetLineWidth(size * 0.15); + OutPoint( x, y + size * 0.5); + OutLineRelative(0.0, -size); + OutPoint( x + 0.433 * size, y + 0.25 * size); + OutLine(x - 0.433 * size, y - 0.25 * size); + OutPoint(x + 0.433 * size, y - 0.25 * size); + OutLine(x - 0.433 * size, y + 0.25 * size); + Out("S"); + break; + case wxPDF_MARKER_SUN: + SetLineWidth(size * 0.15); + halfsize = size * 0.25; + OutPoint(x - halfsize, y); + OutCurve(x - halfsize, y + b * halfsize, x + halfsize, y + b * halfsize, x + halfsize, y); + OutCurve(x + halfsize, y - b * halfsize, x - halfsize, y - b * halfsize, x - halfsize, y); + Out("h"); + OutPoint(x + size * 0.5, y); + OutLine(x + size * 0.25, y); + OutPoint(x - size * 0.5, y); + OutLine(x - size * 0.25, y); + OutPoint(x, y - size * 0.5); + OutLine(x, y - size * 0.25); + OutPoint(x, y + size * 0.5); + OutLine(x, y + size * 0.25); + Out("B"); + break; + + default: + break; + } + Out("Q"); + m_x = x; + m_y = y; + SetLineWidth(saveLineWidth); +} + +void +wxPdfDocument::Arrow(double x1, double y1, double x2, double y2, double linewidth, double height, double width) +{ + double saveLineWidth = m_lineWidth; + double dx = x2 - x1; + double dy = y2 - y1; + double dz = sqrt (dx*dx+dy*dy); + double sina = dy / dz; + double cosa = dx / dz; + double x3 = x2 - cosa * height + sina * width; + double y3 = y2 - sina * height - cosa * width; + double x4 = x2 - cosa * height - sina * width; + double y4 = y2 - sina * height + cosa * width; + + SetLineWidth(0.2); + + //Draw a arrow head + OutAscii(Double2String( x2*m_k,2) + wxString(_T(" ")) + + Double2String( (m_h-y2)*m_k,2) + wxString(_T(" m ")) + + Double2String( x3*m_k,2) + wxString(_T(" ")) + + Double2String( (m_h-y3)*m_k,2) + wxString(_T(" l ")) + + Double2String( x4*m_k,2) + wxString(_T(" ")) + + Double2String( (m_h-y4)*m_k,2) + wxString(_T(" l b"))); + + SetLineWidth(linewidth); + Line(x1+cosa*linewidth, y1+sina*linewidth, x2-cosa*height, y2-sina*height); + SetLineWidth(saveLineWidth); +} diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfimage.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfimage.cpp new file mode 100644 index 000000000..3fe2c84f2 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfimage.cpp @@ -0,0 +1,1221 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfimage.cpp +// Purpose: Implementation of wxPdfImage classes +// Author: Ulrich Telle +// Modified by: +// Created: 2005-08-11 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfimage.cpp Implementation of the wxPdfImage class + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#include +#include +#include +#include +#include + +#include "wx/pdfdoc.h" +#include "wx/pdfimage.h" + +wxFileSystem* wxPdfImage::ms_fileSystem = NULL; + +wxFileSystem* +wxPdfImage::GetFileSystem() +{ + if (ms_fileSystem == NULL) + { + static wxFileSystem fileSystem; + //I dont know why this needs to be added manually, all the documents indicate that nothing needs to be done for local files. + fileSystem.AddHandler(new wxLocalFSHandler()); + ms_fileSystem = &fileSystem; + } + return ms_fileSystem; +} + +// ---------------------------------------------------------------------------- +// wxPdfImage: class representing image objects +// ---------------------------------------------------------------------------- + +wxPdfImage::wxPdfImage(wxPdfDocument* document, int index, const wxString& filename, const wxString& type) +{ + m_document = document; + m_index = index; + m_name = filename; + m_maskImage = 0; + m_isFormObj = false; + m_fromWxImage = false; + m_validWxImage = false; + + m_width = 0; + m_height = 0; + m_cs = _T(""); + m_bpc = '\0'; + m_f = _T(""); + m_parms = _T(""); + + m_palSize = 0; + m_pal = NULL; + m_trnsSize = 0; + m_trns = NULL; + m_dataSize = 0; + m_data = NULL; + + wxString fileURL = m_name; + wxURI uri(m_name); + if (!uri.HasScheme()) + { + fileURL = wxFileSystem::FileNameToURL(m_name); + } + m_imageFile = wxPdfImage::GetFileSystem()->OpenFile(fileURL); + wxString mimeType = m_imageFile->GetMimeType(); + m_type = (mimeType != wxEmptyString) ? mimeType : type.Lower(); + m_imageStream = (m_imageFile != NULL) ? m_imageFile->GetStream() : NULL; +} + +wxPdfImage::wxPdfImage(wxPdfDocument* document, int index, const wxString& name, const wxImage& image) +{ + m_document = document; + m_index = index; + m_name = name; + m_maskImage = 0; + m_isFormObj = false; + m_fromWxImage = true; + + m_width = 0; + m_height = 0; + m_cs = _T(""); + m_bpc = '\0'; + m_f = _T(""); + m_parms = _T(""); + + m_palSize = 0; + m_pal = NULL; + m_trnsSize = 0; + m_trns = NULL; + m_dataSize = 0; + m_data = NULL; + + m_validWxImage = ConvertWxImage(image); + + m_imageFile = NULL; + m_imageStream = NULL; +} + +wxPdfImage::wxPdfImage(wxPdfDocument* document, int index, const wxString& name, wxInputStream& stream, const wxString& mimeType) +{ + m_document = document; + m_index = index; + m_name = name; + m_maskImage = 0; + m_isFormObj = false; + m_fromWxImage = false; + m_validWxImage = false; + + m_width = 0; + m_height = 0; + m_cs = _T(""); + m_bpc = '\0'; + m_f = _T(""); + m_parms = _T(""); + + m_palSize = 0; + m_pal = NULL; + m_trnsSize = 0; + m_trns = NULL; + m_dataSize = 0; + m_data = NULL; + + m_imageFile = NULL; + m_type = mimeType; + m_imageStream = &stream; +} + +wxPdfImage::~wxPdfImage() +{ + if (m_pal != NULL) delete [] m_pal; + if (m_trns != NULL) delete [] m_trns; + if (m_data != NULL) delete [] m_data; +} + +bool +wxPdfImage::ConvertWxImage(const wxImage& image) +{ + bool isValid = false; + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL) + { + wxImage::AddHandler(new wxPNGHandler()); + } + wxMemoryOutputStream os; + isValid = image.SaveFile(os, wxBITMAP_TYPE_PNG); + if (isValid) + { + wxMemoryInputStream is(os); + m_type = _T("png"); + isValid = ParsePNG(&is); + } + return isValid; +} + +bool +wxPdfImage::Parse() +{ + // Check whether this image originated from an wxImage and is valid + if (m_fromWxImage) return m_validWxImage; + + bool isValid = false; + + if (m_imageStream) + { + if (m_type == _T("image/png") || m_type == _T("png")) + { + isValid = ParsePNG(m_imageStream); + } + else if (m_type == _T("image/jpeg") || m_type == _T("jpeg") || m_type == _T("jpg")) + { + isValid = ParseJPG(m_imageStream); + } + else if (m_type == _T("image/gif") || m_type == _T("gif")) + { + isValid = ParseGIF(m_imageStream); + } + else + { + if (m_type == _T("image/wmf") || m_type == _T("wmf") || m_name.Right(2) == _T(".wmf")) + { + m_isFormObj = true; + isValid = ParseWMF(m_imageStream); + } + } + if (m_imageFile != NULL) + { + delete m_imageFile; + m_imageFile = NULL; + } + } + return isValid; +} + +// --- Parse PNG image file --- + +bool +wxPdfImage::ParsePNG(wxInputStream* imageStream) +{ + bool isValid = false; + + // Check signature + char buffer[8]; + imageStream->Read(buffer,8); + if (strncmp(buffer,"\x89PNG\x0D\x0A\x1A\x0A",8) != 0) + { + // Not a PNG file + wxLogDebug(_T("wxPdfImage::ParsePNG: '%s' not a PNG file."), m_name.c_str()); + return false; + } + + // Read header chunk + imageStream->Read(buffer,4); + imageStream->Read(buffer,4); + if (strncmp(buffer,"IHDR",4) != 0) + { + // Incorrect PNG file + wxLogDebug(_T("wxPdfImage::ParsePNG: Incorrect PNG file '%s'."), m_name.c_str()); + return false; + } + + int w = ReadIntBE(imageStream); + int h = ReadIntBE(imageStream); + + imageStream->Read(buffer,1); + char bpc = buffer[0]; + if (bpc > 8) + { + // 16-bit depth not supported + wxLogDebug(_T("wxPdfImage::ParsePNG: 16-bit depth not supported: '%s'."), m_name.c_str()); + return false; + } + + wxString colspace = wxEmptyString; + imageStream->Read(buffer,1); + char ct = buffer[0]; + if (ct == 0) + { + colspace = _T("DeviceGray"); + } + else if (ct == 2) + { + colspace = _T("DeviceRGB"); + } + else if (ct == 3) + { + colspace = _T("Indexed"); + } + else + { + // Alpha channel not supported + wxLogDebug(_T("wxPdfImage::ParsePNG: Alpha channel not supported: '%s'."), m_name.c_str()); + return false; + } + + imageStream->Read(buffer,3); + if (buffer[0] != 0) + { + // Unknown compression method + wxLogDebug(_T("wxPdfImage::ParsePNG: Unknown compression method: '%s'."), m_name.c_str()); + return false; + } + if (buffer[1] != 0) + { + // Unknown filter method + wxLogDebug(_T("wxPdfImage::ParsePNG: Unknown filter method: '%s'."), m_name.c_str()); + return false; + } + if (buffer[2] != 0) + { + // Interlacing not supported + wxLogDebug(_T("wxPdfImage::ParsePNG: Interlacing not supported: '%s'."), m_name.c_str()); + return false; + } + + imageStream->Read(buffer,4); + m_parms = wxString::Format(_T("/DecodeParms <>"), (ct==2 ? 3 : 1), bpc, w); + + // Scan chunks looking for palette, transparency and image data + m_palSize = 0; + m_pal = NULL; + m_trnsSize = 0; + m_trns = NULL; + m_dataSize = 0; + m_data = NULL; + int n; + do + { + n = ReadIntBE(imageStream); + imageStream->Read(buffer,4); + if (strncmp(buffer,"PLTE",4) == 0) + { + // Read palette + m_palSize = n; + m_pal = new char[n]; + imageStream->Read(m_pal,n); + imageStream->Read(buffer,4); + } + else if (strncmp(buffer,"tRNS",4) == 0) + { + // Read transparency info + char* t = new char[n]; + imageStream->Read(t,n); + if (ct == 0) + { + m_trnsSize = 1; + m_trns = new char[1]; + m_trns[0] = t[1]; + } + else if (ct == 2) + { + m_trnsSize = 3; + m_trns = new char[3]; + m_trns[0] = t[1]; + m_trns[1] = t[3]; + m_trns[2] = t[5]; + } + else + { + int pos; + for (pos = 0; (pos < n) && (t[pos] != 0); pos++); + if (pos < n) + { + m_trnsSize = 1; + m_trns = new char[1]; + m_trns[0] = pos; + } + } + imageStream->Read(buffer,4); + delete [] t; + } + else if (strncmp(buffer,"IDAT",4) == 0) + { + // Read image data block + int prevSize = m_dataSize; + char* prevData = m_data; + m_dataSize += n; + m_data = new char[m_dataSize]; + if (prevSize > 0) + { + memcpy(m_data, prevData, prevSize); + delete [] prevData; + } + imageStream->Read(m_data+prevSize,n); + imageStream->Read(buffer,4); + } + else if (strncmp(buffer,"IEND",4) == 0) + { + break; + } + else + { + char* temp = new char[n]; + imageStream->Read(temp,n); + delete [] temp; + imageStream->Read(buffer,4); + } + } + while (n); + + if (colspace == _T("Indexed") && m_pal == NULL) + { + if (m_pal != NULL) delete [] m_pal; + if (m_trns != NULL) delete [] m_trns; + if (m_data != NULL) delete [] m_data; + // Missing palette + wxLogDebug(_T("wxPdfImage::ParsePNG: Missing palette: '%s'."), m_name.c_str()); + return false; + } + + m_width = w; + m_height = h; + m_cs = colspace; + m_bpc = bpc; + m_f = _T("FlateDecode"); + + isValid = true; + return isValid; +} + +//--- Parse JPEG image file + +// some defines for the different JPEG block types + +#define M_SOF0 0xC0 // Start Of Frame N +#define M_SOF1 0xC1 // N indicates which compression process +#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use +#define M_SOF3 0xC3 +#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers +#define M_SOF6 0xC6 +#define M_SOF7 0xC7 +#define M_SOF9 0xC9 +#define M_SOF10 0xCA +#define M_SOF11 0xCB +#define M_SOF13 0xCD +#define M_SOF14 0xCE +#define M_SOF15 0xCF +#define M_SOI 0xD8 +#define M_EOI 0xD9 // End Of Image (end of datastream) +#define M_SOS 0xDA // Start Of Scan (begins compressed data) +#define M_COM 0xFE // COMment + +#define M_PSEUDO 0xFFD8 // pseudo marker for start of image(byte 0) + +bool +wxPdfImage::ParseJPG(wxInputStream* imageStream) +{ + bool isValid = false; + wxString colspace = _T(""); + + m_palSize = 0; + m_pal = NULL; + m_trnsSize = 0; + m_trns = NULL; + m_dataSize = 0; + m_data = NULL; + + unsigned char buffer[3]; + imageStream->Read(buffer,3); + if (strncmp((const char*) buffer,"\xff\xd8\xff",3) != 0) + { + // Not a JPEG file + wxLogDebug(_T("wxPdfImage::ParseJPG: '%s' not a JPEG file."), m_name.c_str()); + return false; + } + + // Extract info from a JPEG file + unsigned int marker = M_PSEUDO; + unsigned short length, ffRead = 1; + unsigned char bits = 0; + unsigned short height = 0; + unsigned short width = 0; + unsigned char channels = 0; + + + bool ready = false; + int lastMarker; + int commentCorrection; + int a; + while (!ready) + { + lastMarker = marker; + commentCorrection = 1; + a = 0; + + // get marker byte, swallowing possible padding + if (lastMarker == M_COM && commentCorrection) + { + // some software does not count the length bytes of COM section + // one company doing so is very much envolved in JPEG... so we accept too + // by the way: some of those companies changed their code now... + commentCorrection = 2; + } + else + { + lastMarker = 0; + commentCorrection = 0; + } + if (ffRead) + { + a = 1; // already read 0xff in filetype detection + } + do + { + imageStream->Read(buffer,1); + if (imageStream->Eof()) + { + marker = M_EOI; // we hit EOF + break; + } + marker = buffer[0]; + if (lastMarker == M_COM && commentCorrection > 0) + { + if (marker != 0xFF) + { + marker = 0xff; + commentCorrection--; + } + else + { + lastMarker = M_PSEUDO; // stop skipping non 0xff for M_COM + } + } + if (++a > 10) + { + // who knows the maxim amount of 0xff? though 7 + // but found other implementations + marker = M_EOI; + break; + } + } + while (marker == 0xff); + + if (a < 2) + { + marker = M_EOI; // at least one 0xff is needed before marker code + } + if (lastMarker == M_COM && commentCorrection) + { + marker = M_EOI; // ah illegal: char after COM section not 0xFF + } + + ffRead = 0; + switch (marker) + { + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + // handle SOFn block + length = ReadUShortBE(imageStream); + imageStream->Read(&bits,1); + height = ReadUShortBE(imageStream); + width = ReadUShortBE(imageStream); + imageStream->Read(&channels,1); + isValid = true; + ready = true; + break; + + case M_SOS: + case M_EOI: + isValid = false; + ready = true; + + default: + { + // anything else isn't interesting + off_t pos = (unsigned int) ReadUShortBE(imageStream); + pos = pos-2; + if (pos) + { + imageStream->SeekI(pos, wxFromCurrent); + } + } + break; + } + } + + if (isValid) + { + if (channels == 3) + { + colspace = _T("DeviceRGB"); + } + else if(channels == 4) + { + colspace = _T("DeviceCMYK"); + } + else + { + colspace = _T("DeviceGray"); + } + m_bpc = bits; + + //Read whole file + imageStream->SeekI(0); + m_dataSize = imageStream->GetLength(); + m_data = new char[m_dataSize]; + imageStream->Read(m_data,m_dataSize); + + m_width = width; + m_height = height; + m_cs = colspace; + m_bpc = bits; + m_f = _T("DCTDecode"); + } + + return isValid; +} + +// --- Parse GIF image file --- + +bool +wxPdfImage::ParseGIF(wxInputStream* imageStream) +{ + bool isValid = false; + + m_palSize = 0; + m_pal = NULL; + m_trnsSize = 0; + m_trns = NULL; + m_dataSize = 0; + m_data = NULL; + +#if wxCHECK_VERSION(2,7,1) + wxGIFDecoder gif; + if (!gif.CanRead(*imageStream)) + { + wxLogDebug(_T("wxPdfImage::ParseGIF: '%s' not a GIF file."), m_name.c_str()); + return false; + } + + if (gif.LoadGIF(*imageStream) != wxGIF_OK) + { + wxLogDebug(_T("wxPdfImage::ParseGIF: Invalid GIF file '%s'."), m_name.c_str()); + return false; + } + + isValid = true; + wxSize gifSize = gif.GetFrameSize(0); + m_width = gifSize.GetWidth(); + m_height = gifSize.GetHeight(); + m_cs = _T("Indexed"); + m_bpc = 8; + + m_palSize = 768; + m_pal = new char[m_palSize]; + memcpy(m_pal,gif.GetPalette(0),m_palSize); + + int trns = gif.GetTransparentColourIndex(0); + if (trns != -1) + { + m_trnsSize = 3; + m_trns = new char[3]; + m_trns[0] = m_pal[3*trns + 0]; + m_trns[1] = m_pal[3*trns + 1]; + m_trns[2] = m_pal[3*trns + 2]; + } + + m_dataSize = m_width * m_height; + if (m_document->m_compress) + { + m_f = _T("FlateDecode"); + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + wxZlibOutputStream q(*p); + q.Write(gif.GetData(0),m_dataSize); + q.Close(); + m_dataSize = p->TellO(); + m_data = new char[m_dataSize]; + p->CopyTo(m_data,m_dataSize); + delete p; + } + else + { + m_f = _T(""); + m_data = new char[m_dataSize]; + memcpy(m_data,gif.GetData(0),m_dataSize); + } +#else + wxGIFDecoder gif(imageStream); + if (!gif.CanRead()) + { + wxLogDebug(_T("wxPdfImage::ParseGIF: '%s' not a GIF file."), m_name.c_str()); + return false; + } + + if (gif.ReadGIF() != wxGIF_OK) + { + wxLogDebug(_T("wxPdfImage::ParseGIF: Invalid GIF file '%s'."), m_name.c_str()); + return false; + } + + isValid = true; + m_width = gif.GetWidth(); + m_height = gif.GetHeight(); + m_cs = _T("Indexed"); + m_bpc = 8; + + m_palSize = 768; + m_pal = new char[m_palSize]; + memcpy(m_pal,gif.GetPalette(),m_palSize); + + int trns = gif.GetTransparentColour(); + if (trns != -1) + { + m_trnsSize = 3; + m_trns = new char[3]; + m_trns[0] = m_pal[3*trns + 0]; + m_trns[1] = m_pal[3*trns + 1]; + m_trns[2] = m_pal[3*trns + 2]; + } + + m_dataSize = m_width * m_height; + if (m_document->m_compress) + { + m_f = _T("FlateDecode"); + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + wxZlibOutputStream q(*p); + q.Write(gif.GetData(),m_dataSize); + q.Close(); + m_dataSize = p->TellO(); + m_data = new char[m_dataSize]; + p->CopyTo(m_data,m_dataSize); + delete p; + } + else + { + m_f = _T(""); + m_data = new char[m_dataSize]; + memcpy(m_data,gif.GetData(),m_dataSize); + } +#endif + return isValid; +} + +// --- Parse WMF image file --- + +/// Class representing GDI objects while parsing WMF files. (For internal use only) +class GdiObject +{ +public: + char type; + short style; + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + unsigned short hatch; + double width; +}; + +static void +AddGdiObject(wxArrayPtrVoid& gdiObjects, void* obj) +{ + // find next available slot + size_t idx; + size_t n = gdiObjects.GetCount(); + for (idx = 0; idx < n; idx++) + { + if (gdiObjects[idx] == NULL) break; + } + if (idx < n) + { + gdiObjects[idx] = obj; + } + else + { + gdiObjects.Add(obj); + } +} + +bool +wxPdfImage::ParseWMF(wxInputStream* imageStream) +{ + bool isValid = false; + char buffer[64]; + + wxArrayPtrVoid gdiObjects; + + // check for Aldus placeable metafile header + unsigned int key = ReadIntLE(imageStream); + int headSize = 18 - 4; // WMF header minus four bytes already read + if (key == 0x9AC6CDD7) + { + headSize += 22; // Aldus header + } + + // strip headers + imageStream->Read(buffer, headSize); + + // define some state variables + short polyFillMode = 0; + bool nullPen = false; + bool nullBrush = false; + bool endRecord = false; + + wxString data = wxEmptyString; + wxString op; + // read the records + unsigned int size; + unsigned short func; + unsigned short idx; + short wo[2]; + short we[2]; + short dashArray[8]; + size_t lenDashArray; + size_t i; + short j, k, px, py; + GdiObject* obj = NULL; + while (!imageStream->Eof() && !endRecord) + { + // size of record given in WORDs (= 2 bytes) + size = ReadUIntLE(imageStream); + // func is number of GDI function + func = ReadUShortLE(imageStream); + + // parameters are read and processed + // as necessary by the case statement below. + // NB. parameters to GDI functions are stored in reverse order + // however structures are not reversed, + // e.g. POINT { int x, int y } where x=3000 (0x0BB8) and y=-1200 (0xFB50) + // is stored as B8 0B 50 FB + + // process each record. + // function numbers are defined in wingdi.h + switch (func) + { + case 0x020b: // SetWindowOrg + // do not allow window origin to be changed + // after drawing has begun + if (data.Length() == 0) + { + wo[1] = ReadShortLE(imageStream); + wo[0] = ReadShortLE(imageStream); + } + break; + + case 0x020c: // SetWindowExt + // do not allow window extent to be changed + // after drawing has begun + if (data.Length() == 0) + { + we[1] = ReadShortLE(imageStream); + we[0] = ReadShortLE(imageStream); + } + break; + + case 0x02fc: // CreateBrushIndirect + { + GdiObject* brush = new GdiObject(); + brush->style = ReadShortLE(imageStream); + imageStream->Read(&brush->r, 1); + imageStream->Read(&brush->g, 1); + imageStream->Read(&brush->b, 1); + imageStream->Read(&brush->a, 1); + brush->hatch = ReadUShortLE(imageStream); + brush->type = 'B'; + AddGdiObject(gdiObjects, brush); + } + break; + + case 0x02fa: // CreatePenIndirect + { + GdiObject* pen = new GdiObject(); + pen->style = ReadShortLE(imageStream); + short width = ReadShortLE(imageStream); + /* short dummy = */ ReadShortLE(imageStream); + imageStream->Read(&pen->r, 1); + imageStream->Read(&pen->g, 1); + imageStream->Read(&pen->b, 1); + imageStream->Read(&pen->a, 1); + + // convert width from twips to user unit + pen->width = width / (20 * m_document->m_k); + pen->type = 'P'; + AddGdiObject(gdiObjects, pen); + } + break; + + // MUST create other GDI objects even if we don't handle them + // otherwise object numbering will get out of sequence + case 0x06fe: // CreateBitmap + case 0x02fd: // CreateBitmapIndirect + case 0x00f8: // CreateBrush + case 0x02fb: // CreateFontIndirect + case 0x00f7: // CreatePalette + case 0x01f9: // CreatePatternBrush + case 0x06ff: // CreateRegion + case 0x0142: // DibCreatePatternBrush + { + GdiObject* dummy = new GdiObject(); + dummy->type = 'D'; + AddGdiObject(gdiObjects, dummy); + } + break; + + case 0x0106: // SetPolyFillMode + polyFillMode = ReadShortLE(imageStream); + break; + + case 0x01f0: // DeleteObject + { + idx = ReadUShortLE(imageStream); + delete ((GdiObject*) gdiObjects[idx]); + gdiObjects[idx] = NULL; + } + break; + + case 0x012d: // SelectObject + { + idx = ReadUShortLE(imageStream); + obj = (GdiObject*) gdiObjects[idx]; + + switch (obj->type) + { + case 'B': + nullBrush = false; + + if (obj->style == 1) // BS_NULL, BS_HOLLOW + { + nullBrush = true; + } + else + { + data += wxPdfDocument::Double2String(obj->r/255.,3) + wxString(_T(" ")); + data += wxPdfDocument::Double2String(obj->g/255.,3) + wxString(_T(" ")); + data += wxPdfDocument::Double2String(obj->b/255.,3) + wxString(_T(" rg\n")); + } + break; + + case 'P': + nullPen = false; + lenDashArray = 0; + + // dash parameters are my own - feel free to change them + switch (obj->style) + { + case 0: // PS_SOLID + break; + case 1: // PS_DASH + dashArray[0] = 3; + dashArray[1] = 1; + lenDashArray = 2; + break; + case 2: // PS_DOT + dashArray[0] = 0; + dashArray[1] = 5; + dashArray[2] = 0; + dashArray[3] = 5; + lenDashArray = 4; + break; + case 3: // PS_DASHDOT + dashArray[0] = 2; + dashArray[1] = 1; + dashArray[2] = 0; + dashArray[3] = 5; + dashArray[4] = 1; + lenDashArray = 5; + break; + case 4: // PS_DASHDOTDOT + dashArray[0] = 2; + dashArray[1] = 1; + dashArray[2] = 0; + dashArray[3] = 5; + dashArray[4] = 1; + dashArray[5] = 0; + dashArray[6] = 5; + dashArray[7] = 1; + lenDashArray = 8; + break; + case 5: // PS_NULL + nullPen = true; + break; + } + + if (!nullPen) + { + data += wxPdfDocument::Double2String(obj->r/255.,3) + wxString(_T(" ")); + data += wxPdfDocument::Double2String(obj->g/255.,3) + wxString(_T(" ")); + data += wxPdfDocument::Double2String(obj->b/255.,3) + wxString(_T(" RG\n")); + + data += wxPdfDocument::Double2String(obj->width*m_document->m_k,2) + wxString(_T(" w\n")); + } + + if (lenDashArray > 0) + { + wxString s = _T("["); + for (i = 0; i < lenDashArray; i++) + { + s += wxPdfDocument::Double2String(dashArray[i] * m_document->m_k,4); + if (i != lenDashArray-1) + { + s += _T(" "); + } + } + s += _T("] 0 d\n"); + data += s; + } + break; + } + } + break; + + case 0x0325: // Polyline + case 0x0324: // Polygon + { + short* coords = new short[size-3]; + for (i = 0; i < size-3; i++) + { + coords[i] = ReadShortLE(imageStream); + } + short numpoints = coords[0]; + + for (k = numpoints; k > 0; k--) + { + px = coords[2*k-1]; + py = coords[2*k]; + + if (k < numpoints) + { + data += wxString::Format(_T("%d %d l\n"), px, py); + } + else + { + data += wxString::Format(_T("%d %d m\n"), px, py); + } + } + + if (func == 0x0325) + { + op = _T("s"); + } + else if (func == 0x0324) + { + if (nullPen) + { + if (nullBrush) + { + op = _T("n"); // no op + } + else + { + op = _T("f"); // fill + } + } + else + { + if (nullBrush) + { + op = _T("s"); // stroke + } + else + { + op = _T("b"); // stroke and fill + } + } + + if (polyFillMode == 1 && (op == _T("b") || op == _T("f"))) + { + op += _T("*"); // use even-odd fill rule + } + } + data += op + _T("\n"); + delete [] coords; + } + break; + + case 0x0538: // PolyPolygon + { + short* coords = new short[size-3]; + for (i = 0; i < size-3; i++) + { + coords[i] = ReadShortLE(imageStream); + } + short numpolygons = coords[0]; + + short adjustment = numpolygons; + + for (j = 1; j <= numpolygons; j++) + { + short numpoints = coords[j + 1]; + + for (k = numpoints; k > 0; k--) + { + px = coords[2*k-1 + adjustment]; + py = coords[2*k + adjustment]; + + if (k == numpoints) + { + data += wxString::Format(_T("%d %d m\n"), px, py); + } + else + { + data += wxString::Format(_T("%d %d m\n"), px, py); + } + } + + adjustment += numpoints * 2; + } + + if (nullPen) + { + if (nullBrush) + { + op = _T("n"); // no op + } + else + { + op = _T("f"); // fill + } + } + else + { + if (nullBrush) + { + op = _T("s"); // stroke + } + else + { + op = _T("b"); // stroke and fill + } + } + + if (polyFillMode == 1 && (op == _T("b") || op == _T("f"))) + { + op += _T("*"); // use even-odd fill rule + } + + data += op + _T("\n"); + delete [] coords; + } + break; + + case 0x0000: + endRecord = true; + isValid = true; + break; + default: + if (size > 3) + { + imageStream->SeekI(2*(size-3), wxFromCurrent); + } + break; + } + } + + for (i = 0; i < gdiObjects.GetCount(); i++) + { + if (gdiObjects[i] != NULL) + { + delete ((GdiObject*) gdiObjects[i]); + } + } + m_x = wo[0]; + m_y = wo[1]; + m_width = we[0]; + m_height = we[1]; + + wxCharBuffer wcb(data.ToAscii()); + m_dataSize = data.Length(); + m_data = new char[m_dataSize]; + memcpy(m_data,(const char*) wcb,m_dataSize); + return isValid; +} + +int +wxPdfImage::ReadIntBE(wxInputStream* imageStream) +{ + // Read a 4-byte integer from file (big endian) + int i32; + imageStream->Read(&i32, 4); + return wxINT32_SWAP_ON_LE(i32); +} + +int +wxPdfImage::ReadIntLE(wxInputStream* imageStream) +{ + // Read a 4-byte integer from file (little endian) + int i32; + imageStream->Read(&i32, 4); + return wxINT32_SWAP_ON_BE(i32); +} + +unsigned int +wxPdfImage::ReadUIntBE(wxInputStream* imageStream) +{ + // Read a unsigned 4-byte integer from file (big endian) + unsigned int i32; + imageStream->Read(&i32, 4); + return wxUINT32_SWAP_ON_LE(i32); +} + +unsigned int +wxPdfImage::ReadUIntLE(wxInputStream* imageStream) +{ + // Read a unsigned 4-byte integer from file (little endian) + unsigned int i32; + imageStream->Read(&i32, 4); + return wxUINT32_SWAP_ON_BE(i32); +} + +short +wxPdfImage::ReadShortBE(wxInputStream* imageStream) +{ + // Read a 2-byte integer from file (big endian) + short i16; + imageStream->Read(&i16, 2); + return wxINT16_SWAP_ON_LE(i16); +} + +short +wxPdfImage::ReadShortLE(wxInputStream* imageStream) +{ + // Read a 2-byte integer from file (little endian) + short i16; + imageStream->Read(&i16, 2); + return wxINT16_SWAP_ON_BE(i16); +} + +unsigned short +wxPdfImage::ReadUShortBE(wxInputStream* imageStream) +{ + // Read a unsigned 2-byte integer from file (big endian) + unsigned short i16; + imageStream->Read(&i16, 2); + return wxUINT16_SWAP_ON_LE(i16); +} + +unsigned short +wxPdfImage::ReadUShortLE(wxInputStream* imageStream) +{ + // Read a unsigned 2-byte integer from file (little endian) + unsigned short i16; + imageStream->Read(&i16, 2); + return wxUINT16_SWAP_ON_BE(i16); +} diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfkernel.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfkernel.cpp new file mode 100644 index 000000000..714e68590 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfkernel.cpp @@ -0,0 +1,2472 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfkernel.cpp +// Purpose: Implementation of wxPdfDocument (internal methods) +// Author: Ulrich Telle +// Modified by: +// Created: 2006-01-27 +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfkernel.cpp Implementation of the wxPdfDocument class (internal methods) + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#include + +#include "wx/pdfdoc.h" +#include "wx/pdfform.h" +#include "wx/pdfgraphics.h" +#include "wx/pdftemplate.h" + +#include "pdffontdata.inc" + +// ---------------------------------------------------------------------------- +// wxPdfDocument: class representing a PDF document +// ---------------------------------------------------------------------------- + +void +wxPdfDocument::InitializeCoreFonts() +{ + m_coreFonts = new wxPdfCoreFontMap(); + int j; + for (j = 0; wxCoreFontTable[j].id != wxEmptyString; j++) + { + (*m_coreFonts)[wxCoreFontTable[j].id] = j; + } +} + +bool +wxPdfDocument::SelectFont(const wxString& family, const wxString& style, double size, bool setFont) +{ + // Select a font; size given in points + + wxString ucStyle = style.Upper(); + wxString lcFamily = family.Lower(); + if (lcFamily.Length() == 0) + { + lcFamily = m_fontFamily; + } + if (lcFamily == _T("arial")) + { + lcFamily = _T("helvetica"); + } + else if (lcFamily == _T("symbol") || lcFamily == _T("zapfdingbats")) + { + ucStyle = wxEmptyString; + } + m_decoration = wxPDF_FONT_NORMAL; + if (ucStyle.Find(_T('U')) >= 0) + { + m_decoration |= wxPDF_FONT_UNDERLINE; + ucStyle.Replace(_T("U"),_T("")); + } + if (ucStyle.Find(_T('O')) >= 0) + { + m_decoration |= wxPDF_FONT_OVERLINE; + ucStyle.Replace(_T("O"),_T("")); + } + if (ucStyle.Find(_T('S')) >= 0) + { + m_decoration |= wxPDF_FONT_STRIKEOUT; + ucStyle.Replace(_T("S"),_T("")); + } + if (ucStyle == _T("IB")) + { + ucStyle = _T("BI"); + } + if (size == 0) + { + size = m_fontSizePt; + } + + // Test if font is already selected + if (m_fontFamily == lcFamily && m_fontStyle == ucStyle && m_fontSizePt == size && !m_inTemplate) + { + return true; + } + + // Test if used for the first time + wxPdfFont* currentFont = NULL; + wxString fontkey = lcFamily + ucStyle; + wxPdfFontHashMap::iterator font = (*m_fonts).find(fontkey); + if (font == (*m_fonts).end()) + { + // Check if one of the standard fonts + wxPdfCoreFontMap::iterator coreFont = (*m_coreFonts).find(fontkey); + if (coreFont != (*m_coreFonts).end()) + { + int i = (*m_fonts).size() + 1; + int j = coreFont->second; + const wxCoreFontDesc& coreFontDesc = wxCoreFontTable[j]; + currentFont = + new wxPdfFont(i, coreFontDesc.name, coreFontDesc.cwArray, + wxPdfFontDescription(coreFontDesc.ascent, coreFontDesc.descent, + coreFontDesc.capHeight, coreFontDesc.flags, + coreFontDesc.bbox, coreFontDesc.italicAngle, + coreFontDesc.stemV, coreFontDesc.missingWidth, + coreFontDesc.xHeight, coreFontDesc.underlinePosition, + coreFontDesc.underlineThickness)); + (*m_fonts)[fontkey] = currentFont; + } + else + { + // Undefined font + wxLogDebug(_T("wxPdfDocument::SetFont: Undefined font: '%s %s'."), family.c_str(), style.c_str()); + return false; + } + } + else + { + currentFont = font->second; + } + + // Select it + m_fontFamily = lcFamily; + m_fontStyle = ucStyle; + m_fontSizePt = size; + m_fontSize = size / m_k; + m_currentFont = currentFont; + if (setFont && m_page > 0) + { + OutAscii(wxString::Format(_T("BT /F%d "),m_currentFont->GetIndex()) + + Double2String(m_fontSizePt,2) + wxString(_T(" Tf ET"))); + } + if (m_inTemplate) + { + (*(m_currentTemplate->m_fonts))[fontkey] = currentFont; + } + return true; +} + +void +wxPdfDocument::EndDoc() +{ + if(m_extGStates->size() > 0 && m_PDFVersion < _T("1.4")) + { + m_PDFVersion = _T("1.4"); + } + if (m_importVersion > m_PDFVersion) + { + m_PDFVersion = m_importVersion; + } + + PutHeader(); + PutPages(); + + PutResources(); + + // Info + NewObj(); + Out("<<"); + PutInfo(); + Out(">>"); + Out("endobj"); + + // Form fields + PutFormFields(); + + // Catalog + NewObj(); + Out("<<"); + PutCatalog(); + Out(">>"); + Out("endobj"); + + // Cross-Reference + int o = m_buffer.TellO(); + Out("xref"); + OutAscii(wxString(_T("0 ")) + wxString::Format(_T("%d"),(m_n+1))); + Out("0000000000 65535 f "); + int i; + for (i = 0; i < m_n; i++) + { + OutAscii(wxString::Format(_T("%010d 00000 n "),(*m_offsets)[i])); + } + + // Trailer + Out("trailer"); + Out("<<"); + PutTrailer(); + Out(">>"); + Out("startxref"); + OutAscii(wxString::Format(_T("%d"),o)); + Out("%%EOF"); + m_state = 3; +} + +void +wxPdfDocument::BeginPage(int orientation) +{ + m_page++; + (*m_pages)[m_page] = new wxMemoryOutputStream(); + m_state = 2; + m_x = m_lMargin; + m_y = m_tMargin; + m_fontFamily = _T(""); + + // Page orientation + if (orientation < 0) + { + orientation = m_defOrientation; + } + else + { + if (orientation != m_defOrientation) + { + (*m_orientationChanges)[m_page] = true; + } + } + if (orientation != m_curOrientation) + { + // Change orientation + if (orientation == wxPORTRAIT) + { + m_wPt = m_fwPt; + m_hPt = m_fhPt; + m_w = m_fw; + m_h = m_fh; + } + else + { + m_wPt = m_fhPt; + m_hPt = m_fwPt; + m_w = m_fh; + m_h = m_fw; + } + m_pageBreakTrigger = m_h - m_bMargin; + m_curOrientation = orientation; + } +} + +void +wxPdfDocument::EndPage() +{ + // End of page contents + while (m_inTransform > 0) + { + StopTransform(); + } + m_state = 1; +} + +void +wxPdfDocument::PutHeader() +{ + OutAscii(wxString(_T("%PDF-")) + m_PDFVersion); +} + +void +wxPdfDocument::PutTrailer() +{ + OutAscii(wxString(_T("/Size ")) + wxString::Format(_T("%d"),(m_n+1))); + OutAscii(wxString(_T("/Root ")) + wxString::Format(_T("%d"),m_n) + wxString(_T(" 0 R"))); + OutAscii(wxString(_T("/Info ")) + wxString::Format(_T("%d"),(m_n-1)) + wxString(_T(" 0 R"))); + + if (m_encrypted) + { + OutAscii(wxString::Format(_T("/Encrypt %d 0 R"), m_encObjId)); + Out("/ID [", false); + m_encrypted = false; + OutHexTextstring(m_encryptor->GetDocumentId(), false); + OutHexTextstring(m_encryptor->GetDocumentId(), false); + m_encrypted = true; + Out("]"); + } +} + +int +wxPdfDocument::GetNewObjId() +{ + m_n++; + return m_n; +} + +void +wxPdfDocument::NewObj(int objId) +{ + // Begin a new object + int id = (objId > 0) ? objId : GetNewObjId(); + (*m_offsets)[id-1] = m_buffer.TellO(); + OutAscii(wxString::Format(_T("%d"),id) + wxString(_T(" 0 obj"))); +} + +void +wxPdfDocument::PutFormFields() +{ + wxPdfFormFieldsMap::iterator formField = m_formFields->begin(); + for (formField = m_formFields->begin(); formField != m_formFields->end(); formField++) + { + OutIndirectObject(formField->second); + } +} + +void +wxPdfDocument::PutInfo() +{ + Out("/Producer ",false); + OutTextstring(wxString(wxPDF_PRODUCER)); + if (m_title.Length() > 0) + { + Out("/Title ",false); + OutTextstring(m_title); + } + if (m_subject.Length() > 0) + { + Out("/Subject ",false); + OutTextstring(m_subject); + } + if (m_author.Length() > 0) + { + Out("/Author ",false); + OutTextstring(m_author); + } + if (m_keywords.Length() > 0) + { + Out("/Keywords ",false); + OutTextstring(m_keywords); + } + if (m_creator.Length() > 0) + { + Out("/Creator ",false); + OutTextstring(m_creator); + } + wxDateTime now = wxDateTime::Now(); + Out("/CreationDate ",false); + OutTextstring(wxString(_T("D:")+now.Format(_T("%Y%m%d%H%M%S")))); +} + +void +wxPdfDocument::PutCatalog() +{ + Out("/Type /Catalog"); + Out("/Pages 1 0 R"); + + Out("/OCProperties <<"); + wxPdfOcgHashMap &ocgHashMap = m_pdfOc.GetOcgMap(); + wxPdfOcgHashMap::iterator ocgIter = ocgHashMap.begin(); + Out(" /OCGs ["); + for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { + wxPdfOcg* ocg = ocgIter->second; + OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); + } + Out("]"); + Out(" /D <<"); + Out(" /BaseState /ON "); + Out(" /Intent /View "); + Out(" /ON ["); + for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { + wxPdfOcg* ocg = ocgIter->second; + if( ocg->GetDefaultVisibilityState() == true) { + OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); + } + } + Out(" ]"); + Out(" /OFF ["); + for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { + wxPdfOcg* ocg = ocgIter->second; + if( ocg->GetDefaultVisibilityState() == false) { + OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); + } + } + Out(" ]"); + Out(" /Order ["); + for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { + wxPdfOcg* ocg = ocgIter->second; + OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); + } + Out("]"); + + Out(">>"); + Out(">>"); + + if (m_zoomMode == wxPDF_ZOOM_FULLPAGE) + { + OutAscii(wxString::Format(_T("/OpenAction [%d 0 R /Fit]"), m_firstPageId)); + } + else if (m_zoomMode == wxPDF_ZOOM_FULLWIDTH) + { + OutAscii(wxString::Format(_T("/OpenAction [%d 0 R /FitH null]"), m_firstPageId)); + } + else if (m_zoomMode == wxPDF_ZOOM_REAL) + { + OutAscii(wxString::Format(_T("/OpenAction [%d 0 R /XYZ null null 1]"), m_firstPageId)); + } + else if (m_zoomMode == wxPDF_ZOOM_FACTOR) + { + OutAscii(wxString::Format(_T("/OpenAction [%d 0 R /XYZ null null "), m_firstPageId) + + Double2String(m_zoomFactor/100.,3) + wxString(_T("]"))); + } + + if (m_layoutMode == wxPDF_LAYOUT_SINGLE) + { + Out("/PageLayout /SinglePage"); + } + else if (m_layoutMode == wxPDF_LAYOUT_CONTINUOUS) + { + Out("/PageLayout /OneColumn"); + } + else if (m_layoutMode == wxPDF_LAYOUT_TWO) + { + Out("/PageLayout /TwoColumnLeft"); + } + + if(m_outlines.GetCount() > 0) + { + OutAscii(wxString::Format(_T("/Outlines %d 0 R"), m_outlineRoot)); + Out("/PageMode /UseOutlines"); + } + + + if (m_viewerPrefs > 0) + { + Out("/ViewerPreferences <<"); + if (m_viewerPrefs & wxPDF_VIEWER_HIDETOOLBAR) + { + Out("/HideToolbar true"); + } + if (m_viewerPrefs & wxPDF_VIEWER_HIDEMENUBAR) + { + Out("/HideMenubar true"); + } + if (m_viewerPrefs & wxPDF_VIEWER_HIDEWINDOWUI) + { + Out("/HideWindowUI true"); + } + if (m_viewerPrefs & wxPDF_VIEWER_FITWINDOW) + { + Out("/FitWindow true"); + } + if (m_viewerPrefs & wxPDF_VIEWER_CENTERWINDOW) + { + Out("/CenterWindow true"); + } + if (m_viewerPrefs & wxPDF_VIEWER_DISPLAYDOCTITLE) + { + Out("/DisplayDocTitle true"); + } + Out(">>"); + } + + if (m_javascript.Length() > 0) + { + OutAscii(wxString::Format(_T("/Names <>"), m_nJS)); + } + + if ((*m_formFields).size() > 0) + { + Out("/AcroForm <<"); + Out("/Fields [", false); + wxPdfFormFieldsMap::iterator formField = m_formFields->begin(); + for (formField = m_formFields->begin(); formField != m_formFields->end(); formField++) + { + wxPdfIndirectObject* field = formField->second; + OutAscii(wxString::Format(_T("%d %d R "), field->GetObjectId(), field->GetGenerationId()), false); + } + Out("]"); + Out("/DR 2 0 R"); +// Out("/DR [ 2 0 R << /Font << /ZaDb << /Type /Font /Subtype /Type1 /Name /ZaDb /BaseFont /ZapfDingbats >> >> >>]"); + Out("/NeedAppearances true"); + Out(">>"); + + // << /DR << /Font << /ZaDb << /Type /Font /Subtype /Type1 /Name /ZaDb /BaseFont /ZapfDingbats >> + // /Helv 3 0 R >> >> /DA (/Helv 10 Tf 0 g ) + // /NeedAppearances true >> + + } +} + +// --- Fast string search (KMP method) for page number alias replacement + +static int* +makeFail(const char* target, int tlen) +{ + int t = 0; + int s, m; + m = tlen; + int* f = new int[m+1]; + f[1] = 0; + for (s = 1; s < m; s++) + { + while ((t > 0) && (target[s] != target[t])) + { + t = f[t]; + } + if (target[t] == target[s]) + { + t++; + f[s+1] = t; + } + else + { + f[s+1] = 0; + } + } + return f; +} + +static int +findString(const char* src, int slen, const char* target, int tlen, int* f) +{ + int s = 0; + int i; + int m = tlen; + for (i = 0; i < slen; i++) + { + while ( (s > 0) && (src[i] != target[s])) + { + s = f[s]; + } + if (src[i] == target[s]) s++; + if (s == m) return (i-m+1); + } + return slen; +} + +void +wxPdfDocument::ReplaceNbPagesAlias() +{ + int lenAsc = m_aliasNbPages.Length(); +#if wxUSE_UNICODE + wxCharBuffer wcb(m_aliasNbPages.ToAscii()); + const char* nbAsc = (const char*) wcb; +#else + const char* nbAsc = m_aliasNbPages.c_str(); +#endif + int* fAsc = makeFail(nbAsc,lenAsc); + +#if wxUSE_UNICODE + wxMBConvUTF16BE conv; + int lenUni = conv.WC2MB(NULL, m_aliasNbPages, 0); + char* nbUni = new char[lenUni+3]; + lenUni = conv.WC2MB(nbUni, m_aliasNbPages, lenUni+3); + int* fUni = makeFail(nbUni,lenUni); +#endif + + wxString pg = wxString::Format(_T("%d"),m_page); + int lenPgAsc = pg.Length(); +#if wxUSE_UNICODE + wxCharBuffer wpg(pg.ToAscii()); + const char* pgAsc = (const char*) wpg; + int lenPgUni = conv.WC2MB(NULL, pg, 0); + char* pgUni = new char[lenPgUni+3]; + lenPgUni = conv.WC2MB(pgUni, pg, lenPgUni+3); +#else + const char* pgAsc = pg.c_str(); +#endif + + int n; + for (n = 1; n <= m_page; n++) + { + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + wxMemoryInputStream inPage(*((*m_pages)[n])); + int len = inPage.GetSize(); + char* buffer = new char[len]; + char* pBuf = buffer; + inPage.Read(buffer,len); + int pAsc = findString(buffer,len,nbAsc,lenAsc,fAsc); +#if wxUSE_UNICODE + int pUni = findString(buffer,len,nbUni,lenUni,fUni); + while (pAsc < len || pUni < len) + { + if (pAsc < len && pAsc < pUni) + { + if (pAsc > 0) + { + p->Write(pBuf,pAsc); + } + p->Write(pgAsc,lenPgAsc); + pBuf += pAsc + lenAsc; + len -= (pAsc + lenAsc); + pUni -= (pAsc + lenAsc); + pAsc = findString(pBuf,len,nbAsc,lenAsc,fAsc); + } + else if (pUni < len && pUni < pAsc) + { + if (pUni > 0) + { + p->Write(pBuf,pUni); + } + p->Write(pgUni,lenPgUni); + pBuf += pUni + lenUni; + len -= (pUni + lenUni); + pAsc -= (pUni + lenUni); + pUni = findString(pBuf,len,nbUni,lenUni,fUni); + } + } +#else + while (pAsc < len) + { + if (pAsc > 0) + { + p->Write(pBuf,pAsc); + } + p->Write(pgAsc,lenPgAsc); + pBuf += pAsc + lenAsc; + len -= (pAsc + lenAsc); + pAsc = findString(pBuf,len,nbAsc,lenAsc,fAsc); + } +#endif + if (len > 0) + { + p->Write(pBuf,len); + } + delete [] buffer; + delete (*m_pages)[n]; + (*m_pages)[n] = p; + } + +#if wxUSE_UNICODE + delete [] pgUni; + delete [] fUni; + delete [] nbUni; +#endif + delete [] fAsc; +} + +void +wxPdfDocument::PutPages() +{ + double wPt, hPt; + int nb = m_page; + int n; + int nbannot = 0; + int firstTextAnnotation = m_n; + + //Text annotations + for (n = 1; n <= nb; n++) + { + wxPdfAnnotationsMap::iterator pageAnnots = (*m_annotations).find(n); + if (pageAnnots != (*m_annotations).end()) + { + // Links + wxArrayPtrVoid* pageAnnotsArray = pageAnnots->second; + + int pageAnnotsCount = pageAnnotsArray->GetCount(); + int j; + for (j = 0; j < pageAnnotsCount; j++) + { + wxPdfAnnotation* annotation = (wxPdfAnnotation*) (*pageAnnotsArray)[j]; + NewObj(); + double x = annotation->GetX(); + double y = annotation->GetY(); + Out("<GetText(), false); + Out(">>"); + Out("endobj"); + } + } + } + + if (m_aliasNbPages.Length() > 0) + { + // Replace number of pages + ReplaceNbPagesAlias(); + } + + if (m_defOrientation == wxPORTRAIT) + { + wPt = m_fwPt; + hPt = m_fhPt; + } + else + { + wPt = m_fhPt; + hPt = m_fwPt; + } + wxString filter = (m_compress) ? _T("/Filter /FlateDecode ") : _T(""); + + m_firstPageId = m_n + 1; + for (n = 1; n <= nb; n++) + { + // Page + NewObj(); + Out("<second; + int pageLinkCount = pageLinkArray->GetCount(); + int j; + for (j = 0; j < pageLinkCount; j++) + { + wxPdfPageLink* pl = (wxPdfPageLink*) (*pageLinkArray)[j]; + wxString rect = Double2String(pl->GetX(),2) + wxString(_T(" ")) + + Double2String(pl->GetY(),2) + wxString(_T(" ")) + + Double2String(pl->GetX()+pl->GetWidth(),2) + wxString(_T(" ")) + + Double2String(pl->GetY()-pl->GetHeight(),2); + Out("<IsLinkRef()) + { + Out("/A <GetLinkURL(),false); + Out(">>>>",false); + } + else + { + wxPdfLink* link = (*m_links)[pl->GetLinkRef()]; + wxPdfBoolHashMap::iterator oChange = (*m_orientationChanges).find(link->GetPage()); + double h = (oChange != (*m_orientationChanges).end()) ? wPt : hPt; + OutAscii(wxString::Format(_T("/Dest [%d 0 R /XYZ 0 "),m_firstPageId+2*(link->GetPage()-1)) + + Double2String(h-link->GetPosition()*m_k,2) + + wxString(_T(" null]>>")),false); + } + delete pl; + (*pageLinkArray)[j] = NULL; + } + } + wxPdfAnnotationsMap::iterator pageAnnots = (*m_annotations).find(n); + if (pageAnnots != (*m_annotations).end()) + { + //Text annotations + wxArrayPtrVoid* pageAnnotsArray = pageAnnots->second; + int pageAnnotsCount = pageAnnotsArray->GetCount(); + int j; + for (j = 0; j < pageAnnotsCount; j++) + { + nbannot++; + OutAscii(wxString::Format(_T("%d 0 R "), firstTextAnnotation+nbannot), false); + delete ((wxPdfAnnotation*) (*pageAnnotsArray)[j]); + (*pageAnnotsArray)[j] = NULL; + } + } + wxPdfFormAnnotsMap::iterator formAnnots = (*m_formAnnotations).find(n); + if (formAnnots != (*m_formAnnotations).end()) + { + // Form annotations + wxArrayPtrVoid* formAnnotsArray = formAnnots->second; + int formAnnotsCount = formAnnotsArray->GetCount(); + int j; + for (j = 0; j < formAnnotsCount; j++) + { + wxPdfIndirectObject* object = static_cast((*formAnnotsArray)[j]); + OutAscii(wxString::Format(_T("%d %d R "), object->GetObjectId(), object->GetGenerationId()), false); +// delete ((wxPdfAnnotation*) (*formAnnotsArray)[j]); +// (*formAnnotsArray)[j] = NULL; + } + } + Out("]"); + + OutAscii(wxString::Format(_T("/Contents %d 0 R>>"), m_n+1)); + Out("endobj"); + + // Page content + wxMemoryOutputStream* p; + if (m_compress) + { + p = new wxMemoryOutputStream(); + wxZlibOutputStream q(*p); + wxMemoryInputStream tmp(*((*m_pages)[n])); + q.Write(tmp); + } + else + { + p = (*m_pages)[n]; + } + + NewObj(); + OutAscii(wxString(_T("<<")) + filter + wxString(_T("/Length ")) + + wxString::Format(_T("%ld"), CalculateStreamLength(p->TellO())) + wxString(_T(">>"))); + PutStream(*p); + Out("endobj"); + if (m_compress) + { + delete p; + } + } + // Pages root + (*m_offsets)[0] = m_buffer.TellO(); + Out("1 0 obj"); + Out("<>"); + Out("endobj"); +} + +void +wxPdfDocument::PutExtGStates() +{ + static wxChar* bms[] = { + _T("/Normal"), _T("/Multiply"), _T("/Screen"), _T("/Overlay"), _T("/Darken"), + _T("/Lighten"), _T("/ColorDodge"), _T("/ColorBurn"), _T("/HardLight"), _T("/SoftLight"), + _T("/Difference"), _T("/Exclusion"), _T("/Hue"), _T("/Saturation"), _T("/Color"), + _T("Luminosity") + }; + wxPdfExtGStateMap::iterator extGState; + for (extGState = m_extGStates->begin(); extGState != m_extGStates->end(); extGState++) + { + NewObj(); + extGState->second->SetObjIndex(m_n); + Out("<second->GetFillAlpha(), 3)); + OutAscii(wxString(_T("/CA ")) + Double2String(extGState->second->GetLineAlpha(), 3)); + OutAscii(wxString(_T("/bm ")) + wxString(bms[extGState->second->GetBlendMode()])); + Out(">>"); + Out("endobj"); + } +} + +void +wxPdfDocument::PutShaders() +{ + wxPdfGradientMap::iterator gradient; + for (gradient = m_gradients->begin(); gradient != m_gradients->end(); gradient++) + { + wxPdfGradientType type = gradient->second->GetType(); + switch (type) + { + case wxPDF_GRADIENT_AXIAL: + case wxPDF_GRADIENT_MIDAXIAL: + case wxPDF_GRADIENT_RADIAL: + { + wxPdfColour color1 = ((wxPdfAxialGradient*)(gradient->second))->GetColor1(); + wxPdfColour color2 = ((wxPdfAxialGradient*)(gradient->second))->GetColor2(); + double intexp = ((wxPdfAxialGradient*)(gradient->second))->GetIntExp(); + + NewObj(); + Out("<<"); + Out("/FunctionType 2"); + Out("/Domain [0.0 1.0]"); + Out("/C0 [", false); + OutAscii(color1.GetColorValue(), false); + Out("]"); + Out("/C1 [", false); + OutAscii(color2.GetColorValue(), false); + Out("]"); + OutAscii(wxString(_T("/N ")) + Double2String(intexp,2)); + Out(">>"); + Out("endobj"); + int f1 = m_n; + + if (type == wxPDF_GRADIENT_MIDAXIAL) + { + double midpoint = ((wxPdfMidAxialGradient*)(gradient->second))->GetMidPoint(); + NewObj(); + Out("<<"); + Out("/FunctionType 3"); + Out("/Domain [0.0 1.0]"); + OutAscii(wxString::Format(_T("/Functions [%d 0 R %d 0 R]"), f1, f1)); + OutAscii(wxString(_T("/Bounds [")) + Double2String(midpoint,3) + wxString(_T("]"))); + Out("/Encode [0.0 1.0 1.0 0.0]"); + Out(">>"); + Out("endobj"); + f1 = m_n; + } + + NewObj(); + Out("<<"); + OutAscii(wxString::Format(_T("/ShadingType %d"), ((type == wxPDF_GRADIENT_RADIAL) ? 3 : 2))); + switch (color1.GetColorType()) + { + case wxPDF_COLOURTYPE_GRAY: + Out("/ColorSpace /DeviceGray"); + break; + case wxPDF_COLOURTYPE_RGB: + Out("/ColorSpace /DeviceRGB"); + break; + case wxPDF_COLOURTYPE_CMYK: + Out("/ColorSpace /DeviceCMYK"); + break; + } + if (type == wxPDF_GRADIENT_AXIAL || + type == wxPDF_GRADIENT_MIDAXIAL) + { + wxPdfAxialGradient* grad = (wxPdfAxialGradient*) (gradient->second); + OutAscii(wxString(_T("/Coords [")) + + Double2String(grad->GetX1(),3) + wxString(_T(" ")) + + Double2String(grad->GetY1(),3) + wxString(_T(" ")) + + Double2String(grad->GetX2(),3) + wxString(_T(" ")) + + Double2String(grad->GetY2(),3) + wxString(_T("]"))); + OutAscii(wxString::Format(_T("/Function %d 0 R"), f1)); + Out("/Extend [true true] "); + } + else + { + wxPdfRadialGradient* grad = (wxPdfRadialGradient*) (gradient->second); + OutAscii(wxString(_T("/Coords [")) + + Double2String(grad->GetX1(),3) + wxString(_T(" ")) + + Double2String(grad->GetY1(),3) + wxString(_T(" ")) + + Double2String(grad->GetR1(),3) + wxString(_T(" ")) + + Double2String(grad->GetX2(),3) + wxString(_T(" ")) + + Double2String(grad->GetY2(),3) + wxString(_T(" ")) + + Double2String(grad->GetR2(),3) + wxString(_T("]"))); + OutAscii(wxString::Format(_T("/Function %d 0 R"), f1)); + Out("/Extend [true true] "); + } + Out(">>"); + Out("endobj"); + gradient->second->SetObjIndex(m_n); + break; + } + case wxPDF_GRADIENT_COONS: + { + wxPdfCoonsPatchGradient* grad = (wxPdfCoonsPatchGradient*) (gradient->second); + NewObj(); + Out("<<"); + Out("/ShadingType 6"); + switch (grad->GetColorType()) + { + case wxPDF_COLOURTYPE_GRAY: + Out("/ColorSpace /DeviceGray"); + break; + case wxPDF_COLOURTYPE_RGB: + Out("/ColorSpace /DeviceRGB"); + break; + case wxPDF_COLOURTYPE_CMYK: + Out("/ColorSpace /DeviceCMYK"); + break; + } + Out("/BitsPerCoordinate 16"); + Out("/BitsPerComponent 8"); + Out("/Decode[0 1 0 1 0 1 0 1 0 1]"); + Out("/BitsPerFlag 8"); + wxMemoryOutputStream* p = grad->GetBuffer(); + OutAscii(wxString::Format(_T("/Length %ld"), CalculateStreamLength(p->TellO()))); + Out(">>"); + PutStream(*p); + Out("endobj"); + gradient->second->SetObjIndex(m_n); + } + default: + break; + } + } +} + +void +wxPdfDocument::PutFonts() +{ + int nf = m_n; + + int nb = (*m_diffs).size(); + int i; + for (i = 1; i <= nb; i++) + { + // Encodings + NewObj(); + Out("<>"); + Out("endobj"); + } + + wxString type; + wxPdfFontHashMap::iterator fontIter = m_fonts->begin(); + for (fontIter = m_fonts->begin(); fontIter != m_fonts->end(); fontIter++) + { + wxPdfFont* font = fontIter->second; + if (font->HasFile()) + { + type = font->GetType(); + // Font file embedding + NewObj(); + font->SetFileIndex(m_n); + + wxString strFontFileName = font->GetFontFile(); + wxFileName fontFileName(strFontFileName); + fontFileName.MakeAbsolute(font->GetFilePath()); + +// wxFileSystem fs; +// wxFSFile* fontFile = fs.OpenFile(fontFileName.GetFullPath()); + if (fontFileName.FileExists()) + { + wxFileInputStream* fontStream = new wxFileInputStream(fontFileName.GetFullPath()); + int fontLen = fontStream->GetSize(); + int fontSize1 = font->GetSize1(); + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + + bool compressed = strFontFileName.Right(2) == _T(".z"); + if (!compressed && font->HasSize2()) + { + unsigned char first = (unsigned char) fontStream->Peek(); + if (first == 128) + { + unsigned char* buffer = new unsigned char[fontLen]; + fontStream->Read(buffer, fontLen); + if (buffer[6+font->GetSize1()] == 128) + { + // Strip first and second binary header + fontLen -= 12; + p->Write(&buffer[6], font->GetSize1()); + p->Write(&buffer[12 + font->GetSize1()], fontLen - font->GetSize1()); + } + else + { + // Strip first binary header + fontLen -= 6; + p->Write(&buffer[6], fontLen); + } + delete [] buffer; + } + else + { + p->Write(*fontStream); + } + } + else + { + if (m_fontSubsetting && font->SupportsSubset()) + { + font->SetSubset(true); + fontSize1 = font->CreateSubset(fontStream, p); + compressed = true; + } + else + { + p->Write(*fontStream); + } + } + fontLen = CalculateStreamLength(p->TellO()); + OutAscii(wxString::Format(_T("<HasSize2()) + { + OutAscii(wxString::Format(_T("/Length2 %d /Length3 0"), font->GetSize2())); + } + } + Out(">>"); + PutStream(*p); + Out("endobj"); + + delete p; +// delete fontFile; + delete fontStream; + } + } + } + + fontIter = m_fonts->begin(); + for (fontIter = m_fonts->begin(); fontIter != m_fonts->end(); fontIter++) + { + // Font objects + wxPdfFont* font = fontIter->second; + font->SetObjIndex(m_n+1); + wxString type = font->GetType(); + wxString name = font->GetName(); + if (type == _T("core")) + { + // Standard font + NewObj(); + Out("<>"); + Out("endobj"); + } + else if (type == _T("Type1") || type == _T("TrueType")) + { + // Additional Type1 or TrueType font + NewObj(); + Out("<GetEncoding() != _T("")) + { + if (font->HasDiffs()) + { + OutAscii(wxString::Format(_T("/Encoding %d 0 R"), (nf+font->GetDiffIndex()))); + } + else + { + Out("/Encoding /WinAnsiEncoding"); + } + } + Out(">>"); + Out("endobj"); + + // Widths + NewObj(); + wxString s = font->GetWidthsAsString(); + OutAscii(s); + Out("endobj"); + + // Descriptor + const wxPdfFontDescription& fd = font->GetDesc(); + NewObj(); + Out("<HasFile()) + { + if (type == _T("Type1")) + { + OutAscii(wxString::Format(_T("/FontFile %d 0 R"), font->GetFileIndex())); + } + else + { + OutAscii(wxString::Format(_T("/FontFile2 %d 0 R"), font->GetFileIndex())); + } + } + Out(">>"); + Out("endobj"); + } + else if (type == _T("TrueTypeUnicode") || type == _T("OpenTypeUnicode")) + { + // Type0 Font + // A composite font composed of other fonts, organized hierarchically + NewObj(); + Out("<>"); + Out("endobj"); + + // CIDFontType + NewObj(); + Out("<GetDesc(); + if (fd.GetMissingWidth() > 0) + { + // The default width for glyphs in the CIDFont MissingWidth + OutAscii(wxString::Format(_T("/DW %d"), fd.GetMissingWidth())); + } + + OutAscii(wxString(_T("/W ")) + font->GetWidthsAsString()); // A description of the widths for the glyphs in the CIDFont + if (type == _T("TrueTypeUnicode")) + { + OutAscii(wxString::Format(_T("/CIDToGIDMap %d 0 R"), (m_n + 3))); + } + + Out(">>"); + Out("endobj"); + + // CIDSystemInfo dictionary + // A dictionary containing entries that define the character collectionof the CIDFont. + NewObj(); + // A string identifying an issuer of character collections + Out("<>"); + Out("endobj"); + + // Font descriptor + // A font descriptor describing the CIDFonts default metrics other than its glyph widths + NewObj(); + Out("<HasFile()) + { + if (type == _T("TrueTypeUnicode")) + { + // A stream containing a TrueType font program + OutAscii(wxString::Format(_T("/FontFile2 %d 0 R"), font->GetFileIndex())); + } + else + { + // A stream containing a CFF font program + OutAscii(wxString::Format(_T("/FontFile3 %d 0 R"), font->GetFileIndex())); + } + } + Out(">>"); + Out("endobj"); + + // Embed CIDToGIDMap + // A specification of the mapping from CIDs to glyph indices + NewObj(); + wxString strCtgFileName = font->GetCtgFile(); + wxFileName ctgFileName(strCtgFileName); + ctgFileName.MakeAbsolute(font->GetFilePath()); + +// wxFileSystem fs; +// wxFSFile* ctgFile = fs.OpenFile(ctgFileName.GetFullPath()); + if (ctgFileName.FileExists()) + { + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + wxFileInputStream* ctgStream = new wxFileInputStream(ctgFileName.GetFullPath()); + int ctgLen = CalculateStreamLength(ctgStream->GetSize()); + OutAscii(wxString::Format(_T("<>"); + p->Write(*ctgStream); + PutStream(*p); + delete p; +// delete ctgFile; + delete ctgStream; + } + else + { + // TODO : file not found, should be checked already when adding font! + wxLogDebug(_T("wxPdfDocument::PutFonts: Font file '%s' not found."), strCtgFileName.c_str()); + } + Out("endobj"); + } + else if (type == _T("Type0")) + { + // Type0 + NewObj(); + Out("<GetCMap()); + OutAscii(wxString(_T("/Encoding /")) + font->GetCMap()); + OutAscii(wxString::Format(_T("/DescendantFonts [%d 0 R]"), (m_n+1))); + Out(">>"); + Out("endobj"); + + // CIDFont + NewObj(); + Out("<GetOrdering(), false); + OutAscii(wxString(_T(" /Supplement ")) + font->GetSupplement() + wxString(_T(">>"))); + OutAscii(wxString::Format(_T("/FontDescriptor %d 0 R"), (m_n+1))); + + // Widths + // A description of the widths for the glyphs in the CIDFont + OutAscii(wxString(_T("/W ")) + font->GetWidthsAsString()); + Out(">>"); + Out("endobj"); + + // Font descriptor + const wxPdfFontDescription& fd = font->GetDesc(); + NewObj(); + Out("<>"); + Out("endobj"); + } + } +} + +void +wxPdfDocument::PutImages() +{ + wxString filter = (m_compress) ? _T("/Filter /FlateDecode ") : _T(""); + int iter; + for (iter = 0; iter < 2; iter++) + { + // We need two passes to resolve dependencies + wxPdfImageHashMap::iterator image = m_images->begin(); + for (image = m_images->begin(); image != m_images->end(); image++) + { + // Image objects + wxPdfImage* currentImage = image->second; + + if (currentImage->GetMaskImage() > 0) + { + // On first pass skip images depending on a mask + if (iter == 0) continue; + } + else + { + // On second pass skip images already processed + if (iter != 0) continue; + } + + NewObj(); + currentImage->SetObjIndex(m_n); + Out("<IsFormObject()) + { + Out("/Subtype /Form"); + OutAscii(wxString::Format(_T("/BBox [%d %d %d %d]"), + currentImage->GetX(), currentImage->GetY(), + currentImage->GetWidth()+currentImage->GetX(), + currentImage->GetHeight() + currentImage->GetY())); + if (m_compress) + { + Out("/Filter /FlateDecode"); + } + int dataLen = currentImage->GetDataSize(); + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + if (m_compress) + { + wxZlibOutputStream q(*p); + q.Write(currentImage->GetData(),currentImage->GetDataSize()); + } + else + { + p->Write(currentImage->GetData(),currentImage->GetDataSize()); + } + dataLen = CalculateStreamLength(p->TellO()); + OutAscii(wxString::Format(_T("/Length %d>>"),dataLen)); + PutStream(*p); + + Out("endobj"); + delete p; + } + else + { + Out("/Subtype /Image"); + OutAscii(wxString::Format(_T("/Width %d"),currentImage->GetWidth())); + OutAscii(wxString::Format(_T("/Height %d"),currentImage->GetHeight())); + + int maskImage = currentImage->GetMaskImage(); + if (maskImage > 0) + { + int maskObjId = 0; + wxPdfImageHashMap::iterator img = m_images->begin(); + while (maskObjId == 0 && img != m_images->end()) + { + if (img->second->GetIndex() == maskImage) + { + maskObjId = img->second->GetObjIndex(); + } + img++; + } + if (maskObjId > 0) + { + OutAscii(wxString::Format(_T("/SMask %d 0 R"), maskObjId)); + } + } + + if (currentImage->GetColorSpace() == _T("Indexed")) + { + int palLen = currentImage->GetPaletteSize() / 3 - 1; + OutAscii(wxString::Format(_T("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]"), + palLen,(m_n+1))); + } + else + { + OutAscii(wxString(_T("/ColorSpace /")) + currentImage->GetColorSpace()); + if (currentImage->GetColorSpace() == _T("DeviceCMYK")) + { + Out("/Decode [1 0 1 0 1 0 1 0]"); + } + } + OutAscii(wxString::Format(_T("/BitsPerComponent %d"),currentImage->GetBitsPerComponent())); + wxString f = currentImage->GetF(); + if (f.Length() > 0) + { + OutAscii(wxString(_T("/Filter /")) + f); + } + wxString parms = currentImage->GetParms(); + if (parms.Length() > 0) + { + OutAscii(parms); + } + int trnsSize = currentImage->GetTransparencySize(); + unsigned char* trnsData = (unsigned char*) currentImage->GetTransparency(); + if (trnsSize > 0) + { + wxString trns = _T("");; + int i; + for (i = 0; i < trnsSize; i++) + { + int trnsValue = trnsData[i]; + trns += wxString::Format(_T("%d %d "), trnsValue, trnsValue); + } + OutAscii(wxString(_T("/Mask [")) + trns + wxString(_T("]"))); + } + + OutAscii(wxString::Format(_T("/Length %d>>"), CalculateStreamLength(currentImage->GetDataSize()))); + + wxMemoryOutputStream* p = new wxMemoryOutputStream(); + p->Write(currentImage->GetData(),currentImage->GetDataSize()); + PutStream(*p); + delete p; + Out("endobj"); + + // Palette + if (currentImage->GetColorSpace() == _T("Indexed")) + { + NewObj(); + int palLen = currentImage->GetPaletteSize(); + p = new wxMemoryOutputStream(); + if (m_compress) + { + wxZlibOutputStream q(*p); + q.Write(currentImage->GetPalette(),currentImage->GetPaletteSize()); + } + else + { + p->Write(currentImage->GetPalette(),currentImage->GetPaletteSize()); + } + palLen = CalculateStreamLength(p->TellO()); + OutAscii(wxString(_T("<<")) + filter + wxString::Format(_T("/Length %d>>"), palLen)); + PutStream(*p); + Out("endobj"); + delete p; + } + } + } + } +} + +void +wxPdfDocument::PutTemplates() +{ + wxString filter = (m_compress) ? _T("/Filter /FlateDecode ") : _T(""); + wxPdfTemplatesMap::iterator templateIter = m_templates->begin(); + for (templateIter = m_templates->begin(); templateIter != m_templates->end(); templateIter++) + { + // Image objects + wxPdfTemplate* currentTemplate = templateIter->second; + NewObj(); + currentTemplate->SetObjIndex(m_n); + + OutAscii(wxString(_T("<<")) + filter + wxString(_T("/Type /XObject"))); + Out("/Subtype /Form"); + Out("/FormType 1"); + + OutAscii(wxString(_T("/BBox [")) + + Double2String(currentTemplate->GetX()*m_k,2) + wxString(_T(" ")) + + Double2String(currentTemplate->GetY()*m_k,2) + wxString(_T(" ")) + + Double2String((currentTemplate->GetX()+currentTemplate->GetWidth())*m_k,2) + wxString(_T(" ")) + + Double2String((currentTemplate->GetY()+currentTemplate->GetHeight())*m_k,2) + wxString(_T("]"))); + + Out("/Resources "); + if (currentTemplate->GetResources() != NULL) + { + m_currentParser = currentTemplate->GetParser(); + WriteObjectValue(currentTemplate->GetResources()); + } + else + { + Out("<m_fonts->size() > 0) + { + Out("/Font <<"); + wxPdfFontHashMap::iterator font = currentTemplate->m_fonts->begin(); + for (font = currentTemplate->m_fonts->begin(); font != currentTemplate->m_fonts->end(); font++) + { + OutAscii(wxString::Format(_T("/F%d %d 0 R"), font->second->GetIndex(), font->second->GetObjIndex())); + } + Out(">>"); + } + // Image and template references + if (currentTemplate->m_images->size() > 0 || + currentTemplate->m_templates->size() > 0) + { + Out("/XObject <<"); + wxPdfImageHashMap::iterator image = currentTemplate->m_images->begin(); + for (image = currentTemplate->m_images->begin(); image != currentTemplate->m_images->end(); image++) + { + wxPdfImage* currentImage = image->second; + OutAscii(wxString::Format(_T("/I%d %d 0 R"), currentImage->GetIndex(), currentImage->GetObjIndex())); + } + wxPdfTemplatesMap::iterator templateIter = currentTemplate->m_templates->begin(); + for (templateIter = currentTemplate->m_templates->begin(); templateIter != currentTemplate->m_templates->end(); templateIter++) + { + wxPdfTemplate* tpl = templateIter->second; + OutAscii(m_templatePrefix + wxString::Format(_T("%d %d 0 R"), tpl->GetIndex(), tpl->GetObjIndex())); + } + Out(">>"); + } + Out(">>"); + } + + // Template data + wxMemoryOutputStream* p; + if (m_compress) + { + p = new wxMemoryOutputStream(); + wxZlibOutputStream q(*p); + wxMemoryInputStream tmp(currentTemplate->m_buffer); + q.Write(tmp); + } + else + { + p = &(currentTemplate->m_buffer); + } + + OutAscii(wxString::Format(_T("/Length %ld >>"), CalculateStreamLength(p->TellO()))); + PutStream(*p); + Out("endobj"); + if (m_compress) + { + delete p; + } + } +} + +void +wxPdfDocument::PutImportedObjects() +{ + wxPdfParserMap::iterator parser = m_parsers->begin(); + for (parser = m_parsers->begin(); parser != m_parsers->end(); parser++) + { + m_currentParser = parser->second; + if (m_currentParser != NULL) + { + m_currentParser->SetUseRawStream(true); + wxPdfObjectQueue* entry = m_currentParser->GetObjectQueue(); + while ((entry = entry->GetNext()) != NULL) + { + wxPdfObject* resolvedObject = m_currentParser->ResolveObject(entry->GetObject()); + NewObj(entry->GetActualObjectId()); + WriteObjectValue(resolvedObject); + Out("endobj"); + entry->SetObject(resolvedObject); + } + } + } +} + +void +wxPdfDocument::PutXObjectDict() +{ + wxPdfImageHashMap::iterator image = m_images->begin(); + for (image = m_images->begin(); image != m_images->end(); image++) + { + wxPdfImage* currentImage = image->second; + OutAscii(wxString::Format(_T("/I%d %d 0 R"), currentImage->GetIndex(), currentImage->GetObjIndex())); + } + wxPdfTemplatesMap::iterator templateIter = m_templates->begin(); + for (templateIter = m_templates->begin(); templateIter != m_templates->end(); templateIter++) + { + wxPdfTemplate* tpl = templateIter->second; + OutAscii(m_templatePrefix + wxString::Format(_T("%d %d 0 R"), tpl->GetIndex(), tpl->GetObjIndex())); + } +} + +void +wxPdfDocument::PutResourceDict() +{ + Out("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]"); + Out("/Font <<"); + wxPdfFontHashMap::iterator font = m_fonts->begin(); + + for (font = m_fonts->begin(); font != m_fonts->end(); font++) + { + OutAscii(wxString::Format(_T("/F%d %d 0 R"), font->second->GetIndex(), font->second->GetObjIndex())); + } + Out(">>"); + Out("/XObject <<"); + PutXObjectDict(); + Out(">>"); + + Out("/ExtGState <<"); + wxPdfExtGStateMap::iterator extGState; + for (extGState = m_extGStates->begin(); extGState != m_extGStates->end(); extGState++) + { + OutAscii(wxString::Format(_T("/GS%ld %d 0 R"), extGState->first, extGState->second->GetObjIndex())); + } + Out(">>"); + + Out("/Shading <<"); + wxPdfGradientMap::iterator gradient; + for (gradient = m_gradients->begin(); gradient != m_gradients->end(); gradient++) + { + //foreach(m_gradients as $id=>$grad) + OutAscii(wxString::Format(_T("/Sh%ld %d 0 R"), gradient->first, gradient->second->GetObjIndex())); + } + Out(">>"); + Out("/ColorSpace <<"); + wxPdfSpotColourMap::iterator spotColor; + for (spotColor = m_spotColors->begin(); spotColor != m_spotColors->end(); spotColor++) + { + OutAscii(wxString::Format(_T("/CS%d %d 0 R"), spotColor->second->GetIndex(), spotColor->second->GetObjIndex())); + } + Out(">>"); + Out("/Pattern <<"); + wxPdfPatternMap::iterator pattern; + for (pattern = m_patterns->begin(); pattern != m_patterns->end(); pattern++) + { + OutAscii(wxString::Format(_T("/P%d %d 0 R"), pattern->second->GetIndex(), pattern->second->GetObjIndex())); + } + Out(">>"); + wxPdfOcgHashMap &ocgHashMap = m_pdfOc.GetOcgMap(); + wxPdfOcgHashMap::iterator ocgIter = ocgHashMap.begin(); + Out("/Properties <<"); + for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { + wxPdfOcg* ocg = ocgIter->second; + OutAscii(wxString::Format(_T("/OC%d %d 0 R"), ocg->GetOcgIndex(), ocg->GetObjectIndex())); + } + Out(">>"); +} + +void +wxPdfDocument::PutBookmarks() +{ + int nb = m_outlines.GetCount(); + if (nb == 0) + { + return; + } + + int i; + int parent; + wxArrayInt lru; + lru.SetCount(m_maxOutlineLevel+1); + int level = 0; + for (i = 0; i < nb; i++) + { + wxPdfBookmark* bookmark = (wxPdfBookmark*) m_outlines[i]; + int currentLevel = bookmark->GetLevel(); + if (currentLevel > 0) + { + parent = lru[currentLevel-1]; + // Set parent and last pointers + bookmark->SetParent(parent); + wxPdfBookmark* parentBookmark = (wxPdfBookmark*) m_outlines[parent]; + parentBookmark->SetLast(i); + if (currentLevel > level) + { + // Level increasing: set first pointer + parentBookmark->SetFirst(i); + } + } + else + { + bookmark->SetParent(nb); + } + if (currentLevel <= level && i > 0) + { + // Set prev and next pointers + int prev = lru[currentLevel]; + wxPdfBookmark* prevBookmark = (wxPdfBookmark*) m_outlines[prev]; + prevBookmark->SetNext(i); + bookmark->SetPrev(prev); + } + lru[currentLevel] = i; + level = currentLevel; + } + + // Outline items + int n = m_n + 1; + for (i = 0; i < nb; i++) + { + wxPdfBookmark* bookmark = (wxPdfBookmark*) m_outlines[i]; + NewObj(); + Out("<GetText()); + OutAscii(wxString::Format(_T("/Parent %d 0 R"), (n+bookmark->GetParent()))); + if (bookmark->GetPrev() >= 0) + { + OutAscii(wxString::Format(_T("/Prev %d 0 R"), (n+bookmark->GetPrev()))); + } + if (bookmark->GetNext() >= 0) + { + OutAscii(wxString::Format(_T("/Next %d 0 R"), (n+bookmark->GetNext()))); + } + if (bookmark->GetFirst() >= 0) + { + OutAscii(wxString::Format(_T("/First %d 0 R"), (n+bookmark->GetFirst()))); + } + if(bookmark->GetLast() >= 0) + { + OutAscii(wxString::Format(_T("/Last %d 0 R"), (n+bookmark->GetLast()))); + } + OutAscii(wxString::Format(_T("/Dest [%d 0 R /XYZ 0 "), (m_firstPageId+2*(bookmark->GetPage()-1))) + + Double2String((m_h-bookmark->GetY())*m_k,2) + wxString(_T(" null]"))); + Out("/Count 0>>"); + Out("endobj"); + } + // Outline root + NewObj(); + m_outlineRoot = m_n; + OutAscii(wxString::Format(_T("<>"), (n+lru[0]))); + Out("endobj"); +} + +void +wxPdfDocument::PutEncryption() +{ + Out("/Filter /Standard"); + switch (m_encryptor->GetRevision()) + { + case 4: + { + Out("/V 4"); + Out("/R 4"); + Out("/Length 128"); + Out("/CF <>>>"); + Out("/StrF /StdCF"); + Out("/StmF /StdCF"); + } + break; + case 3: + { + Out("/V 2"); + Out("/R 3"); + OutAscii(wxString::Format(_T("/Length %d"), m_encryptor->GetKeyLength())); + } + break; + case 2: + default: + { + Out("/V 1"); + Out("/R 2"); + } + break; + } + Out("/O (",false); + OutEscape((char*) m_encryptor->GetOValue(),32); + Out(")"); + Out("/U (",false); + OutEscape((char*) m_encryptor->GetUValue(),32); + Out(")"); + OutAscii(wxString::Format(_T("/P %d"), m_encryptor->GetPValue())); +} + +void +wxPdfDocument::PutSpotColors() +{ + wxPdfSpotColourMap::iterator spotIter = (*m_spotColors).begin(); + for (spotIter = (*m_spotColors).begin(); spotIter != (*m_spotColors).end(); spotIter++) + { + wxPdfSpotColour* spotColor = spotIter->second; + NewObj(); + wxString spotColorName = spotIter->first; + spotColorName.Replace(_T(" "),_T("#20")); + Out("[/Separation /", false); + OutAscii(spotColorName); + Out("/DeviceCMYK <<"); + Out("/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] "); + OutAscii(_T("/C1 [") + + Double2String(ForceRange(spotColor->GetCyan(), 0., 100.)/100., 4) + _T(" ") + + Double2String(ForceRange(spotColor->GetMagenta(), 0., 100.)/100., 4) + _T(" ") + + Double2String(ForceRange(spotColor->GetYellow(), 0., 100.)/100., 4) + _T(" ") + + Double2String(ForceRange(spotColor->GetBlack(), 0., 100.)/100., 4) + _T("] ")); + Out("/FunctionType 2 /Domain [0 1] /N 1>>]"); + Out("endobj"); + spotColor->SetObjIndex(m_n); + } +} + +void +wxPdfDocument::PutPatterns() +{ + wxPdfPatternMap::iterator patternIter = (*m_patterns).begin(); + for (patternIter = (*m_patterns).begin(); patternIter != (*m_patterns).end(); patternIter++) + { + wxPdfPattern* pattern = patternIter->second; + NewObj(); + pattern->SetObjIndex(m_n); + Out("<<"); + Out("/Type /Pattern"); + Out("/PatternType 1"); + Out("/PaintType 1"); + Out("/TilingType 1"); + OutAscii(wxString::Format(_T("/BBox [0 0 %d %d]"), pattern->GetImageWidth(), pattern->GetImageHeight())); + OutAscii(wxString::Format(_T("/XStep %d"), pattern->GetImageWidth())); + OutAscii(wxString::Format(_T("/YStep %d"), pattern->GetImageHeight())); + wxPdfImage *image = pattern->GetImage(); + OutAscii(wxString::Format(_T("/Resources << /XObject << /I%d %d 0 R >> >>"), image->GetIndex(), image->GetObjIndex())); + Out("/Matrix [ 1 0 0 1 0 0 ]"); + + wxString sdata = wxString::Format(_T("q %d 0 0 %d 0 0 cm /I%d Do Q"), pattern->GetImageWidth(), pattern->GetImageHeight(), image->GetIndex()); + wxMemoryOutputStream *p = new wxMemoryOutputStream; + p->Write(sdata.ToAscii(), sdata.Length()); + OutAscii(wxString(_T("/Length ")) + wxString::Format(_T("%ld"), CalculateStreamLength(p->TellO())) ); + Out(">>"); + PutStream(*p); + delete p; + Out("endobj"); + } +} + +void +wxPdfDocument::PutOc() +{ + wxPdfOcgHashMap &ocgHashMap = m_pdfOc.GetOcgMap(); + wxPdfOcgHashMap::iterator ocgIter = ocgHashMap.begin(); + for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { + wxPdfOcg* ocg = ocgIter->second; + NewObj(); + ocg->SetObjectIndex(m_n); + Out("<<"); + Out("/Type /OCG"); + OutAscii(wxString::Format(_T("/Name (%s)"), ocg->GetName())); + OutAscii(_T("/Intent ") + ocg->GetIntentString()); + Out(">>"); + Out("endobj"); + } +} + +void +wxPdfDocument::PutJavaScript() +{ + if (m_javascript.Length() > 0) + { + NewObj(); + m_nJS = m_n; + Out("<<"); + Out("/Names [", false); + OutAsciiTextstring(wxString(_T("EmbeddedJS")), false); + OutAscii(wxString::Format(_T(" %d 0 R ]"), m_n+1)); + Out(">>"); + Out("endobj"); + NewObj(); + Out("<<"); + Out("/S /JavaScript"); + Out("/JS ", false); + // TODO: Write Javascript object as stream + OutTextstring(m_javascript); + Out(">>"); + Out("endobj"); + } +} + +void +wxPdfDocument::PutResources() +{ + PutExtGStates(); + PutShaders(); + PutFonts(); + PutImages(); + PutTemplates(); + PutImportedObjects(); + PutSpotColors(); + PutPatterns(); + PutOc(); + + // Resource dictionary + (*m_offsets)[2-1] = m_buffer.TellO(); + Out("2 0 obj"); + Out("<<"); + PutResourceDict(); + Out(">>"); + Out("endobj"); + + PutBookmarks(); + PutJavaScript(); + + if (m_encrypted) + { + NewObj(); + m_encObjId = m_n; + Out("<<"); + PutEncryption(); + Out(">>"); + Out("endobj"); + } + +} + +wxString +wxPdfDocument::DoDecoration(double x, double y, const wxString& txt) +{ + // Decorate text + int top = m_currentFont->GetBBoxTopPosition(); + int up = m_currentFont->GetUnderlinePosition(); + int ut = m_currentFont->GetUnderlineThickness(); + double w = GetStringWidth(txt) + m_ws * txt.Freq(_T(' ')); + wxString decoration = _T(""); + if (m_decoration & wxPDF_FONT_UNDERLINE) + { + decoration = decoration + _T(" ") + + Double2String(x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - (y - up/1000.*m_fontSize)) * m_k,2) + wxString(_T(" ")) + + Double2String(w * m_k,2) + wxString(_T(" ")) + + Double2String(-ut/1000.*m_fontSizePt,2) + wxString(_T(" re f")); + } + if (m_decoration & wxPDF_FONT_OVERLINE) + { + up = (int) (top * 0.9); + decoration = decoration + _T(" ") + + Double2String(x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - (y - up/1000.*m_fontSize)) * m_k,2) + wxString(_T(" ")) + + Double2String(w * m_k,2) + wxString(_T(" ")) + + Double2String(-ut/1000.*m_fontSizePt,2) + wxString(_T(" re f")); + } + if (m_decoration & wxPDF_FONT_STRIKEOUT) + { + up = (int) (top * 0.26); + decoration = decoration + _T(" ") + + Double2String(x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - (y - up/1000.*m_fontSize)) * m_k,2) + wxString(_T(" ")) + + Double2String(w * m_k,2) + wxString(_T(" ")) + + Double2String(-ut/1000.*m_fontSizePt,2) + wxString(_T(" re f")); + } + return decoration; +} + +void +wxPdfDocument::TextEscape(const wxString& s, bool newline) +{ +#if wxUSE_UNICODE + wxString t = m_currentFont->ConvertCID2GID(s); + wxMBConv* conv = m_currentFont->GetEncodingConv(); + int len = conv->WC2MB(NULL, t, 0); + char* mbstr = new char[len+3]; + len = conv->WC2MB(mbstr, t, len+3); +#else + int len = s.Length();; + char* mbstr = new char[len+1]; + strcpy(mbstr,s.c_str()); +#endif + + OutEscape(mbstr,len); + if (newline) + { + Out("\n",false); + } + delete [] mbstr; +} + +int +wxPdfDocument::CalculateStreamLength(int length) +{ + int realLength = length; + if (m_encrypted) + { + realLength = m_encryptor->CalculateStreamLength(length); + } + return realLength; +} + +int +wxPdfDocument::CalculateStreamOffset() +{ + int offset = 0; + if (m_encrypted) + { + offset = m_encryptor->CalculateStreamOffset(); + } + return offset; +} + +void +wxPdfDocument::PutStream(wxMemoryOutputStream& s) +{ + Out("stream"); + if (s.GetLength() != 0) + { + if (m_encrypted) + { + wxMemoryInputStream instream(s); + int len = instream.GetSize(); + int lenbuf = CalculateStreamLength(len); + int ofs = CalculateStreamOffset(); + char* buffer = new char[lenbuf]; + instream.Read(&buffer[ofs],len); + m_encryptor->Encrypt(m_n, 0, (unsigned char*) buffer, len); + Out(buffer, lenbuf); + delete [] buffer; + } + else + { + wxMemoryInputStream tmp(s); + if(m_state==2) + { + if (!m_inTemplate) + { + (*m_pages)[m_page]->Write(tmp); + (*m_pages)[m_page]->Write("\n",1); + } + else + { + m_currentTemplate->m_buffer.Write(tmp); + m_currentTemplate->m_buffer.Write("\n",1); + } + } + else + { + m_buffer.Write(tmp); + m_buffer.Write("\n",1); + } + } + } + Out("endstream"); +} + +void +wxPdfDocument::OutEscape(const char* s, int len) +{ + int j; + for (j = 0; j < len; j++) + { + switch (s[j]) + { + case '\b': + Out("\\b",false); + break; + case '\f': + Out("\\f",false); + break; + case '\n': + Out("\\n",false); + break; + case '\r': + Out("\\r",false); + break; + case '\t': + Out("\\t",false); + break; + case '\\': + case '(': + case ')': + Out("\\",false); + default: + Out(&s[j],1,false); + break; + } + } +} + +void +wxPdfDocument::OutTextstring(const wxString& s, bool newline) +{ + // Format a text string + int ofs = CalculateStreamOffset(); +#if wxUSE_UNICODE + wxMBConvUTF16BE conv; + int len = conv.WC2MB(NULL, s, 0); + int lenbuf = CalculateStreamLength(len+2); + char* mbstr = new char[lenbuf+3]; + mbstr[ofs+0] = '\xfe'; + mbstr[ofs+1] = '\xff'; + len = 2 + conv.WC2MB(&mbstr[ofs+2], s, len+3); +#else + int len = s.Length();; + int lenbuf = CalculateStreamLength(len); + char* mbstr = new char[lenbuf+1]; + strcpy(&mbstr[ofs], s.c_str()); +#endif + + if (m_encrypted) + { + m_encryptor->Encrypt(m_n, 0, (unsigned char*) mbstr, len); + } + Out("(",false); + OutEscape(mbstr,lenbuf); + Out(")",newline); + delete [] mbstr; +} + +void +wxPdfDocument::OutRawTextstring(const wxString& s, bool newline) +{ + // Format a text string + int ofs = CalculateStreamOffset(); + int len = s.Length();; + int lenbuf = CalculateStreamLength(len); + char* mbstr = new char[lenbuf+1]; +#if wxUSE_UNICODE + int j; + for (j = 0; j < len; j++) + { + mbstr[ofs+j] = s[j]; + } + mbstr[ofs+len] = 0; +#else + strcpy(&mbstr[ofs],s.c_str()); +#endif + + if (m_encrypted) + { + m_encryptor->Encrypt(m_n, 0, (unsigned char*) mbstr, len); + } + Out("(",false); + OutEscape(mbstr,lenbuf); + Out(")",newline); + delete [] mbstr; +} + +void +wxPdfDocument::OutHexTextstring(const wxString& s, bool newline) +{ + static char hexDigits[17] = "0123456789ABCDEF"; + // Format a text string + int j; + int ofs = CalculateStreamOffset(); + int len = s.Length();; + int lenbuf = CalculateStreamLength(len); + char* mbstr = new char[lenbuf+1]; +#if wxUSE_UNICODE + for (j = 0; j < len; j++) + { + mbstr[ofs+j] = s[j]; + } + mbstr[ofs+len] = 0; +#else + strcpy(&mbstr[ofs],s.c_str()); +#endif + + if (m_encrypted) + { + m_encryptor->Encrypt(m_n, 0, (unsigned char*) mbstr, len); + } + + char hexDigit; + Out("<",false); + for (j = 0; j < lenbuf; ++j) + { + hexDigit = hexDigits[(mbstr[j] >> 4) & 0x0f]; + Out(&hexDigit, 1, false); + hexDigit = hexDigits[mbstr[j] & 0x0f]; + Out(&hexDigit, 1, false); + } + Out(">",newline); + delete [] mbstr; +} + +void +wxPdfDocument::OutAsciiTextstring(const wxString& s, bool newline) +{ + // Format an ASCII text string + int ofs = CalculateStreamOffset(); + int len = s.Length();; + int lenbuf = CalculateStreamLength(len); + char* mbstr = new char[lenbuf+1]; + strcpy(&mbstr[ofs],s.ToAscii()); + + if (m_encrypted) + { + m_encryptor->Encrypt(m_n, 0, (unsigned char*) mbstr, len); + } + Out("(",false); + OutEscape(mbstr,lenbuf); + Out(")",newline); + delete [] mbstr; +} + +void +wxPdfDocument::OutAscii(const wxString& s, bool newline) +{ + // Add a line of ASCII text to the document + Out((const char*) s.ToAscii(),newline); +} + +void +wxPdfDocument::Out(const char* s, bool newline) +{ + int len = strlen(s); + Out(s,len,newline); +} + +void +wxPdfDocument::Out(const char* s, int len, bool newline) +{ + if(m_state==2) + { + if (!m_inTemplate) + { + (*m_pages)[m_page]->Write(s,len); + if (newline) + { + (*m_pages)[m_page]->Write("\n",1); + } + } + else + { + m_currentTemplate->m_buffer.Write(s,len); + if (newline) + { + m_currentTemplate->m_buffer.Write("\n",1); + } + } + } + else + { + m_buffer.Write(s,len); + if (newline) + { + m_buffer.Write("\n",1); + } + } +} + +void +wxPdfDocument::OutPoint(double x, double y) +{ + OutAscii(Double2String(x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - y) * m_k,2) + wxString(_T(" m"))); + m_x = x; + m_y = y; +} + +void +wxPdfDocument::OutPointRelative(double dx, double dy) +{ + m_x += dx; + m_y += dy; + OutAscii(Double2String(m_x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - m_y) * m_k,2) + wxString(_T(" m"))); +} + +void +wxPdfDocument::OutLine(double x, double y) +{ + // Draws a line from last draw point + OutAscii(Double2String(x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - y) * m_k,2) + wxString(_T(" l"))); + m_x = x; + m_y = y; +} + +void +wxPdfDocument::OutLineRelative(double dx, double dy) +{ + m_x += dx; + m_y += dy; + // Draws a line from last draw point + OutAscii(Double2String(m_x * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - m_y) * m_k,2) + wxString(_T(" l"))); +} + +void +wxPdfDocument::OutCurve(double x1, double y1, double x2, double y2, double x3, double y3) +{ + // Draws a Bézier curve from last draw point + OutAscii(Double2String(x1 * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - y1) * m_k,2) + wxString(_T(" ")) + + Double2String(x2 * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - y2) * m_k,2) + wxString(_T(" ")) + + Double2String(x3 * m_k,2) + wxString(_T(" ")) + + Double2String((m_h - y3) * m_k,2) + wxString(_T(" c"))); + m_x = x3; + m_y = y3; +} + +void +wxPdfDocument::OutImage(wxPdfImage* currentImage, + double x, double y, double w, double h, const wxPdfLink& link) +{ + // Automatic width and height calculation if needed + if (w == 0 && h == 0) + { + // Put image at 72 dpi, apply scale factor + if (currentImage->IsFormObject()) + { + w = currentImage->GetWidth() / (20 * m_imgscale * m_k); + h = currentImage->GetHeight() / (20 * m_imgscale * m_k); + } + else + { + w = currentImage->GetWidth() / (m_imgscale * m_k); + h = currentImage->GetHeight() / (m_imgscale * m_k); + } + } + if (w == 0) + { + w = (h * currentImage->GetWidth()) / currentImage->GetHeight(); + } + if (h == 0) + { + h = (w * currentImage->GetHeight()) / currentImage->GetWidth(); + } + + double sw, sh, sx, sy; + if (currentImage->IsFormObject()) + { + sw = w * m_k / currentImage->GetWidth(); + sh = -h * m_k / currentImage->GetHeight(); + sx = x * m_k - sw * currentImage->GetX(); + sy = ((m_h - y) * m_k) - sh * currentImage->GetY(); + } + else + { + sw = w * m_k; + sh = h * m_k; + sx = x * m_k; + sy = (m_h-(y+h))*m_k; + } + OutAscii(wxString(_T("q ")) + + Double2String(sw,2) + wxString(_T(" 0 0 ")) + + Double2String(sh,2) + wxString(_T(" ")) + + Double2String(sx,2) + wxString(_T(" ")) + + Double2String(sy,2) + + wxString::Format(_T(" cm /I%d Do Q"),currentImage->GetIndex())); + + if (link.IsValid()) + { + Link(x,y,w,h,link); + } + + // set right-bottom corner coordinates + m_img_rb_x = x + w; + m_img_rb_y = y + h; + + // + if (m_inTemplate) + { + (*(m_currentTemplate->m_images))[currentImage->GetName()] = currentImage; + } +} + +bool wxPdfDocument::ms_seeded = false; +int wxPdfDocument::ms_s1 = 0; +int wxPdfDocument::ms_s2 = 0; + +#define MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m + +wxString +wxPdfDocument::GetUniqueId(const wxString& prefix) +{ + wxString uid = (prefix.Length() <= 114) ? prefix : prefix.Left(114); + + wxDateTime ts; + ts.SetToCurrent(); + + int q; + int z; + if (!ms_seeded) + { + ms_seeded = true; + ms_s1 = ts.GetSecond() ^ (~ts.GetMillisecond()); + if (ms_s1 == 0) ms_s1 = 1; + ms_s2 = wxGetProcessId(); + } + MODMULT(53668, 40014, 12211, 2147483563L, ms_s1); + MODMULT(52774, 40692, 3791, 2147483399L, ms_s2); + + z = ms_s1 - ms_s2; + if (z < 1) + { + z += 2147483562; + } + + uid += wxString::Format(_T("%08x%05x"), ts.GetSecond(), ts.GetMillisecond()); + uid += Double2String(z * 4.656613e-9,8); + + return uid; +} + +void +wxPdfDocument::Transform(double tm[6]) +{ + OutAscii(Double2String( tm[0],3) + wxString(_T(" ")) + + Double2String( tm[1],3) + wxString(_T(" ")) + + Double2String( tm[2],3) + wxString(_T(" ")) + + Double2String( tm[3],3) + wxString(_T(" ")) + + Double2String( tm[4],3) + wxString(_T(" ")) + + Double2String( tm[5],3) + wxString(_T(" cm"))); +} + +void +wxPdfDocument::SetFillGradient(double x, double y, double w, double h, int gradient) +{ + if (gradient > 0 && (size_t) gradient <= (*m_gradients).size()) + { + ClippingRect(x, y, w, h, false); + //set up transformation matrix for gradient + double tm[6]; + tm[0] = w * m_k; + tm[1] = 0; + tm[2] = 0; + tm[3] = h * m_k; + tm[4] = x * m_k; + tm[5] = (m_h - (y+h)) * m_k; + Transform(tm); + // paint the gradient + OutAscii(wxString::Format(_T("/Sh%d sh"), gradient)); + // restore previous Graphic State + Out("Q"); + } + else + { + wxLogError(_("wxPdfDocument::SetFillGradient: Gradient Id out of range.")); + } +} diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfoc.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfoc.cpp new file mode 100644 index 000000000..3b2b44c29 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfoc.cpp @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfoc.cpp +// Purpose: Implementation of pdfoc.h +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfoc.cpp Implementation of pdfoc.h + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#include "wx/pdfoc.h" + +// ---------------------------------------------------------------------------- +// wxPdfOc: class representing the optional content in a document +// ---------------------------------------------------------------------------- + +wxPdfOc::wxPdfOc() +{ + m_nextOcgId = 0; +} + +wxPdfOc::~wxPdfOc() +{ + //need to delete all the OCGs + wxPdfOcgHashMap::iterator ocg = m_ocgs.begin(); + while(m_ocgs.size() != 0) { + ocg = m_ocgs.begin(); + delete ocg->second; + m_ocgs.erase(ocg); + } +} + +void wxPdfOc::AddOcg(wxPdfOcg *ocg) +{ + //set its index + ocg->SetOcgIndex(m_nextOcgId); + m_nextOcgId++; + ocg->SetObjectIndex(0); //not set yet + + //store + m_ocgs[ocg->GetOcgIndex()] = ocg; +} + + +// ---------------------------------------------------------------------------- +// wxPdfOcg: class representing an optional content group +// ---------------------------------------------------------------------------- + +wxPdfOcg::wxPdfOcg(const wxString &name) +: m_name(name) +{ + m_intent = wxPDF_OCG_INTENT_VIEW; + m_index = 0; + m_objIndex = 0; + m_defaultState = true; +} + + +wxPdfOcg::~wxPdfOcg() +{ +} + +wxString& wxPdfOcg::GetIntentString(void) +{ + m_intentStr = _T("[ "); + if(m_intent & wxPDF_OCG_INTENT_VIEW) { + m_intentStr += _T("/View "); + } + if(m_intent & wxPDF_OCG_INTENT_DESIGN) { + m_intentStr += _T("/Design "); + } + m_intentStr += _T("]"); + + return m_intentStr; +} + + +// ---------------------------------------------------------------------------- +// wxPdfOcmd: class representing an optional membership dictionary +// ---------------------------------------------------------------------------- + +/*wxPdfOcmd::wxPdfOcmd() +{ + +} + +wxPdfOcmd::~wxPdfOcmd() +{ + +} + +void wxPdfOcmd::SetVisibilityPolicy(const VisibilityPolicy policy) +{ + m_policy = policy; + + switch(policy) { + case AllOn: + m_strPolicy = wxPdfName(_T("AllOn")); + break; + case AnyOn: + m_strPolicy = wxPdfName(_T("AnyOn")); + break; + case AnyOff: + m_strPolicy = wxPdfName(_T("AnyOff")); + break; + case AllOff: + m_strPolicy = wxPdfName(_T("AllOff")); + break; + } +} +*/ + + diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfparser.cpp b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfparser.cpp new file mode 100644 index 000000000..5d50c2db2 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc/src/pdfparser.cpp @@ -0,0 +1,1893 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: pdfparser.cpp +// Purpose: +// Author: Ulrich Telle +// Modified by: +// Created: 2006-10-15 +// RCS-ID: $$ +// Copyright: (c) Ulrich Telle +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +/// \file pdfparser.cpp Implementation of PDF parser + +// For compilers that support precompilation, includes . +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +// includes +#include +#include + +#include "wx/pdfdoc.h" +#include "wx/pdftemplate.h" +#include "wx/pdfobjects.h" +#include "wx/pdfparser.h" + +#include +WX_DEFINE_OBJARRAY(wxPdfXRef); + +wxPdfXRefEntry::wxPdfXRefEntry() +{ + m_type = -1; + m_ofs_idx = 0; + m_gen_ref = 0; +} + +wxPdfXRefEntry::~wxPdfXRefEntry() +{ +} + +void +wxPdfParser::ReserveXRef(size_t count) +{ + size_t currentCount = m_xref.GetCount(); + if (count > currentCount) + { + m_xref.Add(wxPdfXRefEntry(), count-currentCount); + } +} + +wxFileSystem* wxPdfParser::ms_fileSystem = NULL; + +wxFileSystem* +wxPdfParser::GetFileSystem() +{ + if (ms_fileSystem == NULL) + { + static wxFileSystem fileSystem; + //I dont know why this needs to be added manually, all the documents indicate that nothing needs to be done for local files. + fileSystem.AddHandler(new wxLocalFSHandler()); + ms_fileSystem = &fileSystem; + } + return ms_fileSystem; +} + +wxPdfParser::wxPdfParser(const wxString& filename, const wxString& password) +{ + m_objectQueue = new wxPdfObjectQueue(); + m_objectQueueLast = m_objectQueue; + m_objectMap = new wxPdfObjectMap(); + m_objStmCache = new wxPdfObjStmMap(); + m_tokens = NULL; + m_trailer = NULL; + m_root = NULL; + m_useRawStream = false; + m_cacheObjects = true; + + m_encrypted = false; + m_decryptor = NULL; + + m_filename = filename; + m_password = password; + + wxString fileURL = filename; + wxURI uri(filename); + if (!uri.HasScheme()) + { + fileURL = wxFileSystem::FileNameToURL(filename); + } + m_pdfFile = wxPdfParser::GetFileSystem()->OpenFile(fileURL); + if (m_pdfFile != NULL) + { + m_tokens = new wxPdfTokenizer(m_pdfFile->GetStream()); + m_initialized = ParseDocument(); + } +} + +wxPdfParser::~wxPdfParser() +{ + wxPdfObjectQueue* entry = m_objectQueue; + wxPdfObjectQueue* next; + while (entry != NULL) + { + wxPdfObject* object = entry->GetObject(); + if (object != NULL && object->IsIndirect()) + { + delete object; + } + next = entry->GetNext(); + delete entry; + entry = next; + } + delete m_objectMap; + + wxPdfObjStmMap::iterator objStm = m_objStmCache->begin(); + for (objStm = m_objStmCache->begin(); objStm != m_objStmCache->end(); objStm++) + { + if (objStm->second != NULL) + { + delete objStm->second; + } + } + delete m_objStmCache; + + size_t j; + for (j = 0; j < m_pages.GetCount(); j++) + { + wxPdfObject* obj = (wxPdfObject*) m_pages.Item(j); + delete obj; + } + m_pages.Clear(); + + if (m_trailer != NULL) + { + delete m_trailer; + } + if (m_root != NULL) + { + delete m_root; + } + + delete m_tokens; + if (m_pdfFile != NULL) + { + delete m_pdfFile; + } + + if (m_decryptor != NULL) + { + delete m_decryptor; + } +} + +bool +wxPdfParser::IsOk() +{ + return (m_pdfFile != NULL && m_initialized); +} + +void +wxPdfParser::AppendObject(int originalObjectId, int actualObjectId, wxPdfObject* obj) +{ + wxPdfObjectQueue* newEntry = new wxPdfObjectQueue(originalObjectId, actualObjectId, obj); + m_objectQueueLast->SetNext(newEntry); + m_objectQueueLast = newEntry; + (*m_objectMap)[originalObjectId] = newEntry; +} + +int +wxPdfParser::GetPageCount() +{ + return m_pages.GetCount(); +} + +bool +wxPdfParser::GetSourceInfo(wxPdfInfo& info) +{ + bool ok = false; + wxPdfDictionary* infoDict = (wxPdfDictionary*) ResolveObject(m_trailer->Get(_T("/Info"))); + if (infoDict != NULL && infoDict->GetType() == OBJTYPE_DICTIONARY) + { + typedef void (wxPdfInfo::*InfoSetter) (const wxString& value); + wxChar* entryList[] = { _T("/Title"), _T("/Author"), _T("/Subject"), + _T("/Keywords"), _T("/Creator"), _T("/Producer"), + _T("/CreationDate"), _T("/ModDate"), + NULL }; //, "Trapped") + InfoSetter entryFunc[] = { &wxPdfInfo::SetTitle, &wxPdfInfo::SetAuthor, &wxPdfInfo::SetSubject, + &wxPdfInfo::SetKeywords, &wxPdfInfo::SetCreator, &wxPdfInfo::SetProducer, + &wxPdfInfo::SetCreationDate, &wxPdfInfo::SetModDate, + NULL }; + wxString value; + size_t j; + for (j = 0; entryList[j] != NULL; j++) + { + wxPdfString* entry = (wxPdfString*) infoDict->Get(entryList[j]); + if (entry != NULL) + { + value = entry->GetValue(); +#if wxUSE_UNICODE + if ((value.Length() >= 2) && (value.GetChar(0) == 254) && (value.GetChar(1) == 255)) + { + wxMBConvUTF16BE conv; + size_t k; + size_t len = value.Length()-2; + char* mbstr = new char[len+2]; + for (k = 0; k < len; k++) + { + mbstr[k] = value.GetChar(k+2); + } + mbstr[len] = 0; + mbstr[len+1] = 0; + value = conv.cMB2WC(mbstr); + delete [] mbstr; + } +#endif + (info.*entryFunc[j])(value); + } + } + if (infoDict->IsIndirect()) + { + delete infoDict; + } + ok = true; + } + return ok; +} + +bool +wxPdfParser::ParseDocument() +{ + bool ok = false; + m_fileSize = m_tokens->GetLength(); + m_pdfVersion = m_tokens->CheckPdfHeader(); + if (m_pdfVersion != wxEmptyString) + { + if (ParseXRef()) + { + if (SetupDecryptor()) + { + m_root = (wxPdfDictionary*) m_trailer->Get(_T("/Root")); + m_root = (wxPdfDictionary*) ResolveObject(m_root); + if (m_root != NULL) + { + wxPdfName* versionEntry = (wxPdfName*) ResolveObject(m_root->Get(_T("/Version"))); + if (versionEntry != NULL) + { + wxString version = versionEntry->GetName(); + version = version.Mid(1, 3); + if (m_pdfVersion < version) + { + m_pdfVersion = version; + } + if (versionEntry->IsIndirect()) + { + delete versionEntry; + } + } + wxPdfDictionary* pages = (wxPdfDictionary*) ResolveObject(m_root->Get(_T("/Pages"))); + ok = ParsePageTree(pages); + delete pages; + } + } + } + } + return ok; +} + +bool +wxPdfParser::SetupDecryptor() +{ + bool ok = true; + wxPdfObject* encDic = m_trailer->Get(_T("/Encrypt")); + if (encDic == NULL || encDic->GetType() == OBJTYPE_NULL) + { + return true; + } + wxPdfDictionary* enc = (wxPdfDictionary*) ResolveObject(encDic); + wxPdfObject* obj; + wxPdfArray* documentIDs = (wxPdfArray*) ResolveObject(m_trailer->Get(_T("/ID"))); + wxString documentID; + if (documentIDs != NULL) + { + obj = (wxPdfObject*) documentIDs->Get(0); + if (obj->GetType() == OBJTYPE_STRING) + { + documentID = ((wxPdfString*) obj)->GetValue(); + } + if (documentIDs->IsIndirect()) + { + delete documentIDs; + } + } + + wxString uValue = wxEmptyString; + obj = enc->Get(_T("/U")); + if (obj->GetType() == OBJTYPE_STRING) + { + uValue = ((wxPdfString*) obj)->GetValue(); + if (uValue.Length() != 32) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Invalid length of U value.")); + ok = false; + } + } + + wxString oValue = wxEmptyString; + obj = enc->Get(_T("/O")); + if (obj->GetType() == OBJTYPE_STRING) + { + oValue = ((wxPdfString*) obj)->GetValue(); + if (oValue.Length() != 32) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Invalid length of O value.")); + ok = false; + } + } + + int rValue = 0; + obj = enc->Get(_T("/R")); + if (obj->GetType() == OBJTYPE_NUMBER) + { + rValue = ((wxPdfNumber*) obj)->GetInt(); + if (rValue != 2 && rValue != 3) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Unknown encryption type (%d)."), rValue); + ok = false; + } + } + else + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Illegal R value.")); + ok = false; + } + + int vValue = 0; + obj = enc->Get(_T("/V")); + if (obj != NULL && obj->GetType() == OBJTYPE_NUMBER) + { + vValue = ((wxPdfNumber*) obj)->GetInt(); + if (!((rValue == 2 && vValue == 1) || (rValue == 3 && vValue == 2))) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Unsupported V value.")); + ok = false; + } + } + else + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Illegal V value.")); + ok = false; + } + + int pValue = 0; + obj = enc->Get(_T("/P")); + if (obj->GetType() == OBJTYPE_NUMBER) + { + pValue = ((wxPdfNumber*) obj)->GetInt(); + // Check required permissions (Applications MUST respect the permission settings) + if ((pValue & REQUIRED_PERMISSIONS) != REQUIRED_PERMISSIONS) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Import of document not allowed due to missing permissions.")); + ok = false; + } + } + else + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Illegal P value.")); + ok = false; + } + + int lengthValue = 40; // Default for revisison 2 + if (rValue == 3) + { + // Get the key length if revision is 3 + obj = enc->Get(_T("/Length")); + if (obj->GetType() == OBJTYPE_NUMBER) + { + lengthValue = ((wxPdfNumber*) obj)->GetInt(); + if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Illegal Length value.")); + ok = false; + } + } + else + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Illegal Length value.")); + ok = false; + } + } + + if (enc->IsIndirect()) + { + delete enc; + } + + if (ok) + { + m_encrypted = true; + m_decryptor = new wxPdfEncrypt(); + if (!m_decryptor->Authenticate(documentID, m_password, uValue, oValue, pValue, lengthValue, rValue)) + { + wxLogError(_T("wxPdfParser::SetupDecryptor: Bad password.")); + ok = false; + } + } + + return ok; +} + +bool +wxPdfParser::ParsePageTree(wxPdfDictionary* pages) +{ + bool ok = false; + // Get the kids dictionary + wxPdfArray* kids = (wxPdfArray*) ResolveObject(pages->Get(_T("/Kids"))); + if (kids != NULL) + { + size_t nKids = kids->GetSize(); + size_t j; + ok = true; + for (j = 0; j < nKids; j++) + { + wxPdfDictionary* page = (wxPdfDictionary*) ResolveObject(kids->Get(j)); + wxPdfName* type = (wxPdfName*) page->Get(_T("/Type")); + if (type->GetName() == _T("/Pages")) + { + // If one of the kids is an embedded + // /Pages array, resolve it as well. + ok = ok && ParsePageTree(page); + delete page; + } + else + { + m_pages.Add(page); + } + } + if (kids->IsIndirect()) + { + delete kids; + } + } + else + { + wxLogError(_("wxPdfParser::ParsePageTree: Cannot find /Kids in current /Page-Dictionary")); + } + return ok; +} + +wxPdfObject* +wxPdfParser::GetPageResources(int pageno) +{ + wxPdfObject* resources = NULL; + if (pageno >= 0 && pageno < GetPageCount()) + { + resources = GetPageResources((wxPdfObject*) m_pages[pageno]); + } + return resources; +} + +wxPdfObject* +wxPdfParser::GetPageResources(wxPdfObject* page) +{ + wxPdfObject* resources = NULL; + wxPdfDictionary* dic = (wxPdfDictionary*) ResolveObject(page); + + // If the current object has a resources dictionary associated with it, + // we use it. Otherwise, we move back to its parent object. + wxPdfObject* resourceRef = ResolveObject(dic->Get(_T("/Resources"))); + if (resourceRef != NULL) + { + resources = ResolveObject(resourceRef); + } + else + { + wxPdfObject* parent = ResolveObject(dic->Get(_T("/Parent"))); + if (parent != NULL) + { + resources = GetPageResources(parent); + delete parent; + } + } + return resources; +} + +void +wxPdfParser::GetContent(int pageno, wxArrayPtrVoid& contents) +{ + if (pageno >= 0 && pageno < GetPageCount()) + { + wxPdfObject* content = ((wxPdfDictionary*) m_pages[pageno])->Get(_T("/Contents")); + GetPageContent(content, contents); + } +} + +void +wxPdfParser::GetPageContent(wxPdfObject* contentRef, wxArrayPtrVoid& contents) +{ + int type = contentRef->GetType(); + if (type == OBJTYPE_INDIRECT) + { + wxPdfObject* content = ResolveObject(contentRef); + if (content->GetType() == OBJTYPE_ARRAY) + { + GetPageContent(content, contents); + delete content; + } + else + { + contents.Add(content); + } + } + else if (type == OBJTYPE_ARRAY) + { + wxPdfArray* contentArray = (wxPdfArray*) contentRef; + size_t n = contentArray->GetSize(); + size_t j; + for (j = 0; j < n; j++) + { + GetPageContent(contentArray->Get(j), contents); + } + } +} + +wxPdfArrayDouble* +wxPdfParser::GetPageMediaBox(int pageno) +{ + wxPdfArrayDouble* box = GetPageBox((wxPdfDictionary*) m_pages[pageno], _T("/MediaBox")); + return box; +} + +wxPdfArrayDouble* +wxPdfParser::GetPageCropBox(int pageno) +{ + wxPdfArrayDouble* box = GetPageBox((wxPdfDictionary*) m_pages[pageno], _T("/CropBox")); + if (box == NULL) + { + box = GetPageBox((wxPdfDictionary*) m_pages[pageno], _T("/MediaBox")); + } + return box; +} + +wxPdfArrayDouble* +wxPdfParser::GetPageBleedBox(int pageno) +{ + wxPdfArrayDouble* box = GetPageBox((wxPdfDictionary*) m_pages[pageno], _T("/BleedBox")); + if (box == NULL) + { + box = GetPageCropBox(pageno); + } + return box; +} + +wxPdfArrayDouble* +wxPdfParser::GetPageTrimBox(int pageno) +{ + wxPdfArrayDouble* box = GetPageBox((wxPdfDictionary*) m_pages[pageno], _T("/TrimBox")); + if (box == NULL) + { + box = GetPageCropBox(pageno); + } + return box; +} + +wxPdfArrayDouble* +wxPdfParser::GetPageArtBox(int pageno) +{ + wxPdfArrayDouble* box = GetPageBox((wxPdfDictionary*) m_pages[pageno], _T("/ArtBox")); + if (box == NULL) + { + box = GetPageCropBox(pageno); + } + return box; +} + +wxPdfArrayDouble* +wxPdfParser::GetPageBox(wxPdfDictionary* page, const wxString& boxIndex) +{ + wxPdfArrayDouble* pageBox = NULL; + wxPdfArray* box = (wxPdfArray*) ResolveObject(page->Get(boxIndex)); + if (box == NULL) + { + wxPdfDictionary* parent = (wxPdfDictionary*) ResolveObject(page->Get(_T("/Parent"))); + if (parent != NULL) + { + pageBox = GetPageBox(parent, boxIndex); + delete parent; + } + } + else + { + pageBox = new wxPdfArrayDouble(); + size_t j; + for (j = 0; j < box->GetSize(); j++) + { + wxPdfNumber* item = (wxPdfNumber*) box->Get(j); + pageBox->Add(item->GetValue()); + } + } + return pageBox; +} + +bool +wxPdfParser::ParseXRef() +{ + m_tokens->Seek(m_tokens->GetStartXRef()); + m_tokens->NextToken(); + if (m_tokens->GetStringValue() != _T("startxref")) + { + wxLogError(_("wxPdfParser::ParseXRef: 'startxref' not found.")); + return false; + } + m_tokens->NextToken(); + if (m_tokens->GetTokenType() != /*PRTokeniser.*/ TOKEN_NUMBER) + { + wxLogError(_("wxPdfParser::ParseXRef: 'startxref' is not followed by a number.")); + return false; + } + int startxref = m_tokens->GetIntValue(); + + if (!ParseXRefStream(startxref, true)) + { + m_xref.Clear(); + m_tokens->Seek(startxref); + m_trailer = ParseXRefSection(); + wxPdfDictionary* trailer1 = m_trailer; + wxPdfDictionary* trailer2 = NULL; + while (trailer1 != NULL) + { + wxPdfNumber* prev = (wxPdfNumber*) trailer1->Get(_T("/Prev")); + trailer2 = trailer1; + if (prev != NULL) + { + m_tokens->Seek(prev->GetInt()); + trailer1 = ParseXRefSection(); + } + else + { + trailer1 = NULL; + } + if (trailer2 != m_trailer) + { + delete trailer2; + } + } + } + return (m_trailer != NULL); +} + +wxPdfDictionary* +wxPdfParser::ParseXRefSection() +{ + m_tokens->NextValidToken(); + if (m_tokens->GetStringValue() != _T("xref")) + { + wxLogError(_("wxPdfParser::ParseXRefSection: xref subsection not found.")); + return NULL; + } + int start = 0; + int end = 0; + int pos = 0; + int gen = 0; + while (true) + { + m_tokens->NextValidToken(); + if (m_tokens->GetStringValue() == _T("trailer")) + break; + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + wxLogError(_("wxPdfParser::ParseXRefSection: Object number of the first object in this xref subsection not found.")); + return NULL; + } + start = m_tokens->GetIntValue(); + m_tokens->NextValidToken(); + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + wxLogError(_("wxPdfParser::ParseXRefSection: Number of entries in this xref subsection not found.")); + return NULL; + } + end = m_tokens->GetIntValue() + start; + if (start == 1) + { // fix incorrect start number + int back = m_tokens->Tell(); + m_tokens->NextValidToken(); + pos = m_tokens->GetIntValue(); + m_tokens->NextValidToken(); + gen = m_tokens->GetIntValue(); + if (pos == 0 && gen == 65535) + { + --start; + --end; + } + m_tokens->Seek(back); + } + ReserveXRef(end); + + int k; + for (k = start; k < end; ++k) + { + wxPdfXRefEntry& xrefEntry = m_xref[k]; + m_tokens->NextValidToken(); + pos = m_tokens->GetIntValue(); + m_tokens->NextValidToken(); + gen = m_tokens->GetIntValue(); + m_tokens->NextValidToken(); + if (m_tokens->GetStringValue() == _T("n")) + { + if (xrefEntry.m_ofs_idx == 0 && xrefEntry.m_gen_ref == 0) + { + // TODO: if (pos == 0) + // wxLogError(_T("File position 0 cross-reference entry in this xref subsection")); + xrefEntry.m_ofs_idx = pos; + xrefEntry.m_gen_ref = gen; + xrefEntry.m_type = 1; + } + } + else if (m_tokens->GetStringValue() == _T("f")) + { + if (xrefEntry.m_ofs_idx == 0 && xrefEntry.m_gen_ref == 0) + { + xrefEntry.m_ofs_idx = -1; + xrefEntry.m_gen_ref = 0; + xrefEntry.m_type = 0; + } + } + else + { + wxLogError(_("wxPdfParser:ReadXRefSection: Invalid cross-reference entry in this xref subsection.")); + return NULL; + } + } + } + wxPdfDictionary* trailer = (wxPdfDictionary*) ParseObject(); + wxPdfNumber* xrefSize = (wxPdfNumber*) trailer->Get(_T("/Size")); + ReserveXRef(xrefSize->GetInt()); + + wxPdfObject* xrs = trailer->Get(_T("/XRefStm")); + if (xrs != NULL && xrs->GetType() == OBJTYPE_NUMBER) + { + int loc = ((wxPdfNumber*) xrs)->GetInt(); + ParseXRefStream(loc, false); + } + return trailer; +} + +bool +wxPdfParser::ParseXRefStream(int ptr, bool setTrailer) +{ + int idx, k; + + m_tokens->Seek(ptr); + int streamRef = 0; + if (!m_tokens->NextToken()) + { + return false; + } + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + return false; + } + streamRef = m_tokens->GetIntValue(); + if (!m_tokens->NextToken() || m_tokens->GetTokenType() != TOKEN_NUMBER) + { + return false; + } + if (!m_tokens->NextToken() || m_tokens->GetStringValue() != _T("obj")) + { + return false; + } + wxPdfObject* object = ParseObject(); + wxPdfStream* stm = NULL; + if (object->GetType() == OBJTYPE_STREAM) + { + stm = (wxPdfStream*) object; + if (((wxPdfName*) stm->Get(_T("/Type")))->GetName() != _T("/XRef")) + { + delete object; + return false; + } + } + int size = ((wxPdfNumber*) stm->Get(_T("/Size")))->GetInt(); + bool indexAllocated = false; + wxPdfArray* index; + wxPdfObject* obj = stm->Get(_T("/Index")); + if (obj == NULL) + { + indexAllocated = true; + index = new wxPdfArray(); + index->Add(0); + index->Add(size); + } + else + { + index = (wxPdfArray*) obj; + } + wxPdfArray* w = (wxPdfArray*) stm->Get(_T("/W")); + int prev = -1; + obj = stm->Get(_T("/Prev")); + if (obj != NULL) + { + prev = ((wxPdfNumber* )obj)->GetInt(); + } + // Each xref pair is a position + // type 0 -> -1, 0 + // type 1 -> offset, 0 + // type 2 -> index, obj num + ReserveXRef(size); + + GetStreamBytes(stm); + wxMemoryOutputStream* streamBuffer = stm->GetBuffer(); + wxMemoryInputStream streamBytes(*streamBuffer); + size_t inLength = streamBytes.GetSize(); + char* buffer = new char[inLength]; + streamBytes.Read(buffer, inLength); + + int bptr = 0; + int wc[3]; + for (k = 0; k < 3; ++k) + { + wc[k] = ((wxPdfNumber*) (w->Get(k)))->GetInt(); + } + for (idx = 0; (size_t) idx < index->GetSize(); idx += 2) + { + int start = ((wxPdfNumber*) (index->Get(idx)))->GetInt(); + int length = ((wxPdfNumber*) (index->Get(idx + 1)))->GetInt(); + ReserveXRef(start+length); + while (length-- > 0) + { + wxPdfXRefEntry& xrefEntry = m_xref[start]; + int type = 1; + if (wc[0] > 0) + { + type = 0; + for (k = 0; k < wc[0]; ++k) + { + type = (type << 8) + (buffer[bptr++] & 0xff); + } + } + int field2 = 0; + for (k = 0; k < wc[1]; ++k) + { + field2 = (field2 << 8) + (buffer[bptr++] & 0xff); + } + int field3 = 0; + for (k = 0; k < wc[2]; ++k) + { + field3 = (field3 << 8) + (buffer[bptr++] & 0xff); + } + if (xrefEntry.m_ofs_idx == 0 && xrefEntry.m_gen_ref == 0) + { + switch (type) + { + case 0: + xrefEntry.m_type = 0; + xrefEntry.m_ofs_idx = -1; + xrefEntry.m_gen_ref = 0; + break; + case 1: + xrefEntry.m_type = 1; + xrefEntry.m_ofs_idx = field2; + xrefEntry.m_gen_ref = field3; + break; + case 2: + xrefEntry.m_type = 2; + xrefEntry.m_ofs_idx = field3; + xrefEntry.m_gen_ref = field2; + break; + } + } + start++; + } + } + delete [] buffer; + if ((size_t) streamRef < m_xref.GetCount()) + { + m_xref[streamRef].m_ofs_idx = -1; + } + if (indexAllocated) + { + delete index; + } + + // Set the first xref stream dictionary as the trailer dictionary + if (setTrailer && m_trailer == NULL) + { + + m_trailer = stm->GetDictionary(); + stm->SetDictionary(NULL); + } + delete stm; + + if (prev == -1) + { + return true; + } + return ParseXRefStream(prev, false); +} + +wxPdfDictionary* +wxPdfParser::ParseDictionary() +{ + wxPdfDictionary* dic = new wxPdfDictionary(); + while (true) + { + m_tokens->NextValidToken(); + if (m_tokens->GetTokenType() == TOKEN_END_DICTIONARY) + break; + if (m_tokens->GetTokenType() != TOKEN_NAME) + { + wxLogError(_("wxPdfParser::ParseDictionary: Dictionary key is not a name.")); + break; + } + wxPdfName* name = new wxPdfName(m_tokens->GetStringValue()); + wxPdfObject* obj = ParseObject(); + int type = obj->GetType(); + if (-type == TOKEN_END_DICTIONARY) + { + wxLogError(_("wxPdfParser::ParseDictionary: Unexpected '>>'.")); + delete obj; + delete name; + break; + } + if (-type == TOKEN_END_ARRAY) + { + wxLogError(_("wxPdfParser::ParseDictionary: Unexpected ']'.")); + delete obj; + delete name; + break; + } + dic->Put(name, obj); + delete name; + } + return dic; +} + +wxPdfArray* +wxPdfParser::ParseArray() +{ + wxPdfArray* array = new wxPdfArray(); + while (true) + { + wxPdfObject* obj = ParseObject(); + int type = obj->GetType(); + if (-type == TOKEN_END_ARRAY) + { + delete obj; + break; + } + if (-type == TOKEN_END_DICTIONARY) + { + wxLogError(_("wxPdfParser::ParseArray: Unexpected '>>'.")); + delete obj; + break; + } + array->Add(obj); + } + return array; +} + +wxPdfObject* +wxPdfParser::ParseObject() +{ + wxPdfObject* obj; + m_tokens->NextValidToken(); + int type = m_tokens->GetTokenType(); + switch (type) + { + case TOKEN_START_DICTIONARY: + { + wxPdfDictionary* dic = ParseDictionary(); + int pos = m_tokens->Tell(); + // be careful in the trailer. May not be a "next" token. + if (m_tokens->NextToken() && m_tokens->GetStringValue() == _T("stream")) + { + int ch = m_tokens->ReadChar(); + if (ch != '\n') + ch = m_tokens->ReadChar(); + if (ch != '\n') + m_tokens->BackOnePosition(ch); + wxPdfStream* stream = new wxPdfStream(m_tokens->Tell()); + stream->SetDictionary(dic); + obj = stream; + } + else + { + m_tokens->Seek(pos); + obj = dic; + } + } + break; + + case TOKEN_START_ARRAY: + { + obj = ParseArray(); + } + break; + + case TOKEN_NUMBER: + { + obj = new wxPdfNumber(m_tokens->GetStringValue()); + } + break; + + case TOKEN_STRING: + { + wxString token = m_tokens->GetStringValue(); + // Decrypt if necessary + if (m_encrypted) + { + m_decryptor->Encrypt(m_objNum, m_objGen, token); + } + + wxPdfString* strObj = new wxPdfString(token); + strObj->SetIsHexString(m_tokens->IsHexString()); + obj = strObj; + } + break; + + case TOKEN_NAME: + { + obj = new wxPdfName(m_tokens->GetStringValue()); + } + break; + + case TOKEN_REFERENCE: + { + int num = m_tokens->GetReference(); + obj = new wxPdfIndirectReference(num, m_tokens->GetGeneration()); + } + break; + + case TOKEN_BOOLEAN: + { + obj = new wxPdfBoolean((m_tokens->GetStringValue() == _T("true"))); + } + break; + + case TOKEN_NULL: + { + obj = new wxPdfNull(); + } + break; + + default: + { + wxString token = m_tokens->GetStringValue(); + obj = new wxPdfLiteral(-type, m_tokens->GetStringValue()); + } + break; + } + return obj; +} + +wxPdfObject* +wxPdfParser::ResolveObject(wxPdfObject* obj) +{ + if (obj != NULL && obj->GetType() == OBJTYPE_INDIRECT) + { + wxPdfIndirectReference* ref = (wxPdfIndirectReference*)obj; + int idx = ref->GetNumber(); + obj = ParseSpecificObject(idx); + obj->SetIndirect(true); + } + return obj; +} + +wxPdfObject* +wxPdfParser::ParseSpecificObject(int idx) +{ + wxPdfObject* obj = NULL; + if ((size_t)(idx) >= m_xref.GetCount()) + { + return NULL; + } + obj = ParseDirectObject(idx); + return obj; +} + +wxPdfObject* +wxPdfParser::ParseDirectObject(int k) +{ + int objIndex = 0; + int objStreamIndex = 0; + bool isCached = false; + wxPdfObject* obj = NULL; + + // Check for free object + if (m_xref[k].m_type == 0) + { + return NULL; + } + int pos = m_xref[k].m_ofs_idx; + if (m_xref[k].m_type == 2) + { + objIndex = m_xref[k].m_gen_ref; + wxPdfObjStmMap::iterator objStm = m_objStmCache->find(objIndex); + if (objStm != m_objStmCache->end()) + { + obj = objStm->second; + isCached = true; + } + else + { + objStreamIndex = m_xref[k].m_gen_ref; + pos = m_xref[objStreamIndex].m_ofs_idx; + } + } + if (!isCached) + { + m_tokens->Seek(pos); + m_tokens->NextValidToken(); + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + wxLogError(_T("wxPdfParser::ParseSingleObject: Invalid object number.")); + return NULL; + } + m_objNum = m_tokens->GetIntValue(); + m_tokens->NextValidToken(); + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + wxLogError(_T("wxPdfParser::ParseSingleObject: Invalid generation number.")); + return NULL; + } + m_objGen = m_tokens->GetIntValue(); + m_tokens->NextValidToken(); + if (m_tokens->GetStringValue() != _T("obj")) + { + wxLogError(_T("wxPdfParser::ParseSingleObject: Token 'obj' expected.")); + return NULL; + } + obj = ParseObject(); + } + + // TODO: Check for valid 'endstream' + + if (m_xref[k].m_type == 2) + { + m_objNum = k; + m_objGen = 0; + wxPdfStream* objStream = (wxPdfStream*) obj; + obj = ParseObjectStream((wxPdfStream*) obj, m_xref[k].m_ofs_idx); + if (m_cacheObjects) + { + if (!isCached) + { + (*m_objStmCache)[objIndex] = objStream; + } + } + else + { + delete objStream; + } + } + + if (obj != NULL) + { + obj->SetObjNum(m_objNum, m_objGen); + } + if (obj->GetType() == OBJTYPE_STREAM) + { + GetStreamBytes((wxPdfStream*) obj); + } + return obj; +} + +wxPdfObject* +wxPdfParser::ParseObjectStream(wxPdfStream* objStm, int idx) +{ + wxPdfObject* obj = NULL; + + wxPdfNumber* firstNumber = (wxPdfNumber*) ResolveObject(objStm->Get(_T("/First"))); + int first = firstNumber->GetInt(); + if (objStm->GetBuffer() == NULL) + { + bool saveUseRawStream = m_useRawStream; + m_useRawStream = false; + GetStreamBytes(objStm); + m_useRawStream = saveUseRawStream; + } + + bool saveEncrypted = m_encrypted; + m_encrypted = false; + wxPdfTokenizer* saveTokens = m_tokens; + wxMemoryInputStream objStream(*(objStm->GetBuffer())); + m_tokens = new wxPdfTokenizer(&objStream); + + int address = 0; + bool ok = true; + if (!objStm->HasObjOffsets()) + { + // Read object offsets + wxArrayInt* objOffsets = objStm->GetObjOffsets(); + int objCount = idx + 1; + if (m_cacheObjects) + { + wxPdfNumber* objCountNumber = (wxPdfNumber*) ResolveObject(objStm->Get(_T("/N"))); + objCount = objCountNumber->GetInt(); + } + int offset; + int k; + for (k = 0; k < objCount; ++k) + { + ok = m_tokens->NextToken(); + if (!ok) + break; + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + ok = false; + break; + } + ok = m_tokens->NextToken(); + if (!ok) + break; + if (m_tokens->GetTokenType() != TOKEN_NUMBER) + { + ok = false; + break; + } + offset = m_tokens->GetIntValue() + first; + if (m_cacheObjects) + { + objOffsets->Add(offset); + } + if (k == idx) + { + address = offset; + } + } + if (ok) + { + objStm->SetHasObjOffsets(m_cacheObjects); + } + } + else + { + address = objStm->GetObjOffset(idx); + ok = (address > 0); + } + if (ok) + { + m_tokens->Seek(address); + obj = ParseObject(); + } + else + { + wxLogError(_T("wxPdfParser::ParseOneObjStm: Error reading ObjStm.")); + } + + delete m_tokens; + m_tokens = saveTokens; + m_encrypted = saveEncrypted; + + return obj; +} + +void +wxPdfParser::GetStreamBytes(wxPdfStream* stream) +{ + GetStreamBytesRaw(stream); + + // Do not decode the content of resource object streams + if (m_useRawStream) return; + + // Check whether the stream buffer is empty + wxMemoryOutputStream* osIn = stream->GetBuffer(); + if (osIn->GetLength() == 0) return; + + size_t j; + wxArrayPtrVoid filters; + wxPdfObject* filter = ResolveObject(stream->Get(_T("/Filter"))); + if (filter != NULL) + { + int type = filter->GetType(); + if (type == OBJTYPE_NAME) + { + filters.Add(filter); + } + else if (type == OBJTYPE_ARRAY) + { + wxPdfArray* filterArray = (wxPdfArray*) filter; + size_t size = filterArray->GetSize(); + for (j = 0; j < size; j++) + { + filters.Add(filterArray->Get(j)); + } + } + + // Read decode parameters if available + wxArrayPtrVoid dp; + wxPdfObject* dpo = ResolveObject(stream->Get(_T("/DecodeParms"))); + if (dpo == NULL || (dpo->GetType() != OBJTYPE_DICTIONARY && dpo->GetType() != OBJTYPE_ARRAY)) + { + dpo = ResolveObject(stream->Get(_T("/DP"))); + } + if (dpo != NULL) + { + if (dpo->GetType() == OBJTYPE_DICTIONARY) + { + dp.Add(dpo); + } + else if (dpo->GetType() == OBJTYPE_ARRAY) + { + wxPdfArray* dpArray = (wxPdfArray*) dpo; + size_t size = dpArray->GetSize(); + for (j = 0; j < size; j++) + { + dp.Add(dpArray->Get(j)); + } + } + } + + wxPdfObject* dicParam = NULL; + wxMemoryOutputStream* osOut = NULL; + for (j = 0; j < filters.GetCount(); j++) + { + osIn = stream->GetBuffer(); + wxPdfName* name = (wxPdfName*) filters[j]; + if (name->GetName() == _T("/FlateDecode") || name->GetName() == _T("/Fl")) + { + osOut = FlateDecode(osIn); + if (j < dp.GetCount()) + { + wxMemoryOutputStream* osIn2 = osOut; + dicParam = (wxPdfObject*) dp[j]; + osOut = DecodePredictor(osIn2, dicParam); + if (osOut != osIn2) + { + delete osIn2; + } + } + } + else if(name->GetName() == _T("/ASCIIHexDecode") || name->GetName() == _T("/AHx")) + { + osOut = ASCIIHexDecode(osIn); + } + else if(name->GetName() == _T("/ASCII85Decode") || name->GetName() == _T("/A85")) + { + osOut = ASCII85Decode(osIn); + } + else if(name->GetName() == _T("/LZWDecode")) + { + osOut = LZWDecode(osIn); + if (j < dp.GetCount()) + { + wxMemoryOutputStream* osIn2 = osOut; + dicParam = (wxPdfObject*) dp[j]; + osOut = DecodePredictor(osIn2, dicParam); + if (osOut != osIn2) + { + delete osIn2; + } + } + } + else + { + wxLogError(wxString(_T("wxPdfParser::GetStreamBytes: Filter '")) + + name->GetName() + wxString(_T("' not supported"))); + } + if (osOut != NULL) + { + stream->SetBuffer(osOut); + if (osIn != osOut) + { + delete osIn; + } + } + } + } +} + +void +wxPdfParser::GetStreamBytesRaw(wxPdfStream* stream) +{ + wxPdfNumber* streamLength = (wxPdfNumber*) ResolveObject(stream->Get(_T("/Length"))); + size_t size = streamLength->GetInt(); + m_tokens->Seek(stream->GetOffset()); + wxMemoryOutputStream* memoryBuffer = NULL; + wxMemoryOutputStream* streamBuffer = m_tokens->ReadBuffer(size); + + if (m_encrypted && size > 0) + { + wxMemoryInputStream inData(*streamBuffer); + delete streamBuffer; + memoryBuffer = new wxMemoryOutputStream(); + unsigned char* buffer = new unsigned char[size]; + inData.Read(buffer, size); + if (inData.LastRead() == size) + { + m_decryptor->Encrypt(m_objNum, m_objGen, buffer, size); + memoryBuffer->Write(buffer, size); + } + delete [] buffer; + memoryBuffer->Close(); + } + else + { + memoryBuffer = streamBuffer; + } + + stream->SetBuffer(memoryBuffer); + if (streamLength->IsIndirect()) + { + delete streamLength; + } +} + +// --- Tokenizer + +wxPdfTokenizer::wxPdfTokenizer(wxInputStream* inputStream) +{ + m_inputStream = inputStream; +} + +wxPdfTokenizer::~wxPdfTokenizer() +{ +} + +off_t +wxPdfTokenizer::Seek(off_t pos) +{ + return m_inputStream->SeekI(pos); +} + +off_t +wxPdfTokenizer::Tell() +{ + return m_inputStream->TellI(); +} + +void +wxPdfTokenizer::BackOnePosition(int ch) +{ + if (ch != -1) + { + off_t pos = Tell(); + if (pos > 0) pos--; + Seek(pos); + } +} + +off_t +wxPdfTokenizer::GetLength() +{ + return m_inputStream->GetLength(); +} + +int +wxPdfTokenizer::ReadChar() +{ + int readChar; + char ch = m_inputStream->GetC(); + readChar = (m_inputStream->LastRead() > 0) ? (unsigned char) ch : -1; + return readChar; +} + +wxMemoryOutputStream* +wxPdfTokenizer::ReadBuffer(size_t size) +{ + wxMemoryOutputStream* memoryBuffer = new wxMemoryOutputStream(); + if (size > 0) + { + char* buffer = new char[size]; + m_inputStream->Read(buffer, size); + if (m_inputStream->LastRead() == size) + { + memoryBuffer->Write(buffer, size); + } + delete [] buffer; + } + memoryBuffer->Close(); + return memoryBuffer; +} + +off_t +wxPdfTokenizer::GetStartXRef() +{ + off_t size = GetLength(); + if (size > 1024) size = 1024; + off_t pos = GetLength() - size; + m_inputStream->SeekI(pos); + wxString str = ReadString(1024); + int idx = str.rfind(wxString(_T("startxref"))); + if (idx < 0) + { + wxLogError(_("wxPdfTokenizer::GetStartXRef: PDF startxref not found.")); + } + return pos + idx; +} + +wxString +wxPdfTokenizer::CheckPdfHeader() +{ + wxString version = wxEmptyString; + m_inputStream->SeekI(0); + wxString str = ReadString(1024); + int idx = str.Find(_T("%PDF-1.")); + if (idx >= 0) + { + m_inputStream->SeekI(idx); + version = str.Mid(idx + 5, 3); + } + else + { + m_inputStream->SeekI(0); + wxLogError(_("wxPdfTokenizer::GetStartXref: PDF header signature not found.")); + } + return version; +} + +wxString +wxPdfTokenizer::ReadString(int size) +{ + wxString buf; + int ch; + while (size > 0) + { + size--; + ch = ReadChar(); + if (ch == -1) + break; + buf += ch; + } + return buf; +} + +bool +wxPdfTokenizer::NextToken() +{ + static wxString buffer; + buffer = wxEmptyString; + m_stringValue = wxEmptyString; + int ch = 0; + do + { + ch = ReadChar(); + } + while (ch != -1 && IsWhitespace(ch)); + + if (ch == -1) + return false; + + switch (ch) + { + case '[': + m_type = TOKEN_START_ARRAY; + break; + case ']': + m_type = TOKEN_END_ARRAY; + break; + case '/': + { + m_type = TOKEN_NAME; + buffer += ch; + while (true) + { + ch = ReadChar(); + if (IsDelimiterOrWhitespace(ch)) + break; + buffer += ch; + } + BackOnePosition(ch); + break; + } + case '>': + ch = ReadChar(); + if (ch != '>') + { + wxLogError(_("wxPdfTokenizer::NextToken: '>' not expected.")); + return false; + } + m_type = TOKEN_END_DICTIONARY; + break; + case '<': + { + int v1 = ReadChar(); + if (v1 == '<') + { + m_type = TOKEN_START_DICTIONARY; + break; + } + m_type = TOKEN_STRING; + m_hexString = true; + int v2 = 0; + while (true) + { + while (IsWhitespace(v1)) + { + v1 = ReadChar(); + } + if (v1 == '>') + break; + v1 = GetHex(v1); + if (v1 < 0) + break; + v2 = ReadChar(); + while (IsWhitespace(v2)) + { + v2 = ReadChar(); + } + if (v2 == '>') + { + ch = v1 << 4; + buffer += ch; + break; + } + v2 = GetHex(v2); + if (v2 < 0) + break; + ch = (v1 << 4) + v2; + buffer += ch; + v1 = ReadChar(); + } + if (v1 < 0 || v2 < 0) + { + wxLogError(_("wxPdfTokenizer::NextToken: Error reading string.")); + return false; + } + break; + } + case '%': + m_type = TOKEN_COMMENT; + do + { + ch = ReadChar(); + } + while (ch != -1 && ch != '\r' && ch != '\n'); + break; + case '(': + { + m_type = TOKEN_STRING; + m_hexString = false; + int nesting = 0; + while (true) + { + ch = ReadChar(); + if (ch == -1) + break; + if (ch == '(') + { + ++nesting; + } + else if (ch == ')') + { + --nesting; + } + else if (ch == '\\') + { + bool lineBreak = false; + ch = ReadChar(); + switch (ch) + { + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case '(': + case ')': + case '\\': + break; + case '\r': + lineBreak = true; + ch = ReadChar(); + if (ch != '\n') + BackOnePosition(ch); + break; + case '\n': + lineBreak = true; + break; + default: + { + if (ch < '0' || ch > '7') + { + break; + } + int octal = ch - '0'; + ch = ReadChar(); + if (ch < '0' || ch > '7') + { + BackOnePosition(ch); + ch = octal; + break; + } + octal = (octal << 3) + ch - '0'; + ch = ReadChar(); + if (ch < '0' || ch > '7') + { + BackOnePosition(ch); + ch = octal; + break; + } + octal = (octal << 3) + ch - '0'; + ch = octal & 0xff; + break; + } + } + if (lineBreak) + continue; + if (ch < 0) + break; + } + else if (ch == '\r') + { + ch = ReadChar(); + if (ch < 0) + break; + if (ch != '\n') + { + BackOnePosition(ch); + ch = '\n'; + } + } + if (nesting == -1) + break; + buffer += ch; + } + if (ch == -1) + { + wxLogError(_("wxPdfTokenizer::NextToken: Error reading string.")); + return false; + } + break; + } + default: + { + if (ch == '-' || ch == '+' || ch == '.' || (ch >= '0' && ch <= '9')) + { + m_type = TOKEN_NUMBER; + do + { + buffer += ch; + ch = ReadChar(); + } + while (ch != -1 && ((ch >= '0' && ch <= '9') || ch == '.')); + } + else + { + m_type = TOKEN_OTHER; + do + { + buffer += ch; + ch = ReadChar(); + } + while (!IsDelimiterOrWhitespace(ch)); + } + BackOnePosition(ch); + break; + } + } + if (buffer != wxEmptyString) + { + m_stringValue.Append(buffer); + if (m_type == TOKEN_OTHER && (m_stringValue == _T("true") || m_stringValue == _T("false"))) + { + m_type = TOKEN_BOOLEAN; + } + } + return true; +} + +void +wxPdfTokenizer::NextValidToken() +{ + int level = 0; + wxString n1 = wxEmptyString; + wxString n2 = wxEmptyString; + int ptr = 0; + while (NextToken()) + { + if (m_type == TOKEN_COMMENT) + continue; + switch (level) + { + case 0: + { + if (m_type != TOKEN_NUMBER) + return; + ptr = Tell(); + n1 = m_stringValue; + ++level; + break; + } + case 1: + { + if (m_type != TOKEN_NUMBER) { + Seek(ptr); + m_type = TOKEN_NUMBER; + m_stringValue = n1; + return; + } + n2 = m_stringValue; + ++level; + break; + } + default: + { + if (m_type != TOKEN_OTHER || m_stringValue != _T("R")) + { + Seek(ptr); + m_type = TOKEN_NUMBER; + m_stringValue = n1; + return; + } + m_type = TOKEN_REFERENCE; + long value; + n1.ToLong(&value); + m_reference = value; + n2.ToLong(&value); + m_generation = value; + return; + } + } + } + // throwError("Unexpected end of file"); +} + +int +wxPdfTokenizer::GetTokenType() +{ + return m_type; +} + +wxString +wxPdfTokenizer::GetStringValue() +{ + return m_stringValue; +} + +int +wxPdfTokenizer::GetIntValue() +{ + long value; + m_stringValue.ToLong(&value); + return value; +} + +int +wxPdfTokenizer::GetReference() +{ + return m_reference; +} + +int +wxPdfTokenizer::GetGeneration() +{ + return m_generation; +} + +bool +wxPdfTokenizer::IsWhitespace(int ch) +{ + return (ch == 0 || ch == 9 || ch == 10 || ch == 12 || ch == 13 || ch == 32); +} + +bool +wxPdfTokenizer::IsDelimiter(int ch) +{ + return (ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '/' || ch == '%'); +} + +bool +wxPdfTokenizer::IsDelimiterOrWhitespace(int ch) +{ + return IsWhitespace(ch) || IsDelimiter(ch) || (ch == -1); +} + +int +wxPdfTokenizer::GetHex(int v) +{ + if (v >= '0' && v <= '9') + return v - '0'; + if (v >= 'A' && v <= 'F') + return v - 'A' + 10; + if (v >= 'a' && v <= 'f') + return v - 'a' + 10; + return -1; +} diff --git a/thirdparty/wxPdfDoc-patch/wxpdfdoc_patch_20080112.patch b/thirdparty/wxPdfDoc-patch/wxpdfdoc_patch_20080112.patch new file mode 100644 index 000000000..b6717be41 --- /dev/null +++ b/thirdparty/wxPdfDoc-patch/wxpdfdoc_patch_20080112.patch @@ -0,0 +1,1023 @@ +? components/wxpdfdoc/build/msvc6prjd_lib +? components/wxpdfdoc/build/msvc6prjud_lib +? components/wxpdfdoc/build/wxpdfdoc.sln +? components/wxpdfdoc/build/wxpdfdoc_makefont.vcproj +? components/wxpdfdoc/build/wxpdfdoc_makefont.vcproj.BOMB.Ben.user +? components/wxpdfdoc/build/wxpdfdoc_minimal.vcproj +? components/wxpdfdoc/build/wxpdfdoc_minimal.vcproj.BOMB.Ben.user +? components/wxpdfdoc/build/wxpdfdoc_wxpdfdoc.vcproj +? components/wxpdfdoc/build/wxpdfdoc_wxpdfdoc.vcproj.BOMB.Ben.user +? components/wxpdfdoc/docs/html +? components/wxpdfdoc/include/wx/pdfoc.h +? components/wxpdfdoc/lib/wxpdfdocud.idb +? components/wxpdfdoc/makefont/makefont.idb +? components/wxpdfdoc/samples/barcodes.pdf +? components/wxpdfdoc/samples/bookmark.pdf +? components/wxpdfdoc/samples/charting.pdf +? components/wxpdfdoc/samples/cjktest.pdf +? components/wxpdfdoc/samples/clipping.pdf +? components/wxpdfdoc/samples/drawing.pdf +? components/wxpdfdoc/samples/form.pdf +? components/wxpdfdoc/samples/gradients.pdf +? components/wxpdfdoc/samples/javascript.pdf +? components/wxpdfdoc/samples/labels.pdf +? components/wxpdfdoc/samples/minimal.idb +? components/wxpdfdoc/samples/protection1.pdf +? components/wxpdfdoc/samples/protection2.pdf +? components/wxpdfdoc/samples/rotation.pdf +? components/wxpdfdoc/samples/templates1.pdf +? components/wxpdfdoc/samples/templates2.pdf +? components/wxpdfdoc/samples/transformation.pdf +? components/wxpdfdoc/samples/transparency.pdf +? components/wxpdfdoc/samples/tutorial1.pdf +? components/wxpdfdoc/samples/tutorial2.pdf +? components/wxpdfdoc/samples/tutorial3.pdf +? components/wxpdfdoc/samples/tutorial4.pdf +? components/wxpdfdoc/samples/tutorial5.pdf +? components/wxpdfdoc/samples/tutorial6.pdf +? components/wxpdfdoc/samples/tutorial7.pdf +? components/wxpdfdoc/samples/wmf.pdf +? components/wxpdfdoc/samples/xmlwrite.pdf +? components/wxpdfdoc/src/pdfoc.cpp +Index: components/wxpdfdoc/include/wx/pdfdoc.h +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/include/wx/pdfdoc.h,v +retrieving revision 1.7 +diff -u -r1.7 pdfdoc.h +--- components/wxpdfdoc/include/wx/pdfdoc.h 15 Dec 2006 20:47:08 -0000 1.7 ++++ components/wxpdfdoc/include/wx/pdfdoc.h 30 Dec 2007 20:21:16 -0000 +@@ -21,6 +21,7 @@ + #include "wx/pdffont.h" + #include "wx/pdfimage.h" + #include "wx/pdfproperties.h" ++#include "wx/pdfoc.h" + + #define wxPDF_PRODUCER _T("wxPdfDocument 0.8.0") + +@@ -92,6 +93,9 @@ + /// Hash map class for spot colors + WX_DECLARE_STRING_HASH_MAP(wxPdfSpotColour*, wxPdfSpotColourMap); + ++/// Hash map class for patterns ++WX_DECLARE_STRING_HASH_MAP(wxPdfPattern*, wxPdfPatternMap); ++ + /// Hash map class for spot colors + WX_DECLARE_STRING_HASH_MAP(wxPdfIndirectObject*, wxPdfRadioGroupMap); + +@@ -118,7 +122,27 @@ + wxPdfDocument(int orientation = wxPORTRAIT, + const wxString& unit = wxString(_T("mm")), + wxPaperSize format = wxPAPER_A4); +-// TODO: Constructor with explicit papersize ++ ++ /// Constructor ++ /** ++ * \param orientation Defines the default page orientation. Possible values are: ++ * \li wxPORTRAIT portrait layout (default) ++ * \li wxLANDSCAPE landscape layout ++ * ++ * \param unit Defines the user units. Possible values are: ++ * \li "mm" millimeter (1 mm = 0.0394 in = 2.833 pt = 0.1 cm) (default) ++ * \li "cm" centimeter (1 cm = 0.394 in = 28.33 pt = 10 mm) ++ * \li "pt" points (1 pt = 1/72 in = 0.0353 cm = 0.353 mm) ++ * \li "in" inch (1 in = 72 pt = 2.54 cm = 25.4 mm) ++ * ++ * \param paperWidth Defines the width of the paper in user units. ++ * \param paperHeight Defines the height of the paper in user units. ++ */ ++ wxPdfDocument(const double paperWidth, ++ const double paperHeight, ++ int orientation = wxPORTRAIT, ++ const wxString& unit = wxString(_T("mm"))); ++ + + virtual ~wxPdfDocument(); + +@@ -444,6 +468,21 @@ + */ + virtual void AddSpotColor(const wxString& name, double cyan, double magenta, double yellow, double black); + ++ ++ /// Defines the colorspace for drawing operations ++ /** ++ * \param space 0 = normal, 1 = pattern ++ */ ++ virtual void SetDrawColorSpace(const int space); ++ ++ ++ /// Defines the colorspace for fill operations ++ /** ++ * \param space 0 = normal, 1 = pattern ++ */ ++ virtual void SetFillColorSpace(const int space); ++ ++ + /// Defines the color used for all drawing operations. + /** + * Affected drawing operations are: lines, rectangles and cell borders. It can be expressed in RGB +@@ -510,6 +549,14 @@ + */ + virtual void SetDrawColor(const wxString& name, double tint = 100); + ++ /// Defines the pattern used for all drawing operations. ++ /** ++ * N.B. You muse be in the pattern color space before calling, see SetDrawColorSpace. ++ * ++ * \param name is the name of the pattern ++ */ ++ virtual void SetDrawPattern(const wxString& name); ++ + /// Gets the color used for all drawing operations. + /** + * \see SetDrawColor() +@@ -582,6 +629,16 @@ + */ + virtual void SetFillColor(const wxString& name, double tint = 100); + ++ ++ /// Defines the pattern used for all drawing operations. ++ /** ++ * N.B. You must be in the pattern color space before calling, see SetFillColorSpace. ++ * ++ * \param name is the name of the pattern ++ */ ++ virtual void SetFillPattern(const wxString& name); ++ ++ + /// Gets the color used for all filling operations. + /** + * \see SetFillColor() +@@ -864,8 +921,10 @@ + * \li wxPDF_STYLE_FILL: fill + * \li wxPDF_STYLE_FILLDRAW: draw and fill + * \li wxPDF_STYLE_DRAWCLOSE: close path and draw (can be combined with wxPDF_STYLE_FILL ++ * \param alternativeFillRule: If this is true, the even-odd rule will be used when filling, ++ * otherwise the default nonzero winding method will be used. + */ +- virtual void Shape(const wxPdfShape& shape, int style = wxPDF_STYLE_DRAW); ++ virtual void Shape(const wxPdfShape& shape, int style = wxPDF_STYLE_DRAW, bool alternativeFillRule = false); + + /// Performs a rotation around a given center. + /** +@@ -1089,18 +1148,20 @@ + * \param x Abscissa of the origin + * \param y Ordinate of the origin + * \param txt String to print ++ * \param mode The rendering mode (0=normal, 1 = stroked, 2 = filled then stroked) + * \see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() + */ +- virtual void Text(double x, double y, const wxString& txt); ++ virtual void Text(double x, double y, const wxString& txt, const int mode = 0); + + /// Prints a rotated text string + /** + * \param x: abscissa of the rotation center. + * \param y: ordinate of the rotation center. + * \param txt String to print ++ * \param mode The rendering mode (0=normal, 1 = stroked, 2 = filled then stroked) + * \param angle: angle in degrees. + */ +- virtual void RotatedText(double x, double y, const wxString& txt, double angle); ++ virtual void RotatedText(double x, double y, const wxString& txt, double angle, const int mode = 0); + + /// Whenever a page break condition is met, + /** +@@ -1748,6 +1809,19 @@ + */ + virtual void SetAlphaState(int alphaState); + ++ ++ /// Defines an image pattern ++ /** ++ * Add a pattern which can be reference in fill pattern methods ++ * \param name the name of the pattern (case sensitive) ++ * \param image the image to use for the pattern ++ * \param imagename the name of the image (case sensitive) ++ * \param width the display width ++ * \param height the display height ++ */ ++ virtual void AddPattern(const wxString& name, const wxImage& image, const wxString& imageName, const double width, const double height); ++ ++ + /// Defines a linear gradient shading + /** + * \param col1 first color (RGB or CMYK). +@@ -1757,6 +1831,7 @@ + virtual int LinearGradient(const wxPdfColour& col1, const wxPdfColour& col2, + wxPdfLinearGradientType gradientType = wxPDF_LINEAR_GRADIENT_HORIZONTAL); + ++ + /// Defines a axial gradient shading + /** + * \param col1 first color (RGB or CMYK). +@@ -1823,6 +1898,29 @@ + */ + virtual void SetFillGradient(double x, double y, double w, double h, int gradient); + ++ /// Add an optional content group ++ /** ++ * The OCG will be managed by this class after its added, so you dont need to destroy it yourself. ++ * This means the OCG must have been created dynamically, not statically. ++ * \param[in] ocg The OCG to add ++ */ ++ virtual void AddOcg(wxPdfOcg *ocg) { m_pdfOc.AddOcg(ocg); }; ++ ++ /// Start marking content as optional ++ /** ++ * This starts marking content as belonging to the specified ocg. Call ++ * ExitOcg to stop marking content. ++ * \param[in] ocg The OCG to mark content as belonging to ++ */ ++ virtual void EnterOcg(const wxPdfOcg *ocg); ++ ++ /// Stop marking content as optional ++ /** ++ * This stops marking content as belonging to the previously entered ocg. ++ */ ++ virtual void ExitOcg(void); ++ ++ + /// Draws a graphical marker symbol + /** + * \param x abscissa of the marker's center +@@ -2259,6 +2357,12 @@ + /// Add spot colors + virtual void PutSpotColors(); + ++ /// Add Patterns ++ virtual void PutPatterns(); ++ ++ /// Add OC ++ virtual void PutOc(); ++ + /// Add Javascript (document level) + virtual void PutJavaScript(); + +@@ -2375,6 +2479,13 @@ + void InitializeCoreFonts(); + + private: ++ /// Set the page format ++ void SetPageFormat(const int orientation, const wxPaperSize format); ++ ++ /// Set the page format ++ void SetPageFormat(const int orientation, const double paperWidth, const double paperHeight); ++ ++private: + int m_page; ///< current page number + int m_n; ///< current object number + int m_firstPageId; ///< object id of the first page +@@ -2430,6 +2541,7 @@ + + wxPdfGradientMap* m_gradients; ///< array of gradients + wxPdfSpotColourMap* m_spotColors; ///< array of spot colors ++ wxPdfPatternMap* m_patterns; ///< array of patterns + + wxPdfAnnotationsMap* m_annotations; ///< array of text annotations + +@@ -2508,6 +2620,9 @@ + static int ms_s1; ///< Random number generator seed 1 + static int ms_s2; ///< Random number generator seed 2 + ++ wxPdfOc m_pdfOc; ///< Optional content manager ++ ++ + friend class wxPdfImage; + friend class wxPdfTable; + }; +Index: components/wxpdfdoc/include/wx/pdfproperties.h +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/include/wx/pdfproperties.h,v +retrieving revision 1.2 +diff -u -r1.2 pdfproperties.h +--- components/wxpdfdoc/include/wx/pdfproperties.h 15 Dec 2006 20:47:08 -0000 1.2 ++++ components/wxpdfdoc/include/wx/pdfproperties.h 8 Dec 2007 22:41:11 -0000 +@@ -462,6 +462,53 @@ + double m_black; ///< black level + }; + ++ ++/// Class representing patterns. ++// Implementation in pdfcolor.cpp ++class WXDLLIMPEXP_PDFDOC wxPdfPattern ++{ ++public: ++ /// Constructor for pattern ++ /** ++ * \param index The pattern index ++ * \param width The image width ++ * \param height The image height ++ */ ++ wxPdfPattern(const int index, const double width, const double height); ++ ++ /// Copy constructor ++ wxPdfPattern(const wxPdfPattern& pattern); ++ ++ /// Set object index ++ void SetObjIndex(int index) { m_objIndex = index; }; ++ ++ /// Get object index ++ int GetObjIndex() const { return m_objIndex; }; ++ ++ /// Get pattern index ++ int GetIndex() const { return m_index; }; ++ ++ /// Set image ++ void SetImage(wxPdfImage *image) { m_image = image; }; ++ ++ /// Get image ++ wxPdfImage *GetImage() const {return m_image; }; ++ ++ /// Get image width ++ int GetImageWidth() const {return m_imageWidth; }; ++ ++ /// Get image height ++ int GetImageHeight() const {return m_imageHeight; }; ++ ++private: ++ int m_objIndex; ///< object index ++ int m_index; ///< pattern index ++ wxPdfImage *m_image; ///< image ++ double m_imageWidth; ///< image width ++ double m_imageHeight;///< image height ++}; ++ ++ + /// Class representing wxPdfDocument colors. + class WXDLLIMPEXP_PDFDOC wxPdfColour + { +@@ -683,6 +730,13 @@ + /// Get the line color + const wxPdfColour& GetColour() const { return m_color; }; + ++ /// Set the miter limit (0 for none) ++ void SetMiterLimit(const double miterLimit) { m_miterLimit = miterLimit; }; ++ ++ /// Get the miter limit ++ double GetMiterLimit() const { return m_miterLimit; }; ++ ++ + private: + bool m_isSet; ///< Flag whether the style is initialized + double m_width; ///< Line width +@@ -691,6 +745,7 @@ + wxPdfArrayDouble m_dash; ///< Dash pattern + double m_phase; ///< Dash pattern phase + wxPdfColour m_color; ///< Line color ++ double m_miterLimit; ///< Miter limit (0 for none) + }; + + /// Class representing a coons patch mesh. +Index: components/wxpdfdoc/src/pdfcolor.cpp +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/src/pdfcolor.cpp,v +retrieving revision 1.3 +diff -u -r1.3 pdfcolor.cpp +--- components/wxpdfdoc/src/pdfcolor.cpp 15 Dec 2006 20:47:08 -0000 1.3 ++++ components/wxpdfdoc/src/pdfcolor.cpp 9 Dec 2007 01:11:37 -0000 +@@ -239,9 +239,64 @@ + m_black = color.m_black; + } + ++wxPdfPattern::wxPdfPattern(const int index, const double width, const double height) ++ : m_objIndex(0), m_index(index), m_imageWidth(width), m_imageHeight(height) ++{ ++} ++ ++wxPdfPattern::wxPdfPattern(const wxPdfPattern& pattern) ++{ ++ m_objIndex = pattern.m_objIndex; ++ m_index = pattern.m_index; ++ m_imageWidth = pattern.m_imageWidth; ++ m_imageHeight = pattern.m_imageHeight; ++ m_image = pattern.m_image; ++} ++ + // --- + + void ++wxPdfDocument::AddPattern(const wxString& name, const wxImage& image, const wxString& imageName, const double width, const double height) ++{ ++ wxPdfPatternMap::iterator pattern = (*m_patterns).find(name); ++ wxPdfPattern *newPattern; ++ if (pattern == (*m_patterns).end()) ++ { ++ int i = (*m_patterns).size() + 1; ++ newPattern = new wxPdfPattern(i, width, height); ++ (*m_patterns)[name] = newPattern; ++ ++ ++ //store the image ++ wxImage tempImage = image.Copy(); ++ wxPdfImage* currentImage = NULL; ++ wxPdfImageHashMap::iterator imageIter = (*m_images).find(imageName); ++ int maskImage; ++ if (imageIter == (*m_images).end()) ++ { ++ if (tempImage.HasAlpha()) ++ { ++ maskImage = ImageMask(imageName+wxString(_T(".mask")), tempImage); ++ tempImage.ConvertAlphaToMask(0); ++ ++ } ++ // First use of image, get info ++ tempImage.SetMask(false); ++ int i = (*m_images).size() + 1; ++ currentImage = new wxPdfImage(this, i, imageName, tempImage); ++ currentImage->Parse(); ++ currentImage->SetMaskImage(maskImage); ++ (*m_images)[imageName] = currentImage; ++ } ++ ++ imageIter = (*m_images).find(imageName); ++ newPattern->SetImage( (*imageIter).second ); ++ ++ } ++} ++ ++ ++void + wxPdfDocument::AddSpotColor(const wxString& name, double cyan, double magenta, double yellow, double black) + { + wxPdfSpotColourMap::iterator spotColor = (*m_spotColors).find(name); +@@ -253,6 +308,34 @@ + } + + void ++wxPdfDocument::SetDrawColorSpace(const int space) ++{ ++ switch(space) { ++ case 1: ++ OutAscii(_T("/Pattern CS")); ++ break; ++ case 0: ++ default: ++ OutAscii(_T("/DeviceRGB CS")); ++ break; ++ } ++} ++ ++void ++wxPdfDocument::SetFillColorSpace(const int space) ++{ ++ switch(space) { ++ case 1: ++ OutAscii(_T("/Pattern cs")); ++ break; ++ case 0: ++ default: ++ OutAscii(_T("/DeviceRGB cs")); ++ break; ++ } ++} ++ ++void + wxPdfDocument::SetDrawColor(const wxColour& color) + { + wxPdfColour tempColor(color); +@@ -315,6 +398,20 @@ + } + } + ++void ++wxPdfDocument::SetDrawPattern(const wxString& name) { ++ wxPdfPatternMap::iterator patternIter = (*m_patterns).find(name); ++ wxPdfPattern *pattern; ++ if( patternIter != (*m_patterns).end()) ++ { ++ pattern = patternIter->second; ++ OutAscii(wxString::Format(_T("/P%d SCN"), pattern->GetIndex())); ++ } ++ else { ++ wxLogError(_("SetFillPattern: Undefined pattern: ") + name); ++ } ++} ++ + const wxPdfColour + wxPdfDocument::GetDrawColor() + { +@@ -388,6 +485,21 @@ + } + } + ++void ++wxPdfDocument::SetFillPattern(const wxString& name) { ++ wxPdfPatternMap::iterator patternIter = (*m_patterns).find(name); ++ wxPdfPattern *pattern; ++ if( patternIter != (*m_patterns).end()) ++ { ++ pattern = patternIter->second; ++ OutAscii(wxString::Format(_T("/P%d scn"), pattern->GetIndex())); ++ } ++ else { ++ wxLogError(_("SetFillPattern: Undefined pattern: ") + name); ++ } ++} ++ ++ + const wxPdfColour + wxPdfDocument::GetFillColor() + { +Index: components/wxpdfdoc/src/pdfdoc.cpp +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/src/pdfdoc.cpp,v +retrieving revision 1.9 +diff -u -r1.9 pdfdoc.cpp +--- components/wxpdfdoc/src/pdfdoc.cpp 15 Dec 2006 20:47:08 -0000 1.9 ++++ components/wxpdfdoc/src/pdfdoc.cpp 31 Dec 2007 02:34:47 -0000 +@@ -35,6 +35,47 @@ + + wxPdfDocument::wxPdfDocument(int orientation, const wxString& unit, wxPaperSize format) + { ++ double w, h; ++ double unitscale; ++ ++ // Scale factor ++ if (unit == _T("pt")) ++ { ++ unitscale = 1.; ++ } ++ else if (unit == _T("in")) ++ { ++ unitscale = 72.; ++ } ++ else if (unit == _T("cm")) ++ { ++ unitscale = 72. / 2.54; ++ } ++ else // if (unit == "mm") or unknown ++ { ++ unitscale = 72. / 25.4; ++ } ++ ++ ++ // get the page width/height and pass to other constructor ++ ++ wxPrintPaperDatabase* printPaperDatabase = new wxPrintPaperDatabase; ++ printPaperDatabase->CreateDatabase(); ++ wxPrintPaperType* paperType = printPaperDatabase->FindPaperType(format); ++ if (paperType == NULL) ++ { ++ paperType = printPaperDatabase->FindPaperType(wxPAPER_A4); ++ } ++ wxSize paperSize = paperType->GetSize(); ++ //get the width and height in user units. papersize is in tenths of a mm, convert to inches, then points, then to user units ++ w = (paperSize.GetWidth() / 254.0) * 72.0 * unitscale; ++ h = (paperSize.GetHeight() / 254.0) * 72.0 * unitscale; ++ delete printPaperDatabase; ++ ++ wxPdfDocument(w, h, orientation, unit); ++} ++ ++wxPdfDocument::wxPdfDocument(const double paperWidth, const double paperHeight, int orientation, const wxString& unit) { + // Allocate arrays + m_currentFont = NULL; + +@@ -65,6 +106,7 @@ + m_templates = new wxPdfTemplatesMap(); + m_parsers = new wxPdfParserMap(); + m_spotColors = new wxPdfSpotColourMap(); ++ m_patterns = new wxPdfPatternMap(); + + m_outlineRoot = -1; + m_maxOutlineLevel = 0; +@@ -105,7 +147,13 @@ + m_imgscale = 1.; + + // Page format +- wxPrintPaperDatabase* printPaperDatabase = new wxPrintPaperDatabase; ++ m_fw = paperWidth; ++ m_fh = paperHeight; ++ m_fwPt = m_fw * m_k; ++ m_fhPt = m_fh * m_k; ++ ++ ++ /*wxPrintPaperDatabase* printPaperDatabase = new wxPrintPaperDatabase; + printPaperDatabase->CreateDatabase(); + wxPrintPaperType* paperType = printPaperDatabase->FindPaperType(format); + if (paperType == NULL) +@@ -119,6 +167,7 @@ + + m_fw = m_fwPt / m_k; + m_fh = m_fhPt / m_k; ++*/ + + // Page orientation + if (orientation == wxLANDSCAPE) +@@ -184,6 +233,8 @@ + m_zapfdingbats = m_currentFont->GetIndex(); + } + ++ ++ + wxPdfDocument::~wxPdfDocument() + { + delete m_coreFonts; +@@ -349,6 +400,16 @@ + } + delete m_spotColors; + ++ wxPdfPatternMap::iterator pattern = m_patterns->begin(); ++ for(pattern = m_patterns->begin(); pattern != m_patterns->end(); pattern++) ++ { ++ if(pattern->second != NULL) ++ { ++ delete pattern->second; ++ } ++ } ++ delete m_patterns; ++ + delete m_orientationChanges; + + delete m_offsets; +@@ -633,7 +694,9 @@ + // Open font metrics XML file + wxFileName fontFileName(fileName); + fontFileName.MakeAbsolute(GetFontPath()); +- wxFileSystem fs; ++ //BM: wxFileSystem doesnt appear to have default handler for local files?, just let ++ // the wxXmlDocument load the file itself rather than generating a stream. ++/* wxFileSystem fs; + wxFSFile* xmlFontMetrics = fs.OpenFile(fontFileName.GetFullPath()); + if (!xmlFontMetrics) + { +@@ -641,11 +704,12 @@ + wxLogDebug(_T("wxPdfDocument::AddFont: Font metrics file '%s' not found."), fileName.c_str()); + return false; + } +- ++*/ + // Load the XML file + wxXmlDocument fontMetrics; +- bool loaded = fontMetrics.Load(*xmlFontMetrics->GetStream()); +- delete xmlFontMetrics; ++// bool loaded = fontMetrics.Load(*xmlFontMetrics->GetStream()); ++// delete xmlFontMetrics; ++ bool loaded = fontMetrics.Load(fontFileName.GetFullPath()); + if (!loaded) + { + // Font metrics file loading failed +@@ -856,7 +920,7 @@ + } + + void +-wxPdfDocument::Text(double x, double y, const wxString& txt) ++wxPdfDocument::Text(double x, double y, const wxString& txt, const int mode) + { + // Output a string + if (m_colorFlag) +@@ -867,7 +931,13 @@ + } + OutAscii(wxString(_T("BT ")) + + Double2String(x*m_k,2) + wxString(_T(" ")) + +- Double2String((m_h-y)*m_k,2) + wxString(_T(" Td (")), false); ++ Double2String((m_h-y)*m_k,2) + wxString(_T(" Td ")), false); ++ if(mode<3) ++ { ++ OutAscii(Double2String(mode) + wxString(_T(" Tr")), false); ++ } ++ ++ OutAscii(wxString(_T(" (")), false); + TextEscape(txt,false); + Out(") Tj ET", false); + +@@ -885,12 +955,12 @@ + } + + void +-wxPdfDocument::RotatedText(double x, double y, const wxString& txt, double angle) ++wxPdfDocument::RotatedText(double x, double y, const wxString& txt, double angle, const int mode) + { + // Text rotated around its origin + StartTransform(); + Rotate(angle, x, y); +- Text(x, y, txt); ++ Text(x, y, txt, mode); + StopTransform(); + } + +@@ -2223,4 +2293,14 @@ + return value; + } + ++void ++wxPdfDocument::EnterOcg(const wxPdfOcg *ocg) ++{ ++ OutAscii(wxString::Format(_T("/OC /OC%d BDC"), ocg->GetOcgIndex())); ++} + ++void ++wxPdfDocument::ExitOcg(void) ++{ ++ OutAscii(_T("EMC")); ++} +Index: components/wxpdfdoc/src/pdfgraphics.cpp +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/src/pdfgraphics.cpp,v +retrieving revision 1.3 +diff -u -r1.3 pdfgraphics.cpp +--- components/wxpdfdoc/src/pdfgraphics.cpp 15 Dec 2006 20:47:08 -0000 1.3 ++++ components/wxpdfdoc/src/pdfgraphics.cpp 4 Jan 2008 03:41:51 -0000 +@@ -97,6 +97,7 @@ + m_dash = dash; + m_phase = phase; + m_color = color; ++ m_miterLimit = 0; + } + + wxPdfLineStyle::~wxPdfLineStyle() +@@ -113,6 +114,7 @@ + m_dash = lineStyle.m_dash; + m_phase = lineStyle.m_phase; + m_color = lineStyle.m_color; ++ m_miterLimit = lineStyle.m_miterLimit; + } + + wxPdfLineStyle& +@@ -125,6 +127,7 @@ + m_dash = lineStyle.m_dash; + m_phase = lineStyle.m_phase; + m_color = lineStyle.m_color; ++ m_miterLimit = lineStyle.m_miterLimit; + return *this; + } + +@@ -1339,7 +1342,7 @@ + } + + void +-wxPdfDocument::Shape(const wxPdfShape& shape, int style) ++wxPdfDocument::Shape(const wxPdfShape& shape, int style, bool alternativeFillRule) + { + wxString op; + if ((style & wxPDF_STYLE_MASK) == wxPDF_STYLE_FILL) +@@ -1366,6 +1369,15 @@ + } + } + ++ //if alternative fill rule requested, add the modifier to the command if its f, b or B ++ if (alternativeFillRule) ++ { ++ if(op == _T("f") || op == _T("b") || op == _T("B")) ++ { ++ op += _T("*"); ++ } ++ } ++ + Out("q"); + + double scratch[6]; +@@ -1607,6 +1619,11 @@ + break; + } + ++ if ( (linestyle.GetLineJoin() == wxPDF_LINEJOIN_MITER) && (linestyle.GetMiterLimit() != 0.0) ) ++ { ++ OutAscii(wxString::Format(_T("%f M"), linestyle.GetMiterLimit())); ++ } ++ + const wxPdfArrayDouble& dash = linestyle.GetDash(); + if (&dash != NULL) + { +@@ -1830,6 +1847,11 @@ + { + m_inTransform--; + Out("Q"); ++ ++ //hack to make next font change work, see https://sourceforge.net/tracker/?func=detail&atid=462816&aid=1861202&group_id=51305 ++ m_fontFamily = wxEmptyString; ++ //hack to make colour change work, related to same bug as above ++ m_fillColor = wxPdfColour(1,2,3); + } + } + +Index: components/wxpdfdoc/src/pdfimage.cpp +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/src/pdfimage.cpp,v +retrieving revision 1.9 +diff -u -r1.9 pdfimage.cpp +--- components/wxpdfdoc/src/pdfimage.cpp 15 Dec 2006 20:47:08 -0000 1.9 ++++ components/wxpdfdoc/src/pdfimage.cpp 1 Jan 2008 04:12:50 -0000 +@@ -38,6 +38,8 @@ + if (ms_fileSystem == NULL) + { + static wxFileSystem fileSystem; ++ //I dont know why this needs to be added manually, all the documents indicate that nothing needs to be done for local files. ++ fileSystem.AddHandler(new wxLocalFSHandler()); + ms_fileSystem = &fileSystem; + } + return ms_fileSystem; +Index: components/wxpdfdoc/src/pdfkernel.cpp +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/src/pdfkernel.cpp,v +retrieving revision 1.5 +diff -u -r1.5 pdfkernel.cpp +--- components/wxpdfdoc/src/pdfkernel.cpp 15 Dec 2006 20:47:08 -0000 1.5 ++++ components/wxpdfdoc/src/pdfkernel.cpp 31 Dec 2007 23:16:28 -0000 +@@ -350,6 +350,45 @@ + { + Out("/Type /Catalog"); + Out("/Pages 1 0 R"); ++ ++ Out("/OCProperties <<"); ++ wxPdfOcgHashMap &ocgHashMap = m_pdfOc.GetOcgMap(); ++ wxPdfOcgHashMap::iterator ocgIter = ocgHashMap.begin(); ++ Out(" /OCGs ["); ++ for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { ++ wxPdfOcg* ocg = ocgIter->second; ++ OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); ++ } ++ Out("]"); ++ Out(" /D <<"); ++ Out(" /BaseState /ON "); ++ Out(" /Intent /View "); ++ Out(" /ON ["); ++ for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { ++ wxPdfOcg* ocg = ocgIter->second; ++ if( ocg->GetDefaultVisibilityState() == true) { ++ OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); ++ } ++ } ++ Out(" ]"); ++ Out(" /OFF ["); ++ for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { ++ wxPdfOcg* ocg = ocgIter->second; ++ if( ocg->GetDefaultVisibilityState() == false) { ++ OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); ++ } ++ } ++ Out(" ]"); ++ Out(" /Order ["); ++ for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { ++ wxPdfOcg* ocg = ocgIter->second; ++ OutAscii(wxString::Format(_T("%d 0 R"), ocg->GetObjectIndex())); ++ } ++ Out("]"); ++ ++ Out(">>"); ++ Out(">>"); ++ + if (m_zoomMode == wxPDF_ZOOM_FULLPAGE) + { + OutAscii(wxString::Format(_T("/OpenAction [%d 0 R /Fit]"), m_firstPageId)); +@@ -967,11 +1006,12 @@ + wxString strFontFileName = font->GetFontFile(); + wxFileName fontFileName(strFontFileName); + fontFileName.MakeAbsolute(font->GetFilePath()); +- wxFileSystem fs; +- wxFSFile* fontFile = fs.OpenFile(fontFileName.GetFullPath()); +- if (fontFile) ++ ++// wxFileSystem fs; ++// wxFSFile* fontFile = fs.OpenFile(fontFileName.GetFullPath()); ++ if (fontFileName.FileExists()) + { +- wxInputStream* fontStream = fontFile->GetStream(); ++ wxFileInputStream* fontStream = new wxFileInputStream(fontFileName.GetFullPath()); + int fontLen = fontStream->GetSize(); + int fontSize1 = font->GetSize1(); + wxMemoryOutputStream* p = new wxMemoryOutputStream(); +@@ -1040,7 +1080,8 @@ + Out("endobj"); + + delete p; +- delete fontFile; ++// delete fontFile; ++ delete fontStream; + } + } + } +@@ -1235,12 +1276,13 @@ + wxString strCtgFileName = font->GetCtgFile(); + wxFileName ctgFileName(strCtgFileName); + ctgFileName.MakeAbsolute(font->GetFilePath()); +- wxFileSystem fs; +- wxFSFile* ctgFile = fs.OpenFile(ctgFileName.GetFullPath()); +- if (ctgFile) ++ ++// wxFileSystem fs; ++// wxFSFile* ctgFile = fs.OpenFile(ctgFileName.GetFullPath()); ++ if (ctgFileName.FileExists()) + { + wxMemoryOutputStream* p = new wxMemoryOutputStream(); +- wxInputStream* ctgStream = ctgFile->GetStream(); ++ wxFileInputStream* ctgStream = new wxFileInputStream(ctgFileName.GetFullPath()); + int ctgLen = CalculateStreamLength(ctgStream->GetSize()); + OutAscii(wxString::Format(_T("<Write(*ctgStream); + PutStream(*p); + delete p; +- delete ctgFile; ++// delete ctgFile; ++ delete ctgStream; + } + else + { +@@ -1634,6 +1677,21 @@ + OutAscii(wxString::Format(_T("/CS%d %d 0 R"), spotColor->second->GetIndex(), spotColor->second->GetObjIndex())); + } + Out(">>"); ++ Out("/Pattern <<"); ++ wxPdfPatternMap::iterator pattern; ++ for (pattern = m_patterns->begin(); pattern != m_patterns->end(); pattern++) ++ { ++ OutAscii(wxString::Format(_T("/P%d %d 0 R"), pattern->second->GetIndex(), pattern->second->GetObjIndex())); ++ } ++ Out(">>"); ++ wxPdfOcgHashMap &ocgHashMap = m_pdfOc.GetOcgMap(); ++ wxPdfOcgHashMap::iterator ocgIter = ocgHashMap.begin(); ++ Out("/Properties <<"); ++ for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { ++ wxPdfOcg* ocg = ocgIter->second; ++ OutAscii(wxString::Format(_T("/OC%d %d 0 R"), ocg->GetOcgIndex(), ocg->GetObjectIndex())); ++ } ++ Out(">>"); + } + + void +@@ -1787,6 +1845,56 @@ + } + + void ++wxPdfDocument::PutPatterns() ++{ ++ wxPdfPatternMap::iterator patternIter = (*m_patterns).begin(); ++ for (patternIter = (*m_patterns).begin(); patternIter != (*m_patterns).end(); patternIter++) ++ { ++ wxPdfPattern* pattern = patternIter->second; ++ NewObj(); ++ pattern->SetObjIndex(m_n); ++ Out("<<"); ++ Out("/Type /Pattern"); ++ Out("/PatternType 1"); ++ Out("/PaintType 1"); ++ Out("/TilingType 1"); ++ OutAscii(wxString::Format(_T("/BBox [0 0 %d %d]"), pattern->GetImageWidth(), pattern->GetImageHeight())); ++ OutAscii(wxString::Format(_T("/XStep %d"), pattern->GetImageWidth())); ++ OutAscii(wxString::Format(_T("/YStep %d"), pattern->GetImageHeight())); ++ wxPdfImage *image = pattern->GetImage(); ++ OutAscii(wxString::Format(_T("/Resources << /XObject << /I%d %d 0 R >> >>"), image->GetIndex(), image->GetObjIndex())); ++ Out("/Matrix [ 1 0 0 1 0 0 ]"); ++ ++ wxString sdata = wxString::Format(_T("q %d 0 0 %d 0 0 cm /I%d Do Q"), pattern->GetImageWidth(), pattern->GetImageHeight(), image->GetIndex()); ++ wxMemoryOutputStream *p = new wxMemoryOutputStream; ++ p->Write(sdata.ToAscii(), sdata.Length()); ++ OutAscii(wxString(_T("/Length ")) + wxString::Format(_T("%ld"), CalculateStreamLength(p->TellO())) ); ++ Out(">>"); ++ PutStream(*p); ++ delete p; ++ Out("endobj"); ++ } ++} ++ ++void ++wxPdfDocument::PutOc() ++{ ++ wxPdfOcgHashMap &ocgHashMap = m_pdfOc.GetOcgMap(); ++ wxPdfOcgHashMap::iterator ocgIter = ocgHashMap.begin(); ++ for( ocgIter = ocgHashMap.begin(); ocgIter != ocgHashMap.end(); ocgIter++) { ++ wxPdfOcg* ocg = ocgIter->second; ++ NewObj(); ++ ocg->SetObjectIndex(m_n); ++ Out("<<"); ++ Out("/Type /OCG"); ++ OutAscii(wxString::Format(_T("/Name (%s)"), ocg->GetName())); ++ OutAscii(_T("/Intent ") + ocg->GetIntentString()); ++ Out(">>"); ++ Out("endobj"); ++ } ++} ++ ++void + wxPdfDocument::PutJavaScript() + { + if (m_javascript.Length() > 0) +@@ -1819,7 +1927,9 @@ + PutImages(); + PutTemplates(); + PutImportedObjects(); +- PutSpotColors(); ++ PutSpotColors(); ++ PutPatterns(); ++ PutOc(); + + // Resource dictionary + (*m_offsets)[2-1] = m_buffer.TellO(); +Index: components/wxpdfdoc/src/pdfparser.cpp +=================================================================== +RCS file: /cvsroot/wxcode/wxCode/components/wxpdfdoc/src/pdfparser.cpp,v +retrieving revision 1.1 +diff -u -r1.1 pdfparser.cpp +--- components/wxpdfdoc/src/pdfparser.cpp 15 Dec 2006 20:53:04 -0000 1.1 ++++ components/wxpdfdoc/src/pdfparser.cpp 1 Jan 2008 01:28:11 -0000 +@@ -63,6 +63,8 @@ + if (ms_fileSystem == NULL) + { + static wxFileSystem fileSystem; ++ //I dont know why this needs to be added manually, all the documents indicate that nothing needs to be done for local files. ++ fileSystem.AddHandler(new wxLocalFSHandler()); + ms_fileSystem = &fileSystem; + } + return ms_fileSystem;