Codebase list dvisvgm / 8ef7cb6
New upstream version 2.12 Hilmar Preusse 2 years ago
104 changed file(s) with 1192 addition(s) and 594 deletion(s). Raw diff Collapse all Expand all
0 version: 2.11.1-{build}
0 version: 2.12-{build}
11 configuration: Release
22
33 image: Visual Studio 2019 Preview
1111 - tmp
1212
1313 install:
14 - set FREETYPE_VER=2.10.4
14 - set FREETYPE_VER=2.11.0
1515 - set ZLIB_VER=1.2.11
1616 - set TTFA_VER=1.8.2
1717 - set TTFA_REV=3
00 doc/dvisvgm.* linguist-documentation
1 libs/* linguist-vendored
1 libs/** linguist-vendored
0 name: C/C++ CI
1
2 on:
3 push:
4 branches: [ master ]
5 pull_request:
6 branches: [ master ]
7
8 jobs:
9 build:
10
11 runs-on: ubuntu-latest
12
13 steps:
14 - uses: actions/checkout@v2
15 - name: install dependencies
16 run: sudo apt-get install -qq autotools-dev libkpathsea-dev libfreetype6-dev libgs-dev libz-dev texlive-base python-lxml asciidoc xmlto xsltproc
17 - name: autogen
18 run: ./autogen.sh
19 - name: configure
20 run: ./configure --enable-bundled-libs
21 - name: make clean
22 run: make clean
23 - name: make
24 run: make
25 - name: update timestamps
26 run: make -C src -t
27 - name: make check
28 run: make check
33 *.o
44 *.lo
55 *.Po
6 *.pyc
67 *.obj
78 *.dvi
89 *.gch
33 *.o
44 *.lo
55 *.Po
6 *.pyc
67 *.obj
78 *.dvi
89 *.gch
9696 eeaf1a69d766b12cc417acc7b8a723311417e362 2.10
9797 585e39596d7473b92af9814128cdc2dd0a7615f3 2.10.1
9898 ea2da8135fbbefed47cb954382c90e851cd28189 2.11
99 552dd227ac4ef6fb804e31a71e72a1d5a76a0738 2.11.1
2828 project:
2929 name: mgieseki/dvisvgm
3030 description: "dvisvgm -- A fast DVI to SVG converter"
31 version: 2.11.1
31 version: 2.12
3232 notification_email: martin.gieseking@uos.de
3333 build_command_prepend: "./configure --enable-bundled-libs; make clean"
3434 build_command: "make -j"
0 dvisvgm-2.12 (2021-08-16)
1 - added transparency support of SVG elements created outside the PS handler
2 (GH issue #148)
3 - fixed spacing issue caused by unexpected newline characters in SVG output
4 - fixed PS error occurred when defining (yet unsupported) PS shading patterns
5 - fixed issue in color handling of PS tiling patterns (GH issue #158)
6 - fixed displaced graphics occurred if PDF MediBox is not located at the origin
7 - fixed handling of root directories of file paths
8 - improved handling of drive letters (Windows only)
9 - several code refactorings and improvements
10
011 dvisvgm-2.11.1 (2021-01-21)
112 - fixed possible ambiguity of GID to charcode mappings (GH issue #147)
213 - refactored representation of token objects in calculator class
00 _dvisvgm_ – A fast DVI to SVG converter
11 =============================================
2 [![C/C++ CI](https://github.com/mgieseki/dvisvgm/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/mgieseki/dvisvgm/actions/workflows/c-cpp.yml)
23 [![Build Status](https://travis-ci.org/mgieseki/dvisvgm.svg?branch=master)](https://travis-ci.org/mgieseki/dvisvgm)
34 [![Build Status](https://ci.appveyor.com/api/projects/status/0rbkw88js1on4g2u/branch/master?svg=true)](https://ci.appveyor.com/project/mgieseki/dvisvgm/branch/master)
4 [![Copr Status](https://copr.fedorainfracloud.org/coprs/mgieseki/dvisvgm/package/dvisvgm/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/mgieseki/dvisvgm)
55 [![Code Status](https://scan.coverity.com/projects/1099/badge.svg)](https://scan.coverity.com/projects/1099)
66 [![License](https://img.shields.io/:license-GPL%20v3+-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html)
77 [![Releases](https://img.shields.io/github/release/mgieseki/dvisvgm.svg)](https://github.com/mgieseki/dvisvgm/releases)
33 # Process this file with autoconf to produce a configure script.
44
55 AC_PREREQ(2.59)
6 AC_INIT([dvisvgm],[2.11.1],[martin.gieseking@uos.de])
7 DATE="January 2021"
6 AC_INIT([dvisvgm],[2.12],[martin.gieseking@uos.de])
7 DATE="August 2021"
88 AC_CONFIG_SRCDIR(src)
99 AC_CONFIG_HEADERS([config.h])
1010 AC_CONFIG_MACRO_DIR([m4])
11 .\" Title: dvisvgm
22 .\" Author: Martin Gieseking <martin.gieseking@uos.de>
33 .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
4 .\" Date: 2021-01-03
4 .\" Date: 2021-04-10
55 .\" Manual: dvisvgm Manual
6 .\" Source: dvisvgm 2.11.1
6 .\" Source: dvisvgm 2.12
77 .\" Language: English
88 .\"
9 .TH "DVISVGM" "1" "2021\-01\-03" "dvisvgm 2\&.11\&.1" "dvisvgm Manual"
9 .TH "DVISVGM" "1" "2021\-04\-10" "dvisvgm 2\&.12" "dvisvgm Manual"
1010 .\" -----------------------------------------------------------------
1111 .\" * Define some portability stuff
1212 .\" -----------------------------------------------------------------
4545 .sp
4646 However, TeX\(cqs main source for font descriptions is Metafont, which produces bitmap output (GF files)\&. That\(cqs why not all obtainable TeX fonts are available in a scalable format\&. In these cases, dvisvgm tries to vectorize Metafont\(cqs output by tracing the glyph bitmaps\&. The results are not as perfect as most (manually optimized) PFB or OTF counterparts, but are nonetheless really nice in most cases\&.
4747 .sp
48 When running dvisvgm without option \fB\-\-no\-fonts\fR, it creates \fIfont\fR elements (\fB<font>\fR\&...\fB</font>\fR) to embed the font data into the SVG files\&. Unfortunately, only few SVG renderers support these elements yet\&. Most web browsers and vector graphics applications don\(cqt evaluate them properly so that the text components of the resulting graphics might look strange\&. In order to create more compatible SVG files, command\-line option \fB\-\-no\-fonts\fR can be given to replace the font elements by plain graphics paths\&. Most web browsers (but only few external SVG renderers) also suppport WOFF and WOFF2 fonts that can be used instead of the default SVG fonts\&. Option \fB\-\-font\-format\fR offers the functionality to change the format applied to the fonts being embedded\&. This, however, only works when converting DVI files\&. Text present in PDF and PostScript files is always converted to path elements\&.
48 When running dvisvgm without option \fB\-\-no\-fonts\fR, it creates \fIfont\fR elements (\fB<font>\fR\&...\fB</font>\fR) to embed the font data into the SVG files\&. Unfortunately, only few SVG renderers support these elements yet\&. Most web browsers and vector graphics applications don\(cqt evaluate them properly so that the text components of the resulting graphics might look strange\&. In order to create more compatible SVG files, command\-line option \fB\-\-no\-fonts\fR can be given to replace the font elements by plain graphics paths\&. Most web browsers (but only few external SVG renderers) also support WOFF and WOFF2 fonts that can be used instead of the default SVG fonts\&. Option \fB\-\-font\-format\fR offers the functionality to change the format applied to the fonts being embedded\&. This, however, only works when converting DVI files\&. Text present in PDF and PostScript files is always converted to path elements\&.
4949 .SH "OPTIONS"
5050 .sp
5151 dvisvgm provides a POSIX\-compliant command\-line interface with short and long option names\&. They may be given before and/or after the name of the file to be converted\&. Also, the order of specifying the options is not significant, i\&.e\&. you can add them in any order without changing dvisvgm\(cqs behavior\&. Certain options accept or require additional parameters which are directly appended to or separated by whitespace from a short option (e\&.g\&. \fB\-v0\fR or \fB\-v 0\fR)\&. Long options require an additional equals sign (\fB=\fR) between option name and argument but without any surrounding whitespace (e\&.g\&. \fB\-\-verbosity=0\fR)\&. Multiple short options that don\(cqt expect a further parameter can be combined after a single dash (e\&.g\&. \fB\-ejs\fR rather than \fB\-e \-j \-s\fR)\&.
661661 .PP
662662 \fB\-o, \-\-output\fR=\fIpattern\fR
663663 .RS 4
664 Sets the pattern specifying the names of the generated SVG files\&. Parameter
664 Sets the pattern that determines the names of the generated SVG files\&. The required parameter
665665 \fIpattern\fR
666 is a string that may contain static character sequences as well as the variables
666 may consist of an arbitrary sequence of characters which make up the filenames\&. With the exception of the following mentioned variables and expressions, all characters are treated as static parts of the filenames and are therefore identical for all pages processed during a run of dvisvgm\&. The strings
667667 \fB%f\fR,
668668 \fB%p\fR,
669669 \fB%P\fR,
670670 \fB%hd\fR,
671671 \fB%ho\fR, and
672 \fB%hc\fR\&.
672 \fB%hc\fR
673 are variables that can be used as part of the pattern\&.
673674 \fB%f\fR
674675 expands to the base name of the DVI file, i\&.e\&. the filename without suffix,
675676 \fB%p\fR
676677 is the current page number, and
677678 \fB%P\fR
678 the total number of pages in the DVI file\&. An optional number (0\-9) given directly after the percent sign specifies the minimal number of digits to be written\&. If a particular value consists of less digits, the number is padded with leading zeros\&. Example:
679 the total number of pages in the DVI file\&. An optional number (0\-9) given directly after the percent sign of a variable holding a numeric value denotes the minimal number of digits to be created\&. If a particular value consists of less digits, the number is padded with leading zeros\&. Example:
679680 \fB%3p\fR
680681 enforces 3 digits for the current page number (001, 002, etc\&.)\&. Without an explicit width specifier,
681682 \fB%p\fR
702703 \fB%hc\fR
703704 are only set if option
704705 \fB\-\-page\-hashes\fR
705 is present\&. Otherwise, it\(cqs empty\&. For further information, see the description of option
706 is present\&. Otherwise, they are empty\&. For further information, see the description of option
706707 \fB\-\-page\-hashes\fR
707708 below\&.
708709 .sp
2121 :man source: dvisvgm
2222 :man version: @VERSION@
2323 :man manual: dvisvgm Manual
24 :revdate: 2021-01-03 19:25 +0100
24 :revdate: 2021-04-10 01:45 +0200
2525
2626 Name
2727 ----
6868 elements yet. Most web browsers and vector graphics applications don't evaluate them properly so
6969 that the text components of the resulting graphics might look strange. In order to create more
7070 compatible SVG files, command-line option *--no-fonts* can be given to replace the font elements
71 by plain graphics paths. Most web browsers (but only few external SVG renderers) also suppport
71 by plain graphics paths. Most web browsers (but only few external SVG renderers) also support
7272 WOFF and WOFF2 fonts that can be used instead of the default SVG fonts. Option *--font-format*
7373 offers the functionality to change the format applied to the fonts being embedded. This, however,
7474 only works when converting DVI files. Text present in PDF and PostScript files is always
453453 expression is used.
454454
455455 *-o, --output*='pattern'::
456 Sets the pattern specifying the names of the generated SVG files. Parameter 'pattern' is a string
457 that may contain static character sequences as well as the variables +%f+, +%p+, +%P+, +%hd+,
458 +%ho+, and +%hc+. +%f+ expands to the base name of the DVI file, i.e. the filename without
459 suffix, +%p+ is the current page number, and +%P+ the total number of pages in the DVI file. An
460 optional number (0-9) given directly after the percent sign specifies the minimal number of digits
461 to be written. If a particular value consists of less digits, the number is padded with leading
462 zeros. Example: +%3p+ enforces 3 digits for the current page number (001, 002, etc.). Without an
463 explicit width specifier, +%p+ gets the same number of digits as +%P+.
456 Sets the pattern that determines the names of the generated SVG files. The required parameter
457 'pattern' may consist of an arbitrary sequence of characters which make up the filenames. With the
458 exception of the following mentioned variables and expressions, all characters are treated as static
459 parts of the filenames and are therefore identical for all pages processed during a run of dvisvgm.
460 The strings +%f+, +%p+, +%P+, +%hd+, +%ho+, and +%hc+ are variables that can be used as part of the
461 pattern. +%f+ expands to the base name of the DVI file, i.e. the filename without suffix, +%p+ is the
462 current page number, and +%P+ the total number of pages in the DVI file.
463 An optional number (0-9) given directly after the percent sign of a variable holding a numeric value
464 denotes the minimal number of digits to be created. If a particular value consists of less digits,
465 the number is padded with leading zeros.
466 Example: +%3p+ enforces 3 digits for the current page number (001, 002, etc.). Without an explicit
467 width specifier, +%p+ gets the same number of digits as +%P+.
464468 +
465469 If you need more control over the numbering, you can use arithmetic expressions as part of a pattern.
466470 The syntax is +%(+'expr'+)+ where 'expr' may contain additions, subtractions, multiplications, and
470474 +
471475 The variables +%hX+ contain different hash values computed from the DVI page data and the options
472476 given on the command-line. +%hd+ and +%hc+ are only set if option *--page-hashes* is present.
473 Otherwise, it's empty. For further information, see the description of option *--page-hashes* below.
477 Otherwise, they are empty. For further information, see the description of option *--page-hashes*
478 below.
474479 +
475480 The default pattern is +%f-%p.svg+ if the DVI file consists of more than one page, and
476481 +%f.svg+ otherwise. That means, a DVI file 'foo.dvi' is converted to 'foo.svg' if 'foo.dvi'
99 lines = infile.readlines()
1010 for line in lines:
1111 if re.match(r'(.*\\def)|(.*\\href)', line) == None:
12 line = re.sub(r'([a-zA-Z0-9]+)/', r'\1\slash{}', line)
12 line = re.sub(r'([a-zA-Z0-9]+)/', r'\1\\slash{}', line)
1313 line = re.sub(r'-{}-{}', r'\=/\=/', line)
1414 line = re.sub(r'([^a-zA-Z0-9])-{}', r'\1\=/', line)
15 print >>outfile, line.rstrip()
15 print(line.rstrip(), file=outfile)
1616 os.remove(latex_file_old)
1717 return 0
1515 # The second argument, if specified, indicates whether you insist on an
1616 # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
1717 # -std=c++11). If neither is specified, you get whatever works, with
18 # preference for an extended mode.
18 # preference for no added switch, and then for an extended mode.
1919 #
2020 # The third argument, if specified 'mandatory' or if left unspecified,
2121 # indicates that baseline support for the specified C++ standard is
3434 # Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
3535 # Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
3636 # Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
37 # Copyright (c) 2020 Jason Merrill <jason@redhat.com>
3738 #
3839 # Copying and distribution of this file, with or without modification, are
3940 # permitted in any medium without royalty provided the copyright notice
4041 # and this notice are preserved. This file is offered as-is, without any
4142 # warranty.
4243
43 #serial 11
44 #serial 12
4445
4546 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
4647 dnl (serial version number 13).
6061 [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
6162 AC_LANG_PUSH([C++])dnl
6263 ac_success=no
64
65 m4_if([$2], [], [dnl
66 AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
67 ax_cv_cxx_compile_cxx$1,
68 [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
69 [ax_cv_cxx_compile_cxx$1=yes],
70 [ax_cv_cxx_compile_cxx$1=no])])
71 if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
72 ac_success=yes
73 fi])
6374
6475 m4_if([$2], [noext], [], [dnl
6576 if test x$ac_success = xno; then
3535 if (!_pageColors.empty() && _pageColors.back().first == pageno)
3636 _pageColors.back().second = color;
3737 else
38 _pageColors.emplace_back(PageColor(pageno, color));
38 _pageColors.emplace_back(pageno, color);
3939 }
4040 }
4141
7575 vector<string> lengthStrings = util::split(boxstr, " ");
7676 for (const string &lenstr : lengthStrings) {
7777 if (!lenstr.empty())
78 lengths.emplace_back(Length(lenstr));
78 lengths.emplace_back(lenstr);
7979 }
8080 return lengths;
8181 }
118118
119119
120120 // try to find a compatible encoding CMap
121 const bool is_unicode_map = bool(dynamic_cast<const UnicodeCMap*>(cmap));
121 const bool is_unicode_map = cmap->mapsToUnicode();
122122 const string ro = cmap->getROString();
123123 for (const CharMapIDToEncName &enc : encodings) {
124124 for (const CharMapID &id : charmapIDs) {
2525 #include "Font.hpp"
2626 #include "FontManager.hpp"
2727 #include "HashFunction.hpp"
28 #include "JFM.hpp"
2928 #include "utility.hpp"
3029 #include "VectorStream.hpp"
3130
202201 * @param[in] font current font (corresponding to _currFontNum)
203202 * @param[in] c character to typeset */
204203 void DVIReader::putVFChar (Font *font, uint32_t c) {
205 if (auto vf = dynamic_cast<VirtualFont*>(font)) { // is current font a virtual font?
204 if (auto vf = font_cast<VirtualFont*>(font)) { // is current font a virtual font?
206205 FontManager &fm = FontManager::instance();
207206 const vector<uint8_t> *dvi = vf->getDVI(c); // try to get DVI snippet that represents character c
208207 Font *firstFont = fm.vfFirstFont(vf);
209 if (!dvi && (!firstFont || !dynamic_cast<const JFM*>(firstFont->getMetrics())))
208 if (!dvi && (!firstFont || !firstFont->getMetrics()->isJFM()))
210209 return;
211210 fm.enterVF(vf); // enter VF font number context
212211 int savedFontNum = _currFontNum; // save current font number
484483 else { // TFM-based font specified by name
485484 int id = fm.registerFont(fontnum, name, cs, dsize, ssize);
486485 font = fm.getFontById(id);
487 if (auto vf = dynamic_cast<VirtualFont*>(font)) {
486 if (auto vf = font_cast<VirtualFont*>(font)) {
488487 // read vf file, register its font and character definitions
489488 fm.enterVF(vf);
490489 ifstream ifs(vf->path(), ios::binary);
591590 FontManager::instance().registerFont(fontnum, fontname, fontIndex, ptsize, style, color);
592591 font = FontManager::instance().getFont(fontnum);
593592 }
594 dviXFontDef(fontnum, dynamic_cast<const NativeFont*>(font));
593 dviXFontDef(fontnum, font_cast<const NativeFont*>(font));
595594 }
596595
597596
7373 DVIToSVG::HashSettings DVIToSVG::PAGE_HASH_SETTINGS;
7474
7575
76 DVIToSVG::DVIToSVG (istream &is, SVGOutputBase &out) : DVIReader(is), _out(out)
76 DVIToSVG::DVIToSVG (istream &is, SVGOutputBase &out)
77 : DVIReader(is), _out(out), _prevWritingMode(WritingMode::LR)
7778 {
78 _pageHeight = _pageWidth = 0;
79 _tx = _ty = 0; // no cursor translation
80 _pageByte = 0;
8179 _prevXPos = _prevYPos = numeric_limits<double>::min();
82 _prevWritingMode = WritingMode::LR;
8380 _actions = util::make_unique<DVIToSVGActions>(*this, _svg);
8481 }
8582
361358 unordered_set<const Font*> tracedFonts; // collect unique fonts already traced
362359 for (const auto &fontchar : usedCharsMap) {
363360 const Font *font = fontchar.first;
364 if (auto ph_font = dynamic_cast<const PhysicalFont*>(font)) {
361 if (auto ph_font = font_cast<const PhysicalFont*>(font)) {
365362 // Check if glyphs should be traced. Only trace the glyphs of unique fonts, i.e.
366363 // avoid retracing the same glyphs again if they are referenced in various sizes.
367364 if (TRACE_MODE != 0 && tracedFonts.find(ph_font->uniqueFont()) == tracedFonts.end()) {
480477
481478
482479 void DVIToSVG::dviSetChar0 (uint32_t c, const Font *font) {
483 if (_actions && !dynamic_cast<const VirtualFont*>(font))
480 if (_actions && !font_cast<const VirtualFont*>(font))
484481 _actions->setChar(dviState().h+_tx, dviState().v+_ty, c, dviState().d != WritingMode::LR, *font);
485482 }
486483
519516
520517
521518 void DVIToSVG::dviFontNum (uint32_t fontnum, SetFontMode, const Font *font) {
522 if (_actions && font && !dynamic_cast<const VirtualFont*>(font))
519 if (_actions && font && !font_cast<const VirtualFont*>(font))
523520 _actions->setFont(FontManager::instance().fontID(fontnum), *font); // all fonts get a recomputed ID
524521 }
525522
100100 SVGTree _svg;
101101 SVGOutputBase &_out;
102102 std::unique_ptr<DVIActions> _actions;
103 std::string _bboxFormatString; ///< bounding box size/format set by the user
104 std::string _transCmds; ///< page transformation commands set by the user
105 double _pageHeight, _pageWidth; ///< global page height and width stored in the postamble
106 double _tx, _ty; ///< translation of cursor position
107 double _prevXPos, _prevYPos; ///< previous cursor position
108 WritingMode _prevWritingMode; ///< previous writing mode
109 std::streampos _pageByte; ///< position of the stream pointer relative to the preceding bop (in bytes)
103 std::string _bboxFormatString; ///< bounding box size/format set by the user
104 std::string _transCmds; ///< page transformation commands set by the user
105 double _pageHeight=0, _pageWidth=0; ///< global page height and width stored in the postamble
106 double _tx=0, _ty=0; ///< translation of cursor position
107 double _prevXPos, _prevYPos; ///< previous cursor position
108 WritingMode _prevWritingMode; ///< previous writing mode
109 std::streampos _pageByte=0; ///< position of the stream pointer relative to the preceding bop (in bytes)
110110 };
111111
112112 #endif
104104
105105 GlyphMetrics metrics;
106106 font.getGlyphMetrics(c, vertical, metrics);
107 auto pf = dynamic_cast<const PhysicalFont*>(&font);
107 auto pf = font_cast<const PhysicalFont*>(&font);
108108 if (PhysicalFont::EXACT_BBOX && pf) {
109109 GlyphMetrics exact_metrics;
110110 pf->getExactGlyphBox(c, exact_metrics, vertical, &callback);
165165 return;
166166
167167 // (x,y) is the lower left corner of the rectangle
168 auto rect = util::make_unique<XMLElement>("rect");
168 auto rect = util::make_unique<SVGElement>("rect");
169169 rect->addAttribute("x", x);
170170 rect->addAttribute("y", y-height);
171171 rect->addAttribute("height", height);
172172 rect->addAttribute("width", width);
173 if (!getMatrix().isIdentity())
174 rect->addAttribute("transform", getMatrix().toSVG());
175 if (getColor() != Color::BLACK)
176 rect->addAttribute("fill", _svg.getColor().svgColorString());
173 rect->setTransform(getMatrix());
174 rect->setFillColor(_svg.getColor());
177175 _svg.appendToPage(std::move(rect));
178176
179177 // update bounding box
236234 _svg.transformPage(matrix);
237235 if (_bgcolor != Color::TRANSPARENT) {
238236 // create a rectangle filled with the background color
239 auto rect = util::make_unique<XMLElement>("rect");
237 auto rect = util::make_unique<SVGElement>("rect");
240238 rect->addAttribute("x", _bbox.minX());
241239 rect->addAttribute("y", _bbox.minY());
242240 rect->addAttribute("width", _bbox.width());
243241 rect->addAttribute("height", _bbox.height());
244 rect->addAttribute("fill", _bgcolor.svgColorString());
242 rect->setFillColor(_bgcolor);
245243 _svg.prependToPage(std::move(rect));
246244 }
247245 }
4848 void setBgColor (const Color &color) override;
4949 void setColor (const Color &color) override {_svg.setColor(color);}
5050 void setMatrix (const Matrix &m) override {_svg.setMatrix(m);}
51 void setOpacity (const Opacity &opacity) override {_svg.setOpacity(opacity);}
52 const Opacity& getOpacity () const override {return _svg.getOpacity();}
5153 const Matrix& getMatrix () const override {return _svg.getMatrix();}
5254 Matrix getPageTransformation () const override {return _dvireader->getPageTransformation();}
5355 Color getColor () const override {return _svg.getColor();}
3737 # could be handy for archiving the generated documentation or if some version
3838 # control system is used.
3939
40 PROJECT_NUMBER = 2.11.1
40 PROJECT_NUMBER = 2.12
4141
4242 # Using the PROJECT_BRIEF tag one can provide an optional one line description
4343 # for a project that appears at the top of each page and should give viewer a
8080 _currentMacro = _macros.end();
8181 throw SpecialException("redefinition of SVG fragment '" + id + "'");
8282 }
83 pair<string, StringVector> entry(id, StringVector());
84 pair<MacroMap::iterator, bool> state = _macros.emplace(move(entry));
83 pair<MacroMap::iterator, bool> state = _macros.emplace(id, StringVector());
8584 _currentMacro = state.first;
8685 }
8786
193192 /** Evaluates substrings of the form {?(expr)} where 'expr' is a math expression,
194193 * and replaces the substring by the computed value.
195194 * @param[in,out] str string to scan for expressions */
196 static void evaluate_expressions (string &str, SpecialActions &actions) {
195 static void evaluate_expressions (string &str, const SpecialActions &actions) {
197196 size_t left = str.find("{?("); // start position of expression macro
198197 while (left != string::npos) {
199198 size_t right = str.find(")}", left+2); // end position of expression macro
385384 Length h = read_length(ir);
386385 string f = ir.getString();
387386 update_bbox(w, h, Length(0), false, actions);
388 auto img = util::make_unique<XMLElement>("image");
387 auto img = util::make_unique<SVGElement>("image");
389388 img->addAttribute("x", actions.getX());
390389 img->addAttribute("y", actions.getY());
391390 img->addAttribute("width", w.bp());
392391 img->addAttribute("height", h.bp());
393392 img->addAttribute("xlink:href", f);
394 if (!actions.getMatrix().isIdentity())
395 img->addAttribute("transform", actions.getMatrix().toSVG());
393 img->setTransform(actions.getMatrix());
396394 actions.svgTree().appendToPage(std::move(img));
397395 }
398396 catch (const UnitException &e) {
447445 // collect/extract an XML fragment that only contains complete tags
448446 // incomplete tags are held back
449447 _xmlbuf += xml;
450 size_t left=0, right;
448 size_t left=0;
451449 try {
452450 while (left != string::npos) {
453 right = _xmlbuf.find('<', left);
451 size_t right = _xmlbuf.find('<', left);
454452 if (left < right && left < _xmlbuf.length()) // plain text found?
455453 (actions.svgTree().*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right-left)));
456454 if (right != string::npos) {
522520 BufferInputReader ir(ib);
523521 string name = ir.getString("/ \t\n\r");
524522 ir.skipSpace();
525 auto elemNode = util::make_unique<XMLElement>(name);
523 auto elemNode = util::make_unique<SVGElement>(name);
526524 map<string, string> attribs;
527525 if (ir.parseAttributes(attribs, true, "\"'")) {
528526 for (const auto &attrpair : attribs)
2828
2929 class InputReader;
3030 class SpecialActions;
31 class SVGElement;
3132 class SVGTree;
3233 class XMLElement;
3334 class XMLNode;
4344 class DvisvgmSpecialHandler : public SpecialHandler {
4445 class XMLParser {
4546 using AppendFunc = void (SVGTree::*)(std::unique_ptr<XMLNode>);
46 using PushFunc = void (SVGTree::*)(std::unique_ptr<XMLElement>);
47 using PushFunc = void (SVGTree::*)(std::unique_ptr<SVGElement>);
4748 using PopFunc = void (SVGTree::*)();
4849 using NameStack = std::vector<std::string>;
4950
176176 if (isStraightLine()) {
177177 DPair dir = (_endPoint - _startPoint);
178178 dir /= dir.length()/3.0;
179 beziers.emplace_back(Bezier(_startPoint, _startPoint+dir, _endPoint-dir, _endPoint));
179 beziers.emplace_back(_startPoint, _startPoint+dir, _endPoint-dir, _endPoint);
180180 }
181181 else {
182182 CenterParams cparams = getCenterParams();
189189 if (numCurves > 0) {
190190 double c = cos(_rotationAngle);
191191 double s = sin(_rotationAngle);
192 Matrix ellipse = {_rx*c, -_ry*s, cparams.center.x(), _rx*s, _ry*c, cparams.center.y()};
192 Matrix ellipse{_rx*c, -_ry*s, cparams.center.x(), _rx*s, _ry*c, cparams.center.y()};
193193 double angle = cparams.startAngle;
194194 double diff = cparams.deltaAngle/numCurves;
195195 while (numCurves-- > 0) {
196 beziers.emplace_back(approx_unit_arc(angle, diff).transform(ellipse));
196 beziers.push_back(approx_unit_arc(angle, diff).transform(ellipse));
197197 angle += diff;
198198 }
199199 }
2323 #include "InputReader.hpp"
2424 #include "Length.hpp"
2525 #include "SpecialActions.hpp"
26 #include "SVGElement.hpp"
2627 #include "SVGTree.hpp"
27 #include "XMLNode.hpp"
28 #include "XMLString.hpp"
2928
3029 using namespace std;
3130
7675 static void create_line (const DPair &p1, const DPair &p2, char c1, char c2, double lw, SpecialActions &actions) {
7776 if (actions.outputLocked())
7877 return;
79 unique_ptr<XMLElement> node;
78 unique_ptr<SVGElement> node;
8079 DPair dir = p2-p1;
8180 if (dir.x() == 0 || dir.y() == 0 || (c1 == 'p' && c2 == 'p')) {
8281 // draw regular line
83 node = util::make_unique<XMLElement>("line");
82 node = util::make_unique<SVGElement>("line");
8483 node->addAttribute("x1", p1.x());
8584 node->addAttribute("y1", p1.y());
8685 node->addAttribute("x2", p2.x());
8786 node->addAttribute("y2", p2.y());
88 node->addAttribute("stroke-width", lw);
89 node->addAttribute("stroke", actions.getColor().svgColorString());
87 node->setStrokeWidth(lw);
88 node->setStrokeColor(actions.getColor());
89 node->setStrokeOpacity(actions.getOpacity());
90
9091 // update bounding box
9192 DPair cv = cut_vector('p', dir, lw);
9293 actions.embed(p1+cv);
9697 }
9798 else {
9899 // draw polygon
100 vector<DPair> points;
99101 DPair cv1 = cut_vector(c1, dir, lw);
100102 DPair cv2 = cut_vector(c2, dir, lw);
101 DPair q11 = p1+cv1, q12 = p1-cv1;
102 DPair q21 = p2+cv2, q22 = p2-cv2;
103 ostringstream oss;
104 oss << XMLString(q11.x()) << ',' << XMLString(q11.y()) << ' '
105 << XMLString(q12.x()) << ',' << XMLString(q12.y()) << ' '
106 << XMLString(q22.x()) << ',' << XMLString(q22.y()) << ' '
107 << XMLString(q21.x()) << ',' << XMLString(q21.y());
108 node = util::make_unique<XMLElement>("polygon");
109 node->addAttribute("points", oss.str());
110 if (actions.getColor() != Color::BLACK)
111 node->addAttribute("fill", actions.getColor().svgColorString());
103 points.push_back(p1+cv1);
104 points.push_back(p1-cv1);
105 points.push_back(p2-cv2);
106 points.push_back(p2+cv2);
107
108 node = util::make_unique<SVGElement>("polygon");
109 node->setPoints(points);
110 node->setFillColor(actions.getColor());
111 node->setFillOpacity(actions.getOpacity());
112
112113 // update bounding box
113 actions.embed(q11);
114 actions.embed(q12);
115 actions.embed(q21);
116 actions.embed(q22);
114 actions.embed(points[0]);
115 actions.embed(points[1]);
116 actions.embed(points[2]);
117 actions.embed(points[3]);
117118 }
118119 actions.svgTree().appendToPage(std::move(node));
119120 }
235236 // Line endpoints don't necessarily have to be defined before
236237 // a line definition. If a point isn't defined yet, we put the line
237238 // in a wait list and process the lines at the end of the page.
238 _lines.emplace_back(Line(pointnum1, pointnum2, char(cut1), char(cut2), linewidth));
239 _lines.emplace_back(pointnum1, pointnum2, char(cut1), char(cut2), linewidth);
239240 }
240241 }
241242
3636
3737
3838 #ifdef _WIN32
39 /** Returns the drive letter of a given path string or 0 if there's none. */
40 static char drive_letter (const string &path) {
41 if (path.length() >= 2 && path[1] == ':' && isalpha(path[0]))
42 return tolower(path[0]);
43 return '\0';
44 }
45
46 /** Removes the drive letter and following colon from a given path string if present.
47 * @param[in] path path to strip drive letter from
48 * @return drive letter or 0 if there was none */
3949 static char strip_drive_letter (string &path) {
40 char letter = 0;
50 char letter = '\0';
4151 if (path.length() >= 2 && path[1] == ':' && isalpha(path[0])) {
4252 letter = path[0];
4353 path.erase(0, 2);
4454 }
45 return letter;
46 }
47
48
49 static char adapt_current_path (string &path, char target_drive) {
50 if (char current_drive = strip_drive_letter(path)) {
51 if (target_drive != current_drive) {
52 if (target_drive == 0)
53 target_drive = current_drive;
54 if (path.empty() || path[0] != '/') {
55 if (FileSystem::chdir(string(1, target_drive) + ":")) {
56 path.insert(0, FileSystem::getcwd()+"/");
57 strip_drive_letter(path);
58 }
59 else
60 throw MessageException("drive " + string(1, target_drive) + ": not accessible");
61 }
62 }
63 }
64 return target_drive;
65 }
66
55 return tolower(letter);
56 }
6757 #endif
6858
6959
9888 * @param[in] path absolute or relative path to a file or directory
9989 * @param[in] isfile true if 'path' references a file, false if a directory is referenced */
10090 void FilePath::set (const string &path, bool isfile) {
101 init(path, isfile, FileSystem::getcwd());
91 init(path, isfile, "");
10292 }
10393
10494
120110 _fname.clear();
121111 single_slashes(path);
122112 single_slashes(current_dir);
123 #ifdef _WIN32
113 #ifndef _WIN32
114 if (current_dir.empty())
115 current_dir = FileSystem::getcwd();
116 #else
117 _drive = strip_drive_letter(path);
118 if (current_dir.empty() || drive_letter(current_dir) != _drive)
119 current_dir = FileSystem::getcwd(_drive);
120 if (!_drive)
121 _drive = drive_letter(current_dir);
122 strip_drive_letter(current_dir);
124123 path = FileSystem::ensureForwardSlashes(path);
125 _drive = strip_drive_letter(path);
126124 #endif
127125 if (isfile) {
128126 size_t pos = path.rfind('/');
129127 _fname = path.substr((pos == string::npos) ? 0 : pos+1);
130 if (pos != string::npos)
128 // remove filename from path
129 if (pos == 0 && _fname.length() > 1) // file in root directory?
130 path.erase(1);
131 else if (pos != string::npos)
131132 path.erase(pos);
132133 else
133134 path.clear();
134135 }
135 if (current_dir.empty())
136 current_dir = FileSystem::getcwd();
137 #ifdef _WIN32
138 _drive = adapt_current_path(current_dir, _drive);
139 #endif
140 if (!path.empty()) {
141 if (path[0] == '/')
142 current_dir.clear();
143 else if (current_dir[0] != '/')
144 current_dir = "/";
145 else {
146 FilePath curr(current_dir, false, "/");
147 current_dir = curr.absolute();
148 #ifdef _WIN32
149 adapt_current_path(current_dir, _drive);
150 #endif
151 }
152 }
153 path.insert(0, current_dir + "/");
136 if ((path.empty() || path[0] != '/') && !current_dir.empty())
137 path.insert(0, current_dir + "/");
154138 string elem;
155139 for (char c : path) {
156140 if (c != '/')
157141 elem += c;
158 else {
142 else if (!elem.empty()){
159143 add(elem);
160144 elem.clear();
161145 }
162146 }
163 add(elem);
147 if (!elem.empty())
148 add(elem);
164149 }
165150
166151
168153 void FilePath::add (const string &dir) {
169154 if (dir == ".." && !_dirs.empty())
170155 _dirs.pop_back();
171 else if (dir.length() > 0 && dir != ".")
156 else if (!dir.empty() && dir != ".")
172157 _dirs.emplace_back(dir);
173158 }
174159
222207 * @return the absolute path string */
223208 string FilePath::absolute (bool with_filename) const {
224209 string path;
210 #ifdef _WIN32
211 if (_drive)
212 path = string(1, _drive) + ":";
213 #endif
225214 for (const Directory &dir : _dirs)
226215 path += "/" + string(dir);
227216 if (path.empty())
228217 path = "/";
229218 if (with_filename && !_fname.empty())
230219 path += "/"+_fname;
231 #ifdef _WIN32
232 if (_drive)
233 path.insert(0, string(1, _drive) + ":");
234 #endif
235220 return single_slashes(path);
236221 }
237222
244229 * @param[in] with_filename if false, the filename is omitted
245230 * @return the relative path string */
246231 string FilePath::relative (string reldir, bool with_filename) const {
232 #ifdef _WIN32
233 char reldrive = drive_letter(reldir);
234 if (reldir.empty()) {
235 reldir = FileSystem::getcwd(_drive);
236 reldrive = drive_letter(FileSystem::getcwd());
237 }
238 bool isAbsolute = (reldir[0] == '/')
239 || (reldir.length() >= 3 && isalpha(reldir[0]) && reldir[1] == ':' && reldir[2] == '/');
240 #else
247241 if (reldir.empty())
248242 reldir = FileSystem::getcwd();
249 #ifdef _WIN32
250 adapt_current_path(reldir, _drive);
251 #endif
252 if (reldir[0] != '/')
243 bool isAbsolute = (reldir[0] == '/');
244 #endif
245 if (!isAbsolute)
253246 return absolute();
254 FilePath rel(reldir, false);
247 FilePath relpath(reldir, false);
255248 string path;
256 #ifdef _WIN32
257 if (rel._drive && _drive && tolower(rel._drive) != tolower(_drive))
258 path += string(1, _drive) + ":";
259 #endif
260249 auto it1 = _dirs.begin();
261 auto it2 = rel._dirs.begin();
262 while (it1 != _dirs.end() && it2 != rel._dirs.end() && *it1 == *it2)
250 auto it2 = relpath._dirs.begin();
251 while (it1 != _dirs.end() && it2 != relpath._dirs.end() && *it1 == *it2)
263252 ++it1, ++it2;
264 for (; it2 != rel._dirs.end(); ++it2)
253 for (; it2 != relpath._dirs.end(); ++it2)
265254 path += "../";
266255 for (; it1 != _dirs.end(); ++it1)
267256 path += string(*it1) + "/";
268 if (!path.empty())
257 if (!path.empty() && path.back() == '/')
269258 path.erase(path.length()-1, 1); // remove trailing slash
270259 if (with_filename && !_fname.empty()) {
271260 if (!path.empty() && path != "/")
274263 }
275264 if (path.empty())
276265 path = ".";
266 #ifdef _WIN32
267 if (relpath._drive && _drive && _drive != reldrive)
268 path.insert(0, string(1, _drive) + ":");
269 #endif
277270 return single_slashes(path);
278271 }
279272
134134 }
135135
136136
137 /** Returns the absolute path of the current working directory. */
137138 string FileSystem::getcwd () {
138139 char buf[1024];
139140 #ifdef _WIN32
140 return ensureForwardSlashes(_getcwd(buf, 1024));
141 GetCurrentDirectoryA(1024, buf);
142 return ensureForwardSlashes(buf);
141143 #else
142144 return ::getcwd(buf, 1024);
143145 #endif
144146 }
147
148
149 #ifdef _WIN32
150 /** Returns the absolute path of the current directory of a given drive.
151 * Windows keeps a current directory for every drive, i.e. when accessing a drive
152 * without specifying a path (e.g. with "cd z:"), the current directory of that
153 * drive is used.
154 * @param[in] drive letter of drive to get the current directory from
155 * @return absolute path of the directory */
156 string FileSystem::getcwd (char drive) {
157 string cwd = getcwd();
158 if (cwd.length() > 1 && cwd[1] == ':' && tolower(cwd[0]) != tolower(drive)) {
159 chdir(string(1, drive)+":");
160 string cwd2 = cwd;
161 cwd = getcwd();
162 chdir(string(1, cwd2[0])+":");
163 }
164 return cwd;
165 }
166 #endif
145167
146168
147169 /** Changes the work directory.
4747 static uint64_t filesize (const std::string &fname);
4848 static std::string ensureForwardSlashes (std::string path);
4949 static std::string getcwd ();
50 #ifdef _WIN32
51 static std::string getcwd (char drive);
52 #endif
5053 static std::string tmpdir ();
5154 static bool chdir (const std::string &dir);
5255 static bool exists (const std::string &fname);
678678 return (it == _charDefs.end() ? nullptr : &it->second);
679679 }
680680
681 //////////////////////////////////////////////////////////////////////////////
682
683 void PhysicalFont::visit (FontVisitor &visitor) {visitor.visited(this);}
684 void VirtualFont::visit (FontVisitor &visitor) {visitor.visited(this);}
685 void NativeFont::visit (FontVisitor &visitor) {visitor.visited(this);}
686 void PhysicalFont::visit (FontVisitor &visitor) const {visitor.visited(this);}
687 void VirtualFont::visit (FontVisitor &visitor) const {visitor.visited(this);}
688 void NativeFont::visit (FontVisitor &visitor) const {visitor.visited(this);}
4949 double wl, wr, h, d;
5050 };
5151
52 class FontVisitor;
5253
5354 /** Abstract base for all font classes. */
5455 class Font {
6869 virtual const char* path () const =0;
6970 virtual const char* filename () const;
7071 virtual const FontEncoding* encoding () const;
71 virtual bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback *callback=nullptr) const =0;
72 virtual bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback *callback) const =0;
7273 virtual void getGlyphMetrics (int c, bool vertical, GlyphMetrics &metrics) const;
7374 virtual uint32_t unicode (uint32_t c) const;
7475 virtual void tidy () const {}
7576 virtual bool findAndAssignBaseFontMap () {return true;}
76 virtual bool verticalLayout () const {return getMetrics() ? getMetrics()->verticalLayout() : false;}
77 virtual bool verticalLayout () const {return getMetrics() != nullptr && getMetrics()->verticalLayout();}
7778 virtual bool verifyChecksums () const {return true;}
7879 virtual int fontIndex () const {return 0;}
7980 virtual const FontStyle* style () const {return nullptr;}
8081 virtual Color color () const {return Color::BLACK;}
8182 virtual const FontMap::Entry* fontMapEntry () const;
83 virtual void visit (FontVisitor &visitor) =0;
84 virtual void visit (FontVisitor &visitor) const =0;
8285 };
8386
8487
99102 double italicCorr (int c) const override {return 0;}
100103 const FontMetrics* getMetrics () const override {return nullptr;}
101104 const char* path () const override {return nullptr;}
102 bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback *cb=nullptr) const override {return false;}
105 bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback*) const override {return false;}
106 void visit (FontVisitor &visitor) override {}
107 void visit (FontVisitor &visitor) const override {}
103108
104109 private:
105110 std::string _fontname;
114119 static std::unique_ptr<Font> create (const std::string &name, uint32_t checksum, double dsize, double ssize, PhysicalFont::Type type);
115120 static std::unique_ptr<Font> create (const std::string &name, int fontindex, uint32_t checksum, double dsize, double ssize);
116121 virtual Type type () const =0;
117 bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback *cb=nullptr) const override;
118 virtual bool getExactGlyphBox (int c, BoundingBox &bbox, GFGlyphTracer::Callback *cb=nullptr) const;
119 virtual bool getExactGlyphBox (int c, GlyphMetrics &metrics, bool vertical, GFGlyphTracer::Callback *cb=nullptr) const;
122 bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback *cb) const override;
123 virtual bool getExactGlyphBox (int c, BoundingBox &bbox, GFGlyphTracer::Callback *cb) const;
124 virtual bool getExactGlyphBox (int c, GlyphMetrics &metrics, bool vertical, GFGlyphTracer::Callback *cb) const;
120125 virtual bool isCIDFont () const;
121126 virtual int hAdvance () const;
122127 virtual std::string familyName () const;
128133 virtual double scaledAscent () const;
129134 virtual int ascent () const;
130135 virtual int descent () const;
131 virtual int traceAllGlyphs (bool includeCached, GFGlyphTracer::Callback *cb=nullptr) const;
136 virtual int traceAllGlyphs (bool includeCached, GFGlyphTracer::Callback *cb) const;
132137 virtual int collectCharMapIDs (std::vector<CharMapID> &charmapIDs) const;
133138 virtual CharMapID getCharMapID () const =0;
134139 virtual void setCharMapID (const CharMapID &id) {}
135140 virtual Character decodeChar (uint32_t c) const;
136141 const char* path () const override;
142 void visit (FontVisitor &visitor) override;
143 void visit (FontVisitor &visitor) const override;
137144
138145 protected:
139146 bool createGF (std::string &gfname) const;
158165 public:
159166 static std::unique_ptr<Font> create (const std::string &name, uint32_t checksum, double dsize, double ssize);
160167 virtual const DVIVector* getDVI (int c) const =0;
161 bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback *cb=nullptr) const override {return false;}
168 bool getGlyph (int c, Glyph &glyph, GFGlyphTracer::Callback*) const override {return false;}
169 void visit (FontVisitor &visitor) override;
170 void visit (FontVisitor &visitor) const override;
162171
163172 protected:
164173 virtual void assignChar (uint32_t c, DVIVector &&dvi) =0;
271280 Color color () const override {return _color;}
272281 const FontMap::Entry* fontMapEntry () const override {return nullptr;}
273282 static std::string uniqueName (const std::string &path, const FontStyle &style);
283 void visit (FontVisitor &visitor) override;
284 void visit (FontVisitor &visitor) const override;
274285
275286 protected:
276287 NativeFont (double ptsize, const FontStyle &style, Color color) : _ptsize(ptsize), _style(style), _color(color) {}
389400 };
390401
391402
403 struct FontVisitor {
404 virtual ~FontVisitor () =default;
405 virtual void visited (const PhysicalFont *font) {}
406 virtual void visited (const VirtualFont *font) {}
407 virtual void visited (const NativeFont *font) {visited(static_cast<const PhysicalFont*>(font));}
408 virtual void visited (PhysicalFont *font) {}
409 virtual void visited (VirtualFont *font) {}
410 virtual void visited (NativeFont *font) {visited(static_cast<PhysicalFont*>(font));}
411 };
412
413
414 /** This function works similar to dynamic_cast but only on pointers to Font classes.
415 * It uses double dispatch instead of RTTI and should therefore be faster.
416 * @param[in] font font pointer to be cast
417 * @return cast pointer on success, nullptr otherwise */
418 template <typename C>
419 C font_cast (typename util::set_const_of<Font>::by<typename std::remove_pointer<C>::type>::type *font) {
420 struct : FontVisitor {
421 void visited (C font) override {result = font;}
422 C result = nullptr;
423 } visitor;
424 if (font)
425 font->visit(visitor);
426 return visitor.result;
427 }
428
429
392430 struct FontException : public MessageException {
393431 explicit FontException (const std::string &msg) : MessageException(msg) {}
394432 };
280280 string path = dirname+"/"+(fname.substr(1));
281281 ifstream ifs(path, ios::binary);
282282 if (fontinfo(ifs, info))
283 infos.emplace_back(move(info));
283 infos.push_back(move(info));
284284 else
285 invalid.emplace_back(fname.substr(1));
285 invalid.push_back(fname.substr(1));
286286 }
287287 }
288288 }
102102 return true;
103103
104104 if (const char *path=font.path()) {
105 auto pf = dynamic_cast<const PhysicalFont*>(&font);
105 auto pf = font_cast<const PhysicalFont*>(&font);
106106 if (setFont(path, font.fontIndex(), pf ? pf->getCharMapID() : CharMapID())) {
107107 _currentFont = &font;
108108 return true;
307307 if (_currentFace) {
308308 for (int i=0; i < _currentFace->num_charmaps; i++) {
309309 FT_CharMap charmap = _currentFace->charmaps[i];
310 charmapIDs.emplace_back(CharMapID(charmap->platform_id, charmap->encoding_id));
310 charmapIDs.emplace_back(charmap->platform_id, charmap->encoding_id);
311311 }
312312 }
313313 return charmapIDs.size();
220220 }
221221 _name2id[name] = newid;
222222 }
223 _fonts.emplace_back(std::move(newfont));
223 _fonts.push_back(std::move(newfont));
224224 if (_vfStack.empty()) // register font referenced in dvi file?
225225 _num2id[fontnum] = newid;
226226 else { // register font referenced in vf file
227227 const VirtualFont *vf = _vfStack.top();
228228 _vfnum2id[vf][fontnum] = newid;
229 if (_vfFirstFontNumMap.find(vf) == _vfFirstFontNumMap.end()) { // first fontdef of VF?
230 _vfFirstFontNumMap.emplace(vf, fontnum);
231 _vfFirstFontMap.emplace(vf, _fonts.back().get());
232 }
229 _vfFirstFontNumMap.emplace(vf, fontnum);
230 _vfFirstFontMap.emplace(vf, _fonts.back().get());
233231 }
234232 return newid;
235233 }
268266 const int newid = _fonts.size(); // the new font gets this ID
269267 auto it = _name2id.find(fontname);
270268 if (it != _name2id.end()) { // font with same name already registered?
271 if (auto font = dynamic_cast<NativeFont*>(_fonts[it->second].get()))
269 if (auto font = font_cast<NativeFont*>(_fonts[it->second].get()))
272270 newfont = font->clone(ptsize, style, color);
273271 }
274272 else {
290288 }
291289 _name2id[fontname] = newid;
292290 }
293 _fonts.emplace_back(std::move(newfont));
291 _fonts.push_back(std::move(newfont));
294292 _num2id[fontnum] = newid;
295293 return newid;
296294 }
329327 if (_fonts[i] == font)
330328 id = i;
331329
332 VirtualFont *vf = dynamic_cast<VirtualFont*>(font);
330 VirtualFont *vf = font_cast<VirtualFont*>(font);
333331 for (int j=0; j < level+1; j++)
334332 os << " ";
335333 os << "id " << id
4242 virtual uint32_t getChecksum () const =0;
4343 virtual uint16_t firstChar () const =0;
4444 virtual uint16_t lastChar () const =0;
45 virtual bool isJFM () const {return false;}
4546 static std::unique_ptr<FontMetrics> read (const std::string &fontname);
4647 };
4748
5858 vector<string> FontWriter::supportedFormats () {
5959 vector<string> formats;
6060 for (const FontFormatInfo &info : _formatInfos)
61 formats.emplace_back(info.formatstr_short);
61 formats.push_back(info.formatstr_short);
6262 return formats;
6363 }
6464
2424 /** Constructs a new glyph tracer.
2525 * @param[in] is GF input stream
2626 * @param[in] upp target units per PS point */
27 GFGlyphTracer::GFGlyphTracer (string &fname, double upp, Callback *cb)
27 GFGlyphTracer::GFGlyphTracer (const string &fname, double upp, Callback *cb)
2828 : GFTracer(_ifs, upp), _callback(cb)
2929 {
3030 if (_callback)
3333 }
3434
3535
36 void GFGlyphTracer::reset (string &fname, double upp) {
36 void GFGlyphTracer::reset (const string &fname, double upp) {
3737 if (_callback)
3838 _callback->setFont(fname);
3939 if (_ifs.is_open())
3737
3838 public:
3939 GFGlyphTracer () : GFTracer(_ifs, 0) {}
40 GFGlyphTracer (std::string &fname, double upp, Callback *cb=nullptr);
41 void reset (std::string &fname, double upp);
40 GFGlyphTracer (const std::string &fname, double upp, Callback *cb=nullptr);
41 void reset (const std::string &fname, double upp);
4242 void setCallback (Callback *cb) {_callback = cb;}
4343 bool executeChar (uint8_t c) override;
4444 void moveTo (double x, double y) override;
9191 if (opcode < 0) // at end of file?
9292 throw GFException("unexpected end of file");
9393
94 if (opcode >= 0 && opcode <= 63)
94 if (opcode <= 63)
9595 cmdPaint0(opcode);
9696 else if (opcode >= 74 && opcode <= 238)
9797 cmdNewRow(opcode-74);
214214 /** Retrieves version information about Ghostscript.
215215 * @param[out] r takes the revision information (see GS API documentation for further details)
216216 * @return true on success */
217 bool Ghostscript::revision (gsapi_revision_t *r) {
217 bool Ghostscript::revision (gsapi_revision_t *r) const {
218218 #if defined(HAVE_LIBGS)
219219 return (gsapi_revision(r, sizeof(gsapi_revision_t)) == 0);
220220 #else
226226
227227
228228 /** Returns the revision number of the GS library. */
229 int Ghostscript::revision () {
229 int Ghostscript::revision () const {
230230 gsapi_revision_t r;
231231 if (revision(&r))
232232 return static_cast<int>(r.revision);
7777 ~Ghostscript ();
7878 bool init (int argc, const char **argv, void *caller=nullptr);
7979 bool available ();
80 bool revision (gsapi_revision_t *r);
81 int revision ();
80 bool revision (gsapi_revision_t *r) const;
81 int revision () const;
8282 std::string revisionstr ();
8383 int set_stdio (Stdin in, Stdout out, Stderr err);
8484 int run_string_begin (int user_errors, int *pexit_code);
1919
2020 #pragma once
2121
22 #include <algorithm>
2223 #include <array>
2324 #include <cctype>
2425 #include <cmath>
661662 bool operator == (const GraphicsPath &path) const {
662663 if (size() != path.size())
663664 return false;
664 auto it = _commands.begin();
665 for (const auto &cmd : path._commands) {
666 if (*it++ != cmd)
667 return false;
668 }
669 return true;
665 return std::equal(_commands.begin(), _commands.end(), path._commands.begin());
670666 }
671667
672668 /** Returns true if this path differs from another one (command-wise). */
673669 bool operator != (const GraphicsPath &path) const {
674670 if (size() != path.size())
675671 return true;
676 auto it = _commands.begin();
677 for (const auto &cmd : path._commands) {
678 if (*it++ != cmd)
679 return true;
680 }
681 return false;
672 return !std::equal(_commands.begin(), _commands.end(), path._commands.begin());
682673 }
683674
684675 /** Iterates over all commands defining this path and calls the corresponding template methods.
105105 uri = "/" + uri;
106106 uri = _base + uri;
107107 }
108 auto anchorNode = util::make_unique<XMLElement>("a");
108 auto anchorNode = util::make_unique<SVGElement>("a");
109109 anchorNode->addAttribute("xlink:href", uri);
110110 anchorNode->addAttribute("xlink:title", XMLString(name.empty() ? uri : name, false));
111111 actions.svgTree().pushPageContext(std::move(anchorNode));
147147 if (bbox.width() > 0 && bbox.height() > 0) { // does the bounding box extend in both dimensions?
148148 if (MARKER_TYPE != MarkerType::NONE) {
149149 const double linewidth = _linewidth >= 0 ? _linewidth : min(0.5, bbox.height()/15);
150 auto rect = util::make_unique<XMLElement>("rect");
150 auto rect = util::make_unique<SVGElement>("rect");
151151 double x = bbox.minX();
152152 double y = bbox.maxY()+linewidth;
153153 double w = bbox.width();
154154 double h = linewidth;
155155 const Color linecolor = COLORSOURCE == ColorSource::DEFAULT ? actions.getColor() : LINK_LINECOLOR;
156156 if (MARKER_TYPE == MarkerType::LINE)
157 rect->addAttribute("fill", linecolor.svgColorString());
157 rect->setFillColor(linecolor);
158158 else {
159159 const double offset = _linewidth < 0 ? linewidth : 0 ;
160160 x -= offset;
162162 w += 2*offset;
163163 h += bbox.height()+offset;
164164 if (MARKER_TYPE == MarkerType::BGCOLOR) {
165 rect->addAttribute("fill", LINK_BGCOLOR.svgColorString());
165 rect->setFillColor(LINK_BGCOLOR);
166166 if (COLORSOURCE != ColorSource::DEFAULT) {
167 rect->addAttribute("stroke", linecolor.svgColorString());
168 rect->addAttribute("stroke-width", linewidth);
167 rect->setStrokeColor(linecolor);
168 rect->setStrokeWidth(linewidth);
169169 }
170170 }
171171 else { // LM_BOX
172 rect->addAttribute("fill", "none");
173 rect->addAttribute("stroke", linecolor.svgColorString());
174 rect->addAttribute("stroke-width", linewidth);
172 rect->setNoFillColor();
173 rect->setStrokeColor(linecolor);
174 rect->setStrokeWidth(linewidth);
175175 }
176176 }
177177 rect->addAttribute("x", x);
191191 // Create an invisible rectangle around the linked area so that it's easier to access.
192192 // This is only necessary when using paths rather than real text elements together with fonts.
193193 if (!SVGTree::USE_FONTS) {
194 auto rect = util::make_unique<XMLElement>("rect");
194 auto rect = util::make_unique<SVGElement>("rect");
195195 rect->addAttribute("x", bbox.minX());
196196 rect->addAttribute("y", bbox.minY());
197197 rect->addAttribute("width", bbox.width());
198198 rect->addAttribute("height", bbox.height());
199 rect->addAttribute("fill", "white");
200 rect->addAttribute("fill-opacity", 0);
199 rect->setFillColor(Color::WHITE);
200 rect->setFillOpacity(OpacityAlpha(0, 0));
201201 actions.svgTree().appendToPage(std::move(rect));
202202 }
203203 }
5151 void checkNewLine (SpecialActions &actions);
5252 void createLink (std::string uri, SpecialActions &actions);
5353 void createViews (unsigned pageno, SpecialActions &actions);
54 void setBaseUrl (std::string &base) {_base = base;}
54 void setBaseUrl (const std::string &base) {_base = base;}
5555 void setLineWidth (double w) {_linewidth = w;}
5656 static HyperlinkManager& instance ();
5757 static bool setLinkMarker (const std::string &marker);
5656 void setY (double y) override {_y = y; _svg.setY(y);}
5757 void finishLine () override {}
5858 void setColor (const Color &color) override {_svg.setColor(color);}
59 void setOpacity (const Opacity &opacity) override {_svg.setOpacity(opacity);}
5960 Color getColor () const override {return _svg.getColor();}
6061 void setMatrix (const Matrix &m) override {_svg.setMatrix(m);}
6162 const Matrix& getMatrix () const override {return _svg.getMatrix();}
63 const Opacity& getOpacity () const override {return _svg.getOpacity();}
6264 const SVGTree& svgTree () const override {return _svg;}
6365 void setBgColor (const Color &color) override {}
6466 void embed (const BoundingBox &bbox) override {_bbox.embed(bbox);}
3939 class StreamInputBuffer : public InputBuffer {
4040 public:
4141 explicit StreamInputBuffer (std::istream &is, size_t bufsize=1024);
42 StreamInputBuffer (const StreamInputBuffer &ib) =delete;
4243 ~StreamInputBuffer () override;
4344 int get () override;
4445 int peek () const override;
4546 int peek (size_t n) const override;
4647 bool eof () const override {return pos() == _size1 && _size2 == 0;}
4748 void invalidate () override {_bufptr = _buf1+_size1; _size2 = 0;}
49 void operator = (const StreamInputBuffer &ib) =delete;
4850
4951 protected:
5052 int fillBuffer (uint8_t *buf);
6466 class StringInputBuffer : public InputBuffer {
6567 public:
6668 explicit StringInputBuffer (const std::string &str) : _str(&str) {}
69 StringInputBuffer (const StreamInputBuffer &ib) =delete;
6770 void assign (const std::string &str) {_str = &str; _pos=0;}
6871 int get () override {return _pos < _str->length() ? _str->at(_pos++) : -1;}
6972 int peek () const override {return _pos < _str->length() ? _str->at(_pos) : -1;}
8083 class CharInputBuffer : public InputBuffer {
8184 public:
8285 CharInputBuffer (const char *buf, size_t size) : _pos(buf), _size(buf ? size : 0) {}
86 CharInputBuffer (const CharInputBuffer &ib) =delete;
8387
8488 int get () override {
8589 if (_size == 0)
111115 class SplittedCharInputBuffer : public InputBuffer {
112116 public:
113117 SplittedCharInputBuffer (const char *buf1, size_t s1, const char *buf2, size_t s2);
118 SplittedCharInputBuffer (const SplittedCharInputBuffer &ib) =delete;
114119 int get () override;
115120 int peek () const override;
116121 int peek (size_t n) const override;
2828 public:
2929 explicit JFM (std::istream &is);
3030 bool verticalLayout () const override {return _vertical;}
31 bool isJFM () const override {return true;}
3132 uint32_t minChar () const {return _minchar;}
3233 uint32_t maxChar () const {return static_cast<uint32_t>(_minchar+_charTypeTable.size()-1);}
3334
103103 MetafontWrapper.hpp MetafontWrapper.cpp \
104104 NoPsSpecialHandler.hpp NoPsSpecialHandler.cpp \
105105 NumericRanges.hpp \
106 Opacity.hpp Opacity.cpp \
106107 PageRanges.hpp PageRanges.cpp \
107108 PageSize.hpp PageSize.cpp \
108109 Pair.hpp \
133134 SVGCharHandlerFactory.hpp SVGCharHandlerFactory.cpp \
134135 SVGCharPathHandler.hpp SVGCharPathHandler.cpp \
135136 SVGCharTspanTextHandler.hpp SVGCharTspanTextHandler.cpp \
137 SVGElement.hpp SVGElement.cpp \
136138 SVGOutput.hpp SVGOutput.cpp \
137139 SVGSingleCharTextHandler.hpp SVGSingleCharTextHandler.cpp \
138140 SVGTree.hpp SVGTree.cpp \
519519 vector<double> params;
520520 if (parse_transform_cmd(iss, "matrix", 6, 6, params)) {
521521 if (ne(params[0], 1) || ne(params[1], 0) || ne(params[2], 0) || ne(params[3], 1) || ne(params[4], 0) || ne(params[5], 0))
522 matrix.rmultiply({params[0], params[2], params[4], params[1], params[3], params[5]});
522 matrix.rmultiply(Matrix{params[0], params[2], params[4], params[1], params[3], params[5]});
523523 }
524524 else if (parse_transform_cmd(iss, "rotate", 1, 3, params)) {
525525 if (params.size() == 1) {
3939 friend double det (const Matrix &m, int row, int col);
4040
4141 public:
42 Matrix () {set(0);}
43 Matrix (double d);
4244 Matrix (const std::string &cmds, Calculator &calc);
43 Matrix (double d=0);
4445 explicit Matrix (const double *v, unsigned size=9);
4546 explicit Matrix (const std::vector<double> &v, int start=0);
46 Matrix (std::initializer_list<double> initlist);
47 explicit Matrix (std::initializer_list<double> initlist);
4748 Matrix& set (double d);
4849 Matrix& set (const double *v, unsigned size);
4950 Matrix& set (const std::vector<double> &v, int start=0);
7373 _col++;
7474 }
7575 _nl = false;
76 if (!_nl || c != '\n')
76 if (c != '\n')
7777 os << c;
7878 }
7979 }
0 /*************************************************************************
1 ** Opacity.cpp **
2 ** **
3 ** This file is part of dvisvgm -- a fast DVI to SVG converter **
4 ** Copyright (C) 2005-2021 Martin Gieseking <martin.gieseking@uos.de> **
5 ** **
6 ** This program is free software; you can redistribute it and/or **
7 ** modify it under the terms of the GNU General Public License as **
8 ** published by the Free Software Foundation; either version 3 of **
9 ** the License, or (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, but **
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program; if not, see <http://www.gnu.org/licenses/>. **
18 *************************************************************************/
19
20 #include "Opacity.hpp"
21
22 using namespace std;
23
24 string Opacity::cssBlendMode (BlendMode bm) {
25 switch (bm) {
26 case BM_NORMAL : return "normal";
27 case BM_MULTIPLY : return "multiply";
28 case BM_SCREEN : return "screen";
29 case BM_OVERLAY : return "overlay";
30 case BM_SOFTLIGHT : return "soft-light";
31 case BM_HARDLIGHT : return "hard-light";
32 case BM_COLORDODGE: return "color-dodge";
33 case BM_COLORBURN : return "color-burn";
34 case BM_DARKEN : return "darken";
35 case BM_LIGHTEN : return "lighten";
36 case BM_DIFFERENCE: return "difference";
37 case BM_EXCLUSION : return "exclusion";
38 case BM_HUE : return "hue";
39 case BM_SATURATION: return "saturation";
40 case BM_COLOR : return "color";
41 case BM_LUMINOSITY: return "luminosity";
42 }
43 return "";
44 }
45
46
47 bool Opacity::operator == (const Opacity &opacity) const {
48 return opacity._fillalpha == _fillalpha
49 && opacity._strokealpha == _strokealpha
50 && opacity._blendMode == _blendMode;
51 }
52
53
54 bool Opacity::operator != (const Opacity &opacity) const {
55 return opacity._fillalpha != _fillalpha
56 || opacity._strokealpha != _strokealpha
57 || opacity._blendMode != _blendMode;
58 }
0 /*************************************************************************
1 ** Opacity.hpp **
2 ** **
3 ** This file is part of dvisvgm -- a fast DVI to SVG converter **
4 ** Copyright (C) 2005-2021 Martin Gieseking <martin.gieseking@uos.de> **
5 ** **
6 ** This program is free software; you can redistribute it and/or **
7 ** modify it under the terms of the GNU General Public License as **
8 ** published by the Free Software Foundation; either version 3 of **
9 ** the License, or (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, but **
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program; if not, see <http://www.gnu.org/licenses/>. **
18 *************************************************************************/
19
20 #ifndef OPACITY_HPP
21 #define OPACITY_HPP
22
23 #include <string>
24
25 class OpacityAlpha {
26 public:
27 OpacityAlpha () =default;
28 OpacityAlpha (double constalpha, double shapealpha) : _constalpha(constalpha), _shapealpha(shapealpha) {}
29 void setConstAlpha (double alpha) { _constalpha = alpha;}
30 void setShapeAlpha (double shapealpha) {_shapealpha = shapealpha;}
31 double value () const {return _constalpha * _shapealpha;}
32 bool operator == (const OpacityAlpha &alpha) const {return alpha._constalpha == _constalpha && alpha._shapealpha == _shapealpha;}
33 bool operator != (const OpacityAlpha &alpha) const {return alpha._constalpha != _constalpha || alpha._shapealpha != _shapealpha;}
34 bool isOpaque () const {return _constalpha == 1.0 && _shapealpha == 1.0;}
35
36 private:
37 double _constalpha=1.0;
38 double _shapealpha=1.0;
39 };
40
41 class Opacity {
42 public:
43 enum BlendMode {
44 BM_NORMAL, BM_MULTIPLY, BM_SCREEN, BM_OVERLAY,
45 BM_SOFTLIGHT, BM_HARDLIGHT, BM_COLORDODGE, BM_COLORBURN,
46 BM_DARKEN, BM_LIGHTEN, BM_DIFFERENCE, BM_EXCLUSION,
47 BM_HUE, BM_SATURATION, BM_COLOR, BM_LUMINOSITY
48 };
49
50 public:
51 Opacity () =default;
52 Opacity (OpacityAlpha fillalpha, OpacityAlpha strokealpha, BlendMode bm) : _fillalpha(fillalpha), _strokealpha(strokealpha), _blendMode(bm) {}
53 Opacity (OpacityAlpha fillalpha, OpacityAlpha strokealpha) : Opacity(fillalpha, strokealpha, BM_NORMAL) {}
54 explicit Opacity (BlendMode bm) : _blendMode(bm) {}
55 OpacityAlpha& fillalpha () {return _fillalpha;}
56 OpacityAlpha& strokealpha () {return _strokealpha;}
57 const OpacityAlpha& fillalpha () const {return _fillalpha;}
58 const OpacityAlpha& strokealpha () const {return _strokealpha;}
59 BlendMode blendMode () const {return _blendMode;}
60 void setBlendMode (BlendMode mode) {_blendMode = mode;}
61 std::string cssBlendMode () const {return cssBlendMode(_blendMode);}
62 static std::string cssBlendMode (BlendMode bm);
63 bool isFillDefault () const {return _fillalpha.isOpaque() && _blendMode == BM_NORMAL;}
64 bool isStrokeDefault () const {return _strokealpha.isOpaque() && _blendMode == BM_NORMAL;}
65 bool operator == (const Opacity &opacity) const;
66 bool operator != (const Opacity &opacity) const;
67
68 private:
69 OpacityAlpha _fillalpha;
70 OpacityAlpha _strokealpha;
71 BlendMode _blendMode=BM_NORMAL;
72 };
73
74 #endif
5858 // in conjunction with -dDELAYBIND and -dWRITESYSTEMDICT.
5959 // Thus, -dDELAYSAFER (or -dNOSAFER) must be added.
6060 // https://www.ghostscript.com/doc/9.50/Use.htm#Safer
61 if (gsrev >= 950)
61 if (gsrev >= 950) {
6262 gsargs.emplace_back("-dDELAYSAFER");
63 gsargs.emplace_back("-dALLOWPSTRANSPARENCY");
64 }
6365 }
6466 _gs.init(gsargs.size(), gsargs.data(), this);
6567 _gs.set_stdio(input, output, error);
129131 // feed Ghostscript with code chunks that are not larger than 64KB
130132 // => see documentation of gsapi_run_string_foo()
131133 const char *p=str;
132 while (PS_RUNNING && len > 0) {
134 while (_mode == PS_RUNNING && len > 0) {
133135 SignalHandler::instance().check();
134136 size_t chunksize = min(len, (size_t)0xffff);
135137 _gs.run_string_continue(p, chunksize, 0, &status);
278280 {"rotate", { 1, &PSActions::rotate}},
279281 {"save", { 1, &PSActions::save}},
280282 {"scale", { 2, &PSActions::scale}},
283 {"setalphaisshape", { 1, &PSActions::setalphaisshape}},
281284 {"setblendmode", { 1, &PSActions::setblendmode}},
282285 {"setcolorspace", { 1, &PSActions::setcolorspace}},
283286 {"setcmykcolor", { 4, &PSActions::setcmykcolor}},
285288 {"setfillconstantalpha", { 1, &PSActions::setfillconstantalpha}},
286289 {"setgray", { 1, &PSActions::setgray}},
287290 {"sethsbcolor", { 3, &PSActions::sethsbcolor}},
288 {"setisshapealpha", { 1, &PSActions::setisshapealpha}},
289291 {"setlinecap", { 1, &PSActions::setlinecap}},
290292 {"setlinejoin", { 1, &PSActions::setlinejoin}},
291293 {"setlinewidth", { 1, &PSActions::setlinewidth}},
308310 _rawData.clear();
309311 in.skipSpace();
310312 while (!in.eof()) {
311 _rawData.emplace_back(in.getString());
313 _rawData.push_back(in.getString());
312314 in.skipSpace();
313315 }
314316 }
319321 if (pcount < 0) { // variable number of parameters?
320322 in.skipSpace();
321323 while (!in.eof()) { // read all available parameters
322 params.emplace_back(in.getString());
324 params.push_back(in.getString());
323325 in.skipSpace();
324326 }
325327 }
326328 else { // fix number of parameters
327329 for (int i=0; i < pcount; i++) {
328330 in.skipSpace();
329 params.emplace_back(in.getString());
331 params.push_back(in.getString());
330332 }
331333 }
332334 // convert parameter strings to doubles
414416 return false;
415417 deviceStr = deviceStr.substr(0, deviceStr.find(':')); // strip optional argument
416418 auto infos = getImageDeviceInfos();
417 auto it = find_if(infos.begin(), infos.end(), [&](PSDeviceInfo &info) {
419 auto it = find_if(infos.begin(), infos.end(), [&](const PSDeviceInfo &info) {
418420 return info.name == deviceStr;
419421 });
420422 return it != infos.end();
6161 virtual void rotate (std::vector<double> &p) =0;
6262 virtual void save (std::vector<double> &p) =0;
6363 virtual void scale (std::vector<double> &p) =0;
64 virtual void setalphaisshape (std::vector<double> &p) =0;
6465 virtual void setblendmode (std::vector<double> &p) =0;
6566 virtual void setcolorspace (std::vector<double> &p) =0;
6667 virtual void setcmykcolor (std::vector<double> &cmyk) =0;
6869 virtual void setfillconstantalpha (std::vector<double> &p) =0;
6970 virtual void setgray (std::vector<double> &p) =0;
7071 virtual void sethsbcolor (std::vector<double> &hsb) =0;
71 virtual void setisshapealpha (std::vector<double> &p) =0;
7272 virtual void setlinecap (std::vector<double> &p) =0;
7373 virtual void setlinejoin (std::vector<double> &p) =0;
7474 virtual void setlinewidth (std::vector<double> &p) =0;
113113 BoundingBox pdfPageBox (const std::string &fname, int pageno);
114114 const std::vector<std::string>& rawData () const {return _rawData;}
115115 bool setImageDevice (const std::string &deviceStr);
116 bool hasFullOpacitySupport () const {return _gs.revision() >= 952;}
116117 static std::vector<PSDeviceInfo> getImageDeviceInfos ();
117118 static void listImageDeviceInfos (std::ostream &os);
118119 static bool imageDeviceKnown (std::string deviceStr);
105105 * define the pattern graphic. */
106106 unique_ptr<XMLElement> PSTilingPattern::createGroupNode () const {
107107 // add all succeeding path elements to this group
108 auto group = util::make_unique<XMLElement>("g");
109 group->addAttribute("clip-path", XMLString("url(#pc")+XMLString(psID())+")");
108 auto group = util::make_unique<SVGElement>("g");
109 group->setClipPathUrl("pc"+XMLString(psID()));
110110 return group;
111111 }
112112
3939 virtual int psID () const {return _id;}
4040 virtual std::string svgID () const;
4141 virtual void apply (SpecialActions &actions);
42 virtual void setColor (Color color) {}
43 virtual bool tiled () const =0;
4244
4345 protected:
4446 explicit PSPattern (int id) : _id(id) {}
5355 public:
5456 virtual XMLElement* getContainerNode () {return _groupNode.get();}
5557 void apply (SpecialActions &actions) override;
58 bool tiled () const override {return true;}
59
5660
5761 protected:
5862 PSTilingPattern (int id, BoundingBox &bbox, Matrix &matrix, double xstep, double ystep);
8185 public:
8286 PSUncoloredTilingPattern (int id, BoundingBox &bbox, Matrix &matrix, double xstep, double ystep);
8387 std::string svgID () const override;
84 void setColor (Color color) {_currentColor = color;}
88 void setColor (Color color) override {_currentColor = color;}
8589 void apply (SpecialActions &actions) override;
8690
8791 protected:
5757 if (!_pageSizes.empty() && _pageSizes.back().first == pageno)
5858 _pageSizes.back().second = whpair;
5959 else
60 _pageSizes.emplace_back(PageSize(pageno, whpair));
60 _pageSizes.emplace_back(pageno, whpair);
6161 }
6262 }
6363
9494 void PdfSpecialHandler::preprocessPagesize (StreamInputReader &ir, SpecialActions &actions) {
9595 // add page sizes to collection of paper sizes in order to handle them equally
9696 SpecialHandler *handler = SpecialManager::instance().findHandlerByName("papersize");
97 if (auto papersizeHandler = dynamic_cast<PapersizeSpecialHandler*>(handler)) {
97 if (auto papersizeHandler = static_cast<PapersizeSpecialHandler*>(handler)) {
9898 try {
9999 Length width, height;
100100 // parse parameter sequence of the form (name length)+
3131 #include "PSPreviewFilter.hpp"
3232 #include "PsSpecialHandler.hpp"
3333 #include "SpecialActions.hpp"
34 #include "SVGTree.hpp"
34 #include "SVGElement.hpp"
3535 #include "TensorProductPatch.hpp"
3636 #include "TriangularPatch.hpp"
3737 #include "utility.hpp"
8080 _linecap = _linejoin = 0; // butt end caps and miter joins
8181 _miterlimit = 4;
8282 _xmlnode = _savenode = nullptr;
83 _isshapealpha = false; // opacity operators change constant component by default
84 _fillalpha = _strokealpha = {1, 1}; // set constant and shape opacity to non-transparent
85 _blendmode = 0; // "normal" mode (no blending)
83 _isshapealpha = false; // opacity operators change constant component by default
84 _opacity = Opacity();
8685 _sx = _sy = _cos = 1.0;
8786 _pattern = nullptr;
87 _makingPattern = false;
8888 _patternEnabled = false;
8989 _currentcolor = Color::BLACK;
9090 _dashoffset = 0;
286286 return true;
287287 }
288288
289
290289 /** Handles a psfile/pdffile special which places an external EPS/PDF graphic
291290 * at the current DVI position. The lower left corner (llx,lly) of the
292291 * given bounding box is placed at the DVI position.
365364 matrix.scale(sx, sy).rotate(-angle).scale(hscale/100, vscale/100); // apply transformation attributes
366365 matrix.translate(x+hoffset, y-voffset); // move image to current DVI position
367366 matrix.lmultiply(_actions->getMatrix());
368
369367 // update bounding box
370368 BoundingBox bbox(0, 0, urx-llx, ury-lly);
371369 bbox.transform(matrix);
372370 _actions->embed(bbox);
373371
374372 // insert element containing the image data
375 matrix.rmultiply(TranslationMatrix(-llx, -lly)); // move lower left corner of image to origin
376 if (!matrix.isIdentity())
377 imgNode->addAttribute("transform", matrix.toSVG());
373 if (filetype != FileType::PDF)
374 matrix.rmultiply(TranslationMatrix(-llx, -lly)); // move lower left corner of image to origin
375 imgNode->setTransform(matrix);
378376 _actions->svgTree().appendToPage(std::move(imgNode));
379377 }
380378 // restore DVI position
385383
386384
387385 /** Returns path + basename of temporary bitmap images. */
388 static string image_base_path (SpecialActions &actions) {
386 static string image_base_path (const SpecialActions &actions) {
389387 FilePath imgpath = actions.getSVGFilePath(actions.getCurrentPageNumber());
390388 return FileSystem::tmpdir() + "/" + imgpath.basename() + "-tmp-";
391389 }
398396 * @param[in] bbox bounding box of the image
399397 * @param[in] clip if true, the image is clipped to its bounding box
400398 * @return pointer to the element or nullptr if there's no image data */
401 unique_ptr<XMLElement> PsSpecialHandler::createImageNode (FileType type, const string &fname, int pageno, BoundingBox bbox, bool clip) {
402 unique_ptr<XMLElement> node;
399 unique_ptr<SVGElement> PsSpecialHandler::createImageNode (FileType type, const string &fname, int pageno, BoundingBox bbox, bool clip) {
400 unique_ptr<SVGElement> node;
403401 string pathstr;
404402 if (const char *path = FileFinder::instance().lookup(fname, false))
405403 pathstr = FileSystem::ensureForwardSlashes(path);
408406 if (pathstr.empty())
409407 Message::wstream(true) << "file '" << fname << "' not found\n";
410408 else if (type == FileType::BITMAP || type == FileType::SVG) {
411 node = util::make_unique<XMLElement>("image");
409 node = util::make_unique<SVGElement>("image");
412410 node->addAttribute("x", 0);
413411 node->addAttribute("y", 0);
414412 node->addAttribute("width", bbox.width());
428426 if (clip)
429427 rectclip = to_string(bbox.minX())+" "+to_string(bbox.minY())+" "+to_string(bbox.width())+" "+to_string(bbox.height())+" rectclip";
430428
431 node = util::make_unique<XMLElement>("g"); // put SVG nodes created from the EPS/PDF file in this group
429 node = util::make_unique<SVGElement>("g"); // put SVG nodes created from the EPS/PDF file in this group
432430 _xmlnode = node.get();
433431 _psi.execute(
434432 "\n@beginspecial @setspecial" // enter special environment
555553 _linewidth = 1;
556554 _linecap = _linejoin = 0; // butt end caps and miter joins
557555 _miterlimit = 4;
558 _isshapealpha = false; // opacity operators change constant component by default
559 _fillalpha = _strokealpha = {1, 1}; // set constant and shape opacity to non-transparent
560 _blendmode = 0; // "normal" mode (no blending)
556 _isshapealpha = false; // opacity operators change constant component by default
557 _opacity = Opacity();
561558 _sx = _sy = _cos = 1.0;
562559 _pattern = nullptr;
563560 _currentcolor = Color::BLACK;
612609 }
613610
614611
615 static string css_blendmode_name (int mode) {
616 static const array<const char*,16> modenames = {{
617 "normal", "multiply", "screen", "overlay", "soft-light", "hard-light", "color-dodge", "color-burn",
618 "darken", "lighten", "difference", "exclusion", "hue", "saturation", "color", "luminosity"
619 }};
612 void PsSpecialHandler::setblendmode (vector<double> &p) {
613 int mode = static_cast<int>(p[0]);
614 static const Opacity::BlendMode blendmodes[] = {
615 Opacity::BM_NORMAL, Opacity::BM_MULTIPLY, Opacity::BM_SCREEN, Opacity::BM_OVERLAY,
616 Opacity::BM_SOFTLIGHT, Opacity::BM_HARDLIGHT, Opacity::BM_COLORDODGE, Opacity::BM_COLORBURN,
617 Opacity::BM_DARKEN, Opacity::BM_LIGHTEN, Opacity::BM_DIFFERENCE, Opacity::BM_EXCLUSION,
618 Opacity::BM_HUE, Opacity::BM_SATURATION, Opacity::BM_COLOR, Opacity::BM_LUMINOSITY
619 };
620620 if (mode < 0 || mode > 15)
621 return "";
622 return modenames[mode];
621 mode = Opacity::BM_NORMAL;
622 _opacity.setBlendMode(blendmodes[mode]);
623 }
624
625
626 void PsSpecialHandler::setfillconstantalpha (vector<double> &p) {
627 if (_isshapealpha)
628 _opacity.fillalpha().setShapeAlpha(p[0]);
629 else
630 _opacity.fillalpha().setConstAlpha(p[0]);
631 if (_actions)
632 _actions->setOpacity(_opacity);
633 }
634
635
636 void PsSpecialHandler::setstrokeconstantalpha (vector<double> &p) {
637 if (_isshapealpha)
638 _opacity.strokealpha().setShapeAlpha(p[0]);
639 else
640 _opacity.strokealpha().setConstAlpha(p[0]);
641 if (_actions)
642 _actions->setOpacity(_opacity);
623643 }
624644
625645
638658 }
639659 if (_clipStack.prependedPath())
640660 _path.prepend(*_clipStack.prependedPath());
641 unique_ptr<XMLElement> path;
661 unique_ptr<SVGElement> path;
642662 Pair<double> point;
643663 if (_path.isDot(point)) { // zero-length path?
644664 if (_linecap == 1) { // round line ends? => draw dot
645665 double x = point.x();
646666 double y = point.y();
647667 double r = _linewidth/2.0;
648 path = util::make_unique<XMLElement>("circle");
668 path = util::make_unique<SVGElement>("circle");
649669 path->addAttribute("cx", x);
650670 path->addAttribute("cy", y);
651671 path->addAttribute("r", r);
652 path->addAttribute("fill", _actions->getColor().svgColorString());
672 path->setFillColor(_actions->getColor());
653673 bbox = BoundingBox(x-r, y-r, x+r, y+r);
654674 }
655675 }
660680
661681 ostringstream oss;
662682 _path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
663 path = util::make_unique<XMLElement>("path");
683 path = util::make_unique<SVGElement>("path");
664684 path->addAttribute("d", oss.str());
665 path->addAttribute("stroke", _actions->getColor().svgColorString());
666 path->addAttribute("fill", "none");
667 if (_linewidth != 1)
668 path->addAttribute("stroke-width", _linewidth);
669 if (_miterlimit != 4)
670 path->addAttribute("stroke-miterlimit", _miterlimit);
671 if (_linecap > 0) // default value is "butt", no need to set it explicitly
672 path->addAttribute("stroke-linecap", _linecap == 1 ? "round" : "square");
673 if (_linejoin > 0) // default value is "miter", no need to set it explicitly
674 path->addAttribute("stroke-linejoin", _linecap == 1 ? "round" : "bevel");
675 if (_strokealpha[0] < 1 || _strokealpha[1] < 1)
676 path->addAttribute("stroke-opacity", _strokealpha[0] * _strokealpha[1]);
677 if (_blendmode > 0 && _blendmode < 16)
678 path->addAttribute("style", "mix-blend-mode:"+css_blendmode_name(_blendmode));
679 if (!_dashpattern.empty()) {
680 string patternStr;
681 for (double dashValue : _dashpattern)
682 patternStr += XMLString(dashValue)+",";
683 patternStr.pop_back();
684 path->addAttribute("stroke-dasharray", patternStr);
685 if (_dashoffset != 0)
686 path->addAttribute("stroke-dashoffset", _dashoffset);
687 }
688 }
689 if (path && _clipStack.path() && !_savenode) {
685 path->setStrokeColor(_actions->getColor());
686 path->setNoFillColor();
687 path->setStrokeWidth(_linewidth);
688 path->setStrokeMiterLimit(_miterlimit);
689 path->setStrokeLineCap(_linecap == 0 ? SVGElement::LC_BUTT : _linecap == 1 ? SVGElement::LC_ROUND : SVGElement::LC_SQUARE);
690 path->setStrokeLineJoin(_linejoin == 0 ? SVGElement::LJ_MITER : _linecap == 1 ? SVGElement::LJ_ROUND : SVGElement::LJ_BEVEL);
691 path->setStrokeOpacity(_opacity);
692 path->setStrokeDash(_dashpattern, _dashoffset);
693 }
694 if (path && _clipStack.path() && !_makingPattern) {
690695 // assign clipping path and clip bounding box
691 path->addAttribute("clip-path", XMLString("url(#clip")+XMLString(_clipStack.topID())+")");
696 path->setClipPathUrl("clip"+XMLString(_clipStack.topID()));
692697 bbox.intersect(_clipStack.path()->computeBBox());
693698 _clipStack.removePrependedPath();
694699 }
707712 * @param[in] evenodd true: use even-odd fill algorithm, false: use nonzero fill algorithm */
708713 void PsSpecialHandler::fill (vector<double> &p, bool evenodd) {
709714 _path.removeRedundantCommands();
710 if ((_path.empty() && !_clipStack.prependedPath()) || !_actions)
715 if ((_path.empty() && !_clipStack.prependedPath()) || (_patternEnabled && !_pattern) || !_actions)
711716 return;
712717
713718 // compute bounding box
722727
723728 ostringstream oss;
724729 _path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
725 unique_ptr<XMLElement> path = util::make_unique<XMLElement>("path");
730 auto path = util::make_unique<SVGElement>("path");
726731 path->addAttribute("d", oss.str());
727732 if (_pattern)
728 path->addAttribute("fill", XMLString("url(#")+_pattern->svgID()+")");
729 else if (_actions->getColor() != Color::BLACK || _savenode)
730 path->addAttribute("fill", _actions->getColor().svgColorString());
731 if (_clipStack.path() && !_savenode) { // clip path active and not inside pattern definition?
733 path->setFillPatternUrl(XMLString(_pattern->svgID()));
734 else if (_actions->getColor() != Color::BLACK || _makingPattern)
735 path->setFillColor(_actions->getColor(), false);
736 if (_clipStack.path() && !_makingPattern) { // clip path active and not inside pattern definition?
732737 // assign clipping path and clip bounding box
733 path->addAttribute("clip-path", XMLString("url(#clip")+XMLString(_clipStack.topID())+")");
738 path->setClipPathUrl("clip"+XMLString(_clipStack.topID()));
734739 bbox.intersect(_clipStack.path()->computeBBox());
735740 _clipStack.removePrependedPath();
736741 }
737 if (evenodd) // SVG default fill rule is "nonzero" algorithm
738 path->addAttribute("fill-rule", "evenodd");
739 if (_fillalpha[0] < 1 || _fillalpha[1] < 1)
740 path->addAttribute("fill-opacity", _fillalpha[0] * _fillalpha[1]);
741 if (_blendmode > 0 && _blendmode < 16)
742 path->addAttribute("style", "mix-blend-mode:"+css_blendmode_name(_blendmode));
742 path->setFillRule(evenodd ? SVGElement::FR_EVENODD : SVGElement::FR_NONZERO);
743 path->setFillOpacity(_opacity);
743744 if (_xmlnode)
744745 _xmlnode->append(std::move(path));
745746 else {
787788
788789 // if set, assign clipping path to image
789790 if (_clipStack.path()) {
790 auto group = util::make_unique<XMLElement>("g");
791 group->addAttribute("clip-path", XMLString("url(#clip")+XMLString(_clipStack.topID())+")");
791 auto group = util::make_unique<SVGElement>("g");
792 group->setClipPathUrl("clip"+XMLString(_clipStack.topID()));
792793 group->append(std::move(image));
793794 image = std::move(group); // handle the entire group as image to add
794795 }
845846 switch (pattern_type) {
846847 case 0:
847848 // pattern definition completed
848 if (_savenode) {
849 _xmlnode = _savenode;
850 _savenode = nullptr;
851 }
849 _xmlnode = _savenode;
850 _savenode = nullptr;
851 _makingPattern = false;
852852 break;
853853 case 1: { // tiling pattern
854854 int id = static_cast<int>(p[1]);
868868 _savenode = _xmlnode;
869869 _xmlnode = pattern->getContainerNode(); // insert the following SVG elements into this node
870870 _patterns[id] = std::move(pattern);
871 _makingPattern = true;
871872 break;
872873 }
873874 case 2: {
890891 if (it == _patterns.end())
891892 _pattern = nullptr;
892893 else {
893 if (auto pattern = dynamic_cast<PSUncoloredTilingPattern*>(it->second.get()))
894 pattern->setColor(color);
894 it->second->setColor(color);
895895 it->second->apply(*_actions);
896 if (auto pattern = dynamic_cast<PSTilingPattern*>(it->second.get()))
897 _pattern = pattern;
898 else
899 _pattern = nullptr;
896 _pattern = it->second->tiled() ? static_cast<PSTilingPattern*>(it->second.get()) : nullptr;
900897 }
901898 }
902899
960957 intersectedPath.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
961958 }
962959 if (pathReplaced) {
963 auto pathElem = util::make_unique<XMLElement>("path");
960 auto pathElem = util::make_unique<SVGElement>("path");
964961 pathElem->addAttribute("d", oss.str());
965 if (evenodd)
966 pathElem->addAttribute("clip-rule", "evenodd");
962 pathElem->setClipRule(evenodd ? SVGElement::FR_EVENODD : SVGElement::FR_NONZERO);
967963
968964 int newID = _clipStack.topID();
969 auto clipElem = util::make_unique<XMLElement>("clipPath");
965 auto clipElem = util::make_unique<SVGElement>("clipPath");
970966 clipElem->addAttribute("id", XMLString("clip")+XMLString(newID));
971967 if (!COMPUTE_CLIPPATHS_INTERSECTIONS && oldID)
972 clipElem->addAttribute("clip-path", XMLString("url(#clip")+XMLString(oldID)+")");
968 clipElem->setClipPathUrl("clip"+XMLString(oldID));
973969
974970 clipElem->append(std::move(pathElem));
975971 _actions->svgTree().appendToDefs(std::move(clipElem));
10451041 * @param[in,out] it iterator used to sequentially access the patch data
10461042 * @param[out] points the points defining the geometry of the patch
10471043 * @param[out] colors the colors assigned to the vertices of the patch */
1048 static void read_patch_data (ShadingPatch &patch, int edgeflag,
1044 static void read_patch_data (const ShadingPatch &patch, int edgeflag,
10491045 VectorIterator<double> &it, vector<DPair> &points, vector<Color> &colors)
10501046 {
10511047 // number of control points and colors required to define a single patch
10861082 ShadingCallback (SpecialActions &actions, XMLElement *parent, int clippathID)
10871083 : _actions(actions)
10881084 {
1089 auto group = util::make_unique<XMLElement>("g");
1085 auto group = util::make_unique<SVGElement>("g");
10901086 _group = group.get();
10911087 if (parent)
10921088 parent->append(std::move(group));
10931089 else
10941090 actions.svgTree().appendToPage(std::move(group));
10951091 if (clippathID > 0)
1096 _group->addAttribute("clip-path", XMLString("url(#clip")+XMLString(clippathID)+")");
1092 _group->setClipPathUrl("clip"+XMLString(clippathID));
10971093 }
10981094
10991095 void patchSegment (GraphicsPath<double> &path, const Color &color) override {
11031099 // draw a single patch segment
11041100 ostringstream oss;
11051101 path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
1106 auto pathElem = util::make_unique<XMLElement>("path");
1102 auto pathElem = util::make_unique<SVGElement>("path");
11071103 pathElem->addAttribute("d", oss.str());
1108 pathElem->addAttribute("fill", color.svgColorString());
1104 pathElem->setFillColor(color);
11091105 _group->append(std::move(pathElem));
11101106 }
11111107
11121108 private:
11131109 SpecialActions &_actions;
1114 XMLElement *_group;
1110 SVGElement *_group;
11151111 };
11161112
11171113
2727 #include <vector>
2828 #include "GraphicsPath.hpp"
2929 #include "PSInterpreter.hpp"
30 #include "SpecialHandler.hpp"
30 #include "Opacity.hpp"
3131 #include "PSPattern.hpp"
3232 #include "PSPreviewFilter.hpp"
33
33 #include "SpecialHandler.hpp"
3434
3535 class PSPattern;
36 class SVGElement;
3637 class XMLElement;
3738
3839 class PsSpecialHandler : public SpecialHandler, protected PSActions {
103104 void executeAndSync (std::istream &is, bool updatePos);
104105 void processHeaderFile (const char *fname);
105106 void imgfile (FileType type, const std::string &fname, const std::map<std::string,std::string> &attr);
106 std::unique_ptr<XMLElement> createImageNode (FileType type, const std::string &fname, int pageno, BoundingBox bbox, bool clip);
107 std::unique_ptr<SVGElement> createImageNode (FileType type, const std::string &fname, int pageno, BoundingBox bbox, bool clip);
107108 void dviBeginPage (unsigned int pageno, SpecialActions &actions) override;
108109 void dviEndPage (unsigned pageno, SpecialActions &actions) override;
109110 void clip (Path path, bool evenodd);
137138 void rotate (std::vector<double> &p) override;
138139 void save (std::vector<double> &p) override;
139140 void scale (std::vector<double> &p) override;
140 void setblendmode (std::vector<double> &p) override {_blendmode = int(p[0]);}
141 void setalphaisshape (std::vector<double> &p) override { _isshapealpha = bool(p[0]);}
142 void setblendmode (std::vector<double> &p) override;
141143 void setcolorspace (std::vector<double> &p) override {_patternEnabled = bool(p[0]);}
142144 void setcmykcolor (std::vector<double> &cmyk) override;
143145 void setdash (std::vector<double> &p) override;
144 void setfillconstantalpha (std::vector<double> &p) override {_fillalpha[_isshapealpha ? 1 : 0] = p[0];}
146 void setfillconstantalpha (std::vector<double> &p) override;
145147 void setgray (std::vector<double> &p) override;
146148 void sethsbcolor (std::vector<double> &hsb) override;
147 void setisshapealpha (std::vector<double> &p) override {_isshapealpha = bool(p[0]);}
148149 void setlinecap (std::vector<double> &p) override {_linecap = uint8_t(p[0]);}
149150 void setlinejoin (std::vector<double> &p) override {_linejoin = uint8_t(p[0]);}
150151 void setlinewidth (std::vector<double> &p) override {_linewidth = scale(p[0] ? p[0] : 0.5);}
154155 void setpagedevice (std::vector<double> &p) override;
155156 void setpattern (std::vector<double> &p) override;
156157 void setrgbcolor (std::vector<double> &rgb) override;
157 void setstrokeconstantalpha (std::vector<double> &p) override {_strokealpha[_isshapealpha ? 1 : 0] = p[0];}
158 void setstrokeconstantalpha (std::vector<double> &p) override;
158159 void shfill (std::vector<double> &p) override;
159160 void stroke (std::vector<double> &p) override;
160161 void translate (std::vector<double> &p) override;
175176 double _cos; ///< cosine of angle between (1,0) and transform(1,0)
176177 double _linewidth; ///< current line width in bp units
177178 double _miterlimit; ///< current miter limit in bp units
178 bool _isshapealpha; ///< if true, opacity operators act on index 1 (shape component), otherwise on index 0 (constant component)
179 std::array<double,2> _fillalpha; ///< constant and shape opacity used for fill operations (0=fully transparent, ..., 1=opaque)
180 std::array<double,2> _strokealpha; ///< constant and shape opacity used for stroke operations (0=fully transparent, ..., 1=opaque)
181 int _blendmode; ///< blend mode used when overlaying colored areas
179 bool _isshapealpha; ///< if true, opacity operators act on shape component, otherwise on constant component
180 Opacity _opacity;
182181 uint8_t _linecap : 2; ///< current line cap (0=butt, 1=round, 2=projecting square)
183182 uint8_t _linejoin : 2; ///< current line join (0=miter, 1=round, 2=bevel)
184183 double _dashoffset; ///< current dash offset
185184 std::vector<double> _dashpattern;
186185 ClippingStack _clipStack;
186 bool _makingPattern=false; ///< true if executing makepattern operator
187187 std::map<int, std::unique_ptr<PSPattern>> _patterns;
188188 PSTilingPattern *_pattern; ///< current pattern
189189 bool _patternEnabled; ///< true if active color space is a pattern
8181
8282 Range range(cmin, cmax, vmin);
8383 if (_ranges.empty())
84 _ranges.emplace_back(std::move(range));
84 _ranges.push_back(std::move(range));
8585 else {
8686 // check for simple cases that can be handled pretty fast
8787 Range &lrange = *_ranges.begin();
8888 Range &rrange = *_ranges.rbegin();
8989 if (cmin > rrange.max()) { // non-overlapping range at end of vector?
9090 if (!rrange.join(range))
91 _ranges.emplace_back(std::move(range));
91 _ranges.push_back(std::move(range));
9292 }
9393 else if (cmax < lrange.min()) { // non-overlapping range at begin of vector?
9494 if (!lrange.join(range))
1919
2020 #include "SVGCharHandler.hpp"
2121 #include "utility.hpp"
22 #include "XMLNode.hpp"
22 #include "SVGElement.hpp"
2323
2424 using namespace std;
2525
2626
27 void SVGCharHandler::setInitialContextNode (XMLElement *node) {
27 void SVGCharHandler::setInitialContextNode (SVGElement *node) {
2828 resetContextNode();
2929 _initialContextNode = node;
3030 }
3333 /** Changes the context element. All following nodes will be appended to this node.
3434 * @param[in] node the new context node
3535 * @return bare pointer to the new context node or 0 if context hasn't changed */
36 XMLElement* SVGCharHandler::pushContextNode (unique_ptr<XMLElement> node) {
36 SVGElement* SVGCharHandler::pushContextNode (unique_ptr<SVGElement> node) {
3737 if (node && (_contextNodeStack.empty() || node.get() != _contextNodeStack.top())) {
38 XMLElement *nodeptr = node.get();
38 SVGElement *nodeptr = node.get();
3939 contextNode()->append(std::move(node));
4040 _contextNodeStack.push(nodeptr);
4141 return nodeptr;
6060 /** Creates and returns a new SVG text element.
6161 * @param[in] x current x coordinate
6262 * @param[in] y current y coordinate */
63 unique_ptr<XMLElement> SVGCharTextHandler::createTextNode (double x, double y) const {
63 unique_ptr<SVGElement> SVGCharTextHandler::createTextNode (double x, double y) const {
6464 const Font *font = _font.get();
6565 if (!font)
6666 return nullptr;
67 auto textNode = util::make_unique<XMLElement>("text");
67 auto textNode = util::make_unique<SVGElement>("text");
6868 if (_selectFontByClass)
6969 textNode->addAttribute("class", string("f")+XMLString(_fontnum));
7070 else {
7171 textNode->addAttribute("font-family", font->name());
72 textNode->addAttribute("font-size", XMLString(font->scaledSize()));
73 if (font->color() != Color::BLACK)
74 textNode->addAttribute("fill", font->color().svgColorString());
72 textNode->addAttribute("font-size", font->scaledSize());
73 textNode->setFillColor(font->color());
7574 }
7675 if (_vertical) {
7776 textNode->addAttribute("writing-mode", "tb");
7877 // align glyphs designed for horizontal layout properly
79 if (auto pf = dynamic_cast<const PhysicalFont*>(font)) {
78 if (auto pf = font_cast<const PhysicalFont*>(font)) {
8079 if (!pf->getMetrics()->verticalLayout()) { // alphabetic text designed for horizontal layout?
8180 x += pf->scaledAscent()/2.5; // move vertical baseline to the right by strikethrough offset
8281 textNode->addAttribute("glyph-orientation-vertical", 90); // ensure rotation
8584 }
8685 textNode->addAttribute("x", x);
8786 textNode->addAttribute("y", y);
88 if (!_matrix.get().isIdentity())
89 textNode->addAttribute("transform", _matrix.get().toSVG());
87 if (!_matrix->isIdentity())
88 textNode->addAttribute("transform", _matrix->toSVG());
9089 return textNode;
9190 }
2525 #include "Color.hpp"
2626 #include "Font.hpp"
2727 #include "Matrix.hpp"
28 #include "Opacity.hpp"
2829
2930
3031 template <typename T>
3132 class CharProperty {
3233 public:
34 CharProperty () =default;
3335 CharProperty (const T &v) : _value(v) {}
36 CharProperty (T &&v) : _value(std::move(v)) {}
3437
3538 void set (const T &v) {
3639 if (v != _value) {
3942 }
4043 }
4144
42 const T& get () const {return _value;}
43 operator const T& () const {return _value;}
44 bool changed () const {return _changed;}
45 void changed (bool c) {_changed = c;}
45 const T& get () const {return _value;}
46 const T* operator -> () const {return &_value;}
47 operator const T& () const {return _value;}
48 bool changed () const {return _changed;}
49 void changed (bool c) {_changed = c;}
4650
4751 private:
4852 T _value;
5054 };
5155
5256
53 class XMLElement;
57 class SVGElement;
5458
5559
5660 /** Base class for all character handlers. These handlers create SVG representations
5963 public:
6064 SVGCharHandler () : _matrix(1) {}
6165 virtual ~SVGCharHandler() =default;
62 virtual void setInitialContextNode (XMLElement *node);
66 virtual void setInitialContextNode (SVGElement *node);
6367 virtual void appendChar (uint32_t c, double x, double y) =0;
6468 virtual void notifyXAdjusted () {}
6569 virtual void notifyYAdjusted () {}
66 void setColor (const Color &color) {_color.set(color);}
67 void setFont (const Font &font, int id) {_font.set(&font); _fontnum = id;}
68 void setMatrix (const Matrix &matrix) {_matrix.set(matrix);}
69 void setVertical (bool vertical) {_vertical.set(vertical);}
70 Color getColor () const {return _color.get();}
71 const Font* getFont () const {return _font.get();}
72 const Matrix& getMatrix () const {return _matrix.get();}
70 void setColor (const Color &color) {_color.set(color);}
71 void setOpacity (const Opacity &opacity) {_opacity.set(opacity);}
72 void setFont (const Font &font, int id) {_font.set(&font); _fontnum = id;}
73 void setMatrix (const Matrix &matrix) {_matrix.set(matrix);}
74 void setVertical (bool vertical) {_vertical.set(vertical);}
75 Color getColor () const {return _color.get();}
76 const Opacity& getOpacity () const {return _opacity.get();}
77 const Font* getFont () const {return _font.get();}
78 const Matrix& getMatrix () const {return _matrix.get();}
7379
7480 protected:
7581 virtual void resetContextNode ();
76 XMLElement* pushContextNode (std::unique_ptr<XMLElement> node);
82 SVGElement* pushContextNode (std::unique_ptr<SVGElement> node);
7783 void popContextNode ();
7884
79 XMLElement* contextNode () const {
85 SVGElement* contextNode () const {
8086 return _contextNodeStack.empty() ? _initialContextNode : _contextNodeStack.top();
8187 }
8288
8389 CharProperty<Color> _color=Color::BLACK; ///< current color
90 CharProperty<Opacity> _opacity; ///< current opacity values
8491 CharProperty<const Font*> _font=0; ///< current font
8592 int _fontnum=0; ///< current font ID
8693 CharProperty<Matrix> _matrix; ///< current transformation
8794 CharProperty<bool> _vertical=false; ///< current writing mode
8895
8996 private:
90 XMLElement *_initialContextNode= nullptr; ///< SVG element the generated character nodes are attached to
91 std::stack<XMLElement*> _contextNodeStack;
97 SVGElement *_initialContextNode= nullptr; ///< SVG element the generated character nodes are attached to
98 std::stack<SVGElement*> _contextNodeStack;
9299 };
93100
94101
98105 explicit SVGCharTextHandler (bool selectFontByClass) : _selectFontByClass(selectFontByClass) {}
99106
100107 protected:
101 std::unique_ptr<XMLElement> createTextNode (double x, double y) const;
108 std::unique_ptr<SVGElement> createTextNode (double x, double y) const;
102109
103110 private:
104111 bool _selectFontByClass;
2222 #include "FontManager.hpp"
2323 #include "SVGCharPathHandler.hpp"
2424 #include "utility.hpp"
25 #include "XMLNode.hpp"
25 #include "SVGElement.hpp"
2626
2727 using namespace std;
2828
6060 // Glyphs of non-black fonts (e.g. defined in a XeTeX document) can't change their color.
6161 CharProperty<Color> &color = (_fontColor.get() != Color::BLACK) ? _fontColor : _color;
6262 bool applyColor = color.get() != Color::BLACK;
63 bool applyMatrix = !_matrix.get().isIdentity();
63 bool applyMatrix = !_matrix->isIdentity();
64 bool applyOpacity = !_opacity->isFillDefault();
6465 if (!_groupNode) {
6566 color.changed(applyColor);
6667 _matrix.changed(applyMatrix);
6768 }
68 if (color.changed() || _matrix.changed()) {
69 if (color.changed() || _matrix.changed() || _opacity.changed()) {
6970 resetContextNode();
70 if (applyColor || applyMatrix) {
71 _groupNode = pushContextNode(util::make_unique<XMLElement>("g"));
72 if (applyColor)
73 contextNode()->addAttribute("fill", color.get().svgColorString());
74 if (applyMatrix)
75 contextNode()->addAttribute("transform", _matrix.get().toSVG());
71 if (applyColor || applyMatrix || applyOpacity) {
72 _groupNode = pushContextNode(util::make_unique<SVGElement>("g"));
73 contextNode()->setFillColor(color);
74 contextNode()->setFillOpacity(_opacity->fillalpha());
75 contextNode()->setTransform(_matrix);
7676 }
7777 color.changed(false);
7878 _matrix.changed(false);
79 _opacity.changed(false);
7980 }
8081 const Font *font = _font.get();
8182 if (font->verticalLayout()) {
8384 GlyphMetrics metrics;
8485 font->getGlyphMetrics(c, _vertical, metrics);
8586 x -= metrics.wl;
86 if (auto pf = dynamic_cast<const PhysicalFont*>(font)) {
87 if (auto pf = font_cast<const PhysicalFont*>(font)) {
8788 // Center glyph between top and bottom border of the TFM box.
8889 // This is just an approximation used until I find a way to compute
8990 // the exact location in vertical mode.
9091 GlyphMetrics exact_metrics;
91 pf->getExactGlyphBox(c, exact_metrics, false);
92 pf->getExactGlyphBox(c, exact_metrics, false, nullptr);
9293 y += exact_metrics.h+(metrics.d-exact_metrics.h-exact_metrics.d)/2;
9394 }
9495 else
108109
109110 void SVGCharPathHandler::appendUseElement (uint32_t c, double x, double y, const Matrix &matrix) {
110111 string id = "#g" + to_string(FontManager::instance().fontID(_font)) + "-" + to_string(c);
111 auto useNode = util::make_unique<XMLElement>("use");
112 useNode->addAttribute("x", XMLString(x));
113 useNode->addAttribute("y", XMLString(y));
112 auto useNode = util::make_unique<SVGElement>("use");
113 useNode->addAttribute("x", x);
114 useNode->addAttribute("y", y);
114115 useNode->addAttribute("xlink:href", id);
115 if (!matrix.isIdentity())
116 useNode->addAttribute("transform", matrix.toSVG());
116 useNode->setFillOpacity(_opacity->blendMode()); // add blend mode style here because it's not inheritable
117 useNode->setTransform(matrix);
117118 contextNode()->append(std::move(useNode));
118119 }
119120
120121
121122 void SVGCharPathHandler::appendPathElement (uint32_t c, double x, double y, const Matrix &matrix) {
122123 Glyph glyph;
123 auto pf = dynamic_cast<const PhysicalFont*>(_font.get());
124 if (pf && pf->getGlyph(c, glyph)) {
124 auto pf = font_cast<const PhysicalFont*>(_font.get());
125 if (pf && pf->getGlyph(c, glyph, nullptr)) {
125126 double sx = pf->scaledSize()/pf->unitsPerEm();
126127 double sy = -sx;
127128 ostringstream oss;
128129 glyph.writeSVG(oss, _relativePathCommands, sx, sy, x, y);
129 auto glyphNode = util::make_unique<XMLElement>("path");
130 auto glyphNode = util::make_unique<SVGElement>("path");
130131 glyphNode->addAttribute("d", oss.str());
131 if (!matrix.isIdentity())
132 glyphNode->addAttribute("transform", matrix.toSVG());
132 glyphNode->setTransform(matrix);
133133 contextNode()->append(std::move(glyphNode));
134134 }
135135 }
3838 private:
3939 AppendMethod _appendChar; ///< method called to append a single character
4040 bool _relativePathCommands; ///< if true, create relative rather than absolute SVG path commands
41 XMLElement *_groupNode=nullptr; ///< current group node taking the path elements
41 SVGElement *_groupNode=nullptr; ///< current group node taking the path elements
4242 CharProperty<Color> _fontColor=Color::BLACK; ///< color of current font
4343 };
4444
1919
2020 #include "SVGCharTspanTextHandler.hpp"
2121 #include "utility.hpp"
22 #include "XMLNode.hpp"
22 #include "SVGElement.hpp"
2323
2424 using namespace std;
2525
3535 if (!_textNode || _font.changed() || _matrix.changed() || _vertical.changed()) {
3636 resetContextNode();
3737 _textNode = pushContextNode(createTextNode(x, y));
38 _color.changed(true); // force creating tspan with color attribute if current color differs from font color
38 _color.changed(true); // force creating tspan with color attribute if current color differs from font color
39 _opacity.changed(true); // dito for opacity properties
3940 }
40 if (_tspanNode && (_xchanged || _ychanged || _color.changed())) {
41 if (_tspanNode && (_xchanged || _ychanged || _color.changed() || _opacity.changed())) {
4142 // if drawing position or color was explicitly changed, finish current tspan element
4243 popContextNode();
4344 _tspanNode = nullptr;
4546 // Apply text color changes only if the color of the entire font is black.
4647 // Glyphs of non-black fonts (e.g. defined in a XeTeX document) can't change their color.
4748 bool applyColor = _color.get() != Color::BLACK && _font.get()->color() == Color::BLACK;
48 if (_xchanged || _ychanged || (_color.changed() && applyColor)) {
49 _tspanNode = pushContextNode(util::make_unique<XMLElement>("tspan"));
49 bool applyOpacity = !_opacity->isFillDefault();
50 if (_xchanged || _ychanged || (_color.changed() && applyColor) || (_opacity.changed() && applyOpacity)) {
51 _tspanNode = pushContextNode(util::make_unique<SVGElement>("tspan"));
5052 if (applyColor)
51 _tspanNode->addAttribute("fill", _color.get().svgColorString());
53 _tspanNode->setFillColor(_color);
5254 _color.changed(false);
55 _tspanNode->setFillOpacity(_opacity);
56 _opacity.changed(false);
5357 if (_xchanged) {
5458 if (_vertical) {
5559 // align glyphs designed for horizontal layout properly
56 if (auto pf = dynamic_cast<const PhysicalFont*>(_font.get()))
60 if (auto pf = font_cast<const PhysicalFont*>(_font.get()))
5761 if (!pf->getMetrics()->verticalLayout())
5862 x += pf->scaledAscent()/2.5; // move vertical baseline to the right by strikethrough offset
5963 }
6973 }
7074
7175
72 void SVGCharTspanTextHandler::setInitialContextNode (XMLElement *node) {
76 void SVGCharTspanTextHandler::setInitialContextNode (SVGElement *node) {
7377 SVGCharHandler::setInitialContextNode(node);
7478 _textNode = _tspanNode = nullptr;
7579 _xchanged = _ychanged = false;
2828 void notifyXAdjusted () override {_xchanged = true;}
2929 void notifyYAdjusted() override {_ychanged = true;}
3030 void appendChar (uint32_t c, double x, double y) override;
31 void setInitialContextNode (XMLElement *node) override;
31 void setInitialContextNode (SVGElement *node) override;
3232
3333 protected:
3434 void resetContextNode () override;
3535
3636 private:
3737 bool _xchanged, _ychanged;
38 XMLElement *_textNode;
39 XMLElement *_tspanNode;
38 SVGElement *_textNode;
39 SVGElement *_tspanNode;
4040 };
4141
4242 #endif
0 /*************************************************************************
1 ** SVGElement.cpp **
2 ** **
3 ** This file is part of dvisvgm -- a fast DVI to SVG converter **
4 ** Copyright (C) 2005-2021 Martin Gieseking <martin.gieseking@uos.de> **
5 ** **
6 ** This program is free software; you can redistribute it and/or **
7 ** modify it under the terms of the GNU General Public License as **
8 ** published by the Free Software Foundation; either version 3 of **
9 ** the License, or (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, but **
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program; if not, see <http://www.gnu.org/licenses/>. **
18 *************************************************************************/
19
20 #include <sstream>
21 #include "Color.hpp"
22 #include "Matrix.hpp"
23 #include "Opacity.hpp"
24 #include "SVGElement.hpp"
25 #include "XMLString.hpp"
26
27 using namespace std;
28
29
30 void SVGElement::setClipPathUrl (const string &url) {
31 if (!url.empty())
32 addAttribute("clip-path", "url(#"+url+")");
33 }
34
35
36 void SVGElement::setClipRule (FillRule rule) {
37 if (rule != FR_NONZERO)
38 addAttribute("clip-rule", "evenodd");
39 }
40
41
42 void SVGElement::setFillColor (Color color, bool skipBlack) {
43 if (color != Color::BLACK || !skipBlack)
44 addAttribute("fill", color.svgColorString());
45 }
46
47
48 void SVGElement::setFillOpacity (const Opacity &opacity) {
49 if (!opacity.isFillDefault()) {
50 setFillOpacity(opacity.fillalpha());
51 setFillOpacity(opacity.blendMode());
52 }
53 }
54
55
56 void SVGElement::setFillOpacity (const OpacityAlpha &alpha) {
57 if (!alpha.isOpaque())
58 addAttribute("fill-opacity", alpha.value());
59 }
60
61
62 void SVGElement::setFillOpacity (Opacity::BlendMode blendMode) {
63 if (blendMode != Opacity::BM_NORMAL)
64 addAttribute("style", "mix-blend-mode:"+Opacity::cssBlendMode(blendMode));
65 }
66
67
68 void SVGElement::setFillRule (FillRule rule) {
69 if (rule != FR_NONZERO)
70 addAttribute("fill-rule", "evenodd");
71 }
72
73
74 void SVGElement::setFillPatternUrl (const std::string &url) {
75 if (!url.empty())
76 addAttribute("fill", "url(#" + url + ")");
77 }
78
79
80 void SVGElement::setNoFillColor () {
81 addAttribute("fill", "none");
82 }
83
84
85 void SVGElement::setPoints (const vector<DPair> &points) {
86 if (!points.empty()) {
87 ostringstream oss;
88 for (const DPair &p : points)
89 oss << XMLString(p.x()) << ' ' << XMLString(p.y()) << ' ';
90 string str = oss.str();
91 str.pop_back();
92 addAttribute("points", str);
93 }
94 }
95
96
97 void SVGElement::setStrokeColor (Color color) {
98 addAttribute("stroke", color.svgColorString());
99 }
100
101
102 void SVGElement::setStrokeDash (const vector<double> &pattern, double offset) {
103 if (!pattern.empty()) {
104 string patternStr;
105 for (double dashValue : pattern)
106 patternStr += XMLString(dashValue)+" ";
107 patternStr.pop_back();
108 addAttribute("stroke-dasharray", patternStr);
109 if (offset != 0)
110 addAttribute("stroke-dashoffset", offset);
111 }
112 }
113
114
115 void SVGElement::setStrokeLineCap (LineCap cap) {
116 if (cap != LC_BUTT)
117 addAttribute("stroke-linecap", cap == LC_ROUND ? "round" : "square");
118 }
119
120
121 void SVGElement::setStrokeLineJoin (LineJoin join) {
122 if (join != LJ_MITER)
123 addAttribute("stroke-linejoin", join == LJ_BEVEL ? "bevel" : "round");
124 }
125
126
127 void SVGElement::setStrokeMiterLimit (double limit) {
128 if (limit != 4)
129 addAttribute("stroke-miterlimit", limit);
130 }
131
132
133 void SVGElement::setStrokeOpacity (const Opacity &opacity) {
134 if (!opacity.isStrokeDefault()) {
135 if (!opacity.strokealpha().isOpaque())
136 addAttribute("stroke-opacity", opacity.strokealpha().value());
137 if (opacity.blendMode() != Opacity::BM_NORMAL)
138 addAttribute("style", "mix-blend-mode:"+opacity.cssBlendMode());
139 }
140 }
141
142
143 void SVGElement::setStrokeWidth (double width) {
144 if (width != 1)
145 addAttribute("stroke-width", width);
146 }
147
148
149 void SVGElement::setTransform (const Matrix &matrix) {
150 if (!matrix.isIdentity())
151 addAttribute("transform", matrix.toSVG());
152 }
0 /*************************************************************************
1 ** SVGElement.hpp **
2 ** **
3 ** This file is part of dvisvgm -- a fast DVI to SVG converter **
4 ** Copyright (C) 2005-2021 Martin Gieseking <martin.gieseking@uos.de> **
5 ** **
6 ** This program is free software; you can redistribute it and/or **
7 ** modify it under the terms of the GNU General Public License as **
8 ** published by the Free Software Foundation; either version 3 of **
9 ** the License, or (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, but **
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program; if not, see <http://www.gnu.org/licenses/>. **
18 *************************************************************************/
19
20 #ifndef SVGELEMENT_HPP
21 #define SVGELEMENT_HPP
22
23 #include "XMLNode.hpp"
24
25 class Color;
26 class Matrix;
27 class Opacity;
28
29 class SVGElement : public XMLElement {
30 public:
31 enum FillRule {FR_EVENODD, FR_NONZERO};
32 enum LineCap {LC_BUTT, LC_ROUND, LC_SQUARE};
33 enum LineJoin {LJ_BEVEL, LJ_MITER, LJ_ROUND};
34
35 public:
36 explicit SVGElement (std::string name) : XMLElement(std::move(name)) {}
37 explicit SVGElement (const XMLElement &node) : XMLElement(node) {}
38 explicit SVGElement (XMLElement &&node) noexcept : XMLElement(std::move(node)) {}
39 void setClipPathUrl (const std::string &url);
40 void setClipRule (FillRule rule);
41 void setFillColor (Color color, bool skipBlack=true);
42 void setFillOpacity (const Opacity &opacity);
43 void setFillOpacity (const OpacityAlpha &alpha);
44 void setFillOpacity (Opacity::BlendMode blendMode);
45 void setFillPatternUrl (const std::string &url);
46 void setFillRule (FillRule rule);
47 void setNoFillColor ();
48 void setPoints (const std::vector<DPair> &points);
49 void setStrokeColor (Color color);
50 void setStrokeDash (const std::vector<double> &pattern, double offset=0);
51 void setStrokeLineCap (LineCap cap);
52 void setStrokeLineJoin (LineJoin join);
53 void setStrokeOpacity (const Opacity &opacity);
54 void setStrokeWidth (double width);
55 void setStrokeMiterLimit (double limit);
56 void setTransform (const Matrix &matrix);
57 };
58
59 #endif
1818 *************************************************************************/
1919
2020 #include "SVGSingleCharTextHandler.hpp"
21 #include "XMLNode.hpp"
21 #include "SVGElement.hpp"
2222
2323 using namespace std;
2424
2828 textNode->append(XMLString(font->unicode(c), false));
2929 // Apply color changes only if the color differs from black and if the font color itself is black.
3030 // Glyphs from non-black fonts (e.g. defined in a XeTeX document) can't change their color.
31 if (_color.get() != Color::BLACK && font->color() == Color::BLACK) {
32 textNode->addAttribute("fill", _color.get().svgColorString());
33 _color.changed(false);
34 }
31 if (_color.get() != Color::BLACK && font->color() == Color::BLACK)
32 textNode->setFillColor(_color);
33 _color.changed(false);
34 if (!_opacity->isFillDefault())
35 textNode->setFillOpacity(_opacity);
36 _opacity.changed(false);
3537 contextNode()->append(std::move(textNode));
3638 }
3030 #include "SVGCharHandlerFactory.hpp"
3131 #include "SVGTree.hpp"
3232 #include "XMLDocument.hpp"
33 #include "XMLNode.hpp"
3433 #include "XMLString.hpp"
3534
3635 using namespace std;
5453 /** Clears the SVG tree and initializes the root element. */
5554 void SVGTree::reset () {
5655 _doc.clear();
57 auto rootNode = util::make_unique<XMLElement>("svg");
56 auto rootNode = util::make_unique<SVGElement>("svg");
5857 rootNode->addAttribute("version", "1.1");
5958 rootNode->addAttribute("xmlns", "http://www.w3.org/2000/svg");
6059 rootNode->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
110109 /** Starts a new page.
111110 * @param[in] pageno number of new page */
112111 void SVGTree::newPage (int pageno) {
113 auto pageNode = util::make_unique<XMLElement>("g");
112 auto pageNode = util::make_unique<SVGElement>("g");
114113 if (pageno >= 0)
115114 pageNode->addAttribute("id", string("page")+XMLString(pageno));
116115 _charHandler->setInitialContextNode(pageNode.get());
117116 _page = pageNode.get();
118117 _root->append(std::move(pageNode));
119 _defsContextStack = stack<XMLElement*>();
120 _pageContextStack = stack<XMLElement*>();
118 _defsContextStack = stack<SVGElement*>();
119 _pageContextStack = stack<SVGElement*>();
121120 }
122121
123122
124123 void SVGTree::appendToDefs (unique_ptr<XMLNode> node) {
125124 if (!_defs) {
126 auto defsNode = util::make_unique<XMLElement>("defs");
125 auto defsNode = util::make_unique<SVGElement>("defs");
127126 _defs = defsNode.get();
128127 _root->prepend(std::move(defsNode));
129128 }
133132
134133
135134 void SVGTree::appendToPage (unique_ptr<XMLNode> node) {
136 XMLElement *parent = _pageContextStack.empty() ? _page : _pageContextStack.top();
135 SVGElement *parent = _pageContextStack.empty() ? _page : _pageContextStack.top();
137136 parent->append(std::move(node));
138137 _charHandler->setInitialContextNode(parent);
139138 }
148147
149148
150149 void SVGTree::transformPage (const Matrix &usermatrix) {
151 if (!usermatrix.isIdentity())
152 _page->addAttribute("transform", usermatrix.toSVG());
150 _page->setTransform(usermatrix);
153151 }
154152
155153
170168 double extend = font.style() ? font.style()->extend : 1;
171169 glyphNode = util::make_unique<XMLElement>("glyph");
172170 glyphNode->addAttribute("unicode", XMLString(font.unicode(c), false));
173 glyphNode->addAttribute("horiz-adv-x", XMLString(font.hAdvance(c)*extend));
174 glyphNode->addAttribute("vert-adv-y", XMLString(font.vAdvance(c)));
171 glyphNode->addAttribute("horiz-adv-x", font.hAdvance(c)*extend);
172 glyphNode->addAttribute("vert-adv-y", font.vAdvance(c));
175173 string name = font.glyphName(c);
176174 if (!name.empty())
177175 glyphNode->addAttribute("glyph-name", name);
191189
192190 static string font_info (const Font &font) {
193191 ostringstream oss;
194 if (auto nf = dynamic_cast<const NativeFont*>(&font)) {
192 if (auto nf = font_cast<const NativeFont*>(&font)) {
195193 oss << nf->familyName() << ' ' << nf->styleName() << "; " << nf->filename();
196194 if (nf->style()) {
197195 if (nf->style()->bold != 0)
210208 if (CREATE_CSS && USE_FONTS && !fonts.empty() && _page) {
211209 map<int, const Font*> sortmap;
212210 for (const Font *font : fonts)
213 if (!dynamic_cast<const VirtualFont*>(font)) // skip virtual fonts
211 if (!font_cast<const VirtualFont*>(font)) // skip virtual fonts
214212 sortmap[FontManager::instance().fontID(font)] = font;
215213 ostringstream style;
216214 // add font style definitions in ascending order
259257 auto fontNode = util::make_unique<XMLElement>("font");
260258 string fontname = font.name();
261259 fontNode->addAttribute("id", fontname);
262 fontNode->addAttribute("horiz-adv-x", XMLString(font.hAdvance()));
260 fontNode->addAttribute("horiz-adv-x", font.hAdvance());
263261
264262 auto faceNode = util::make_unique<XMLElement>("font-face");
265263 faceNode->addAttribute("font-family", fontname);
266 faceNode->addAttribute("units-per-em", XMLString(font.unitsPerEm()));
264 faceNode->addAttribute("units-per-em", font.unitsPerEm());
267265 if (!font.verticalLayout()) {
268 faceNode->addAttribute("ascent", XMLString(font.ascent()));
269 faceNode->addAttribute("descent", XMLString(font.descent()));
266 faceNode->addAttribute("ascent", font.ascent());
267 faceNode->addAttribute("descent", font.descent());
270268 }
271269 fontNode->append(std::move(faceNode));
272270 for (int c : chars)
296294 }
297295
298296
299 void SVGTree::pushDefsContext (unique_ptr<XMLElement> node) {
300 XMLElement *nodePtr = node.get();
297 void SVGTree::pushDefsContext (unique_ptr<SVGElement> node) {
298 SVGElement *nodePtr = node.get();
301299 if (_defsContextStack.empty())
302300 appendToDefs(std::move(node));
303301 else
313311
314312
315313 /** Pushes a new context element that will take all following nodes added to the page. */
316 void SVGTree::pushPageContext (unique_ptr<XMLElement> node) {
317 XMLElement *nodePtr = node.get();
314 void SVGTree::pushPageContext (unique_ptr<SVGElement> node) {
315 SVGElement *nodePtr = node.get();
318316 if (_pageContextStack.empty())
319317 _page->append(std::move(node));
320318 else
3030 #include "GFGlyphTracer.hpp"
3131 #include "Matrix.hpp"
3232 #include "SVGCharHandler.hpp"
33 #include "SVGElement.hpp"
3334 #include "XMLDocument.hpp"
34 #include "XMLNode.hpp"
3535
3636 class BoundingBox;
3737 class Color;
3838 class Font;
3939 class Matrix;
40 class Opacity;
4041 class PhysicalFont;
4142
4243 class SVGTree {
5354 void appendChar (int c, double x, double y) {_charHandler->appendChar(c, x, y);}
5455 void appendFontStyles (const std::unordered_set<const Font*> &fonts);
5556 void append (const PhysicalFont &font, const std::set<int> &chars, GFGlyphTracer::Callback *callback=nullptr);
56 void pushDefsContext (std::unique_ptr<XMLElement> node);
57 void pushDefsContext (std::unique_ptr<SVGElement> node);
5758 void popDefsContext ();
58 void pushPageContext (std::unique_ptr<XMLElement> node);
59 void pushPageContext (std::unique_ptr<SVGElement> node);
5960 void popPageContext ();
6061 void setBBox (const BoundingBox &bbox);
6162 void setFont (int id, const Font &font);
6263 static bool setFontFormat (std::string formatstr);
63 void setX (double x) {_charHandler->notifyXAdjusted();}
64 void setY (double y) {_charHandler->notifyYAdjusted();}
65 void setMatrix (const Matrix &m) {_charHandler->setMatrix(m);}
64 void setX (double x) {_charHandler->notifyXAdjusted();}
65 void setY (double y) {_charHandler->notifyYAdjusted();}
66 void setMatrix (const Matrix &m) {_charHandler->setMatrix(m);}
6667 void setColor (const Color &c);
67 void setVertical (bool state) {_charHandler->setVertical(state);}
68 void setOpacity (const Opacity &op) {_charHandler->setOpacity(op);}
69 void setVertical (bool state) {_charHandler->setVertical(state);}
6870 void transformPage (const Matrix &m);
69 Color getColor () const {return _charHandler->getColor();}
70 const Matrix& getMatrix () const {return _charHandler->getMatrix();}
71 XMLElement* rootNode () const {return _root;}
72 XMLElement* defsNode () const {return _defs;}
73 XMLElement* pageNode () const {return _page;}
71 Color getColor () const {return _charHandler->getColor();}
72 const Opacity& getOpacity () const {return _charHandler->getOpacity();}
73 const Matrix& getMatrix () const {return _charHandler->getMatrix();}
74 XMLElement* rootNode () const {return _root;}
75 XMLElement* defsNode () const {return _defs;}
76 XMLElement* pageNode () const {return _page;}
7477
7578 protected:
7679 XMLCData* styleCDataNode ();
8790
8891 private:
8992 XMLDocument _doc;
90 XMLElement *_root, *_page, *_defs;
93 SVGElement *_root, *_page, *_defs;
9194 XMLCData *_styleCDataNode;
9295 std::unique_ptr<SVGCharHandler> _charHandler;
93 std::stack<XMLElement*> _defsContextStack;
94 std::stack<XMLElement*> _pageContextStack;
96 std::stack<SVGElement*> _defsContextStack;
97 std::stack<SVGElement*> _pageContextStack;
9598 };
9699
97100 #endif
2626 #include "Color.hpp"
2727 #include "FilePath.hpp"
2828 #include "Matrix.hpp"
29 #include "Opacity.hpp"
2930 #include "SVGTree.hpp"
3031
3132 class XMLElement;
4546 virtual const Matrix& getMatrix () const =0;
4647 virtual Matrix getPageTransformation () const {return Matrix(1);}
4748 virtual void setBgColor (const Color &color) =0;
49 virtual void setOpacity (const Opacity &opacity) =0;
50 virtual const Opacity& getOpacity () const =0;
4851 virtual const SVGTree& svgTree () const =0;
4952 SVGTree& svgTree () {return const_cast<SVGTree&>(const_cast<const SpecialActions*>(this)->svgTree());}
5053 virtual BoundingBox& bbox () =0;
7477 void finishLine () override {}
7578 void setColor (const Color &color) override {}
7679 void setBgColor (const Color &color) override {}
80 void setOpacity (const Opacity &opacity) override {}
81 const Opacity& getOpacity () const override {return _svg.getOpacity();}
7782 Color getColor () const override {return Color::BLACK;}
7883 void setMatrix (const Matrix &m) override {}
7984 const Matrix& getMatrix () const override {return _matrix;}
5252 while (is && !isspace(is.peek()))
5353 id += char(is.get());
5454 if (!id.empty()) {
55 auto state = _subfonts.emplace(pair<string,unique_ptr<Subfont>>(id, unique_ptr<Subfont>()));
55 auto state = _subfonts.emplace(id, unique_ptr<Subfont>());
5656 if (state.second) // id was not present in map already
5757 state.first->second = unique_ptr<Subfont>(new Subfont(*this, state.first->first));
5858 skip_mapping_data(is);
6262 * @param[in] edgeflag defines how to connect this patch with another one
6363 * @param[in] patch reference patch required if edgeflag > 0 */
6464 void TensorProductPatch::setPoints (const PointVec &points, int edgeflag, ShadingPatch *patch) {
65 auto tpPatch = dynamic_cast<TensorProductPatch*>(patch);
65 TensorProductPatch *tpPatch = nullptr;
66 if (patch && patch->psShadingType() == psShadingType())
67 tpPatch = static_cast<TensorProductPatch*>(patch);
6668 if (edgeflag > 0 && !tpPatch)
6769 throw ShadingException("missing preceding data in definition of tensor-product patch");
6870 if ((edgeflag == 0 && points.size() != 16) || (edgeflag > 0 && points.size() != 12))
106108 * @param[in] edgeflag defines how to connect this patch with another one
107109 * @param[in] patch reference patch required if edgeflag > 0 */
108110 void TensorProductPatch::setColors(const ColorVec &colors, int edgeflag, ShadingPatch* patch) {
109 auto tpPatch = dynamic_cast<TensorProductPatch*>(patch);
111 TensorProductPatch *tpPatch = nullptr;
112 if (patch && patch->psShadingType() == psShadingType())
113 tpPatch = static_cast<TensorProductPatch*>(patch);
110114 if (edgeflag > 0 && !tpPatch)
111115 throw ShadingException("missing preceding data in definition of tensor-product patch");
112116 if ((edgeflag == 0 && colors.size() != 4) || (edgeflag > 0 && colors.size() != 2))
491495 * @param[in] edgeflag defines how to connect this patch to another one
492496 * @param[in] patch reference patch required if edgeflag > 0 */
493497 void CoonsPatch::setPoints (const PointVec &points, int edgeflag, ShadingPatch *patch) {
494 auto coonsPatch = dynamic_cast<CoonsPatch*>(patch);
498 CoonsPatch *coonsPatch = nullptr;
499 if (patch && patch->psShadingType() == psShadingType())
500 coonsPatch = static_cast<CoonsPatch*>(patch);
495501 if (edgeflag > 0 && !coonsPatch)
496502 throw ShadingException("missing preceding data in definition of relative Coons patch");
497503 if ((edgeflag == 0 && points.size() != 12) || (edgeflag > 0 && points.size() != 8))
528534
529535
530536 void CoonsPatch::setColors (const ColorVec &colors, int edgeflag, ShadingPatch *patch) {
531 auto coonsPatch = dynamic_cast<CoonsPatch*>(patch);
537 CoonsPatch *coonsPatch = nullptr;
538 if (patch && patch->psShadingType() == psShadingType())
539 coonsPatch = static_cast<CoonsPatch*>(patch);
532540 if (edgeflag > 0 && !coonsPatch)
533541 throw ShadingException("missing preceding data in definition of relative Coons patch");
534542 if ((edgeflag == 0 && colors.size() != 4) || (edgeflag > 0 && colors.size() != 2))
2727 #include "InputReader.hpp"
2828 #include "GraphicsPath.hpp"
2929 #include "SpecialActions.hpp"
30 #include "SVGElement.hpp"
3031 #include "SVGTree.hpp"
3132 #include "TpicSpecialHandler.hpp"
3233 #include "utility.hpp"
6970 * @param[in] penwidth pen with used to compute the stroke parameters
7071 * @param[in] pencolor the drawing color
7172 * @param[in] ddist dash/dot distance of line in PS point units (0:solid line, >0:dashed line, <0:dotted line) */
72 static void add_stroke_attribs (XMLElement *elem, double penwidth, Color pencolor, double ddist) {
73 static void add_stroke_attribs (SVGElement *elem, double penwidth, Color pencolor, double ddist) {
7374 if (penwidth > 0) { // attributes actually required?
74 elem->addAttribute("stroke", pencolor.svgColorString());
75 elem->addAttribute("stroke-width", XMLString(penwidth));
75 elem->setStrokeColor(pencolor);
76 elem->setStrokeWidth(penwidth);
77 vector<double> dasharray;
7678 if (ddist > 0)
77 elem->addAttribute("stroke-dasharray", XMLString(ddist));
78 else if (ddist < 0)
79 elem->addAttribute("stroke-dasharray", XMLString(penwidth) + ' ' + XMLString(-ddist));
80 }
81 }
82
83
84 static unique_ptr<XMLElement> create_ellipse_element (double cx, double cy, double rx, double ry) {
79 dasharray.push_back(ddist);
80 else if (ddist < 0) {
81 dasharray.push_back(penwidth);
82 dasharray.push_back(-ddist);
83 }
84 elem->setStrokeDash(dasharray);
85 }
86 }
87
88
89 static unique_ptr<SVGElement> create_ellipse_element (double cx, double cy, double rx, double ry) {
8590 bool is_circle = (rx == ry);
86 auto elem = util::make_unique<XMLElement>(is_circle ? "circle" : "ellipse");
87 elem->addAttribute("cx", XMLString(cx));
88 elem->addAttribute("cy", XMLString(cy));
91 auto elem = util::make_unique<SVGElement>(is_circle ? "circle" : "ellipse");
92 elem->addAttribute("cx", cx);
93 elem->addAttribute("cy", cy);
8994 if (is_circle)
90 elem->addAttribute("r", XMLString(rx));
95 elem->addAttribute("r", rx);
9196 else {
92 elem->addAttribute("rx", XMLString(rx));
93 elem->addAttribute("ry", XMLString(ry));
97 elem->addAttribute("rx", rx);
98 elem->addAttribute("ry", ry);
9499 }
95100 return elem;
96101 }
101106 * @param[in] actions object providing the actions that can be performed by the SpecialHandler */
102107 void TpicSpecialHandler::drawLines (double ddist, SpecialActions &actions) {
103108 if (!_points.empty() && (_penwidth > 0 || _grayLevel >= 0) && !actions.outputLocked()) {
104 unique_ptr<XMLElement> elem;
109 unique_ptr<SVGElement> elem;
105110 if (_points.size() == 1) {
106111 const DPair &p = _points.back();
107112 elem = create_ellipse_element(p.x()+actions.getX(), p.y()+actions.getY(), _penwidth/2.0, _penwidth/2.0);
109114 }
110115 else {
111116 if (_points.size() == 2 || (_grayLevel < 0 && _points.front() != _points.back())) {
112 elem = util::make_unique<XMLElement>("polyline");
113 elem->addAttribute("fill", "none");
114 elem->addAttribute("stroke-linecap", "round");
117 elem = util::make_unique<SVGElement>("polyline");
118 elem->setNoFillColor();
119 elem->setStrokeLineCap(SVGElement::LC_ROUND);
115120 }
116121 else {
117122 while (_points.front() == _points.back())
118123 _points.pop_back();
119 elem = util::make_unique<XMLElement>("polygon");
120 elem->addAttribute("fill", _grayLevel < 0 ? "none" : fillColor(false).svgColorString());
121 }
122 ostringstream oss;
123 for (auto it=_points.begin(); it != _points.end(); ++it) {
124 if (it != _points.begin())
125 oss << ' ';
126 double x = it->x()+actions.getX();
127 double y = it->y()+actions.getY();
128 oss << XMLString(x) << ',' << XMLString(y);
124 elem = util::make_unique<SVGElement>("polygon");
125 if (_grayLevel < 0)
126 elem->setNoFillColor();
127 else
128 elem->setFillColor(fillColor(false));
129 }
130 vector<DPair> points;
131 for (const DPair &p : _points) {
132 double x = p.x()+actions.getX();
133 double y = p.y()+actions.getY();
134 points.emplace_back(x, y);
129135 actions.embed(DPair(x, y));
130136 }
131 elem->addAttribute("points", oss.str());
137 elem->setPoints(points);
132138 add_stroke_attribs(elem.get(), _penwidth, Color::BLACK, ddist);
133139 }
134140 actions.svgTree().appendToPage(std::move(elem));
174180 path.lineto(p+_points[numPoints-1]);
175181 actions.embed(p+_points[numPoints-1]);
176182 }
177 auto pathElem = util::make_unique<XMLElement>("path");
178 pathElem->addAttribute("fill", "none");
183 auto pathElem = util::make_unique<SVGElement>("path");
184 pathElem->setNoFillColor();
179185 ostringstream oss;
180186 path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
181187 pathElem->addAttribute("d", oss.str());
199205 if ((_penwidth > 0 || _grayLevel >= 0) && !actions.outputLocked()) {
200206 cx += actions.getX();
201207 cy += actions.getY();
202 unique_ptr<XMLElement> elem;
208 unique_ptr<SVGElement> elem;
203209 bool closed=true;
204210 if (abs(angle2-angle1) >= math::TWO_PI) // closed ellipse?
205211 elem = create_ellipse_element(cx, cy, rx, ry);
212218 path.closepath();
213219 else
214220 closed = false;
215 elem = util::make_unique<XMLElement>("path");
221 elem = util::make_unique<SVGElement>("path");
216222 ostringstream oss;
217223 path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
218224 elem->addAttribute("d", oss.str());
219225 }
220226 if (_penwidth > 0) {
221 elem->addAttribute("stroke-width", _penwidth);
222 elem->addAttribute("stroke", actions.getColor().svgColorString());
227 elem->setStrokeWidth(_penwidth);
228 elem->setStrokeColor(actions.getColor());
223229 if (!closed)
224 elem->addAttribute("stroke-linecap", "round");
225 }
226 elem->addAttribute("fill", _grayLevel < 0 ? "none" : fillColor(true).svgColorString());
230 elem->setStrokeLineCap(SVGElement::LC_ROUND);
231 }
232 if (_grayLevel < 0)
233 elem->setNoFillColor();
234 else
235 elem->setFillColor(fillColor(true));
227236 actions.svgTree().appendToPage(std::move(elem));
228237 double pw = _penwidth/2.0;
229238 actions.embed(BoundingBox(cx-rx-pw, cy-ry-pw, cx+rx+pw, cy+ry+pw));
295304 case cmd_id("pa"): { // add point to path
296305 double x = ir.getDouble()*mi2bp;
297306 double y = ir.getDouble()*mi2bp;
298 _points.emplace_back(DPair(x,y));
307 _points.emplace_back(x, y);
299308 break;
300309 }
301310 case cmd_id("fp"): // draw solid lines through recorded points; close and fill path if fill color was defined
3030
3131
3232 void TriangularPatch::setPoints (const PointVec &points, int edgeflag, ShadingPatch *patch) {
33 auto triangularPatch = dynamic_cast<TriangularPatch*>(patch);
33 TriangularPatch *triangularPatch = nullptr;
34 if (patch && patch->psShadingType() == psShadingType())
35 triangularPatch = static_cast<TriangularPatch*>(patch);
3436 if (edgeflag > 0 && !triangularPatch)
3537 throw ShadingException("missing preceding data in definition of triangular patch");
3638 if ((edgeflag == 0 && points.size() != 3) || (edgeflag > 0 && points.size() != 1))
6163
6264
6365 void TriangularPatch::setColors (const ColorVec &colors, int edgeflag, ShadingPatch *patch) {
64 auto triangularPatch = dynamic_cast<TriangularPatch*>(patch);
66 TriangularPatch *triangularPatch = nullptr;
67 if (patch && patch->psShadingType() == psShadingType())
68 triangularPatch = static_cast<TriangularPatch*>(patch);
6569 if (edgeflag > 0 && !triangularPatch)
6670 throw ShadingException("missing preceding data in definition of triangular patch");
6771 if ((edgeflag == 0 && colors.size() != 3) || (edgeflag > 0 && colors.size() != 1))
5555 if ((offset | length) > _buffer.size() || offset+length > _buffer.size())
5656 return false;
5757 TTFTableRecord record = {tag, checksum, length, reinterpret_cast<const uint8_t*>(_buffer.data())+offset};
58 _tableRecords.emplace_back(std::move(record));
58 _tableRecords.push_back(std::move(record));
5959 }
6060 return true;
6161 }
9393 woffRecord.compressTableData();
9494 woffSize += woffRecord.paddedSize();
9595 ttfSize += ttfRecord.paddedSize();
96 woffRecords.emplace_back(std::move(woffRecord));
96 woffRecords.push_back(std::move(woffRecord));
9797 }
9898 // write WOFF header
9999 StreamWriter writer(os);
9292 bool operator >= (const VectorIterator &it) const {return _pos >= it._pos;}
9393 bool operator < (const VectorIterator &it) const {return _pos < it._pos;}
9494 bool operator > (const VectorIterator &it) const {return _pos > it._pos;}
95 bool valid () const {return _pos >= 0 && _pos < _vector.size();}
95 bool valid () const {return _pos < _vector.size();}
9696 void invalidate () {_pos = _vector.size();}
9797 void reset () {_pos = 0;}
9898
3838 if (node->toElement())
3939 _rootElement = util::static_unique_ptr_cast<XMLElement>(std::move(node));
4040 else
41 _nodes.emplace_back(std::move(node));
41 _nodes.push_back(std::move(node));
4242 }
4343 }
4444
106106 if (Attribute *attr = getAttribute(name))
107107 attr->value = value;
108108 else
109 _attributes.emplace_back(Attribute(name, value));
109 _attributes.emplace_back(name, value);
110110 }
111111
112112
386386 os << '>';
387387 // Insert newlines around children except text nodes. According to the
388388 // SVG specification, pure whitespace nodes are ignored by the SVG renderer.
389 if (WRITE_NEWLINES && !_firstChild->toText())
389 if (WRITE_NEWLINES && name() != "text" && !_firstChild->toText())
390390 os << '\n';
391391 for (XMLNode *child = _firstChild.get(); child; child = child->next()) {
392392 child->write(os);
393393 if (!child->toText()) {
394 if (WRITE_NEWLINES && (!child->next() || !child->next()->toText()))
394 if (WRITE_NEWLINES && name() != "text" && (!child->next() || !child->next()->toText()))
395395 os << '\n';
396396 }
397397 }
127127 explicit XMLElement (std::string name);
128128 XMLElement (const XMLElement &node);
129129 XMLElement (XMLElement &&node) noexcept;
130 ~XMLElement ();
130 ~XMLElement () override;
131131 std::unique_ptr<XMLNode> clone () const override {return util::make_unique<XMLElement>(*this);}
132132 void clear () override;
133133 void addAttribute (const std::string &name, const std::string &value);
243243
244244 protected:
245245 void append (const string &name, const string &version) {
246 _versionPairs.emplace_back(pair<string,string>(name, version));
246 _versionPairs.emplace_back(name, version);
247247 }
248248
249249 private:
348348 string msg = "unknown font format '"+cmdline.fontFormatOpt.value()+"' (supported formats: ";
349349 for (const string &format : FontWriter::supportedFormats())
350350 msg += format + ", ";
351 msg.erase(msg.end()-2);
351 msg.erase(msg.end()-2, msg.end());
352 msg += ")";
352353 throw CL::CommandLineException(msg);
353354 }
354355 SVGTree::CREATE_USE_ELEMENTS = cmdline.noFontsOpt.value() < 1;
106106 * @return true if all attributes have been moved */
107107 bool GroupCollapser::moveAttributes (XMLElement &source, XMLElement &dest) {
108108 vector<string> movedAttributes;
109 for (const XMLElement::Attribute &attr : source.attributes()) {
109 for (const auto &attr : source.attributes()) {
110110 if (attr.name == "transform") {
111111 string transform;
112112 if (const char *destvalue = dest.getAttributeValue("transform"))
3838 if (commonAttribs.empty()) {
3939 if (intersected)
4040 break;
41 for (const auto attrib : elem->attributes()) {
41 for (const auto &attrib : elem->attributes()) {
4242 if (AttributeExtractor::inheritable(attrib))
4343 commonAttribs.push_back(attrib);
4444 }
9393 vector<XMLElement::Attribute> attribs = common_inheritable_attributes(tspans);
9494 if (!tspans.empty() && !attribs.empty()) {
9595 // move all common tspan attributes to the parent text element
96 for (const XMLElement::Attribute &attr : attribs)
96 for (const auto &attr : attribs)
9797 context->addAttribute(attr.name, attr.value);
9898 // remove all common attributes from the tspan elements
9999 for (XMLElement *tspan : tspans) {
100 for (const XMLElement::Attribute &attr : attribs)
100 for (const auto &attr : attribs)
101101 tspan->removeAttribute(attr.name);
102102 // unwrap the tspan if there are no remaining attributes
103103 if (tspan->attributes().empty())
114114 "rgbcolor 3(setrgbcolor)prcmd}def/printgstate{@dodraw @GD/@nulldev get not and{"
115115 "matrix currentmatrix aload pop 6(setmatrix)prcmd applyscalevals currentlinewid"
116116 "th 1(setlinewidth)prcmd currentlinecap 1(setlinecap)prcmd currentlinejoin 1(se"
117 "tlinejoin)prcmd currentmiterlimit 1(setmiterlimit)prcmd prcolor currentdash ma"
118 "rk 3 1 roll exch aload length 1 add -1 roll counttomark(setdash)prcmd pop}if}d"
119 "ef/strconcat{exch dup length 2 index length add string dup dup 4 2 roll copy l"
120 "ength 4 -1 roll putinterval}def/setgstate{/setgstate sysexec printgstate}def/s"
121 "ave{@UD begin/@saveID vmstatus pop pop def end :save @saveID 1(save)prcmd}def/"
122 "restore{:restore @checknulldev printgstate @UD/@saveID known{@UD begin @saveID"
123 " end}{0}ifelse 1(restore)prcmd}def/gsave 0 defpr/grestore{:grestore @checknull"
124 "dev printgstate 0(grestore)prcmd}def/grestoreall{:grestoreall @checknulldev se"
125 "tstate 0(grestoreall)prcmd}def/rotate{dup type/arraytype ne @dodraw and{dup 1("
126 "rotate)prcmd}if/rotate sysexec applyscalevals}def/scale{dup type/arraytype ne "
127 "@dodraw and{2 copy 2(scale)prcmd}if/scale sysexec applyscalevals}def/translate"
128 "{dup type/arraytype ne @dodraw and{2 copy 2(translate)prcmd}if/translate sysex"
129 "ec}def/setmatrix{dup/setmatrix sysexec @dodraw{aload pop 6(setmatrix)prcmd app"
130 "lyscalevals}{pop}ifelse}def/initmatrix{matrix setmatrix}def/concat{matrix curr"
131 "entmatrix matrix concatmatrix setmatrix}def/makepattern{gsave<</mx 3 -1 roll>>"
132 "begin<</XUID[1000000 @patcnt]>>copy mx/makepattern sysexec dup begin PatternTy"
133 "pe 2 lt{PatternType @patcnt BBox aload pop XStep YStep PaintType mx aload pop "
134 "15(makepattern)prcmd :newpath matrix setmatrix dup PaintProc 0 1(makepattern)p"
135 "rcmd @GD/@patcnt @patcnt 1 add put}if end end grestore}def/setpattern{begin Pa"
136 "tternType 1 eq{PaintType 1 eq{XUID aload pop exch pop 1}{:gsave[currentcolorsp"
137 "ace aload length -1 roll pop]/setcolorspace sysexec/setcolor sysexec XUID aloa"
138 "d pop exch pop currentrgbcolor :grestore 4}ifelse(setpattern)prcmd currentcolo"
139 "rspace 0 get/Pattern ne{[/Pattern currentcolorspace]/setcolorspace sysexec}if "
140 "currentcolorspace @setcolorspace}{/setpattern sysexec}ifelse end}def/setcolor{"
141 "dup type/dicttype eq{setpattern}{/setcolor sysexec/currentrgbcolor sysexec set"
142 "rgbcolor}ifelse}def/setcolorspace{dup/setcolorspace sysexec @setcolorspace}def"
143 "/@setcolorspace{dup type/arraytype eq{0 get}if/Pattern eq{1}{0}ifelse 1(setcol"
144 "orspace)prcmd}def/setgray 1 defpr/setcmykcolor 4 defpr/sethsbcolor 3 defpr/set"
145 "rgbcolor 3 defpr/.setalphaisshape{@SD/.setalphaisshape known{dup/.setalphaissh"
146 "ape sysexec}if{1}{0}ifelse 1(setisshapealpha)prcmd}bind def/.setfillconstantal"
147 "pha{@SD/.setfillconstantalpha known{dup/.setfillconstantalpha sysexec}if 1(set"
148 "fillconstantalpha)prcmd}bind def/.setstrokeconstantalpha{@SD/.setstrokeconstan"
149 "talpha known{dup/.setstrokeconstantalpha sysexec}if 1(setstrokeconstantalpha)p"
150 "rcmd}bind def/.setopacityalpha{false .setalphaisshape dup .setfillconstantalph"
151 "a .setstrokeconstantalpha}bind def/.setshapealpha{true .setalphaisshape dup .s"
152 "etfillconstantalpha .setstrokeconstantalpha}bind def/.setblendmode{dup/.setble"
153 "ndmode sysexec<</Normal 0/Compatible 0/Multiply 1/Screen 2/Overlay 3/SoftLight"
154 " 4/HardLight 5/ColorDodge 6/ColorBurn 7/Darken 8/Lighten 9/Difference 10/Exclu"
155 "sion 11/Hue 12/Saturation 13/Color 14/Luminosity 15/CompatibleOverprint 16>>ex"
156 "ch get 1(setblendmode)prcmd}def/@pdfpagecount{(r)file runpdfbegin pdfpagecount"
157 " runpdfend}def/@pdfpagebox{(r)file runpdfbegin dup dup 1 lt exch pdfpagecount "
158 "gt or{pop}{pdfgetpage/MediaBox pget pop aload pop}ifelse runpdfend}def DELAYBI"
159 "ND{.bindnow}if ";
117 "tlinejoin)prcmd currentmiterlimit 1(setmiterlimit)prcmd revision dup 952 lt{po"
118 "p}{.currentblendmode .setblendmode 952 eq{.currentopacityalpha .setopacityalph"
119 "a .currentshapealpha .setshapealpha}{.currentalphaisshape{1}{0}ifelse 1(setalp"
120 "haisshape)prcmd .currentstrokeconstantalpha 1(setstrokeconstantalpha)prcmd .cu"
121 "rrentfillconstantalpha 1(setfillconstantalpha)prcmd}ifelse}ifelse prcolor curr"
122 "entdash mark 3 1 roll exch aload length 1 add -1 roll counttomark(setdash)prcm"
123 "d pop}if}def/strconcat{exch dup length 2 index length add string dup dup 4 2 r"
124 "oll copy length 4 -1 roll putinterval}def/setgstate{/setgstate sysexec printgs"
125 "tate}def/save{@UD begin/@saveID vmstatus pop pop def end :save @saveID 1(save)"
126 "prcmd}def/restore{:restore @checknulldev printgstate @UD/@saveID known{@UD beg"
127 "in @saveID end}{0}ifelse 1(restore)prcmd}def/gsave 0 defpr/grestore{:grestore "
128 "@checknulldev printgstate 0(grestore)prcmd}def/grestoreall{:grestoreall @check"
129 "nulldev setstate 0(grestoreall)prcmd}def/rotate{dup type/arraytype ne @dodraw "
130 "and{dup 1(rotate)prcmd}if/rotate sysexec applyscalevals}def/scale{dup type/arr"
131 "aytype ne @dodraw and{2 copy 2(scale)prcmd}if/scale sysexec applyscalevals}def"
132 "/translate{dup type/arraytype ne @dodraw and{2 copy 2(translate)prcmd}if/trans"
133 "late sysexec}def/setmatrix{dup/setmatrix sysexec @dodraw{aload pop 6(setmatrix"
134 ")prcmd applyscalevals}{pop}ifelse}def/initmatrix{matrix setmatrix}def/concat{m"
135 "atrix currentmatrix matrix concatmatrix setmatrix}def/makepattern{gsave<</mx 3"
136 " -1 roll>>begin<</XUID[1000000 @patcnt]>>copy mx/makepattern sysexec dup begin"
137 " PatternType 2 lt{PatternType @patcnt BBox aload pop XStep YStep PaintType mx "
138 "aload pop 15(makepattern)prcmd :newpath matrix setmatrix dup PaintProc 0 1(mak"
139 "epattern)prcmd @GD/@patcnt @patcnt 1 add put}if end end grestore}def/setpatter"
140 "n{dup begin PatternType end 1 eq{begin PaintType 1 eq{XUID aload pop exch pop "
141 "1}{:gsave[currentcolorspace aload length -1 roll pop]/setcolorspace sysexec/se"
142 "tcolor sysexec XUID aload pop exch pop currentrgbcolor :grestore 4}ifelse(setp"
143 "attern)prcmd currentcolorspace 0 get/Pattern ne{[/Pattern currentcolorspace]/s"
144 "etcolorspace sysexec}if currentcolorspace @setcolorspace end}{/setpattern syse"
145 "xec}ifelse}def/setcolor{dup type/dicttype eq{setpattern}{/setcolor sysexec/cur"
146 "rentrgbcolor sysexec setrgbcolor}ifelse}def/setcolorspace{dup/setcolorspace sy"
147 "sexec @setcolorspace}def/@setcolorspace{dup type/arraytype eq{0 get}if/Pattern"
148 " eq{1}{0}ifelse 1(setcolorspace)prcmd}def/setgray 1 defpr/setcmykcolor 4 defpr"
149 "/sethsbcolor 3 defpr/setrgbcolor 3 defpr/.setalphaisshape{@SD/.setalphaisshape"
150 " known{dup/.setalphaisshape sysexec}if{1}{0}ifelse 1(setalphaisshape)prcmd}bin"
151 "d def/.setfillconstantalpha{@SD/.setfillconstantalpha known{dup/.setfillconsta"
152 "ntalpha sysexec}if 1(setfillconstantalpha)prcmd}bind def/.setstrokeconstantalp"
153 "ha{@SD/.setstrokeconstantalpha known{dup/.setstrokeconstantalpha sysexec}if 1("
154 "setstrokeconstantalpha)prcmd}bind def/.setopacityalpha{false .setalphaisshape "
155 "dup .setfillconstantalpha .setstrokeconstantalpha}bind def/.setshapealpha{true"
156 " .setalphaisshape dup .setfillconstantalpha .setstrokeconstantalpha}bind def/."
157 "setblendmode{dup/.setblendmode sysexec<</Normal 0/Compatible 0/Multiply 1/Scre"
158 "en 2/Overlay 3/SoftLight 4/HardLight 5/ColorDodge 6/ColorBurn 7/Darken 8/Light"
159 "en 9/Difference 10/Exclusion 11/Hue 12/Saturation 13/Color 14/Luminosity 15/Co"
160 "mpatibleOverprint 16>>exch get 1(setblendmode)prcmd}def/@pdfpagecount{(r)file "
161 "runpdfbegin pdfpagecount runpdfend}def/@pdfpagebox{(r)file runpdfbegin dup dup"
162 " 1 lt exch pdfpagecount gt or{pop}{pdfgetpage/MediaBox pget pop aload pop}ifel"
163 "se runpdfend}def DELAYBIND{.bindnow}if ";
160164
133133 vector<string> util::split (const string &str, const string &sep) {
134134 vector<string> parts;
135135 if (str.empty() || sep.empty())
136 parts.emplace_back(str);
136 parts.push_back(str);
137137 else {
138138 size_t left=0;
139139 while (left <= str.length()) {
140140 size_t right = str.find(sep, left);
141141 if (right == string::npos) {
142 parts.emplace_back(str.substr(left));
142 parts.push_back(str.substr(left));
143143 left = string::npos;
144144 }
145145 else {
146 parts.emplace_back(str.substr(left, right-left));
146 parts.push_back(str.substr(left, right-left));
147147 left = right+sep.length();
148148 }
149149 }
2424 #include <memory>
2525 #include <sstream>
2626 #include <string>
27 #include <type_traits>
2728 #include <vector>
2829
2930 namespace math {
144145 return std::unique_ptr<T>{static_cast<T*>(old.release())};
145146 }
146147
148 template <typename T>
149 struct set_const_of {
150 template <typename U>
151 struct by {
152 using type = typename std::conditional<
153 std::is_const<U>::value,
154 typename std::add_const<T>::type,
155 typename std::remove_const<T>::type
156 >::type;
157 };
158 };
147159 } // namespace util
148160
149161 #endif
2121 #define VERSION_HPP
2222
2323 constexpr const char *PROGRAM_NAME = "dvisvgm";
24 constexpr const char *PROGRAM_VERSION = "2.11.1";
24 constexpr const char *PROGRAM_VERSION = "2.12";
2525
2626 #endif
2727
151151 handler.processSpecial("point 1, 10, 10");
152152 handler.processSpecial("point 2, 100, 100");
153153 handler.processSpecial("line 1v, 2v, 10bp"); // cut line ends vertically
154 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='10,17.07 10,2.93 100,92.93 100,107.07'/>\n</g>");
154 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='10 17.07 10 2.93 100 92.93 100 107.07'/>\n</g>");
155155 }
156156
157157
159159 handler.processSpecial("point 1, 10, 10");
160160 handler.processSpecial("point 2, 100, 100");
161161 handler.processSpecial("line 1h, 2h, 10bp"); // cut line ends horizontally
162 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='2.93,10 17.07,10 107.07,100 92.93,100'/>\n</g>");
162 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='2.93 10 17.07 10 107.07 100 92.93 100'/>\n</g>");
163163 }
164164
165165
167167 handler.processSpecial("point 1, 10, 10");
168168 handler.processSpecial("point 2, 100, 100");
169169 handler.processSpecial("line 1h, 2v, 10bp"); // cut line ends horizontally
170 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='2.93,10 17.07,10 100,92.93 100,107.07'/>\n</g>");
170 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='2.93 10 17.07 10 100 92.93 100 107.07'/>\n</g>");
171171
172172 recorder.clear();
173173 recorder.setColor(Color(0.0, 0.0, 1.0));
174174 handler.processSpecial("point 1, 10, 10");
175175 handler.processSpecial("point 2, 100, 100");
176176 handler.processSpecial("line 1v, 2h, 10bp"); // cut line ends horizontally
177 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='10,17.07 10,2.93 107.07,100 92.93,100' fill='#00f'/>\n</g>");
177 EXPECT_EQ(recorder.getPageXML(), "<g id='page1'>\n<polygon points='10 17.07 10 2.93 107.07 100 92.93 100' fill='#00f'/>\n</g>");
178178 }
179179
180180
8989 }
9090
9191
92 TEST(FilePathTest, file3) {
93 FilePath fp("/f.ext", true, "/x/y");
94 ASSERT_EQ(fp.absolute(), "/f.ext");
95 ASSERT_EQ(fp.relative("/a/b"), "../../f.ext");
96 }
97
98
9299 TEST(FilePathTest, autodetect) {
93100 FileSystem::chdir(SRCDIR);
94101 FilePath fp1("FilePathTest.cpp");
95102 ASSERT_TRUE(fp1.isFile());
96103 ASSERT_FALSE(fp1.empty());
97104 string cwd = FileSystem::getcwd();
105 #ifdef _WIN32
106 if (cwd.length() >=2 && isalpha(cwd[0]) && cwd[1] == ':')
107 cwd[0] = tolower(cwd[0]);
108 #endif
98109 ASSERT_EQ(fp1.absolute(), cwd + "/FilePathTest.cpp") << "fp1=" << fp1.absolute();
99110
100111 FilePath fp2("");
101112 ASSERT_FALSE(fp2.isFile());
102113 ASSERT_FALSE(fp2.empty());
103 ASSERT_EQ(fp2.absolute(), FileSystem::getcwd());
114 ASSERT_EQ(fp2.absolute(), cwd);
104115 }
2222 #include "Font.hpp"
2323 #include "FontManager.hpp"
2424
25 #ifndef SRCDIR
26 #define SRCDIR "."
27 #endif
28
29
2530 class FontManagerTest : public ::testing::Test {
2631 public:
2732 FontManagerTest () : fm(FontManager::instance()) {
2833 fm.registerFont(10, "cmr10", 1274110073, 10, 10);
2934 fm.registerFont(11, "cmr10", 1274110073, 10, 12);
3035 fm.registerFont( 9, "cmr10", 1274110073, 10, 14);
36 fm.registerFont(12, SRCDIR"/data/lmmono12-regular.otf", 0, 12, _fontStyle, Color(.0, .0, 1.0));
3137 }
3238
3339 protected:
3440 FontManager &fm;
41 FontStyle _fontStyle;
3542 };
3643
3744
3946 EXPECT_EQ(fm.fontID(10), 0);
4047 EXPECT_EQ(fm.fontID(11), 1);
4148 EXPECT_EQ(fm.fontID(9), 2);
49 EXPECT_EQ(fm.fontID(12), 3);
4250 EXPECT_EQ(fm.fontID(1), -1);
4351 }
4452
4553
46 TEST_F(FontManagerTest, font_ID2) {
54 TEST_F(FontManagerTest, fontID2) {
4755 EXPECT_EQ(fm.fontID("cmr10"), 0);
4856 }
4957
5361 EXPECT_TRUE(f1);
5462 EXPECT_EQ(f1->name(), "cmr10");
5563 EXPECT_TRUE(dynamic_cast<const PhysicalFontImpl*>(f1));
64 EXPECT_EQ(f1->color(), Color::BLACK);
5665
5766 const Font *f2 = fm.getFont(11);
5867 EXPECT_TRUE(f2);
6069 EXPECT_EQ(f2->name(), "cmr10");
6170 EXPECT_TRUE(dynamic_cast<const PhysicalFontProxy*>(f2));
6271 EXPECT_EQ(f2->uniqueFont(), f1);
72 EXPECT_EQ(f2->color(), Color::BLACK);
73
74 const Font *f3 = fm.getFont(12);
75 EXPECT_TRUE(f3);
76 EXPECT_NE(f2, f3);
77 EXPECT_EQ(f3->name(), "nf0");
78 EXPECT_TRUE(dynamic_cast<const NativeFontImpl*>(f3));
79 EXPECT_TRUE(dynamic_cast<const PhysicalFont*>(f3));
80 EXPECT_EQ(f3->uniqueFont(), f3);
81 EXPECT_EQ(f3->color(), Color(.0, .0, 1.0));
82 }
83
84
85 TEST_F(FontManagerTest, font_cast) {
86 const Font *f1 = fm.getFont(10);
87 EXPECT_TRUE(f1);
88 EXPECT_EQ(font_cast<const PhysicalFont*>(f1), f1);
89 EXPECT_EQ(font_cast<const NativeFont*>(f1), nullptr);
90 EXPECT_EQ(font_cast<const VirtualFont*>(f1), nullptr);
91
92 const Font *f2 = fm.getFont(11);
93 EXPECT_TRUE(f2);
94 EXPECT_EQ(font_cast<const PhysicalFont*>(f2), f2);
95 EXPECT_EQ(font_cast<const NativeFont*>(f1), nullptr);
96 EXPECT_EQ(font_cast<const VirtualFont*>(f1), nullptr);
97
98 const Font *f3 = fm.getFont(12);
99 EXPECT_TRUE(f3);
100 EXPECT_EQ(font_cast<const PhysicalFont*>(f3), f3);
101 EXPECT_EQ(font_cast<const NativeFont*>(f3), f3);
102 EXPECT_EQ(font_cast<const VirtualFont*>(f3), nullptr);
63103 }
64104
65105
66106 TEST_F(FontManagerTest, getFontById) {
67107 EXPECT_EQ(fm.getFont(10), fm.getFontById(0));
68108 EXPECT_EQ(fm.getFont("cmr10"), fm.getFontById(0));
109 EXPECT_EQ(fm.getFont(12), fm.getFontById(3));
69110 }
70
4747
4848
4949 TEST(MatrixTest, construct2) {
50 Matrix m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
50 Matrix m1{1, 2, 3, 4, 5, 6, 7, 8, 9};
5151 for (int row=0; row < 3; row++)
5252 for (int col=0; col < 3; col++)
5353 ASSERT_EQ(m1.get(row, col), 3*row+col+1) << "row=" << row << ", col=" << col;
5454
55 Matrix m2 = {1, 2, 3, 4, 5, 6};
55 Matrix m2{1, 2, 3, 4, 5, 6};
5656 for (int row=0; row < 2; row++)
5757 for (int col=0; col < 3; col++)
5858 ASSERT_EQ(m2.get(row, col), 3*row+col+1) << "row=" << row << ", col=" << col;
5151 void rotate (vector<double> &p) override {print("rotate", p);}
5252 void save(std::vector<double> &p) override {print("save", p);}
5353 void scale (vector<double> &p) override {print("scale", p);}
54 void setalphaisshape (vector<double> &p) override {print("setalphaisshape", p);}
5455 void setblendmode (vector<double> &p) override {print("setblendmode", p);}
5556 void setcolorspace (vector<double> &p) override {print("setcolorspace", p);}
5657 void setcmykcolor (vector<double> &p) override {print("setcmykcolor", p);}
5859 void setfillconstantalpha (vector<double> &p) override {print("setfillconstantalpha", p);}
5960 void setgray (vector<double> &p) override {print("setgray", p);}
6061 void sethsbcolor (vector<double> &p) override {print("sethsbcolor", p);}
61 void setisshapealpha (vector<double> &p) override {print("setisshapealpha", p);}
6262 void setlinecap (vector<double> &p) override {print("setlinecap", p);}
6363 void setlinejoin (vector<double> &p) override {print("setlinejoin", p);}
6464 void setlinewidth (vector<double> &p) override {print("setlinewidth", p);}
104104 actions.clear();
105105
106106 psi.execute("grestore ");
107 EXPECT_EQ(actions.result(), "setmatrix 1 0 0 1 0 0;applyscalevals 1 1 1;setlinewidth 1;setlinecap 0;setlinejoin 0;setmiterlimit 10;setcolorspace 0;setrgbcolor 0 0 0;setdash 0;grestore;");
107 if (psi.hasFullOpacitySupport())
108 EXPECT_EQ(actions.result(), "setmatrix 1 0 0 1 0 0;applyscalevals 1 1 1;setlinewidth 1;setlinecap 0;setlinejoin 0;setmiterlimit 10;setblendmode 0;setalphaisshape 0;setstrokeconstantalpha 1;setfillconstantalpha 1;setcolorspace 0;setrgbcolor 0 0 0;setdash 0;grestore;");
109 else
110 EXPECT_EQ(actions.result(), "setmatrix 1 0 0 1 0 0;applyscalevals 1 1 1;setlinewidth 1;setlinecap 0;setlinejoin 0;setmiterlimit 10;setcolorspace 0;setrgbcolor 0 0 0;setdash 0;grestore;");
108111 actions.clear();
109112
110113 psi.execute("1 setlinecap 5 setmiterlimit 0 1 0 setrgbcolor gsave 0 setlinecap 10 setmiterlimit ");
112115 actions.clear();
113116
114117 psi.execute("grestore ");
115 EXPECT_EQ(actions.result(), "setmatrix 1 0 0 1 0 0;applyscalevals 1 1 1;setlinewidth 1;setlinecap 1;setlinejoin 0;setmiterlimit 5;setcolorspace 0;setrgbcolor 0 1 0;setdash 0;grestore;");
118 if (psi.hasFullOpacitySupport())
119 EXPECT_EQ(actions.result(), "setmatrix 1 0 0 1 0 0;applyscalevals 1 1 1;setlinewidth 1;setlinecap 1;setlinejoin 0;setmiterlimit 5;setblendmode 0;setalphaisshape 0;setstrokeconstantalpha 1;setfillconstantalpha 1;setcolorspace 0;setrgbcolor 0 1 0;setdash 0;grestore;");
120 else
121 EXPECT_EQ(actions.result(), "setmatrix 1 0 0 1 0 0;applyscalevals 1 1 1;setlinewidth 1;setlinecap 1;setlinejoin 0;setmiterlimit 5;setcolorspace 0;setrgbcolor 0 1 0;setdash 0;grestore;");
116122 }
117123
118124
153153 handler.processSpecial("pa", "1000 0");
154154 handler.processSpecial("fp");
155155 EXPECT_EQ(recorder.getXMLSnippet(),
156 "<polyline fill='none' stroke-linecap='round' points='0,0 72,72 72,0' stroke='#000' stroke-width='1'/>"
156 "<polyline fill='none' stroke-linecap='round' points='0 0 72 72 72 0' stroke='#000'/>"
157157 );
158158 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
159159 EXPECT_LT(handler.grayLevel(), 0);
168168 handler.processSpecial("pa", "0 0");
169169 handler.processSpecial("fp");
170170 EXPECT_EQ(recorder.getXMLSnippet(),
171 "<polygon fill='none' points='0,0 72,72 72,0' stroke='#000' stroke-width='1'/>"
171 "<polygon fill='none' points='0 0 72 72 72 0' stroke='#000'/>"
172172 );
173173 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
174174 EXPECT_LT(handler.grayLevel(), 0);
183183 handler.processSpecial("wh");
184184 handler.processSpecial("fp");
185185 EXPECT_EQ(recorder.getXMLSnippet(),
186 "<polygon fill='#fff' points='0,0 72,72 72,0' stroke='#000' stroke-width='1'/>"
186 "<polygon fill='#fff' points='0 0 72 72 72 0' stroke='#000'/>"
187187 );
188188 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
189189 EXPECT_LT(handler.grayLevel(), 0);
195195 handler.processSpecial("wh");
196196 handler.processSpecial("ip");
197197 EXPECT_EQ(recorder.getXMLSnippet(),
198 "<polygon fill='#fff' points='0,0 72,72 72,0'/>"
198 "<polygon fill='#fff' points='0 0 72 72 72 0'/>"
199199 );
200200 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
201201 EXPECT_LT(handler.grayLevel(), 0);
210210 handler.processSpecial("wh");
211211 handler.processSpecial("da", "2");
212212 EXPECT_EQ(recorder.getXMLSnippet(),
213 "<polygon fill='#fff' points='0,0 72,72 72,0' stroke='#000' stroke-width='1' stroke-dasharray='144'/>"
213 "<polygon fill='#fff' points='0 0 72 72 72 0' stroke='#000' stroke-dasharray='144'/>"
214214 );
215215 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
216216 EXPECT_LT(handler.grayLevel(), 0);
226226 handler.processSpecial("wh");
227227 handler.processSpecial("dt", "2 2");
228228 EXPECT_EQ(recorder.getXMLSnippet(),
229 "<polygon fill='#fff' points='0,0 72,72 72,0' stroke='#000' stroke-width='36' stroke-dasharray='36 144'/>"
229 "<polygon fill='#fff' points='0 0 72 72 72 0' stroke='#000' stroke-width='36' stroke-dasharray='36 144'/>"
230230 );
231231 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
232232 EXPECT_LT(handler.grayLevel(), 0);
238238 handler.processSpecial("pa", "1000 1000");
239239 handler.processSpecial("sp");
240240 EXPECT_EQ(recorder.getXMLSnippet(),
241 "<polyline fill='none' stroke-linecap='round' points='0,0 72,72' stroke='#000' stroke-width='1'/>"
241 "<polyline fill='none' stroke-linecap='round' points='0 0 72 72' stroke='#000'/>"
242242 );
243243 recorder.clear();
244244 handler.processSpecial("pa", "0 0");
249249 handler.processSpecial("pa", "1000 500");
250250 handler.processSpecial("sp");
251251 EXPECT_EQ(recorder.getXMLSnippet(),
252 "<path fill='none' d='M0 0L36 36Q72 72 90 54T126 54T180 108T144 90L72 36' stroke='#000' stroke-width='1'/>"
252 "<path fill='none' d='M0 0L36 36Q72 72 90 54T126 54T180 108T144 90L72 36' stroke='#000'/>"
253253 );
254254 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
255255 EXPECT_LT(handler.grayLevel(), 0);
265265 handler.processSpecial("pa", "0 0");
266266 handler.processSpecial("sp", "1");
267267 EXPECT_EQ(recorder.getXMLSnippet(),
268 "<path fill='none' d='M0 0L36 36Q72 72 90 54T126 54T180 108T108 72Z' stroke='#000' stroke-width='1' stroke-dasharray='72'/>"
268 "<path fill='none' d='M0 0L36 36Q72 72 90 54T126 54T180 108T108 72Z' stroke='#000' stroke-dasharray='72'/>"
269269 );
270270 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
271271 EXPECT_LT(handler.grayLevel(), 0);
281281 handler.processSpecial("pa", "1000 500");
282282 handler.processSpecial("sp", "-1");
283283 EXPECT_EQ(recorder.getXMLSnippet(),
284 "<path fill='none' d='M0 0L36 36Q72 72 90 54T126 54T180 108T144 90L72 36' stroke='#000' stroke-width='1' stroke-dasharray='1 72'/>"
284 "<path fill='none' d='M0 0L36 36Q72 72 90 54T126 54T180 108T144 90L72 36' stroke='#000' stroke-dasharray='1 72'/>"
285285 );
286286 EXPECT_DOUBLE_EQ(handler.penwidth(), 1.0);
287287 EXPECT_LT(handler.grayLevel(), 0);
291291 TEST_F(TpicSpecialTest, stroke_ellipse) {
292292 handler.processSpecial("ar", "0 0 500 500 0 7");
293293 EXPECT_EQ(recorder.getXMLSnippet(),
294 "<circle cx='0' cy='0' r='36' stroke-width='1' stroke='#000' fill='none'/>"
294 "<circle cx='0' cy='0' r='36' stroke='#000' fill='none'/>"
295295 );
296296 recorder.clear();
297297 handler.processSpecial("ar", "0 0 1000 500 0 7");
298298 EXPECT_EQ(recorder.getXMLSnippet(),
299 "<ellipse cx='0' cy='0' rx='72' ry='36' stroke-width='1' stroke='#000' fill='none'/>"
299 "<ellipse cx='0' cy='0' rx='72' ry='36' stroke='#000' fill='none'/>"
300300 );
301301 recorder.clear();
302302 handler.processSpecial("pn", "100");
311311 handler.processSpecial("bk");
312312 handler.processSpecial("ia", "0 0 500 500 0 7");
313313 EXPECT_EQ(recorder.getXMLSnippet(),
314 "<circle cx='0' cy='0' r='36' fill='#000'/>"
314 "<circle cx='0' cy='0' r='36'/>"
315315 );
316316 recorder.clear();
317317 handler.processSpecial("bk");
318318 handler.processSpecial("ia", "0 0 1000 500 0 7");
319319 EXPECT_EQ(recorder.getXMLSnippet(),
320 "<ellipse cx='0' cy='0' rx='72' ry='36' fill='#000'/>"
320 "<ellipse cx='0' cy='0' rx='72' ry='36'/>"
321321 );
322322 recorder.clear();
323323 handler.processSpecial("pn", "100");
332332 TEST_F(TpicSpecialTest, stroke_arc) {
333333 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(math::PI/4));
334334 EXPECT_EQ(recorder.getXMLSnippet(),
335 "<path d='M72 0A72 36 0 0 1 50.91 25.46' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
335 "<path d='M72 0A72 36 0 0 1 50.91 25.46' stroke='#000' stroke-linecap='round' fill='none'/>"
336336 );
337337 recorder.clear();
338338 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(math::PI/2));
339339 EXPECT_EQ(recorder.getXMLSnippet(),
340 "<path d='M72 0A72 36 0 0 1 0 36' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
340 "<path d='M72 0A72 36 0 0 1 0 36' stroke='#000' stroke-linecap='round' fill='none'/>"
341341 );
342342 recorder.clear();
343343 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(3*math::PI/4));
344344 EXPECT_EQ(recorder.getXMLSnippet(),
345 "<path d='M72 0A72 36 0 0 1-50.91 25.46' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
345 "<path d='M72 0A72 36 0 0 1-50.91 25.46' stroke='#000' stroke-linecap='round' fill='none'/>"
346346 );
347347 recorder.clear();
348348 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(math::PI));
349349 EXPECT_EQ(recorder.getXMLSnippet(),
350 "<path d='M72 0A72 36 0 1 1-72 0' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
350 "<path d='M72 0A72 36 0 1 1-72 0' stroke='#000' stroke-linecap='round' fill='none'/>"
351351 );
352352 recorder.clear();
353353 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(5*math::PI/4));
354354 EXPECT_EQ(recorder.getXMLSnippet(),
355 "<path d='M72 0A72 36 0 1 1-50.91-25.46' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
355 "<path d='M72 0A72 36 0 1 1-50.91-25.46' stroke='#000' stroke-linecap='round' fill='none'/>"
356356 );
357357 recorder.clear();
358358 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(3*math::PI/2));
359359 EXPECT_EQ(recorder.getXMLSnippet(),
360 "<path d='M72 0A72 36 0 1 1 0-36' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
360 "<path d='M72 0A72 36 0 1 1 0-36' stroke='#000' stroke-linecap='round' fill='none'/>"
361361 );
362362 recorder.clear();
363363 handler.processSpecial("ar", "0 0 1000 500 0 "+to_string(-3*math::PI/2));
364364 EXPECT_EQ(recorder.getXMLSnippet(),
365 "<path d='M72 0A72 36 0 0 1 0 36' stroke-width='1' stroke='#000' stroke-linecap='round' fill='none'/>"
365 "<path d='M72 0A72 36 0 0 1 0 36' stroke='#000' stroke-linecap='round' fill='none'/>"
366366 );
367367 }
368368
1212 frktest.dvi \
1313 frktest-nf-cmp.svg \
1414 frktest-wf-cmp.svg \
15 lmmono12-regular.otf \
1516 sample.dvi \
1617 sample-nf-cmp.svg \
1718 sample.sfd \
00 #include <winver.h>
11
2 #define DVISVGM_VERSION 2,11,1,0
3 #define DVISVGM_VERSION_STR "2.11.1\0"
2 #define DVISVGM_VERSION 2,12,0,0
3 #define DVISVGM_VERSION_STR "2.12\0"
44
55 VS_VERSION_INFO VERSIONINFO
66 FILEVERSION DVISVGM_VERSION
208208 <ClCompile Include="..\src\EllipticalArc.cpp" />
209209 <ClCompile Include="..\src\HashFunction.cpp" />
210210 <ClCompile Include="..\src\ImageToSVG.cpp" />
211 <ClCompile Include="..\src\Opacity.cpp" />
211212 <ClCompile Include="..\src\optimizer\AttributeExtractor.cpp" />
212213 <ClCompile Include="..\src\optimizer\GroupCollapser.cpp" />
213214 <ClCompile Include="..\src\optimizer\RedundantElementRemover.cpp" />
287288 <ClCompile Include="..\src\SVGCharHandler.cpp" />
288289 <ClCompile Include="..\src\SVGCharHandlerFactory.cpp" />
289290 <ClCompile Include="..\src\SVGCharPathHandler.cpp" />
291 <ClCompile Include="..\src\SVGElement.cpp" />
290292 <ClCompile Include="..\src\SVGSingleCharTextHandler.cpp" />
291293 <ClCompile Include="..\src\SVGCharTspanTextHandler.cpp" />
292294 <ClCompile Include="..\src\SVGOutput.cpp" />
328330 <ClInclude Include="..\src\HashFunction.hpp" />
329331 <ClInclude Include="..\src\ImageToSVG.hpp" />
330332 <ClInclude Include="..\src\MD5HashFunction.hpp" />
333 <ClInclude Include="..\src\Opacity.hpp" />
331334 <ClInclude Include="..\src\optimizer\AttributeExtractor.hpp" />
332335 <ClInclude Include="..\src\optimizer\DependencyGraph.hpp" />
333336 <ClInclude Include="..\src\optimizer\GroupCollapser.hpp" />
373376 <ClInclude Include="..\src\SVGCharHandler.hpp" />
374377 <ClInclude Include="..\src\SVGCharHandlerFactory.hpp" />
375378 <ClInclude Include="..\src\SVGCharPathHandler.hpp" />
379 <ClInclude Include="..\src\SVGElement.hpp" />
376380 <ClInclude Include="..\src\SVGSingleCharTextHandler.hpp" />
377381 <ClInclude Include="..\src\SVGCharTspanTextHandler.hpp" />
378382 <ClInclude Include="..\src\SVGOutput.hpp" />
331331 <ClCompile Include="..\src\optimizer\TextSimplifier.cpp">
332332 <Filter>Source Files\optimizer</Filter>
333333 </ClCompile>
334 <ClCompile Include="..\src\Opacity.cpp">
335 <Filter>Source Files</Filter>
336 </ClCompile>
337 <ClCompile Include="..\src\SVGElement.cpp">
338 <Filter>Source Files</Filter>
339 </ClCompile>
334340 </ItemGroup>
335341 <ItemGroup>
336342 <ClInclude Include="..\src\BgColorSpecialHandler.hpp">
674680 </ClInclude>
675681 <ClInclude Include="..\src\optimizer\TextSimplifier.hpp">
676682 <Filter>Header Files\optimizer</Filter>
683 </ClInclude>
684 <ClInclude Include="..\src\Opacity.hpp">
685 <Filter>Header Files</Filter>
686 </ClInclude>
687 <ClInclude Include="..\src\SVGElement.hpp">
688 <Filter>Header Files</Filter>
677689 </ClInclude>
678690 </ItemGroup>
679691 <ItemGroup>
157157 <ItemGroup>
158158 <ClCompile Include="src\autofit\autofit.c" />
159159 <ClCompile Include="src\base\ftcid.c" />
160 <ClCompile Include="src\base\ftfntfmt.c" />
161160 <ClCompile Include="src\bdf\bdf.c" />
162161 <ClCompile Include="src\cff\cff.c" />
163162 <ClCompile Include="src\base\ftbase.c" />
172171 <ClCompile Include="src\lzw\ftlzw.c" />
173172 <ClCompile Include="src\base\ftstroke.c" />
174173 <ClCompile Include="src\base\ftsystem.c" />
174 <ClCompile Include="src\sdf\ftsdfcommon.c" />
175 <ClCompile Include="src\sdf\sdf.c" />
175176 <ClCompile Include="src\smooth\smooth.c" />
176177 <ClCompile Include="src\base\ftbbox.c" />
177178 <ClCompile Include="src\base\ftmm.c" />
179180 <ClCompile Include="src\base\ftsynth.c" />
180181 <ClCompile Include="src\base\fttype1.c" />
181182 <ClCompile Include="src\base\ftwinfnt.c" />
182 <ClCompile Include="src\base\ftlcdfil.c" />
183183 <ClCompile Include="src\base\ftgxval.c" />
184184 <ClCompile Include="src\base\ftotval.c" />
185185 <ClCompile Include="src\base\ftpatent.c" />
7979 <ClCompile Include="src\base\ftxf86.c">
8080 <Filter>Source Files\FT_MODULES</Filter>
8181 </ClCompile>
82 <ClCompile Include="src\base\ftlcdfil.c">
83 <Filter>Source Files\FT_MODULES</Filter>
84 </ClCompile>
8582 <ClCompile Include="src\base\ftgxval.c">
8683 <Filter>Source Files\FT_MODULES</Filter>
8784 </ClCompile>