├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── Findlibemf2svg.cmake ├── Findlibrevenge.cmake ├── Findlibvisio.cmake ├── Findlibwmf.cmake └── LibFindMacros.cmake ├── deps ├── memstream └── memstream-0.1 │ ├── memstream.c │ ├── memstream.h │ └── test.c ├── goodies └── format ├── inc ├── memstream.h └── visio2svg │ ├── TitleGenerator.h │ └── Visio2Svg.h ├── src ├── conv │ ├── vsd2svg.cpp │ └── vss2svg.cpp └── lib │ └── visio2svg │ ├── TitleGenerator.cpp │ ├── Visio2Svg.cpp │ └── memstream.c └── tests ├── resources ├── check_correctness.sh ├── check_truncate.sh ├── colors.sh └── svg11-flat.dtd └── travis-setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | *.so.* 12 | 13 | # Compiled Static libraries 14 | *.lai 15 | *.la 16 | *.a 17 | *.lib 18 | 19 | # Executables 20 | *.exe 21 | *.out 22 | *.app 23 | 24 | # cMake files 25 | CMakeCache.txt 26 | CMakeFiles 27 | Makefile 28 | cmake_install.cmake 29 | install_manifest.txt 30 | 31 | # compiled exe 32 | vss2svg-conv 33 | vsd2svg-conv 34 | 35 | # out directory 36 | out 37 | tests/deps_build/ 38 | tests/usr/ 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | install: 4 | - ./tests/travis-setup.sh 5 | 6 | addons_shortcuts: 7 | addons_clang: &clang 8 | apt: 9 | sources: [ 'ubuntu-toolchain-r-test', 'ppa:kubuntu-ppa/backports' ] 10 | packages: 11 | - libvisio-dev 12 | - clang 13 | - libboost-dev 14 | - libxml2-dev 15 | - valgrind 16 | - cmake 17 | - libcppunit-dev 18 | - autotools-dev 19 | - doxygen 20 | - gperf 21 | - libicu-dev 22 | - zlib1g-dev 23 | - libpng-dev 24 | - libwmf-dev 25 | 26 | before_install: 27 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then xcode-select --install;true; fi 28 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi 29 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install argp-standalone; fi 30 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libwmf; fi 31 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install valgrind; fi 32 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libxml2; fi 33 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install icu4c; fi 34 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install intltool; fi 35 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install librevenge; fi 36 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libvisio; fi 37 | 38 | matrix: 39 | include: 40 | - os: linux 41 | compiler: "clang" 42 | addons: *clang 43 | dist: trusty 44 | - os: osx 45 | 46 | script: CC=clang CXX=clang++ cmake . -DCMAKE_LIBRARY_PATH=./tests/usr/local/lib/ -DCMAKE_INCLUDE_PATH=./tests/usr/local/include/ -DUSE_CLANG=ON -DUNSAFE_FILENAME=ON && make #&& ./tests/resources/check_correctness.sh -rsxn 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | 3 | project (vss2svg) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") 6 | 7 | find_package(libemf2svg REQUIRED) 8 | find_package(libwmf REQUIRED) 9 | find_package(librevenge REQUIRED) 10 | find_package(libvisio REQUIRED) 11 | find_package(LibXml2 REQUIRED) 12 | find_package(Freetype REQUIRED) 13 | 14 | # Project version (sematic versionning) 15 | set(vss2svg_VERSION_MAJOR 0) 16 | set(vss2svg_VERSION_MINOR 5) 17 | set(vss2svg_VERSION_PATCH 6) 18 | 19 | set(vss2svg_VERSION ${vss2svg_VERSION_MAJOR}.${vss2svg_VERSION_MINOR}.${vss2svg_VERSION_PATCH}) 20 | # set version as a definition 21 | set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -DV2S_VERSION='\"${vss2svg_VERSION}\"'") 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DV2S_VERSION='\"${vss2svg_VERSION}\"'") 23 | 24 | add_custom_target(tag 25 | COMMAND git tag -a ${vss2svg_VERSION} -m "tagging version ${vss2svg_VERSION}" 26 | COMMAND git push origin ${vss2svg_VERSION} 27 | ) 28 | 29 | # Options 30 | option(DEBUG "compile with debug symbol" OFF) 31 | option(STATIC "compile statically" OFF) 32 | option(USE_CLANG "build application with clang" OFF) 33 | option(USE_GCC "build application with gcc" OFF) 34 | option(UNSAFE_FILENAME "disable safe file naming (if your stdlib doesn't support regex)" OFF) 35 | 36 | if(UNSAFE_FILENAME) 37 | else(UNSAFE_FILENAME) 38 | set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -DSAFE_FILENAME") 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSAFE_FILENAME") 40 | endif(UNSAFE_FILENAME) 41 | 42 | if(USE_CLANG) 43 | set(CMAKE_CXX_COMPILER "clang++") 44 | set(CMAKE_CC_COMPILER "clang" ) 45 | endif(USE_CLANG) 46 | 47 | if(USE_GCC) 48 | set(CMAKE_CXX_COMPILER "g++") 49 | set(CMAKE_CC_COMPILER "gcc") 50 | endif(USE_GCC) 51 | 52 | # Build external dependancies if we are on OSX 53 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 54 | # Mac OS X specific code 55 | set(EXTERNAL_MEMSTREAM "memstream") 56 | set(EXTERNAL_ARGP "argp") 57 | add_definitions(-DDARWIN) 58 | ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 59 | 60 | if(DEBUG) 61 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -DDEBUG") 62 | set(CMAKE_BUILD_TYPE Debug) 63 | endif(DEBUG) 64 | 65 | if(STATIC) 66 | set(SHARED "") 67 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 68 | set(BUILD_SHARED_LIBRARIES OFF) 69 | set(CMAKE_EXE_LINKER_FLAGS "-static") 70 | else(STATIC) 71 | set(SHARED "SHARED") 72 | set(BUILD_SHARED_LIBRARIES ON) 73 | endif(STATIC) 74 | 75 | LINK_DIRECTORIES( 76 | /usr/local/lib 77 | /usr/lib/ 78 | ) 79 | 80 | include_directories( 81 | inc/ 82 | ${LIBEMF2SVG_INCLUDE_DIR} 83 | ${LIBWMF_INCLUDE_DIR} 84 | ${LIBREVENGE_INCLUDE_DIR} 85 | ${LIBVISIO_INCLUDE_DIR} 86 | ${FREETYPE_INCLUDE_DIRS} 87 | ${FREETYPE_INCLUDE_DIR_ft2build} 88 | ${FREETYPE_INCLUDE_DIR_freetype2} 89 | ${LIBXML2_INCLUDE_DIR} 90 | ) 91 | 92 | add_executable(vss2svg-conv src/conv/vss2svg.cpp) 93 | add_executable(vsd2svg-conv src/conv/vsd2svg.cpp) 94 | 95 | add_library(TitleGenerator 96 | ${SHARED} 97 | src/lib/visio2svg/TitleGenerator.cpp 98 | ) 99 | 100 | add_library(Visio2Svg 101 | ${SHARED} 102 | src/lib/visio2svg/Visio2Svg.cpp 103 | ) 104 | 105 | set_target_properties(Visio2Svg 106 | PROPERTIES 107 | VERSION ${vss2svg_VERSION} 108 | SOVERSION ${vss2svg_VERSION_MAJOR} 109 | ) 110 | 111 | set_target_properties(TitleGenerator 112 | PROPERTIES 113 | VERSION ${vss2svg_VERSION} 114 | SOVERSION ${vss2svg_VERSION_MAJOR} 115 | ) 116 | 117 | target_link_libraries(TitleGenerator 118 | ${LIBREVENGE_LIBRARIES} 119 | ${LIBVISIO_LIBRARIES} 120 | ) 121 | 122 | target_link_libraries(Visio2Svg 123 | ${LIBREVENGE_LIBRARIES} 124 | ${LIBVISIO_LIBRARIES} 125 | ${LIBEMF2SVG_LIBRARIES} 126 | ${LIBWMF_LIBRARIES} 127 | ${LIBWMFLITE_LIBRARIES} 128 | ${LIBREVENGE_STREAM_LIBRARY} 129 | ${LIBXML2_LIBRARIES} 130 | TitleGenerator 131 | ${EXTERNAL_ARGP} 132 | ) 133 | 134 | target_link_libraries(vss2svg-conv 135 | Visio2Svg 136 | ) 137 | 138 | target_link_libraries(vsd2svg-conv 139 | Visio2Svg 140 | ) 141 | 142 | if (NOT LIB_INSTALL_DIR) 143 | set(LIB_INSTALL_DIR lib) 144 | endif () 145 | 146 | if (NOT BIN_INSTALL_DIR) 147 | set(BIN_INSTALL_DIR bin) 148 | endif () 149 | 150 | if (NOT INCLUDE_INSTALL_DIR) 151 | set(INCLUDE_INSTALL_DIR include) 152 | endif () 153 | 154 | 155 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 156 | INSTALL(TARGETS vss2svg-conv vsd2svg-conv TitleGenerator Visio2Svg 157 | RUNTIME DESTINATION ${BIN_INSTALL_DIR} 158 | LIBRARY DESTINATION ${LIB_INSTALL_DIR} 159 | ARCHIVE DESTINATION ${LIB_INSTALL_DIR} 160 | ) 161 | 162 | INSTALL(DIRECTORY inc/visio2svg DESTINATION ${INCLUDE_INSTALL_DIR}) 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Libvisio2svg 2 | ============ 3 | 4 | [![Build Status](https://travis-ci.org/kakwa/libvisio2svg.svg?branch=master)](https://travis-ci.org/kakwa/libvisio2svg) 5 | 6 | 7 | Library/Utilities to convert Microsoft (MS) Visio Documents and Stencils (VSS and VSD) to SVG. 8 | 9 | Motivation 10 | ========== 11 | 12 | There are tons of publicly available MS Visio stencils, for example 13 | [the Cisco ones](http://www.cisco.com/c/en/us/products/visio-stencil-listing.html) 14 | or the stencils from [VisioCafe](http://www.visiocafe.com/). 15 | This library and utilities were created to be able to convert these stencils to SVG 16 | and reuse them outside of Microsoft Visio, in programs like 17 | [yEd](https://www.yworks.com/products/yed), [Inkscape](https://inkscape.org), 18 | [Dia](http://dia-installer.de/), [Calligra Flow](https://www.calligra.org/flow/)... 19 | 20 | This library is mainly the glue between [librevenge](http://sourceforge.net/p/libwpd/wiki/librevenge/)/[libvisio](https://github.com/LibreOffice/libvisio), [libemf2svg](https://github.com/kakwa/libemf2svg) and [libwmf](http://wvware.sourceforge.net/libwmf.html). 21 | 22 | About libemf2svg 23 | ---------------- 24 | 25 | [libemf2svg](https://github.com/kakwa/libemf2svg) is another library of mine. 26 | It was developed to handle MS EMF (Enhanced Metafile) blobs which constitute most of Visio VSS shapes. 27 | 28 | Librevenge/Libvisio would otherwise only dump the EMF blob as base64 in an \ SVG tag, which most viewer/editor/browser 29 | would be unable to display. 30 | 31 | License 32 | ======= 33 | 34 | Libvisio2svg is licensed under GPLv2. 35 | 36 | Dependencies 37 | ============ 38 | 39 | * [librevenge](http://sourceforge.net/p/libwpd/wiki/librevenge/) 40 | * [libvisio](https://github.com/LibreOffice/libvisio) 41 | * [libemf2svg](https://github.com/kakwa/libemf2svg) 42 | * [libxml2](http://www.xmlsoft.org/) 43 | * [libwmf](http://wvware.sourceforge.net/libwmf.html) 44 | * [fonts from the ghostscript project](https://www.ghostscript.com/) (libwmf requirement, usually the distribution package is named `gsfonts`) 45 | 46 | Building 47 | ======== 48 | 49 | Commands to build this project: 50 | 51 | ```bash 52 | # CMAKE_INSTALL_PREFIX is optional, default is /usr/local/ 53 | $ cmake . -DCMAKE_INSTALL_PREFIX=/usr/ 54 | 55 | # compilation 56 | $ make 57 | 58 | # installation 59 | $ make install 60 | ``` 61 | 62 | Regex 63 | ----- 64 | 65 | The vss2svg and vsd2svg utilities rely on regular expressions to construct safe file names from stencils/sheets names. 66 | It will replace anything that is not matching **[A-Za-z0-9-]** by an underscore. 67 | 68 | This functionality relies on regexp from libstdc++ (c++11 standard), but older libraries doesn't support regex. 69 | 70 | Yet you can compile without this file name sanitization: 71 | 72 | ``` 73 | $ cmake . -DUNSAFE_FILENAME=ON 74 | $ make 75 | ``` 76 | 77 | However, be cautious with the stencil/sheet names, otherwise some files might be written outside the output directory. 78 | 79 | Usage 80 | ===== 81 | 82 | Command Line 83 | ------------ 84 | 85 | Convert VSS: 86 | 87 | ```bash 88 | # conversion 89 | $ vss2svg-conv -i ./2960CX.vss -o ./out/ -s 4.5 90 | 91 | $ ls out/ 92 | 'Cisco R42610 Front.svg' 'WS-C2960CX-8PC-L Rear.svg' 'WS-C2960CX-8TC-L Rear.svg' 93 | 'WS-C2960CX-8PC-L Front.svg' 'WS-C2960CX-8TC-L Front.svg' 94 | 95 | # help 96 | $ vss2svg-conv --help 97 | ``` 98 | 99 | Convert VSD: 100 | 101 | ```bash 102 | # conversion 103 | $ vsd2svg-conv -i ./my.VSD -o ./out/ -s 7 104 | 105 | $ ls out/ 106 | Page-1.svg Page-2.svg Page-3.svg Page-4.svg 107 | 108 | # help 109 | $ vsd2svg-conv --help 110 | ``` 111 | 112 | Library 113 | ======= 114 | 115 | Convert Visio Documents 116 | ----------------------- 117 | 118 | example: 119 | ```cpp 120 | #include 121 | #include 122 | #include 123 | #include 124 | 125 | int main(int argc, char **argv) { 126 | // Put visio file in an std::string 127 | // (no error checking, should be done in actual code) 128 | std::ifstream stin(argv[1]); 129 | 130 | std::string visio_in((std::istreambuf_iterator(stin)), 131 | std::istreambuf_iterator()); 132 | 133 | // Initialize converter and output map 134 | visio2svg::Visio2Svg converter; 135 | std::unordered_map out; 136 | 137 | // return code 138 | int ret = 0; 139 | 140 | // Pick one of the following 141 | 142 | // Convert vsd documents 143 | ret = converter.vsd2svg(visio_in, out); 144 | 145 | // Convert vss (Stencils) documents 146 | ret = converter.vss2svg(visio_in, out); 147 | 148 | // or with rescaling 149 | ret = converter.vsd2svg(visio_in, out, 4.5); 150 | ret = converter.vss2svg(visio_in, out, 4.5); 151 | 152 | if (ret) 153 | std::cerr << "Conversion errors occured" 154 | << "\n"; 155 | 156 | // Do something with the output 157 | for (const auto &rule_pair : out) { 158 | std::cout << "Sheet Title: " << rule_pair.first << std::endl; 159 | std::cout << rule_pair.second << std::endl; 160 | } 161 | } 162 | ``` 163 | 164 | Recover symbol titles 165 | --------------------- 166 | 167 | This library also comes with a RVNG generator to recover symbol titles: 168 | 169 | ```cpp 170 | #include 171 | #include 172 | #include 173 | #include 174 | #include 175 | #include 176 | 177 | int main(int argc, char **argv) { 178 | // put the Visio document in an RVNGFileStream 179 | // (no error checking, should be done in actual code) 180 | librevenge::RVNGFileStream input(argv[1]); 181 | 182 | /* String stream version (need to be set) 183 | librevenge::RVNGStringStream input; 184 | */ 185 | 186 | // Recover Titles of each sheets 187 | librevenge::RVNGStringVector output_names; 188 | visio2svg::TitleGenerator generator_names(output_names); 189 | 190 | bool ret = libvisio::VisioDocument::parseStencils(&input, &generator_names); 191 | /* or this for vsd 192 | ret = libvisio::VisioDocument::parse(&input, &generator_names); 193 | */ 194 | 195 | if (!ret || output_names.empty()) { 196 | std::cerr << "ERROR: Failed to recover sheets titles failed!" 197 | << std::endl; 198 | return 1; 199 | } 200 | 201 | // do something with the names 202 | for (unsigned k = 0; k < output_names.size(); ++k) { 203 | std::cout << "Title of stencil " << std::to_string(k) << std::endl; 204 | if (output_names[k].empty()) { 205 | std::cout << "no title set in stencil number " << std::to_string(k) 206 | << std::endl; 207 | } else { 208 | std::cout << output_names[k].cstr() << std::endl; 209 | } 210 | } 211 | } 212 | ``` 213 | 214 | Changelog 215 | ========= 216 | 217 | 0.5.5 218 | ----- 219 | 220 | * add LIB_INSTALL_DIR variable in cmake 221 | 222 | 0.5.4 223 | ----- 224 | 225 | * fix freetype discovery in cmake 226 | 227 | 0.5.3 228 | ----- 229 | 230 | * fix segfault if shape doesn't contain title 231 | 232 | 0.5.2 233 | ----- 234 | 235 | * fix error in setting title field with titles containing "&" 236 | * add XML_PARSE_HUGE in xml parsing to allow big image blobs. 237 | * better precision for image dimensions (keep 10 decimals) 238 | 239 | 0.5.1 240 | ----- 241 | 242 | * add support for OSX 243 | 244 | 0.5.0 245 | ----- 246 | 247 | * adding support to convert wmf blob (using libwmf) 248 | * requires new libraries as dependencies: libwmf and freetype) 249 | * requires libemf2svg version >= 1.0.0 250 | * cleaner and more explicit dependencies resolving in cmake 251 | 252 | 0.4.1 253 | ----- 254 | 255 | * adding safeguard regarding unsafe output file paths 256 | 257 | 0.3.0 258 | ----- 259 | 260 | * better error handling 261 | 262 | 0.2.0 263 | ----- 264 | 265 | * add rescaling option + title 266 | 267 | 0.1.0 268 | ----- 269 | 270 | * first release 271 | -------------------------------------------------------------------------------- /cmake/Findlibemf2svg.cmake: -------------------------------------------------------------------------------- 1 | if (NOT LIBEMF2SVG_LIBRARIES) 2 | find_path(LIBEMF2SVG_INCLUDE_DIR emf2svg.h ${_LIBEMF2SVG_PATHS} PATH_SUFFIXES include include/emf2svg/) 3 | endif () 4 | 5 | if (NOT LIBEMF2SVG_LIBRARIES) 6 | find_library(LIBEMF2SVG_LIBRARIES NAMES emf2svg ${_LIBEMF2SVG_PATHS} PATH_SUFFIXES lib) 7 | endif () 8 | 9 | MESSAGE(STATUS "Find Header Directory for libemf2svg: " ${LIBEMF2SVG_INCLUDE_DIR}) 10 | MESSAGE(STATUS "Find Dynamic Library for libemf2svg: " ${LIBEMF2SVG_LIBRARIES}) 11 | -------------------------------------------------------------------------------- /cmake/Findlibrevenge.cmake: -------------------------------------------------------------------------------- 1 | 2 | # - Try to find the librevenge 3 | # Once done this will define 4 | # 5 | # LIBREVENGE_FOUND - system has LIBREVENGE 6 | # LIBREVENGE_INCLUDE_DIRS - the LIBREVENGE include directory 7 | # LIBREVENGE_LIBRARIES - Link these to use LIBREVENGE 8 | # LIBREVENGE_DEFINITIONS - Compiler switches required for using LIBREVENGE 9 | # 10 | 11 | include(LibFindMacros) 12 | #libfind_pkg_check_modules(REVENGE_PKGCONF librevenge-0.0) 13 | 14 | find_path(LIBREVENGE_INCLUDE_DIR 15 | NAMES librevenge/librevenge.h 16 | HINTS ${REVENGE_PKGCONF_INCLUDE_DIRS} ${REVENGE_PKGCONF_INCLUDEDIR} 17 | PATH_SUFFIXES librevenge-0.0 18 | ) 19 | 20 | find_path(LIBREVENGE_STREAM_INCLUDE_DIR 21 | NAMES librevenge-stream/librevenge-stream.h 22 | HINTS ${REVENGE_STREAM_PKGCONF_INCLUDE_DIRS} ${REVENGE_STREAM_PKGCONF_INCLUDEDIR} 23 | PATH_SUFFIXES librevenge-0.0 24 | ) 25 | 26 | find_library(LIBREVENGE_LIBRARIES 27 | NAMES revenge librevenge revenge-0.0 librevenge-0.0 28 | HINTS ${REVENGE_STREAM_PKGCONF_LIBRARY_DIRS} ${REVENGE_STREAM_PKGCONF_LIBDIR} 29 | ) 30 | 31 | find_library(LIBREVENGE_STREAM_LIBRARY 32 | NAMES revenge-stream librevenge-stream revenge-stream-0.0 librevenge-stream-0.0 33 | HINTS ${REVENGE_PKGCONF_LIBRARY_DIRS} ${REVENGE_PKGCONF_LIBDIR} 34 | ) 35 | 36 | set(LIBREVENGE_PROCESS_LIBS REVENGE_LIBRARY REVENGE_STREAM_LIBRARY) 37 | set(LIBREVENGE_PROCESS_INCLUDES REVENGE_INCLUDE_DIR REVENGE_STREAM_INCLUDE_DIR) 38 | libfind_process(LIBREVENGE) 39 | 40 | MESSAGE( STATUS "Find Header Directory for librevenge: " ${LIBREVENGE_INCLUDE_DIR}) 41 | MESSAGE( STATUS "Find Header Directory for librevenge: " ${LIBREVENGE_STREAM_INCLUDE_DIR}) 42 | MESSAGE( STATUS "Find Dynamic Library for librevenge: " ${LIBREVENGE_LIBRARIES}) 43 | MESSAGE( STATUS "Find Dynamic Library for librevenge: " ${LIBREVENGE_STREAM_LIBRARY}) 44 | -------------------------------------------------------------------------------- /cmake/Findlibvisio.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find LibVisio 2 | # Once done this will define 3 | # 4 | # LIBVISIO_FOUND - libvisio is available 5 | # LIBVISIO_INCLUDE_DIRS - include directory, e.g. /usr/include 6 | # LIBVISIO_LIBRARIES - the libraries needed to use LibVisio 7 | # LIBVISIO_DEFINITIONS - Compiler switches required for using LibVisio 8 | # 9 | # Copyright (C) 2013 Yue Liu 10 | # Redistribution and use is allowed according to the terms of the BSD license. 11 | 12 | include(LibFindMacros) 13 | 14 | find_path(LIBVISIO_INCLUDE_DIR 15 | NAMES libvisio/libvisio.h 16 | HINTS ${LIBVISIO_PKGCONF_INCLUDE_DIRS} ${LIBVISIO_PKGCONF_INCLUDEDIR} 17 | PATH_SUFFIXES libvisio-0.1 18 | ) 19 | 20 | find_library(LIBVISIO_LIBRARY 21 | NAMES visio visio-0.1 22 | HINTS ${LIBVISIO_PKGCONF_LIBRARY_DIRS} ${LIBVISIO_PKGCONF_LIBDIR} 23 | ) 24 | 25 | set(LIBVISIO_PROCESS_LIBS LIBVISIO_LIBRARY LIBWPD_LIBRARIES LIBWPG_LIBRARIES) 26 | libfind_process(LIBVISIO) 27 | 28 | MESSAGE( STATUS "Find Header Directory for libvisio: " ${LIBVISIO_INCLUDE_DIR}) 29 | MESSAGE( STATUS "Find Dynamic Library for libvisio: " ${LIBVISIO_LIBRARIES}) 30 | -------------------------------------------------------------------------------- /cmake/Findlibwmf.cmake: -------------------------------------------------------------------------------- 1 | 2 | #if (NOT LIBWMF_LIBRARIES) 3 | 4 | find_path(LIBWMF_INCLUDE_DIR 5 | NAMES libwmf/api.h api.h 6 | HINTS ${WMF_PKGCONF_INCLUDE_DIRS} ${WMF_PKGCONF_INCLUDEDIR} 7 | PATH_SUFFIXES include include/libwmf/ 8 | ) 9 | 10 | #endif () 11 | 12 | if (NOT LIBWMF_LIBRARIES) 13 | find_library(LIBWMF_LIBRARIES NAMES wmf ${_LIBWMF_PATHS} PATH_SUFFIXES lib) 14 | find_library(LIBWMFLITE_LIBRARIES NAMES wmflite ${_LIBWMF_PATHS} PATH_SUFFIXES lib) 15 | endif () 16 | 17 | MESSAGE( STATUS "Find Header Directory for libwmf: " ${LIBWMF_INCLUDE_DIR}) 18 | MESSAGE( STATUS "Find Dynamic Library for libwmf: " ${LIBWMF_LIBRARIES}) 19 | MESSAGE( STATUS "Find Dynamic Library for libwmflite: " ${LIBWMFLITE_LIBRARIES}) 20 | -------------------------------------------------------------------------------- /cmake/LibFindMacros.cmake: -------------------------------------------------------------------------------- 1 | # Version 1.0 (2013-04-12) 2 | # Public Domain, originally written by Lasse Kärkkäinen 3 | # Published at http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries 4 | 5 | # If you improve the script, please modify the forementioned wiki page because 6 | # I no longer maintain my scripts (hosted as static files at zi.fi). Feel free 7 | # to remove this entire header if you use real version control instead. 8 | 9 | # Changelog: 10 | # 2013-04-12 Added version number (1.0) and this header, no other changes 11 | # 2009-10-08 Originally published 12 | 13 | 14 | # Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments 15 | # used for the current package. For this to work, the first parameter must be the 16 | # prefix of the current package, then the prefix of the new package etc, which are 17 | # passed to find_package. 18 | macro (libfind_package PREFIX) 19 | set (LIBFIND_PACKAGE_ARGS ${ARGN}) 20 | if (${PREFIX}_FIND_QUIETLY) 21 | set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) 22 | endif () 23 | if (${PREFIX}_FIND_REQUIRED) 24 | set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) 25 | endif () 26 | find_package(${LIBFIND_PACKAGE_ARGS}) 27 | endmacro (libfind_package) 28 | 29 | # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) 30 | # where they added pkg_check_modules. Consequently I need to support both in my scripts 31 | # to avoid those deprecated warnings. Here's a helper that does just that. 32 | # Works identically to pkg_check_modules, except that no checks are needed prior to use. 33 | macro (libfind_pkg_check_modules PREFIX PKGNAME) 34 | if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 35 | include(UsePkgConfig) 36 | pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) 37 | else () 38 | find_package(PkgConfig) 39 | if (PKG_CONFIG_FOUND) 40 | pkg_check_modules(${PREFIX} ${PKGNAME}) 41 | endif () 42 | endif () 43 | endmacro (libfind_pkg_check_modules) 44 | 45 | # Do the final processing once the paths have been detected. 46 | # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain 47 | # all the variables, each of which contain one include directory. 48 | # Ditto for ${PREFIX}_PROCESS_LIBS and library files. 49 | # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. 50 | # Also handles errors in case library detection was required, etc. 51 | macro (libfind_process PREFIX) 52 | # Skip processing if already processed during this run 53 | if (NOT ${PREFIX}_FOUND) 54 | # Start with the assumption that the library was found 55 | set (${PREFIX}_FOUND TRUE) 56 | 57 | # Process all includes and set _FOUND to false if any are missing 58 | foreach (i ${${PREFIX}_PROCESS_INCLUDES}) 59 | if (${i}) 60 | set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) 61 | mark_as_advanced(${i}) 62 | else () 63 | set (${PREFIX}_FOUND FALSE) 64 | endif () 65 | endforeach (i) 66 | 67 | # Process all libraries and set _FOUND to false if any are missing 68 | foreach (i ${${PREFIX}_PROCESS_LIBS}) 69 | if (${i}) 70 | set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) 71 | mark_as_advanced(${i}) 72 | else () 73 | set (${PREFIX}_FOUND FALSE) 74 | endif () 75 | endforeach (i) 76 | 77 | # Print message and/or exit on fatal error 78 | if (${PREFIX}_FOUND) 79 | if (NOT ${PREFIX}_FIND_QUIETLY) 80 | message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") 81 | endif () 82 | else () 83 | if (${PREFIX}_FIND_REQUIRED) 84 | foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) 85 | message("${i}=${${i}}") 86 | endforeach (i) 87 | message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") 88 | endif () 89 | endif () 90 | endif () 91 | endmacro (libfind_process) 92 | 93 | macro(libfind_library PREFIX basename) 94 | set(TMP "") 95 | if(MSVC80) 96 | set(TMP -vc80) 97 | endif() 98 | if(MSVC90) 99 | set(TMP -vc90) 100 | endif() 101 | set(${PREFIX}_LIBNAMES ${basename}${TMP}) 102 | if(${ARGC} GREATER 2) 103 | set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) 104 | string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) 105 | set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) 106 | endif() 107 | find_library(${PREFIX}_LIBRARY 108 | NAMES ${${PREFIX}_LIBNAMES} 109 | PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} 110 | ) 111 | endmacro(libfind_library) 112 | -------------------------------------------------------------------------------- /deps/memstream: -------------------------------------------------------------------------------- 1 | memstream-0.1 -------------------------------------------------------------------------------- /deps/memstream-0.1/memstream.c: -------------------------------------------------------------------------------- 1 | /* Compile this file and link the object with your program. On a recent 2 | * GNU/Linux machine the object file will be empty. On anything derived from 3 | * 4.4BSD (Darwin, the Three BSDs, etc.) it will contain an implementation of 4 | * open_memstream() as described in the POSIX and Linux manual pages. On 5 | * anything else it will probably cause a compilation error. 6 | * 7 | * ---------------------------------------------------------------------------- 8 | * 9 | * OPEN_MEMSTREAM(3) BSD and Linux Library Functions OPEN_MEMSTREAM(3) 10 | * 11 | * SYNOPSIS 12 | * #include "memstream.h" 13 | * 14 | * FILE *open_memstream(char **bufp, size_t *sizep); 15 | * 16 | * DESCRIPTION 17 | * The open_memstream() function opens a stream for writing to a buffer. 18 | * The buffer is dynamically allocated (as with malloc(3)), and 19 | * automatically grows as required. After closing the stream, the caller 20 | * should free(3) this buffer. 21 | * 22 | * When the stream is closed (fclose(3)) or flushed (fflush(3)), the 23 | * locations pointed to by bufp and sizep are updated to contain, 24 | * respectively, a pointer to the buffer and the current size of the 25 | * buffer. These values remain valid only as long as the caller performs 26 | * no further output on the stream. If further output is performed, then 27 | * the stream must again be flushed before trying to access these 28 | * variables. 29 | * 30 | * A null byte is maintained at the end of the buffer. This byte is not 31 | * included in the size value stored at sizep. 32 | * 33 | * The stream's file position can be changed with fseek(3) or fseeko(3). 34 | * Moving the file position past the end of the data already written fills 35 | * the intervening space with zeros. 36 | * 37 | * RETURN VALUE 38 | * Upon successful completion open_memstream() returns a FILE pointer. 39 | * Otherwise, NULL is returned and errno is set to indicate the error. 40 | * 41 | * CONFORMING TO 42 | * POSIX.1-2008 43 | * 44 | * ---------------------------------------------------------------------------- 45 | */ 46 | 47 | #include "memstream.h" 48 | 49 | #if _POSIX_C_SOURCE < 200809L 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #define min(X, Y) (((X) < (Y)) ? (X) : (Y)) 58 | 59 | struct memstream 60 | { 61 | int position; 62 | int size; 63 | int capacity; 64 | char *contents; 65 | char **ptr; 66 | size_t *sizeloc; 67 | }; 68 | 69 | #if MEMSTREAM_DEBUG 70 | static void memstream_print(struct memstream *ms) 71 | { 72 | printf("memstream %p {", ms); 73 | printf(" %i", ms->position); 74 | printf(" %i", ms->size); 75 | printf(" %i", ms->capacity); 76 | printf(" %p", ms->contents); 77 | printf(" }\n"); 78 | } 79 | # define memstream_info(ARGS) printf ARGS 80 | #else 81 | # define memstream_print(ms) 82 | # define memstream_info(ARGS) 83 | #endif 84 | 85 | #define memstream_check(MS) if (!(MS)->contents) { errno= ENOMEM; return -1; } 86 | 87 | static int memstream_grow(struct memstream *ms, int minsize) 88 | { 89 | int newcap= ms->capacity * 2; memstream_check(ms); 90 | while (newcap <= minsize) newcap *= 2; memstream_info(("grow %p to %i\n", ms, newcap)); 91 | ms->contents= (char *)realloc(ms->contents, newcap); 92 | if (!ms->contents) return -1; /* errno == ENOMEM */ 93 | memset(ms->contents + ms->capacity, 0, newcap - ms->capacity); 94 | ms->capacity= newcap; 95 | *ms->ptr= ms->contents; /* size has not changed */ 96 | return 0; 97 | } 98 | 99 | static int memstream_read(void *cookie, char *buf, int count) 100 | { 101 | struct memstream *ms= (struct memstream *)cookie; memstream_check(ms); 102 | int n= min(ms->size - ms->position, count); memstream_info(("memstream_read %p %i\n", ms, count)); 103 | if (n < 1) return 0; 104 | memcpy(buf, ms->contents, n); 105 | ms->position += n; memstream_print(ms); 106 | return n; 107 | } 108 | 109 | static int memstream_write(void *cookie, const char *buf, int count) 110 | { 111 | struct memstream *ms= (struct memstream *)cookie; memstream_check(ms); 112 | if (ms->capacity <= ms->position + count) 113 | if (memstream_grow(ms, ms->position + count) < 0) /* errno == ENOMEM */ 114 | return -1; 115 | memcpy(ms->contents + ms->position, buf, count); memstream_info(("memstream_write %p %i\n", ms, count)); 116 | ms->position += count; 117 | if (ms->size < ms->position) *ms->sizeloc= ms->size= ms->position; memstream_print(ms); 118 | assert(ms->size < ms->capacity); 119 | assert(ms->contents[ms->size] == 0); 120 | return count; 121 | } 122 | 123 | static fpos_t memstream_seek(void *cookie, fpos_t offset, int whence) 124 | { 125 | struct memstream *ms= (struct memstream *)cookie; 126 | fpos_t pos= 0; memstream_check(ms); 127 | memstream_info(("memstream_seek %p %i %i\n", ms, (int)offset, whence)); 128 | switch (whence) { 129 | case SEEK_SET: pos= offset; break; 130 | case SEEK_CUR: pos= ms->position + offset; break; 131 | case SEEK_END: pos= ms->size + offset; break; 132 | default: errno= EINVAL; return -1; 133 | } 134 | if (pos >= ms->capacity) memstream_grow(ms, pos); 135 | ms->position= pos; 136 | if (ms->size < ms->position) *ms->sizeloc= ms->size= ms->position; memstream_print(ms); memstream_info(("=> %i\n", (int)pos)); 137 | assert(ms->size < ms->capacity && ms->contents[ms->size] == 0); 138 | return pos; 139 | } 140 | 141 | static int memstream_close(void *cookie) 142 | { 143 | struct memstream *ms= (struct memstream *)cookie; if (!ms->contents) { free(ms); errno= ENOMEM; return -1; } 144 | ms->size= min(ms->size, ms->position); 145 | *ms->ptr= ms->contents; 146 | *ms->sizeloc= ms->size; assert(ms->size < ms->capacity); 147 | ms->contents[ms->size]= 0; 148 | free(ms); 149 | return 0; 150 | } 151 | 152 | FILE *open_memstream(char **ptr, size_t *sizeloc) 153 | { 154 | if (ptr && sizeloc) { 155 | struct memstream *ms= (struct memstream *)calloc(1, sizeof(struct memstream)); 156 | FILE *fp= 0; if (!ms) return 0; /* errno == ENOMEM */ 157 | ms->position= ms->size= 0; 158 | ms->capacity= 4096; 159 | ms->contents= (char *)calloc(ms->capacity, 1); if (!ms->contents) { free(ms); return 0; } /* errno == ENOMEM */ 160 | ms->ptr= ptr; 161 | ms->sizeloc= sizeloc; 162 | memstream_print(ms); 163 | fp= funopen(ms, memstream_read, memstream_write, memstream_seek, memstream_close); 164 | if (!fp) { 165 | free(ms->contents); 166 | free(ms); 167 | return 0; /* errno set by funopen */ 168 | } 169 | *ptr= ms->contents; 170 | *sizeloc= ms->size; 171 | return fp; 172 | } 173 | errno= EINVAL; 174 | return 0; 175 | } 176 | 177 | #endif /* _POSIX_C_SOURCE < 200809L */ 178 | -------------------------------------------------------------------------------- /deps/memstream-0.1/memstream.h: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) 2 | # include 3 | #endif 4 | 5 | #include 6 | 7 | #if _POSIX_C_SOURCE < 200809L 8 | 9 | FILE *open_memstream(char **ptr, size_t *sizeloc); 10 | 11 | #endif /* _POSIX_C_SOURCE < 200809L */ 12 | -------------------------------------------------------------------------------- /deps/memstream-0.1/test.c: -------------------------------------------------------------------------------- 1 | #include "memstream.h" 2 | 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | char *buffer= 0; 9 | size_t size= 0; 10 | FILE *fp= open_memstream(&buffer, &size); 11 | int i; 12 | for (i= 0; i < 10240; ++i) { 13 | static char c= 42; 14 | fflush(fp); assert(size == i); 15 | fwrite(&c, 1, 1, fp); 16 | } 17 | fclose(fp); assert(size == 10240); 18 | free(buffer); 19 | fp= open_memstream(&buffer, &size); 20 | fprintf(fp, "This is a test of memstream, from main at %p.\n", main); 21 | fclose(fp); 22 | fputs(buffer, stdout); 23 | free(buffer); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /goodies/format: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0`/../ 4 | 5 | clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, AllowShortFunctionsOnASingleLine: None, KeepEmptyLinesAtTheStartOfBlocks: false}" -i \ 6 | `find ./src/ ./inc/ -type f -name "*.cpp" -o \ 7 | -type f -name "*.c" -o \ 8 | -type f -name "*.h" -o \ 9 | -type f -name "*.hpp"` 10 | 11 | -------------------------------------------------------------------------------- /inc/memstream.h: -------------------------------------------------------------------------------- 1 | ../deps/memstream/memstream.h -------------------------------------------------------------------------------- /inc/visio2svg/TitleGenerator.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace visio2svg { 13 | 14 | class REVENGE_API TitleGenerator : public librevenge::RVNGDrawingInterface { 15 | public: 16 | TitleGenerator(librevenge::RVNGStringVector &titles); 17 | ~TitleGenerator(); 18 | void startPage(const librevenge::RVNGPropertyList &propList); 19 | void startDocument(const librevenge::RVNGPropertyList &propList); 20 | void endDocument(); 21 | void setDocumentMetaData(const librevenge::RVNGPropertyList &propList); 22 | void defineEmbeddedFont(const librevenge::RVNGPropertyList &propList); 23 | void endPage(); 24 | void startMasterPage(const librevenge::RVNGPropertyList &propList); 25 | void endMasterPage(); 26 | void startLayer(const librevenge::RVNGPropertyList &propList); 27 | void endLayer(); 28 | void startEmbeddedGraphics(const librevenge::RVNGPropertyList &propList); 29 | void endEmbeddedGraphics(); 30 | 31 | void openGroup(const librevenge::RVNGPropertyList &propList); 32 | void closeGroup(); 33 | 34 | void setStyle(const librevenge::RVNGPropertyList &propList); 35 | 36 | void drawRectangle(const librevenge::RVNGPropertyList &propList); 37 | void drawEllipse(const librevenge::RVNGPropertyList &propList); 38 | void drawPolyline(const librevenge::RVNGPropertyList &propList); 39 | void drawPolygon(const librevenge::RVNGPropertyList &propList); 40 | void drawPath(const librevenge::RVNGPropertyList &propList); 41 | void drawGraphicObject(const librevenge::RVNGPropertyList &propList); 42 | void drawConnector(const librevenge::RVNGPropertyList &propList); 43 | void startTextObject(const librevenge::RVNGPropertyList &propList); 44 | void endTextObject(); 45 | 46 | void startTableObject(const librevenge::RVNGPropertyList &propList); 47 | void openTableRow(const librevenge::RVNGPropertyList &propList); 48 | void closeTableRow(); 49 | void openTableCell(const librevenge::RVNGPropertyList &propList); 50 | void closeTableCell(); 51 | void insertCoveredTableCell(const librevenge::RVNGPropertyList &propList); 52 | void endTableObject(); 53 | 54 | void openOrderedListLevel(const librevenge::RVNGPropertyList &propList); 55 | void closeOrderedListLevel(); 56 | 57 | void openUnorderedListLevel(const librevenge::RVNGPropertyList &propList); 58 | void closeUnorderedListLevel(); 59 | void openListElement(const librevenge::RVNGPropertyList &propList); 60 | void closeListElement(); 61 | 62 | void defineParagraphStyle(const librevenge::RVNGPropertyList &propList); 63 | void openParagraph(const librevenge::RVNGPropertyList &propList); 64 | void closeParagraph(); 65 | 66 | void defineCharacterStyle(const librevenge::RVNGPropertyList &propList); 67 | void openSpan(const librevenge::RVNGPropertyList &propList); 68 | void closeSpan(); 69 | 70 | void openLink(const librevenge::RVNGPropertyList &propList); 71 | void closeLink(); 72 | 73 | void insertTab(); 74 | void insertSpace(); 75 | void insertText(const librevenge::RVNGString &text); 76 | void insertLineBreak(); 77 | void insertField(const librevenge::RVNGPropertyList &propList); 78 | 79 | private: 80 | librevenge::RVNGStringVector &titles; 81 | }; 82 | } 83 | 84 | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ 85 | -------------------------------------------------------------------------------- /inc/visio2svg/Visio2Svg.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace visio2svg { 15 | 16 | enum IMG_TYPE { 17 | UNKNOWN_IMGTYPE, 18 | EMF_IMGTYPE, 19 | WMF_IMGTYPE, 20 | }; 21 | 22 | class Visio2Svg { 23 | public: 24 | Visio2Svg(); 25 | ~Visio2Svg(); 26 | int vss2svg(std::string &in, 27 | std::unordered_map &out); 28 | int vsd2svg(std::string &in, 29 | std::unordered_map &out); 30 | int vss2svg(std::string &in, 31 | std::unordered_map &out, 32 | double scaling); 33 | int vsd2svg(std::string &in, 34 | std::unordered_map &out, 35 | double scaling); 36 | 37 | private: 38 | int postTreatement(const librevenge::RVNGString *in, 39 | const librevenge::RVNGString *name, char **out, 40 | double scaling); 41 | int scale_title(xmlNode **root, xmlDocPtr *doc, double scaling, 42 | const xmlChar *title, int title_len); 43 | int visio2svg(std::string &in, 44 | std::unordered_map &out, 45 | double scaling, int mode); 46 | }; 47 | } 48 | 49 | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ 50 | -------------------------------------------------------------------------------- /src/conv/vsd2svg.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | 3 | /* vsd2svg 4 | * work based on vsd2xhtml from libvisio 5 | */ 6 | 7 | // <<<<<<<<<<<<<<<<<<< START ORIGINAL HEADER >>>>>>>>>>>>>>>>>>>>>>>>>>> 8 | 9 | /* 10 | * This file is part of the libvisio project. 11 | * 12 | * This Source Code Form is subject to the terms of the Mozilla Public 13 | * License, v. 2.0. If a copy of the MPL was not distributed with this 14 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 15 | */ 16 | 17 | // <<<<<<<<<<<<<<<<<<< END ORIGINAL HEADER >>>>>>>>>>>>>>>>>>>>>>>>>>> 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #ifdef SAFE_FILENAME 24 | #include 25 | #endif 26 | #include "visio2svg/Visio2Svg.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | const char *argp_program_version = V2S_VERSION; 36 | 37 | const char *argp_program_bug_address = ""; 38 | 39 | static char doc[] = "vsd2svg -- Visio VSD to SVG converter"; 40 | 41 | static struct argp_option options[] = { 42 | {"input", 'i', "FILE", 0, "Input Visio .vsd file"}, 43 | {"output", 'o', "DIR", 0, "Output directory"}, 44 | {"scaling", 's', "FLOAT", 0, "Scaling factor"}, 45 | {"version", 'V', 0, 0, "Print vsd2svg version"}, 46 | {0}}; 47 | 48 | /* A description of the arguments we accept. */ 49 | static char args_doc[] = "[options] -i -o "; 50 | 51 | struct arguments { 52 | char *args[2]; /* arg1 & arg2 */ 53 | bool version; 54 | char *output; 55 | char *scaling; 56 | char *input; 57 | }; 58 | 59 | static error_t parse_opt(int key, char *arg, struct argp_state *state) { 60 | /* Get the input argument from argp_parse, which we 61 | know is a pointer to our arguments structure. */ 62 | struct arguments *arguments = (struct arguments *)state->input; 63 | 64 | switch (key) { 65 | case 'o': 66 | arguments->output = arg; 67 | break; 68 | case 'i': 69 | arguments->input = arg; 70 | break; 71 | case 's': 72 | arguments->scaling = arg; 73 | break; 74 | case 'V': 75 | arguments->version = 1; 76 | break; 77 | case ARGP_KEY_ARG: 78 | if (state->arg_num >= 6) 79 | /* Too many arguments. */ 80 | argp_usage(state); 81 | 82 | arguments->args[state->arg_num] = arg; 83 | 84 | break; 85 | 86 | case ARGP_KEY_END: 87 | break; 88 | default: 89 | return ARGP_ERR_UNKNOWN; 90 | } 91 | return 0; 92 | } 93 | 94 | /* Our argp parser. */ 95 | static struct argp argp = {options, parse_opt, args_doc, doc}; 96 | 97 | int main(int argc, char *argv[]) { 98 | struct arguments arguments; 99 | arguments.version = 0; 100 | arguments.output = NULL; 101 | arguments.input = NULL; 102 | arguments.scaling = NULL; 103 | argp_parse(&argp, argc, argv, 0, 0, &arguments); 104 | 105 | double scaling; 106 | 107 | if (arguments.version) { 108 | std::cout << "vsd2svg version: " << V2S_VERSION << "\n"; 109 | return 0; 110 | } 111 | 112 | if (arguments.scaling == NULL) { 113 | scaling = 1.0; 114 | } else { 115 | scaling = atof(arguments.scaling); 116 | } 117 | 118 | if (arguments.input == NULL) { 119 | std::cerr << "[ERROR] " 120 | << "Missing --input=FILE argument\n"; 121 | return 1; 122 | } 123 | 124 | if (arguments.output == NULL) { 125 | std::cerr << "[ERROR] " 126 | << "Missing --output=DIR argument\n"; 127 | return 1; 128 | } 129 | 130 | std::ifstream stin(arguments.input); 131 | if (!stin.is_open()) { 132 | std::cerr << "[ERROR] " 133 | << "Impossible to open input file '" << arguments.input 134 | << "'\n"; 135 | return 1; 136 | } 137 | int ret = 0; 138 | std::string in((std::istreambuf_iterator(stin)), 139 | std::istreambuf_iterator()); 140 | 141 | visio2svg::Visio2Svg converter; 142 | std::unordered_map out; 143 | 144 | ret = converter.vsd2svg(in, out, scaling); 145 | 146 | std::string outputdir(arguments.output); 147 | mkdir(arguments.output, S_IRWXU); 148 | 149 | for (const auto &rule_pair : out) { 150 | ofstream myfile; 151 | #ifdef SAFE_FILENAME 152 | std::regex e("[^A-Za-z0-9-]"); 153 | std::basic_string newfilename = 154 | outputdir + "/" + std::regex_replace(rule_pair.first, e, "_") + 155 | ".svg"; 156 | #else 157 | std::basic_string newfilename = 158 | outputdir + rule_pair.first + ".svg"; 159 | #endif 160 | myfile.open(newfilename); 161 | myfile << rule_pair.second << std::endl; 162 | myfile.close(); 163 | if (!myfile) { 164 | std::cerr << "[ERROR] " 165 | << "Failed to write file '" << rule_pair.first << "'\n"; 166 | ret = 1; 167 | } 168 | } 169 | 170 | return ret; 171 | } 172 | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ 173 | -------------------------------------------------------------------------------- /src/conv/vss2svg.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | 3 | /* vss2svg 4 | * work based on vss2xhtml from libvisio 5 | */ 6 | 7 | // <<<<<<<<<<<<<<<<<<< START ORIGINAL HEADER >>>>>>>>>>>>>>>>>>>>>>>>>>> 8 | 9 | /* 10 | * This file is part of the libvisio project. 11 | * 12 | * This Source Code Form is subject to the terms of the Mozilla Public 13 | * License, v. 2.0. If a copy of the MPL was not distributed with this 14 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 15 | */ 16 | 17 | // <<<<<<<<<<<<<<<<<<< END ORIGINAL HEADER >>>>>>>>>>>>>>>>>>>>>>>>>>> 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #ifdef SAFE_FILENAME 24 | #include 25 | #endif 26 | #include "visio2svg/Visio2Svg.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | const char *argp_program_version = V2S_VERSION; 36 | 37 | const char *argp_program_bug_address = ""; 38 | 39 | static char doc[] = "vss2svg -- Visio stencil to SVG converter"; 40 | 41 | static struct argp_option options[] = { 42 | {"input", 'i', "FILE", 0, "Input Visio .vss file"}, 43 | {"output", 'o', "DIR", 0, "Output directory"}, 44 | {"scaling", 's', "FLOAT", 0, "Scaling factor"}, 45 | {"version", 'V', 0, 0, "Print vss2svg version"}, 46 | {0}}; 47 | 48 | /* A description of the arguments we accept. */ 49 | static char args_doc[] = "[options] -i -o "; 50 | 51 | struct arguments { 52 | char *args[2]; /* arg1 & arg2 */ 53 | bool version; 54 | char *output; 55 | char *scaling; 56 | char *input; 57 | }; 58 | 59 | static error_t parse_opt(int key, char *arg, struct argp_state *state) { 60 | /* Get the input argument from argp_parse, which we 61 | know is a pointer to our arguments structure. */ 62 | struct arguments *arguments = (struct arguments *)state->input; 63 | 64 | switch (key) { 65 | case 'o': 66 | arguments->output = arg; 67 | break; 68 | case 'i': 69 | arguments->input = arg; 70 | break; 71 | case 's': 72 | arguments->scaling = arg; 73 | break; 74 | case 'V': 75 | arguments->version = 1; 76 | break; 77 | case ARGP_KEY_ARG: 78 | if (state->arg_num >= 6) 79 | /* Too many arguments. */ 80 | argp_usage(state); 81 | 82 | arguments->args[state->arg_num] = arg; 83 | 84 | break; 85 | 86 | case ARGP_KEY_END: 87 | break; 88 | default: 89 | return ARGP_ERR_UNKNOWN; 90 | } 91 | return 0; 92 | } 93 | 94 | /* Our argp parser. */ 95 | static struct argp argp = {options, parse_opt, args_doc, doc}; 96 | 97 | int main(int argc, char *argv[]) { 98 | struct arguments arguments; 99 | arguments.version = 0; 100 | arguments.output = NULL; 101 | arguments.input = NULL; 102 | arguments.scaling = NULL; 103 | argp_parse(&argp, argc, argv, 0, 0, &arguments); 104 | 105 | double scaling; 106 | 107 | if (arguments.version) { 108 | std::cout << "vss2svg version: " << V2S_VERSION << "\n"; 109 | return 0; 110 | } 111 | 112 | if (arguments.scaling == NULL) { 113 | scaling = 1.0; 114 | } else { 115 | scaling = atof(arguments.scaling); 116 | } 117 | 118 | if (arguments.input == NULL) { 119 | std::cerr << "[ERROR] " 120 | << "Missing --input=FILE argument\n"; 121 | return 1; 122 | } 123 | 124 | if (arguments.output == NULL) { 125 | std::cerr << "[ERROR] " 126 | << "Missing --output=DIR argument\n"; 127 | return 1; 128 | } 129 | 130 | std::ifstream stin(arguments.input); 131 | if (!stin.is_open()) { 132 | std::cerr << "[ERROR] " 133 | << "Impossible to open input file '" << arguments.input 134 | << "'\n"; 135 | return 1; 136 | } 137 | int ret = 0; 138 | std::string in((std::istreambuf_iterator(stin)), 139 | std::istreambuf_iterator()); 140 | 141 | visio2svg::Visio2Svg converter; 142 | std::unordered_map out; 143 | 144 | ret = converter.vss2svg(in, out, scaling); 145 | 146 | std::string outputdir(arguments.output); 147 | mkdir(arguments.output, S_IRWXU); 148 | 149 | for (const auto &rule_pair : out) { 150 | ofstream myfile; 151 | #ifdef SAFE_FILENAME 152 | std::regex e("[^A-Za-z0-9-]"); 153 | std::basic_string newfilename = 154 | outputdir + "/" + std::regex_replace(rule_pair.first, e, "_") + 155 | ".svg"; 156 | #else 157 | std::basic_string newfilename = 158 | outputdir + rule_pair.first + ".svg"; 159 | #endif 160 | myfile.open(newfilename); 161 | myfile << rule_pair.second << std::endl; 162 | myfile.close(); 163 | if (!myfile) { 164 | std::cerr << "[ERROR] " 165 | << "Failed to write file '" << rule_pair.first << "'\n"; 166 | ret = 1; 167 | } 168 | } 169 | 170 | return ret; 171 | } 172 | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ 173 | -------------------------------------------------------------------------------- /src/lib/visio2svg/TitleGenerator.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "visio2svg/TitleGenerator.h" 12 | #include 13 | #include 14 | 15 | namespace visio2svg { 16 | 17 | void TitleGenerator::startPage(const librevenge::RVNGPropertyList &propList) { 18 | if (propList["draw:name"]) { 19 | titles.append(propList["draw:name"]->getStr().cstr()); 20 | } else { 21 | titles.append(NULL); 22 | } 23 | } 24 | void TitleGenerator::startDocument( 25 | const librevenge::RVNGPropertyList &propList){}; 26 | void TitleGenerator::endDocument(){}; 27 | void TitleGenerator::setDocumentMetaData( 28 | const librevenge::RVNGPropertyList &propList){}; 29 | void TitleGenerator::defineEmbeddedFont( 30 | const librevenge::RVNGPropertyList &propList){}; 31 | void TitleGenerator::endPage(){}; 32 | void TitleGenerator::startMasterPage( 33 | const librevenge::RVNGPropertyList &propList){}; 34 | void TitleGenerator::endMasterPage(){}; 35 | void TitleGenerator::startLayer(const librevenge::RVNGPropertyList &propList){}; 36 | void TitleGenerator::endLayer(){}; 37 | void TitleGenerator::startEmbeddedGraphics( 38 | const librevenge::RVNGPropertyList &propList){}; 39 | void TitleGenerator::endEmbeddedGraphics(){}; 40 | 41 | void TitleGenerator::openGroup(const librevenge::RVNGPropertyList &propList){}; 42 | void TitleGenerator::closeGroup(){}; 43 | 44 | void TitleGenerator::setStyle(const librevenge::RVNGPropertyList &propList){}; 45 | 46 | void TitleGenerator::drawRectangle( 47 | const librevenge::RVNGPropertyList &propList){}; 48 | void TitleGenerator::drawEllipse( 49 | const librevenge::RVNGPropertyList &propList){}; 50 | void TitleGenerator::drawPolyline( 51 | const librevenge::RVNGPropertyList &propList){}; 52 | void TitleGenerator::drawPolygon( 53 | const librevenge::RVNGPropertyList &propList){}; 54 | void TitleGenerator::drawPath(const librevenge::RVNGPropertyList &propList){}; 55 | void TitleGenerator::drawGraphicObject( 56 | const librevenge::RVNGPropertyList &propList){}; 57 | void TitleGenerator::drawConnector( 58 | const librevenge::RVNGPropertyList &propList){}; 59 | void TitleGenerator::startTextObject( 60 | const librevenge::RVNGPropertyList &propList){}; 61 | void TitleGenerator::endTextObject(){}; 62 | 63 | void TitleGenerator::startTableObject( 64 | const librevenge::RVNGPropertyList &propList){}; 65 | void TitleGenerator::openTableRow( 66 | const librevenge::RVNGPropertyList &propList){}; 67 | void TitleGenerator::closeTableRow(){}; 68 | void TitleGenerator::openTableCell( 69 | const librevenge::RVNGPropertyList &propList){}; 70 | void TitleGenerator::closeTableCell(){}; 71 | void TitleGenerator::insertCoveredTableCell( 72 | const librevenge::RVNGPropertyList &propList){}; 73 | void TitleGenerator::endTableObject(){}; 74 | 75 | void TitleGenerator::openOrderedListLevel( 76 | const librevenge::RVNGPropertyList &propList){}; 77 | void TitleGenerator::closeOrderedListLevel(){}; 78 | 79 | void TitleGenerator::openUnorderedListLevel( 80 | const librevenge::RVNGPropertyList &propList){}; 81 | void TitleGenerator::closeUnorderedListLevel(){}; 82 | void TitleGenerator::openListElement( 83 | const librevenge::RVNGPropertyList &propList){}; 84 | void TitleGenerator::closeListElement(){}; 85 | 86 | void TitleGenerator::defineParagraphStyle( 87 | const librevenge::RVNGPropertyList &propList){}; 88 | void TitleGenerator::openParagraph( 89 | const librevenge::RVNGPropertyList &propList){}; 90 | void TitleGenerator::closeParagraph(){}; 91 | 92 | void TitleGenerator::defineCharacterStyle( 93 | const librevenge::RVNGPropertyList &propList){}; 94 | void TitleGenerator::openSpan(const librevenge::RVNGPropertyList &propList){}; 95 | void TitleGenerator::closeSpan(){}; 96 | 97 | void TitleGenerator::openLink(const librevenge::RVNGPropertyList &propList){}; 98 | void TitleGenerator::closeLink(){}; 99 | 100 | void TitleGenerator::insertTab(){}; 101 | void TitleGenerator::insertSpace(){}; 102 | void TitleGenerator::insertText(const librevenge::RVNGString &text){}; 103 | void TitleGenerator::insertLineBreak(){}; 104 | void TitleGenerator::insertField( 105 | const librevenge::RVNGPropertyList &propList){}; 106 | 107 | TitleGenerator::TitleGenerator(librevenge::RVNGStringVector &titles) 108 | : titles(titles) { 109 | } 110 | 111 | TitleGenerator::~TitleGenerator() { 112 | } 113 | } 114 | 115 | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ 116 | -------------------------------------------------------------------------------- /src/lib/visio2svg/Visio2Svg.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ 2 | 3 | #include "visio2svg/Visio2Svg.h" 4 | #include "visio2svg/TitleGenerator.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef DARWIN 26 | #include "memstream.c" 27 | #endif 28 | 29 | #define VISIOVSS 1 30 | #define VISIOVSD 2 31 | 32 | #define WHITESPACE 64 33 | #define EQUALS 65 34 | #define INVALID 66 35 | #define WMF2SVG_MAXPECT (1 << 0) 36 | 37 | namespace visio2svg { 38 | 39 | static void or_wmf_svg_device_begin(wmfAPI *API) { 40 | wmf_svg_t *ddata = WMF_SVG_GetData(API); 41 | 42 | wmfStream *out = ddata->out; 43 | 44 | if (out == 0) 45 | return; 46 | 47 | if ((out->reset(out->context)) && 48 | ((API->flags & WMF_OPT_IGNORE_NONFATAL) == 0)) { 49 | API->err = wmf_E_DeviceError; 50 | return; 51 | } 52 | 53 | if ((ddata->bbox.BR.x <= ddata->bbox.TL.x) || 54 | (ddata->bbox.BR.y <= ddata->bbox.TL.y)) { 55 | API->err = wmf_E_Glitch; 56 | return; 57 | } 58 | 59 | if ((ddata->width == 0) || (ddata->height == 0)) { 60 | ddata->width = (unsigned int)ceil(ddata->bbox.BR.x - ddata->bbox.TL.x); 61 | ddata->height = (unsigned int)ceil(ddata->bbox.BR.y - ddata->bbox.TL.y); 62 | } 63 | 64 | wmf_stream_printf(API, out, (char *)"\n"); 65 | 66 | if (ddata->Description) { 67 | wmf_stream_printf(API, out, (char *)"%s\n", 68 | ddata->Description); 69 | } 70 | } 71 | 72 | /* This is called from the end of each play for page termination 73 | */ 74 | static void or_wmf_svg_device_end(wmfAPI *API) { 75 | wmf_svg_t *ddata = WMF_SVG_GetData(API); 76 | 77 | wmfStream *out = ddata->out; 78 | wmf_stream_printf(API, out, (char *)"\n"); 79 | 80 | WMF_DEBUG(API, "~~~~~~~~wmf_[svg_]device_end"); 81 | 82 | if (out == 0) 83 | return; 84 | } 85 | 86 | Visio2Svg::Visio2Svg() { 87 | } 88 | 89 | Visio2Svg::~Visio2Svg() { 90 | } 91 | 92 | typedef struct _ImageContext ImageContext; 93 | 94 | struct _ImageContext { 95 | int number; 96 | char *prefix; 97 | }; 98 | 99 | int Visio2Svg::vss2svg(std::string &in, 100 | std::unordered_map &out, 101 | double scaling) { 102 | return visio2svg(in, out, scaling, VISIOVSS); 103 | } 104 | 105 | int Visio2Svg::vss2svg(std::string &in, 106 | std::unordered_map &out) { 107 | return visio2svg(in, out, 1.0, VISIOVSS); 108 | } 109 | 110 | int Visio2Svg::vsd2svg(std::string &in, 111 | std::unordered_map &out) { 112 | return visio2svg(in, out, 1.0, VISIOVSD); 113 | } 114 | 115 | int Visio2Svg::vsd2svg(std::string &in, 116 | std::unordered_map &out, 117 | double scaling) { 118 | return visio2svg(in, out, scaling, VISIOVSD); 119 | } 120 | 121 | int explicit_wmf_error(char const *str, wmf_error_t err) { 122 | int status = 0; 123 | 124 | switch (err) { 125 | case wmf_E_None: 126 | #ifdef DEBUG 127 | fprintf(stderr, "%s returned with wmf_E_None.\n", str); 128 | #endif 129 | status = 0; 130 | break; 131 | 132 | case wmf_E_InsMem: 133 | #ifdef DEBUG 134 | fprintf(stderr, "%s returned with wmf_E_InsMem.\n", str); 135 | #endif 136 | status = 1; 137 | break; 138 | 139 | case wmf_E_BadFile: 140 | #ifdef DEBUG 141 | fprintf(stderr, "%s returned with wmf_E_BadFile.\n", str); 142 | #endif 143 | status = 1; 144 | break; 145 | 146 | case wmf_E_BadFormat: 147 | #ifdef DEBUG 148 | fprintf(stderr, "%s returned with wmf_E_BadFormat.\n", str); 149 | #endif 150 | status = 1; 151 | break; 152 | 153 | case wmf_E_EOF: 154 | #ifdef DEBUG 155 | fprintf(stderr, "%s returned with wmf_E_EOF.\n", str); 156 | #endif 157 | status = 1; 158 | break; 159 | 160 | case wmf_E_DeviceError: 161 | #ifdef DEBUG 162 | fprintf(stderr, "%s returned with wmf_E_DeviceError.\n", str); 163 | #endif 164 | status = 1; 165 | break; 166 | 167 | case wmf_E_Glitch: 168 | #ifdef DEBUG 169 | fprintf(stderr, "%s returned with wmf_E_Glitch.\n", str); 170 | #endif 171 | status = 1; 172 | 173 | case wmf_E_Assert: 174 | #ifdef DEBUG 175 | fprintf(stderr, "%s returned with wmf_E_Assert.\n", str); 176 | #endif 177 | status = 1; 178 | break; 179 | 180 | default: 181 | #ifdef DEBUG 182 | fprintf(stderr, "%s returned unexpected value.\n", str); 183 | #endif 184 | status = 1; 185 | break; 186 | } 187 | 188 | return (status); 189 | } 190 | 191 | int wmf2svg_draw(char *content, size_t size, float wmf_width, float wmf_height, 192 | char **out, size_t *out_length) { 193 | int status = 0; 194 | 195 | unsigned long flags; 196 | 197 | FILE *out_f; 198 | out_f = open_memstream(out, out_length); 199 | 200 | ImageContext IC; 201 | 202 | wmf_error_t err; 203 | 204 | wmf_svg_t *ddata = 0; 205 | 206 | wmfAPI *API = 0; 207 | wmfD_Rect bbox; 208 | 209 | wmfAPI_Options api_options; 210 | 211 | flags = 0; 212 | 213 | flags |= WMF_OPT_FUNCTION; 214 | flags |= WMF_OPT_IGNORE_NONFATAL; 215 | 216 | api_options.function = wmf_svg_function; 217 | 218 | err = wmf_api_create(&API, flags, &api_options); 219 | status = explicit_wmf_error("wmf_api", err); 220 | 221 | wmfFunctionReference *FR = (wmfFunctionReference *)API->function_reference; 222 | 223 | FR->device_begin = or_wmf_svg_device_begin; 224 | FR->device_end = or_wmf_svg_device_end; 225 | 226 | if (status) { 227 | if (API) 228 | wmf_api_destroy(API); 229 | return (status); 230 | } 231 | 232 | err = wmf_mem_open(API, (unsigned char *)content, (long)size); 233 | status = explicit_wmf_error("open", err); 234 | 235 | if (status) { 236 | wmf_api_destroy(API); 237 | return (status); 238 | } 239 | 240 | err = wmf_scan(API, 0, &bbox); 241 | status = explicit_wmf_error("scan", err); 242 | 243 | if (status) { 244 | wmf_api_destroy(API); 245 | return (status); 246 | } 247 | 248 | ddata = WMF_SVG_GetData(API); 249 | 250 | float width; 251 | float height; 252 | wmf_size(API, &width, &height); 253 | 254 | if ((width <= 0) || (height <= 0)) { 255 | #ifdef DEBUG 256 | fprintf(stderr, "Bad image size - but this error shouldn't occur...\n"); 257 | #endif 258 | status = 1; 259 | wmf_api_destroy(API); 260 | return (status); 261 | } 262 | 263 | // ddata->type = wmf_gd_jpeg; 264 | 265 | // ddata->flags |= WMF_SVG_OUTPUT_FILE; 266 | ddata->out = wmf_stream_create(API, out_f); 267 | 268 | ddata->bbox = bbox; 269 | ddata->width = wmf_width; 270 | ddata->height = wmf_height; 271 | ddata->flags |= WMF_SVG_INLINE_IMAGES; 272 | 273 | wmfD_Rect d_r; 274 | if (status == 0) { 275 | err = wmf_play(API, 0, &d_r); 276 | status = explicit_wmf_error("play", err); 277 | } 278 | 279 | fclose(out_f); 280 | wmf_api_destroy(API); 281 | 282 | #ifdef DEBUG 283 | printf("%d, %d\n", err, status); 284 | #endif 285 | return (status); 286 | } 287 | 288 | int Visio2Svg::visio2svg(std::string &in, 289 | std::unordered_map &out, 290 | double scaling, int mode) { 291 | librevenge::RVNGStringStream input((const unsigned char *)in.c_str(), 292 | in.size()); 293 | 294 | // check document type 295 | if (!libvisio::VisioDocument::isSupported(&input)) { 296 | #ifdef DEBUG 297 | std::cerr << "ERROR: Unsupported file format (unsupported version) or " 298 | "file is encrypted!" 299 | << std::endl; 300 | #endif 301 | return 1; 302 | } 303 | 304 | int ret = 0; 305 | 306 | // Recover Titles of each sheets 307 | librevenge::RVNGStringVector output_names; 308 | visio2svg::TitleGenerator generator_names(output_names); 309 | if (mode == VISIOVSS) { 310 | ret = libvisio::VisioDocument::parseStencils(&input, &generator_names); 311 | } else { 312 | ret = libvisio::VisioDocument::parse(&input, &generator_names); 313 | } 314 | 315 | if (!ret || output_names.empty()) { 316 | #ifdef DEBUG 317 | std::cerr << "ERROR: Failed to recover sheets titles failed!" 318 | << std::endl; 319 | #endif 320 | return 1; 321 | } 322 | 323 | // Convert vss/vsd to SVG 324 | librevenge::RVNGStringVector output; 325 | librevenge::RVNGSVGDrawingGenerator generator(output, NULL); 326 | if (mode == VISIOVSS) { 327 | ret = libvisio::VisioDocument::parseStencils(&input, &generator); 328 | } else { 329 | ret = libvisio::VisioDocument::parse(&input, &generator); 330 | } 331 | if (!ret || output.empty()) { 332 | #ifdef DEBUG 333 | std::cerr << "ERROR: SVG Generation failed!" << std::endl; 334 | #endif 335 | return 1; 336 | } 337 | ret = 0; 338 | 339 | // Post Treatment loop and construction of the output hash table 340 | for (unsigned k = 0; k < output.size(); ++k) { 341 | char *post_treated; 342 | // Convert tag containing emf blobs 343 | // and resize image 344 | ret |= postTreatement(&output[k], &output_names[k], &post_treated, 345 | scaling); 346 | std::string title; 347 | if (output_names[k].empty()) { 348 | title = "no_title_" + std::to_string(k); 349 | } else { 350 | title = output_names[k].cstr(); 351 | } 352 | std::pair item(title, 353 | std::string(post_treated)); 354 | // output[k].cstr()); 355 | free(post_treated); 356 | out.insert(item); 357 | } 358 | return ret; 359 | } 360 | 361 | // base64 decoder 362 | // just copy paste from 363 | // https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64 364 | static const unsigned char d[] = { 365 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 366 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 367 | 66, 66, 66, 66, 66, 62, 66, 66, 66, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 368 | 61, 66, 66, 66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 369 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 66, 66, 66, 66, 370 | 66, 66, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 371 | 43, 44, 45, 46, 47, 48, 49, 50, 51, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 372 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 373 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 374 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 375 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 376 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 377 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 378 | 66, 66, 66, 66, 66, 66, 66, 66, 66}; 379 | int base64decode(char *in, size_t inLen, unsigned char *out, size_t *outLen) { 380 | char *end = in + inLen; 381 | char iter = 0; 382 | size_t buf = 0, len = 0; 383 | 384 | while (in < end) { 385 | unsigned char c = d[*in++]; 386 | 387 | switch (c) { 388 | case WHITESPACE: 389 | continue; /* skip whitespace */ 390 | case INVALID: 391 | return 1; /* invalid input, return error */ 392 | case EQUALS: /* pad character, end of data */ 393 | in = end; 394 | continue; 395 | default: 396 | buf = buf << 6 | c; 397 | iter++; // increment the number of iteration 398 | /* If the buffer is full, split it into bytes */ 399 | if (iter == 4) { 400 | if ((len += 3) > *outLen) 401 | return 1; /* buffer overflow */ 402 | *(out++) = (buf >> 16) & 255; 403 | *(out++) = (buf >> 8) & 255; 404 | *(out++) = buf & 255; 405 | buf = 0; 406 | iter = 0; 407 | } 408 | } 409 | } 410 | 411 | if (iter == 3) { 412 | if ((len += 2) > *outLen) 413 | return 1; /* buffer overflow */ 414 | *(out++) = (buf >> 10) & 255; 415 | *(out++) = (buf >> 2) & 255; 416 | } else if (iter == 2) { 417 | if (++len > *outLen) 418 | return 1; /* buffer overflow */ 419 | *(out++) = (buf >> 4) & 255; 420 | } 421 | 422 | *outLen = len; /* modify to reflect the actual output size */ 423 | return 0; 424 | } 425 | 426 | // Recursive convert. 427 | // Parse each node of the svg generated by librevenge/libvisio. 428 | // If it encounters an node, it checks if it's an emf blob 429 | // and replace it with the result of emf2svg put inside a node 430 | // with proper translate() to get proper position. 431 | int convert_iterator(xmlNode *a_node, xmlDocPtr root_doc) { 432 | xmlNode *cur_node = NULL; 433 | xmlNode *next_node; 434 | int ret = 0; 435 | 436 | for (cur_node = a_node; cur_node;) { 437 | next_node = cur_node->next; 438 | 439 | // image node specific treatment 440 | if ((!xmlStrcmp(cur_node->name, (const xmlChar *)"image"))) { 441 | xmlAttr *attribute = cur_node->properties; 442 | double x = 0; 443 | double y = 0; 444 | double width = 0; 445 | double height = 0; 446 | xmlChar *imgb64 = NULL; 447 | // recover some attributes needed for the conversion and the base64 448 | // encoded image. 449 | while (attribute) { 450 | xmlChar *value = 451 | xmlNodeListGetString(cur_node->doc, attribute->children, 1); 452 | if ((!xmlStrcmp(attribute->name, (const xmlChar *)"x"))) { 453 | x = atof((const char *)value); 454 | } 455 | if ((!xmlStrcmp(attribute->name, (const xmlChar *)"y"))) { 456 | y = atof((const char *)value); 457 | } 458 | if ((!xmlStrcmp(attribute->name, (const xmlChar *)"width"))) { 459 | width = atof((const char *)value); 460 | } 461 | if ((!xmlStrcmp(attribute->name, (const xmlChar *)"height"))) { 462 | height = atof((const char *)value); 463 | } 464 | if ((!xmlStrcmp(attribute->name, (const xmlChar *)"href"))) { 465 | imgb64 = value; 466 | attribute = attribute->next; 467 | continue; 468 | } 469 | attribute = attribute->next; 470 | xmlFree(value); 471 | } 472 | 473 | // detect the image type 474 | // right now, we handle emf and wmf 475 | // if image_type stays equal to UNKNOWN_IMGTYPE, it's not a type we 476 | // handle 477 | IMG_TYPE image_type = UNKNOWN_IMGTYPE; 478 | if (imgb64 != NULL) { 479 | if ((!xmlStrncmp(imgb64, 480 | (const xmlChar *)"data:image/emf;base64,", 481 | 22))) { 482 | image_type = EMF_IMGTYPE; 483 | } else if ((!xmlStrncmp( 484 | imgb64, 485 | (const xmlChar *)"data:image/wmf;base64,", 486 | 22))) { 487 | image_type = WMF_IMGTYPE; 488 | } 489 | } 490 | 491 | // if the image is something we handle, convert it to SVG 492 | // else, just free the imgb64 and keep the node 493 | if (image_type != UNKNOWN_IMGTYPE) { 494 | xmlAttr *attribute = cur_node->properties; 495 | // create an svg group node (emf conversion will be put in it) 496 | xmlNode *node = xmlNewNode(NULL, (const xmlChar *)"g"); 497 | 498 | // prepare translate to position emf conversion 499 | // (inside transform attribute). 500 | size_t tlen = 501 | (size_t)snprintf(NULL, 0, " translate(%f,%f) ", x, y); 502 | char *translate = (char *)malloc(tlen); 503 | tlen = snprintf(translate, tlen, " translate(%f,%f) ", x, y); 504 | bool translate_set = false; 505 | 506 | // copy all attributes of the image node in the group node 507 | // except a few 508 | while (attribute) { 509 | if (xmlStrcmp(attribute->name, (const xmlChar *)"href") && 510 | xmlStrcmp(attribute->name, (const xmlChar *)"x") && 511 | xmlStrcmp(attribute->name, (const xmlChar *)"y") && 512 | xmlStrcmp(attribute->name, (const xmlChar *)"width") && 513 | xmlStrcmp(attribute->name, (const xmlChar *)"height")) { 514 | xmlChar *value = xmlNodeListGetString( 515 | cur_node->doc, attribute->children, 1); 516 | // special treatement for transform, must append the 517 | // translate 518 | // previously prepared 519 | if ((!xmlStrcmp(attribute->name, 520 | (const xmlChar *)"transform"))) { 521 | translate_set = true; 522 | value = xmlStrncat( 523 | value, (const xmlChar *)translate, tlen); 524 | } 525 | xmlNewProp(node, attribute->name, value); 526 | } 527 | attribute = attribute->next; 528 | } 529 | // if there was no "transform" attribute, add it with our 530 | // translate 531 | if (!(translate_set)) { 532 | xmlNewProp(node, (const xmlChar *)"transform", 533 | (const xmlChar *)translate); 534 | } 535 | 536 | // recover content (base64 encoded) 537 | char *svg_out = NULL; 538 | size_t len_out = 0; 539 | size_t len = strlen((char const *)imgb64); 540 | size_t size = len; //(len * 3 / 4 + 4); 541 | unsigned char *content = (unsigned char *)calloc(size, 1); 542 | int b64e = base64decode((char *)(imgb64 + 22), (len - 22), 543 | content, &size); 544 | ret |= b64e; 545 | xmlFree(imgb64); 546 | imgb64 = NULL; 547 | int e2se; 548 | xmlNodePtr s_sta; 549 | xmlNodePtr s_end; 550 | if (b64e) { 551 | #ifdef DEBUG 552 | std::cerr << "ERROR: Base64 decode failed" << std::endl; 553 | #endif 554 | ret = 1; 555 | } 556 | 557 | switch (image_type) { 558 | case EMF_IMGTYPE: { 559 | // configure generator options 560 | generatorOptions *options = 561 | (generatorOptions *)calloc(1, sizeof(generatorOptions)); 562 | options->verbose = false; 563 | options->emfplus = true; 564 | // options->nameSpace = (char *)"svg"; 565 | options->nameSpace = NULL; 566 | options->svgDelimiter = false; 567 | options->imgWidth = width; 568 | options->imgHeight = height; 569 | 570 | // convert emf 571 | e2se = emf2svg((char *)content, size, &svg_out, &len_out, 572 | options); 573 | if (!e2se) { 574 | #ifdef DEBUG 575 | std::cerr << "ERROR: Failed to convert emf blob" 576 | << std::endl; 577 | #endif 578 | ret = 1; 579 | } 580 | free(options); 581 | s_sta = xmlNewDocComment( 582 | root_doc, (const unsigned char *)"emf-blob start"); 583 | s_end = xmlNewDocComment( 584 | root_doc, (const unsigned char *)"emf-blob end"); 585 | break; 586 | } 587 | case WMF_IMGTYPE: { 588 | e2se = wmf2svg_draw((char *)content, size, width, height, 589 | &svg_out, &len_out); 590 | s_sta = xmlNewDocComment( 591 | root_doc, (const unsigned char *)"wmf-blob start"); 592 | s_end = xmlNewDocComment( 593 | root_doc, (const unsigned char *)"wmf-blob end"); 594 | if (e2se) { 595 | #ifdef DEBUG 596 | std::cerr << "ERROR: Failed to convert wmf blob" 597 | << std::endl; 598 | #endif 599 | ret = 1; 600 | } 601 | break; 602 | } 603 | default: { 604 | #ifdef DEBUG 605 | std::cerr << "ERROR: Unknown image type" << std::endl; 606 | #endif 607 | ret = 1; 608 | break; 609 | } 610 | } 611 | 612 | xmlDocPtr doc; 613 | xmlNode *root_element = NULL; 614 | 615 | // parse svg generated by emf2svg or wmf2svg with libxml2 616 | doc = xmlReadMemory(svg_out, len_out, NULL, NULL, 617 | XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | 618 | XML_PARSE_NONET | XML_PARSE_NOERROR | 619 | XML_PARSE_HUGE); 620 | root_element = xmlDocGetRootElement(doc); 621 | 622 | xmlAddChild(node, s_sta); 623 | 624 | // insert new nodes 625 | xmlNode *blob_svg = xmlCopyNodeList(root_element); 626 | xmlAddChildList(node, blob_svg); 627 | xmlAddChildList(cur_node->parent, node); 628 | xmlAddChild(node, s_end); 629 | 630 | // remove image node 631 | xmlUnlinkNode(cur_node); 632 | 633 | // freeing some memory 634 | xmlFreeNode(cur_node); 635 | free(content); 636 | free(svg_out); 637 | free(translate); 638 | xmlFreeDoc(doc); 639 | } else { 640 | ret |= convert_iterator(cur_node->children, root_doc); 641 | } 642 | free(imgb64); 643 | } else { 644 | ret |= convert_iterator(cur_node->children, root_doc); 645 | } 646 | cur_node = next_node; 647 | } 648 | return ret; 649 | } 650 | 651 | int Visio2Svg::scale_title(xmlNode **root, xmlDocPtr *doc, double scaling, 652 | const xmlChar *title, int title_len) { 653 | int ret = 0; 654 | // create a group with transform="scale()" attribute 655 | xmlNode *node = xmlNewNode(NULL, (const xmlChar *)"g"); 656 | size_t tlen = (size_t)snprintf(NULL, 0, " scale(%f) ", scaling); 657 | char *translate = (char *)malloc(tlen); 658 | tlen = snprintf(translate, tlen, " scale(%f) ", scaling); 659 | xmlNewProp(node, (const xmlChar *)"transform", (const xmlChar *)translate); 660 | 661 | xmlNode *new_root = xmlNewNode(NULL, (const xmlChar *)"svg"); 662 | xmlAttr *attribute = (*root)->properties; 663 | double width = 0; 664 | double height = 0; 665 | while (attribute) { 666 | xmlChar *value = xmlNodeListGetString(*doc, attribute->children, 1); 667 | if ((!xmlStrcmp(attribute->name, (const xmlChar *)"width")) || 668 | (!xmlStrcmp(attribute->name, (const xmlChar *)"height"))) { 669 | double geom = atof((const char *)value); 670 | char *cgeom = (char *)calloc(1, 50); 671 | snprintf(cgeom, 50, "%.10f", geom * scaling); 672 | xmlNewProp(new_root, attribute->name, (const xmlChar *)cgeom); 673 | free(cgeom); 674 | } else { 675 | xmlNewProp(new_root, attribute->name, value); 676 | } 677 | attribute = attribute->next; 678 | xmlFree(value); 679 | } 680 | 681 | xmlNsPtr ns = (*root)->nsDef; 682 | while (ns) { 683 | xmlNewNs(new_root, ns->href, ns->prefix); 684 | ns = ns->next; 685 | } 686 | 687 | xmlDocPtr new_doc = xmlCopyDoc(*doc, 0); 688 | xmlNodePtr title_cdata = xmlNewCDataBlock(new_doc, title, title_len); 689 | xmlNodePtr title_node = 690 | xmlNewChild(new_root, NULL, (const xmlChar *)"title", NULL); 691 | xmlAddChild(title_node, title_cdata); 692 | xmlDocSetRootElement(new_doc, new_root); 693 | xmlNode *children = xmlCopyNodeList((*root)->children); 694 | xmlAddChildList(new_root, node); 695 | xmlAddChildList(node, children); 696 | xmlFreeDoc(*doc); 697 | *doc = new_doc; 698 | *root = new_root; 699 | free(translate); 700 | return ret; 701 | } 702 | 703 | int Visio2Svg::postTreatement(const librevenge::RVNGString *in, 704 | const librevenge::RVNGString *name, char **out, 705 | double scaling) { 706 | xmlDocPtr doc; 707 | xmlNode *root_element = NULL; 708 | int ret = 0; 709 | // parse svg/xml generated by librevenge/libvisio with libxml2 710 | #ifdef DEBUG 711 | std::cerr << "Converting: " << name->cstr() << std::endl; 712 | // std::cerr << "Converting: " << in->cstr() << std::endl; 713 | #endif 714 | doc = xmlReadMemory(in->cstr(), in->size(), name->cstr(), NULL, 715 | XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | 716 | XML_PARSE_NONET | XML_PARSE_HUGE); 717 | root_element = xmlDocGetRootElement(doc); 718 | xmlNodePtr comment = xmlNewDocComment( 719 | doc, (const unsigned char *)"converted by libvisio2svg"); 720 | xmlAddChild(root_element, comment); 721 | // convert blobs (wmf, emf, ...) 722 | ret |= convert_iterator(root_element, doc); 723 | scale_title(&root_element, &doc, scaling, (const xmlChar *)name->cstr(), 724 | name->size()); 725 | 726 | xmlBufferPtr nodeBuffer = xmlBufferCreate(); 727 | xmlNodeDump(nodeBuffer, doc, root_element, 0, 1); 728 | // Dump the generated svg to out 729 | *out = (char *)nodeBuffer->content; 730 | // free some memory 731 | xmlFreeDoc(doc); 732 | nodeBuffer->content = NULL; 733 | xmlBufferFree(nodeBuffer); 734 | xmlCleanupParser(); 735 | return ret; 736 | } 737 | } 738 | 739 | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ 740 | -------------------------------------------------------------------------------- /src/lib/visio2svg/memstream.c: -------------------------------------------------------------------------------- 1 | ../../../deps/memstream/memstream.c -------------------------------------------------------------------------------- /tests/resources/check_correctness.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "`uname`" = "Darwin" ] 4 | then 5 | RL=greadlink 6 | else 7 | RL=readlink 8 | fi 9 | 10 | OUTDIR="../out" 11 | EMFDIR="./vss" 12 | ret=0 13 | VAGRIND_CMD="valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes --error-exitcode=42" 14 | 15 | VERBOSE=1 16 | ABSPATH=$($RL -f "$(dirname $0)") 17 | STOPONERROR="no" 18 | 19 | help(){ 20 | cat <] [-s] [-n] 22 | 23 | Script checking memleaks, segfault and svg correctness of vss2svg-conv 24 | 25 | arguments: 26 | -h: diplays this help 27 | -v: verbose mode, print vss records 28 | -e: alternate vss dir (default '`$RL -f "$ABSPATH/$EMFDIR"`') 29 | -s: stop on first error 30 | -r: resize to 800x600 31 | -x: disable xmllint check (svg integrity) 32 | -n: disable valgrind (memleaks checks) 33 | -N: ignore return code of vss2svg-conv (useful for checks on corrupted files) 34 | EOF 35 | exit 1 36 | } 37 | 38 | 39 | while getopts ":hnNxrvse:" opt; do 40 | case $opt in 41 | 42 | h) 43 | help 44 | ;; 45 | n) 46 | VAGRIND_CMD="" 47 | ;; 48 | v) 49 | VERBOSE=0 50 | VERBOSE_OPT='--verbose' 51 | ;; 52 | e) 53 | EMFDIR=`$RL -f "$OPTARG" |sed "s%$ABSPATH%.%"` 54 | ;; 55 | s) 56 | STOPONERROR="yes" 57 | ;; 58 | x) 59 | XMLLINT="no" 60 | ;; 61 | r) 62 | RESIZE_OPTS="-s 10" 63 | ;; 64 | N) 65 | IGNORECONVERR="yes" 66 | ;; 67 | \?) 68 | echo "Invalid option: -$OPTARG" >&2 69 | help 70 | exit 1 71 | ;; 72 | :) 73 | echo "Option -$OPTARG requires an argument." >&2 74 | help 75 | exit 1 76 | ;; 77 | esac 78 | done 79 | 80 | verbose_print(){ 81 | msg="$1" 82 | if [ $VERBOSE -eq 0 ] 83 | then 84 | printf "$msg\n" 85 | fi 86 | } 87 | 88 | cd $ABSPATH 89 | . ./colors.sh 90 | rm -rf $OUTDIR 91 | mkdir -p $OUTDIR 92 | CMD="`$RL -f ../../vss2svg-conv`" 93 | OUTDIR=`$RL -f $OUTDIR` 94 | DTD=`$RL -f ./svg11-flat.dtd` 95 | 96 | mkdir -p "$EMFDIR" 97 | cd "$EMFDIR" 98 | if ! [ -f HP-Classic-ProLiant.zip ] 99 | then 100 | wget http://www.visiocafe.com/downloads/hp/HP-Classic-ProLiant.zip 101 | unzip HP-Classic-ProLiant.zip 102 | fi 103 | cd - 104 | 105 | for vss in `find $EMFDIR -type f -name "*.vss" |sort` 106 | do 107 | EMF="`$RL -f $vss`" 108 | verbose_print "\n############## `basename "${vss}"` ####################" 109 | verbose_print "Command: $CMD $RESIZE_OPTS -i \"$EMF\" -o \"${OUTDIR}\"" 110 | $VAGRIND_CMD $CMD -i "$EMF" -o ${OUTDIR} $VERBOSE_OPT 111 | tmpret=$? 112 | if [ $tmpret -ne 0 ] 113 | then 114 | printf "[${BYel}ERROR${RCol}] vss2svg-conv exited on error or memleaked or crashed converting vss '$EMF'\n" 115 | [ $tmpret -eq 42 ] || [ $tmpret -eq 139 ] || ! [ "$IGNORECONVERR" = "yes" ] && ret=1 116 | fi 117 | if ! [ "$XMLLINT" = "no" ] 118 | then 119 | for SVG in `find ${OUTDIR} -type f -name "*.svg" |sort` 120 | do 121 | xmllint --dtdvalid ./svg11-flat.dtd --noout ${SVG} >/dev/null 2>&1 122 | if [ $? -ne 0 ] 123 | then 124 | printf "[${BYel}ERROR${RCol}] vss2svg-conv generate bad svg\n" 125 | printf "source vss: $EMF\n" 126 | printf "out svg : $SVG\n\n" 127 | printf "xmllint result:\n" 128 | xmllint --dtdvalid ./svg11-flat.dtd --noout ${SVG} 2>&1 >/dev/null 129 | printf "\n" 130 | printf "Convert and xmllint commands:\n" 131 | printf "$CMD -i \"$EMF\" -o \"${OUTDIR}\"\n" 132 | printf "xmllint --dtdvalid $DTD --noout ${SVG}\n\n" 133 | ret=1 134 | fi 135 | done 136 | rm -f "${OUTDIR}/*.svg" 137 | fi 138 | verbose_print "\n#####################################################\n" 139 | [ "${STOPONERROR}" = "yes" ] && [ $ret -eq 1 ] && exit 1 140 | done 141 | if [ $ret -ne 0 ] 142 | then 143 | printf "[${BRed}FAIL${RCol}] Check exited with error(s)\n" 144 | else 145 | printf "[${BGre}SUCCESS${RCol}] Check Ok\n" 146 | fi 147 | exit $ret 148 | -------------------------------------------------------------------------------- /tests/resources/check_truncate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0` 4 | mkdir -p ../out 5 | 6 | # Once a file is seen as corrupted by vss2svg 7 | # we alter if $MAX more times to try to make 8 | # vss2svg crash 9 | MAX=100 10 | 11 | burn_in_hell(){ 12 | counter1=0 13 | while [ $counter1 -lt $MAX ] 14 | do 15 | tmp_vss=`mktemp -p ../out/` 16 | cp vss/`ls vss |shuf -n 1` ${tmp_vss} 17 | counter2=0 18 | while [ $counter2 -lt $MAX ] 19 | do 20 | ../../vss2svg -p -w 800 -h 600 -i ${tmp_vss} -o ../out/test.svg -p 21 | ret=$? 22 | if [ $ret -eq 1 ] 23 | then 24 | counter2=$MAX 25 | elif [ $ret -gt 1 ] 26 | then 27 | ts=`date +"%F-%H%M%S"` 28 | printf "[ERROR] corrupted file 'bad_corrupted_${ts}.vss' caused something wrong\n" 29 | cp ${tmp_vss} ../out/bad_corrupted_${ts}.vss 30 | exit $ret 31 | fi 32 | size=`du -b ${tmp_vss} |sed "s/\t.*//"` 33 | count=$(( `od -vAn -N4 -tu4 < /dev/urandom` % $size + 1 )) 34 | tmp_vss2=`mktemp -p ../out/` 35 | dd if=${tmp_vss} of=${tmp_vss2} count=$count bs=1 >/dev/null 2>&1 36 | mv ${tmp_vss2} ${tmp_vss} 37 | counter2=$(( $counter2 + 1 )) 38 | done 39 | rm "${tmp_vss}" 40 | counter1=$(( $counter1 + 1 )) 41 | done 42 | } 43 | 44 | if [ "$1" = "inf" ] 45 | then 46 | while true 47 | do 48 | burn_in_hell 49 | done 50 | else 51 | burn_in_hell 52 | fi 53 | -------------------------------------------------------------------------------- /tests/resources/colors.sh: -------------------------------------------------------------------------------- 1 | RCol='\33[0m' # Text Reset 2 | 3 | # Regular Bold Underline High Intensity 4 | Bla='\33[0;30m'; BBla='\33[1;30m'; UBla='\33[4;30m'; IBla='\33[0;90m'; 5 | Red='\33[0;31m'; BRed='\33[1;31m'; URed='\33[4;31m'; IRed='\33[0;91m'; 6 | Gre='\33[0;32m'; BGre='\33[1;32m'; UGre='\33[4;32m'; IGre='\33[0;92m'; 7 | Yel='\33[0;33m'; BYel='\33[1;33m'; UYel='\33[4;33m'; IYel='\33[0;93m'; 8 | Blu='\33[0;34m'; BBlu='\33[1;34m'; UBlu='\33[4;34m'; IBlu='\33[0;94m'; 9 | Pur='\33[0;35m'; BPur='\33[1;35m'; UPur='\33[4;35m'; IPur='\33[0;95m'; 10 | Cya='\33[0;36m'; BCya='\33[1;36m'; UCya='\33[4;36m'; ICya='\33[0;96m'; 11 | Whi='\33[0;37m'; BWhi='\33[1;37m'; UWhi='\33[4;37m'; IWhi='\33[0;97m'; 12 | 13 | #BoldHigh Intens Background High Intensity Backgrounds 14 | BIBla='\33[1;90m'; On_Bla='\33[40m'; On_IBla='\33[0;100m'; 15 | BIRed='\33[1;91m'; On_Red='\33[41m'; On_IRed='\33[0;101m'; 16 | BIGre='\33[1;92m'; On_Gre='\33[42m'; On_IGre='\33[0;102m'; 17 | BIYel='\33[1;93m'; On_Yel='\33[43m'; On_IYel='\33[0;103m'; 18 | BIBlu='\33[1;94m'; On_Blu='\33[44m'; On_IBlu='\33[0;104m'; 19 | BIPur='\33[1;95m'; On_Pur='\33[45m'; On_IPur='\33[0;105m'; 20 | BICya='\33[1;96m'; On_Cya='\33[46m'; On_ICya='\33[0;106m'; 21 | BIWhi='\33[1;97m'; On_Whi='\33[47m'; On_IWhi='\33[0;107m'; 22 | -------------------------------------------------------------------------------- /tests/travis-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | help(){ 4 | cat < 6 | 7 | 8 | 9 | arguments: 10 | 11 | EOF 12 | exit 1 13 | } 14 | 15 | while getopts ":hn:" opt; do 16 | case $opt in 17 | 18 | h) 19 | help 20 | ;; 21 | n) 22 | NAME="$OPTARG" 23 | ;; 24 | \?) 25 | echo "Invalid option: -$OPTARG" >&2 26 | help 27 | exit 1 28 | ;; 29 | :) 30 | echo "Option -$OPTARG requires an argument." >&2 31 | help 32 | exit 1 33 | ;; 34 | esac 35 | done 36 | 37 | OUT=`dirname $0` 38 | OUT=`readlink -f $OUT` 39 | mkdir -p ${OUT}/deps_build 40 | cd ${OUT}/deps_build 41 | 42 | RVNG_VERSION=0.0.4 43 | VISIO_VERSION=0.1.6 44 | EMF_VERSION=master 45 | 46 | ! [ -e "${EMF_VERSION}.tar.gz" ] && wget https://github.com/kakwa/libemf2svg/archive/${EMF_VERSION}.tar.gz 47 | rm -rf libemf2svg-${EMF_VERSION} 48 | tar -xf ${EMF_VERSION}.tar.gz || exit 1 49 | cd libemf2svg-${EMF_VERSION} && CC=clang CXX=clang++ cmake . -DUSE_CLANG=ON && make && make install DESTDIR=$OUT && cd - || exit 1 50 | 51 | if ! [ "`uname`" = "Darwin" ] 52 | then 53 | ! [ -e "librevenge-${RVNG_VERSION}.tar.xz" ] && wget http://mirror.kakwalab.ovh/src/librevenge/${RVNG_VERSION}/librevenge-${RVNG_VERSION}.tar.xz 54 | rm -rf librevenge-${RVNG_VERSION} 55 | tar -xf librevenge-${RVNG_VERSION}.tar.xz || exit 1 56 | cd librevenge-${RVNG_VERSION} && ./configure --disable-tests --without-docs && make && make install DESTDIR=$OUT && cd - || exit 1 57 | find $OUT -name "*.pc" -exec sed -i "s|^prefix=|prefix=$OUT|" {} \; 58 | 59 | export PKG_CONFIG_PATH="$OUT/usr/local/lib/pkgconfig" 60 | ! [ -e "libvisio-${VISIO_VERSION}.tar.xz" ] && wget http://mirror.kakwalab.ovh/src/libvisio/${VISIO_VERSION}/libvisio-${VISIO_VERSION}.tar.xz 61 | rm -rf libvisio-${VISIO_VERSION} 62 | tar -xf libvisio-${VISIO_VERSION}.tar.xz || exit 1 63 | cd libvisio-${VISIO_VERSION} && ./configure --disable-tests --disable-tools --without-docs && make && make install DESTDIR=$OUT && cd - || exit 1 64 | fi 65 | --------------------------------------------------------------------------------