diff options
| author | kx <kx@radix-linux.su> | 2026-10-17 16:39:23 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-17 16:39:23 +0300 |
| commit | ec1891d58b9514b621725620453cec0e31e18389 (patch) | |
| tree | 6721f92c0afae3d933343b4a573ac8e5389827c6 | |
| parent | 73d23259a690cd29c0ac74a4de963e23ecfebfac (diff) | |
| download | svgc-trunk.tar.xz | |
43 files changed, 6024 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6904bf8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ + +autom4te.cache + +Makefile +Makefile.in +config.h +config.h.in +config.log +config.status +compile +config.guess +config.sub +configure +install-sh +ltmain.sh +missing +stamp-h1 +aclocal.m4 +depcomp +test-driver + +.deps/ +.libs/ + +m4/*.m4 + +*.o +*~ @@ -0,0 +1,4 @@ + +Authors of SVGC (in chronological order of initial contribution) + +Andrey V.Kosteltsev Main author diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..a4438b3 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,75 @@ +2026-10-17 Andrey V. Kosteltsev + + * Version 1.1.0 is the first public release of SVGC, + +2026-10-13 Andrey V. Kosteltsev + + * src/svgc.c, src/svgc.h: Add __svg_sheet_add_script_file(), + SVG_SCRIPT definitions, and shared inline source handling for CSS + and ECMAScript. Preserve safe UTF-8/XML validation and CDATA output. + * tests/Makefile.am, tests/test-script-file.c: Add script regression + tests alongside unchanged CSS tests. + * examples/script-file: Add interactive demonstration with JavaScript + source, C generator, Makefile and view.html using an SVG object. + * configure.ac, version.h, version.c, Makefile.am, NEWS: Release + SVGC 1.0.9. Preserve Libtool version-info 1:0:0. + +2026-10-13 Andrey V. Kosteltsev + + * src/svgc.c, src/svgc.h: Add sheet-local CSS file definitions, + UTF-8/XML input validation, CDATA-safe serialization and cleanup. + * configure.ac, version.h, version.c, NEWS, tests/Makefile.am, + tests/test-css-file.c: Release SVGC 1.0.8 with CSS regression tests. + Preserve Libtool ABI 1:0:0. + +2026-10-13 Andrey V. Kosteltsev + + * configure.ac, acsite.m4: Update the Autotools configuration, + canonical build/host/target initialization and Bash setup. + * svgc.pc.in, Makefile.am: Add pkg-config metadata generation + and installation. + * _make_config/_make-version-c.sh: Convert the existing comments + to UTF-8, preserving the script's operation. + * configure.ac, version.h, version.c, NEWS, Makefile.am: + Release SVGC 1.0.7; preserve Libtool ABI 1:0:0. + +2026-10-13 Andrey V. Kosteltsev + + * acsite.m4, configure.ac: Define the Libtool interface version in + AC_SVGC_LT_VERSION_INFO([1:0:0]) and substitute its components. + * src/Makefile.am, version.h: Use matching Libtool version numbers; + preserve the libsvgc.so.1 ABI soname. + +2026-10-13 Andrey V. Kosteltsev + + * src/svgc.c, src/svgc-dlist.c: Convert K&R function definitions to + prototype-style C definitions; retain behavior and public ABI. + * configure.ac, acsite.m4: Restore readable configure headlines, + section messages and the post-configuration help text. + * config.h.in: Regenerate for updated configure.ac. + * Makefile.am, version.h, version.c: Release 1.0.6. + +2026-10-13 Andrey V. Kosteltsev + + * configure.ac, Makefile.am: Modernize Autotools setup for GCC and GNU Make. + * svgc-config: Generate a libdir-aware installation configuration script. + * svgc.m4: Repair the link sanity test used by consumer projects. + * Build system: Preserve existing SVGC C API and library ABI. + +2011-01-09 Andrey V.Kosteltsev <kosteltsev@gmail.com> + + * src/svgc.h, src/svgc.c: radialGradient element is added. + +2011-01-09 Andrey V.Kosteltsev <kosteltsev@gmail.com> + + * src/svgc.h, src/svgc.c: linearGradient element is added. + +2011-01-08 Andrey V.Kosteltsev <kosteltsev@gmail.com> + + * src/Makefile.am: New revision. + * version.h: New revision. + * src/svgc.h, src/svgc.c: The rx, ry attributes are added to rect element. + +2010-12-21 Andrey V.Kosteltsev <kosteltsev@gmail.com> + + * all-files: The first wersion of SVG C Library. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..063a85b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,14 @@ + +SUBDIRS = svgc-config src examples tests + +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = config AUTHORS COPYING.LIB ChangeLog NEWS README \ + bootstrap m4/README version.h version.c svgc.m4 acsite.m4 + + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = svgc.pc + +aclocaldir = $(datadir)/aclocal +aclocal_DATA = svgc.m4 @@ -0,0 +1,43 @@ +SVGC 1.1.0 (2026-10-17) + + Version 1.1.0 is the first public release of SVGC. + +SVGC 1.0.9 (2026-10-13) + + Add __svg_sheet_add_script_file() for sheet-local ECMAScript sources. + Like the CSS file API, user files contain only language source text; + SVGC adds <script type="text/javascript"> and XML-safe CDATA in defs. + Share encoding validation, file loading, storage and CDATA serialization + with CSS without changing the existing CSS behavior. + Include a script regression test and an interactive standalone example. + Keep Libtool ABI version-info 1:0:0 and existing SVG primitives. + +SVGC 1.0.8 (2026-10-13) + + Add __svg_sheet_add_css_file() to embed a UTF-8 stylesheet in the + SVG sheet <defs> as text/css CDATA. CSS is read at registration time. + Preserve the CSS text, including CDATA terminators, as XML-safe output. + Leave external font files and their url(...) paths untouched. + Preserve existing primitives and Libtool ABI 1:0:0. + +SVGC 1.0.7 (2026-10-13) + + Preserve the author's updated Autotools configuration and configure + presentation, including canonical host/target selection and Bash setup. + Install the svgc.pc pkg-config metadata with the library. + Convert legacy comments in the version-generation script to UTF-8. + Keep the existing C API and Libtool ABI 1:0:0 unchanged. + +SVGC 1.0.6 (2026-10-13) + + Compatibility update for GNU C23 with -Wall -Wextra -Werror. + Restore configure headlines/sections with acsite.m4 and config.h. + Preserve the public API and library ABI. + +SVGC 1.0.5 (2026-10-13) + + Build-system stabilization. Existing public C API and coordinate + conventions are unchanged. The built-in examples are retained. + + +Andrey V.Kosteltsev @@ -0,0 +1,36 @@ + +SVGC — SVG C Library + +SVGC is a C library for programmatically creating Scalable Vector Graphics (SVG) documents. + +The library provides functions for drawing geometric primitives, paths, text, gradients, +and styled graphical objects. It is intended for scientific visualization, technical diagrams, +mathematical illustrations, and reproducible vector graphics. + +SVGC generates standard SVG documents that can be displayed in web browsers, edited with +SVG-compatible applications, or converted to vector PDF for use in scientific publications. + +SVGC uses GNU Autotools. + +Building and Installation: + +./configure --prefix=/usr +make +make check +make install + +The installation prefix can be changed as needed. + +Using the Library: + +SVGC provides the public header svgc.h and a shared library. + +Compiler and linker options can be obtained using pkg-config: + +pkg-config --cflags --libs svgc + +The source distribution includes examples demonstrating the library's main features. + +License: + +See the COPYING.LIB file for license information. diff --git a/acsite.m4 b/acsite.m4 new file mode 100644 index 0000000..583462d --- /dev/null +++ b/acsite.m4 @@ -0,0 +1,142 @@ + +dnl ============================================================ +dnl We define the macro GLIBC_PROVIDES to do an AC_PROVIDE for +dnl each macro which appears in configure.ac before the sysdep +dnl configure scripts are run. Each sysdep configure.ac does +dnl SVGC_PROVIDES first, to avoid any AC_REQUIREs or +dnl AC_BEFOREs duplicating their code. +dnl ============================================================ +define([SVGC_PROVIDES], [dnl +AC_PROVIDE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_PROVIDE([AC_CONFIG_SUBDIRS])dnl +AC_PROVIDE([_AS_ECHO_N_PREPARE])dnl +AC_PROVIDE([_AS_ECHO_PREPARE])dnl +AC_PROVIDE([_AS_CR_PREPARE])dnl +AC_PROVIDE([_AS_TR_SH_PREPARE])dnl +AC_PROVIDE([_AS_VAR_ARITH_PREPARE])dnl +AC_PROVIDE([AC_PROG_INSTALL])dnl +AC_PROVIDE([AC_PROG_CC])dnl +AC_PROVIDE([AC_PROG_CPP])dnl +AC_PROVIDE([_AS_PATH_SEPARATOR_PREPARE])dnl +AC_PROVIDE([_AS_TEST_PREPARE])dnl +AC_PROVIDE([_AS_BASENAME_PREPARE])dnl +AC_PROVIDE([_AS_ME_PREPARE])dnl +AC_PROVIDE([_AS_LINENO_PREPARE])dnl +AC_PROVIDE([AS_SHELL_FN_as_fn_set_status])dnl +AC_PROVIDE([AS_SHELL_FN_as_fn_exit])dnl +AC_PROVIDE([AS_SHELL_FN_as_fn_arith])dnl +AC_PROVIDE([AS_SHELL_FN_ac_fn_c_try_compile])dnl +define([AS_MESSAGE_LOG_FD],5)dnl +define([AS_MESSAGE_FD],6)dnl +dnl Ripped out of AS_INIT, which does more cruft we do not want. +m4_wrap([m4_divert_pop([BODY])[]]) +m4_divert_push([BODY])[]dnl +dnl End of ripped out of AS_INIT. +# This file is generated from configure.ac by Autoconf. DO NOT EDIT! +define([_AC_LANG], [C])dnl +])dnl + +dnl ============================================================ +dnl Force configure to run under /bin/bash +dnl ============================================================ +m4_define([AC_SVGC_REQUIRE_BASH], [dnl +m4_divert_push([M4SH-SANITIZE])dnl + +if test ! -x /bin/bash; then + echo "configure: error: /bin/bash is required" >&2 + exit 1 +fi + +CONFIG_SHELL=/bin/bash +export CONFIG_SHELL + +m4_divert_pop([M4SH-SANITIZE])dnl +])dnl + +dnl ============================================================ +dnl Support for Configuration Headers +dnl +dnl configure.ac: +dnl AC_SVGC_HEADLINE(<short-name>, <long-name>, +dnl <vers-var>, <copyright>) +dnl +dnl See Autoconf manual paragraph 8.3.3, Diversion support. +dnl ============================================================ +m4_define([AC_SVGC_HEADLINE], [dnl +m4_divert_push([M4SH-INIT])dnl +{ + if test ".`echo dummy [$]@ | grep help`" = .; then + + dnl Use portable printf rather than echo -e, which is not POSIX. + if test -t 1; then + TB=`printf '\033@<:@1m'` + TN=`printf '\033@<:@0m'` + else + TB='' + TN='' + fi + + $3="AC_PACKAGE_VERSION" + AC_SUBST($3) + + echo "Configuring:" + echo "" + echo "${TB}$1${TN} ($2), Version ${TB}${$3}${TN}" + echo "$4" + echo "" + fi +} +m4_divert_pop([M4SH-INIT])dnl +])dnl + + +dnl ============================================================ +dnl Display Configuration Headers +dnl +dnl configure.ac: +dnl AC_MSG_CFG_PART(<text>) +dnl ============================================================ +AC_DEFUN([AC_MSG_CFG_PART], [dnl + AC_MSG_RESULT() + AC_MSG_RESULT([${TB}$1:${TN}]) +])dnl + + +dnl ============================================================ +dnl SVGC Library Version Information for GNU Libtool +dnl +dnl configure.ac: +dnl AC_SVGC_LT_VERSION_INFO([current:revision:age]) +dnl +dnl This controls the Libtool interface version (not the SVGC +dnl package version). Keep current:revision:age in one place. +dnl ============================================================ +AC_DEFUN([AC_SVGC_LT_VERSION_INFO], [dnl + AC_MSG_CHECKING([for SVGC Library Version Information]) + + svgc_lt_info="$1" + svgc_lt_current=`echo "${svgc_lt_info}" | cut -f1 -d':'` + svgc_lt_revision=`echo "${svgc_lt_info}" | cut -f2 -d':'` + svgc_lt_age=`echo "${svgc_lt_info}" | cut -f3 -d':'` + + AS_IF([test -z "${svgc_lt_info}" || + test -z "${svgc_lt_current}" || + test -z "${svgc_lt_revision}" || + test -z "${svgc_lt_age}"], [ + AC_MSG_ERROR([SVGC Library Version Information is not declared.]) + ]) + + ac_cv_svgc_lt_release="${svgc_lt_current}.${svgc_lt_revision}" + ac_cv_svgc_lt_current="${svgc_lt_current}" + ac_cv_svgc_lt_revision="${svgc_lt_revision}" + ac_cv_svgc_lt_age="${svgc_lt_age}" + ac_cv_svgc_lt_version_info="${svgc_lt_current}:${svgc_lt_revision}:${svgc_lt_age}" + + AC_SUBST([SVGC_LT_RELEASE], [$ac_cv_svgc_lt_release]) + AC_SUBST([SVGC_LT_VERSION_INFO], [$ac_cv_svgc_lt_version_info]) + AC_SUBST([SVGC_LT_CURRENT], [$ac_cv_svgc_lt_current]) + AC_SUBST([SVGC_LT_REVISION], [$ac_cv_svgc_lt_revision]) + AC_SUBST([SVGC_LT_AGE], [$ac_cv_svgc_lt_age]) + + AC_MSG_RESULT([$ac_cv_svgc_lt_version_info]) +])dnl diff --git a/auto-clean b/auto-clean new file mode 100755 index 0000000..d933fcb --- /dev/null +++ b/auto-clean @@ -0,0 +1,48 @@ +#!/bin/bash + +CWD=`pwd` + +program=`basename $0` + +usage() { + cat << EOF + +Usage: $program [options] + +Options: + -h,--help Display this message. + +EOF +} + +if [ -f "${CWD}/Makefile" ] ; then + make distclean +fi + +gitignore='.gitignore' + +while read ln; do + line=`echo "${ln}" | sed 's,^[ \t],,' | sed 's,[ \t]$,,'` + if [ "x$line" != "x" -a "${line:0:1}" != "#" ] ; then + if `echo "${line}" | grep -q '\*~$'` ; then + find "`dirname "${line}"`" -type f -iname '*~' -print0 | while IFS= read -r -d '' file ; do + rm -f "$file" + done + elif `echo "${line}" | grep -q '\*'` ; then + find "`dirname "${line}"`" -type f -iname "`basename "${line}"`" -print0 | while IFS= read -r -d '' file ; do + rm -f "$file" + done + else + if [ -d "${line}" ] ; then rm -rf "${line}" ; fi + if [ -f "${line}" ] ; then rm -f "${line}" ; fi + fi + fi +done < ${CWD}/${gitignore} + +rm -f examples/Makefile.in +rm -f examples/css-file/Makefile.in +rm -f examples/oscilator/Makefile.in +rm -f examples/script-file/Makefile.in +rm -f src/Makefile.in +rm -f svgc-config/Makefile.in +rm -f tests/Makefile.in diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..98c7a9b --- /dev/null +++ b/bootstrap @@ -0,0 +1,108 @@ +#!/bin/bash + +CWD=`pwd` + +program=`basename $0` + +usage() { + cat << EOF + +Usage: $program [options] + +Options: + -h,--help Display this message. + -d,--target-dest-dir=DIR The target ROOTFS directory + [default: DIR=/]. + +EOF +} + +TARGET_DEST_DIR=/ +ACDIR=usr/share/aclocal +INCDIR=usr/include +SYSTEM_ACDIR= +SYSTEM_INCDIR= + +while [ 0 ] ; do + if [ "$1" = "-h" -o "$1" = "--help" ] ; then + usage + exit 0 + elif [ "$1" = "-d" -o "$1" = "--target-dest-dir" ] ; then + if [ "$2" = "" ] ; then + echo -e "\n${program}: ERROR: --target-dest-dir is not specified.\n" + usage + exit 1 + fi + TARGET_DEST_DIR="$2" + shift 2 + elif [[ $1 == --target-dest-dir=* ]] ; then + TARGET_DEST_DIR="`echo $1 | cut -f2 -d'='`" + shift 1 + else + if [ "$1" != "" ] ; then + echo -e "\n${program}: ERROR: Unknown argument: $1.\n" + usage + exit 1 + fi + break + fi +done + +if [ ! -d "${TARGET_DEST_DIR}" ] ; then + echo -e "\n${program}: ERROR: --target-dest-dir is not a directory.\n" + usage + exit 1 +fi + +# +# Absolute path: +# +if [ "${TARGET_DEST_DIR:0:1}" != "/" ] ; then + TARGET_DEST_DIR=${CWD}/${TARGET_DEST_DIR} +fi + +# +# Remove last '/' char: +# +if [ "${TARGET_DEST_DIR: -1}" = "/" ] ; then + len=${#TARGET_DEST_DIR} + let "len = len - 1" + tmp="${TARGET_DEST_DIR:0:$len}" + TARGET_DEST_DIR=${tmp} +fi + +SYSTEM_ACDIR="${TARGET_DEST_DIR}/${ACDIR}" +SYSTEM_INCDIR="${TARGET_DEST_DIR}/${INCDIR}" + +################################################################ +# Libtool: +# +libtoolize --force --copy +# +# Используется если в configure.ac вызывается LT_INIT. +# Patch необходим по тому, что и lib-ld.m4 и libtool.m4 +# используют функцию AC_ARG_WITH([gnu-ld], ... +# а это приводит к тому, что ./configure --help дважды +# выдает подсказку --with-gnu-ld : +# +patch -p0 < $CWD/config/patches/libtool.m4.patch +patch -p0 < $CWD/config/patches/libtool.m4-shared.patch + +# +# End of Libtool. +################################################################ + + +aclocal --install -I m4 --force --system-acdir=${SYSTEM_ACDIR} +autoheader --include=${SYSTEM_INCDIR} +automake --gnu --add-missing --copy --force-missing +autoconf --force + + +################################################################ +# Remove cache and back files: +# +rm -rf autom4te.cache *~ +# +# End of Cleanup. +################################################################ diff --git a/config/patches/libtool.m4-shared.patch b/config/patches/libtool.m4-shared.patch new file mode 100644 index 0000000..9389d02 --- /dev/null +++ b/config/patches/libtool.m4-shared.patch @@ -0,0 +1,20 @@ +--- m4-orig/libtool.m4 2026-06-07 17:43:33.877209247 +0300 ++++ m4/libtool.m4 2026-06-07 17:45:22.508205160 +0300 +@@ -4998,7 +4998,7 @@ + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) +- _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ++ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC -DSHARED],[m4_if([$1],[CXX],[ -DPIC -DSHARED],[])])" + ;; + esac + +@@ -5013,7 +5013,7 @@ + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], +- [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], ++ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC -DSHARED],[m4_if([$1],[CXX],[ -DPIC -DSHARED],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; diff --git a/config/patches/libtool.m4.patch b/config/patches/libtool.m4.patch new file mode 100644 index 0000000..eba3758 --- /dev/null +++ b/config/patches/libtool.m4.patch @@ -0,0 +1,15 @@ +--- m4-orig/libtool.m4 2026-06-07 17:38:21.701220990 +0300 ++++ m4/libtool.m4 2026-06-07 17:39:59.924217295 +0300 +@@ -3374,12 +3374,6 @@ + m4_require([_LT_DECL_EGREP])dnl + m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +-AC_ARG_WITH([gnu-ld], +- [AS_HELP_STRING([--with-gnu-ld], +- [assume the C compiler uses GNU ld @<:@default=no@:>@])], +- [test no = "$withval" || with_gnu_ld=yes], +- [with_gnu_ld=no])dnl +- + ac_prog=ld + if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..a8984b4 --- /dev/null +++ b/configure.ac @@ -0,0 +1,84 @@ + +AC_PREREQ([2.71]) + +AC_INIT([SVG C Library],[1.1.0],[Andrey V.Kosteltsev <kx@radix-linux.su>],[svgc],[https://radix-linux.su]) + +AC_SVGC_REQUIRE_BASH + +AC_SVGC_HEADLINE([svgc], [SVG C Library], [SVGC_VERSION],dnl +[Copyright (c) 1999-2026 Andrey V.Kosteltsev])dnl + +AC_MSG_CFG_PART(Getting the canonical system type) +dnl +dnl NOTE: Следующие вызовы должны осуществляться строго до +dnl вызова AM_INIT_AUTOMAKE . +dnl +dnl Для библиотеки достаточно вызвать AC_CANONICAL_HOST и при этом +dnl будут определены build_alias и host_alias. Однако опция --target +dnl будет запрещена. +dnl +dnl Для программ достаточно задать AC_CANONICAL_TARGET и при этом +dnl будут определены build_alias, host_alias и target_alias. Естественно +dnl будут доступны все три опции: --build, --host, и --target . +dnl + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET +AC_USE_SYSTEM_EXTENSIONS + +dnl Должна вызываться строго после AC_CANONICAL_TARGET . +AC_ARG_PROGRAM + +AC_MSG_CFG_PART(Init Automake environment) +AM_INIT_AUTOMAKE([foreign subdir-objects dist-xz no-dist-gzip]) + +AC_CONFIG_SRCDIR([acsite.m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIRS([m4]) + +AC_MSG_CFG_PART([Test for Build Tools]) +AC_PROG_CC + +AC_MSG_CFG_PART([Test for GNU libtool]) +LT_PREREQ([2.4.6]) +LT_INIT +AC_SUBST([LIBTOOL_DEPS]) + +AC_MSG_CFG_PART([Test for libtool VERSION Information]) +AC_SVGC_LT_VERSION_INFO([1:0:0]) + +AC_MSG_CFG_PART([Checking System Headers]) +AC_CHECK_HEADERS([stdio.h stdlib.h string.h unistd.h]) + +AC_MSG_CFG_PART([Build Parameters]) +AC_MSG_CHECKING([whether static libraries are enabled]) +AC_MSG_RESULT([$enable_static]) +AC_MSG_CHECKING([whether shared libraries are enabled]) +AC_MSG_RESULT([$enable_shared]) + +AC_MSG_CFG_PART([OUTPUT]) +AC_CONFIG_FILES([ + Makefile + src/Makefile + tests/Makefile + svgc-config/Makefile + svgc-config/svgc-config + svgc.pc + examples/Makefile + examples/css-file/Makefile + examples/oscilator/Makefile + examples/script-file/Makefile +]) +AC_CONFIG_COMMANDS([svgc-config-chmod], [chmod +x svgc-config/svgc-config]) +AC_OUTPUT + +if test -f "config.h"; then + echo "" + echo "Now you can run:" + echo " \`${TB}make${TN}' to compile," + echo " \`${TB}make install${TN}' to make and install ${TB}SVGC${TN}," + echo " \`${TB}make dist${TN}' to create distributable tarballs, or" + echo " \`${TB}make distclean${TN}' to clean before configure for another target." + echo "Good luck." + echo "" +fi diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..aba3ff4 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = css-file oscilator script-file diff --git a/examples/css-file/Makefile.am b/examples/css-file/Makefile.am new file mode 100644 index 0000000..fdabb9a --- /dev/null +++ b/examples/css-file/Makefile.am @@ -0,0 +1,27 @@ + +AM_CFLAGS = -Wall -Wextra -Werror -std=gnu23 +AM_CPPFLAGS = -I$(top_srcdir)/src + +noinst_PROGRAMS = main +noinst_DATA = example.svg + +main_SOURCES = main.c +main_LDADD = $(top_builddir)/src/.libs/libsvgc.la + +EXTRA_DIST = fonts.css view.html +CLEANFILES = example.svg + +example.svg: main $(srcdir)/fonts.css + @if test "$(srcdir)" != "."; then \ + cp "$(srcdir)/fonts.css" fonts.css; \ + cp "$(srcdir)/view.html" view.html; \ + fi + ./main + @if test "$(srcdir)" != "."; then \ + rm -f fonts.css; \ + fi + +clean-local: + @if test "$(srcdir)" != "."; then \ + rm -f view.html; \ + fi diff --git a/examples/css-file/README b/examples/css-file/README new file mode 100644 index 0000000..93c43e5 --- /dev/null +++ b/examples/css-file/README @@ -0,0 +1,18 @@ +CSS-FILE EXAMPLE +================ + +Run `make` using the installed SVGC and open `view.html`. +The program includes `fonts.css` in the generated example.svg <defs> +section. + +The CSS text is embedded in example.svg, but the font files are *not* +embedded. Place a legitimately redistributable `Roboto-Regular.woff2` +file in the `fonts/` subdirectory next to example.svg, or use a locally +installed Roboto font. This package intentionally does not distribute +font binaries. + +Relative url(...) paths in the embedded CSS are resolved against +example.svg, not against the original fonts.css or view.html. Some +browsers enforce security restrictions on local file:// font loading. +Serving the example from a local HTTP server may be needed for reliable +font rendering. diff --git a/examples/css-file/fonts.css b/examples/css-file/fonts.css new file mode 100644 index 0000000..022ac3b --- /dev/null +++ b/examples/css-file/fonts.css @@ -0,0 +1,8 @@ +/* The URL is resolved relative to example.svg, not relative to fonts.css. */ +@font-face { + font-family: 'Roboto'; + src: local('Roboto Regular'), + url('./fonts/Roboto-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; +} diff --git a/examples/css-file/main.c b/examples/css-file/main.c new file mode 100644 index 0000000..5f56cd1 --- /dev/null +++ b/examples/css-file/main.c @@ -0,0 +1,47 @@ +/*************************************************************** + SVGC EXAMPLE: STYLESHEET FILE + + This example reads CSS once and embeds its contents into SVG defs. + ***************************************************************/ + +#include <stdio.h> +#include <svgc.h> + + +int +main( void ) +{ + struct __svg_sheet *sheet; + struct __svg_scene *scene; + struct __svg_node *text; + + sheet = (struct __svg_sheet *)__svg_new_sheet( 640, 160, 16, 16, 16, 16 ); + if( !sheet ) return( 1 ); + + /* File content is copied into the sheet; no external CSS is needed later. */ + if( __svg_sheet_add_css_file( sheet, "fonts.css" ) != 1 ) + { + __svg_free_node( sheet ); + fprintf( stderr, "Cannot add fonts.css to the SVG sheet\n" ); + return( 1 ); + } + + scene = __svg_new_scene( sheet ); + if( !scene ) + { + __svg_free_node( sheet ); + return( 1 ); + } + + text = __svg_new_text( 36, 90, "SVG fonts from CSS", "Roboto", 28 ); + if( !text || __svg_scene_add_node( scene, text ) != 1 ) + { + if( text ) __svg_free_node( text ); + __svg_free_scene( scene ); + return( 1 ); + } + + __svg_paint_scene( scene, 0, "example.svg" ); + __svg_free_scene( scene ); + return( 0 ); +} diff --git a/examples/css-file/view.html b/examples/css-file/view.html new file mode 100644 index 0000000..e82c98d --- /dev/null +++ b/examples/css-file/view.html @@ -0,0 +1,9 @@ +<!doctype html> +<html lang="en"> +<head><meta charset="utf-8"><title>SVGC embedded CSS</title></head> +<body> + <object type="image/svg+xml" data="example.svg" width="640" height="160"> + SVG preview is not available. + </object> +</body> +</html> diff --git a/examples/oscilator/Makefile.am b/examples/oscilator/Makefile.am new file mode 100644 index 0000000..e77c704 --- /dev/null +++ b/examples/oscilator/Makefile.am @@ -0,0 +1,23 @@ + +AM_CFLAGS = -Wall -Wextra -Werror -std=gnu23 +AM_CPPFLAGS = -I$(top_srcdir)/src + +noinst_PROGRAMS = main +noinst_DATA = M.svg + +main_SOURCES = main.c +main_LDADD = -lm $(top_builddir)/src/.libs/libsvgc.la + +EXTRA_DIST = view.html +CLEANFILES = M.svg + +M.svg: main + @if test "$(srcdir)" != "."; then \ + cp "$(srcdir)/view.html" view.html; \ + fi + ./main + +clean-local: + @if test "$(srcdir)" != "."; then \ + rm -f view.html; \ + fi diff --git a/examples/oscilator/README b/examples/oscilator/README new file mode 100644 index 0000000..b519cac --- /dev/null +++ b/examples/oscilator/README @@ -0,0 +1,11 @@ + +BUILD: + + $ make + $ ./main + $ view.html + + +CLEAN: + $ make clean + diff --git a/examples/oscilator/main.c b/examples/oscilator/main.c new file mode 100644 index 0000000..813f64f --- /dev/null +++ b/examples/oscilator/main.c @@ -0,0 +1,441 @@ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <math.h> + +#include <svgc.h> + +struct __svg_sheet *sheet; +struct __svg_line *line; +struct __svg_rect *rect; +struct __svg_circle *circle; +struct __svg_ellipse *ellipse; +struct __svg_text *text; + +struct __svg_points *points; +struct __svg_polygon *polygon; +struct __svg_polyline *polyline; + +struct __svg_cmd *cmd; +struct __svg_cmds *cmds; +struct __svg_path *path; + + +struct __svg_scene *scene; + +void arrow( double sx0, double sy0, double f0 ) +{ + double sx1, sy1, sx2, sy2, sx3, sy3, sx4, sy4; + double px1, py1, px2, py2, px3, py3, px4, py4; + + sx1 = sx0 - 3.0; + sy1 = sy0 + 3.0; + sx2 = sx0; + sy2 = sy0 - 6.0; + sx3 = sx0 + 3.0; + sy3 = sy0 + 3.0; + sx4 = sx0; + sy4 = sy0 + 1.0; + + px1 = sx0 + ( sx1 - sx0 )*cos( f0 ) - ( sy1 - sy0 )*sin( f0 ); + py1 = sy0 + ( sx1 - sx0 )*sin( f0 ) + ( sy1 - sy0 )*cos( f0 ); + px2 = sx0 + ( sx2 - sx0 )*cos( f0 ) - ( sy2 - sy0 )*sin( f0 ); + py2 = sy0 + ( sx2 - sx0 )*sin( f0 ) + ( sy2 - sy0 )*cos( f0 ); + px3 = sx0 + ( sx3 - sx0 )*cos( f0 ) - ( sy3 - sy0 )*sin( f0 ); + py3 = sy0 + ( sx3 - sx0 )*sin( f0 ) + ( sy3 - sy0 )*cos( f0 ); + px4 = sx0 + ( sx4 - sx0 )*cos( f0 ) - ( sy4 - sy0 )*sin( f0 ); + py4 = sy0 + ( sx4 - sx0 )*sin( f0 ) + ( sy4 - sy0 )*cos( f0 ); + + cmds = __svg_new_cmds(); + __svg_add_moveto_cmd( cmds, ABSOLUTE, px1, py1 ); + __svg_add_lineto_cmd( cmds, ABSOLUTE, px2, py2 ); + __svg_add_lineto_cmd( cmds, ABSOLUTE, px3, py3 ); + __svg_add_short_curveto_cmd( cmds, ABSOLUTE, px4, py4, px1, py1 ); + __svg_add_closepath_cmd( cmds, ABSOLUTE ); + path = (struct __svg_path *)__svg_new_path( cmds ); + (void)__svg_set_stroke_rgb( (struct __svg_node *)path, 0, 0, 0 ); + (void)__svg_set_stroke_width( (struct __svg_node *)path, 1.0 ); + (void)__svg_set_stroke_linecap( (struct __svg_node *)path, "square" ); + (void)__svg_set_stroke_linejoin( (struct __svg_node *)path, "miter" ); + (void)__svg_set_fill_rgb( (struct __svg_node *)path, 255, 255, 255 ); + + (void)__svg_scene_add_node( scene, (struct __svg_node *)path ); +} + +void small_arrow( double sx0, double sy0, double f0 ) +{ + double sx1, sy1, sx2, sy2, sx3, sy3, sx4, sy4; + double px1, py1, px2, py2, px3, py3, px4, py4; + + sx1 = sx0 - 2.0; + sy1 = sy0 + 2.0; + sx2 = sx0; + sy2 = sy0 - 4.0; + sx3 = sx0 + 2.0; + sy3 = sy0 + 2.0; + sx4 = sx0; + sy4 = sy0 + 1.0; + + px1 = sx0 + ( sx1 - sx0 )*cos( f0 ) - ( sy1 - sy0 )*sin( f0 ); + py1 = sy0 + ( sx1 - sx0 )*sin( f0 ) + ( sy1 - sy0 )*cos( f0 ); + px2 = sx0 + ( sx2 - sx0 )*cos( f0 ) - ( sy2 - sy0 )*sin( f0 ); + py2 = sy0 + ( sx2 - sx0 )*sin( f0 ) + ( sy2 - sy0 )*cos( f0 ); + px3 = sx0 + ( sx3 - sx0 )*cos( f0 ) - ( sy3 - sy0 )*sin( f0 ); + py3 = sy0 + ( sx3 - sx0 )*sin( f0 ) + ( sy3 - sy0 )*cos( f0 ); + px4 = sx0 + ( sx4 - sx0 )*cos( f0 ) - ( sy4 - sy0 )*sin( f0 ); + py4 = sy0 + ( sx4 - sx0 )*sin( f0 ) + ( sy4 - sy0 )*cos( f0 ); + + cmds = __svg_new_cmds(); + __svg_add_moveto_cmd( cmds, ABSOLUTE, px1, py1 ); + __svg_add_lineto_cmd( cmds, ABSOLUTE, px2, py2 ); + __svg_add_lineto_cmd( cmds, ABSOLUTE, px3, py3 ); + __svg_add_short_curveto_cmd( cmds, ABSOLUTE, px4, py4, px1, py1 ); + __svg_add_closepath_cmd( cmds, ABSOLUTE ); + path = (struct __svg_path *)__svg_new_path( cmds ); + (void)__svg_set_stroke_rgb( (struct __svg_node *)path, 0, 0, 0 ); + (void)__svg_set_stroke_width( (struct __svg_node *)path, 0.5 ); + (void)__svg_set_stroke_linecap( (struct __svg_node *)path, "square" ); + (void)__svg_set_stroke_linejoin( (struct __svg_node *)path, "miter" ); + (void)__svg_set_fill_rgb( (struct __svg_node *)path, 255, 255, 255 ); + + (void)__svg_scene_add_node( scene, (struct __svg_node *)path ); +} + + +int main( void ) +{ + int i; + double x0 = 100.0, + y0 = 30.0, + r0 = 250.0, + a0 = 60.0 * M_PI / 180.0, + xM, yM; + + sheet = (struct __svg_sheet *)__svg_new_sheet( 350, 380, 10, 10, 10, 10 ); + if( !sheet ) + { + printf( "Unable to create sheet\n" ); + } + (void)__svg_set_stroke_width( (struct __svg_node *)sheet, 1024.1236 ); + + scene = __svg_new_scene( sheet ); + if( !scene ) + { + printf( "Unable to create scene\n" ); + } + + __svg_set_scene_stroke( "black" ); + __svg_set_scene_stroke_width( 1.0 ); + __svg_set_scene_stroke_linecap( LINECAP_SQUARE ); + + + rect = (struct __svg_rect *)__svg_new_rect( x0-25.0, y0-1.0, 50, 2, 0, 0 ); + (void)__svg_set_fill_rgb( (struct __svg_node *)rect, 0, 0, 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)rect ); + + { + int x1 = x0-24.0, y1 = y0-1.0, x2 = x0-18.0, y2 = y0-7.0; + + for( i = 0; i < 8; ++i ) + { + line = (struct __svg_line *)__svg_new_line( x1, y1, x2, y2 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + x1 += 6; x2 += 6; + } + } + + + xM = x0 + r0 * cos( a0 ); + yM = y0 + r0 * sin( a0 ); + + __svg_set_scene_stroke_width( 0.5 ); + + line = (struct __svg_line *)__svg_new_line( x0, y0, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + /* Set current values for newest objects: */ + __svg_set_scene_stroke_width( 0.5 ); + __svg_set_scene_stroke_rgb( 99, 99, 99 ); + __svg_set_scene_stroke_dasharray( "3,2" ); + + line = (struct __svg_line *)__svg_new_line( x0, y0, x0, y0+r0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + { + double x1, y1, x2, y2, a1 = 45.0, a2 = 75.0; + + a1 = a1 * M_PI / 180.0; + a2 = a2 * M_PI / 180.0; + x1 = x0 + r0 * cos(a1); + y1 = y0 + r0 * sin(a1); + x2 = x0 - r0 * cos(a2); + y2 = y0 + r0 * sin(a2); + + cmds = __svg_new_cmds(); + /* M x1,y1 */ + __svg_add_moveto_cmd( cmds, ABSOLUTE, x1, y1 ); + + __svg_add_arc_cmd( cmds, + ABSOLUTE, + r0, r0, /* rx, ry */ + 0.0, /* angle */ + 0,1, /* lf, sf */ + x2, y2 ); + + path = (struct __svg_path *)__svg_new_path( cmds ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)path ); + } + + { + double x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6, + x7, y7, x8, y8, x9, y9, x10, y10, x11, y11, x12, y12; + + double x3_a, y3_a, x11_a, y11_a, x12_a, y12_a; + + double r1 = 120.0, + r2 = 100.0, + r3 = 90.0, + r4 = 100.0, + r5 = 90.0, + r6 = 30.0, + r7 = 100.0, + r8 = 30.0, + r9 = 60.0, + r10 = 60.0, + r11, r12; + + /* Set current values for newest objects: */ + __svg_set_scene_stroke_rgb( 99, 99, 99 ); + __svg_set_scene_stroke_width( 0.5 ); + __svg_set_scene_stroke_dasharray( "3,2" ); + __svg_set_scene_fill( "none" ); + + x1 = xM - r1 * sin( a0 ); + y1 = yM + r1 * cos( a0 ); + + x2 = xM - r2 * sin( a0 ); + y2 = yM + r2 * cos( a0 ); + + x3 = xM; + y3 = yM + r3; + + x3_a = xM; + y3_a = yM + r3 + 6.0; + + x4 = xM + r4 * cos( a0 ); + y4 = yM + r4 * sin( a0 ); + + x5 = xM + r5 * sin( a0 ); + y5 = yM - r5 * cos( a0 ); + + x6 = xM + r6 * sin( a0 ); + y6 = yM - r6 * cos( a0 ); + + x7 = xM - r7 * cos( a0 ); + y7 = yM - r7 * sin( a0 ); + + x8 = xM - r8 * cos( a0 ); + y8 = yM - r8 * sin( a0 ); + + x9 = x0 + r9 * cos( a0 ); + y9 = y0 + r9 * sin( a0 ); + + x10 = x0; + y10 = y0 + r10; + + r11 = r3 * cos( a0 ); + x11 = xM - r11 * sin( a0 ); + y11 = yM + r11 * cos( a0 ); + x11_a = xM - (r11+6.0) * sin( a0 ); + y11_a = yM + (r11+6.0) * cos( a0 ); + + + r12 = r3 * sin( a0 ); + x12 = xM + r12 * cos( a0 ); + y12 = yM + r12 * sin( a0 ); + x12_a = xM + (r12+6.0) * cos( a0 ); + y12_a = yM + (r12+6.0) * sin( a0 ); + + line = (struct __svg_line *)__svg_new_line( x1, y1, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x4, y4, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x3_a, y3_a, x11_a, y11_a ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x3_a, y3_a, x12_a, y12_a ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + __svg_set_scene_stroke( "black" ); + __svg_set_scene_stroke_width( 0.5 ); + __svg_set_scene_stroke_dasharray( "none" ); + + cmds = __svg_new_cmds(); + /* M x1,y1 */ + __svg_add_moveto_cmd( cmds, ABSOLUTE, x9, y9 ); + + __svg_add_arc_cmd( cmds, + ABSOLUTE, + r10, r10, /* rx, ry */ + 0.0, /* angle */ + 0,1, /* lf, sf */ + x10, y10 ); + + path = (struct __svg_path *)__svg_new_path( cmds ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)path ); + + __svg_set_scene_stroke_width( 1.0 ); + + line = (struct __svg_line *)__svg_new_line( x2, y2, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x5, y5, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x7, y7, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + __svg_set_scene_stroke_width( 2.0 ); + + line = (struct __svg_line *)__svg_new_line( x3, y3, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x6, y6, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x8, y8, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x11, y11, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + line = (struct __svg_line *)__svg_new_line( x12, y12, xM, yM ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + + /* áâàÕÛÚØ */ + arrow( x2, y2, M_PI + a0 ); + arrow( x3, y3, M_PI ); + arrow( x11, y11, M_PI + a0 ); + arrow( x12, y12, M_PI - (M_PI / 2 - a0) ); + arrow( x5, y5, a0 ); + arrow( x6, y6, a0 ); + arrow( x7, y7, -(M_PI /2 - a0) ); + arrow( x8, y8, -(M_PI /2 - a0) ); + + { + double fx1, fy1; + fx1 = x9 - 4.0 * sin( a0 ); + fy1 = y9 + 4.0 * cos( a0 ); + small_arrow( fx1, fy1, a0 ); + } + + __svg_set_scene_text_fill_rgb( 0, 99, 99 ); + text = (struct __svg_text *)__svg_new_text( x2, y2+15, + "<tspan font-style=\"oblique\">B</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x11+7, y11+15, + "<tspan font-style=\"oblique\">G<tspan dy=\"4\" font-family=\"Georgia\" font-size=\"10\">τ</tspan></tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x12+7, y12, + "<tspan font-style=\"oblique\">G<tspan dy=\"4\" font-family=\"Georgia\" font-size=\"10\">n</tspan></tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x3, y3+20, + "<tspan font-style=\"oblique\">G</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( xM-20, yM, + "<tspan font-weight=\"bold\" font-style=\"oblique\">M</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x6+5, y6+12, + "<tspan font-family=\"Georgia\" font-style=\"oblique\">τ</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x8+5, y8-5, + "<tspan font-family=\"Georgia\" font-style=\"oblique\">n</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x5+5, y5+12, + "<tspan font-family=\"Georgia\" font-style=\"oblique\">ν</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x7+5, y7-5, + "<tspan font-style=\"oblique\">N</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x0+10, y0+r9+10, + "<tspan font-family=\"Georgia\" font-style=\"oblique\">φ</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x0-10, y0+r0+20, + "<tspan font-style=\"oblique\">O<tspan dy=\"4\" font-size=\"8\">1</tspan></tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x0-15, y0+15, + "<tspan font-style=\"oblique\">O</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x0-15, y0+r0-5, + "<tspan font-style=\"oblique\">-</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x0+7, y0+r0-5, + "<tspan font-style=\"oblique\">+</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + + text = (struct __svg_text *)__svg_new_text( x0+40, y0+r0-10, + "<tspan font-style=\"oblique\">s</tspan>", + "Verdana", 0 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)text ); + } + + /* Set current values for newest objects: */ + __svg_set_scene_stroke( "black" ); + __svg_set_scene_stroke_width( 1.0 ); + __svg_set_scene_stroke_dasharray( "none" ); + __svg_set_scene_fill_rgb( 255, 255, 255 ); + + circle = (struct __svg_circle *)__svg_new_circle( x0, y0, 3 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)circle ); + + circle = (struct __svg_circle *)__svg_new_circle( xM, yM, 3 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)circle ); + + circle = (struct __svg_circle *)__svg_new_circle( x0, y0+r0, 3 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)circle ); + + __svg_set_scene_fill( "none" ); + + __svg_paint_scene( scene, 0, "M.svg" ); + + __svg_free_scene( scene ); + + return( 0 ); +} + diff --git a/examples/oscilator/view.html b/examples/oscilator/view.html new file mode 100644 index 0000000..debe4eb --- /dev/null +++ b/examples/oscilator/view.html @@ -0,0 +1,41 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta http-equiv="content-type" content="text/html; charset=iso-8859-5"> +</head> +<body link="#333333" vlink="#666666" alink="#000066"> + + <div + style="font-size: 12px; margin-left: 8px; margin-right: 4px; margin-top: 6px; margin-bottom: 8px; text-align: justify;"> + +<p> +Text: +</p> + + +<p> + <table border="0" cellspacing="0" cellpadding="0" style="width: 100%;"> + <tbody> + <tr> + <td style="text-align: center;"> +<object type="image/svg+xml" data="M.svg" width="600" height="400"> +Your browser is currently unable to display SVG images. +</object> + </td> + <td style="text-align: right; width: 20px;"> +<!-- index --> + </td> + </tr> + </tbody> + </table> +</p> + + +<p> +End. +</p> + + </div> + +</body> +</html> diff --git a/examples/script-file/Makefile.am b/examples/script-file/Makefile.am new file mode 100644 index 0000000..9c6cd12 --- /dev/null +++ b/examples/script-file/Makefile.am @@ -0,0 +1,27 @@ + +AM_CFLAGS = -Wall -Wextra -Werror -std=gnu23 +AM_CPPFLAGS = -I$(top_srcdir)/src + +noinst_PROGRAMS = main +noinst_DATA = example.svg + +main_SOURCES = main.c +main_LDADD = $(top_builddir)/src/.libs/libsvgc.la + +EXTRA_DIST = change-color.js view.html +CLEANFILES = example.svg + +example.svg: main $(srcdir)/change-color.js + @if test "$(srcdir)" != "."; then \ + cp "$(srcdir)/change-color.js" change-color.js; \ + cp "$(srcdir)/view.html" view.html; \ + fi + ./main + @if test "$(srcdir)" != "."; then \ + rm -f change-color.js; \ + fi + +clean-local: + @if test "$(srcdir)" != "."; then \ + rm -f view.html; \ + fi diff --git a/examples/script-file/README b/examples/script-file/README new file mode 100644 index 0000000..8f10bdf --- /dev/null +++ b/examples/script-file/README @@ -0,0 +1,24 @@ +SVGC interactive-script example +=============================== + +This example demonstrates __svg_sheet_add_script_file(). + +The user writes only change-color.js as plain UTF-8 JavaScript source. +The C application registers that file with the SVG sheet; SVGC reads and +copies it into a <script type="text/javascript"><![CDATA[...]]></script> +definition when serializing the SVG. + +After installing SVGC: + + make + +Open example.svg directly as an SVG document, or use view.html, which +embeds it with <object type="image/svg+xml">. Click the red circle: the +script changes its fill to blue. Loading the SVG with <img> deliberately +does not execute scripts in modern browsers. Opening local files may also +be affected by browser security settings; a local HTTP server is an +alternative for manual testing when the browser restricts file://. + +No binary scripts or external dependencies are bundled with the SVG. +No JavaScript parser is part of the SVGC library; the viewer executes +any embedded source in its normal security context. diff --git a/examples/script-file/change-color.js b/examples/script-file/change-color.js new file mode 100644 index 0000000..e831b22 --- /dev/null +++ b/examples/script-file/change-color.js @@ -0,0 +1,13 @@ +/* JavaScript source only: do NOT add <defs>, <script> or CDATA tags. */ + +function changeColor(event) +{ + event.target.style.fill = 'blue'; +} + +document.addEventListener('DOMContentLoaded', function () +{ + var circle = document.querySelector('circle'); + if (circle) + circle.addEventListener('click', changeColor); +}); diff --git a/examples/script-file/main.c b/examples/script-file/main.c new file mode 100644 index 0000000..936916f --- /dev/null +++ b/examples/script-file/main.c @@ -0,0 +1,51 @@ + +/*************************************************************** + SVGC EXAMPLE: EMBEDDED ECMASCRIPT + + The file change-color.js contains only JavaScript source + text. Interactive script execution requires opening the SVG + as a document. + ***************************************************************/ + +#include <stdio.h> +#include <svgc.h> + + +int +main( void ) +{ + struct __svg_sheet *sheet; + struct __svg_scene *scene; + struct __svg_node *circle; + + sheet = (struct __svg_sheet *)__svg_new_sheet( 520, 220, 16, 16, 16, 16 ); + if( !sheet ) return( 1 ); + + if( __svg_sheet_add_script_file( sheet, "change-color.js" ) != 1 ) + { + __svg_free_node( sheet ); + fprintf( stderr, "Cannot add change-color.js to the SVG sheet\n" ); + return( 1 ); + } + + scene = __svg_new_scene( sheet ); + if( !scene ) + { + __svg_free_node( sheet ); + return( 1 ); + } + + circle = __svg_new_circle( 260, 110, 72 ); + if( !circle || + __svg_set_fill( circle, "red" ) != 1 || + __svg_scene_add_node( scene, circle ) != 1 ) + { + if( circle ) __svg_free_node( circle ); + __svg_free_scene( scene ); + return( 1 ); + } + + __svg_paint_scene( scene, 0, "example.svg" ); + __svg_free_scene( scene ); + return( 0 ); +} diff --git a/examples/script-file/view.html b/examples/script-file/view.html new file mode 100644 index 0000000..5494951 --- /dev/null +++ b/examples/script-file/view.html @@ -0,0 +1,13 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>SVGC: Embedded Script</title> +</head> +<body> + <p>Click the red circle to change it to blue.</p> + <object data="example.svg" type="image/svg+xml" width="520" height="220"> + <p>Open example.svg as a standalone SVG document.</p> + </object> +</body> +</html> diff --git a/m4/README b/m4/README new file mode 100644 index 0000000..aad2960 --- /dev/null +++ b/m4/README @@ -0,0 +1,4 @@ +This directory is reserved for external/vendor Autoconf M4 macros. + +Project-owned SVGC macros belong in ../acsite.m4 so locally maintained +configure logic remains clearly separated from imported macro packages. diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..4347204 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,16 @@ + +lib_LTLIBRARIES = libsvgc.la + +libsvgc_la_SOURCES = svgc-dlist.c \ + svgc-dlist.h \ + svgc.c \ + svgc.h \ + _svgc_version.c \ + $(top_srcdir)/version.c \ + $(top_srcdir)/version.h + +libsvgc_la_CFLAGS = -I$(top_srcdir) + +libsvgc_la_LDFLAGS = -version-info $(SVGC_LT_VERSION_INFO) + +include_HEADERS = svgc.h svgc-dlist.h diff --git a/src/_svgc_version.c b/src/_svgc_version.c new file mode 100644 index 0000000..091f7ca --- /dev/null +++ b/src/_svgc_version.c @@ -0,0 +1,75 @@ + +/*************************************************************** + _SVGC_VERSION.C + + This file containt VERSION operation functions & data + definitions. + + PART OF : SVGC - Simple SVG generator . + + COMPILE : -I. . + + NOTE : NONE . + + Copyright (C) 2000 - 2026 by Andrey V.Kosteltsev. + All Rights Reserved. + ***************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + + +#define _VERSION_C_AS_HEADER_ +#include <version.c> +#undef _VERSION_C_AS_HEADER_ + + +int +_svgc_get_major_version() +{ + int major, minor, micro; + char *tmp_version; + + tmp_version = (char *)_svgc_version.v_short; + if( sscanf( tmp_version, "%d.%d.%d", &major, &minor, µ) != 3 ) + { + return( -1 ); + } + + return( major ); +} + +int +_svgc_get_minor_version() +{ + int major, minor, micro; + char *tmp_version; + + tmp_version = (char *)_svgc_version.v_short; + if( sscanf( tmp_version, "%d.%d.%d", &major, &minor, µ) != 3 ) + { + return( -1 ); + } + + return( minor ); +} + +int +_svgc_get_micro_version() +{ + int major, minor, micro; + char *tmp_version; + + tmp_version = (char *)_svgc_version.v_short; + if( sscanf( tmp_version, "%d.%d.%d", &major, &minor, µ) != 3 ) + { + return( -1 ); + } + + return( micro ); +} diff --git a/src/svgc-dlist.c b/src/svgc-dlist.c new file mode 100644 index 0000000..0b2fcd2 --- /dev/null +++ b/src/svgc-dlist.c @@ -0,0 +1,535 @@ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* exit() */ +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <svgc-dlist.h> + +struct svgc_dlist *create_svgc_dlist( void ) +{ + struct svgc_dlist *ret = (struct svgc_dlist *)0; + + ret = (struct svgc_dlist *)malloc( sizeof( struct svgc_dlist ) ); + if( !ret ) + { + return( (struct svgc_dlist *)0 ); + } + + ret->root_up = (struct svgc_dlist_node *)0; + ret->root_down = (struct svgc_dlist_node *)0; + ret->ptr = (struct svgc_dlist_node *)0; + + return( ret ); + +} + + +void free_svgc_dlist( struct svgc_dlist * plist, FREE_SVGC_DLIST_NODE_DATA free_data ) +{ + struct svgc_dlist_node *p; + + if( plist != (struct svgc_dlist *)0 ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->ptr = (struct svgc_dlist_node *)0; + return; + } + + for( p = plist->root_up; p != (struct svgc_dlist_node *)0; p = p->down ) + { + if( p->up ) + { + if( p->up->id ) free( p->up->id ); + if( free_data ) free_data( p->up->data ); + free( p->up ); + } + } + if( plist->root_down->id ) free( plist->root_down->id ); + if( free_data ) free_data( plist->root_down->data ); + free( plist->root_down ); + + plist->root_up = (struct svgc_dlist_node *)0; + plist->root_down = (struct svgc_dlist_node *)0; + plist->ptr = (struct svgc_dlist_node *)0; + + free( plist ); + } +} + +/* add svgc_dlist_node up */ +struct svgc_dlist_node *add_to_svgc_dlist_up( struct svgc_dlist * plist, char * id, void * data ) +{ + struct svgc_dlist_node *ret = (struct svgc_dlist_node *)0; + char *s = (char *)0; + + if( !plist ) return( (struct svgc_dlist_node *)0 ); + + ret = (struct svgc_dlist_node *)malloc( sizeof( struct svgc_dlist_node ) ); + if( !ret ) + { + return( (struct svgc_dlist_node *)0 ); + } + + ret->id = (char *)0; + ret->data = (void *)0; + + if( id && id[0] ) + { + s = (char *)malloc( strlen( id ) + 1 ); + if( !s ) + { + free( ret ); + return( (struct svgc_dlist_node *)0 ); + } + strcpy( s, id ); + ret->id = s; + } + + if( data ) ret->data = data; + + /* add to list */ + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->root_up = plist->root_down = plist->ptr = ret; + ret->up = (struct svgc_dlist_node *)0; + ret->down = (struct svgc_dlist_node *)0; + } + else + { + plist->root_up->up = ret; + ret->up = (struct svgc_dlist_node *)0; + ret->down = plist->root_up; + plist->root_up = ret; + } + + return( ret ); +} + +/* add svgc_dlist_node down */ +struct svgc_dlist_node *add_to_svgc_dlist_down( struct svgc_dlist * plist, char * id, void * data ) +{ + struct svgc_dlist_node *ret = (struct svgc_dlist_node *)0; + char *s = (char *)0; + + if( !plist ) return( (struct svgc_dlist_node *)0 ); + + ret = (struct svgc_dlist_node *)malloc( sizeof( struct svgc_dlist_node ) ); + if( !ret ) + { + return( (struct svgc_dlist_node *)0 ); + } + + ret->id = (char *)0; + ret->data = (void *)0; + + if( id && id[0] ) + { + s = (char *)malloc( strlen( id ) + 1 ); + if( !s ) + { + free( ret ); + return( (struct svgc_dlist_node *)0 ); + } + strcpy( s, id ); + ret->id = s; + } + + if( data ) ret->data = data; + + /* add to list */ + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->root_up = plist->root_down = plist->ptr = ret; + ret->up = (struct svgc_dlist_node *)0; + ret->down = (struct svgc_dlist_node *)0; + } + else + { + plist->root_down->down = ret; + ret->up = plist->root_down; + ret->down = (struct svgc_dlist_node *)0; + plist->root_down = ret; + plist->ptr = ret; + } + + return( ret ); +} + + +struct svgc_dlist_node *insert_into_svgc_dlist_up( struct svgc_dlist * plist, struct svgc_dlist_node * p ) +{ + if( plist && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->root_up = plist->root_down = plist->ptr = p; + p->up = (struct svgc_dlist_node *)0; + p->down = (struct svgc_dlist_node *)0; + } + else + { + plist->root_up->up = p; + p->up = (struct svgc_dlist_node *)0; + p->down = plist->root_up; + plist->root_up = p; + } + return( p ); + } + else + { + return( (struct svgc_dlist_node *)0 ); + } +} + +struct svgc_dlist_node *insert_into_svgc_dlist_down( struct svgc_dlist * plist, struct svgc_dlist_node * p ) +{ + if( plist && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->root_up = plist->root_down = plist->ptr = p; + p->up = (struct svgc_dlist_node *)0; + p->down = (struct svgc_dlist_node *)0; + } + else + { + plist->root_down->down = p; + p->up = plist->root_down; + p->down = (struct svgc_dlist_node *)0; + plist->root_down = p; + plist->ptr = p; + } + return( p ); + } + else + { + return( (struct svgc_dlist_node *)0 ); + } +} + +struct svgc_dlist_node * +insert_into_svgc_dlist_after( struct svgc_dlist * plist, + struct svgc_dlist_node * point, struct svgc_dlist_node * p ) +{ + if( plist && point && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->root_up = plist->root_down = plist->ptr = p; + p->up = (struct svgc_dlist_node *)0; + p->down = (struct svgc_dlist_node *)0; + } + else + { + point->down->up = p; + p->down = point->down; + point->down = p; + p->up = point; + } + return( p ); + } + else + { + return( (struct svgc_dlist_node *)0 ); + } +} + + +/* + lookup svgc_dlist_node, since up for down ( down ). + */ +struct svgc_dlist_node *lookup_svgc_dlist_node_down( struct svgc_dlist * plist, char * id ) +{ + struct svgc_dlist_node *p; + + if( !plist && !id && !id[0] ) return( (struct svgc_dlist_node *)0 ); + + if( plist != (struct svgc_dlist *)0 ) + { + for( p = plist->root_up; p != (struct svgc_dlist_node *)0; p = p->down ) + { + if( strcmp( p->id, id ) == 0 ) return( p ); + } + } + return( (struct svgc_dlist_node *)0 ); +} + +/* + lookup svgc_dlist_node, since down for up ( down ). + */ +struct svgc_dlist_node *lookup_svgc_dlist_node_up( struct svgc_dlist * plist, char * id ) +{ + struct svgc_dlist_node *p; + + if( !plist && !id && !id[0] ) return( (struct svgc_dlist_node *)0 ); + + if( plist != (struct svgc_dlist *)0 ) + { + for( p = plist->root_down; p != (struct svgc_dlist_node *)0; p = p->up ) + { + if( strcmp( p->id, id ) == 0 ) return( p ); + } + } + return( (struct svgc_dlist_node *)0 ); +} + + + +/* detach svgc_dlist_node */ +struct svgc_dlist_node *detach_svgc_dlist_node( struct svgc_dlist * plist, struct svgc_dlist_node * p ) +{ + if( plist && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + /* EMPTY svgc_dlist */ + return( (struct svgc_dlist_node *)0 ); + } + else + { + if( p->up ) p->up->down = p->down; + if( p->down ) p->down->up = p->up; + + + if( p == plist->root_up ) plist->root_up = p->down; + if( p == plist->root_down ) plist->root_down = p->up; + if( p == plist->ptr ) plist->ptr = p->down; + + return( p ); + } + } + else + { + return( (struct svgc_dlist_node *)0 ); + } +} + +/* detach svgc_dlist_node by id */ +struct svgc_dlist_node *detach_svgc_dlist_node_by_id( struct svgc_dlist * plist, char * id ) +{ + struct svgc_dlist_node *p = (struct svgc_dlist_node *)0; + + p = lookup_svgc_dlist_node_down( plist, id ); + + if( plist && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + /* EMPTY svgc_dlist */ + return( (struct svgc_dlist_node *)0 ); + } + else + { + if( p->up ) p->up->down = p->down; + if( p->down ) p->down->up = p->up; + + + if( p == plist->root_up ) plist->root_up = p->down; + if( p == plist->root_down ) plist->root_down = p->up; + if( p == plist->ptr ) plist->ptr = p->down; + + return( p ); + } + } + else + { + return( (struct svgc_dlist_node *)0 ); + } +} + +/* remove svgc_dlist_node */ +int +remove_svgc_dlist_node( struct svgc_dlist * plist, + struct svgc_dlist_node * p, FREE_SVGC_DLIST_NODE_DATA free_data ) +{ + if( plist && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + /* EMPTY svgc_dlist */ + return( 0 ); /* FALSE */ + } + else + { + if( p->up ) p->up->down = p->down; + if( p->down ) p->down->up = p->up; + + + if( p == plist->root_up ) plist->root_up = p->down; + if( p == plist->root_down ) plist->root_down = p->up; + if( p == plist->ptr ) plist->ptr = p->down; + + if( p->id ) free( p->id ); + if( free_data ) free_data( p->data ); + free( p ); + + return( 1 ); /* TRUE */ + } + } + else + { + return( 0 ); /* FALSE */ + } +} + +/* remove svgc_dlist_node by id */ +int remove_svgc_dlist_node_by_id( struct svgc_dlist * plist, char * id, FREE_SVGC_DLIST_NODE_DATA free_data ) +{ + struct svgc_dlist_node *p = (struct svgc_dlist_node *)0; + + p = lookup_svgc_dlist_node_down( plist, id ); + + if( plist && p ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + /* EMPTY svgc_dlist */ + return( 0 ); /* FALSE */ + } + else + { + if( p->up ) p->up->down = p->down; + if( p->down ) p->down->up = p->up; + + + if( p == plist->root_up ) plist->root_up = p->down; + if( p == plist->root_down ) plist->root_down = p->up; + if( p == plist->ptr ) plist->ptr = p->down; + + if( p->id ) free( p->id ); + if( free_data ) free_data( p->data ); + free( p ); + + return( 1 ); /* TRUE */ + } + } + else + { + return( 0 ); /* FALSE */ + } +} + + +/* move svgc_dlist_node: detach from SLIST, and insert_into_svgc_dlist_down() to TLIST */ +struct svgc_dlist_node * +move_svgc_dlist_node( struct svgc_dlist * tlist, struct svgc_dlist * slist, + struct svgc_dlist_node * p ) +{ + if( !slist || !p ) return( (struct svgc_dlist_node *)0 ); + + if( slist->root_up == (struct svgc_dlist_node *)0 && + slist->root_down == (struct svgc_dlist_node *)0 ) + { + /* EMPTY partition_list */ + return( (struct svgc_dlist_node *)0 ); + } + else + { + if( p->up ) p->up->down = p->down; + if( p->down ) p->down->up = p->up; + + + if( p == slist->root_up ) slist->root_up = p->down; + if( p == slist->root_down ) slist->root_down = p->up; + if( p == slist->ptr ) slist->ptr = p->down; + + insert_into_svgc_dlist_down( tlist, p ); + + return( p ); + } +} + + + +void set_svgc_dlist_ptr_up( struct svgc_dlist * plist ) +{ + if( !plist ) return; + plist->ptr = plist->root_up; +} +void set_svgc_dlist_ptr_down( struct svgc_dlist * plist ) +{ + if( !plist ) return; + plist->ptr = plist->root_down; +} + +void for_each_svgc_dlist_node_data( struct svgc_dlist * plist, FOR_EACH_SVGC_DLIST_NODE_DATA func ) +{ + struct svgc_dlist_node *p; + + if( plist != (struct svgc_dlist *)0 ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->ptr = (struct svgc_dlist_node *)0; + return; + } + + for( p = plist->root_up; p != (struct svgc_dlist_node *)0; p = p->down ) + { + if( p->up ) + { + if( func ) func( p->up->data ); + } + } + if( func ) func( plist->root_down->data ); + } +} + +void printf_svgc_dlist( struct svgc_dlist * plist, PRINT_SVGC_DLIST_NODE_DATA func ) +{ + struct svgc_dlist_node *p; + + fprintf( stderr, "\nD-LIST:\n" + "======\n\n" ); + + if( plist != (struct svgc_dlist *)0 ) + { + if( plist->root_up == (struct svgc_dlist_node *)0 && + plist->root_down == (struct svgc_dlist_node *)0 ) + { + plist->ptr = (struct svgc_dlist_node *)0; + return; + } + + for( p = plist->root_up; p != (struct svgc_dlist_node *)0; p = p->down ) + { + if( p->up ) + { + if( p->up->id ) + fprintf( stderr, " id = '%s':\n", p->up->id ); + else + fprintf( stderr, " id = '':\n" ); + if( func ) func( p->up->data ); + } + } + if( plist->root_down->id ) + fprintf( stderr, " id = '%s':\n", plist->root_down->id ); + else + fprintf( stderr, " id = '':\n" ); + if( func ) func( plist->root_down->data ); + } +} diff --git a/src/svgc-dlist.h b/src/svgc-dlist.h new file mode 100644 index 0000000..676c98a --- /dev/null +++ b/src/svgc-dlist.h @@ -0,0 +1,90 @@ + +/*************************************************************** + SVGC-DLIST.H + + This file containt declarations of SVG double linked + list functions. + ***************************************************************/ + +#ifndef __SVGC_DLIST_H__ +#define __SVGC_DLIST_H__ + +#if defined( __cplusplus ) +extern "C" +{ +#endif + +/* the structure of a double linked list entry */ +typedef struct svgc_dlist_node svgc_dlist_node; +struct svgc_dlist_node +{ + struct svgc_dlist_node *up; + struct svgc_dlist_node *down; + + char *id; + void *data; +}; + +/* the structure of a double linked list instance */ +typedef struct svgc_dlist svgc_dlist; +struct svgc_dlist +{ + struct svgc_dlist_node *root_up; + struct svgc_dlist_node *root_down; + struct svgc_dlist_node *ptr; +}; + +typedef void ( *FREE_SVGC_DLIST_NODE_DATA )( void * ); +typedef void ( *FOR_EACH_SVGC_DLIST_NODE_DATA )( void * ); +typedef void ( *PRINT_SVGC_DLIST_NODE_DATA )( void * ); + + +extern struct svgc_dlist *create_svgc_dlist( void ); +extern void free_svgc_dlist( struct svgc_dlist *plist, FREE_SVGC_DLIST_NODE_DATA free_data ); + +/* add svgc_dlist_node up */ +extern struct svgc_dlist_node *add_to_svgc_dlist_up( struct svgc_dlist *plist, char *id, void *data ); +/* add svgc_dlist_node down */ +extern struct svgc_dlist_node *add_to_svgc_dlist_down( struct svgc_dlist *plist, char *id, void *data ); + +extern struct svgc_dlist_node *insert_into_svgc_dlist_up( struct svgc_dlist *plist, struct svgc_dlist_node *p ); + +extern struct svgc_dlist_node *insert_into_svgc_dlist_down( struct svgc_dlist *plist, struct svgc_dlist_node *p ); + +extern struct svgc_dlist_node *insert_into_svgc_dlist_after( struct svgc_dlist *plist, struct svgc_dlist_node *point, struct svgc_dlist_node *p ); + +/* + lookup svgc_dlist_node, since up for down ( down ). + */ +extern struct svgc_dlist_node *lookup_svgc_dlist_node_down( struct svgc_dlist *plist, char *id ); + +/* + lookup svgc_dlist_node, since down for up ( down ). + */ +extern struct svgc_dlist_node *lookup_svgc_dlist_node_up( struct svgc_dlist *plist, char *id ); + +/* detach svgc_dlist_node */ +extern struct svgc_dlist_node *detach_svgc_dlist_node( struct svgc_dlist *plist, struct svgc_dlist_node *p ); +/* detach svgc_dlist_node by id */ +extern struct svgc_dlist_node *detach_svgc_dlist_node_by_id( struct svgc_dlist *plist, char *id ); + +/* remove svgc_dlist_node */ +extern int remove_svgc_dlist_node( struct svgc_dlist *plist, struct svgc_dlist_node *p, FREE_SVGC_DLIST_NODE_DATA free_data ); +/* remove svgc_dlist_node by id */ +int remove_svgc_dlist_node_by_id( struct svgc_dlist *plist, char *id, FREE_SVGC_DLIST_NODE_DATA free_data ); + +/* move svgc_dlist_node: detach from SLIST, and insert_into_svgc_dlist_down() to TLIST */ +extern struct svgc_dlist_node *move_svgc_dlist_node( struct svgc_dlist *tlist, struct svgc_dlist *slist, struct svgc_dlist_node *p ); + + +extern void set_svgc_dlist_ptr_up( struct svgc_dlist *plist ); +extern void set_svgc_dlist_ptr_down( struct svgc_dlist *plist ); + +extern void for_each_svgc_dlist_node_data( struct svgc_dlist *plist, FOR_EACH_SVGC_DLIST_NODE_DATA func ); +extern void printf_svgc_dlist( struct svgc_dlist *plist, PRINT_SVGC_DLIST_NODE_DATA func ); + +#if defined( __cplusplus ) +} /* extern "C" */ +#endif + +#endif /* __SVGC_DLIST_H__ */ diff --git a/src/svgc.c b/src/svgc.c new file mode 100644 index 0000000..7c0a209 --- /dev/null +++ b/src/svgc.c @@ -0,0 +1,2461 @@ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> /* exit() */ +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + + +#include <svgc.h> + +/* Embedded CSS and ECMAScript definitions use the ordinary SVG node header. */ +struct __svg_embedded_text +{ + int type; + char *id; + struct __svg_style *style; + + char *content; + size_t length; +}; + +int current_id = 3700; + +/*************************************************************** + __svg_str_dup() + + ²ëÔÕÛïÕâ ßÐÜïâì ÔÛï ÔãÑÛØÚÐâÐ áâàÞÚØ S Ø ÚÞߨàãÕâ Ò ÝÕÕ + áÞÔÕàÖØÜÞÕ S. + + ²Þ×ÒàÐéÐÕÜÞÕ ×ÝÐçÕÝØÕ: + ÃÚÐ×ÐâÕÛì ÝÐ áÞ×ÔÐÝÝëÙ ÔãÑÛØÚÐâ. + ***************************************************************/ +char * +__svg_str_dup( const char * s ) +{ + int len = 0; + void *new = NULL; + + if( s == (char *)NULL ) return( (char *)NULL ); + + len = (strlen( s ) + 1) * sizeof( char ); + new = malloc( len ); + + if( new == (void *)NULL ) return( (char *)NULL ); + + return( (char *)memcpy( new, (void *)s, len ) ); + +} /* End of __svg_str_dup() */ + + +/*************************************************************** + POINTS: + ***************************************************************/ +void +__svg_free_point( void * data ) +{ + struct __svg_point *p = (struct __svg_point *)data; + + if( !p ) return; + + free( p ); +} + +void __svg_free_points( struct __svg_points * p ) +{ + if( !p ) return; + if( p->plist ) + { + /* clear svgc_dlist */ + free_svgc_dlist( p->plist, __svg_free_point ); + } + + free( p ); +} + +struct __svg_points * +__svg_new_points( void ) +{ + struct __svg_points *new = (struct __svg_points *)NULL; + + new = (struct __svg_points *)malloc( sizeof(struct __svg_points) ); + if( new == (struct __svg_points *)NULL ) return( (struct __svg_points *)NULL ); + memset( (void *)new, 0, sizeof(struct __svg_points) ); + + new->plist = create_svgc_dlist(); + if( !new->plist ) + { + free( new ); + return( (struct __svg_points *)NULL ); + } + new->count = 0; + + return( (struct __svg_points *)new ); +} + +int __svg_add_point( struct __svg_points * ps, double x, double y ) +{ + struct __svg_point *new = (struct __svg_point *)NULL; + struct svgc_dlist_node *np = NULL; + char buf[256]; + int error = -1; /* EINVAL */ + + if( !ps || !ps->plist) return( error ); + error = 0; + + new = (struct __svg_point *)malloc( sizeof(struct __svg_point) ); + if( new == (struct __svg_point *)NULL ) return( error ); + memset( (void *)new, 0, sizeof(struct __svg_point) ); + + new->x = x; + new->y = y; + error = ++ps->count; + sprintf( buf, "%d", error ); + + np = add_to_svgc_dlist_down( ps->plist, buf, (void *)new ); + if( !np ) error = 0; + + return( error ); +} + +/*************************************************************** + PATH CMDS: + ***************************************************************/ +void +__svg_free_cmd( void * data ) +{ + struct __svg_cmd *cmd = (struct __svg_cmd *)data; + + if( !cmd ) return; + if( !cmd->points ) + __svg_free_points( cmd->points ); + + free( cmd ); +} + +void __svg_free_cmds( struct __svg_cmds * cmds ) +{ + if( !cmds ) return; + if( cmds->plist ) + { + /* clear svgc_dlist */ + free_svgc_dlist( cmds->plist, __svg_free_cmd ); + } + + free( cmds ); +} + +struct __svg_cmd * +__svg_new_cmd( char cmd, struct __svg_points * ps ) +{ + struct __svg_cmd *new = NULL; + + if( !ps ) return( new ); + + new = (struct __svg_cmd *)malloc( sizeof( struct __svg_cmd ) ); + if( new == (struct __svg_cmd *)NULL ) return( (struct __svg_cmd *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_cmd ) ); + + new->cmd = cmd; + new->points = ps; + + return( (struct __svg_cmd *)new ); +} + +struct __svg_cmds * +__svg_new_cmds( void ) +{ + struct __svg_cmds *new = (struct __svg_cmds *)NULL; + + new = (struct __svg_cmds *)malloc( sizeof(struct __svg_cmds) ); + if( new == (struct __svg_cmds *)NULL ) return( (struct __svg_cmds *)NULL ); + memset( (void *)new, 0, sizeof(struct __svg_cmds) ); + + new->plist = create_svgc_dlist(); + if( !new->plist ) + { + free( new ); + return( (struct __svg_cmds *)NULL ); + } + new->count = 0; + + return( (struct __svg_cmds *)new ); +} + +int __svg_add_cmd( struct __svg_cmds * cmds, struct __svg_cmd * cmd ) +{ + struct svgc_dlist_node *np = NULL; + char buf[256]; + int error = -1; /* EINVAL */ + + if( !cmd || !cmds || !cmds->plist) return( error ); + error = 0; + + error = ++cmds->count; + sprintf( buf, "%d", error ); + + np = add_to_svgc_dlist_down( cmds->plist, buf, (void *)cmd ); + if( !np ) error = 0; + + return( error ); +} + +/* + Start a new sub-path at the given (x,y) coordinate. M (uppercase) indicates + that absolute coordinates will follow; m (lowercase) indicates that relative + coordinates will follow. If a relative moveto (m) appears as the first + element of the path, then it is treated as a pair of absolute coordinates. + If a moveto is followed by multiple pairs of coordinates, the subsequent + pairs are treated as implicit lineto commands. + + Args: (x y)+ + */ +int +__svg_add_moveto_cmd( struct __svg_cmds * cmds, int abs, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'm'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + if( abs ) c = 'M'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Close the current subpath by drawing a straight line from the current point + to current subpath's initial point. + */ +int +__svg_add_closepath_cmd( struct __svg_cmds * cmds, int abs ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'z'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + error = __svg_add_point( points, 0.0, 0.0 ); + if( error < 1 ) return( error ); + if( abs ) c = 'Z'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draw a line from the current point to the given (x,y) coordinate which + becomes the new current point. L (uppercase) indicates that absolute + coordinates will follow; l (lowercase) indicates that relative coordinates + will follow. A number of coordinates pairs may be specified to draw + a polyline. At the end of the command, the new current point is set to the + final set of coordinates provided. + + Args: (x y)+ + */ +int +__svg_add_lineto_cmd( struct __svg_cmds * cmds, int abs, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'l'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + if( abs ) c = 'L'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). + H (uppercase) indicates that absolute coordinates will follow; h (lowercase) + indicates that relative coordinates will follow. Multiple x values can + be provided (although usually this doesn't make sense). At the end of the + command, the new current point becomes (x, cpy) for the final value of x. + + Args: (x)+ + */ +int +__svg_add_hlineto_cmd( struct __svg_cmds * cmds, int abs, double x ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'h'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + error = __svg_add_point( points, x, 0.0 ); + if( error < 1 ) return( error ); + if( abs ) c = 'H'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws a vertical line from the current point (cpx, cpy) to (cpx, y). + V (uppercase) indicates that absolute coordinates will follow; v (lowercase) + indicates that relative coordinates will follow. Multiple y values can be + provided (although usually this doesn't make sense). At the end of the + command, the new current point becomes (cpx, y) for the final value of y. + + Args: (y)+ + */ +int +__svg_add_vlineto_cmd( struct __svg_cmds * cmds, int abs, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'v'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + error = __svg_add_point( points, 0.0, y ); + if( error < 1 ) return( error ); + if( abs ) c = 'V'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws a cubic Bezier curve from the current point to (x,y) using (x1,y1) as + the control point at the beginning of the curve and (x2,y2) as the control + point at the end of the curve. C (uppercase) indicates that absolute + coordinates will follow; c (lowercase) indicates that relative coordinates + will follow. Multiple sets of coordinates may be specified to draw a + polybezier. At the end of the command, the new current point becomes the + final (x,y) coordinate pair used in the polybezier. + + Args: (x1 y1 x2 y2 x y)+ + */ +int +__svg_add_curveto_cmd( struct __svg_cmds * cmds, int abs, + double x1, double y1, double x2, double y2, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'c'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + + error = __svg_add_point( points, x1, y1 ); + if( error < 1 ) return( error ); + error = __svg_add_point( points, x2, y2 ); + if( error < 1 ) return( error ); + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + + if( abs ) c = 'C'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws a cubic Bezier curve from the current point to (x,y). The first control + point is assumed to be the reflection of the second control point on the + previous command relative to the current point. (If there is no previous + command or if the previous command was not an C, c, S or s, assume the first + control point is coincident with the current point.) (x2,y2) is the second + control point (i.e., the control point at the end of the curve). + S (uppercase) indicates that absolute coordinates will follow; s (lowercase) + indicates that relative coordinates will follow. Multiple sets of coordinates + may be specified to draw a polybezier. At the end of the command, the new + current point becomes the final (x,y) coordinate pair used in the polybézier. + + Args: (x2 y2 x y)+ + */ +int +__svg_add_short_curveto_cmd( struct __svg_cmds * cmds, int abs, double x2, double y2, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 's'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + + error = __svg_add_point( points, x2, y2 ); + if( error < 1 ) return( error ); + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + + if( abs ) c = 'S'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws a quadratic Bezier curve from the current point to (x,y) using (x1,y1) + as the control point. Q (uppercase) indicates that absolute coordinates will + follow; q (lowercase) indicates that relative coordinates will follow. + Multiple sets of coordinates may be specified to draw a polybezier. At the + end of the command, the new current point becomes the final (x,y) coordinate + pair used in the polybezier. + + Args: (x1 y1 x y)+ + */ +int +__svg_add_quadro_curveto_cmd( struct __svg_cmds * cmds, int abs, double x1, double y1, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'q'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + + error = __svg_add_point( points, x1, y1 ); + if( error < 1 ) return( error ); + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + + if( abs ) c = 'Q'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws a quadratic Bezier curve from the current point to (x,y). The control + point is assumed to be the reflection of the control point on the previous + command relative to the current point. (If there is no previous command or if + the previous command was not a Q, q, T or t, assume the control point is + coincident with the current point.) T (uppercase) indicates that absolute + coordinates will follow; t (lowercase) indicates that relative coordinates + will follow. At the end of the command, the new current point becomes the + final (x,y) coordinate pair used in the polybezier. + + Args: (x y)+ + */ +int +__svg_add_short_quadro_curveto_cmd( struct __svg_cmds * cmds, int abs, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 't'; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + + if( abs ) c = 'T'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + +/* + Draws an elliptical arc from the current point to (x, y). The size and + orientation of the ellipse are defined by two radii (rx, ry) and an + x-axis-rotation, which indicates how the ellipse as a whole is rotated + relative to the current coordinate system. The center (cx, cy) of the + ellipse is calculated automatically to satisfy the constraints imposed + by the other parameters. large-arc-flag and sweep-flag contribute to the + automatic calculations and help determine how the arc is drawn. + + Args: (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ + */ +int +__svg_add_arc_cmd( struct __svg_cmds * cmds, int abs, + double rx, double ry, double x_axis_rotation, + int large_arc_flag, int sweep_flag, double x, double y ) +{ + struct __svg_points *points = NULL; + struct __svg_cmd *cmd = NULL; + char c = 'a'; + double lf = 0.0, sf = 0.0; + int error = -1; /* EINVAL */ + + if( !cmds || !cmds->plist) return( error ); + error = 0; + + points = __svg_new_points(); + if( !points ) return( error ); + + error = __svg_add_point( points, rx, ry ); + if( error < 1 ) return( error ); + error = __svg_add_point( points, x_axis_rotation, 0.0 ); + if( error < 1 ) return( error ); + if( large_arc_flag ) lf = 1.0; + if( sweep_flag ) sf = 1.0; + error = __svg_add_point( points, lf, sf ); + if( error < 1 ) return( error ); + error = __svg_add_point( points, x, y ); + if( error < 1 ) return( error ); + + if( abs ) c = 'A'; + cmd = __svg_new_cmd( c, points ); + error = __svg_add_cmd( cmds, cmd ); + + return( error ); +} + + +/*************************************************************** + STYLE: + ***************************************************************/ + +/*************************************************************** + Default values: + ***************************************************************/ +static struct __svg_rgb + default_stroke_rgb = { 0, 0, 0 }; +static const char *default_stroke = STROKE_DEFAULT; +static double default_stroke_width = 1.0; +static const char *default_stroke_linecap = LINECAP_DEFAULT; +static const char *default_stroke_linejoin = LINEJOIN_DEFAULT; +static const char *default_stroke_dasharray = DASHARRAY_DEFAULT; + +static struct __svg_rgb + default_fill_rgb = { 255, 255, 255 }; +static const char *default_fill = FILL_DEFAULT; + +static struct __svg_rgb + default_stop_color_rgb = { 0, 0, 0 }; +static const char *default_stop_color = STOP_COLOR_DEFAULT; + +static double default_stop_opacity = STOP_OPACITY_DEFAULT; + +/* static buffer for stroke: */ +static char stroke_buf[32]; + +/* static buffer for fill: */ +static char fill_buf[32]; + +/* static buffer for stop color: */ +static char stop_color_buf[32]; + +static const char *default_text_font_family = TEXT_FONT_FAMILY_DEFAULT; +static int default_text_font_size = TEXT_FONT_SIZE_DEFAULT; +static struct __svg_rgb + default_text_fill_rgb = { 255, 255, 255 }; +static const char *default_text_fill = TEXT_FILL_DEFAULT; +/* static buffer for text fill: */ +static char text_fill_buf[32]; +static char text_family_buf[128]; + + +void __svg_set_scene_default_style( void ) +{ + default_stroke_rgb.r = 0; + default_stroke_rgb.g = 0; + default_stroke_rgb.b = 0; + + default_stroke = STROKE_DEFAULT; + default_stroke_width = 1.0; + default_stroke_linecap = LINECAP_DEFAULT; + default_stroke_linejoin = LINEJOIN_DEFAULT; + default_stroke_dasharray = DASHARRAY_DEFAULT; + + + default_fill_rgb.r = 255; + default_fill_rgb.g = 255; + default_fill_rgb.b = 255; + + default_fill = FILL_DEFAULT; + + default_stop_color_rgb.r = 0; + default_stop_color_rgb.g = 0; + default_stop_color_rgb.b = 0; + + default_stop_opacity = STOP_OPACITY_DEFAULT; + + default_text_fill_rgb.r = 255; + default_text_fill_rgb.g = 255; + default_text_fill_rgb.b = 255; + + default_text_fill = TEXT_FILL_DEFAULT; + default_text_font_family = TEXT_FONT_FAMILY_DEFAULT; + default_text_font_size = TEXT_FONT_SIZE_DEFAULT; +} + +void __svg_set_scene_stroke( const char * stroke ) +{ + default_stroke = stroke; +} +void __svg_set_scene_stroke_rgb( int r, int g, int b ) +{ + default_stroke_rgb.r = r; + default_stroke_rgb.g = g; + default_stroke_rgb.b = b; + + sprintf( stroke_buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + default_stroke = stroke_buf; +} +void __svg_set_scene_stroke_width( double width ) +{ + default_stroke_width = width; +} +void __svg_set_scene_stroke_linecap( const char * linecap ) +{ + default_stroke_linecap = linecap; +} +void __svg_set_scene_stroke_linejoin( const char * linejoin ) +{ + default_stroke_linejoin = linejoin; +} +void __svg_set_scene_stroke_dasharray( const char * dasharray ) +{ + default_stroke_dasharray = dasharray; +} + +void __svg_set_scene_fill( const char * fill ) +{ + default_fill = fill; +} +void __svg_set_scene_fill_rgb( int r, int g, int b ) +{ + default_fill_rgb.r = r; + default_fill_rgb.g = g; + default_fill_rgb.b = b; + + sprintf( fill_buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + default_fill = fill_buf; +} + +void __svg_set_scene_stop_color( const char * stop_color ) +{ + default_stop_color = stop_color; +} +void __svg_set_scene_stop_color_rgb( int r, int g, int b ) +{ + default_stop_color_rgb.r = r; + default_stop_color_rgb.g = g; + default_stop_color_rgb.b = b; + + sprintf( stop_color_buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + default_stop_color = stop_color_buf; +} + +void __svg_set_scene_stop_opacity( double stop_opacity ) +{ + default_stop_opacity = stop_opacity; +} + +void __svg_set_scene_text_fill( const char * fill ) +{ + default_text_fill = fill; +} +void __svg_set_scene_text_fill_rgb( int r, int g, int b ) +{ + default_text_fill_rgb.r = r; + default_text_fill_rgb.g = g; + default_text_fill_rgb.b = b; + + sprintf( text_fill_buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + default_text_fill = text_fill_buf; +} +void __svg_set_scene_text_font_family( const char * family ) +{ + if( !family || !family[0] || + strlen( family ) > (sizeof( text_family_buf ) - 1) ) + return; + + sprintf( text_family_buf, "%s", family ); + + default_text_font_family = text_family_buf; +} +void __svg_set_scene_text_font_size( int size ) +{ + default_text_font_size = size; +} + +void +__svg_free_stroke( struct __svg_stroke * s ) +{ + if( !s ) return; + if( s->use_stroke ) free( s->use_stroke ); + if( s->stroke_linecap ) free( s->stroke_linecap ); + if( s->stroke_linejoin ) free( s->stroke_linejoin ); + if( s->stroke_dasharray ) free( s->stroke_dasharray ); + free( s ); +} + +struct __svg_stroke * +__svg_new_stroke( void ) +{ + struct __svg_stroke *new = NULL; + + new = malloc( sizeof( struct __svg_stroke ) ); + + if( new == (struct __svg_stroke *)NULL ) return( (struct __svg_stroke *)NULL ); + + new->stroke.r = default_stroke_rgb.r; + new->stroke.g = default_stroke_rgb.g; + new->stroke.b = default_stroke_rgb.b; + + new->stroke_width = default_stroke_width; + + new->use_stroke = __svg_str_dup( default_stroke ); + new->stroke_linecap = __svg_str_dup( default_stroke_linecap ); + new->stroke_linejoin = __svg_str_dup( default_stroke_linejoin ); + new->stroke_dasharray = __svg_str_dup( default_stroke_dasharray ); + + return( (struct __svg_stroke *)new ); +} + +void +__svg_free_fill( struct __svg_fill * f ) +{ + if( !f ) return; + if( f->use_fill ) free( f->use_fill ); + free( f ); +} + +struct __svg_fill * +__svg_new_fill( void ) +{ + struct __svg_fill *new = NULL; + + new = malloc( sizeof( struct __svg_fill ) ); + + if( new == (struct __svg_fill *)NULL ) return( (struct __svg_fill *)NULL ); + + new->fill.r = default_fill_rgb.r; + new->fill.g = default_fill_rgb.g; + new->fill.b = default_fill_rgb.b; + + new->use_fill = __svg_str_dup( default_fill ); + + return( (struct __svg_fill *)new ); +} + + +void +__svg_free_stop_color( struct __svg_stop_color * sc ) +{ + if( !sc ) return; + if( sc->use_stop_color ) free( sc->use_stop_color ); + free( sc ); +} + +struct __svg_stop_color * +__svg_new_stop_color( void ) +{ + struct __svg_stop_color *new = NULL; + + new = malloc( sizeof( struct __svg_stop_color ) ); + + if( new == (struct __svg_stop_color *)NULL ) return( (struct __svg_stop_color *)NULL ); + + new->stop_color.r = default_stop_color_rgb.r; + new->stop_color.g = default_stop_color_rgb.g; + new->stop_color.b = default_stop_color_rgb.b; + + new->use_stop_color = __svg_str_dup( default_stop_color ); + + return( (struct __svg_stop_color *)new ); +} + + +void +__svg_free_stop_opacity( struct __svg_stop_opacity * so ) +{ + if( !so ) return; + free( so ); +} + +struct __svg_stop_opacity * +__svg_new_stop_opacity( void ) +{ + struct __svg_stop_opacity *new = NULL; + + new = malloc( sizeof( struct __svg_stop_opacity ) ); + + if( new == (struct __svg_stop_opacity *)NULL ) return( (struct __svg_stop_opacity *)NULL ); + + new->stop_opacity = default_stop_opacity; + + return( (struct __svg_stop_opacity *)new ); +} + + +void +__svg_free_style( struct __svg_style * style ) +{ + if( !style ) return; + if( style->stroke ) __svg_free_stroke ( style->stroke ); + if( style->fill ) __svg_free_fill ( style->fill ); + if( style->stop_color ) __svg_free_stop_color ( style->stop_color ); + if( style->stop_opacity ) __svg_free_stop_opacity ( style->stop_opacity ); + free( style ); +} + +struct __svg_style * +__svg_new_style( void ) +{ + struct __svg_style *new = NULL; + + new = malloc( sizeof( struct __svg_style ) ); + + if( new == (struct __svg_style *)NULL ) return( (struct __svg_style *)NULL ); + + new->stroke = __svg_new_stroke(); + new->fill = __svg_new_fill(); + new->stop_color = __svg_new_stop_color(); + new->stop_opacity = __svg_new_stop_opacity(); + if( !new->stroke || !new->fill || !new->stop_color || !new->stop_opacity ) + { + __svg_free_style( new ); + return( (struct __svg_style *)NULL ); + } + return( (struct __svg_style *)new ); +} + + +void +__svg_free_node( void * data ) +{ + struct __svg_node *node = (struct __svg_node *)data; + + if( !node ) return; + if( node->id ) free( node->id ); + if( node->style ) __svg_free_style( node->style ); + + if( node->type == SVG_SHEET ) + { + if( ((struct __svg_sheet *)node)->defs ) + { + /* clear svgc_dlist */ + free_svgc_dlist( ((struct __svg_sheet *)node)->defs, __svg_free_node ); + } + } + + if( node->type == SVG_POLYGON ) + __svg_free_points( ((struct __svg_polygon *)node)->points ); + + if( node->type == SVG_POLYLINE ) + __svg_free_points( ((struct __svg_polyline *)node)->points ); + + if( node->type == SVG_PATH ) + __svg_free_cmds( ((struct __svg_path *)node)->cmds ); + + if( node->type == SVG_TEXT ) + { + if( ((struct __svg_text *)node)->text ) + free( ((struct __svg_text *)node)->text ); + if( ((struct __svg_text *)node)->font_family ) + free( ((struct __svg_text *)node)->font_family ); + if( ((struct __svg_text *)node)->fill ) + free( ((struct __svg_text *)node)->fill ); + } + + if( node->type == SVG_IMAGE ) + { + if( ((struct __svg_image *)node)->href ) + free( ((struct __svg_image *)node)->href ); + if( ((struct __svg_image *)node)->title ) + free( ((struct __svg_image *)node)->title ); + } + + if( node->type == SVG_CSS || node->type == SVG_SCRIPT ) + { + free( ((struct __svg_embedded_text *)node)->content ); + } + + if( node->type == SVG_L_GRADIENT ) + { + if( ((struct __svg_l_gradient *)node)->units ) + free( ((struct __svg_l_gradient *)node)->units ); + if( ((struct __svg_l_gradient *)node)->spread ) + free( ((struct __svg_l_gradient *)node)->spread ); + if( ((struct __svg_l_gradient *)node)->stops ) + { + /* clear svgc_dlist */ + free_svgc_dlist( ((struct __svg_l_gradient *)node)->stops, __svg_free_node ); + } + } + + if( node->type == SVG_R_GRADIENT ) + { + if( ((struct __svg_r_gradient *)node)->units ) + free( ((struct __svg_r_gradient *)node)->units ); + if( ((struct __svg_r_gradient *)node)->spread ) + free( ((struct __svg_r_gradient *)node)->spread ); + if( ((struct __svg_r_gradient *)node)->stops ) + { + /* clear svgc_dlist */ + free_svgc_dlist( ((struct __svg_r_gradient *)node)->stops, __svg_free_node ); + } + } + + free( node ); +} + + +struct __svg_node * +__svg_new_sheet( double width, double height, + double left_margin, double right_margin, + double top_margin, double bottom_margin ) +{ + struct __svg_sheet *new = NULL; + char buf[256]; + + new = (struct __svg_sheet *)malloc( sizeof( struct __svg_sheet ) ); + if( new == (struct __svg_sheet *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_sheet ) ); + new->type = SVG_SHEET; + sprintf( buf, "svg%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->width = width; + new->height = height; + new->left_margin = left_margin; + new->right_margin = right_margin; + new->top_margin = top_margin; + new->bottom_margin = bottom_margin; + + new->defs = create_svgc_dlist(); + if( !new->defs ) + { + if( new->id ) free( new->id ); + if( new->style ) __svg_free_style( new->style );; + free( new ); + return( (struct __svg_node *)NULL ); + } + + return( (struct __svg_node *)new ); +} + +int +__svg_sheet_add_definition( void * sheet, struct __svg_node * definition ) +{ + struct __svg_sheet *ps = (struct __svg_sheet *)sheet; + struct svgc_dlist_node *np = NULL; + int error = -1; /* EINVAL */ + + if( !ps || !ps->defs || !definition ) return( error ); + error = 0; + + if( !ps->defs ) return( error ); + + np = add_to_svgc_dlist_down( ps->defs, definition->id, (void *)definition ); + if( np ) error = 1; + + return( error ); +} + + +/* Validate UTF-8 and reject code points forbidden by XML 1.0. */ +static int +__svg_embedded_text_valid_utf8( const unsigned char *text, size_t length ) +{ + size_t i = 0; + unsigned int cp; + unsigned int b; + int n, j; + + while( i < length ) + { + b = text[i++]; + if( b < 0x80 ) + { + cp = b; + n = 0; + } + else if( b >= 0xC2 && b <= 0xDF ) + { + cp = b & 0x1F; + n = 1; + } + else if( b >= 0xE0 && b <= 0xEF ) + { + cp = b & 0x0F; + n = 2; + } + else if( b >= 0xF0 && b <= 0xF4 ) + { + cp = b & 0x07; + n = 3; + } + else return( 0 ); + + if( length - i < (size_t)n ) return( 0 ); + for( j = 0; j < n; ++j ) + { + b = text[i++]; + if( (b & 0xC0) != 0x80 ) return( 0 ); + cp = (cp << 6) | (b & 0x3F); + } + + if( n == 1 && cp < 0x80 ) return( 0 ); + if( n == 2 && cp < 0x800 ) return( 0 ); + if( n == 3 && cp < 0x10000 ) return( 0 ); + if( cp != 0x09 && cp != 0x0A && cp != 0x0D && cp < 0x20 ) return( 0 ); + if( cp >= 0xD800 && cp <= 0xDFFF ) return( 0 ); + if( cp == 0xFFFE || cp == 0xFFFF || cp > 0x10FFFF ) return( 0 ); + } + return( 1 ); +} + + +/* Load CSS or ECMAScript source at registration time. The caller owns + only the filename; the resulting definition owns a private copy. */ +static int +__svg_sheet_add_text_file( struct __svg_sheet *sheet, const char *fname, + int type, const char *prefix ) +{ + FILE *fp; + struct __svg_embedded_text *embedded; + unsigned char *buffer, *new_buffer; + size_t length, capacity, nr; + char id[64]; + int result; + + if( !sheet || sheet->type != SVG_SHEET || !sheet->defs || + !fname || !fname[0] ) return( -1 ); + + fp = fopen( fname, "rb" ); + if( !fp ) return( 0 ); + + capacity = 4096; + buffer = (unsigned char *)malloc( capacity ); + if( !buffer ) + { + fclose( fp ); + return( 0 ); + } + + length = 0; + result = 1; + for( ;; ) + { + if( length == capacity - 1 ) + { + if( capacity > ((size_t)-1) / 2 ) + { + result = 0; + break; + } + capacity *= 2; + new_buffer = (unsigned char *)realloc( buffer, capacity ); + if( !new_buffer ) + { + result = 0; + break; + } + buffer = new_buffer; + } + nr = fread( buffer + length, 1, capacity - length - 1, fp ); + length += nr; + if( ferror( fp ) ) + { + result = 0; + break; + } + if( feof( fp ) ) break; + } + if( fclose( fp ) != 0 ) result = 0; + + if( !result || !__svg_embedded_text_valid_utf8( buffer, length ) ) + { + free( buffer ); + return( 0 ); + } + buffer[length] = '\0'; + + embedded = (struct __svg_embedded_text *)malloc( sizeof( struct __svg_embedded_text ) ); + if( !embedded ) + { + free( buffer ); + return( 0 ); + } + memset( embedded, 0, sizeof( struct __svg_embedded_text ) ); + embedded->type = type; + embedded->content = (char *)buffer; + embedded->length = length; + + sprintf( id, "%s%04d", prefix, current_id++ ); + embedded->id = __svg_str_dup( id ); + if( !embedded->id ) + { + __svg_free_node( embedded ); + return( 0 ); + } + if( __svg_sheet_add_definition( sheet, (struct __svg_node *)embedded ) != 1 ) + { + __svg_free_node( embedded ); + return( 0 ); + } + return( 1 ); +} + + +int +__svg_sheet_add_css_file( struct __svg_sheet *sheet, const char *fname ) +{ + return( __svg_sheet_add_text_file( sheet, fname, SVG_CSS, "css" ) ); +} + + +int +__svg_sheet_add_script_file( struct __svg_sheet *sheet, const char *fname ) +{ + return( __svg_sheet_add_text_file( sheet, fname, SVG_SCRIPT, "js" ) ); +} + + +struct __svg_node * +__svg_new_line( double x1, double y1, double x2, double y2 ) +{ + struct __svg_line *new = NULL; + char buf[256]; + + new = (struct __svg_line *)malloc( sizeof( struct __svg_line ) ); + if( new == (struct __svg_line *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_line ) ); + new->type = SVG_LINE; + sprintf( buf, "line%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->x1 = x1; + new->y1 = y1; + new->x2 = x2; + new->y2 = y2; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_rect( double x, double y, double width, double height, double rx, double ry ) +{ + struct __svg_rect *new = NULL; + char buf[256]; + + new = (struct __svg_rect *)malloc( sizeof( struct __svg_rect ) ); + if( new == (struct __svg_rect *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_rect ) ); + new->type = SVG_RECT; + sprintf( buf, "rect%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + if( rx < 0.0 ) rx = 0.0; + if( ry < 0.0 ) ry = 0.0; + + new->x = x; + new->y = y; + new->width = width; + new->height = height; + new->rx = rx; + new->ry = ry; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_circle( double cx, double cy, double r ) +{ + struct __svg_circle *new = NULL; + char buf[256]; + + new = (struct __svg_circle *)malloc( sizeof( struct __svg_circle ) ); + if( new == (struct __svg_circle *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_circle ) ); + new->type = SVG_CIRCLE; + sprintf( buf, "circle%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->cx = cx; + new->cy = cy; + new->r = r; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_ellipse( double cx, double cy, double rx, double ry ) +{ + struct __svg_ellipse *new = NULL; + char buf[256]; + + new = (struct __svg_ellipse *)malloc( sizeof( struct __svg_ellipse ) ); + if( new == (struct __svg_ellipse *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_ellipse ) ); + new->type = SVG_ELLIPSE; + sprintf( buf, "ellipse%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->cx = cx; + new->cy = cy; + new->rx = rx; + new->ry = ry; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_polygon( struct __svg_points * ps ) +{ + struct __svg_polygon *new = NULL; + char buf[256]; + + if( !ps ) return( (struct __svg_node *)new ); + + new = (struct __svg_polygon *)malloc( sizeof( struct __svg_polygon ) ); + if( new == (struct __svg_polygon *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_polygon ) ); + new->type = SVG_POLYGON; + sprintf( buf, "polygon%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->points = ps; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_polyline( struct __svg_points * ps ) +{ + struct __svg_polyline *new = NULL; + char buf[256]; + + if( !ps ) return( (struct __svg_node *)new ); + + new = (struct __svg_polyline *)malloc( sizeof( struct __svg_polyline ) ); + if( new == (struct __svg_polyline *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_polyline ) ); + new->type = SVG_POLYLINE; + sprintf( buf, "polyline%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->points = ps; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_path( struct __svg_cmds * cmds ) +{ + struct __svg_path *new = NULL; + char buf[256]; + + if( !cmds ) return( (struct __svg_node *)new ); + + new = (struct __svg_path *)malloc( sizeof( struct __svg_path ) ); + if( new == (struct __svg_path *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_path ) ); + new->type = SVG_PATH; + sprintf( buf, "path%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->cmds = cmds; + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_text( double x, double y, const char * text, const char * font_family, int font_size ) +{ + struct __svg_text *new = NULL; + char buf[256]; + + if( !text || !text[0] ) return( (struct __svg_node *)new ); + + new = (struct __svg_text *)malloc( sizeof( struct __svg_text ) ); + if( new == (struct __svg_text *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_text ) ); + new->type = SVG_TEXT; + sprintf( buf, "text%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->x = x; + new->y = y; + new->text = __svg_str_dup( text ); + + if( !font_family || !font_family[0] ) + new->font_family = __svg_str_dup( default_text_font_family ); + else + new->font_family = __svg_str_dup( font_family ); + + if( !font_size ) + new->font_size = default_text_font_size; + else + new->font_size = font_size; + + new->fill = __svg_str_dup( default_text_fill ); + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_image( double x, double y, int width, int height, char * href, char * title ) +{ + struct __svg_image *new = NULL; + char buf[256]; + + if( !href || !href[0] ) return( (struct __svg_node *)new ); + + new = (struct __svg_image *)malloc( sizeof( struct __svg_image ) ); + if( new == (struct __svg_image *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_image ) ); + new->type = SVG_IMAGE; + sprintf( buf, "image%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + new->x = x; + new->y = y; + new->width = width; + new->height = height; + + new->href = __svg_str_dup( href ); + + if( title ) + new->title = __svg_str_dup( title ); + else + new->title = __svg_str_dup( "image" ); + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_l_gradient( const char * units, const char * spread, double x1, double y1, double x2, double y2 ) +{ + struct __svg_l_gradient *new = NULL; + char buf[256]; + + new = (struct __svg_l_gradient *)malloc( sizeof( struct __svg_l_gradient ) ); + if( new == (struct __svg_l_gradient *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_l_gradient ) ); + new->type = SVG_L_GRADIENT; + sprintf( buf, "lg%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + if( !units || !units[0] ) new->units = __svg_str_dup( "objectBoundingBox" ); + else new->units = __svg_str_dup( units ); + + if( !spread || !spread[0] ) new->spread = __svg_str_dup( "pad" ); + else new->spread = __svg_str_dup( spread ); + + new->x1 = x1; + new->y1 = y1; + new->x2 = x2; + new->y2 = y2; + + new->stops = create_svgc_dlist(); + if( !new->stops ) + { + if( new->id ) free( new->id ); + if( new->style ) __svg_free_style( new->style );; + if( new->units ) free( new->units ); + free( new ); + return( (struct __svg_node *)NULL ); + } + + return( (struct __svg_node *)new ); +} + +struct __svg_node * +__svg_new_r_gradient( const char * units, const char * spread, + double cx, double cy, double r, double fx, double fy ) +{ + struct __svg_r_gradient *new = NULL; + char buf[256]; + + new = (struct __svg_r_gradient *)malloc( sizeof( struct __svg_r_gradient ) ); + if( new == (struct __svg_r_gradient *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_r_gradient ) ); + new->type = SVG_R_GRADIENT; + sprintf( buf, "rg%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + if( !units || !units[0] ) new->units = __svg_str_dup( "objectBoundingBox" ); + else new->units = __svg_str_dup( units ); + + if( !spread || !spread[0] ) new->spread = __svg_str_dup( "pad" ); + else new->spread = __svg_str_dup( spread ); + + new->cx = cx; + new->cy = cy; + new->r = r; + new->fx = fx; + new->fy = fy; + + new->stops = create_svgc_dlist(); + if( !new->stops ) + { + if( new->id ) free( new->id ); + if( new->style ) __svg_free_style( new->style );; + if( new->units ) free( new->units ); + free( new ); + return( (struct __svg_node *)NULL ); + } + + return( (struct __svg_node *)new ); +} + +int +__svg_gradient_add_stop( void * gradient, struct __svg_stop * stop ) +{ + struct __svg_gradient *pg = (struct __svg_gradient *)gradient; + struct svgc_dlist_node *np = NULL; + int error = -1; /* EINVAL */ + + if( !pg || !pg->stops || !stop ) return( error ); + error = 0; + + if( !pg->stops ) return( error ); + + np = add_to_svgc_dlist_down( pg->stops, stop->id, (void *)stop ); + if( np ) error = 1; + + return( error ); +} + +struct __svg_node * +__svg_new_stop( double offset ) +{ + struct __svg_stop *new = NULL; + char buf[256]; + + new = (struct __svg_stop *)malloc( sizeof( struct __svg_stop ) ); + if( new == (struct __svg_stop *)NULL ) return( (struct __svg_node *)NULL ); + memset( (void *)new, 0, sizeof( struct __svg_stop ) ); + new->type = SVG_STOP; + sprintf( buf, "stop%04d", current_id++ ); + new->id = __svg_str_dup( buf ); + new->style = __svg_new_style(); + + if( !new->id || !new->style ) return( (struct __svg_node *)NULL ); + + if( offset > 1.0 || offset < 0.0 ) offset = 0.0; + new->offset = offset; + + return( (struct __svg_node *)new ); +} + +/*************************************************************** + Style controls: + ***************************************************************/ +int __svg_set_stroke( struct __svg_node * node, const char * stroke ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->stroke->stroke.r = 0; + node->style->stroke->stroke.g = 0; + node->style->stroke->stroke.b = 0; + + if( node->style->stroke->use_stroke ) + free( node->style->stroke->use_stroke ); + node->style->stroke->use_stroke = __svg_str_dup( stroke ); + if( node->style->stroke->use_stroke ) error = 1; + + return( error ); +} + +int __svg_set_stroke_rgb( struct __svg_node * node, int r, int g, int b ) +{ + char buf[256]; + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->stroke->stroke.r = r; + node->style->stroke->stroke.g = g; + node->style->stroke->stroke.b = b; + + sprintf( buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + if( node->style->stroke->use_stroke ) + free( node->style->stroke->use_stroke ); + node->style->stroke->use_stroke = __svg_str_dup( buf ); + if( node->style->stroke->use_stroke ) error = 1; + + return( error ); +} + +int __svg_set_stroke_width( struct __svg_node * node, double width ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->stroke->stroke_width = width; + return( error ); +} + +int __svg_set_stroke_linecap( struct __svg_node * node, const char * linecap ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + if( node->style->stroke->stroke_linecap ) + free( node->style->stroke->stroke_linecap ); + node->style->stroke->stroke_linecap = __svg_str_dup( linecap ); + if( node->style->stroke->stroke_linecap ) error = 1; + + return( error ); +} + +int __svg_set_stroke_linejoin( struct __svg_node * node, const char * linejoin ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + if( node->style->stroke->stroke_linejoin ) + free( node->style->stroke->stroke_linejoin ); + node->style->stroke->stroke_linejoin = __svg_str_dup( linejoin ); + if( node->style->stroke->stroke_linejoin ) error = 1; + + return( error ); +} + +int __svg_set_stroke_dasharray( struct __svg_node * node, const char * dasharray ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + if( node->style->stroke->stroke_dasharray ) + free( node->style->stroke->stroke_dasharray ); + node->style->stroke->stroke_dasharray = __svg_str_dup( dasharray ); + if( node->style->stroke->stroke_dasharray ) error = 1; + + return( error ); +} + + +int __svg_set_fill( struct __svg_node * node, const char * fill ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->fill->fill.r = 0; + node->style->fill->fill.g = 0; + node->style->fill->fill.b = 0; + + if( node->style->fill->use_fill ) + free( node->style->fill->use_fill ); + node->style->fill->use_fill = __svg_str_dup( fill ); + if( node->style->fill->use_fill ) error = 1; + + return( error ); +} + +int __svg_set_fill_rgb( struct __svg_node * node, int r, int g, int b ) +{ + char buf[256]; + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->fill->fill.r = r; + node->style->fill->fill.g = g; + node->style->fill->fill.b = b; + + sprintf( buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + if( node->style->fill->use_fill ) + free( node->style->fill->use_fill ); + node->style->fill->use_fill = __svg_str_dup( buf ); + if( node->style->fill->use_fill ) error = 1; + + return( error ); +} + + +int __svg_set_stop_color( struct __svg_node * node, const char * stop_color ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->stop_color->stop_color.r = 0; + node->style->stop_color->stop_color.g = 0; + node->style->stop_color->stop_color.b = 0; + + if( node->style->stop_color->use_stop_color ) + free( node->style->stop_color->use_stop_color ); + node->style->stop_color->use_stop_color = __svg_str_dup( stop_color ); + if( node->style->stop_color->use_stop_color ) error = 1; + + return( error ); +} + +int __svg_set_stop_color_rgb( struct __svg_node * node, int r, int g, int b ) +{ + char buf[256]; + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + node->style->stop_color->stop_color.r = r; + node->style->stop_color->stop_color.g = g; + node->style->stop_color->stop_color.b = b; + + sprintf( buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + if( node->style->stop_color->use_stop_color ) + free( node->style->stop_color->use_stop_color ); + node->style->stop_color->use_stop_color = __svg_str_dup( buf ); + if( node->style->stop_color->use_stop_color ) error = 1; + + return( error ); +} + + +int __svg_set_stop_opacity( struct __svg_node * node, double stop_opacity ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + if( stop_opacity > 1.0 || + stop_opacity < 0.0 ) stop_opacity = default_stop_opacity; + node->style->stop_opacity->stop_opacity = stop_opacity; + + return( error ); +} + + +int __svg_set_text( struct __svg_node * node, const char * text ) +{ + int error = -1; /* EINVAL */ + + if( !node || !text || !text[0] ) return( error ); + error = 0; + + if( ((__svg_text *)node)->text ) + free( ((__svg_text *)node)->text ); + ((__svg_text *)node)->text = __svg_str_dup( text ); + if( !((__svg_text *)node)->text ) error = 1; + + return( error ); +} + +int __svg_set_text_fill( struct __svg_node * node, const char * fill ) +{ + int error = -1; /* EINVAL */ + + if( !node || !fill || !fill[0] ) return( error ); + error = 0; + + if( ((__svg_text *)node)->fill ) + free( ((__svg_text *)node)->fill ); + ((__svg_text *)node)->fill = __svg_str_dup( fill ); + if( !((__svg_text *)node)->fill ) error = 1; + + return( error ); +} + +int __svg_set_text_fill_rgb( struct __svg_node * node, int r, int g, int b ) +{ + char buf[256]; + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + sprintf( buf, "rgb(%03d,%03d,%03d)", r, g, b ); + + if( ((__svg_text *)node)->fill ) + free( ((__svg_text *)node)->fill ); + ((__svg_text *)node)->fill = __svg_str_dup( buf ); + if( !((__svg_text *)node)->fill ) error = 1; + + return( error ); +} + +int __svg_set_text_font_family( struct __svg_node * node, const char * family ) +{ + int error = -1; /* EINVAL */ + + if( !node || !family || !family[0] ) return( error ); + error = 0; + + if( ((__svg_text *)node)->font_family ) + free( ((__svg_text *)node)->font_family ); + ((__svg_text *)node)->font_family = __svg_str_dup( family ); + if( !((__svg_text *)node)->font_family ) error = 1; + + return( error ); +} + + +int __svg_set_text_font_size( struct __svg_node * node, int size ) +{ + int error = -1; /* EINVAL */ + + if( !node ) return( error ); + error = 0; + + ((__svg_text *)node)->font_size = size; + error = 1; + + return( error ); +} + + + +void __svg_free_scene( struct __svg_scene * scene ) +{ + if( !scene ) return; + if( scene->plist ) + { + /* clear svgc_dlist */ + free_svgc_dlist( scene->plist, __svg_free_node ); + } + + free( scene ); +} + + +struct __svg_scene * +__svg_new_scene( struct __svg_sheet * sheet ) +{ + struct __svg_scene *new = (struct __svg_scene *)NULL; + + new = (struct __svg_scene *)malloc( sizeof(struct __svg_scene) ); + if( new == (struct __svg_scene *)NULL ) return( (struct __svg_scene *)NULL ); + memset( (void *)new, 0, sizeof(struct __svg_scene) ); + + new->plist = create_svgc_dlist(); + if( !new->plist ) + { + free( new ); + return( (struct __svg_scene *)NULL ); + } + + add_to_svgc_dlist_down( new->plist, sheet->id, (void *)sheet ); + + return( (struct __svg_scene *)new ); +} + +int +__svg_scene_add_node( struct __svg_scene * scene, struct __svg_node * node ) +{ + struct svgc_dlist_node *np = NULL; + int error = -1; /* EINVAL */ + + if( !scene || !scene->plist || !node ) return( error ); + error = 0; + + if( !scene->plist ) return( error ); + + np = add_to_svgc_dlist_down( scene->plist, node->id, (void *)node ); + if( np ) error = 1; + + return( error ); +} + + +/* Get node ID: */ +const char * +__svg_get_node_id( struct __svg_node * node ) +{ + const char *error = (const char *)NULL; + + if( !node->id || !node->id[0] ) return( error ); + + return( (const char *)node->id ); +} + +/* Create node URL: */ +char * +__svg_create_node_url( struct __svg_node * node ) +{ + int len; + char *url = (char *)NULL; + + if( !node->id || !node->id[0] ) return( url ); + + len = strlen( node->id ) + 7; + url = (char *)malloc( len ); + if( !url ) return( url ); + + sprintf( url, "url(#%s)", node->id ); + + return( url ); +} + + +/*************************************************************** + OUTPUT: + ***************************************************************/ +int output_group = 0; +char *output_file_name; +FILE *output_file; + +void +__svg_paint_point( void * data ) +{ + struct __svg_point *p = (struct __svg_point *)data; + + if( !p ) return; + + fprintf( output_file, " %0.3f,%0.3f", p->x, p->y ); +} + +void +__svg_paint_point_x( void * data ) +{ + struct __svg_point *p = (struct __svg_point *)data; + + if( !p ) return; + + fprintf( output_file, " %0.3f", p->x ); +} + +void +__svg_paint_point_y( void * data ) +{ + struct __svg_point *p = (struct __svg_point *)data; + + if( !p ) return; + + fprintf( output_file, " %0.3f", p->y ); +} + +void +__svg_paint_point_bool( void * data ) +{ + struct __svg_point *p = (struct __svg_point *)data; + + if( !p ) return; + + if( p->x < 0.5 ) fprintf( output_file, " 0," ); + else fprintf( output_file, " 1," ); + if( p->y < 0.5 ) fprintf( output_file, "0" ); + else fprintf( output_file, "1" ); +} + + + +void +__svg_paint_cmd( void * data ) +{ + struct __svg_cmd *cmd = (struct __svg_cmd *)data; + + if( !cmd ) return; + switch( cmd->cmd ) + { + case 'M': + case 'm': + fprintf( output_file, "%c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + break; + + case 'L': + case 'l': + fprintf( output_file, " %c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + break; + + case 'H': + case 'h': + fprintf( output_file, "%c", cmd->cmd ); + __svg_paint_point_x( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + break; + + case 'V': + case 'v': + fprintf( output_file, "%c", cmd->cmd ); + __svg_paint_point_y( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + break; + + case 'C': + case 'c': + fprintf( output_file, " %c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "2" )->data ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "3" )->data ); + break; + + case 'S': + case 's': + fprintf( output_file, " %c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "2" )->data ); + break; + + case 'Q': + case 'q': + fprintf( output_file, " %c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "2" )->data ); + break; + + case 'T': + case 't': + fprintf( output_file, " %c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + break; + + case 'A': + case 'a': + fprintf( output_file, " %c", cmd->cmd ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "1" )->data ); + __svg_paint_point_x( lookup_svgc_dlist_node_down( cmd->points->plist, "2" )->data ); + __svg_paint_point_bool( lookup_svgc_dlist_node_down( cmd->points->plist, "3" )->data ); + __svg_paint_point( lookup_svgc_dlist_node_down( cmd->points->plist, "4" )->data ); + break; + + case 'Z': + case 'z': + fprintf( output_file, " %c", cmd->cmd ); + break; + default: + break; + } +} + + +void +__svg_paint_stop( void * data ) +{ + struct __svg_node *node = (struct __svg_node *)data; + + if( !node ) return; + + + fprintf( output_file, +" <stop id=\"%s\" offset=\"%0.3f\" style=\"stop-color:%s;stop-opacity:%0.3f;\" />\n", + node->id, + ((struct __svg_stop *)node)->offset, + node->style->stop_color->use_stop_color, + node->style->stop_opacity->stop_opacity ); +} + + +void +__svg_paint_l_gradient( void * data ) +{ + struct __svg_node *node = (struct __svg_node *)data; + + if( !node ) return; + + fprintf( output_file, +" <linearGradient\n" +" id=\"%s\" gradientUnits=\"%s\" spreadMethod=\"%s\"\n" +" x1=\"%0.3f\"\n" +" y1=\"%0.3f\"\n" +" x2=\"%0.3f\"\n" +" y2=\"%0.3f\">\n", + node->id, + ((struct __svg_l_gradient *)node)->units, + ((struct __svg_l_gradient *)node)->spread, + ((struct __svg_l_gradient *)node)->x1, + ((struct __svg_l_gradient *)node)->y1, + ((struct __svg_l_gradient *)node)->x2, + ((struct __svg_l_gradient *)node)->y2 ); + + if( ((struct __svg_l_gradient *)node)->stops ) + { + /* paint svgc_dlist */ + for_each_svgc_dlist_node_data( + ((struct __svg_l_gradient *)node)->stops, __svg_paint_stop ); + } + + fprintf( output_file, +" </linearGradient>\n" ); +} + +void +__svg_paint_r_gradient( void * data ) +{ + struct __svg_node *node = (struct __svg_node *)data; + + if( !node ) return; + + fprintf( output_file, +" <radialGradient\n" +" id=\"%s\" gradientUnits=\"%s\" spreadMethod=\"%s\"\n" +" cx=\"%0.3f\"\n" +" cy=\"%0.3f\"\n" +" r=\"%0.3f\"\n" +" fx=\"%0.3f\"\n" +" fy=\"%0.3f\">\n", + node->id, + ((struct __svg_r_gradient *)node)->units, + ((struct __svg_r_gradient *)node)->spread, + ((struct __svg_r_gradient *)node)->cx, + ((struct __svg_r_gradient *)node)->cy, + ((struct __svg_r_gradient *)node)->r, + ((struct __svg_r_gradient *)node)->fx, + ((struct __svg_r_gradient *)node)->fy ); + + if( ((struct __svg_r_gradient *)node)->stops ) + { + /* paint svgc_dlist */ + for_each_svgc_dlist_node_data( + ((struct __svg_r_gradient *)node)->stops, __svg_paint_stop ); + } + + fprintf( output_file, +" </radialGradient>\n" ); +} + + +/* CDATA terminators are split while retaining the source characters. */ +static void +__svg_paint_embedded_text( struct __svg_embedded_text *embedded ) +{ + const char *begin, *point; + const char *tag; + const char *media_type; + + tag = embedded->type == SVG_CSS ? "style" : "script"; + media_type = embedded->type == SVG_CSS ? "text/css" : "text/javascript"; + fprintf( output_file, " <%s type=\"%s\">\n", tag, media_type ); + fprintf( output_file, " <![CDATA[\n" ); + begin = embedded->content; + while( (point = strstr( begin, "]]>") ) != NULL ) + { + (void)fwrite( begin, 1, (size_t)(point - begin), output_file ); + fprintf( output_file, "]]]]><![CDATA[>" ); + begin = point + 3; + } + (void)fwrite( begin, 1, embedded->length - (size_t)(begin - embedded->content), output_file ); + if( embedded->length && embedded->content[embedded->length - 1] != '\n' ) + fputc( '\n', output_file ); + fprintf( output_file, " ]]>\n" ); + fprintf( output_file, " </%s>\n", tag ); +} + + +void +__svg_paint_defs( void * data ) +{ + struct __svg_node *node = (struct __svg_node *)data; + + if( !node ) return; + + switch( node->type ) + { + default: + fprintf( output_file, "<node type=\"unknown\" />\n" ); + break; + + case SVG_L_GRADIENT: + __svg_paint_l_gradient( data ); + break; + + case SVG_R_GRADIENT: + __svg_paint_r_gradient( data ); + break; + + case SVG_CSS: + case SVG_SCRIPT: + __svg_paint_embedded_text( (struct __svg_embedded_text *)data ); + break; + } +} + + +void +__svg_paint_node( void * data ) +{ + struct __svg_node *node = (struct __svg_node *)data; + char *group; + + if( !node ) return; + + switch( node->type ) + { + default: + fprintf( output_file, "<node type=\"unknown\" />\n" ); + break; + + case SVG_SHEET : + if( output_group ) group = " <g id=\"layer0\">\n"; + else group = ""; + fprintf( output_file, +"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n" +" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" +"<svg\n" +" xmlns:svg=\"http://www.w3.org/2000/svg\"\n" +" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" +" xmlns=\"http://www.w3.org/2000/svg\"\n" +" version=\"1.1\"\n" +" width=\"%d\"\n" +" height=\"%d\">\n" +" <defs>\n" +" <sheet id=\"%s\"\n" +" left_margin=\"%d\"\n" +" right_margin=\"%d\"\n" +" top_margin=\"%d\"\n" +" bottom_margin=\"%d\" />\n", + (int)((struct __svg_sheet *)node)->width, + (int)((struct __svg_sheet *)node)->height, + node->id, + (int)((struct __svg_sheet *)node)->left_margin, + (int)((struct __svg_sheet *)node)->right_margin, + (int)((struct __svg_sheet *)node)->top_margin, + (int)((struct __svg_sheet *)node)->bottom_margin ); + + if( ((struct __svg_sheet *)node)->defs ) + { + /* paint svgc_dlist */ + for_each_svgc_dlist_node_data( + ((struct __svg_sheet *)node)->defs, __svg_paint_defs ); + } + + fprintf( output_file, +" </defs>\n%s", + group ); + break; + + case SVG_LINE : + fprintf( output_file, +" <line\n" +" id=\"%s\"\n" +" x1=\"%0.3f\"\n" +" y1=\"%0.3f\"\n" +" x2=\"%0.3f\"\n" +" y2=\"%0.3f\"\n" +" style=\"stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->id, + ((struct __svg_line *)node)->x1, + ((struct __svg_line *)node)->y1, + ((struct __svg_line *)node)->x2, + ((struct __svg_line *)node)->y2, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + + break; + case SVG_RECT : + fprintf( output_file, +" <rect\n" +" id=\"%s\"\n" +" x=\"%0.3f\"\n" +" y=\"%0.3f\"\n" +" width=\"%0.3f\"\n" +" height=\"%0.3f\"\n" +" rx=\"%0.3f\"\n" +" ry=\"%0.3f\"\n" +" style=\"fill:%s;stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->id, + ((struct __svg_rect *)node)->x, + ((struct __svg_rect *)node)->y, + ((struct __svg_rect *)node)->width, + ((struct __svg_rect *)node)->height, + ((struct __svg_rect *)node)->rx, + ((struct __svg_rect *)node)->ry, + node->style->fill->use_fill, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + break; + + case SVG_CIRCLE : + fprintf( output_file, +" <circle\n" +" id=\"%s\"\n" +" cx=\"%0.3f\"\n" +" cy=\"%0.3f\"\n" +" r=\"%0.3f\"\n" +" style=\"fill:%s;stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->id, + ((struct __svg_circle *)node)->cx, + ((struct __svg_circle *)node)->cy, + ((struct __svg_circle *)node)->r, + node->style->fill->use_fill, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + + case SVG_ELLIPSE : + fprintf( output_file, +" <ellipse\n" +" id=\"%s\"\n" +" cx=\"%0.3f\"\n" +" cy=\"%0.3f\"\n" +" rx=\"%0.3f\"\n" +" ry=\"%0.3f\"\n" +" style=\"fill:%s;stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->id, + ((struct __svg_ellipse *)node)->cx, + ((struct __svg_ellipse *)node)->cy, + ((struct __svg_ellipse *)node)->rx, + ((struct __svg_ellipse *)node)->ry, + node->style->fill->use_fill, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + + case SVG_POLYGON : + fprintf( output_file, +" <polygon\n" +" id=\"%s\"\n" +" points=\"", + node->id ); + /* print points */ + for_each_svgc_dlist_node_data( ((struct __svg_polygon *)node)->points->plist, __svg_paint_point ); + fprintf( output_file, +"\"\n" +" style=\"fill:%s;stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->style->fill->use_fill, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + + case SVG_POLYLINE : + fprintf( output_file, +" <polyline\n" +" id=\"%s\"\n" +" points=\"", + node->id ); + /* print points */ + for_each_svgc_dlist_node_data( ((struct __svg_polygon *)node)->points->plist, __svg_paint_point ); + fprintf( output_file, +"\"\n" +" style=\"fill:%s;stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->style->fill->use_fill, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + + case SVG_PATH : + fprintf( output_file, +" <path\n" +" id=\"%s\"\n" +" d=\"", + node->id ); + /* print points */ + for_each_svgc_dlist_node_data( ((struct __svg_path *)node)->cmds->plist, __svg_paint_cmd ); + fprintf( output_file, +"\"\n" +" style=\"fill:%s;stroke:%s;stroke-width:%0.3f;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s;\" />\n", + node->style->fill->use_fill, + node->style->stroke->use_stroke, + node->style->stroke->stroke_width, + node->style->stroke->stroke_linecap, + node->style->stroke->stroke_linejoin, + node->style->stroke->stroke_dasharray ); + break; + + case SVG_TEXT : + fprintf( output_file, +" <text\n" +" id=\"%s\"\n" +" x=\"%0.3f\"\n" +" y=\"%0.3f\"\n" +" font-family=\"%s\"\n" +" font-size=\"%d\"\n" +" fill=\"%s\" >\n" +" %s\n" +" </text>\n", + node->id, + ((struct __svg_text *)node)->x, + ((struct __svg_text *)node)->y, + ((struct __svg_text *)node)->font_family, + ((struct __svg_text *)node)->font_size, + ((struct __svg_text *)node)->fill, + ((struct __svg_text *)node)->text ); + break; + + case SVG_IMAGE : + fprintf( output_file, +" <image\n" +" id=\"%s\"\n" +" x=\"%0.3f\"\n" +" y=\"%0.3f\"\n" +" width=\"%dpx\"\n" +" height=\"%dpx\"\n" +" xlink:href=\"%s\">\n", + node->id, + ((struct __svg_image *)node)->x, + ((struct __svg_image *)node)->y, + ((struct __svg_image *)node)->width, + ((struct __svg_image *)node)->height, + ((struct __svg_image *)node)->href ); + fprintf( output_file, +" <title>%s</title>\n", + ((struct __svg_image *)node)->title ); + fprintf( output_file, +" </image>\n" ); + break; + } +} + + +void __svg_paint_scene( struct __svg_scene * scene, int group, char * fname ) +{ + int save_group = output_group; + + if( !scene ) return; + + if( group ) output_group = 1; + else output_group = 0; + + if( !fname || !fname[0] ) + { + output_file_name = "stdout"; + output_file = stdout; + } + else + { + output_file_name = fname; + /* open file output_file */ + if( (output_file = fopen( output_file_name, "w+" )) == NULL ) + { + fprintf( stderr, "ERROR: Cannot open file: %s\n", output_file_name ); + output_group = save_group; + exit( 1 ); + } + } + + if( scene->plist ) + { + /* paint svgc_dlist */ + for_each_svgc_dlist_node_data( scene->plist, __svg_paint_node ); + } + + if( output_group ) + fprintf( output_file, " </g>\n" ); + fprintf( output_file, "</svg>\n" ); + + output_group = save_group; + + fclose( output_file ); +} + diff --git a/src/svgc.h b/src/svgc.h new file mode 100644 index 0000000..aea6616 --- /dev/null +++ b/src/svgc.h @@ -0,0 +1,620 @@ + +/*************************************************************** + SVGC.H + + This file containt declarations of SVG C lib functions. + ***************************************************************/ + +#ifndef __SVGC_H__ +#define __SVGC_H__ + +#if defined( __cplusplus ) +extern "C" +{ +#endif + +#include <svgc-dlist.h> + +typedef struct __svg_point __svg_point; +struct __svg_point +{ + double x, y; +}; + +typedef struct __svg_points __svg_points; +struct __svg_points +{ + int count; + struct svgc_dlist *plist; +}; + +extern void __svg_free_point( void *data ); +extern void __svg_free_points( struct __svg_points *p ); +extern struct __svg_points *__svg_new_points( void ); +extern int __svg_add_point( struct __svg_points *ps, double x, double y ); + + +/*************************************************************** + PATH CMDS: + ***************************************************************/ + +typedef struct __svg_cmd __svg_cmd; +struct __svg_cmd +{ + char cmd; + struct __svg_points *points; +}; + +typedef struct __svg_cmds __svg_cmds; +struct __svg_cmds +{ + int count; + struct svgc_dlist *plist; +}; + +extern void __svg_free_cmd( void *data ); +extern void __svg_free_cmds( struct __svg_cmds *cmds ); +extern struct __svg_cmd *__svg_new_cmd( char cmd, struct __svg_points *ps ); +extern struct __svg_cmds *__svg_new_cmds( void ); +extern int __svg_add_cmd( struct __svg_cmds *cmds, struct __svg_cmd *cmd ); + +#define ABSOLUTE 1 +#define RELATIVE 0 +extern int +__svg_add_moveto_cmd( struct __svg_cmds *cmds, + int abs, + double x, + double y ); + +extern int +__svg_add_lineto_cmd( struct __svg_cmds *cmds, + int abs, + double x, + double y ); + +extern int +__svg_add_hlineto_cmd( struct __svg_cmds *cmds, + int abs, + double x ); + +extern int +__svg_add_vlineto_cmd( struct __svg_cmds *cmds, + int abs, + double y ); + +extern int +__svg_add_curveto_cmd( struct __svg_cmds *cmds, + int abs, + double x1, + double y1, + double x2, + double y2, + double x, + double y ); +extern int +__svg_add_short_curveto_cmd( struct __svg_cmds *cmds, + int abs, + double x2, + double y2, + double x, + double y ); +extern int +__svg_add_quadro_curveto_cmd( struct __svg_cmds *cmds, + int abs, + double x1, + double y1, + double x, + double y ); +extern int +__svg_add_short_quadro_curveto_cmd( struct __svg_cmds *cmds, + int abs, + double x, + double y ); + +extern int +__svg_add_arc_cmd( struct __svg_cmds *cmds, + int abs, + double rx, + double ry, + double x_axis_rotation, + int large_arc_flag, + int sweep_flag, + double x, + double y ); + +extern int +__svg_add_closepath_cmd( struct __svg_cmds *cmds, + int abs ); + + + +/*************************************************************** + STYLE: + ***************************************************************/ +typedef struct __svg_rgb __svg_rgb; +struct __svg_rgb +{ + int r, g, b; +}; + + +#define STROKE_DEFAULT "black" + +#define LINECAP_BUTT "butt" +#define LINECAP_ROUND "round" +#define LINECAP_SQUARE "square" + +#define LINECAP_DEFAULT "round" + + +#define LINEJOIN_MITER "miter" +#define LINEJOIN_ROUND "round" +#define LINEJOIN_BEVEL "bevel" + +#define LINEJOIN_DEFAULT "round" + + +#define DASHARRAY_DEFAULT "none" + +typedef struct __svg_stroke __svg_stroke; +struct __svg_stroke +{ + struct __svg_rgb stroke; + char *use_stroke; + + double stroke_width; + + char *stroke_linecap; + char *stroke_linejoin; + char *stroke_dasharray; +}; + + +#define FILL_DEFAULT "none" + +typedef struct __svg_fill __svg_fill; +struct __svg_fill +{ + struct __svg_rgb fill; + char *use_fill; +}; + + +#define STOP_COLOR_DEFAULT "black" + +typedef struct __svg_stop_color __svg_stop_color; +struct __svg_stop_color +{ + struct __svg_rgb stop_color; + char *use_stop_color; +}; + + +#define STOP_OPACITY_DEFAULT 1.0 + +typedef struct __svg_stop_opacity __svg_stop_opacity; +struct __svg_stop_opacity +{ + double stop_opacity; +}; + + +typedef struct __svg_style __svg_style; +struct __svg_style +{ + struct __svg_stroke *stroke; + struct __svg_fill *fill; + struct __svg_stop_color *stop_color; + struct __svg_stop_opacity *stop_opacity; +}; + + +#define TEXT_FILL_DEFAULT "black" +#define TEXT_FONT_FAMILY_DEFAULT "Verdana" +#define TEXT_FONT_SIZE_DEFAULT 12 + + +extern char *__svg_str_dup( const char *s ); + +extern struct __svg_stroke *__svg_new_stroke( void ); +extern void __svg_free_stroke( struct __svg_stroke *s ); +extern struct __svg_fill *__svg_new_fill( void ); +extern void __svg_free_fill( struct __svg_fill *f ); +extern struct __svg_stop_color *__svg_new_stop_color( void ); +extern void __svg_free_stop_color( struct __svg_stop_color *sc ); +extern struct __svg_stop_opacity *__svg_new_stop_opacity( void ); +extern void __svg_free_stop_opacity( struct __svg_stop_opacity *so ); + +extern struct __svg_style *__svg_new_style( void ); +extern void __svg_free_style( struct __svg_style *style ); + + + + +/* NODE: */ + +#define SVG_SHEET 1 +#define SVG_LINE 2 +#define SVG_RECT 3 +#define SVG_CIRCLE 4 +#define SVG_ELLIPSE 5 +#define SVG_POLYGON 6 +#define SVG_POLYLINE 7 +#define SVG_PATH 8 +#define SVG_TEXT 9 +#define SVG_IMAGE 10 +#define SVG_L_GRADIENT 11 +#define SVG_R_GRADIENT 12 +#define SVG_CSS 13 +#define SVG_STOP 14 +#define SVG_SCRIPT 15 + +typedef struct __svg_node __svg_node; +struct __svg_node +{ + int type; + char *id; + struct __svg_style *style; +}; + +/* The MAIN svg nide is SHEET: */ +typedef struct __svg_sheet __svg_sheet; +struct __svg_sheet +{ + int type; + char *id; + struct __svg_style *style; + + struct svgc_dlist *defs; + + double width, height; + + double left_margin; + double right_margin; + double top_margin; + double bottom_margin; +}; + + +/*************************************************************** + SVG primitives: + ***************************************************************/ +typedef struct __svg_line __svg_line; +struct __svg_line +{ + int type; + char *id; + struct __svg_style *style; + + double x1, y1; + double x2, y2; +}; + +typedef struct __svg_rect __svg_rect; +struct __svg_rect +{ + int type; + char *id; + struct __svg_style *style; + + double x, y; + double width, height; + double rx, ry; +}; + +typedef struct __svg_circle __svg_circle; +struct __svg_circle +{ + int type; + char *id; + struct __svg_style *style; + + double cx, cy, r; +}; + +typedef struct __svg_ellipse __svg_ellipse; +struct __svg_ellipse +{ + int type; + char *id; + struct __svg_style *style; + + double cx, cy, rx, ry; +}; + +typedef struct __svg_polygon __svg_polygon; +struct __svg_polygon +{ + int type; + char *id; + struct __svg_style *style; + + struct __svg_points *points; +}; + +typedef struct __svg_polyline __svg_polyline; +struct __svg_polyline +{ + int type; + char *id; + struct __svg_style *style; + + struct __svg_points *points; +}; + +typedef struct __svg_path __svg_path; +struct __svg_path +{ + int type; + char *id; + struct __svg_style *style; + + struct __svg_cmds *cmds; +}; + +typedef struct __svg_text __svg_text; +struct __svg_text +{ + int type; + char *id; + struct __svg_style *style; + + double x, y; + char *text; + + int font_size; + char *font_family; + char *fill; +}; + +typedef struct __svg_image __svg_image; +struct __svg_image +{ + int type; + char *id; + struct __svg_style *style; + + double x, y; + int width, height; + char *href; + char *title; +}; + +/* NOTE: this is common gradient struct */ +typedef struct __svg_gradient __svg_gradient; +struct __svg_gradient +{ + int type; + char *id; + struct __svg_style *style; + + struct svgc_dlist *stops; +}; + +/* NOTE: gradients are added into sheet list as defs */ +typedef struct __svg_l_gradient __svg_l_gradient; +struct __svg_l_gradient +{ + int type; + char *id; + struct __svg_style *style; + + struct svgc_dlist *stops; + + char *units; + char *spread; + + double x1, y1; + double x2, y2; +}; + +typedef struct __svg_r_gradient __svg_r_gradient; +struct __svg_r_gradient +{ + int type; + char *id; + struct __svg_style *style; + + struct svgc_dlist *stops; + + char *units; + char *spread; + + double cx, cy; + double r; + double fx, fy; +}; + +/* NOTE: only for gradients */ +typedef struct __svg_stop __svg_stop; +struct __svg_stop +{ + int type; + char *id; + struct __svg_style *style; + + double offset; +}; + + +/*************************************************************** + SCENE is the list of SVG primitives. + ***************************************************************/ +typedef struct __svg_scene __svg_scene; +struct __svg_scene +{ + struct svgc_dlist *plist; +}; + +extern void __svg_free_scene( struct __svg_scene *scene ); +extern struct __svg_scene *__svg_new_scene( struct __svg_sheet *sheet ); + +/*************************************************************** + FREE and ADD SVG primitives. + ***************************************************************/ +extern void __svg_free_node( void *data ); + +/* SVG sheet should be created before SCENE */ +extern struct __svg_node *__svg_new_sheet( double width, + double height, + double left_margin, + double right_margin, + double top_margin, + double bottom_margin ); + +extern int __svg_sheet_add_definition( void *sheet, + struct __svg_node *definition ); + +/* Insert the contents of a UTF-8 CSS file into sheet <defs>. + Return 1 on success, 0 on read/allocation/encoding failure, -1 on + invalid arguments. Relative url(...) references are resolved by the + SVG viewer against the generated SVG file, not against fname. */ +extern int __svg_sheet_add_css_file( struct __svg_sheet *sheet, + const char *fname ); + +/* Insert the contents of a UTF-8 JavaScript file into sheet <defs>. + The caller supplies JavaScript source, without XML, script tags or CDATA. + Return values and ownership follow __svg_sheet_add_css_file(). + Script execution depends on the viewer; SVG used as <img> is inert. */ +extern int __svg_sheet_add_script_file( struct __svg_sheet *sheet, + const char *fname ); + +/*************************************************************** + Creation of SVG primirives: + ***************************************************************/ +extern struct __svg_node *__svg_new_line( double x1, + double y1, + double x2, + double y2 ); + +extern struct __svg_node *__svg_new_rect( double x, + double y, + double width, + double height, + double rx, + double ry ); + +extern struct __svg_node *__svg_new_circle( double cx, + double cy, + double r ); + +extern struct __svg_node *__svg_new_ellipse( double cx, + double cy, + double rx, + double ry ); + +extern struct __svg_node *__svg_new_polygon ( struct __svg_points *ps ); +extern struct __svg_node *__svg_new_polyline( struct __svg_points *ps ); +extern struct __svg_node *__svg_new_path( struct __svg_cmds *cmds ); + +extern struct __svg_node *__svg_new_text( double x, + double y, + const char *text, + const char *font_family, + int font_size ); + +extern struct __svg_node *__svg_new_image( double x, + double y, + int width, + int height, + char *href, + char *title ); + +extern struct __svg_node *__svg_new_l_gradient( const char *units, /* objectBoundingBox | userSpaceOnUse */ + const char *spread, /* pad | reflect | repeat */ + double x1, + double y1, + double x2, + double y2 ); + +extern struct __svg_node *__svg_new_r_gradient( const char *units, /* objectBoundingBox | userSpaceOnUse */ + const char *spread, /* pad | reflect | repeat */ + double cx, + double cy, + double r, + double fx, + double fy ); + +extern struct __svg_node *__svg_new_stop( double offset ); + +extern int __svg_gradient_add_stop( void *gradient, + struct __svg_stop *stop ); + + +/*************************************************************** + Adding some primitive to SCENE: + ***************************************************************/ +extern int +__svg_scene_add_node( struct __svg_scene *scene, struct __svg_node *node ); + + +/*************************************************************** + Style controls: + ***************************************************************/ +extern int __svg_set_stroke( struct __svg_node *node, const char *stroke ); +extern int __svg_set_stroke_rgb( struct __svg_node *node, int r, int g, int b ); +extern int __svg_set_stroke_width( struct __svg_node *node, double width ); +extern int __svg_set_stroke_linecap( struct __svg_node *node, const char *linecap ); +extern int __svg_set_stroke_linejoin( struct __svg_node *node, const char *linejoin ); +extern int __svg_set_stroke_dasharray( struct __svg_node *node, const char *dasharray ); + +extern int __svg_set_fill( struct __svg_node *node, const char *fill ); +extern int __svg_set_fill_rgb( struct __svg_node *node, int r, int g, int b ); + +extern int __svg_set_stop_color( struct __svg_node *node, const char *stop_color ); +extern int __svg_set_stop_color_rgb( struct __svg_node *node, int r, int g, int b ); + +extern int __svg_set_stop_opacity( struct __svg_node *node, double stop_opacity ); + +extern int __svg_set_text_text( struct __svg_node *node, const char *text ); +extern int __svg_set_text_fill( struct __svg_node *node, const char *fill ); +extern int __svg_set_text_fill_rgb( struct __svg_node *node, int r, int g, int b ); +extern int __svg_set_text_font_family( struct __svg_node *node, const char *family ); +extern int __svg_set_text_font_size( struct __svg_node *node, int size ); + + +/* Set Global Default Values: */ +extern void __svg_set_scene_default_style( void ); +extern void __svg_set_scene_stroke( const char *stroke ); +extern void __svg_set_scene_stroke_rgb( int r, int g, int b ); +extern void __svg_set_scene_stroke_width( double width ); +extern void __svg_set_scene_stroke_linecap( const char *linecap ); +extern void __svg_set_scene_stroke_linejoin( const char *linejoin ); +extern void __svg_set_scene_stroke_dasharray( const char *dasharray ); + +extern void __svg_set_scene_fill( const char *fill ); +extern void __svg_set_scene_fill_rgb( int r, int g, int b ); + +extern void __svg_set_scene_stop_color( const char *stop_color ); +extern void __svg_set_scene_stop_color_rgb( int r, int g, int b ); + +extern void __svg_set_scene_stop_opacity( double stop_opacity ); + +extern void __svg_set_scene_text_fill( const char *fill ); +extern void __svg_set_scene_text_fill_rgb( int r, int g, int b ); +extern void __svg_set_scene_text_font_family( const char *family ); +extern void __svg_set_scene_text_font_size( int size ); + + +/* Get node ID: */ +extern const char *__svg_get_node_id( struct __svg_node *node ); +extern char *__svg_create_node_url( struct __svg_node *node ); + + +/*************************************************************** + OUTPUT: + ***************************************************************/ +extern void __svg_paint_scene( struct __svg_scene *scene, + int group, + char *fname ); + + +extern int _svgc_get_major_version( void ); +extern int _svgc_get_minor_version( void ); +extern int _svgc_get_micro_version( void ); + +#if defined( __cplusplus ) +} /* extern "C" */ +#endif + +#endif /* __SVGC_H__ */ diff --git a/svgc-config/Makefile.am b/svgc-config/Makefile.am new file mode 100644 index 0000000..9b167dd --- /dev/null +++ b/svgc-config/Makefile.am @@ -0,0 +1,4 @@ + +# Configuration script generated from svgc-config.in by configure. + +bin_SCRIPTS = svgc-config diff --git a/svgc-config/svgc-config.in b/svgc-config/svgc-config.in new file mode 100644 index 0000000..6a39d19 --- /dev/null +++ b/svgc-config/svgc-config.in @@ -0,0 +1,78 @@ +#!/bin/sh + +# SVGC build flags for standalone C programs. +# Each option writes a single line; multiple options are allowed. + +VERSION='@PACKAGE_VERSION@' +prefix='@prefix@' +exec_prefix='@exec_prefix@' +libdir='@libdir@' +includedir='@includedir@' + +usage() +{ + cat <<EOF +Usage: svgc-config [--prefix[=DIR]] [--exec-prefix[=DIR]] + [--version] [--cppflags] [--cflags] + [--libs] [--ldflags] +EOF + exit "$1" +} + +if test "$#" -eq 0; then + usage 1 >&2 +fi + +# Expand the standard Autoconf directory references after option parsing. +for arg in "$@"; do + case "$arg" in + --prefix=*) + prefix=${arg#--prefix=} + ;; + --exec-prefix=*) + exec_prefix=${arg#--exec-prefix=} + ;; + --prefix|--exec-prefix|--version|--cppflags|--cflags|--libs|--ldflags|svgc) + ;; + *) + usage 1 >&2 + ;; + esac +done + +# Autoconf substitutions may contain ${prefix} and ${exec_prefix}. +eval 'exec_prefix="'"$exec_prefix"'"' +eval 'libdir="'"$libdir"'"' +eval 'includedir="'"$includedir"'"' + +for arg in "$@"; do + case "$arg" in + --prefix=*) + ;; + --exec-prefix=*) + ;; + --prefix) + printf '%s\n' "$prefix" + ;; + --exec-prefix) + printf '%s\n' "$exec_prefix" + ;; + --version) + printf '%s\n' "$VERSION" + ;; + --cppflags) + printf '%s\n' "-I${includedir}" + ;; + --cflags) + printf '%s\n' "-funsigned-char -I${includedir}" + ;; + --libs) + printf '%s\n' '-lsvgc' + ;; + --ldflags) + printf '%s\n' "-L${libdir}" + ;; + svgc) + ;; + esac +done @@ -0,0 +1,404 @@ +dnl # +dnl # /usr/share/aclocal/svgc.m4 +dnl # +dnl # Configure paths for svgc +dnl # Andrew V.Kosteltsev + +dnl ============================================================ +dnl +dnl Synopsis: +dnl AC_CHECK_SVGC([MIN-VERSION [, # minimum svgc version, e.g. 1.1.0 +dnl DEFAULT-WITH-SVGC [, # default value for --with-svgc option +dnl DEFAULT-WITH-SVGC-TEST [,# default value for --with-svgc-test option +dnl EXTEND-VARS [, # whether CFLAGS/LDFLAGS/etc are extended +dnl ACTION-IF-FOUND [, # action to perform if svgc was found +dnl ACTION-IF-NOT-FOUND # action to perform if svgc was not found +dnl ]]]]]]) +dnl Examples: +dnl AC_CHECK_SVGC(1.1.0) +dnl AC_CHECK_SVGC(1.1.0,,,no,CFLAGS="$CFLAGS -DHAVE_SVGC $SVGC_CFLAGS") +dnl AC_CHECK_SVGC(1.1.0,yes,yes,yes,CFLAGS="$CFLAGS -DHAVE_SVGC") +dnl +dnl +dnl If you have to change prefix returned by svgc-config script or change +dnl location of svgc-config, you may set environment variable SVGC_CONFIG, +dnl for example: +dnl +dnl # export SVGC_CONFIG="svgc-config --prefix=/usr/local" +dnl # export SVGC_CONFIG="/usr/bin/svgc-config --prefix=/usr/local" +dnl +dnl ============================================================ +dnl +dnl ============================================================ +dnl auxilliary macros +dnl ============================================================ +AC_DEFUN([_AC_SVGC_ERROR], [dnl +AC_MSG_RESULT([*FAILED*]) +cat <<EOT | sed -e 's/^[[ ]]*/ | /' -e 's/>>/ /' 1>&2 +$1 +EOT +exit 1 +]) + +AC_DEFUN([_AC_SVGC_VERBOSE], [dnl +if test ".$verbose" = .yes; then + AC_MSG_RESULT([ $1]) +fi +]) + +dnl ============================================================ +dnl the user macro +dnl ============================================================ +AC_DEFUN([AC_CHECK_SVGC], [dnl +dnl +dnl ============================================================ +dnl prerequisites +dnl ============================================================ +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +dnl +dnl ============================================================ +dnl set SVGC_CONFIG variable +dnl ============================================================ +if test -z "$SVGC_CONFIG"; then + SVGC_CONFIG='svgc-config' +fi +dnl +SVGC_CPPFLAGS='' +SVGC_CFLAGS='' +SVGC_LDFLAGS='' +SVGC_LIBS='' +AC_SUBST(SVGC_CPPFLAGS) +AC_SUBST(SVGC_CFLAGS) +AC_SUBST(SVGC_LDFLAGS) +AC_SUBST(SVGC_LIBS) +dnl +dnl ============================================================ +dnl command line options +dnl ============================================================ +AC_MSG_CHECKING(for svgc) +_AC_SVGC_VERBOSE([]) +AC_ARG_WITH(svgc,dnl +[ --with-svgc[=ARG] Build with svgc Library (default=]ifelse([$2],,yes,$2)[)],dnl +,dnl +with_svgc="ifelse([$2],,yes,$2)" +)dnl +AC_ARG_WITH(svgc-test,dnl +[ --with-svgc-test Perform svgc Sanity Test (default=]ifelse([$3],,yes,$3)[)],dnl +,dnl +with_svgc_test="ifelse([$3],,yes,$3)" +)dnl +_AC_SVGC_VERBOSE([+ Command Line Options:]) +_AC_SVGC_VERBOSE([ o --with-svgc=$with_svgc]) +_AC_SVGC_VERBOSE([ o --with-svgc-test=$with_svgc_test]) +dnl +dnl ============================================================ +dnl configuration +dnl ============================================================ +if test ".$with_svgc" != .no; then + svgc_subdir=no + svgc_subdir_opts='' + case "$with_svgc" in + subdir:* ) + svgc_subdir=yes + changequote(, )dnl + svgc_subdir_opts=`echo $with_svgc | sed -e 's/^subdir:[^ ]*[ ]*//'` + with_svgc=`echo $with_svgc | sed -e 's/^subdir:\([^ ]*\).*$/\1/'` + changequote([, ])dnl + ;; + esac + svgc_version="" + svgc_location="" + svgc_type="" + svgc_cppflags="" + svgc_cflags="" + svgc_ldflags="" + svgc_libs="" + if test ".$with_svgc" = .yes; then + # via config script in $PATH + changequote(, )dnl + svgc_version=`($SVGC_CONFIG --version) 2>/dev/null |\ + sed -e 's/^.*\([0-9]\.[0-9]*[ab.][0-9]*\).*$/\1/'` + changequote([, ])dnl + if test ".$svgc_version" != .; then + svgc_location=`$SVGC_CONFIG --prefix` + svgc_type='installed' + svgc_cppflags=`$SVGC_CONFIG --cppflags` + svgc_cflags=`$SVGC_CONFIG --cflags` + svgc_ldflags=`$SVGC_CONFIG --ldflags` + svgc_libs=`$SVGC_CONFIG --libs` + fi + elif test -d "$with_svgc"; then + with_svgc=`echo $with_svgc | sed -e 's;/*$;;'` + svgc_found=no + # via config script under a specified directory + # (a standard installation, but not a source tree) + if test ".$svgc_found" = .no; then + for _dir in $with_svgc/bin $with_svgc; do + if test -f "$_dir/svgc-config"; then + test -f "$_dir/svgc-config.in" && continue # svgc-config in source tree! + changequote(, )dnl + svgc_version=`($_dir/svgc-config --version) 2>/dev/null |\ + sed -e 's/^.*\([0-9]\.[0-9]*[ab.][0-9]*\).*$/\1/'` + changequote([, ])dnl + if test ".$svgc_version" != .; then + svgc_location=`$_dir/svgc-config --prefix` + svgc_type="installed" + svgc_cppflags=`$_dir/svgc-config --cppflags` + svgc_cflags=`$_dir/svgc-config --cflags` + svgc_ldflags=`$_dir/svgc-config --ldflags` + svgc_libs=`$_dir/svgc-config --libs` + svgc_found=yes + break + fi + fi + done + fi + fi + _AC_SVGC_VERBOSE([+ Determined Location:]) + _AC_SVGC_VERBOSE([ o path: $svgc_location]) + _AC_SVGC_VERBOSE([ o type: $svgc_type]) + if test ".$svgc_version" = .; then + if test ".$with_svgc" != .yes; then + _AC_SVGC_ERROR([dnl + Unable to locate svgc under $with_svgc. + Please specify the correct path to either a svgc installation tree + (use --with-svgc=DIR if you used --prefix=DIR for installing svgc in + the past).]) + else + _AC_SVGC_ERROR([dnl + Unable to locate svgc in any system-wide location (see \$PATH). + Please specify the correct path to either a svgc installation tree + (use --with-svgc=DIR if you used --prefix=DIR for installing svgc in + the past, or set the SVGC_CONFIG environment variable to the full path + to svgc-config).]) + fi + fi + dnl ======================================================== + dnl Check whether the found version is sufficiently new + dnl ======================================================== + _req_version="ifelse([$1],,1.0.0,$1)" + for _var in svgc_version _req_version; do + eval "_val=\"\$${_var}\"" + _major=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[ab.]]\)\([[0-9]]*\)/\1/'` + _minor=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[ab.]]\)\([[0-9]]*\)/\2/'` + _rtype=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[ab.]]\)\([[0-9]]*\)/\3/'` + _micro=`echo $_val | sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\([[ab.]]\)\([[0-9]]*\)/\4/'` + case $_rtype in + "a" ) _rtype=0 ;; + "b" ) _rtype=1 ;; + "." ) _rtype=2 ;; + esac + _hex=`echo dummy | awk '{ printf("%d%02d%1d%02d", major, minor, rtype, micro); }' \ + "major=$_major" "minor=$_minor" "rtype=$_rtype" "micro=$_micro"` + eval "${_var}_hex=\"\$_hex\"" + done + _AC_SVGC_VERBOSE([+ Determined Versions:]) + _AC_SVGC_VERBOSE([ o existing: $svgc_version -> 0x$svgc_version_hex]) + _AC_SVGC_VERBOSE([ o required: $_req_version -> 0x$_req_version_hex]) + _ok=0 + if test ".$svgc_version_hex" != .; then + if test ".$_req_version_hex" != .; then + if test $svgc_version_hex -ge $_req_version_hex; then + _ok=1 + fi + fi + fi + if test ".$_ok" = .0; then + _AC_SVGC_ERROR([dnl + Found svgc version $svgc_version, but required at least version $_req_version. + Upgrade svgc under $svgc_location to $_req_version or higher first, please.]) + fi + dnl ======================================================== + dnl Perform svgc Sanity Compile Check + dnl ======================================================== + if test ".$with_svgc_test" = .yes; then + _ac_save_CPPFLAGS="$CPPFLAGS" + _ac_save_CFLAGS="$CFLAGS" + _ac_save_LDFLAGS="$LDFLAGS" + _ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $svgc_cppflags" + CFLAGS="$CFLAGS $svgc_cflags" + LDFLAGS="$LDFLAGS $svgc_ldflags" + LIBS="$LIBS $svgc_libs" + _AC_SVGC_VERBOSE([+ Test Build Environment:]) + _AC_SVGC_VERBOSE([ o CPPFLAGS=\"$CPPFLAGS\"]) + _AC_SVGC_VERBOSE([ o CFLAGS=\"$CFLAGS\"]) + _AC_SVGC_VERBOSE([ o LDFLAGS=\"$LDFLAGS\"]) + _AC_SVGC_VERBOSE([ o LIBS=\"$LIBS\"]) + cross_compile=no + define(_code1, [dnl + +#include <stdlib.h> /* exit( 3 ) */ +#include <stdio.h> /* printf( 3 ) */ +#include <unistd.h> /* unlink( 2 ) */ + +#include <svgc.h> + + ]) + define(_code2, [dnl + +struct __svg_sheet *sheet; +struct __svg_line *line; +struct __svg_cmd *cmd; +struct __svg_cmds *cmds; + +struct __svg_scene *scene; + +double width = 500.0; +double height = 400.0; +double lm = 10.0; +double rm = 10.0; +double tm = 10.0; +double bm = 10.0; + +int main( void ) +{ + FILE *fp; + + sheet = (struct __svg_sheet *)__svg_new_sheet( width, height, + lm, rm, tm, bm ); + if( !sheet ) + { + printf( "Unable to create sheet\n" ); + } + (void)__svg_set_stroke_width( (struct __svg_node *)sheet, 0.5 ); + + scene = __svg_new_scene( sheet ); + if( !scene ) + { + printf( "Unable to create scene\n" ); + } + + __svg_set_scene_stroke( "black" ); + __svg_set_scene_stroke_width( 0.5 ); + __svg_set_scene_stroke_linecap( LINECAP_SQUARE ); + + + line = (struct __svg_line *)__svg_new_line( lm, 200, width - rm, 200 ); + (void)__svg_scene_add_node( scene, (struct __svg_node *)line ); + + + __svg_paint_scene( scene, 1, "test.svg" ); + + __svg_free_scene( scene ); + + if( !(fp = fopen( "conftestval", "w" )) ) + { + return( 1 ); + } + + fprintf( fp, "yes" ); + fclose( fp ); + + unlink( "test.svg" ); + + return( 0 ); +} + ]) + _AC_SVGC_VERBOSE([+ Performing Sanity Checks:]) + _AC_SVGC_VERBOSE([ o pre-processor test]) + AC_TRY_CPP(_code1, _ok=yes, _ok=no) + if test ".$_ok" != .yes; then + _AC_SVGC_ERROR([dnl + Found svgc $svgc_version under $svgc_location, but + was unable to perform a sanity pre-processor check. This means + the svgc header svgc.h was not found. + We used the following build environment: + >> CPP="$CPP" + >> CPPFLAGS="$CPPFLAGS" + See config.log for possibly more details.]) + fi + _AC_SVGC_VERBOSE([ o link check]) + AC_TRY_LINK(_code1, + [struct __svg_node *sheet; + + sheet = __svg_new_sheet( 500.0, 400.0, 10.0, 10.0, 10.0, 10.0 ); + if( !sheet ) return( 1 ); + __svg_free_node( sheet );], + _ok=yes, _ok=no) + if test ".$_ok" != .yes; then + _AC_SVGC_ERROR([dnl + Found svgc $svgc_version under $svgc_location, but + was unable to perform a sanity linker check. This means + the svgc library libsvgc.a was not found. + We used the following build environment: + >> CC="$CC" + >> CFLAGS="$CFLAGS" + >> LDFLAGS="$LDFLAGS" + >> LIBS="$LIBS" + See config.log for possibly more details.]) + fi + _AC_SVGC_VERBOSE([ o run-time check]) + AC_TRY_RUN(_code1 _code2, _ok=`cat conftestval`, _ok=no, _ok=no) + if test ".$_ok" != .yes; then + if test ".$_ok" = .no; then + _AC_SVGC_ERROR([dnl + Found svgc $_svgc_version under $svgc_location, but + was unable to perform a sanity execution check. This usually + means that the svgc shared library libsvgc.so is present + but \$LD_LIBRARY_PATH is incomplete to execute a svgc test. + In this case either disable this test via --without-svgc-test, + or extend \$LD_LIBRARY_PATH. + We used the following build environment: + >> CC="$CC" + >> CFLAGS="$CFLAGS" + >> LDFLAGS="$LDFLAGS" + >> LIBS="$LIBS" + See config.log for possibly more details.]) + else + _AC_SVGC_ERROR([dnl + Found svgc $svgc_version under $svgc_location, but + was unable to perform a sanity run-time check. This usually + means that the svgc library failed to work and possibly + caused a core dump in the test program. In this case it + is strongly recommended that you re-install svgc. + We used the following build environment: + >> CC="$CC" + >> CFLAGS="$CFLAGS" + >> LDFLAGS="$LDFLAGS" + >> LIBS="$LIBS" + See config.log for possibly more details.]) + fi + fi + _extendvars="ifelse([$4],,yes,$4)" + if test ".$_extendvars" != .yes; then + CPPFLAGS="$_ac_save_CPPFLAGS" + CFLAGS="$_ac_save_CFLAGS" + LDFLAGS="$_ac_save_LDFLAGS" + LIBS="$_ac_save_LIBS" + fi + else + _extendvars="ifelse([$4],,yes,$4)" + if test ".$_extendvars" = .yes; then + if test ".$svgc_subdir" = .yes; then + CPPFLAGS="$CPPFLAGS $svgc_cppflags" + CFLAGS="$CFLAGS $svgc_cflags" + LDFLAGS="$LDFLAGS $svgc_ldflags" + LIBS="$LIBS $svgc_libs" + fi + fi + fi + SVGC_CPPFLAGS="$svgc_cppflags" + SVGC_CFLAGS="$svgc_cflags" + SVGC_LDFLAGS="$svgc_ldflags" + SVGC_LIBS="$svgc_libs" + AC_SUBST(SVGC_CPPFLAGS) + AC_SUBST(SVGC_CFLAGS) + AC_SUBST(SVGC_LDFLAGS) + AC_SUBST(SVGC_LIBS) + _AC_SVGC_VERBOSE([+ Final Results:]) + _AC_SVGC_VERBOSE([ o SVGC_CPPFLAGS=\"$SVGC_CPPFLAGS\"]) + _AC_SVGC_VERBOSE([ o SVGC_CFLAGS=\"$SVGC_CFLAGS\"]) + _AC_SVGC_VERBOSE([ o SVGC_LDFLAGS=\"$SVGC_LDFLAGS\"]) + _AC_SVGC_VERBOSE([ o SVGC_LIBS=\"$SVGC_LIBS\"]) +fi +if test ".$with_svgc" != .no; then + AC_MSG_RESULT([version $svgc_version, $svgc_type under $svgc_location]) + ifelse([$5], , :, [$5]) +else + AC_MSG_RESULT([no]) + ifelse([$6], , :, [$6]) +fi +]) + diff --git a/svgc.pc.in b/svgc.pc.in new file mode 100644 index 0000000..8009de1 --- /dev/null +++ b/svgc.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: svgc +Description: SVG C library +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lsvgc +Cflags: -I${includedir} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..ae244f2 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,12 @@ + +check_PROGRAMS = test-css-file test-script-file + +TESTS = test-css-file test-script-file + +test_css_file_SOURCES = test-css-file.c +test_css_file_CPPFLAGS = -I$(top_srcdir)/src +test_css_file_LDADD = $(top_builddir)/src/libsvgc.la + +test_script_file_SOURCES = test-script-file.c +test_script_file_CPPFLAGS = -I$(top_srcdir)/src +test_script_file_LDADD = $(top_builddir)/src/libsvgc.la diff --git a/tests/test-css-file.c b/tests/test-css-file.c new file mode 100644 index 0000000..fa4c159 --- /dev/null +++ b/tests/test-css-file.c @@ -0,0 +1,130 @@ + +/*************************************************************** + SVGC CSS TEST + + Exercise stylesheet inclusion and preservation of input text. + ***************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <svgc.h> + + +static int +write_file( const char *path, const void *data, size_t length ) +{ + FILE *fp; + int status; + + fp = fopen( path, "wb" ); + if( !fp ) return( 0 ); + status = (fwrite( data, 1, length, fp ) == length); + if( fclose( fp ) != 0 ) status = 0; + return( status ); +} + + +static char * +read_file( const char *path ) +{ + FILE *fp; + char *data; + long length; + + fp = fopen( path, "rb" ); + if( !fp ) return( NULL ); + if( fseek( fp, 0L, SEEK_END ) != 0 ) goto failed; + length = ftell( fp ); + if( length < 0 ) goto failed; + if( fseek( fp, 0L, SEEK_SET ) != 0 ) goto failed; + if( (unsigned long)length >= (size_t)-1 ) goto failed; + + data = (char *)malloc( (size_t)length + 1 ); + if( !data ) goto failed; + if( fread( data, 1, (size_t)length, fp ) != (size_t)length ) + { + free( data ); + goto failed; + } + data[length] = '\0'; + fclose( fp ); + return( data ); + +failed: + fclose( fp ); + return( NULL ); +} + + +int +main( void ) +{ + static const char css_text[] = + "@font-face { font-family: 'Roboto';\n" + " src: url('./fonts/Roboto/Roboto-Regular.woff2') format('woff2'); }\n" + ".a::after { content: '\xD0\xAF ]]> test'; }\n"; + static const char invalid_utf8[] = { (char)0xC0, (char)0x80 }; + char large_css[10000]; + static const char invalid_xml[] = { '.', 'a', '{', (char)0x01, '}' }; + struct __svg_sheet *sheet; + struct __svg_scene *scene = NULL; + char *svg = NULL; + char css_path[] = "svgc-test-css-XXXXXX"; + char svg_path[] = "svgc-test-svg-XXXXXX"; + int fd; + int status = 1; + + fd = mkstemp( css_path ); + if( fd < 0 ) return( 1 ); + close( fd ); + fd = mkstemp( svg_path ); + if( fd < 0 ) + { + unlink( css_path ); + return( 1 ); + } + close( fd ); + + sheet = (struct __svg_sheet *)__svg_new_sheet( 300, 200, 8, 8, 8, 8 ); + if( !sheet ) goto done; + scene = __svg_new_scene( sheet ); + if( !scene ) goto done; + + if( __svg_sheet_add_css_file( NULL, css_path ) != -1 ) goto done; + if( __svg_sheet_add_css_file( sheet, NULL ) != -1 ) goto done; + if( __svg_sheet_add_css_file( sheet, "" ) != -1 ) goto done; + if( __svg_sheet_add_css_file( sheet, "svgc-file-which-does-not-exist.css" ) != 0 ) goto done; + if( !write_file( css_path, invalid_utf8, sizeof( invalid_utf8 ) ) ) goto done; + if( __svg_sheet_add_css_file( sheet, css_path ) != 0 ) goto done; + if( !write_file( css_path, invalid_xml, sizeof( invalid_xml ) ) ) goto done; + if( __svg_sheet_add_css_file( sheet, css_path ) != 0 ) goto done; + if( !write_file( css_path, css_text, sizeof( css_text ) - 1 ) ) goto done; + if( __svg_sheet_add_css_file( sheet, css_path ) != 1 ) goto done; + memset( large_css, 'a', sizeof( large_css ) ); + memcpy( large_css, "/*", 2 ); + memcpy( large_css + sizeof( large_css ) - 2, "*/", 2 ); + if( !write_file( css_path, large_css, sizeof( large_css ) ) ) goto done; + if( __svg_sheet_add_css_file( sheet, css_path ) != 1 ) goto done; + if( !write_file( css_path, "replaced", 8 ) ) goto done; + __svg_paint_scene( scene, 0, svg_path ); + svg = read_file( svg_path ); + if( !svg ) goto done; + if( !strstr( svg, "<style type=\"text/css\">\n <![CDATA[\n" ) ) goto done; + if( !strstr( svg, "src: url('./fonts/Roboto/Roboto-Regular.woff2')" ) ) goto done; + if( !strstr( svg, "\xD0\xAF ]]]]><![CDATA[> test" ) ) goto done; + if( strstr( svg, "replaced" ) ) goto done; + if( !strstr( svg, " ]]>\n </style>\n" ) ) goto done; + if( !strstr( svg, "*/\n ]]>\n </style>" ) ) goto done; + if( !strstr( svg, "/*aaaaaaaa" ) ) goto done; + + status = 0; +done: + if( status ) fprintf( stderr, "SVGC CSS regression failed\n" ); + if( scene ) __svg_free_scene( scene ); + free( svg ); + unlink( css_path ); + unlink( svg_path ); + return( status ); +} diff --git a/tests/test-script-file.c b/tests/test-script-file.c new file mode 100644 index 0000000..d0c5688 --- /dev/null +++ b/tests/test-script-file.c @@ -0,0 +1,160 @@ + +/*************************************************************** + SVGC ECMASCRIPT FILE TEST + + Exercise sheet-local scripts and CSS, raw text preservation, + CDATA splitting, invalid input, and copied source ownership. + ***************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <svgc.h> + + +static int +write_file( const char *path, const void *data, size_t length ) +{ + FILE *fp; + int status; + + fp = fopen( path, "wb" ); + if( !fp ) return( 0 ); + status = (fwrite( data, 1, length, fp ) == length); + if( fclose( fp ) != 0 ) status = 0; + return( status ); +} + + +static char * +read_file( const char *path ) +{ + FILE *fp; + char *data; + long length; + + fp = fopen( path, "rb" ); + if( !fp ) return( NULL ); + if( fseek( fp, 0L, SEEK_END ) != 0 ) goto failed; + length = ftell( fp ); + if( length < 0 ) goto failed; + if( fseek( fp, 0L, SEEK_SET ) != 0 ) goto failed; + if( (unsigned long)length >= (size_t)-1 ) goto failed; + + data = (char *)malloc( (size_t)length + 1 ); + if( !data ) goto failed; + if( fread( data, 1, (size_t)length, fp ) != (size_t)length ) + { + free( data ); + goto failed; + } + data[length] = '\0'; + fclose( fp ); + return( data ); + +failed: + fclose( fp ); + return( NULL ); +} + + +int +main( void ) +{ + static const char script_text[] = + "function changeColor(event) {\n" + " if (2 < 3 && event.target) {\n" + " event.target.style.fill = 'blue';\n" + " }\n" + "}\n" + "// CDATA test: ]]> must be preserved.\n" + "// UTF-8 text: \xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\n"; + static const char css_text[] = "rect { stroke: black; }\n"; + static const char bad_utf8[] = { (char)0xC0, (char)0x80 }; + static const char bad_xml[] = { '/', '/', (char)0x01, '\n' }; + char big_script[9500]; + char script_path[] = "svgc-script-test-XXXXXX"; + char svg_path[] = "svgc-script-svg-XXXXXX"; + char css_path[] = "svgc-script-css-XXXXXX"; + struct __svg_sheet *sheet = NULL; + struct __svg_scene *scene = NULL; + struct __svg_node *rectangle = NULL; + char *svg = NULL; + char *script, *css, *defs_end; + int fd; + int status = 1; + + fd = mkstemp( script_path ); + if( fd < 0 ) return( 1 ); + close( fd ); + fd = mkstemp( svg_path ); + if( fd < 0 ) goto done; + close( fd ); + fd = mkstemp( css_path ); + if( fd < 0 ) goto done; + close( fd ); + + sheet = (struct __svg_sheet *)__svg_new_sheet( 400, 200, 8, 8, 8, 8 ); + if( !sheet ) goto done; + scene = __svg_new_scene( sheet ); + if( !scene ) goto done; + + if( __svg_sheet_add_script_file( NULL, script_path ) != -1 ) goto done; + if( __svg_sheet_add_script_file( sheet, NULL ) != -1 ) goto done; + if( __svg_sheet_add_script_file( sheet, "" ) != -1 ) goto done; + if( __svg_sheet_add_script_file( sheet, "file-which-does-not-exist.js" ) != 0 ) goto done; + + rectangle = __svg_new_rect( 10, 20, 200, 100, 0, 0 ); + if( !rectangle ) goto done; + if( __svg_sheet_add_script_file( (struct __svg_sheet *)rectangle, script_path ) != -1 ) goto done; + __svg_free_node( rectangle ); + rectangle = NULL; + + if( !write_file( script_path, bad_utf8, sizeof( bad_utf8 ) ) ) goto done; + if( __svg_sheet_add_script_file( sheet, script_path ) != 0 ) goto done; + if( !write_file( script_path, bad_xml, sizeof( bad_xml ) ) ) goto done; + if( __svg_sheet_add_script_file( sheet, script_path ) != 0 ) goto done; + + if( !write_file( css_path, css_text, sizeof( css_text ) - 1 ) ) goto done; + if( __svg_sheet_add_css_file( sheet, css_path ) != 1 ) goto done; + if( !write_file( script_path, script_text, sizeof( script_text ) - 1 ) ) goto done; + if( __svg_sheet_add_script_file( sheet, script_path ) != 1 ) goto done; + + memset( big_script, 'a', sizeof( big_script ) ); + memcpy( big_script, "/*", 2 ); + memcpy( big_script + sizeof( big_script ) - 2, "*/", 2 ); + if( !write_file( script_path, big_script, sizeof( big_script ) ) ) goto done; + if( __svg_sheet_add_script_file( sheet, script_path ) != 1 ) goto done; + if( !write_file( script_path, "changed", 7 ) ) goto done; + + __svg_paint_scene( scene, 0, svg_path ); + svg = read_file( svg_path ); + if( !svg ) goto done; + css = strstr( svg, "<style type=\"text/css\">\n <![CDATA[\n" ); + script = strstr( svg, "<script type=\"text/javascript\">\n <![CDATA[\n" ); + defs_end = strstr( svg, "</defs>" ); + if( !css || !script || !defs_end || !(css < script && script < defs_end) ) goto done; + if( !strstr( svg, "rect { stroke: black; }" ) ) goto done; + if( !strstr( svg, "if (2 < 3 && event.target)" ) ) goto done; + if( !strstr( svg, "CDATA test: ]]]]><![CDATA[> must be preserved" ) ) goto done; + if( !strstr( svg, "// UTF-8 text: \xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82" ) ) goto done; + if( !strstr( svg, "/*aaaaaaaa" ) ) goto done; + if( strstr( svg, "changed" ) ) goto done; + if( !strstr( svg, " ]]>\n </script>\n" ) ) goto done; + if( !strstr( svg, "*/\n ]]>\n </script>" ) ) goto done; + if( strstr( svg, "<script type=\"text/javascript\"><