diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..aea3c132 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +*.c text eol=lf +*.cpp text eol=lf +*.h text eol=lf +*.giattributes text eol=lf +*.txt text eol=lf +*.patch text eol=lf +*.svg text eol=lf +*.sh text eol=lf \ No newline at end of file diff --git a/.github/workflows/alpine-x86_64.yml b/.github/workflows/alpine-x86_64.yml new file mode 100644 index 00000000..a088fcd9 --- /dev/null +++ b/.github/workflows/alpine-x86_64.yml @@ -0,0 +1,167 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: Alpine-x86_64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/macos-x86_64.yml' + - '.github/workflows/macos-arm64.yml' + - '.github/workflows/ubuntu-x86_64.yml' + - '.github/workflows/ubuntu-aarch64.yml' + - '.github/workflows/windows-x86_64.yml' + - '.github/workflows/msys-x86_64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + VCPKG_FORCE_SYSTEM_BINARIES: 1 + CC: gcc + CXX: g++ + +jobs: + build-and-test: + runs-on: ubuntu-latest + container: + image: alpine:latest + + steps: + - name: Install packages + run: | + apk --no-cache --upgrade add build-base cmake git valgrind tar argp-standalone \ + gcompat gperf flex bison gettext-dev python3 ninja zip unzip curl pkgconfig \ + libxml2-utils font-noto-hebrew font-noto-arabic + +# vcpkg prerequisites (and VCPKG_FORCE_SYSTEM_BINARIES: 1) +# gcompat gperf flex bison gettext-dev python3 ninja zip unzip curl pkg-config +# testing prerequisites +# libxml2-utils +# [ tests/resources/emf/test-183.emf ] -- font-noto-hebrew font-noto-arabic + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure cache + run: | + echo 'VCPKG_DEFAULT_BINARY_CACHE<> $GITHUB_ENV + echo "$(pwd)/cache" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git config --global --add safe.directory $(pwd) + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET=x64-linux -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Test well formed files + run: ./tests/resources/check_correctness.sh -r -s + + - name: Test corrupted files + run: ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-corrupted/ -xN + + - name: Test EA files + run: ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-ea/ + + build-for-ruby: + runs-on: ubuntu-latest + container: + image: alpine:latest + + steps: + - name: Install packages + run: | + apk --no-cache --upgrade add build-base cmake git tar bash \ + gcompat gperf flex bison gettext-dev python3 ninja zip unzip curl pkgconfig + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure cache + run: | + echo 'VCPKG_DEFAULT_BINARY_CACHE<> $GITHUB_ENV + echo "$(pwd)/cache" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git config --global --add safe.directory /__w/libemf2svg/libemf2svg + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release \ + -DVCPKG_TARGET_TRIPLET=x64-linux \ + -DLONLY=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Checkout shell test framework + uses: actions/checkout@v4 + with: + repository: kward/shunit2 + path: tests/shunit2 + fetch-depth: 1 + + - name: Run additional tests + run: tests/resources/lcheck.sh diff --git a/.github/workflows/macos-arm64.yml b/.github/workflows/macos-arm64.yml new file mode 100644 index 00000000..8c4857a4 --- /dev/null +++ b/.github/workflows/macos-arm64.yml @@ -0,0 +1,103 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: MacOS-arm64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/alpine-x86_64.yml' + - '.github/workflows/ubuntu-x86_64.yml' + - '.github/workflows/ubuntu-aarch64.yml' + - '.github/workflows/msys-x86_64.yml' + - '.github/workflows/windows-x86_64.yml' + - '.github/workflows/macos-x86_64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/cache + +jobs: + build-for-ruby: + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup environment + run: | + echo 'BREW_HOME<> $GITHUB_ENV + brew --prefix >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Install packages + run: brew install bash + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -DVCPKG_TARGET_TRIPLET=arm64-osx \ + -DLONLY=ON \ + -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + + - name: Checkout shell test framework + uses: actions/checkout@v4 + with: + repository: kward/shunit2 + path: tests/shunit2 + fetch-depth: 1 + + - name: Run additional tests + run: ${{ env.BREW_HOME }}/bin/bash tests/resources/lcheck.sh diff --git a/.github/workflows/macos-x86_64.yml b/.github/workflows/macos-x86_64.yml new file mode 100644 index 00000000..ca1172d8 --- /dev/null +++ b/.github/workflows/macos-x86_64.yml @@ -0,0 +1,138 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: MacOS-x86_64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/alpine-x86_64.yml' + - '.github/workflows/ubuntu-x86_64.yml' + - '.github/workflows/ubuntu-aarch64.yml' + - '.github/workflows/msys-x86_64.yml' + - '.github/workflows/windows-x86_64.yml' + - '.github/workflows/macos-arm64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/cache + +jobs: + build-and-test: + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install packages + run: | + brew install argp-standalone coreutils + brew tap LouisBrunner/valgrind + brew install --HEAD LouisBrunner/valgrind/valgrind + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Test well formed files + working-directory: ${{ github.workspace }} + continue-on-error: true + run: ./tests/resources/check_correctness.sh -r -s + + - name: Test corrupted files + working-directory: ${{ github.workspace }} + continue-on-error: true + run: ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-corrupted/ -xN + + - name: Test EA files + working-directory: ${{ github.workspace }} + continue-on-error: true + run: ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-ea/ + + build-for-ruby: + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup environment + run: | + echo 'BREW_HOME<> $GITHUB_ENV + brew --prefix >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Install packages + run: brew install bash + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -DVCPKG_TARGET_TRIPLET=x64-osx \ + -DLONLY=ON \ + -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Checkout shell test framework + uses: actions/checkout@v4 + with: + repository: kward/shunit2 + path: tests/shunit2 + fetch-depth: 1 + + - name: Run additional tests + run: ${{ env.BREW_HOME }}/bin/bash tests/resources/lcheck.sh diff --git a/.github/workflows/msys-x86_64.yml b/.github/workflows/msys-x86_64.yml new file mode 100644 index 00000000..75f4766c --- /dev/null +++ b/.github/workflows/msys-x86_64.yml @@ -0,0 +1,174 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: MSys-x86_64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/alpine-x86_64.yml' + - '.github/workflows/macos-x86_64.yml' + - '.github/workflows/macos-arm64.yml' + - '.github/workflows/ubuntu-aarch64.yml' + - '.github/workflows/ubuntu-x86_64.yml' + - '.github/workflows/windows-x86_64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + MAKEFLAGS: -j4 + +jobs: + build-and-smoke-test: + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + steps: + - name: Setup msys + uses: msys2/setup-msys2@v2 + with: + path-type: inherit + msystem: mingw64 + install: git flex bison gettext-devel + pacboy: gperf:p cmake:p python3:p ninja:p curl:p pkgconf:p gcc:p + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure cache + run: | + echo 'VCPKG_DEFAULT_BINARY_CACHE<> $GITHUB_ENV + echo "$(pwd)/cache" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + echo 'CI_CACHE<> $GITHUB_ENV + echo "$(pwd -W)/cache" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.CI_CACHE }} + key: ${{ github.workflow }}-mingw64-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -DVCPKG_TARGET_TRIPLET=x64-mingw-static \ + -DCMAKE_TOOLCHAIN_FILE=$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Smoke test + run: | + ./build/emf2svg-conv.exe --version + ./build/emf2svg-conv.exe -i tests/resources/emf/test-000.emf -o test-000.svg + ls | grep test-000.svg + + build-for-ruby: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + sys: + - mingw64 + - ucrt64 + defaults: + run: + shell: msys2 {0} + steps: + - name: Setup msys + uses: msys2/setup-msys2@v2 + with: + path-type: inherit + msystem: ${{ matrix.sys }} + install: git flex bison gettext-devel + pacboy: gperf:p cmake:p python3:p ninja:p curl:p pkgconf:p gcc:p + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure cache + run: | + echo 'VCPKG_DEFAULT_BINARY_CACHE<> $GITHUB_ENV + echo "$(pwd)/cache" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + echo 'CI_CACHE<> $GITHUB_ENV + echo "$(pwd -W)/cache" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.CI_CACHE }} + key: ${{ github.workflow }}-${{ matrix.sys }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release \ + -DVCPKG_TARGET_TRIPLET=x64-mingw-static \ + -DLONLY=ON \ + -DCMAKE_TOOLCHAIN_FILE=$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Checkout shell test framework + uses: actions/checkout@v4 + with: + repository: kward/shunit2 + path: tests/shunit2 + fetch-depth: 1 + + - name: Run additional tests + run: tests/resources/lcheck.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..1139802f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,67 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Collect + run: | + cd .. + rm -rf libemf2svg/.git* + rm -rf libemf2svg/vcpkg/.git* + rm -rf libemf2svg/tests + tar cvzf libemf2svg.tar.gz libemf2svg + sha256sum -b libemf2svg.tar.gz > libemf2svg.tar.gz.sha256 + + - name: Release + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{github.workspace}}/../libemf2svg.tar.gz + ${{github.workspace}}/../libemf2svg.tar.gz.sha256 diff --git a/.github/workflows/ubuntu-aarch64.yml b/.github/workflows/ubuntu-aarch64.yml new file mode 100644 index 00000000..fef6273b --- /dev/null +++ b/.github/workflows/ubuntu-aarch64.yml @@ -0,0 +1,95 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: Ubuntu-aarch64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/alpine-x86_64.yml' + - '.github/workflows/ubuntu-x86_64.yml' + - '.github/workflows/macos-x86_64.yml' + - '.github/workflows/macos-arm64.yml' + - '.github/workflows/msys-x86_64.yml' + - '.github/workflows/windows-x86_64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/cache + +jobs: + build-and-smoke-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Install packages + run: | + sudo apt-get update + sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu \ + gperf qemu-user + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ + -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_TARGET_TRIPLET=arm64-linux + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Smoke test + run: | + qemu-aarch64 -L /usr/aarch64-linux-gnu ${{ github.workspace }}/build/emf2svg-conv --version + qemu-aarch64 -L /usr/aarch64-linux-gnu ${{ github.workspace }}/build/emf2svg-conv \ + -i ${{ github.workspace }}/tests/resources/emf/test-000.emf -o test-000.svg + ls | grep test-000.svg diff --git a/.github/workflows/ubuntu-x86_64.yml b/.github/workflows/ubuntu-x86_64.yml new file mode 100644 index 00000000..1abc9f33 --- /dev/null +++ b/.github/workflows/ubuntu-x86_64.yml @@ -0,0 +1,163 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: Ubuntu-x86_64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/alpine-x86_64.yml' + - '.github/workflows/macos-x86_64.yml' + - '.github/workflows/macos-arm64.yml' + - '.github/workflows/ubuntu-aarch64.yml' + - '.github/workflows/msys-x86_64.yml' + - '.github/workflows/windows-x86_64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/cache + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install packages + run: | + sudo apt-get update + sudo apt-get install libxml2-utils valgrind + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE }} + + - name: Test well formed files + working-directory: ${{ github.workspace }} + run: ./tests/resources/check_correctness.sh -r -s + + - name: Test corrupted files + working-directory: ${{ github.workspace }} + run: ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-corrupted/ -xN + + - name: Test EA files + working-directory: ${{ github.workspace }} + run: ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-ea/ + + coverage: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install packages + run: | + sudo apt-get update + sudo apt-get install libxml2-utils valgrind lcov + + - name: Install coveralls + run: sudo pip install cpp-coveralls + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Debug -DGCOV=ON + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Report + run: | + cd build + coveralls \ + -t ${{ secrets.COVERALLS_REPO_TOKEN }} \ + -E '.*CMakeFiles.*' \ + -E '.*conv.*' \ + -E '.*uemf.*' \ + -E '.*upmf.*' \ + -E '.*goodies.*' \ + -E '.*tests.*' \ + --gcov-options '\-lp' + + build-for-ruby: + runs-on: ubuntu-latest + steps: + - name: Install packages + run: | + sudo apt-get update + sudo apt-get install gperf + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release \ + -DVCPKG_TARGET_TRIPLET=x64-linux \ + -DLONLY=ON \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Checkout shell test framework + uses: actions/checkout@v4 + with: + repository: kward/shunit2 + path: tests/shunit2 + fetch-depth: 1 + + - name: Run additional tests + run: tests/resources/lcheck.sh diff --git a/.github/workflows/windows-x86_64.yml b/.github/workflows/windows-x86_64.yml new file mode 100644 index 00000000..12a66c07 --- /dev/null +++ b/.github/workflows/windows-x86_64.yml @@ -0,0 +1,84 @@ +# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tamatebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The purpose of this workflow is to check that build procedures work correctly +# in specific environment. Due to this reason there is no caching. It is done by +# intention. All caching is in upstream projects. + +name: Windows-x86_64 + +on: + push: + branches: [ master ] + paths-ignore: + - '.github/workflows/alpine-x86_64.yml' + - '.github/workflows/macos-x86_64.yml' + - '.github/workflows/macos-arm64.yml' + - '.github/workflows/ubuntu-aarch64.yml' + - '.github/workflows/ubuntu-x86_64.yml' + - '.github/workflows/msys-x86_64.yml' + pull_request: + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + +env: + BUILD_TYPE: Release + VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/cache + +jobs: + build-and-smoke-test: + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Create cache storage and cache key + run: | + mkdir ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + git submodule status > sm.txt + + - name: Process cache + uses: actions/cache@v4 + with: + path: ${{env.VCPKG_DEFAULT_BINARY_CACHE}} + key: ${{ github.workflow }}-vcpkg-${{ hashFiles('sm.txt') }} + + - name: Configure + run: cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Smoke test + run: | + build/${{env.BUILD_TYPE}}/emf2svg-conv.exe --version + build/${{env.BUILD_TYPE}}/emf2svg-conv.exe -i tests/resources/emf/test-000.emf -o test-000.svg + dir . | findstr test-000.svg diff --git a/.gitignore b/.gitignore index 69747c1b..0166197b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,14 @@ emf2svg-conv # out directory out +build + +# test files +tests/resources/tmp + +# VS files +.vs + # external build directory external-build @@ -45,3 +53,12 @@ emf2svg-test # exceptions !tests/resources/filesmasher-master/Makefile + +# external projects +vcpkg +vcpkg_installed +vcpkg-manifest-install.log +deps/tmp +deps/src +deps/include +deps/lib \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..a0a57f3d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vcpkg"] + path = vcpkg + url = https://github.com/microsoft/vcpkg.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ec3443ef..00000000 --- a/.travis.yml +++ /dev/null @@ -1,53 +0,0 @@ -matrix: - include: - - os: osx - compiler: clang - env: - N=OSX - LAST_CMD="./tests/resources/check_correctness.sh -r -s -n && ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-corrupted/ -xNn" - - os: linux - dist: trusty - compiler: clang - env: - N=BASE - LAST_CMD="./tests/resources/check_correctness.sh -r -s && ./tests/resources/check_correctness.sh -r -s -e tests/resources/emf-corrupted/ -xN" - - os: linux - dist: trusty - env: - N=GCOV - CMAKE_ARG="-DGCOV=ON" - LAST_CMD="make coverage" - - -sudo: required - -before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then xcode-select --install;true; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install argp-standalone; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install valgrind; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libxml2; fi - - if [[ "$N" == "GCOV" ]]; then pip install --user cpp-coveralls; fi - -addons: - apt: - packages: - - cmake - - time - - valgrind - - libxml2-utils - - libc6-dev - - gcc - - g++ - - gcc - - g++ - - lcov - - -language: cpp - - -script: cmake . -DFORCELE=ON $CMAKE_ARG && make && $LAST_CMD - -after_success: - - if [[ "$N" == "GCOV" ]];then ~/.local/bin/coveralls -E '.*CMakeFiles.*' -E '.*conv.*' -E '.*uemf.*' -E '.*upmf.*' -E '.*goodies.*' -E '.*tests.*' --gcov-options '\-lp';fi diff --git a/CMakeLists.txt b/CMakeLists.txt index d7e84641..50904d69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,41 +1,118 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.12) +include(CheckFunctionExists) +include(ExternalProject) + +# Comments re vcpkg +# - We use vcpkg to install the following libraries +# and their dependencies: +# -- libpng +# -- freetype +# -- fontconfig +# -- iconv +# - vcpkg is configured as git submodule. Some experts consider it is the best practice +# (though other experts do not) +# - The list of vcpkg modules is specified at vcpkg.json +# - With this setup and when called with -DCMAKE_TOOLCHAIN_FILE={project root}/vcpkg/scripts/buildsystems/vcpkg.cmake +# CMake automatically runs vcpkg install for all listed packages before the project directive is executed project (emf2svg) -# Project version (sematic versionning) set(emf2svg_VERSION_MAJOR 1) -set(emf2svg_VERSION_MINOR 1) -set(emf2svg_VERSION_PATCH 0) +set(emf2svg_VERSION_MINOR 7) +set(emf2svg_VERSION_PATCH 3) +set(emf2svg_VERSION ${emf2svg_VERSION_MAJOR}.${emf2svg_VERSION_MINOR}.${emf2svg_VERSION_PATCH}) + +if(VCPKG_TARGET_TRIPLET) + set(PLATFORM_TOOLCHAIN ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${VCPKG_TARGET_TRIPLET}.cmake) + message(STATUS "Configured with platform toolchain = '${PLATFORM_TOOLCHAIN}'") + if(EXISTS ${PLATFORM_TOOLCHAIN}) + set(PLATFORM_TOOLCHAIN_OPTION -DCMAKE_TOOLCHAIN_FILE=${PLATFORM_TOOLCHAIN}) + include(${PLATFORM_TOOLCHAIN}) + endif(EXISTS ${PLATFORM_TOOLCHAIN}) +endif(VCPKG_TARGET_TRIPLET) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") +option(LONLY "build library only" OFF) +option(GCOV "compile with gcov support" OFF) +option(UNITTEST "compile unit tests" OFF) +option(INDEX "print record indexes" OFF) +option(STATIC "compile statically" OFF) +option(FORCELE "force little endian architecture" OFF) + +if(STATIC) + set(SHARED "SHARED") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + set(BUILD_SHARED_LIBRARIES OFF) + set(CMAKE_EXE_LINKER_FLAGS "-static") +else(STATIC) + set(SHARED "SHARED") +endif(STATIC) + +if(GCOV) + SET(UNITTEST ON) + SET(CMAKE_BUILD_TYPE "Debug") +endif(GCOV) + +if(UNITTEST) + SET(LONLY OFF) + message(WARNING "LONLY cannot be used with GCOV and/or UNITTEST; ignoring") +endif(UNITTEST) + find_package(PNG REQUIRED) find_package(Freetype REQUIRED) find_package(Fontconfig REQUIRED) +find_package(LibXml2 REQUIRED) + +set(DEPS ${CMAKE_CURRENT_SOURCE_DIR}/deps) +set(EXTERNAL_INCLUDE_DIR ${DEPS}/include) +set(EXTERNAL_LIB_DIR ${DEPS}/lib) +set(PATCHES ${CMAKE_CURRENT_SOURCE_DIR}/patches) + +# Comments re fmem setup +# The patch fixes https://github.com/Snaipe/fmem/issues/4 +# https://gitlab.kitware.com/cmake/cmake/-/issues/21086 +# Basically, you cannot predict when PATCH_COMMAND is executed so we always roll back +# the patch that may be applied during the previous build in order to ensure that +# the same patch may be applied correctly again + +# And finally a small helper for building native Windows and WSL from single source tree +set(FMEM_NAME fm${EXTERNAL_LIB_DIR_SUFFIX}) + +ExternalProject_Add(${FMEM_NAME} + PREFIX ${DEPS} + GIT_REPOSITORY https://github.com/tamatebako/fmem.git + GIT_TAG bdce2760f0190253600f11984220fc0a007742c8 + CMAKE_ARGS -DBUILD_TESTING=FALSE + -DCMAKE_INSTALL_PREFIX=${DEPS} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + ${PLATFORM_TOOLCHAIN_OPTION} + ${CMAKE_OSX_ARCHITECTURES_OPTION} +) +set(EXTERNAL_FMEM "fmem") -set(emf2svg_VERSION ${emf2svg_VERSION_MAJOR}.${emf2svg_VERSION_MINOR}.${emf2svg_VERSION_PATCH}) +set(DEPS_UEMF libUEMF-0.2.5) add_custom_target(tag COMMAND git tag -a ${emf2svg_VERSION} -m "tagging version ${emf2svg_VERSION}" COMMAND git push origin ${emf2svg_VERSION} ) - # set version as a definition set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DE2S_VERSION='\"${emf2svg_VERSION}\"'") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DE2S_VERSION='\"${emf2svg_VERSION}\"'") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DE2S_VERSION=${emf2svg_VERSION}") -# Options -option(GCOV "compile with gcov support" OFF) -option(UNITTEST "compile unit tests" OFF) -option(INDEX "print record indexes" OFF) -option(STATIC "compile statically" OFF) -option(FORCELE "force little endian architecture" OFF) +if(NOT LONLY) + message(STATUS "Configuring argp") + list(APPEND CMAKE_MESSAGE_INDENT " ") + message(STATUS "Checking if argp is provided by system libraries") + CHECK_FUNCTION_EXISTS(argp_parse HAVE_BUNDLED_ARGP_PARSE_FUNCTION) -if(GCOV) - SET(UNITTEST ON) - SET(CMAKE_BUILD_TYPE "Debug") -endif(GCOV) + message(STATUS "Looking for standalone argp library") + set(CMAKE_REQUIRED_LIBRARIES argp) + CHECK_FUNCTION_EXISTS(argp_parse HAVE_EXTERNAL_ARGP_PARSE_FUNCTION) + list(POP_BACK CMAKE_MESSAGE_INDENT) +endif(NOT LONLY) if(INDEX) set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -DRECORD_INDEX='true'") @@ -43,124 +120,200 @@ if(INDEX) endif(INDEX) if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Od -Zi") + else(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g") - set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_EXE_LINKER_FLAGS "-INCREMENTAL:NO") + endif(MSVC) endif() -if(STATIC) - set(SHARED "") - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - set(BUILD_SHARED_LIBRARIES OFF) - set(CMAKE_EXE_LINKER_FLAGS "-static") -else(STATIC) - set(SHARED "SHARED") -endif(STATIC) - if(UNIX) link_libraries(m) + add_compile_options(-fPIC) endif(UNIX) -IF(NOT FORCELE) - include(TestBigEndian) - TEST_BIG_ENDIAN(BIGENDIAN) - IF(${BIGENDIAN}) - add_definitions(-DWORDS_BIGENDIAN) - ENDIF(${BIGENDIAN}) +if(NOT FORCELE) + include(TestBigEndian) + TEST_BIG_ENDIAN(BIGENDIAN) + IF(${BIGENDIAN}) + add_definitions(-DWORDS_BIGENDIAN) + ENDIF(${BIGENDIAN}) endif(NOT FORCELE) # Build external dependancies if we are on OSX -IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - # Mac OS X specific code - set(EXTERNAL_ICONV "iconv") - set(EXTERNAL_ARGP "argp") - add_definitions(-DDARWIN) -ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - -# linking directories -LINK_DIRECTORIES( - ${CMAKE_BINARY_DIR}/ - /usr/local/lib - /usr/lib/ -) +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + execute_process( + COMMAND brew --prefix + RESULT_VARIABLE BREW_PREFIX_RES + OUTPUT_VARIABLE BREW_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT (BREW_PREFIX_RES EQUAL 0)) + message(FATAL_ERROR "Could not find brew setup") + endif() + set(BREW_LIB_DIR ${BREW_PREFIX}/lib) + set(EXTERNAL_ICONV "iconv") + add_definitions(-DDARWIN) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + +# Find/build external dependencies if it is Microsoft Visual Studio build +if(MSVC) + find_package(Iconv REQUIRED) + set(EXTERNAL_ICONV ${Iconv_LIBRARY}) +endif(MSVC) + +if(NOT LONLY) + if(HAVE_BUNDLED_ARGP_PARSE_FUNCTION) + message(STATUS "Using bundled argp") + elseif(HAVE_EXTERNAL_ARGP_PARSE_FUNCTION) + message(STATUS "Using stand-alone argp") + set(EXTERNAL_ARGP "argp") + else() + message(STATUS "Building argp") + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # https://www.gnu.org/software/gnulib/manual/html_node/argp_005fprogram_005fversion_005fhook.html + set(EXTERNAL_ARGP "-Wl,-force_load,${DEPS}/lib/libargp-standalone.a") + else(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(EXTERNAL_ARGP "argp-standalone") + endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + + set(ARGP_NAME argp${EXTERNAL_LIB_DIR_SUFFIX}) + ExternalProject_Add(${ARGP_NAME} + PREFIX ${DEPS} + GIT_REPOSITORY https://github.com/tom42/argp-standalone.git + GIT_TAG 238d83d6fb4fbdbb3e0893f51698d8d54696bfb0 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DEPS} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + ${PLATFORM_TOOLCHAIN_OPTION} + ${CMAKE_OSX_ARCHITECTURES_OPTION} + PATCH_COMMAND cd ${DEPS}/src/${ARGP_NAME} && git restore CMakeLists.txt && git apply ${PATCHES}/argp/CMakeLists.txt.patch + ) + endif(HAVE_BUNDLED_ARGP_PARSE_FUNCTION) +endif(NOT LONLY) -# headers directories -include_directories( - ./inc/ +if(MSVC) + include_directories( + ./inc ${PNG_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} - ${FONTCONFIG_INCLUDE_DIR} - /usr/include/ - /sw/include/ -) + ${FONTCONFIG_INCLUDE_DIRS} + ${EXTERNAL_INCLUDE_DIR} + ${DEPS_UEMF} + ) + link_directories( + ${CMAKE_BINARY_DIR} + ${EXTERNAL_LIB_DIR} + ) +else(MSVC) + include_directories( + ./inc + ${PNG_INCLUDE_DIRS} + ${FREETYPE_INCLUDE_DIRS} + ${FONTCONFIG_INCLUDE_DIRS} + ${EXTERNAL_INCLUDE_DIR} + ${DEPS_UEMF} + /usr/local/include + /usr/include + /sw/include + ) + link_directories( + ${CMAKE_BINARY_DIR} + ${EXTERNAL_LIB_DIR} + ${BREW_LIB_DIR} + ) +endif(MSVC) -# Compile the library add_library(emf2svg - ${SHARED} - src/lib/pmf2svg.c - src/lib/pmf2svg_print.c - src/lib/uemf_utf.c - src/lib/uemf_endian.c - src/lib/uemf.c - src/lib/upmf.c - src/lib/emf2svg_utils.c - src/lib/emf2svg_img_utils.c - src/lib/emf2svg_clip_utils.c - src/lib/emf2svg_rec_control.c - src/lib/emf2svg_rec_object_creation.c - src/lib/emf2svg_rec_path.c - src/lib/emf2svg_rec_clipping.c - src/lib/emf2svg_rec_drawing.c - src/lib/emf2svg_rec_bitmap.c - src/lib/emf2svg_rec_object_manipulation.c - src/lib/emf2svg_rec_comment.c - src/lib/emf2svg_rec_transform.c - src/lib/emf2svg_rec_state_record.c - src/lib/emf2svg_print.c - src/lib/emf2svg.c + ${SHARED} + src/lib/pmf2svg.c + src/lib/pmf2svg_print.c + ${DEPS_UEMF}/uemf_utf.c + ${DEPS_UEMF}/uemf_endian.c + ${DEPS_UEMF}/uemf.c + ${DEPS_UEMF}/upmf.c + src/lib/emf2svg_utils.c + src/lib/emf2svg_img_utils.c + src/lib/emf2svg_clip_utils.c + src/lib/emf2svg_rec_control.c + src/lib/emf2svg_rec_object_creation.c + src/lib/emf2svg_rec_path.c + src/lib/emf2svg_rec_clipping.c + src/lib/emf2svg_rec_drawing.c + src/lib/emf2svg_rec_bitmap.c + src/lib/emf2svg_rec_object_manipulation.c + src/lib/emf2svg_rec_comment.c + src/lib/emf2svg_rec_transform.c + src/lib/emf2svg_rec_state_record.c + src/lib/emf2svg_print.c + src/lib/emf2svg.c ) -set_target_properties(emf2svg - PROPERTIES - VERSION ${emf2svg_VERSION} - SOVERSION ${emf2svg_VERSION_MAJOR} +set_target_properties(emf2svg + PROPERTIES + VERSION ${emf2svg_VERSION} + SOVERSION ${emf2svg_VERSION_MAJOR} ) -# Compile the executable -add_executable(emf2svg-conv src/conv/emf2svg.cpp) target_link_libraries(emf2svg + ${PNG_LIBRARIES} + ${LIBXML2_LIBRARIES} + ${EXTERNAL_ICONV} + ${FREETYPE_LIBRARIES} + ${FONTCONFIG_LIBRARIES} + ${EXPAT_LIBRARY_RELEASE} + ${EXTERNAL_FMEM} +) + +add_dependencies(emf2svg ${FMEM_NAME}) + +if(NOT LONLY) + add_executable(emf2svg-conv src/conv/emf2svg.cpp) + + target_link_libraries(emf2svg-conv + emf2svg + ${EXTERNAL_ARGP} ${PNG_LIBRARIES} + ${LIBXML2_LIBRARIES} ${EXTERNAL_ICONV} - ${EXTERNAL_ARGP} ${FREETYPE_LIBRARIES} ${FONTCONFIG_LIBRARIES} -) + ${EXPAT_LIBRARY_RELEASE} + ${EXTERNAL_FMEM} + ) -target_link_libraries(emf2svg-conv - emf2svg -) + if(ARGP_NAME) + add_dependencies(emf2svg-conv ${ARGP_NAME}) + endif(ARGP_NAME) -if(GCOV) - Set(COVERAGE_EXCLUDES '*conv*' '*uemf*' '*upmf*' '*tests*' '*c++*') - include(CodeCoverage) - setup_target_for_coverage(NAME coverage - EXECUTABLE emf2svg-test ./tests/resources/emf*/* - DEPENDENCIES emf2svg-test + if(GCOV) + Set(COVERAGE_EXCLUDES '*conv*' '*uemf*' '*upmf*' '*tests*' '*c++*') + include(CodeCoverage) + setup_target_for_coverage(NAME coverage + EXECUTABLE emf2svg-test ./tests/resources/emf*/* + DEPENDENCIES emf2svg-test ) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") -endif(GCOV) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") + endif(GCOV) -if(UNITTEST) - add_executable(emf2svg-test tests/test.c) + if(UNITTEST) + add_executable(emf2svg-test tests/test.c) - target_link_libraries(emf2svg-test - emf2svg - ) -endif(UNITTEST) - - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall") + target_link_libraries(emf2svg-test + emf2svg + ) + endif(UNITTEST) +endif(NOT LONLY) + +if (MSVC) +# x64: suppress mostly harmless warnings about 64 to 32 bit conversion (4244, 4267, 4305). + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std:c++14 -W3 -D_CRT_SECURE_NO_WARNINGS -wd4244 -wd4267 -wd4305") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std:c11 -W3 -D_CRT_SECURE_NO_WARNINGS -wd4244 -wd4267 -wd4305") +else(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall") +endif (MSVC) if (NOT LIB_INSTALL_DIR) set(LIB_INSTALL_DIR lib) @@ -174,13 +327,15 @@ if (NOT INCLUDE_INSTALL_DIR) set(INCLUDE_INSTALL_DIR include) endif () +list(APPEND BINTARGETS emf2svg) +if(NOT LONLY) + list(APPEND BINTARGETS emf2svg-conv) +endif(NOT LONLY) -# install binaries and library -INSTALL(TARGETS emf2svg emf2svg-conv +INSTALL(TARGETS ${BINTARGETS} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} ) -# install header file INSTALL(FILES inc/emf2svg.h DESTINATION ${INCLUDE_INSTALL_DIR}) diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 00000000..01b374f0 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,28 @@ +{ + "configurations": [ + { + "name": "WSL-GCC-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeExecutable": "cmake", + "cmakeCommandArgs": "-DEXTERNAL_LIB_DIR_SUFFIX=-${name}", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "linux_x64" ], + "wslPath": "${defaultWSLPath}" + }, + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "-DEXTERNAL_LIB_DIR_SUFFIX=-${name} -DCMAKE_TOOLCHAIN_FILE=${projectDir}\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x64_x64" ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index a8b1ab91..46caea92 100644 --- a/README.md +++ b/README.md @@ -1,389 +1,489 @@ -libemf2svg -========== - -[![Join the chat at https://gitter.im/kakwa/libemf2svg](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kakwa/libemf2svg?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://travis-ci.org/kakwa/libemf2svg.svg?branch=master)](https://travis-ci.org/kakwa/libemf2svg) -[![Coverage Status](https://coveralls.io/repos/github/kakwa/libemf2svg/badge.svg?branch=gcov)](https://coveralls.io/github/kakwa/libemf2svg?branch=gcov) - -MS EMF (Enhanced Metafile) to SVG conversion library. - -Motivation ----------- - -By themselves, EMF/EMF+ files are rare in the wild. However, they are frequently embedded inside other MS file formats. - -This project was started to properly convert Visio stencils (.VSS) to svg and be able to reuse public stencils -in other environments than MS Visio (see [libvisio2svg](https://github.com/kakwa/libvisio2svg)). - -However this project could be use beyond its original motivations to handle emf blobs in any MS formats. - -Output example --------------- - -![Example](https://cdn.rawgit.com/kakwa/libemf2svg/master/goodies/demo-example.svg) - -Dependencies ------------- - -* libiconv -* libpng -* libfontconfig -* libfreetype - -Installing the dependencies on Debian: - -```bash -# compiler -apt-get install gcc g++ -# or -apt-get install clang - -# build deps -apt-get install cmake pkg-config - -# library deps with their headers -apt-get install libpng-dev libc6-dev libfontconfig1-dev libfreetype6-dev zlib1g-dev -``` - -Installing the dependencies on OS X: -```bash -$ brew install argp-standalone -``` - -Installing the dependencies on RHEL/CentOS/Fedora: -```bash -yum install cmake libpng-devel freetype-devel fontconfig-devel gcc-c++ gcc -``` - -Also note that in some rare cases, to properly handle text fields (ETO_GLYPH_INDEX flag), the ttf font -used by the documents must be present and indexed (fontconfig) on your system. - -Building --------- - -Commands to build this project: - -```bash - -# options: -# * [-DUSE_CLANG=on]: use clang instead of gcc -# * [-DSTATIC=on]: build static library -# * [-DDEBUG=on]: compile with debugging symbols -# -# CMAKE_INSTALL_PREFIX is optional, default is /usr/local/ -$ cmake . -DCMAKE_INSTALL_PREFIX=/usr/ - -# compilation -$ make - -# installation -$ make install -``` - -Command line tool ------------------ - -```bash -$ ./emf2svg-conv --help -Usage: emf2svg-conv [OPTION...] -i FILE -o FILE -emf2svg -- Enhanced Metafile to SVG converter - - -h, --height=HEIGHT Max height in px - -i, --input=FILE Input EMF file - -o, --output=FILE Output SVG file - -p, --emfplus Handle EMF+ records - -v, --verbose Produce verbose output - -w, --width=WIDTH Max width in px - -?, --help Give this help list - --usage Give a short usage message - --version Print program version - -V, --version Print emf2svg version - -Mandatory or optional arguments to long options are also mandatory or optional -for any corresponding short options. - -Report bugs to https://github.com/kakwa/libemf2svg/issues. - -# usage example: -$ ./emf2svg-conv -i ./tests/resources/emf/test-037.emf -o example.svg -v -``` - -Library -------- - -Shorten examples: - - -Conversion from EMF to SVG ([complete example here](https://github.com/kakwa/libemf2svg/blob/master/goodies/example.c)): -```C -#include -//[...] -int main(int argc, char *argv[]){ - - /* emf content size */ - size_t emf_size; - /* emf content */ - char * emf_content; - /* svg output string */ - char *svg_out = NULL; - /* svg output length */ - size_t svg_out_len = 0; - - //[...] - - /*************************** options settings **************************/ - - /* allocate the options structure) */ - generatorOptions *options = (generatorOptions *)calloc(1, \ - sizeof(generatorOptions)); - /* debugging flag (prints the emf record in stdout if true) */ - options->verbose = true; - /* emf+ flag (handles emf+ records if true) */ - options->emfplus = true; - /* if a custom xml/svg namespace is needed (keep empty in doubt) */ - options->nameSpace = (char *)"svg"; - /* includes the svg start and stop tags (set to false if the result - * of this call is meant to be used inside another svg) */ - options->svgDelimiter = true; - /* image width in px (set to 0 to use the original emf device width) */ - options->imgWidth = 0; - /* image height in px (set to 0 to use the original emf device height) */ - options->imgHeight = 0; - - /***************************** conversion ******************************/ - - int ret = emf2svg(emf_content, emf_size, &svg_out, &svg_out_len, options); - - /***********************************************************************/ - - //[...] -} -``` - -Check document for EMF+ record presence ([complete example here](https://github.com/kakwa/libemf2svg/blob/master/goodies/check_emfp.c)): -```C -int main(int argc, char *argv[]){ - - /* emf content size */ - size_t emf_size; - /* emf content */ - char * emf_content; - /* svg output string */ - char *svg_out = NULL; - /* svg output length */ - size_t svg_out_len = 0; - - bool emfplus; - int ret = emf2svg_is_emfplus(emf_content, emf_size, &emfplus); - if(emfplus) - fprintf(stdout,"%s contains EMF+ records\n", file_name); -} -``` - -See [./src/conv/emf2svg.cpp](https://github.com/kakwa/libemf2svg/blob/master/src/conv/emf2svg.cpp) for a real life example. - -EMF/EMF+ record type coverage ------------------------------ - -EMF RECORDS: - -| Status | Count | Percent | -|:---------:|:-----:|:-------:| -| Supported | 37 | [ 35%] | -| Partial | 33 | [ 31%] | -| Unused | 2 | [ 1%] | -| Ignored | 33 | [ 31%] | -| Total | 105 | | - -EMF+ RECORDS: - -| Status | Count | Percent | -|:---------:|:-----:|:-------:| -| Supported | 0 | [ 0%] | -| Partial | 0 | [ 0%] | -| Unused | 0 | [ 0%] | -| Ignored | 85 | [ 100%] | -| Total | 85 | | - -ChangeLogs ----------- - -1.1.0: - -* add handling of font index encoding -* add fontconfig dependency -* add freetype dependency -* add common variables LIB_INSTALL_DIR, BIN_INSTALL_DIR, INCLUDE_INSTALL_DIR to set install directories - -1.0.3: - -* Fixing compilation on CentOS 7 (work around argp bug) - -1.0.2: - -* broken release, please don't use - -1.0.1: - -* cleaner handling of memstream on OSX (don't install libmemstream, just embed it) - -1.0.0: - -* better cmake regarding finding dependency libraries (libpng) -* /!\ API break, must pass an additionnal argument to emf2svg function: -```diff ---- a/goodies/old.c -+++ b/goodies/new.c -@@ -22,6 +22,8 @@ int main(int argc, char *argv[]){ - char * emf_content = mmap(0, emf_size, PROT_READ, MAP_PRIVATE, fd, 0); - /* svg output string */ - char *svg_out = NULL; -+ /* svg output length */ -+ size_t svg_out_len; - - /*************************** options settings **************************/ - -@@ -44,7 +46,7 @@ int main(int argc, char *argv[]){ - - /***************************** conversion ******************************/ - -- int ret = emf2svg(emf_content, emf_size, &svg_out, options); -+ int ret = emf2svg(emf_content, emf_size, &svg_out, &svg_out_len, options); - - /***********************************************************************/ -``` -* general cleanup of the project (remove external files not needed) - -0.5.1: - -* fix build on OS X - -0.5.0: - -* add alpha layer handling in bitmap blobs conversion -* add brush patterns - -0.4.0: - -* fix text orientation -* fix origin handling in special case - -0.3.0: - -* completly rework how the origin is calculated, it now takes correctly into account both viewport and window orgs - -0.2.0: - -* code reorganization -* add support for ANGLEARC, EMRSTRETCHBLT, EMRBITBLT and more -* add handling of bitmap, RLE4 and RLE8 image blobs -* add some rough handling of clipping forms -* fix text rendering to not collapse spaces - -0.1.0: - -* first version - -Development ------------ - -General source code organisation: - -* [./src/lib/emf2svg.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/emf2svg.c): API entry point. -* [./src/lib/emf2svg_rec_*](https://github.com/kakwa/libemf2svg/blob/master/src/lib/): EMF record handlers -* [./src/lib/emf2svg_print.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/emf2svg_print.c): EMF record printer (debugging). -* [./src/lib/pmf2svg.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/pmf2svg.c): EMF+ record handler. -* [./src/lib/pmf2svg_print.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/pmf2svg_print.c): EMF+ record printer (debugging). -* [./src/conv/emf2svg.cpp](https://github.com/kakwa/libemf2svg/blob/master/src/conv/emf2svg.cpp): Command line tool. -* [./deps](https://github.com/kakwa/libemf2svg/blob/master/deps): external dependencies. - -Useful links: - -* [MS-EMF](http://msdn.microsoft.com/en-us/library/cc230514.aspx): EMF specifications. -* [MS-EMF+](http://msdn.microsoft.com/en-us/library/cc230724.aspx): EMF+ specifications. -* [MS-WMF](http://msdn.microsoft.com/en-us/library/cc250370.aspx): WMF specifications. -* [GDI](https://msdn.microsoft.com/fr-fr/library/windows/desktop/dd145203(v=vs.85).aspx): GDI specification (clearer than EMF in explaining how it works). -* [SVG](http://www.w3.org/TR/SVG/Overview.html): SVG specifications. - -Testing -------- - -* Stats on the number of emf records covered: - -```bash -$ ./tests/resources/coverage.sh -``` - -* Fuzzing on the library: - -Using American Fuzzy Lop: - - -```bash -# remove big files from test pool -$ mkdir ./tmp -$ find tests/resources/emf -size +1M -name "*.emf" -exec mv {} ./tmp \; - -# compile with afl compiler -$ cmake -DCMAKE_CXX_COMPILER=afl-clang++ -DCMAKE_C_COMPILER=afl-clang . -$ make - -# run afl (see man for more advanced usage) -$ afl-fuzz -i tests/resources/emf -o out/ -t 10000 -- ./emf2svg-conv -i '@@' -o out/ - -# restore the files -mv ./tmp/* tests/resources/emf -``` - -* Check correctness and memory leaks (xmllint and valgrind needed): - -```bash -# options: -n to disable valgrind tests, -v for verbose output -# see -h for complete list of options -$ ./tests/resources/check_correctness.sh #[-n] [-v] - -# generated svg: -$ ls tests/out/test-* -tests/out/test-000.emf.svg tests/out/test-051.emf.svg -[...] -``` - -The emf files used for these checks are located in [./tests/resources/emf/](https://github.com/kakwa/libemf2svg/blob/master/tests/resources/emf/). - -Useful Commands ---------------- - -To build, run on emf test files and visualize (with geeqie): -```bash -$ cmake .&& \ - make &&\ - "./tests/resources/check_correctness.sh" -n &&\ - geeqie "tests/out" -``` - -To check against corrupted emf: -```bash -$ cmake -DDEBUG=ON . &&\ - make &&\ - "./tests/resources/check_correctness.sh" -sxN \ - -e "./tests/resources/emf-corrupted/" -``` - -To print records index in svg as comments: - -```bash -$ cmake -DINDEX=ON . && make -``` - -To reformat/reindent the code (clang-format): - -```bash -$ ./goodies/format -``` - -Contributing ------------- - -Contribution are welcomed. -Nothing special here, it's the usual "fork; commit(s); pull request". -Only one thing however, run `./goodies/format` (clang-format) before the pull request. +libemf2svg +========== +[![Ubuntu-x86_64](https://github.com/metanorma/libemf2svg/actions/workflows/ubuntu-x86_64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/ubuntu-x86_64.yml) [![Ubuntu-aarch64](https://github.com/metanorma/libemf2svg/actions/workflows/ubuntu-aarch64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/ubuntu-aarch64.yml) [![Alpine-x86_64](https://github.com/metanorma/libemf2svg/actions/workflows/alpine-x86_64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/alpine-x86_64.yml) + +[![MacOS-x86_64](https://github.com/metanorma/libemf2svg/actions/workflows/macos-x86_64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/macos-x86_64.yml) [![MacOS-arm64](https://github.com/metanorma/libemf2svg/actions/workflows/macos-arm64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/macos-arm64.yml) [![Windows-x86_64](https://github.com/metanorma/libemf2svg/actions/workflows/windows-x86_64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/windows-x86_64.yml) [![MSys-x86_64](https://github.com/metanorma/libemf2svg/actions/workflows/msys-x86_64.yml/badge.svg?branch=master)](https://github.com/metanorma/libemf2svg/actions/workflows/msys-x86_64.yml) + +[![Coverage Status](https://coveralls.io/repos/github/metanorma/libemf2svg/badge.svg?branch=master)](https://coveralls.io/github/metanorma/libemf2svg?branch=master) + +MS EMF (Enhanced Metafile) to SVG conversion library. + +Motivation +---------- + +By themselves, EMF/EMF+ files are rare in the wild. However, they are frequently embedded inside other MS file formats. + +This project was started to properly convert Visio stencils (.VSS) to svg and be able to reuse public stencils +in other environments than MS Visio (see [libvisio2svg](https://github.com/kakwa/libvisio2svg)). + +However this project could be use beyond its original motivations to handle emf blobs in any MS formats. + +Output example +-------------- + +![Example](https://cdn.rawgit.com/kakwa/libemf2svg/master/goodies/demo-example.svg) + +Dependencies +------------ + +* libiconv +* libpng +* libfontconfig +* libfreetype +* fmem (https://github.com/tamatebako/fmem) -- a cross-platform library for opening memory-backed libc streams +* argp-standalone (https://github.com/tom42/argp-standalone) -- a standalone version of the argp argument parsing functions from glibc, Windows only + +fmem and argp-standalone libraries are integrated as CMake external projects. No additional installation or handling is required. + +Installing the dependencies on Debian: + +```bash +# compiler +apt-get install gcc g++ +# or +apt-get install clang + +# build deps +apt-get install cmake pkg-config + +# library deps with their headers +apt-get install libpng-dev libc6-dev libfontconfig1-dev libfreetype6-dev zlib1g-dev +``` + +Installing the dependencies on macOS: +```bash +$ brew install argp-standalone cmake libpng freetype fontconfig gcc +``` + +Installing the dependencies on RHEL/CentOS/Fedora: +```bash +yum install cmake libpng-devel freetype-devel fontconfig-devel gcc-c++ gcc +``` + +Installing the dependencies on Windows for MSVC native builds +Dependencies are installed by vcpkg package manager. Installation is implemented as a step of CMake configuration procedure. + +Also note that in some rare cases, to properly handle text fields (ETO_GLYPH_INDEX flag), the ttf font +used by the documents must be present and indexed (fontconfig) on your system. + +Building +-------- + +Commands to build this project: + +```bash + +# options: +# * [-DUSE_CLANG=on]: use clang instead of gcc +# * [-DSTATIC=on]: build static library +# * [-DDEBUG=on]: compile with debugging symbols +# * [-DLONLY=on]: build the library only, no demo/test apps +# +# CMAKE_INSTALL_PREFIX is optional, default is /usr/local/ + +# Linux, MacOS +$ cmake . -DCMAKE_INSTALL_PREFIX=/usr/ + +# Windows native (MSVC) build +$ cmake . -DCMAKE_TOOLCHAIN_FILE=$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake + +# Cross-compilation +# This project employs vcpkg (https://github.com/microsoft/vcpkg) to setup cross-compilation environment +$ cmake . -DCMAKE_TOOLCHAIN_FILE=$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET= +# The following triplets are tested in CI: +# * x64-linux (both for Ubuntu and ALpine Linux) +# * arm64-linux (Ubuntu) +# * x64-osx +# * arm64-osx +# * x64-mingw-static + +# compilation +$ make + +# installation +$ make install +``` + +Please note that you cannot use relative pathes when CMAKE_TOOLCHAIN_FILE is specified at cmake command line. You may need to replace +```$(pwd)``` with a reference that is appropriate for your environment. + +Command line tool +----------------- + +```bash +$ ./emf2svg-conv --help +Usage: emf2svg-conv [OPTION...] -i FILE -o FILE +emf2svg -- Enhanced Metafile to SVG converter + + -h, --height=HEIGHT Max height in px + -i, --input=FILE Input EMF file + -o, --output=FILE Output SVG file + -p, --emfplus Handle EMF+ records + -v, --verbose Produce verbose output + -w, --width=WIDTH Max width in px + -?, --help Give this help list + --usage Give a short usage message + --version Print program version + -V, --version Print emf2svg version + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. + +Report bugs to https://github.com/kakwa/libemf2svg/issues. + +# usage example: +$ ./emf2svg-conv -i ./tests/resources/emf/test-037.emf -o example.svg -v +``` + +Library +------- + +Shorten examples: + + +Conversion from EMF to SVG ([complete example here](https://github.com/kakwa/libemf2svg/blob/master/goodies/example.c)): +```C +#include +//[...] +int main(int argc, char *argv[]){ + + /* emf content size */ + size_t emf_size; + /* emf content */ + char * emf_content; + /* svg output string */ + char *svg_out = NULL; + /* svg output length */ + size_t svg_out_len = 0; + + //[...] + + /*************************** options settings **************************/ + + /* allocate the options structure) */ + generatorOptions *options = (generatorOptions *)calloc(1, \ + sizeof(generatorOptions)); + /* debugging flag (prints the emf record in stdout if true) */ + options->verbose = true; + /* emf+ flag (handles emf+ records if true) */ + options->emfplus = true; + /* if a custom xml/svg namespace is needed (keep empty in doubt) */ + options->nameSpace = (char *)"svg"; + /* includes the svg start and stop tags (set to false if the result + * of this call is meant to be used inside another svg) */ + options->svgDelimiter = true; + /* image width in px (set to 0 to use the original emf device width) */ + options->imgWidth = 0; + /* image height in px (set to 0 to use the original emf device height) */ + options->imgHeight = 0; + + /***************************** conversion ******************************/ + + int ret = emf2svg(emf_content, emf_size, &svg_out, &svg_out_len, options); + + /***********************************************************************/ + + //[...] +} +``` + +Check document for EMF+ record presence ([complete example here](https://github.com/kakwa/libemf2svg/blob/master/goodies/check_emfp.c)): +```C +int main(int argc, char *argv[]){ + + /* emf content size */ + size_t emf_size; + /* emf content */ + char * emf_content; + /* svg output string */ + char *svg_out = NULL; + /* svg output length */ + size_t svg_out_len = 0; + + bool emfplus; + int ret = emf2svg_is_emfplus(emf_content, emf_size, &emfplus); + if(emfplus) + fprintf(stdout,"%s contains EMF+ records\n", file_name); +} +``` + +See [./src/conv/emf2svg.cpp](https://github.com/kakwa/libemf2svg/blob/master/src/conv/emf2svg.cpp) for a real life example. + +EMF/EMF+ record type coverage +----------------------------- + +EMF RECORDS: + +| Status | Count | Percent | +|:---------:|:-----:|:-------:| +| Supported | 37 | [ 35%] | +| Partial | 33 | [ 31%] | +| Unused | 2 | [ 1%] | +| Ignored | 33 | [ 31%] | +| Total | 105 | | + +EMF+ RECORDS: + +| Status | Count | Percent | +|:---------:|:-----:|:-------:| +| Supported | 0 | [ 0%] | +| Partial | 0 | [ 0%] | +| Unused | 0 | [ 0%] | +| Ignored | 85 | [ 100%] | +| Total | 85 | | + +ChangeLogs +---------- + +1.7.3: + +* Fixed incorrect handling of polygon fill modes + +1.7.2: + +* vcpkg and GHA scripts update + +1.7.1: + +* added width and heigt attributes for svg even when Y-coordinates are repaired + +1.7.0: + +* refactor build scripts to facilitate better portability and ruby integration + +1.6.0: + +* add arm64 MacOS support (cross-compilation only, no tests) + +1.5.0: + +* add Alpine Linux support + +1.4.0: + +* add arm64 Debian Linux support (cross-compilation only, no tests) + +1.3.1: + +* add MSVC 17 (2022) support + +1.3.0: + +* add MSVC Windows native build + +1.X.X: (forked to metanorma) + +* add support for EMF images without an initial viewport setup +* add handling of EMF images with wrong transformation applied (Wine-generated) + +1.1.0: + +* add handling of font index encoding +* add fontconfig dependency +* add freetype dependency +* add common variables LIB_INSTALL_DIR, BIN_INSTALL_DIR, INCLUDE_INSTALL_DIR to set install directories + +1.0.3: + +* Fixing compilation on CentOS 7 (work around argp bug) + +1.0.2: + +* broken release, please don't use + +1.0.1: + +* cleaner handling of memstream on OSX (don't install libmemstream, just embed it) + +1.0.0: + +* better cmake regarding finding dependency libraries (libpng) +* /!\ API break, must pass an additionnal argument to emf2svg function: +```diff +--- a/goodies/old.c ++++ b/goodies/new.c +@@ -22,6 +22,8 @@ int main(int argc, char *argv[]){ + char * emf_content = mmap(0, emf_size, PROT_READ, MAP_PRIVATE, fd, 0); + /* svg output string */ + char *svg_out = NULL; ++ /* svg output length */ ++ size_t svg_out_len; + + /*************************** options settings **************************/ + +@@ -44,7 +46,7 @@ int main(int argc, char *argv[]){ + + /***************************** conversion ******************************/ + +- int ret = emf2svg(emf_content, emf_size, &svg_out, options); ++ int ret = emf2svg(emf_content, emf_size, &svg_out, &svg_out_len, options); + + /***********************************************************************/ +``` +* general cleanup of the project (remove external files not needed) + +0.5.1: + +* fix build on OS X + +0.5.0: + +* add alpha layer handling in bitmap blobs conversion +* add brush patterns + +0.4.0: + +* fix text orientation +* fix origin handling in special case + +0.3.0: + +* completly rework how the origin is calculated, it now takes correctly into account both viewport and window orgs + +0.2.0: + +* code reorganization +* add support for ANGLEARC, EMRSTRETCHBLT, EMRBITBLT and more +* add handling of bitmap, RLE4 and RLE8 image blobs +* add some rough handling of clipping forms +* fix text rendering to not collapse spaces + +0.1.0: + +* first version + +Development +----------- + +General source code organisation: + +* [./src/lib/emf2svg.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/emf2svg.c): API entry point. +* [./src/lib/emf2svg_rec_*](https://github.com/kakwa/libemf2svg/blob/master/src/lib/): EMF record handlers +* [./src/lib/emf2svg_print.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/emf2svg_print.c): EMF record printer (debugging). +* [./src/lib/pmf2svg.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/pmf2svg.c): EMF+ record handler. +* [./src/lib/pmf2svg_print.c](https://github.com/kakwa/libemf2svg/blob/master/src/lib/pmf2svg_print.c): EMF+ record printer (debugging). +* [./src/conv/emf2svg.cpp](https://github.com/kakwa/libemf2svg/blob/master/src/conv/emf2svg.cpp): Command line tool. +* [./deps](https://github.com/kakwa/libemf2svg/blob/master/deps): external dependencies. + +Useful links: + +* [MS-EMF](http://msdn.microsoft.com/en-us/library/cc230514.aspx): EMF specifications. +* [MS-EMF+](http://msdn.microsoft.com/en-us/library/cc230724.aspx): EMF+ specifications. +* [MS-WMF](http://msdn.microsoft.com/en-us/library/cc250370.aspx): WMF specifications. +* [GDI](https://msdn.microsoft.com/fr-fr/library/windows/desktop/dd145203(v=vs.85).aspx): GDI specification (clearer than EMF in explaining how it works). +* [SVG](http://www.w3.org/TR/SVG/Overview.html): SVG specifications. + +Testing +------- + +* Stats on the number of emf records covered: + +```bash +$ ./tests/resources/coverage.sh +``` + +* Fuzzing on the library: + +Using American Fuzzy Lop: + + +```bash +# remove big files from test pool +$ mkdir ./tmp +$ find tests/resources/emf -size +1M -name "*.emf" -exec mv {} ./tmp \; + +# compile with afl compiler +$ cmake -DCMAKE_CXX_COMPILER=afl-clang++ -DCMAKE_C_COMPILER=afl-clang . +$ make + +# run afl (see man for more advanced usage) +$ afl-fuzz -i tests/resources/emf -o out/ -t 10000 -- ./emf2svg-conv -i '@@' -o out/ + +# restore the files +mv ./tmp/* tests/resources/emf +``` + +* Check correctness and memory leaks (xmllint and valgrind needed): + +```bash +# options: -n to disable valgrind tests, -v for verbose output +# see -h for complete list of options +$ ./tests/resources/check_correctness.sh #[-n] [-v] + +# generated svg: +$ ls tests/out/test-* +tests/out/test-000.emf.svg tests/out/test-051.emf.svg +[...] +``` + +The emf files used for these checks are located in [./tests/resources/emf/](https://github.com/kakwa/libemf2svg/blob/master/tests/resources/emf/). + +Useful Commands +--------------- + +To build, run on emf test files and visualize (with geeqie): +```bash +$ cmake .&& \ + make &&\ + "./tests/resources/check_correctness.sh" -n &&\ + geeqie "tests/out" +``` + +To check against corrupted emf: +```bash +$ cmake -DDEBUG=ON . &&\ + make &&\ + "./tests/resources/check_correctness.sh" -sxN \ + -e "./tests/resources/emf-corrupted/" +``` + +To print records index in svg as comments: + +```bash +$ cmake -DINDEX=ON . && make +``` + +To reformat/reindent the code (clang-format): + +```bash +$ ./goodies/format +``` + + +Y-coordinates repair in EMF files +--------------------------------- + +In EMF coordinates are specified using an origin (`[0,0]` point) located at +the upper-left corner: x-coordinates increase to the right; y-coordinates +increase from top to bottom. + +The SVG coordinate system, on the other hand, uses the same origin (`[0,0]` +point) at the bottom-left corner: x-coordinates increase to the right; but +y-coordinates increase from top to bottom. + +Typically, a simple shift of the y-axis through a single SVG/CSS +transformation is used to transform from EMF coordinates to SVG coordinates. + +However, under certain circumstances some tools (for instance, SparxSystem +Enterprise Architect in Wine) will generate EMF files with malformed +coordinates. These images have an origin at the top-left corner with +y-coordinates increasing from top to bottom, yet these y-coordinates are +inverted (multiplied by `-1`) to simulate a normal EMF look. + +Furthermore, this inversion phenomenon cannot be solved with plain mirroring +as it occurs to all (complex) objects of the hierarchy. For example, text +boxes have only their y-coordinate anchor point mirrored, but the text +direction is set properly. + +This specific layout issue cannot be fixed by a single SVG/CSS +transformation, and therefore the processing code is required to detect and +invert only the affected y-coordinates, while keeping other attributes +intact. + + +Contributing +------------ + +Contribution are welcomed. +Nothing special here, it's the usual "fork; commit(s); pull request". +Only one thing however, run `./goodies/format` (clang-format) before the pull request. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 01717452..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: 1.0.{build} - -os: Visual Studio 2015 - -platform: - - x64 - -configuration: - - Release - -build: - project: C:/projects/libemf2svg/build/emf2svg.sln - -before_build: - - mkdir build - - cd build - - cmake -G "Visual Studio 14 2015 Win64" .. - - ls C:/projects/libemf2svg/build/ - -test_script: - - cmd: pwd - - cmd: ls diff --git a/cmake/FindFontconfig.cmake b/cmake/FindFontconfig.cmake index e6fa81d8..9769a042 100644 --- a/cmake/FindFontconfig.cmake +++ b/cmake/FindFontconfig.cmake @@ -17,7 +17,7 @@ # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products +# 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR @@ -63,7 +63,7 @@ else (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Fontconfig DEFAULT_MSG FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR ) - + mark_as_advanced(FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR) endif (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) diff --git a/cmake/arm64-linux.cmake b/cmake/arm64-linux.cmake new file mode 100644 index 00000000..abf3428d --- /dev/null +++ b/cmake/arm64-linux.cmake @@ -0,0 +1,15 @@ +# CMake Toolchain file for crosscompiling on arm64(aks aarch64) linux. +# Assumed: +# sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu + +# Target operating system name +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +# Names of compilers +set(CMAKE_C_COMPILER "/usr/bin/aarch64-linux-gnu-gcc") +set(CMAKE_CXX_COMPILER "/usr/bin/aarch64-linux-gnu-g++") + +# Where to look for the target environment +set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/cmake/arm64-osx.cmake b/cmake/arm64-osx.cmake new file mode 100644 index 00000000..cf844c5f --- /dev/null +++ b/cmake/arm64-osx.cmake @@ -0,0 +1,11 @@ +# CMake Toolchain file for crosscompiling on arm64(aks aarch64) macos. + +# Target operating system name +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR arm64) + +# Names of compilers +set(CMAKE_C_COMPILER "clang") +set(CMAKE_CXX_COMPILER "clang++") +set(CMAKE_OSX_ARCHITECTURES arm64) +set(CMAKE_OSX_ARCHITECTURES_OPTION -DCMAKE_OSX_ARCHITECTURES=arm64) diff --git a/deps/libUEMF b/deps/libUEMF deleted file mode 120000 index e7c694b3..00000000 --- a/deps/libUEMF +++ /dev/null @@ -1 +0,0 @@ -libUEMF-0.2.5 \ No newline at end of file diff --git a/deps/memstream b/deps/memstream deleted file mode 120000 index f22704c4..00000000 --- a/deps/memstream +++ /dev/null @@ -1 +0,0 @@ -memstream-0.1 \ No newline at end of file diff --git a/deps/memstream-0.1/memstream.c b/deps/memstream-0.1/memstream.c deleted file mode 100644 index 9795ce82..00000000 --- a/deps/memstream-0.1/memstream.c +++ /dev/null @@ -1,211 +0,0 @@ -/* Compile this file and link the object with your program. On a recent - * GNU/Linux machine the object file will be empty. On anything derived from - * 4.4BSD (Darwin, the Three BSDs, etc.) it will contain an implementation of - * open_memstream() as described in the POSIX and Linux manual pages. On - * anything else it will probably cause a compilation error. - * - * ---------------------------------------------------------------------------- - * - * OPEN_MEMSTREAM(3) BSD and Linux Library Functions OPEN_MEMSTREAM(3) - * - * SYNOPSIS - * #include "memstream.h" - * - * FILE *open_memstream(char **bufp, size_t *sizep); - * - * DESCRIPTION - * The open_memstream() function opens a stream for writing to a buffer. - * The buffer is dynamically allocated (as with malloc(3)), and - * automatically grows as required. After closing the stream, the caller - * should free(3) this buffer. - * - * When the stream is closed (fclose(3)) or flushed (fflush(3)), the - * locations pointed to by bufp and sizep are updated to contain, - * respectively, a pointer to the buffer and the current size of the - * buffer. These values remain valid only as long as the caller performs - * no further output on the stream. If further output is performed, then - * the stream must again be flushed before trying to access these - * variables. - * - * A null byte is maintained at the end of the buffer. This byte is not - * included in the size value stored at sizep. - * - * The stream's file position can be changed with fseek(3) or fseeko(3). - * Moving the file position past the end of the data already written fills - * the intervening space with zeros. - * - * RETURN VALUE - * Upon successful completion open_memstream() returns a FILE pointer. - * Otherwise, NULL is returned and errno is set to indicate the error. - * - * CONFORMING TO - * POSIX.1-2008 - * - * ---------------------------------------------------------------------------- - */ - -#include "memstream.h" - -#if _POSIX_C_SOURCE < 200809L - -#include -#include -#include -#include -#include - -#define min(X, Y) (((X) < (Y)) ? (X) : (Y)) - -struct memstream { - int position; - int size; - int capacity; - char *contents; - char **ptr; - size_t *sizeloc; -}; - -#if MEMSTREAM_DEBUG -static void memstream_print(struct memstream *ms) { - printf("memstream %p {", ms); - printf(" %i", ms->position); - printf(" %i", ms->size); - printf(" %i", ms->capacity); - printf(" %p", ms->contents); - printf(" }\n"); -} -#define memstream_info(ARGS) printf ARGS -#else -#define memstream_print(ms) -#define memstream_info(ARGS) -#endif - -#define memstream_check(MS) \ - if (!(MS)->contents) { \ - errno = ENOMEM; \ - return -1; \ - } - -static int memstream_grow(struct memstream *ms, int minsize) { - int newcap = ms->capacity * 2; - memstream_check(ms); - while (newcap <= minsize) - newcap *= 2; - memstream_info(("grow %p to %i\n", ms, newcap)); - ms->contents = realloc(ms->contents, newcap); - if (!ms->contents) - return -1; /* errno == ENOMEM */ - memset(ms->contents + ms->capacity, 0, newcap - ms->capacity); - ms->capacity = newcap; - *ms->ptr = ms->contents; /* size has not changed */ - return 0; -} - -static int memstream_read(void *cookie, char *buf, int count) { - struct memstream *ms = (struct memstream *)cookie; - memstream_check(ms); - int n = min(ms->size - ms->position, count); - memstream_info(("memstream_read %p %i\n", ms, count)); - if (n < 1) - return 0; - memcpy(buf, ms->contents, n); - ms->position += n; - memstream_print(ms); - return n; -} - -static int memstream_write(void *cookie, const char *buf, int count) { - struct memstream *ms = (struct memstream *)cookie; - memstream_check(ms); - if (ms->capacity <= ms->position + count) - if (memstream_grow(ms, ms->position + count) < 0) /* errno == ENOMEM */ - return -1; - memcpy(ms->contents + ms->position, buf, count); - memstream_info(("memstream_write %p %i\n", ms, count)); - ms->position += count; - if (ms->size < ms->position) - *ms->sizeloc = ms->size = ms->position; - memstream_print(ms); - assert(ms->size < ms->capacity); - assert(ms->contents[ms->size] == 0); - return count; -} - -static fpos_t memstream_seek(void *cookie, fpos_t offset, int whence) { - struct memstream *ms = (struct memstream *)cookie; - fpos_t pos = 0; - memstream_check(ms); - memstream_info(("memstream_seek %p %i %i\n", ms, (int)offset, whence)); - switch (whence) { - case SEEK_SET: - pos = offset; - break; - case SEEK_CUR: - pos = ms->position + offset; - break; - case SEEK_END: - pos = ms->size + offset; - break; - default: - errno = EINVAL; - return -1; - } - if (pos >= ms->capacity) - memstream_grow(ms, pos); - ms->position = pos; - if (ms->size < ms->position) - *ms->sizeloc = ms->size = ms->position; - memstream_print(ms); - memstream_info(("=> %i\n", (int)pos)); - assert(ms->size < ms->capacity && ms->contents[ms->size] == 0); - return pos; -} - -static int memstream_close(void *cookie) { - struct memstream *ms = (struct memstream *)cookie; - if (!ms->contents) { - free(ms); - errno = ENOMEM; - return -1; - } - ms->size = min(ms->size, ms->position); - *ms->ptr = ms->contents; - *ms->sizeloc = ms->size; - assert(ms->size < ms->capacity); - ms->contents[ms->size] = 0; - free(ms); - return 0; -} - -FILE *open_memstream(char **ptr, size_t *sizeloc) { - if (ptr && sizeloc) { - struct memstream *ms = calloc(1, sizeof(struct memstream)); - FILE *fp = 0; - if (!ms) - return 0; /* errno == ENOMEM */ - ms->position = ms->size = 0; - ms->capacity = 4096; - ms->contents = calloc(ms->capacity, 1); - if (!ms->contents) { - free(ms); - return 0; - } /* errno == ENOMEM */ - ms->ptr = ptr; - ms->sizeloc = sizeloc; - memstream_print(ms); - fp = funopen(ms, memstream_read, memstream_write, memstream_seek, - memstream_close); - if (!fp) { - free(ms->contents); - free(ms); - return 0; /* errno set by funopen */ - } - *ptr = ms->contents; - *sizeloc = ms->size; - return fp; - } - errno = EINVAL; - return 0; -} - -#endif /* _POSIX_C_SOURCE < 200809L */ diff --git a/deps/memstream-0.1/memstream.h b/deps/memstream-0.1/memstream.h deleted file mode 100644 index 27b5482d..00000000 --- a/deps/memstream-0.1/memstream.h +++ /dev/null @@ -1,11 +0,0 @@ -#if defined(__linux__) -#include -#endif - -#include - -#if _POSIX_C_SOURCE < 200809L - -FILE *open_memstream(char **ptr, size_t *sizeloc); - -#endif /* _POSIX_C_SOURCE < 200809L */ diff --git a/deps/memstream-0.1/test.c b/deps/memstream-0.1/test.c deleted file mode 100644 index dc8c08f1..00000000 --- a/deps/memstream-0.1/test.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "memstream.h" - -#include -#include - -int main() { - char *buffer = 0; - size_t size = 0; - FILE *fp = open_memstream(&buffer, &size); - int i; - for (i = 0; i < 10240; ++i) { - static char c = 42; - fflush(fp); - assert(size == i); - fwrite(&c, 1, 1, fp); - } - fclose(fp); - assert(size == 10240); - free(buffer); - fp = open_memstream(&buffer, &size); - fprintf(fp, "This is a test of memstream, from main at %p.\n", main); - fclose(fp); - fputs(buffer, stdout); - free(buffer); - return 0; -} diff --git a/inc/emf2svg.h b/inc/emf2svg.h index 4709b665..5c86e84d 100644 --- a/inc/emf2svg.h +++ b/inc/emf2svg.h @@ -25,6 +25,9 @@ typedef struct { } generatorOptions; // convert function +#ifdef _MSC_VER +__declspec(dllexport) +#endif int emf2svg(char *contents, size_t length, char **out, size_t *out_length, generatorOptions *options); diff --git a/inc/emf2svg_img_utils.h b/inc/emf2svg_img_utils.h index ea034a56..a630f8da 100644 --- a/inc/emf2svg_img_utils.h +++ b/inc/emf2svg_img_utils.h @@ -5,7 +5,6 @@ #include #include #include -#include #define RLE_MARK 0x00 #define RLE_EOL 0x00 diff --git a/inc/emf2svg_print.h b/inc/emf2svg_print.h index 7daaf3ef..4e9acac3 100644 --- a/inc/emf2svg_print.h +++ b/inc/emf2svg_print.h @@ -22,9 +22,6 @@ extern "C" { #include #include #include -#ifdef DARWIN -#include -#endif #define KNRM "\x1B[0m" #define KRED "\x1B[31m" diff --git a/inc/emf2svg_private.h b/inc/emf2svg_private.h index a86fbd7b..34098415 100644 --- a/inc/emf2svg_private.h +++ b/inc/emf2svg_private.h @@ -7,9 +7,6 @@ extern "C" { #include #include #include -#ifdef DARWIN -#include -#endif #define KNRM "\x1B[0m" #define KRED "\x1B[31m" @@ -34,7 +31,7 @@ extern "C" { #define FLAG_RESET verbose_printf("%s", KNRM); #define returnOutOfEmf(a) \ - if (checkOutOfEMF(states, (intptr_t)(a))) { \ + if (checkOutOfEMF(states, (uintptr_t)(a))) { \ return; \ } #define returnOutOfOTIndex(a) \ @@ -287,10 +284,16 @@ typedef struct { double viewPortOrgY; double viewPortExX; double viewPortExY; + // true if viewport extent has been set, false otherwise + bool viewPortExSet; double windowOrgX; double windowOrgY; double windowExX; double windowExY; + // true if windows extent has been set, false otherwise + bool windowExSet; + // true if we are fixing layout problems from Wine-generated EMF + bool fixBrokenYTransform; double pxPerMm; uint16_t MapMode; // Text orientation @@ -339,7 +342,7 @@ void point16_draw(drawingStates *states, U_POINT16 pt, FILE *out); void point_draw(drawingStates *states, U_POINT pt, FILE *out); void freePathStack(pathStack *stack); // checks if address is outside the memory containing the emf file -bool checkOutOfEMF(drawingStates *states, intptr_t address); +bool checkOutOfEMF(drawingStates *states, uintptr_t address); // checks if index is greater than the object table size bool checkOutOfOTIndex(drawingStates *states, int64_t index); void fill_draw(drawingStates *states, FILE *out, bool *filled, bool *stroked); diff --git a/inc/memstream.h b/inc/memstream.h deleted file mode 120000 index 8d433a79..00000000 --- a/inc/memstream.h +++ /dev/null @@ -1 +0,0 @@ -../deps/memstream/memstream.h \ No newline at end of file diff --git a/inc/uemf.h b/inc/uemf.h deleted file mode 120000 index 01b924ca..00000000 --- a/inc/uemf.h +++ /dev/null @@ -1 +0,0 @@ -../deps/libUEMF/uemf.h \ No newline at end of file diff --git a/inc/uemf_endian.h b/inc/uemf_endian.h deleted file mode 120000 index f90ec0d8..00000000 --- a/inc/uemf_endian.h +++ /dev/null @@ -1 +0,0 @@ -../deps/libUEMF/uemf_endian.h \ No newline at end of file diff --git a/inc/uemf_utf.h b/inc/uemf_utf.h deleted file mode 120000 index ce272464..00000000 --- a/inc/uemf_utf.h +++ /dev/null @@ -1 +0,0 @@ -../deps/libUEMF/uemf_utf.h \ No newline at end of file diff --git a/inc/upmf.h b/inc/upmf.h deleted file mode 120000 index 5c348d13..00000000 --- a/inc/upmf.h +++ /dev/null @@ -1 +0,0 @@ -../deps/libUEMF/upmf.h \ No newline at end of file diff --git a/deps/libUEMF-0.2.5/COPYING b/libUEMF-0.2.5/COPYING similarity index 100% rename from deps/libUEMF-0.2.5/COPYING rename to libUEMF-0.2.5/COPYING diff --git a/deps/libUEMF-0.2.5/README b/libUEMF-0.2.5/README similarity index 100% rename from deps/libUEMF-0.2.5/README rename to libUEMF-0.2.5/README diff --git a/deps/libUEMF-0.2.5/readwmf.c b/libUEMF-0.2.5/readwmf.c similarity index 100% rename from deps/libUEMF-0.2.5/readwmf.c rename to libUEMF-0.2.5/readwmf.c diff --git a/deps/libUEMF-0.2.5/uemf.c b/libUEMF-0.2.5/uemf.c similarity index 99% rename from deps/libUEMF-0.2.5/uemf.c rename to libUEMF-0.2.5/uemf.c index 94d05ffb..e16a35ef 100644 --- a/deps/libUEMF-0.2.5/uemf.c +++ b/libUEMF-0.2.5/uemf.c @@ -10,7 +10,7 @@ and so not usable. If something goes wrong a NULL pointer is returned and recsize is set to 0. - Compile with "U_VALGRIND" defined defined to enable code which lets valgrind + Compile with "U_VALGRIND" defined to enable code which lets valgrind check each record for uninitialized data. diff --git a/deps/libUEMF-0.2.5/uemf.h b/libUEMF-0.2.5/uemf.h similarity index 100% rename from deps/libUEMF-0.2.5/uemf.h rename to libUEMF-0.2.5/uemf.h diff --git a/deps/libUEMF-0.2.5/uemf_endian.c b/libUEMF-0.2.5/uemf_endian.c similarity index 100% rename from deps/libUEMF-0.2.5/uemf_endian.c rename to libUEMF-0.2.5/uemf_endian.c diff --git a/deps/libUEMF-0.2.5/uemf_endian.h b/libUEMF-0.2.5/uemf_endian.h similarity index 100% rename from deps/libUEMF-0.2.5/uemf_endian.h rename to libUEMF-0.2.5/uemf_endian.h diff --git a/deps/libUEMF-0.2.5/uemf_print.c b/libUEMF-0.2.5/uemf_print.c similarity index 100% rename from deps/libUEMF-0.2.5/uemf_print.c rename to libUEMF-0.2.5/uemf_print.c diff --git a/deps/libUEMF-0.2.5/uemf_print.h b/libUEMF-0.2.5/uemf_print.h similarity index 100% rename from deps/libUEMF-0.2.5/uemf_print.h rename to libUEMF-0.2.5/uemf_print.h diff --git a/deps/libUEMF-0.2.5/uemf_safe.c b/libUEMF-0.2.5/uemf_safe.c similarity index 100% rename from deps/libUEMF-0.2.5/uemf_safe.c rename to libUEMF-0.2.5/uemf_safe.c diff --git a/deps/libUEMF-0.2.5/uemf_safe.h b/libUEMF-0.2.5/uemf_safe.h similarity index 100% rename from deps/libUEMF-0.2.5/uemf_safe.h rename to libUEMF-0.2.5/uemf_safe.h diff --git a/deps/libUEMF-0.2.5/uemf_utf.c b/libUEMF-0.2.5/uemf_utf.c similarity index 100% rename from deps/libUEMF-0.2.5/uemf_utf.c rename to libUEMF-0.2.5/uemf_utf.c diff --git a/deps/libUEMF-0.2.5/uemf_utf.h b/libUEMF-0.2.5/uemf_utf.h similarity index 100% rename from deps/libUEMF-0.2.5/uemf_utf.h rename to libUEMF-0.2.5/uemf_utf.h diff --git a/deps/libUEMF-0.2.5/upmf.c b/libUEMF-0.2.5/upmf.c similarity index 100% rename from deps/libUEMF-0.2.5/upmf.c rename to libUEMF-0.2.5/upmf.c diff --git a/deps/libUEMF-0.2.5/upmf.h b/libUEMF-0.2.5/upmf.h similarity index 100% rename from deps/libUEMF-0.2.5/upmf.h rename to libUEMF-0.2.5/upmf.h diff --git a/deps/libUEMF-0.2.5/upmf_print.c b/libUEMF-0.2.5/upmf_print.c similarity index 100% rename from deps/libUEMF-0.2.5/upmf_print.c rename to libUEMF-0.2.5/upmf_print.c diff --git a/deps/libUEMF-0.2.5/upmf_print.h b/libUEMF-0.2.5/upmf_print.h similarity index 100% rename from deps/libUEMF-0.2.5/upmf_print.h rename to libUEMF-0.2.5/upmf_print.h diff --git a/deps/libUEMF-0.2.5/uwmf.c b/libUEMF-0.2.5/uwmf.c similarity index 100% rename from deps/libUEMF-0.2.5/uwmf.c rename to libUEMF-0.2.5/uwmf.c diff --git a/deps/libUEMF-0.2.5/uwmf.h b/libUEMF-0.2.5/uwmf.h similarity index 100% rename from deps/libUEMF-0.2.5/uwmf.h rename to libUEMF-0.2.5/uwmf.h diff --git a/deps/libUEMF-0.2.5/uwmf_endian.c b/libUEMF-0.2.5/uwmf_endian.c similarity index 100% rename from deps/libUEMF-0.2.5/uwmf_endian.c rename to libUEMF-0.2.5/uwmf_endian.c diff --git a/deps/libUEMF-0.2.5/uwmf_endian.h b/libUEMF-0.2.5/uwmf_endian.h similarity index 100% rename from deps/libUEMF-0.2.5/uwmf_endian.h rename to libUEMF-0.2.5/uwmf_endian.h diff --git a/deps/libUEMF-0.2.5/uwmf_print.c b/libUEMF-0.2.5/uwmf_print.c similarity index 100% rename from deps/libUEMF-0.2.5/uwmf_print.c rename to libUEMF-0.2.5/uwmf_print.c diff --git a/deps/libUEMF-0.2.5/uwmf_print.h b/libUEMF-0.2.5/uwmf_print.h similarity index 100% rename from deps/libUEMF-0.2.5/uwmf_print.h rename to libUEMF-0.2.5/uwmf_print.h diff --git a/patches/argp/CMakeLists.txt.patch b/patches/argp/CMakeLists.txt.patch new file mode 100644 index 00000000..c1fe1212 --- /dev/null +++ b/patches/argp/CMakeLists.txt.patch @@ -0,0 +1,21 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 94b10d5..81a33b0 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -126,6 +126,13 @@ endif() + + add_subdirectory(src) + +-if((IS_MAIN_PROJECT OR argp-standalone_BUILD_TESTING) AND BUILD_TESTING) +- add_subdirectory(test) +-endif() ++include(GNUInstallDirs) ++ ++install(TARGETS argp-standalone ++ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ++ ++install(FILES ++ ${PROJECT_SOURCE_DIR}/include/argp-standalone/argp.h ++ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/src/conv/emf2svg.cpp b/src/conv/emf2svg.cpp index 684bf064..a4d5072d 100644 --- a/src/conv/emf2svg.cpp +++ b/src/conv/emf2svg.cpp @@ -26,7 +26,10 @@ using namespace std; -const char *argp_program_version = E2S_VERSION; +#define __STRINGIFY__(V) __STR__(V) +#define __STR__(V) #V + +const char *argp_program_version = __STRINGIFY__(E2S_VERSION); const char *argp_program_bug_address = "https://github.com/kakwa/libemf2svg/issues"; @@ -100,34 +103,52 @@ int main(int argc, char *argv[]) { arguments.input = NULL; arguments.output = NULL; arguments.emfplus = 0; + argp_parse(&argp, argc, argv, 0, 0, &arguments); if (arguments.version) { - std::cout << "emf2svg version: " << E2S_VERSION << "\n"; + std::cout << "emf2svg version: " + << __STRINGIFY__(E2S_VERSION) + << std::endl; return 0; } if (arguments.input == NULL) { std::cerr << "[ERROR] " - << "Missing --input=FILE argument\n"; + << "Missing --input=FILE argument" + << std::endl; return 1; } if (arguments.output == NULL) { std::cerr << "[ERROR] " - << "Missing --output=FILE argument\n"; + << "Missing --output=FILE argument" + << std::endl; return 1; } - std::ifstream in(arguments.input); + std::ifstream in(arguments.input, ios::binary); if (!in.is_open()) { std::cerr << "[ERROR] " << "Impossible to open input file '" << arguments.input - << "'\n"; + << std::endl; + return 1; + } + + in.seekg(0, std::ios::end); + size_t size = in.tellg(); + char* contents = new char[size]; + if (!contents) { + std::cerr << "[ERROR] Cannot allocate input buffer" << std::endl; + in.close(); return 1; } - std::string contents((std::istreambuf_iterator(in)), - std::istreambuf_iterator()); + in.seekg(0, std::ios::beg); + in.read(contents, size); + in.close(); + + // std::string contents((std::istreambuf_iterator(in)), + // std::istreambuf_iterator()); char *svg_out = NULL; size_t svg_len; @@ -139,25 +160,25 @@ int main(int argc, char *argv[]) { options->svgDelimiter = true; options->imgWidth = arguments.width; options->imgHeight = arguments.height; - int ret = emf2svg((char *)contents.c_str(), contents.size(), &svg_out, - &svg_len, options); + int ret = emf2svg(contents, size, &svg_out, &svg_len, options); if (ret != 0) { std::ofstream out(arguments.output); if (!out.is_open()) { std::cerr << "[ERROR] " - << "Impossible to open output file '" << arguments.output - << "'\n"; + << "Impossible to open output file '" << arguments.output + << std::endl; + delete[] contents; + free(svg_out); + free(options); return 1; } out << std::string(svg_out); out.close(); } + delete[] contents; free(svg_out); free(options); - in.close(); - if (ret == 0) - return 1; - return 0; + return (ret==0)?1:0; } /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ diff --git a/src/lib/emf2svg.c b/src/lib/emf2svg.c index a655d490..dd00bc73 100644 --- a/src/lib/emf2svg.c +++ b/src/lib/emf2svg.c @@ -11,9 +11,7 @@ extern "C" { #include #include #include -#ifdef DARWIN -#include "memstream.c" -#endif +#include int U_emf_onerec_is_emfp(const char *contents, const char *blimit, int recnum, size_t off, bool *ret) { @@ -646,7 +644,7 @@ int U_emf_onerec_draw(const char *contents, const char *blimit, int recnum, return (size); } -int emf2svg(char *contents, size_t length, char **out, size_t *out_length, +int emf2svg(char *contents, size_t length, char ** fm_out, size_t * fm_out_length, generatorOptions *options) { size_t off = 0; size_t result; @@ -655,6 +653,10 @@ int emf2svg(char *contents, size_t length, char **out, size_t *out_length, PU_ENHMETARECORD pEmr; char *blimit; FILE *stream; + fmem fm; + fmem_init(&fm); + *fm_out = NULL; + *fm_out_length = 0; #if U_BYTE_SWAP // This is a Big Endian machine, EMF data is Little Endian @@ -662,7 +664,10 @@ int emf2svg(char *contents, size_t length, char **out, size_t *out_length, #endif drawingStates *states = (drawingStates *)calloc(1, sizeof(drawingStates)); + states->fixBrokenYTransform = false; states->verbose = options->verbose; + states->viewPortExSet = false; + states->windowExSet = false; states->emfplus = options->emfplus; states->imgWidth = options->imgWidth; states->imgHeight = options->imgHeight; @@ -687,7 +692,7 @@ int emf2svg(char *contents, size_t length, char **out, size_t *out_length, blimit = contents + length; int err = 1; - stream = open_memstream(out, out_length); + stream = fmem_open(&fm, "w"); if (stream == NULL) { if (states->verbose) { printf("Failed to allocate output stream\n"); @@ -746,7 +751,7 @@ int emf2svg(char *contents, size_t length, char **out, size_t *out_length, FLAG_RESET; setTransformIdentity(states); - // continu only if no previous errors + // continue only if no previous errors if (err == 0) { OK = 0; } else { @@ -769,8 +774,7 @@ int emf2svg(char *contents, size_t length, char **out, size_t *out_length, pEmr = (PU_ENHMETARECORD)(contents + off); - result = - U_emf_onerec_draw(contents, blimit, recnum, off, stream, states); + result = U_emf_onerec_draw(contents, blimit, recnum, off, stream, states); if (result == (size_t)-1 || states->Error) { if (states->verbose) { printf( @@ -795,8 +799,23 @@ int emf2svg(char *contents, size_t length, char **out, size_t *out_length, freeEmfImageLibrary(states); free(states); - fflush(stream); - fclose(stream); + if (stream) { + fflush(stream); + void* out; + fmem_mem(&fm, &out, fm_out_length); + if (*fm_out_length) { + *fm_out = (char*)malloc(*fm_out_length+1); + } + if (*fm_out) { + memcpy((void*)(*fm_out), out, *fm_out_length); + (*fm_out)[*fm_out_length] = 0; + } + else { + err = 0; + } + fclose(stream); + fmem_term(&fm); + } return err; } diff --git a/src/lib/emf2svg_img_utils.c b/src/lib/emf2svg_img_utils.c index 17140244..ee8e9f5c 100644 --- a/src/lib/emf2svg_img_utils.c +++ b/src/lib/emf2svg_img_utils.c @@ -9,11 +9,7 @@ extern "C" { #include #include #include - -#ifdef DARWIN -#include -#endif - +#include #include RGBAPixel *pixel_at(RGBABitmap *bitmap, int x, int y) { @@ -40,8 +36,12 @@ float get_pixel_size(uint32_t colortype) { } /* Attempts to save PNG to file; returns 0 on success, non-zero on error. */ -int rgb2png(RGBABitmap *bitmap, char **out, size_t *size) { - FILE *fp = open_memstream(out, size); +int rgb2png(RGBABitmap *bitmap, char ** fm_out, size_t * fm_out_length) { + fmem fm; + fmem_init(&fm); + *fm_out = NULL; + *fm_out_length = 0; + FILE* fp = fmem_open(&fm, "w"); if (fp == NULL) { return -1; } @@ -53,9 +53,6 @@ int rgb2png(RGBABitmap *bitmap, char **out, size_t *size) { png_byte **row_pointers = NULL; bool alpha_channel_empty = true; - if (fp == NULL) - return -1; - /* Initialize the write struct. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { @@ -138,16 +135,26 @@ int rgb2png(RGBABitmap *bitmap, char **out, size_t *size) { /* Finish writing. */ png_destroy_write_struct(&png_ptr, &info_ptr); fflush(fp); + void* out; + fmem_mem(&fm, &out, fm_out_length); + if (*fm_out_length) { + *fm_out = (char*)malloc(*fm_out_length+1); + } + if (*fm_out) { + memcpy((void*)(*fm_out), out, *fm_out_length); + (*fm_out)[*fm_out_length] = 0; + } fclose(fp); - return 0; + fmem_term(&fm); + return (*fm_out)?0:-1; } // uncompress RLE8 to get bitmap (section 3.1.6.2 [MS-WMF].pdf) RGBBitmap rle8ToRGB8(RGBBitmap img) { FILE *stream; bool decode = true; - char *out; - size_t size; + fmem fm; + fmem_init(&fm); RGBBitmap out_img; out_img.size = 0; @@ -166,8 +173,9 @@ RGBBitmap rle8ToRGB8(RGBBitmap img) { return out_img; } - stream = open_memstream(&out, &size); + stream = fmem_open(&fm, "w"); if (stream == NULL) { + fmem_term(&fm); return out_img; } @@ -176,7 +184,6 @@ RGBBitmap rle8ToRGB8(RGBBitmap img) { // check against potential overflow if ((bm + 2) > end || x > MAX_BMP_WIDTH || y > MAX_BMP_HEIGHT) { fclose(stream); - free(out); return out_img; }; switch (bm[0]) { @@ -198,7 +205,7 @@ RGBBitmap rle8ToRGB8(RGBBitmap img) { // offset handling, pad with (off.x + off.y * width) zeros if ((bm + 3) > end) { fclose(stream); - free(out); + fmem_term(&fm); return out_img; }; for (int i = 0; i < (bm[2] + img.width * bm[3]); i++) @@ -215,7 +222,7 @@ RGBBitmap rle8ToRGB8(RGBBitmap img) { bm_next = bm + 1 + ((bm[1] + 1) / 2) * 2; if (bm_next > end) { fclose(stream); - free(out); + fmem_term(&fm); return out_img; }; for (int i = 2; i < bm[1] + 2; i++) @@ -238,15 +245,27 @@ RGBBitmap rle8ToRGB8(RGBBitmap img) { } } // pad the rest of the bitmap - for (int i = 0; i < (((int)img.width - x) + (int)img.width * y); i++) + for (size_t i = 0; i < ((img.width - x) + img.width * y); i++) fputc(0x00, stream); fflush(stream); + void* out; + fmem_mem(&fm, &out, &out_img.size); + if (out_img.size) { + out_img.pixels = (RGBPixel*)malloc(out_img.size+1); + } + if (out_img.pixels) { + memcpy((void*)out_img.pixels, out, out_img.size); + ((char*)(out_img.pixels))[out_img.size] = 0; + out_img.width = img.width; + out_img.height = img.height; + } + else { + out_img.size = 0; + } + fclose(stream); - out_img.pixels = (RGBPixel *)out; - out_img.size = size; - out_img.width = img.width; - out_img.height = img.height; + fmem_term(&fm); return out_img; } @@ -293,8 +312,8 @@ int e2s_get_DIB_params(PU_BITMAPINFO Bmi, const U_RGBQUAD **ct, uint32_t *numCt, RGBBitmap rle4ToRGB(RGBBitmap img) { FILE *stream; bool decode = true; - char *out; - size_t size; + fmem fm; + fmem_init(&fm); RGBBitmap out_img; out_img.size = 0; @@ -313,7 +332,7 @@ RGBBitmap rle4ToRGB(RGBBitmap img) { return out_img; } - stream = open_memstream(&out, &size); + stream = fmem_open(&fm, "w"); if (stream == NULL) { return out_img; } @@ -335,7 +354,6 @@ RGBBitmap rle4ToRGB(RGBBitmap img) { // check against potential overflow if ((bm + 2) > end || x > MAX_BMP_WIDTH || y > MAX_BMP_HEIGHT) { fclose(stream); - free(out); return out_img; }; switch (bm[0]) { @@ -363,7 +381,6 @@ RGBBitmap rle4ToRGB(RGBBitmap img) { // offset handling, pad with (off.x + off.y * width) zeros if ((bm + 3) > end) { fclose(stream); - free(out); return out_img; }; @@ -392,7 +409,6 @@ RGBBitmap rle4ToRGB(RGBBitmap img) { bm_next = bm + (bm[1] / 2) + 2; if (bm_next > end) { fclose(stream); - free(out); return out_img; }; for (int i = 2; i < (bm[1] / 2) + 2; i++) { @@ -450,15 +466,27 @@ RGBBitmap rle4ToRGB(RGBBitmap img) { fputc(upper | 0x00, stream); } // end of line, pad the rest of the line with zeros - for (int i = 0; i < (((int)img.width - x + (int)img.width * y) / 2); i++) + for (size_t i = 0; i < ((img.width - x + img.width * y) / 2); i++) fputc(0x00, stream); fflush(stream); + void* out; + fmem_mem(&fm, &out, &out_img.size); + if (out_img.size) { + out_img.pixels = (RGBPixel*)malloc(out_img.size+1); + } + if (out_img.pixels) { + memcpy((void*)out_img.pixels, out, out_img.size); + ((char*)(out_img.pixels))[out_img.size] = 0; + out_img.width = img.width; + out_img.height = img.height; + } + else { + out_img.size = 0; + } + fclose(stream); - out_img.pixels = (RGBPixel *)out; - out_img.size = size; - out_img.width = img.width; - out_img.height = img.height; + fmem_term(&fm); return out_img; } diff --git a/src/lib/emf2svg_print.c b/src/lib/emf2svg_print.c index cd8ca41c..76e8ebb6 100644 --- a/src/lib/emf2svg_print.c +++ b/src/lib/emf2svg_print.c @@ -42,7 +42,7 @@ extern "C" { */ void fill_print(drawingStates *states) { - switch (states->currentDeviceContext.fill_mode) { + switch (states->currentDeviceContext.fill_polymode) { case (U_ALTERNATE): verbose_printf(" Fill Rule: U_ALTERNATE\n"); break; diff --git a/src/lib/emf2svg_rec_control.c b/src/lib/emf2svg_rec_control.c index cb9bcd53..036e3b1d 100644 --- a/src/lib/emf2svg_rec_control.c +++ b/src/lib/emf2svg_rec_control.c @@ -60,6 +60,45 @@ void U_EMRHEADER_draw(const char *contents, FILE *out, drawingStates *states) { double ratioXY = (double)(pEmr->rclBounds.right - pEmr->rclBounds.left) / (double)(pEmr->rclBounds.bottom - pEmr->rclBounds.top); + /** + In EMF coordinates are specified using an origin (`[0,0]` point) located at + the upper-left corner: x-coordinates increase to the right; y-coordinates + increase from top to bottom. + + The SVG coordinate system, on the other hand, uses the same origin (`[0,0]` + point) at the bottom-left corner: x-coordinates increase to the right; but + y-coordinates increase from top to bottom. + + Typically, a simple shift of the y-axis through a single SVG/CSS + transformation is used to transform from EMF coordinates to SVG coordinates. + + However, under certain circumstances some tools (for instance, SparxSystem + Enterprise Architect in Wine) will generate EMF files with malformed + coordinates. These images have an origin at the top-left corner with + y-coordinates increasing from top to bottom, yet these y-coordinates are + inverted (multiplied by `-1`) to simulate a normal EMF look. + + Furthermore, this inversion phenomenon cannot be solved with plain mirroring + as it occurs to all (complex) objects of the hierarchy. For example, text + boxes have only their y-coordinate anchor point mirrored, but the text + direction is set properly. + + This specific layout issue cannot be fixed by a single SVG/CSS + transformation, and therefore the processing code is required to detect and + invert only the affected y-coordinates, while keeping other attributes + intact. + + Condition: top and bottom points are at different sides of the X axis. We + assume this condition indicates that this image was generated with broken + transformation (possibly on Wine) and a fix is required as described above. + + While this may be a weak assumption, nothing better came to mind. + **/ + + if (pEmr->rclBounds.top*pEmr->rclBounds.bottom < 0) { + states->fixBrokenYTransform = true; + } + if ((states->imgHeight != 0) && (states->imgWidth != 0)) { double tmpWidth = states->imgHeight * ratioXY; double tmpHeight = states->imgWidth / ratioXY; @@ -84,6 +123,7 @@ void U_EMRHEADER_draw(const char *contents, FILE *out, drawingStates *states) { states->scaling = states->imgWidth / (double)abs(pEmr->rclBounds.right - pEmr->rclBounds.left); + // remember reference point of the output DC states->RefX = (double)pEmr->rclBounds.left; states->RefY = (double)pEmr->rclBounds.top; @@ -97,18 +137,26 @@ void U_EMRHEADER_draw(const char *contents, FILE *out, drawingStates *states) { "\n"); fprintf(out, "<%ssvg version=\"1.1\" ", states->nameSpaceString); fprintf(out, "xmlns=\"http://www.w3.org/2000/svg\" "); - fprintf(out, "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "); + fprintf(out, "xmlns:xlink=\"http://www.w3.org/1999/xlink\""); if ((states->nameSpace != NULL) && (strlen(states->nameSpace) != 0)) { - fprintf(out, "xmlns:%s=\"http://www.w3.org/2000/svg\" ", + fprintf(out, "xmlns:%s=\"http://www.w3.org/2000/svg\"", states->nameSpace); } - fprintf(out, "width=\"%.4f\" height=\"%.4f\">\n", states->imgWidth, + // https://www.w3.org/TR/SVG2/coords.html + if (states->fixBrokenYTransform) { + fprintf(out, " width=\"%.4f\" height=\"%.4f\">\n", + states->imgWidth + 1, + states->imgHeight + 1); + fprintf(out, "<%sg transform=\"translate(0.0000, 0.00 00)\">\n", + states->nameSpaceString); + } else { + fprintf(out, " width=\"%.4f\" height=\"%.4f\">\n", states->imgWidth, states->imgHeight); + fprintf(out, "<%sg transform=\"translate(%.4f, %.4f)\">\n", + states->nameSpaceString, -1.0 * states->RefX * states->scaling, + -1.0 * states->RefY * states->scaling); + } } - - fprintf(out, "<%sg transform=\"translate(%.4f, %.4f)\">\n", - states->nameSpaceString, -1.0 * states->RefX * states->scaling, - -1.0 * states->RefY * states->scaling); } #ifdef __cplusplus diff --git a/src/lib/emf2svg_rec_drawing.c b/src/lib/emf2svg_rec_drawing.c index 3d3ef141..2d8c82f6 100644 --- a/src/lib/emf2svg_rec_drawing.c +++ b/src/lib/emf2svg_rec_drawing.c @@ -480,6 +480,13 @@ void U_EMRRECTANGLE_draw(const char *contents, FILE *out, POINT_D dim; dim.x = RB.x - LT.x; dim.y = RB.y - LT.y; + + // If fixBrokenYTransform is true, we have to flip points on the Y axis. + if (states->fixBrokenYTransform && dim.y < 0) { + dim.y = -dim.y; + LT.y -= dim.y; + } + fprintf(out, "<%srect x=\"%.4f\" y=\"%.4f\" width=\"%.4f\" height=\"%.4f\" ", states->nameSpaceString, LT.x, LT.y, dim.x, dim.y); @@ -506,15 +513,19 @@ void U_EMRROUNDRECT_draw(const char *contents, FILE *out, POINT_D RB = point_cal(states, (double)pEmr->rclBox.right, (double)pEmr->rclBox.bottom); POINT_D dim; - POINT_D round; dim.x = RB.x - LT.x; dim.y = RB.y - LT.y; + + // If fixBrokenYTransform is true, we have to flip points on the Y axis. + if (states->fixBrokenYTransform && dim.y < 0) { + dim.y = -dim.y; + LT.y -= dim.y; + } + fprintf(out, "<%srect x=\"%.4f\" y=\"%.4f\" width=\"%.4f\" height=\"%.4f\" ", states->nameSpaceString, LT.x, LT.y, dim.x, dim.y); - round = point_cal(states, (double)pEmr->szlCorner.cx, - (double)pEmr->szlCorner.cy); - fprintf(out, "rx=\"%.4f\" ry=\"%.4f\" ", round.x, round.y); + fprintf(out, "rx=\"%.4f\" ry=\"%.4f\" ", scaleX(states, (double)pEmr->szlCorner.cx), scaleX(states, (double)pEmr->szlCorner.cy)); bool filled = false; bool stroked = false; fill_draw(states, out, &filled, &stroked); diff --git a/src/lib/emf2svg_rec_state_record.c b/src/lib/emf2svg_rec_state_record.c index ec9401b4..45192e79 100644 --- a/src/lib/emf2svg_rec_state_record.c +++ b/src/lib/emf2svg_rec_state_record.c @@ -228,6 +228,7 @@ void U_EMRSETVIEWPORTEXTEX_draw(const char *contents, FILE *out, states->viewPortExX = (double)pEmr->szlExtent.cx; states->viewPortExY = (double)pEmr->szlExtent.cy; + states->viewPortExSet = true; } void U_EMRSETVIEWPORTORGEX_draw(const char *contents, FILE *out, drawingStates *states) { @@ -249,6 +250,7 @@ void U_EMRSETWINDOWEXTEX_draw(const char *contents, FILE *out, PU_EMRSETWINDOWEXTEX pEmr = (PU_EMRSETVIEWPORTEXTEX)(contents); states->windowExX = (double)pEmr->szlExtent.cx; states->windowExY = (double)pEmr->szlExtent.cy; + states->windowExSet = true; } void U_EMRSETWINDOWORGEX_draw(const char *contents, FILE *out, drawingStates *states) { diff --git a/src/lib/emf2svg_utils.c b/src/lib/emf2svg_utils.c index fe778b0d..57659ca8 100644 --- a/src/lib/emf2svg_utils.c +++ b/src/lib/emf2svg_utils.c @@ -11,7 +11,6 @@ extern "C" { #include #include #include -#include #include #include #include @@ -32,12 +31,14 @@ void U_swap4(void *ul, unsigned int count); points. \param rect U_RECTL object */ +#ifndef _MSC_VER double _dsign(double v) { if (v >= 0) return 1; else return -1; } +#endif void arc_circle_draw(const char *contents, FILE *out, drawingStates *states) { PU_EMRANGLEARC pEmr = (PU_EMRANGLEARC)(contents); @@ -147,7 +148,7 @@ void basic_stroke(drawingStates *states, FILE *out) { color_stroke(states, out); width_stroke(states, out, states->currentDeviceContext.stroke_width); } -bool checkOutOfEMF(drawingStates *states, intptr_t address) { +bool checkOutOfEMF(drawingStates *states, uintptr_t address) { if (address > states->endAddress) { states->Error = true; return true; @@ -509,10 +510,18 @@ double scaleX(drawingStates *states, double x) { scalingX = states->pxPerMm / 1440 * mmPerInch * 1; break; case U_MM_ISOTROPIC: - scalingX = states->viewPortExX / states->windowExX; + if (states->windowExSet && states->viewPortExSet) { + scalingX = states->viewPortExX / states->windowExX; + } else { + scalingX = 1.0; + } break; case U_MM_ANISOTROPIC: - scalingX = states->viewPortExX / states->windowExX; + if (states->windowExSet && states->viewPortExSet) { + scalingX = states->viewPortExX / states->windowExX; + } else { + scalingX = 1.0; + } break; default: scalingX = 1.0; @@ -550,10 +559,18 @@ double scaleY(drawingStates *states, double y) { scalingY = states->pxPerMm / 1440 * mmPerInch * 1; break; case U_MM_ISOTROPIC: - scalingY = states->viewPortExX / states->windowExX; + if (states->windowExSet && states->viewPortExSet) { + scalingY = states->viewPortExX / states->windowExX; + } else { + scalingY = 1.0; + } break; case U_MM_ANISOTROPIC: - scalingY = states->viewPortExY / states->windowExY; + if (states->windowExSet && states->viewPortExSet) { + scalingY = states->viewPortExY / states->windowExY; + } else { + scalingY = 1.0; + } break; default: scalingY = 1.0; @@ -602,7 +619,11 @@ POINT_D point_cal(drawingStates *states, double x, double y) { scalingY = states->pxPerMm / 1440 * mmPerInch * -1; break; case U_MM_ISOTROPIC: - scalingX = states->viewPortExX / states->windowExX; + if (states->windowExSet && states->viewPortExSet) { + scalingX = states->viewPortExX / states->windowExX; + } else { + scalingX = 1.0; + } scalingY = scalingX; windowOrgX = states->windowOrgX; windowOrgY = states->windowOrgY; @@ -610,8 +631,14 @@ POINT_D point_cal(drawingStates *states, double x, double y) { viewPortOrgY = states->viewPortOrgY; break; case U_MM_ANISOTROPIC: - scalingX = states->viewPortExX / states->windowExX; - scalingY = states->viewPortExY / states->windowExY; + if (states->windowExSet && states->viewPortExSet) { + scalingX = states->viewPortExX / states->windowExX; + scalingY = states->viewPortExY / states->windowExY; + } else { + scalingX = 1.0; + // If fixBrokenYTransform is true, we have to flip the Y axis. + scalingY = (states->fixBrokenYTransform ? -1.0 : 1.0); + } windowOrgX = states->windowOrgX; windowOrgY = states->windowOrgY; viewPortOrgX = states->viewPortOrgX; @@ -1119,17 +1146,17 @@ static int cmap_rev(const char *fpath, cmap_collection *rcmap) { // printf("font %s | name %s | style %s\n", fpath, face->family_name, // face->style_name); // printf("%d\n", face->num_charmaps); - int rmap_s = 1000; + FT_UInt rmap_s = 1000; rcmap->uni = calloc(rmap_s, sizeof(uint32_t)); FT_Select_Charmap(face, FT_ENCODING_UNICODE); FT_UInt gindex = 0; FT_ULong charcode = FT_Get_First_Char(face, &gindex); while (gindex != 0) { if (gindex >= rmap_s) { - int old_rmap_s = rmap_s; + FT_UInt old_rmap_s = rmap_s; rmap_s += 1000; uint32_t *tmp = realloc(rcmap->uni, sizeof(uint32_t) * rmap_s); - for (int i = old_rmap_s; i < rmap_s; i++) + for (FT_UInt i = old_rmap_s; i < rmap_s; i++) tmp[i] = 0; // free(rcmap->uni); rcmap->uni = tmp; @@ -1401,65 +1428,69 @@ void text_convert(char *in, size_t size_in, char **out, size_t *size_out, states->currentDeviceContext.font_family, states->currentDeviceContext.font_weight, states->currentDeviceContext.font_italic); - switch (states->currentDeviceContext.font_charset) { - case U_HEBREW_CHARSET: - case U_ARABIC_CHARSET: - /* with Utf-8 strings, the strings must always be - * stored in logical order, not visual order. - * Unicode Bidirectional (bidi) Algorithm does the work - * of rendering the text properly. - * So we reverse the text to be in logical order. - * However, this seems imcomplete, - * Right to Left ordering can also be set in - * ExtTextOutOptions (EMR_*TEXTOUT* records) and EMR_SETLAYOUT - * records, and it's completely ignored here. - * FIXME this is probably to simplistic. - */ - reverse_utf8((char *)string, *size_out); - break; - case U_ANSI_CHARSET: - case U_DEFAULT_CHARSET: - case U_SYMBOL_CHARSET: - case U_SHIFTJIS_CHARSET: - case U_HANGUL_CHARSET: - case U_GB2312_CHARSET: - case U_CHINESEBIG5_CHARSET: - case U_GREEK_CHARSET: - case U_TURKISH_CHARSET: - case U_BALTIC_CHARSET: - case U_RUSSIAN_CHARSET: - case U_EASTEUROPE_CHARSET: - case U_THAI_CHARSET: - case U_JOHAB_CHARSET: - case U_MAC_CHARSET: - case U_OEM_CHARSET: - case U_VISCII_CHARSET: - case U_TCVN_CHARSET: - case U_KOI8_CHARSET: - case U_ISO3_CHARSET: - case U_ISO4_CHARSET: - case U_ISO10_CHARSET: - case U_CELTIC_CHARSET: - default: - break; + if (ret==0 && string!=NULL) { + switch (states->currentDeviceContext.font_charset) { + case U_HEBREW_CHARSET: + case U_ARABIC_CHARSET: + /* with Utf-8 strings, the strings must always be + * stored in logical order, not visual order. + * Unicode Bidirectional (bidi) Algorithm does the work + * of rendering the text properly. + * So we reverse the text to be in logical order. + * However, this seems imcomplete, + * Right to Left ordering can also be set in + * ExtTextOutOptions (EMR_*TEXTOUT* records) and EMR_SETLAYOUT + * records, and it's completely ignored here. + * FIXME this is probably to simplistic. + */ + reverse_utf8((char*)string, *size_out); + break; + case U_ANSI_CHARSET: + case U_DEFAULT_CHARSET: + case U_SYMBOL_CHARSET: + case U_SHIFTJIS_CHARSET: + case U_HANGUL_CHARSET: + case U_GB2312_CHARSET: + case U_CHINESEBIG5_CHARSET: + case U_GREEK_CHARSET: + case U_TURKISH_CHARSET: + case U_BALTIC_CHARSET: + case U_RUSSIAN_CHARSET: + case U_EASTEUROPE_CHARSET: + case U_THAI_CHARSET: + case U_JOHAB_CHARSET: + case U_MAC_CHARSET: + case U_OEM_CHARSET: + case U_VISCII_CHARSET: + case U_TCVN_CHARSET: + case U_KOI8_CHARSET: + case U_ISO3_CHARSET: + case U_ISO4_CHARSET: + case U_ISO10_CHARSET: + case U_CELTIC_CHARSET: + default: + break; + } } break; default: if (checkOutOfEMF(states, - (intptr_t)((intptr_t)in + (intptr_t)size_in))) { + (uintptr_t)((uintptr_t)in + (uintptr_t)size_in))) { string = NULL; - return; } - - string = (uint8_t *)calloc((size_in + 1), 1); - strncpy((char *)string, in, size_in); - *size_out = size_in; + else { + string = (uint8_t *)calloc((size_in + 1), 1); + strncpy((char *)string, in, size_in); + *size_out = size_in; + } + break; } + if (ret != 0) string = NULL; if (string == NULL) { - return; + return; } int i = 0; diff --git a/src/lib/memstream.c b/src/lib/memstream.c deleted file mode 120000 index da4bd512..00000000 --- a/src/lib/memstream.c +++ /dev/null @@ -1 +0,0 @@ -../../deps/memstream/memstream.c \ No newline at end of file diff --git a/src/lib/pmf2svg.c b/src/lib/pmf2svg.c index e81da3ae..4216f0c9 100644 --- a/src/lib/pmf2svg.c +++ b/src/lib/pmf2svg.c @@ -1691,8 +1691,8 @@ int U_PMR_OBJECT_draw(const char *contents, const char *blimit, U_PMF_OBJECTTYPEENUMERATION_draw(otype, out, states); if (ntype) { if (checkOutOfEMF(states, - (intptr_t)((intptr_t)Data + - (intptr_t)Header.DataSize - 4)) || + (uintptr_t)((uintptr_t)Data + + (uintptr_t)Header.DataSize - 4)) || ((int64_t)Header.DataSize - 4) < 0) { status = 0; } else { @@ -1702,7 +1702,7 @@ int U_PMR_OBJECT_draw(const char *contents, const char *blimit, } } else { if (checkOutOfEMF(states, - (intptr_t)Data + (intptr_t)Header.DataSize)) { + (uintptr_t)Data + (uintptr_t)Header.DataSize)) { status = 0; } else { U_OA_append( diff --git a/src/lib/pmf2svg_print.c b/src/lib/pmf2svg_print.c index a45dc3ee..81d725c7 100644 --- a/src/lib/pmf2svg_print.c +++ b/src/lib/pmf2svg_print.c @@ -3431,8 +3431,8 @@ int U_PMR_OBJECT_print(const char *contents, const char *blimit, verbose_printf(" ContinueB:%c", (ntype ? 'Y' : 'N')); if (ntype) { if (checkOutOfEMF(states, - (intptr_t)((intptr_t)Data + - (intptr_t)Header.DataSize - 4)) || + (uintptr_t)((uintptr_t)Data + + (uintptr_t)Header.DataSize - 4)) || ((int64_t)Header.DataSize - 4) < 0) { status = 0; verbose_printf(" corrupt record\n"); @@ -3445,7 +3445,7 @@ int U_PMR_OBJECT_print(const char *contents, const char *blimit, } } else { if (checkOutOfEMF(states, - (intptr_t)Data + (intptr_t)Header.DataSize)) { + (uintptr_t)Data + (uintptr_t)Header.DataSize)) { status = 0; verbose_printf(" corrupt record\n"); } else { diff --git a/src/lib/uemf.c b/src/lib/uemf.c deleted file mode 120000 index bca94086..00000000 --- a/src/lib/uemf.c +++ /dev/null @@ -1 +0,0 @@ -../../deps/libUEMF/uemf.c \ No newline at end of file diff --git a/src/lib/uemf_endian.c b/src/lib/uemf_endian.c deleted file mode 120000 index 88ff31c8..00000000 --- a/src/lib/uemf_endian.c +++ /dev/null @@ -1 +0,0 @@ -../../deps/libUEMF/uemf_endian.c \ No newline at end of file diff --git a/src/lib/uemf_utf.c b/src/lib/uemf_utf.c deleted file mode 120000 index 32325c72..00000000 --- a/src/lib/uemf_utf.c +++ /dev/null @@ -1 +0,0 @@ -../../deps/libUEMF/uemf_utf.c \ No newline at end of file diff --git a/src/lib/upmf.c b/src/lib/upmf.c deleted file mode 120000 index 791a548c..00000000 --- a/src/lib/upmf.c +++ /dev/null @@ -1 +0,0 @@ -../../deps/libUEMF/upmf.c \ No newline at end of file diff --git a/tests/resources/check_correctness.sh b/tests/resources/check_correctness.sh index 22578e56..ef905459 100755 --- a/tests/resources/check_correctness.sh +++ b/tests/resources/check_correctness.sh @@ -18,7 +18,7 @@ STOPONERROR="no" help(){ cat <] [-s] [-n] +usage: `basename $0` [-h] [-v] [-e ] [-s] [-n] Script checking memleaks, segfault and svg correctness of emf2svg-conv @@ -39,7 +39,7 @@ EOF while getopts ":hnNxrvse:" opt; do case $opt in - h) + h) help ;; n) @@ -89,7 +89,7 @@ cd $ABSPATH . ./colors.sh rm -rf $OUTDIR mkdir -p $OUTDIR -CMD="`$RL -f ../../emf2svg-conv`" +CMD="`$RL -f ../../build/emf2svg-conv`" OUTDIR=`$RL -f $OUTDIR` DTD=`$RL -f ./svg11-flat.dtd` for emf in `find $EMFDIR -type f -name "*.emf" |sort` diff --git a/tests/resources/check_truncate.sh b/tests/resources/check_truncate.sh index a9011bf4..6127caf9 100755 --- a/tests/resources/check_truncate.sh +++ b/tests/resources/check_truncate.sh @@ -13,7 +13,7 @@ burn_in_hell(){ while [ $counter1 -lt $MAX ] do tmp_emf=`mktemp -p ../out/` - cp emf/`ls emf |shuf -n 1` ${tmp_emf} + cp emf/`ls emf |shuf -n 1` ${tmp_emf} counter2=0 while [ $counter2 -lt $MAX ] do diff --git a/tests/resources/colors.sh b/tests/resources/colors.sh index 17aa9e9c..189fdfe3 100644 --- a/tests/resources/colors.sh +++ b/tests/resources/colors.sh @@ -1,5 +1,5 @@ RCol='\33[0m' # Text Reset - + # Regular Bold Underline High Intensity Bla='\33[0;30m'; BBla='\33[1;30m'; UBla='\33[4;30m'; IBla='\33[0;90m'; Red='\33[0;31m'; BRed='\33[1;31m'; URed='\33[4;31m'; IRed='\33[0;91m'; diff --git a/tests/resources/emf-ea/EA-test-file-001.emf b/tests/resources/emf-ea/EA-test-file-001.emf new file mode 100644 index 00000000..fc340de2 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-001.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-002.emf b/tests/resources/emf-ea/EA-test-file-002.emf new file mode 100644 index 00000000..b01855c1 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-002.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-003.emf b/tests/resources/emf-ea/EA-test-file-003.emf new file mode 100644 index 00000000..e1f0ccc2 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-003.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-004.emf b/tests/resources/emf-ea/EA-test-file-004.emf new file mode 100644 index 00000000..7e5f28a2 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-004.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-005.emf b/tests/resources/emf-ea/EA-test-file-005.emf new file mode 100644 index 00000000..93c0fd95 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-005.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-006.emf b/tests/resources/emf-ea/EA-test-file-006.emf new file mode 100644 index 00000000..c29e2fa4 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-006.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-007.emf b/tests/resources/emf-ea/EA-test-file-007.emf new file mode 100644 index 00000000..4d598332 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-007.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-008.emf b/tests/resources/emf-ea/EA-test-file-008.emf new file mode 100644 index 00000000..2eb177f5 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-008.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-009.emf b/tests/resources/emf-ea/EA-test-file-009.emf new file mode 100644 index 00000000..294de73b Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-009.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-010.emf b/tests/resources/emf-ea/EA-test-file-010.emf new file mode 100644 index 00000000..144d5e65 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-010.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-011.emf b/tests/resources/emf-ea/EA-test-file-011.emf new file mode 100644 index 00000000..bf1a2631 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-011.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-012.emf b/tests/resources/emf-ea/EA-test-file-012.emf new file mode 100644 index 00000000..a5d109af Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-012.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-013.emf b/tests/resources/emf-ea/EA-test-file-013.emf new file mode 100644 index 00000000..8dc8d659 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-013.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-014.emf b/tests/resources/emf-ea/EA-test-file-014.emf new file mode 100644 index 00000000..ec5bdb9a Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-014.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-015.emf b/tests/resources/emf-ea/EA-test-file-015.emf new file mode 100644 index 00000000..3a13240d Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-015.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-020.emf b/tests/resources/emf-ea/EA-test-file-020.emf new file mode 100644 index 00000000..ebc1f9db Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-020.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-030.emf b/tests/resources/emf-ea/EA-test-file-030.emf new file mode 100644 index 00000000..be2d521d Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-030.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-031.emf b/tests/resources/emf-ea/EA-test-file-031.emf new file mode 100644 index 00000000..7665da1d Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-031.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-032.emf b/tests/resources/emf-ea/EA-test-file-032.emf new file mode 100644 index 00000000..9904ef98 Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-032.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-101.emf b/tests/resources/emf-ea/EA-test-file-101.emf new file mode 100755 index 00000000..5d92ca6e Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-101.emf differ diff --git a/tests/resources/emf-ea/EA-test-file-102.emf b/tests/resources/emf-ea/EA-test-file-102.emf new file mode 100755 index 00000000..4d955c6c Binary files /dev/null and b/tests/resources/emf-ea/EA-test-file-102.emf differ diff --git a/tests/resources/lcheck.sh b/tests/resources/lcheck.sh new file mode 100755 index 00000000..2272fc7d --- /dev/null +++ b/tests/resources/lcheck.sh @@ -0,0 +1,89 @@ +#! /bin/bash +# +# Copyright (c) 2022 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -o errexit -o pipefail -o noclobber -o nounset + +# Checks referenced shared libraries +check_shared_libs() { + expected_size="${#expected[@]}" + actual_size="${#actual[@]}" + assertTrue "The number of references to shared libraries does not meet our expectations" "[ $expected_size -ge $actual_size ]" + + for exp in "${expected[@]}"; do + for i in "${!actual[@]}"; do + if [[ "${actual[i]}" == *"$exp"* ]]; then + unset 'actual[i]' + fi + done + done + + for unexp in "${actual[@]}"; do + echo "Unxpected reference to shared library $unexp" + done + + assertEquals "Unxpected references to shared libraries" 0 "${#actual[@]}" +} + +test_linkage() { + echo "==> References to shared libraries test" + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + expected=("linux-vdso.so" "libpthread.so" "libc.so" "libm.so" "ld-linux-x86-64.so") + readarray -t actual < <(ldd "$probe.so") + assertEquals "readarray -t actual < <(ldd "$probe.so") failed" 0 "${PIPESTATUS[0]}" + check_shared_libs + elif [[ "$OSTYPE" == "linux-musl"* ]]; then + expected=("libc.musl-x86_64.so" "ld-musl-x86_64.so") + readarray -t actual < <(ldd "$probe.so") + assertEquals "readarray -t actual < <(ldd $probe.so) failed" 0 "${PIPESTATUS[0]}" + check_shared_libs + elif [[ "$OSTYPE" == "darwin"* ]]; then + expected=("libSystem.B.dylib" "libiconv.2.dylib" "libcharset.1.dylib" "libemf2svg.1.dylib" "libemf2svg.dylib") + readarray -t actual < <(otool -L "$probe.dylib") + assertEquals "readarray -t actual < <(otool -L $probe.dylib) failed" 0 "${PIPESTATUS[0]}" + check_shared_libs "${expected[@]}" + elif [[ "$OSTYPE" == "msys"* ]]; then + expected=("ntdll.dll" "KERNEL32.DLL" "KERNELBASE.dll" "msvcrt.dll" "libgcc_s_seh-1.dll" "libwinpthread-1.dll" "ucrtbase.dll" + "advapi32.dll" "sechost.dll" "bcrypt.dll" "RPCRT4.dll" "CRYPTBASE.DLL" "bcryptPrimitives.dll") + readarray -t actual < <(ldd "$probe.dll") + assertEquals "readarray -t actual < <(ldd "$probe.dll") failed" 0 "${PIPESTATUS[0]}" + check_shared_libs + else + echo "... unknown - $OSTYPE ... skipping" + fi +} +# ...................................................................... +# main +DIR0="$( cd "$( dirname "$0" )" && pwd )" +DIR1="${DIR_ROOT:="$DIR0/../.."}" +DIR_ROOT="$( cd "$DIR1" && pwd )" + +DIR_TESTS="$( cd "$DIR0/.." && pwd)" + +echo "Running libemf2svg linkage tests" +probe="$DIR_ROOT/build/libemf2svg" +# shellcheck source=/dev/null +. "$DIR_TESTS"/shunit2/shunit2 diff --git a/vcpkg b/vcpkg new file mode 160000 index 00000000..e105a86c --- /dev/null +++ b/vcpkg @@ -0,0 +1 @@ +Subproject commit e105a86c97b8ab13d4dbe3f7bd9ebc17ece31634 diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..81cd1726 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,11 @@ +{ + "name": "libemf2svg", + "version-string": "1.7.3", + "dependencies": [ + "libxml2", + "libpng", + "libiconv", + "freetype", + "fontconfig" + ] +} \ No newline at end of file