Commit 2be2457b authored by Anton Gladky's avatar Anton Gladky

New upstream version 1.2.3+dsfg1

parents
doc/sphinx/_build/
doc/html/
As SVG++ is a header-only library, no installation is required. Just add path to 'include' subfolder to your project include dirs.
Boost includes should also be avaiable.
'src' folder contains tests, demos and samples. To build them you need:
1. Get CMake installed (http://www.cmake.org/).
2. Run 'cmake -G <generator-name> -D BOOST_ROOT=<path to Boost root> <path to svgpp/src>' from folder that will contain
build files.
<generator-name> may be "Unix Makefiles" for GCC, Clang etc and "Visual Studio 12" for Visual Studio 2013. See CMake reference for
details about generators.
3a. For make-based build run 'make'
3b. For Visual Studio build open created .sln file and build solution.
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
SVG++ library 1.2.1
=======================
Please visit http://svgpp.org/ for description, tutorial and reference.
#### About
SVG++ library can be thought of as a framework, containing parsers for various SVG syntaxes, adapters that simplify
handling of parsed data and a lot of other utilities and helpers for the most common tasks.
Take a look at [Tutorial](http://svgpp.org/lesson01.html) to get the idea about what it is.
#### SVG++ features
* Is a header-only library
* Can be used with any XML parser
* Compile time configured - no virtual functions
* Minimal runtime overhead - you pay only for what you get
* Has wide range of possible uses:
* Fully functional, conforming SVG viewers
* Simple in-app SVG rasterizers
* Import modules of vector editing software
* Implementing path-only input of SVG format with minimal efforts in any graphics or math software
* Requires only Boost library itself (demo, tests and sample have their own requirements)
* Compatible with C++03, but requires conforming implementation, due to heavy usage of templates
* Supports any character type (`char` and `wchar_t`). Other (Unicode from C++11) were not tested, but should be ok.
#### SVG 1.1 features covered by SVG++
* Path data - parsing, simplification
* Transform list - parsing, simplification
* Color, including ICC color - parsing
* Lengths - highly configurable handling of SVG lengths, full support of all SVG length units
* Basic shapes - optional conversion to path
* Style attribute - parsing, taking in account differences in parsing presentation attributes and style properties
* Automatic marker positions calculation
* Viewport and viewbox handling
#### SVG++ is NOT
* An SVG viewer or library that produces raster image from SVG
(though C++ demo app included, which renders SVG with [AGG](http://antigrain.com), GDI+ or [Skia](https://code.google.com/p/skia/) graphics library)
* Renders anything itself
#### What's new
* SVG++ update 1.2.1 fixes handling text content inside `<a>` element that is a child of `<text>` element.
* SVG++ update 1.2 focuses on reducing compiler memory usage by allowing separation
of template heavy Boost.Spirit code to other compilation unit.
#### How to Help
* *CSS Processor.* Processing CSS is outside of SVG++ scope, but it'll be nice to have some generic implementation of CSS
selectors to use at preprocessing stage.
* *SVG Filter Effects.* Some filters are already implemented in demo application but the complete solution requires more efforts.
Generic enough parts may fit in SVG++ source base. Usage of Boost.GIL may make the code more reusable.
* *Animation.* Implementation of animation in the demo application. Probably some utilities may get to SVG++.
* Try to reduce compiler memory usage by switching to some C++11 metaprogramming library (brigand?).
Evaluate upgrade to Boost.Spirit 3.0.
* Extend demo with Cairo support?
* More wrappers for XML parsers?
* Evolve demo to fully functional SVG rendering component for one of the graphic engines.
A major gap in demo's SVG implementation is text and font support. It'll be too complex to make it cross-engine, but
for the single engine chosen and probably with third-party libraries like FreeType it's pretty feasible.
cmake_minimum_required (VERSION 2.6)
project(Documentation)
add_custom_target(
Documentation ALL
SOURCES
advanced.rst
basic_shapes.rst
color.rst
document_traversal.rst
error.rst
faq.rst
help.rst
index.rst
installation.rst
iri.rst
length.rst
lesson01.rst
license.rst
markers.rst
overview.rst
path.rst
reference.rst
text.rst
transform.rst
value.rst
value_parser.rst
viewport.rst
conf.py
)
if (WIN32)
add_custom_command(TARGET Documentation
POST_BUILD
COMMAND make.bat html
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif (WIN32)
\ No newline at end of file
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
BUILDDIR_html = ../html
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR_html)
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR_html)."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SVG.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SVG.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/SVG"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SVG"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
Advanced Topics
=====================
Adding new XML parser
-------------------------------------------------
Implementing support for the new XML parser requires creating new specializations for
``svgpp::policy::xml::attribute_iterator`` and ``svgpp::policy::xml::element_iterator``
class templates using one of existing implementations in folder ``include/svgpp/policy/xml`` as a sample.
Understanding following types may cause some difficulties::
template<>
struct attribute_iterator<CustomXMLAttribute>
{
typedef /* ... */ string_type;
typedef /* ... */ attribute_name_type;
typedef /* ... */ attribute_value_type;
typedef /* ... */ saved_value_type;
/* ... */
};
``string_type``
A model of
`Forward Range <http://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/concepts/forward_range.html>`_,
with items of some character type (``char``, ``wchar_t``, ``char16_t``, ``char32_t``).
Easy solution is to use ``boost::iterator_range<CharT const *>``.
``attribute_name_type``
Copy constructible type, for which exists method
``string_type attribute_iterator::get_string_range(attribute_name_type const &)``.
Value of ``attribute_name_type`` is used only before ``attribute_iterator``
gets incremented and may become invalid after that.
``attribute_value_type``
Copy constructible type, for which exists method
``string_type attribute_iterator::get_string_range(attribute_value_type const &)``.
Value of ``attribute_value_type`` must be accessible independently of ``attribute_iterator`` state changes.
``saved_value_type``
Copy constructible type, for which exists method
``attribute_value_type attribute_iterator::get_value(saved_value_type const &)``.
Object of this type must as efficiently as possible save attribute value that may be not requested.
For example XML parser provides access to XML attribute like this::
class XmlAttribute
{
public:
std::string getValue() const;
};
In this case ``saved_value_type`` may be defined as ``XmlAttribute const *``, instead of ``std::string``,
to avoid expences of creating and coping string that may not be requested later.
.. include:: value_parser.rst
\ No newline at end of file
.. _Associative Sequence: http://www.boost.org/doc/libs/1_56_0/libs/mpl/doc/refmanual/associative-sequence.html
.. _basic_shapes:
Basic Shapes
====================
`Basic shapes <http://www.w3.org/TR/SVG/shapes.html>`_ (**rect**, **circle**, **ellipse**, **line**, **polyline**
and **polygon**) can be automatically converted to *path* commands.
Basic Shapes Policy Concept
--------------------------------
::
struct basic_shapes_policy
{
typedef /* Associative Sequence */ convert_to_path;
typedef /* Associative Sequence */ collect_attributes;
static const bool convert_only_rounded_rect_to_path = /* true or false */;
};
``convert_to_path``
`Associative Sequence`_ (e.g. ``boost::mpl::set``), containing SVG *basic shapes* elements tags,
which will be converted to **path**. Generated **path** uses *Path Policy* and *Path Events Policy* settings.
``collect_attributes``
`Associative Sequence`_, containing any of **rect**, **circle**, **ellipse** or **line** element tags.
Geometry of elements included in the sequence will be passed with single call instead of separate attribute
handling (see *Basic Shapes Events Policy*).
``convert_only_rounded_rect_to_path``
If static member constant ``convert_only_rounded_rect_to_path`` equals to ``true`` and
``tag::element::rect`` is included in ``convert_to_path`` sequence,
then only rounded rectangles will be converted to **path**,
and regular rectangles will be handle like if ``tag::element::rect`` is included in ``collect_attributes``.
In ``document_traversal`` processing of attributes describing *basic shapes* geometry (**x**, **y**,
**r** etc) must be :ref:`enabled <processed_attributes>`,
i. e. they must be included in ``processed_attributes`` or excluded from ``ignored_attributes``.
`Associative Sequence`_ ``traits::shapes_attributes_by_element`` contains tags of all such attributes
for *basic shapes*.
:ref:`Named class template parameter <named-params>` for *Basic Shapes Policy* is ``basic_shapes_policy``.
Basic Shapes Events Policy Concept
-------------------------------------
::
struct basic_shapes_events_policy
{
static void set_rect(Context & context, Coordinate x, Coordinate y, Coordinate width, Coordinate height,
Coordinate rx, Coordinate ry);
static void set_rect(Context & context, Coordinate x, Coordinate y, Coordinate width, Coordinate height);
static void set_line(Context & context, Coordinate x1, Coordinate y1, Coordinate x2, Coordinate y2);
static void set_circle(Context & context, Coordinate cx, Coordinate cy, Coordinate r);
static void set_ellipse(Context & context, Coordinate cx, Coordinate cy, Coordinate rx, Coordinate ry);
};
*Basic Shapes Events Policy* is used for *basic shapes* (except **polyline** and **polygon**) elements,
that are listed in ``collect_attributes`` field of *Basic Shapes Policy*.
Adapters that implement these conversions, use ``length_to_user_coordinate`` method of *Length Factory*
to get *user coordinates* value by *length*. These adapters passes default values if attributes are missing
and check correctness of attributes. If value disables rendering of the element
according to SVG specification, then *Basic Shapes Events Policy* methods aren't called,
and if an attribute has negative value that is not permitted by specification,
then ``negative_value`` function of :ref:`Error Policy <error_policy>` is called.
Default *Basic Shapes Events Policy* (``policy::basic_shapes_events::forward_to_method``)
forwards calls to its static methods to ``context`` object methods::
struct forward_to_method
{
template<class Coordinate>
static void set_rect(Context & context, Coordinate x, Coordinate y, Coordinate width, Coordinate height,
Coordinate rx, Coordinate ry)
{
context.set_rect(x, y, width, height, rx, ry);
}
/*...*/
};
:ref:`Named class template parameter <named-params>` for *Basic Shapes Events Policy* is ``basic_shapes_events_policy``.
.. _color-section:
Color
========
*Color Factory* defines what type corresponds to `<color> <http://www.w3.org/TR/SVG/types.html#DataTypeColor>`_
SVG type and how values of this type are created from SVG color description.
*ICC Color Factory* defines what type corresponds to
`<icccolor> <http://www.w3.org/TR/SVG/types.html#DataTypeICCColor>`_ SVG type
and how values of this type are created from SVG ICC color description.
Color Factory Concept
------------------------
::
struct color_factory
{
typedef /* ... */ color_type;
typedef /* floating point number type */ percentage_type;
static color_type create(unsigned char r, unsigned char g, unsigned char b);
static color_type create_from_percent(percentage_type r, percentage_type g, percentage_type b);
};
``create_from_percent`` is called when color components are set as percents (e.g. **rgb(100%,100%,100%)**).
Percent values are passed as is, i.e. **100%** is passed as ``100``.
In other cases ``create`` function is called with integer component values in range ``0`` to ``255``.
`Recognized color keyword names <http://www.w3.org/TR/SVG/types.html#ColorKeywords>`_
are converted to corresponding component values by SVG++ library.
`System colors <http://www.w3.org/TR/2008/REC-CSS2-20080411/ui.html#system-colors>`_ aren't handled yet.
:ref:`Named class template parameter <named-params>` for *Color Factory* is ``color_factory``.
Integer Color Factory
-------------------------
SVG++ by default uses ``factory::color::integer<>`` as *Color Factory*.
This factory returns color packed in ``int`` value: Red component in 3rd byte,
Green in 2nd, Blue in 1st (least significant).
Component offsets and number type can be configured.
.. _icc-color-factory-section:
ICC Color Factory Concept
---------------------------
::
struct icc_color_factory
{
typedef /* floating point number type */ component_type;
typedef /* ... */ icc_color_type;
typedef /* ... */ builder_type;
template<class Iterator>
void set_profile_name(builder_type &, typename boost::iterator_range<Iterator> const &) const;
void append_component_value(builder_type &, component_type) const;
icc_color_type create_icc_color(builder_type const &) const;
};
``icc_color_type`` is a type that will be passed to the user code.
``builder_type`` is used as a temporary object during building ``icc_color_type``
from color profile name and some components values.
Pseudo-code that illustrates usage of *ICC Color Factory* from inside SVG++
to parse **icc-color(profile1 0.75, 0.15, 0.25)** value::
void parse_icc(icc_color_factory const & factory)
{
icc_color_factory::builder_type builder;
factory.set_profile_name(builder, boost::as_literal("profile1"));
factory.append_component_value(builder, 0.75);
factory.append_component_value(builder, 0.15);
factory.append_component_value(builder, 0.25);
value_events_policy::set(context, attribute_tag, factory.create_icc_color(builder));
}
ICC Color Policy Concept
---------------------------
*ICC Color Policy* defines which *ICC Color Factory* instance is used for the context.
It can be used to configure *ICC Color Factory* in runtime, e.g. taking in account referenced color profiles in SVG document.
::
struct icc_color_policy
{
typedef /* ... */ context_type;
typedef /* ... */ icc_color_factory_type;
static icc_color_factory_type & icc_color_factory(Context const &);
};
Default *ICC Color Policy* returns static instance of ``factory::icc_color::stub``.
``factory::icc_color::stub`` is a model of *ICC Color Factory* that skips passed values
and returns instance of empty ``tag::skip_icc_color`` class as ICC color.
:ref:`Named class template parameter <named-params>` for *ICC Color Policy* is ``icc_color_policy``.
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# SVG++ documentation build configuration file, created by
# sphinx-quickstart on Wed Jul 23 21:06:03 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.mathjax',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'SVG++'
copyright = '2014, Oleg Maximenko'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.