├── .gitignore
├── Doc
├── circle.png
├── manual.xml
├── CMakeLists.txt
└── manual.md
├── .gitmodules
├── Examples
├── coverpage
│ ├── cover.pdf
│ ├── umdoc.xml
│ └── example.md
├── environmentscript
│ ├── myscript2
│ ├── myscript2.bat
│ ├── myscript1
│ ├── example.md
│ ├── umdoc.xml
│ └── myscript1.bat
├── tablesfigures
│ ├── circle.png
│ ├── umdoc.xml
│ └── example.md
├── tables
│ ├── umdoc.xml
│ └── example.md
├── codeblocks
│ ├── umdoc.xml
│ └── example.md
├── tablestyles
│ ├── umdoc.xml
│ └── example.md
├── headerfooter
│ ├── style.tex
│ ├── umdoc.xml
│ └── example.md
├── latexclass
│ ├── umdoc.xml
│ ├── example.md
│ └── exampleclass.cls
├── customenvironment
│ ├── umdoc.xml
│ ├── style.tex
│ └── example.md
├── md2pdf
│ └── example.md
└── CMakeLists.txt
├── Src
├── PlainGenerator.cpp
├── Reader.hpp
├── Tests
│ ├── CMakeLists.txt
│ └── Test.cpp
├── InputData.hpp
├── CMakeLists.txt
├── OutputData.cpp
├── PlainGenerator.hpp
├── TexGenerator.hpp
├── Parser.hpp
├── Generator.hpp
├── HtmlGenerator.hpp
├── Reader.cpp
├── OutputData.hpp
├── Generator.cpp
├── Main.cpp
├── HtmlGenerator.cpp
└── TexGenerator.cpp
├── Ext
└── CMakeLists.txt
├── CPackConfig.txt
├── CPackInstall.txt
├── NOTICE
├── README.md
├── conanfile.py
├── CMakeLists.txt
├── Jenkinsfile
├── CHANGELOG.md
├── LICENSE
└── CDeploy
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vscode
2 | /build*
3 |
--------------------------------------------------------------------------------
/Doc/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craflin/umdoc/HEAD/Doc/circle.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Ext/libnstd"]
2 | path = Ext/libnstd
3 | url = ../libnstd.git
4 |
--------------------------------------------------------------------------------
/Examples/coverpage/cover.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craflin/umdoc/HEAD/Examples/coverpage/cover.pdf
--------------------------------------------------------------------------------
/Examples/environmentscript/myscript2:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "* This was created by myscript2."
4 | cat <&0
5 |
--------------------------------------------------------------------------------
/Examples/tablesfigures/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craflin/umdoc/HEAD/Examples/tablesfigures/circle.png
--------------------------------------------------------------------------------
/Examples/tables/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/codeblocks/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/tablestyles/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/headerfooter/style.tex:
--------------------------------------------------------------------------------
1 |
2 | \usepackage{fancyhdr}
3 | \pagestyle{fancy}
4 | \fancyhf{}
5 | \rhead{\rightmark}
6 | \rfoot{Page \thepage}
7 |
--------------------------------------------------------------------------------
/Examples/latexclass/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/environmentscript/myscript2.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | setlocal
3 |
4 | echo * This was created by myscript2.
5 | for /F "tokens=*" %%a in ('more') do (
6 | echo %%a
7 | )
8 |
--------------------------------------------------------------------------------
/Examples/headerfooter/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Src/PlainGenerator.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "PlainGenerator.hpp"
3 |
4 | #include
5 |
6 | String PlainGenerator::escapeChar(uint32 c)
7 | {
8 | return Unicode::toString(c);
9 | }
10 |
--------------------------------------------------------------------------------
/Examples/tablesfigures/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Examples/coverpage/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Examples/customenvironment/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Examples/environmentscript/myscript1:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$1" == "html" ]; then
4 | echo ""
5 | cat <&0
6 | echo "
"
7 | else
8 | echo "\begin{verbatim}"
9 | cat <&0
10 | echo "\end{verbatim}"
11 | fi
12 |
--------------------------------------------------------------------------------
/Examples/environmentscript/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Verbatim Script *myscript1*
3 |
4 | ```myscript1
5 | This was processed by myscript1.
6 | ```
7 |
8 | # Non-Verbatim Script *myscript2*
9 |
10 | ```myscript2
11 | * This was processed by myscript2.
12 | ```
13 |
--------------------------------------------------------------------------------
/Examples/environmentscript/umdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Examples/customenvironment/style.tex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | \NewEnviron{important}{\fcolorbox{black}{red}{%
5 | \bgroup%
6 | \def\arraystretch{0}%
7 | \begin{tabular}{p{1.8cm}p{13.14cm}}%
8 | &\\[5pt]
9 | Important: & \BODY\\
10 | &\\[2pt]
11 | \end{tabular}%
12 | \egroup}\par\vspace{0.18cm}\par
13 |
14 | \setlength\leftskip{0cm}}
--------------------------------------------------------------------------------
/Examples/environmentscript/myscript1.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | setlocal
3 |
4 | if "%1" == "html" (
5 | echo ^
6 | for /F "tokens=*" %%a in ('more') do (
7 | echo %%a
8 | )
9 | echo ^
10 | ) else (
11 | echo \begin{verbatim}
12 | for /F "tokens=*" %%a in ('more') do (
13 | echo %%a
14 | )
15 | echo \end{verbatim}
16 | )
--------------------------------------------------------------------------------
/Examples/tablesfigures/example.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Here are some figures
4 |
5 | 
6 |
7 | {width=2cm}
8 |
9 | {width=20%}
10 |
11 | # Here are some tables
12 |
13 | |a|b|
14 | |c|d|
15 |
16 | Table: Example Table 1
17 |
18 | |e|f| {.grid}
19 | |g|h|
20 |
21 | Table: Example Table 2
22 |
--------------------------------------------------------------------------------
/Ext/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | set(DISABLE_ADD_SUBDIRECTORY_TEST true)
3 | macro(add_subdirectory)
4 | if(NOT DISABLE_ADD_SUBDIRECTORY_TEST OR NOT ("${ARGV0}" STREQUAL "test"))
5 | _add_subdirectory(${ARGV})
6 | endif()
7 | endmacro()
8 |
9 | add_subdirectory(libnstd)
10 |
11 | set_target_properties(nstd nstdCrypto nstdDocument nstdSocket PROPERTIES
12 | FOLDER "Ext"
13 | )
14 |
--------------------------------------------------------------------------------
/CPackConfig.txt:
--------------------------------------------------------------------------------
1 |
2 | if(CPACK_GENERATOR STREQUAL "DEB")
3 |
4 | set(CPACK_PACKAGE_CONTACT "colin.graf@sovereign-labs.com")
5 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A Markdown to LaTeX to PDF converter")
6 | set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
7 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/craflin/umdoc")
8 | set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/share/umdoc-${CPACK_PACKAGE_VERSION}")
9 |
10 | endif()
11 |
--------------------------------------------------------------------------------
/Examples/coverpage/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Lorem Ipsum
3 |
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5 |
--------------------------------------------------------------------------------
/Examples/latexclass/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Lorem Ipsum
3 |
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5 |
--------------------------------------------------------------------------------
/Examples/md2pdf/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Lorem Ipsum
3 |
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5 |
--------------------------------------------------------------------------------
/Examples/headerfooter/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Lorem Ipsum
3 |
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5 |
--------------------------------------------------------------------------------
/Doc/manual.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | \title{\bfseries \Huge umdoc\\ \Large The Universal Markdown to \emph{LaTeX} to \emph{PDF} Converter} \author{Colin Graf} \date{Version %packageVersion%\\ \today}\maketitle\thispagestyle{empty}
8 |
9 | \sloppy
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Src/Reader.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 |
6 | struct InputData;
7 |
8 | class Reader
9 | {
10 | public:
11 | Reader() : _errorLine(0), _errorColumn(0) {}
12 |
13 | bool read(const String& inputFile, InputData& inputData);
14 |
15 | int getErrorLine() const {return _errorLine;}
16 | int getErrorColumn() const {return _errorColumn;}
17 | const String& getErrorString() const {return _errorString;}
18 |
19 | private:
20 | int _errorLine;
21 | int _errorColumn;
22 | String _errorString;
23 | };
24 |
--------------------------------------------------------------------------------
/CPackInstall.txt:
--------------------------------------------------------------------------------
1 |
2 | if("${CMAKE_INSTALL_PREFIX}" MATCHES "/usr/share/umdoc-.*")
3 |
4 | #file(MAKE_DIRECTORY usr/bin)
5 | #file(CREATE_LINK "${CMAKE_INSTALL_PREFIX}/umdoc" usr/bin/umdoc)
6 |
7 | execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${CMAKE_INSTALL_PREFIX}/umdoc" umdoc)
8 | get_filename_component(CURRENT_DIR "${CMAKE_PARENT_LIST_FILE}" DIRECTORY)
9 | file(INSTALL DESTINATION usr/bin FILES "${CURRENT_DIR}/umdoc")
10 | execute_process(COMMAND "${CMAKE_COMMAND}" -E remove -f umdoc)
11 |
12 | endif()
13 |
--------------------------------------------------------------------------------
/Examples/customenvironment/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Lorem Ipsum
3 |
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5 |
6 | ```important
7 | This is some **important** text.
8 | ```
9 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 |
2 | Copyright 2017-2020 Colin Graf
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/Src/Tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | set(sources
3 | Test.cpp
4 | ../Parser.cpp
5 | ../OutputData.cpp
6 | ../TexGenerator.cpp
7 | ../Generator.cpp
8 | ../Reader.cpp
9 | )
10 |
11 |
12 | add_executable(umdoc_test ${sources})
13 | add_dependencies(umdoc_test umdoc)
14 |
15 | source_group("" FILES ${sources})
16 |
17 | target_link_libraries(umdoc_test
18 | PRIVATE libnstd::Document
19 | )
20 |
21 | target_compile_definitions(umdoc_test
22 | PRIVATE
23 | DEBUG
24 | "UMDOC_EXECUTABLE=\"$\""
25 | )
26 |
27 | target_compile_features(umdoc_test
28 | PRIVATE cxx_override
29 | )
30 |
31 | add_test(NAME umdoc_test COMMAND umdoc_test)
32 |
33 | set_target_properties(umdoc_test PROPERTIES
34 | FOLDER "Src/Tests"
35 | )
36 |
--------------------------------------------------------------------------------
/Src/InputData.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | struct InputData
9 | {
10 | struct Component
11 | {
12 | enum Type
13 | {
14 | texType,
15 | texTableOfContentsType,
16 | texListOfFiguresType,
17 | texListOfTablesType,
18 | texNewPageType,
19 | texPartType,
20 | pdfType,
21 | mdType,
22 | };
23 |
24 | Type type;
25 | String name;
26 | String filePath;
27 | String value;
28 | };
29 |
30 | struct Environment
31 | {
32 | bool verbatim;
33 | String command;
34 | };
35 |
36 | String inputFile;
37 | String className;
38 | List headerTexFiles;
39 | HashMap variables;
40 | HashMap environments;
41 | List document;
42 | };
43 |
--------------------------------------------------------------------------------
/Src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | set(sources
3 | Generator.cpp
4 | Generator.hpp
5 | HtmlGenerator.cpp
6 | HtmlGenerator.hpp
7 | InputData.hpp
8 | Main.cpp
9 | OutputData.cpp
10 | OutputData.hpp
11 | Parser.cpp
12 | Parser.hpp
13 | Reader.cpp
14 | Reader.hpp
15 | TexGenerator.cpp
16 | TexGenerator.hpp
17 | PlainGenerator.cpp
18 | PlainGenerator.hpp
19 | )
20 |
21 | add_executable(umdoc ${sources})
22 |
23 | target_link_libraries(umdoc
24 | PRIVATE libnstd::Document
25 | )
26 | target_compile_definitions(umdoc
27 | PRIVATE "VERSION=\"${PROJECT_VERSION}\""
28 | )
29 | target_compile_features(umdoc
30 | PRIVATE cxx_override
31 | )
32 |
33 | set_target_properties(umdoc PROPERTIES
34 | FOLDER "Src"
35 | )
36 |
37 | source_group("" FILES ${sources})
38 |
39 | install(TARGETS umdoc DESTINATION .)
40 |
41 | add_subdirectory(Tests)
42 |
--------------------------------------------------------------------------------
/Doc/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | find_package(LATEX COMPONENTS XELATEX REQUIRED)
3 |
4 | set(sources
5 | circle.png
6 | manual.md
7 | manual.xml
8 | )
9 |
10 | add_custom_command(
11 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/umdoc.pdf" "${CMAKE_CURRENT_BINARY_DIR}/umdoc-${PROJECT_VERSION}.pdf"
12 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/manual.xml" "--packageVersion=${PROJECT_VERSION}" -o "${CMAKE_CURRENT_BINARY_DIR}/umdoc.pdf"
13 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/umdoc.pdf" "${CMAKE_CURRENT_BINARY_DIR}/umdoc-${PROJECT_VERSION}.pdf"
14 | DEPENDS $ ${sources}
15 | )
16 |
17 | add_custom_command(
18 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/umdoc.html"
19 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/manual.xml" "--packageVersion=${PROJECT_VERSION}" -o "${CMAKE_CURRENT_BINARY_DIR}/umdoc.html"
20 | DEPENDS $ ${sources}
21 | )
22 |
23 | add_custom_target(manual ALL
24 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/umdoc.pdf"
25 | "${CMAKE_CURRENT_BINARY_DIR}/umdoc.html"
26 | SOURCES ${sources}
27 | )
28 |
29 | set_target_properties(manual PROPERTIES
30 | FOLDER "Doc"
31 | )
32 |
33 | source_group("" FILES ${sources})
34 |
35 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/umdoc.pdf" DESTINATION .)
36 |
--------------------------------------------------------------------------------
/Src/OutputData.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "OutputData.hpp"
3 | #include "Generator.hpp"
4 |
5 | String OutputData::ParagraphSegment::generate(Generator& generator) const {return generator.generate(*this);}
6 | String OutputData::TitleSegment::generate(Generator& generator) const {return generator.generate(*this);}
7 | String OutputData::SeparatorSegment::generate(Generator& generator) const {return generator.generate(*this);}
8 | String OutputData::FigureSegment::generate(Generator& generator) const {return generator.generate(*this);}
9 | String OutputData::RuleSegment::generate(Generator& generator) const {return generator.generate(*this);}
10 | String OutputData::BulletListSegment::generate(Generator& generator) const {return generator.generate(*this);}
11 | String OutputData::NumberedListSegment::generate(Generator& generator) const {return generator.generate(*this);}
12 | String OutputData::BlockquoteSegment::generate(Generator& generator) const {return generator.generate(*this);}
13 | String OutputData::EnvironmentSegment::generate(Generator& generator) const {return generator.generate(*this);}
14 | String OutputData::TableSegment::generate(Generator& generator) const {return generator.generate(*this);}
15 | String OutputData::TexSegment::generate(Generator& generator) const {return generator.generate(*this);}
16 | String OutputData::TexPartSegment::generate(Generator& generator) const {return generator.generate(*this);}
17 | String OutputData::PdfSegment::generate(Generator& generator) const {return generator.generate(*this);}
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # umdoc
3 |
4 | [](http://xaws6t1emwa2m5pr.myfritz.net:8080/job/craflin/job/umdoc/job/master/)
5 |
6 | *umdoc* is a Markdown to *LaTeX* to *PDF* converter.
7 | It is command line tool to convert a Markdown file or set of Markdown files into an input file (`.tex` file) for a *LaTeX* engine like `xelatex`, `lualatex` or `pdflatex`.
8 | The *LaTeX* engine is then launched to convert the generated file into a *PDF* document.
9 | Optional layout information written in *LaTeX* may be provided to customize the look of the generated document.
10 |
11 | ## Examples & Documentation
12 |
13 | Further documentation and a showcase of *umdoc*'s capabilities can be found in its [user manual](https://github.com/craflin/umdoc/releases/download/0.3.0/umdoc-0.3.0.pdf).
14 |
15 | ## Build Instructions
16 |
17 | To compile *umdoc* from the sources follow the following steps:
18 |
19 | ### Windows
20 |
21 | * Ensure you have Visual Studio, Git and CMake installed.
22 | * Install [MiKTeX](https://miktex.org),
23 | * Clone the Git repository.
24 | * Initialize submodules.
25 | * Use CMake to generate a solution file (`umdoc.sln`) for some version of Visual Studio.
26 | * Open the generated `umdoc.sln` file in Visual Studio.
27 | * Compile the *umdoc* project in Visual Studio.
28 |
29 | ### Linux
30 |
31 | * Ensure you have `git`, `g++` and `cmake` installed. (Ubuntu: `sudo apt-get git g++ cmake`)
32 | * Install `xelatex` with various fonts. (Ubuntu: `sudo apt-get install texlive-xetex texlive-fonts-extra`)
33 | * Clone the Git repository. `git clone git@github.com:craflin/umdoc.git`
34 | * Initialize submodules. `git submodule update --init`
35 | * Use CMake to build *umdoc*.
--------------------------------------------------------------------------------
/conanfile.py:
--------------------------------------------------------------------------------
1 | from conan import ConanFile
2 | from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
3 | from conan.tools.env import VirtualBuildEnv
4 | from conan.tools.files import load
5 | import re, os
6 |
7 | class umdocConan(ConanFile):
8 | name = "umdoc"
9 | license = "Apache-2.0"
10 | author = "Colin Graf"
11 | url = "https://github.com/craflin/umdoc"
12 | description = "Markdown to LaTeX to PDF converter"
13 | settings = "os", "compiler", "build_type", "arch"
14 | exports_sources = "Doc/*", "Examples/*", "Ext/*", "Src/*", "CDeploy", "CHANGELOG.md", "CMakeLists.txt", "CPackConfig.txt", "CPackInstall.txt", "LICENSE", "NOTICE", "README.md"
15 |
16 | def set_version(self):
17 | content = load(self, os.path.join(self.recipe_folder, "CMakeLists.txt"))
18 | self.version = re.search("project\\([^ ]* VERSION ([0-9.]*)", content).group(1)
19 |
20 | def build_requirements(self):
21 | self.tool_requires("cmake/3.30.1")
22 | self.tool_requires("ninja/1.12.1")
23 |
24 | def generate(self):
25 | ms = VirtualBuildEnv(self)
26 | ms.generate()
27 | tc = CMakeToolchain(self, generator='Ninja')
28 | tc.generate()
29 |
30 | def build(self):
31 | cmake = CMake(self)
32 | cmake.configure()
33 | cmake.build()
34 |
35 | def package(self):
36 | cmake = CMake(self)
37 | cmake.install()
38 |
39 | def layout(self):
40 | cmake_layout(self)
41 |
42 | def package_id(self):
43 | del self.info.settings.compiler
44 | del self.info.settings.build_type
45 |
46 | def package_info(self):
47 | self.cpp_info.bindirs = ['.']
48 | self.cpp_info.set_property("cmake_find_mode", "none")
49 | self.cpp_info.builddirs.append(os.path.join("lib", "cmake", "umdoc"))
50 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.1)
2 | cmake_policy(SET CMP0048 NEW)
3 |
4 | project(umdoc VERSION 0.3.0)
5 |
6 | set(CDEPLOY_NO_DEBUG_BUILD True)
7 | set(CDEPLOY_NO_COMPILER True)
8 |
9 | include(CDeploy)
10 |
11 | enable_testing()
12 |
13 | set_property(GLOBAL PROPERTY USE_FOLDERS ON)
14 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER ".cmake")
15 |
16 | if(MSVC AND NOT CMAKE_TOOLCHAIN_FILE)
17 | string(REPLACE "/MD" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
18 | string(REPLACE "/MD" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
19 | string(REPLACE "/MD" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
20 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
21 | set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MT /Os /Oy /O1 /GF /GL")
22 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MT")
23 | set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "/OPT:REF /OPT:ICF /INCREMENTAL:NO /LTCG")
24 | set(CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL "/LTCG")
25 | endif()
26 |
27 | add_subdirectory(Ext)
28 | add_subdirectory(Src)
29 | add_subdirectory(Doc)
30 | add_subdirectory(Examples)
31 |
32 | install(FILES NOTICE LICENSE DESTINATION .)
33 |
34 | if(NOT WIN32)
35 | list(APPEND CPACK_GENERATOR "DEB")
36 | set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CPackConfig.txt")
37 | set(CPACK_STRIP_FILES True)
38 | install(SCRIPT CPackInstall.txt)
39 | endif()
40 |
41 | include(CPack)
42 |
43 | if(WIN32)
44 | deploy_export(umdoc EXECUTABLE
45 | CONFIGURATION Release
46 | IMPORTED_LOCATION umdoc.exe
47 | )
48 | deploy_export(umdoc EXECUTABLE
49 | CONFIGURATION Debug
50 | IMPORTED_LOCATION umdoc.exe
51 | )
52 | else()
53 | deploy_export(umdoc EXECUTABLE
54 | IMPORTED_LOCATION umdoc
55 | )
56 | endif()
57 |
58 | install_deploy_export()
59 |
--------------------------------------------------------------------------------
/Src/PlainGenerator.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "Generator.hpp"
5 |
6 | class PlainGenerator : public Generator
7 | {
8 | public:
9 | String generate(const OutputData::ParagraphSegment& segment) override {return String();}
10 | String generate(const OutputData::TitleSegment& segment) override {return String();}
11 | String generate(const OutputData::SeparatorSegment& segment) override {return String();}
12 | String generate(const OutputData::FigureSegment& segment) override {return String();}
13 | String generate(const OutputData::RuleSegment& segment) override {return String();}
14 | String generate(const OutputData::BulletListSegment& segment) override {return String();}
15 | String generate(const OutputData::NumberedListSegment& segment) override {return String();}
16 | String generate(const OutputData::BlockquoteSegment& segment) override {return String();}
17 | String generate(const OutputData::EnvironmentSegment& segment) override {return String();}
18 | String generate(const OutputData::TableSegment& segment) override {return String();}
19 | String generate(const OutputData::TexSegment& segment) override {return String();}
20 | String generate(const OutputData::TexPartSegment& segment) override {return String();}
21 | String generate(const OutputData::PdfSegment& segment) override {return String();}
22 |
23 | String escapeChar(uint32 c) override;
24 | String getSpanStart(const String& sequence) override {return String();}
25 | String getSpanEnd(const String& sequence) override {return String();}
26 | String getWordBreak(const char l, const char r) override {return String();}
27 | String getLink(const String& link, const String& name) override {return String();}
28 | String getLineBreak() override {return String();}
29 | String getInlineImage(const String& path) override {return String();}
30 | String getFootnote(const String& text) override {return String();}
31 | String getLatexFormula(const String& formula) override {return String();};
32 | };
33 |
--------------------------------------------------------------------------------
/Src/TexGenerator.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "Generator.hpp"
5 |
6 | struct OutputData;
7 |
8 | class TexGenerator : public Generator
9 | {
10 | public:
11 | bool generate(const String& engine, const OutputData& outputData, const String& outputFile);
12 |
13 | String getErrorString() const;
14 |
15 | public:
16 | static const char* _defaultListingsLanguages[];
17 | static const usize _numOfDefaultListingsLanguages;
18 |
19 | public:
20 | String generate(const OutputData::ParagraphSegment& segment) override;
21 | String generate(const OutputData::TitleSegment& segment) override;
22 | String generate(const OutputData::SeparatorSegment& segment) override;
23 | String generate(const OutputData::FigureSegment& segment) override;
24 | String generate(const OutputData::RuleSegment& segment) override;
25 | String generate(const OutputData::BulletListSegment& segment) override;
26 | String generate(const OutputData::NumberedListSegment& segment) override;
27 | String generate(const OutputData::BlockquoteSegment& segment) override;
28 | String generate(const OutputData::EnvironmentSegment& segment) override;
29 | String generate(const OutputData::TableSegment& segment) override;
30 | String generate(const OutputData::TexSegment& segment) override;
31 | String generate(const OutputData::TexPartSegment& segment) override;
32 | String generate(const OutputData::PdfSegment& segment) override;
33 |
34 | String escapeChar(uint32 c) override;
35 | String getSpanStart(const String& sequence) override;
36 | String getSpanEnd(const String& sequence) override;
37 | String getWordBreak(const char l, const char r) override;
38 | String getLink(const String& link, const String& name) override;
39 | String getLineBreak() override;
40 | String getInlineImage(const String& path) override;
41 | String getFootnote(const String& text) override;
42 | String getLatexFormula(const String& formula) override;
43 |
44 | public:
45 | static String texTranslate(const String& str);
46 | static String getEnvironmentName(const String& language);
47 | static String getTexSize(const String& size, bool width = true);
48 | };
49 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent none
3 | stages {
4 | stage('All') {
5 | matrix {
6 | agent {
7 | label "${platform}"
8 | }
9 | axes {
10 | axis {
11 | name 'platform'
12 | values 'ubuntu22.04-x86_64','ubuntu20.04-x86_64', 'ubuntu18.04-x86_64', 'windows10-x64', 'windows10-x86'
13 | }
14 | }
15 | stages {
16 | stage('Build') {
17 | steps {
18 | script {
19 | if (isUnix()) {
20 | sh 'rm -rf build/umdoc-*.zip build/umdoc-*.deb build/Doc/umdoc-*.pdf'
21 | }
22 | else {
23 | bat 'if exist build\\umdoc-*.zip del build\\umdoc-*.zip'
24 | }
25 |
26 | cmakeBuild buildDir: 'build', installation: 'InSearchPath', buildType: 'MinSizeRel', cmakeArgs: '-G Ninja'
27 | cmake workingDir: 'build', arguments: '--build . --target package', installation: 'InSearchPath'
28 |
29 | if (platform == 'ubuntu20.04-x86_64') {
30 | dir('build/Doc') {
31 | archiveArtifacts artifacts: 'umdoc-*.pdf'
32 | }
33 | }
34 |
35 | dir('build') {
36 | archiveArtifacts artifacts: 'umdoc-*.zip,umdoc-*.deb'
37 | }
38 | }
39 | }
40 | }
41 | stage('Test') {
42 | steps {
43 | ctest workingDir: 'build', installation: 'InSearchPath', arguments: '--output-on-failure'
44 | }
45 | }
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/Src/Parser.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "OutputData.hpp"
5 |
6 | struct InputData;
7 |
8 | class Parser
9 | {
10 | public:
11 | Parser() : _parserMode(normalMode), _newParagraphNextLine(false) {}
12 |
13 | bool parse(const InputData& inputData, const String& outputFile, OutputData& outputData);
14 | bool parseMarkdown(const OutputData::Info& info, const String& filePath, const String& fileContent, List& segments);
15 |
16 | String getErrorFile() const {return _error.file;}
17 | int getErrorLine() const {return _error.line;}
18 | String getErrorString() const {return _error.string;}
19 |
20 | public:
21 | static void extractArguments(String& line, Map& args);
22 | static bool extractStringArgument(String& line, String& result);
23 |
24 | private:
25 | enum ParserMode
26 | {
27 | normalMode,
28 | environmentMode,
29 | verbatimEnvironmentMode,
30 | };
31 |
32 | struct Error
33 | {
34 | String file;
35 | int line;
36 | String string;
37 |
38 | Error() : line(0) {}
39 | };
40 |
41 | private:
42 | ParserMode _parserMode;
43 | Error _error;
44 | List _outputSegments;
45 | List _segments;
46 | bool _newParagraphNextLine;
47 |
48 | private:
49 | void addSegment(const RefCount::Ptr& segment, bool newLine, const String& data);
50 |
51 | bool matchFigureImage(const char* s, const char* end, String& title, String& path, String& remainingLine);
52 |
53 | bool parseMarkdown(const OutputData::Info& info, const String& filePath, const String& fileContent);
54 | bool parseMarkdownLine(const OutputData::Info& info, const String& line, int additionalIndent);
55 | bool parseMarkdownTableLine(int indent, bool newLine, const String& remainingLine);
56 |
57 | bool process(const OutputData::Info& info);
58 |
59 | static String translateHtmlEntities(const String& line);
60 | static String replacePlaceholderVariables(const String& data, const HashMap& variables, bool allowEscaping);
61 |
62 | friend void test_Parser_translateHtmlEntities();
63 | friend void test_Parser_replacePlaceholderVariables();
64 | };
65 |
--------------------------------------------------------------------------------
/Examples/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | find_package(LATEX COMPONENTS XELATEX REQUIRED)
3 |
4 | function(add_example name input0)
5 |
6 | add_custom_command(
7 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.pdf"
8 | COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/${input0}" -o "${CMAKE_CURRENT_BINARY_DIR}/${name}.pdf"
9 | DEPENDS $ ${input0} ${ARGN}
10 | )
11 |
12 | add_custom_target(${name} ALL
13 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${name}.pdf"
14 | SOURCES ${input0} ${ARGN}
15 | )
16 |
17 | set_target_properties(${name} PROPERTIES
18 | FOLDER "Examples"
19 | )
20 |
21 | source_group("" FILES ${input0} ${ARGN})
22 |
23 | install(FILES ${input0} ${ARGN}
24 | DESTINATION "Examples/${name}"
25 | )
26 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${name}.pdf"
27 | DESTINATION "Examples"
28 | )
29 |
30 | endfunction()
31 |
32 | add_example(coverpage
33 | coverpage/umdoc.xml
34 | coverpage/cover.pdf
35 | coverpage/example.md
36 | )
37 |
38 | add_example(customenvironment
39 | customenvironment/umdoc.xml
40 | customenvironment/style.tex
41 | customenvironment/example.md
42 | )
43 |
44 | add_example(environmentscript
45 | environmentscript/umdoc.xml
46 | environmentscript/myscript1
47 | environmentscript/myscript1.bat
48 | environmentscript/myscript2
49 | environmentscript/myscript2.bat
50 | environmentscript/example.md
51 | )
52 |
53 | add_example(headerfooter
54 | headerfooter/umdoc.xml
55 | headerfooter/style.tex
56 | headerfooter/example.md
57 | )
58 |
59 | add_example(latexclass
60 | latexclass/umdoc.xml
61 | latexclass/exampleclass.cls
62 | latexclass/example.md
63 | )
64 |
65 | add_example(md2pdf
66 | md2pdf/example.md
67 | )
68 |
69 | add_example(tables
70 | tables/umdoc.xml
71 | tables/example.md
72 | )
73 |
74 | add_example(tablesfigures
75 | tablesfigures/umdoc.xml
76 | tablesfigures/example.md
77 | tablesfigures/circle.png
78 | )
79 |
80 | add_example(tablestyles
81 | tablestyles/umdoc.xml
82 | tablestyles/example.md
83 | )
84 |
85 | add_example(codeblocks
86 | codeblocks/umdoc.xml
87 | codeblocks/example.md
88 | )
89 |
--------------------------------------------------------------------------------
/Src/Generator.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 |
6 | #include "OutputData.hpp"
7 |
8 | #pragma once
9 |
10 | class Generator
11 | {
12 | public:
13 | virtual String generate(const OutputData::ParagraphSegment& segment) = 0;
14 | virtual String generate(const OutputData::TitleSegment& segment) = 0;
15 | virtual String generate(const OutputData::SeparatorSegment& segment) = 0;
16 | virtual String generate(const OutputData::FigureSegment& segment) = 0;
17 | virtual String generate(const OutputData::RuleSegment& segment) = 0;
18 | virtual String generate(const OutputData::BulletListSegment& segment) = 0;
19 | virtual String generate(const OutputData::NumberedListSegment& segment) = 0;
20 | virtual String generate(const OutputData::BlockquoteSegment& segment) = 0;
21 | virtual String generate(const OutputData::EnvironmentSegment& segment) = 0;
22 | virtual String generate(const OutputData::TableSegment& segment) = 0;
23 | virtual String generate(const OutputData::TexSegment& segment) = 0;
24 | virtual String generate(const OutputData::TexPartSegment& segment) = 0;
25 | virtual String generate(const OutputData::PdfSegment& segment) = 0;
26 |
27 | virtual String escapeChar(uint32 c) = 0;
28 | virtual String getSpanStart(const String& sequence) = 0;
29 | virtual String getSpanEnd(const String& sequence) = 0;
30 | virtual String getWordBreak(const char l, const char r) = 0;
31 | virtual String getLink(const String& link, const String& name) = 0;
32 | virtual String getLineBreak() = 0;
33 | virtual String getInlineImage(const String& path) = 0;
34 | virtual String getFootnote(const String& text) = 0;
35 | virtual String getLatexFormula(const String& formula) = 0;
36 |
37 | private:
38 | static bool matchInlineLink(Generator& generator, const char* s, const char* end, const char*& pos, String& result);
39 | static bool matchInlineImage(Generator& generator, const char* s, const char* end, const char*& pos, String& result);
40 | static bool matchLineBreak(Generator& generator, const char* s, const char* end, const char*& pos, String& result);
41 | static bool matchInlineFootnote(Generator& generator, const char* s, const char* end, const char*& pos, String& result);
42 | static bool matchInlineLatexFormula(Generator& generator, const char* s, const char* end, const char*& pos, String& result);
43 |
44 | protected:
45 | static String generate(Generator& generator, const OutputData& data);
46 | static String translate(Generator& generator, const String& str);
47 | };
48 |
--------------------------------------------------------------------------------
/Examples/tables/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Table 1
3 |
4 | | left {width=3cm} | right {width=7cm} | {.grid}
5 | | ---------------- | ----------------- |
6 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
7 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
8 |
9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
10 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
11 |
12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
13 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
14 | | Lorem ipsum | Lorem ipsum dolor sit |
15 |
16 |
17 | # Table 2
18 |
19 | | left {width=3cm} | right {width=7cm} | {.grid}
20 | | ---------------- | ----------------- |
21 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
22 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
23 |
24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
25 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
26 |
27 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
28 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 | | Lorem ipsum | Lorem ipsum dolor sit |
30 |
31 | # Table 3
32 |
33 | +------------------------------+-----------------------------------------------------------------+ {.grid}
34 | | left {width=3cm} | right {width=7cm} |
35 | +------------------------------+-----------------------------------------------------------------+
36 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do |
37 | | | eiusmod tempor incididunt ut labore et dolore magna aliqua. |
38 | | Lorem ipsum dolor sit amet, | |
39 | | consectetur adipiscing elit, | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do |
40 | | sed do eiusmod tempor | eiusmod tempor incididunt ut labore et dolore magna aliqua. |
41 | | incididunt ut labore et | |
42 | | dolore magna aliqua. | |
43 | +------------------------------+-----------------------------------------------------------------+
44 | | Lorem ipsum | Lorem ipsum dolor sit |
45 | +------------------------------+-----------------------------------------------------------------+
46 |
--------------------------------------------------------------------------------
/Src/HtmlGenerator.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "Generator.hpp"
5 |
6 | #include
7 |
8 | struct OutputData;
9 |
10 | class HtmlGenerator : public Generator
11 | {
12 | public:
13 | bool generate(const OutputData& outputData, const String& outputFile);
14 |
15 | String getErrorString() const;
16 |
17 | public:
18 | String generate(const OutputData::ParagraphSegment& segment) override;
19 | String generate(const OutputData::TitleSegment& segment) override;
20 | String generate(const OutputData::SeparatorSegment& segment) override;
21 | String generate(const OutputData::FigureSegment& segment) override;
22 | String generate(const OutputData::RuleSegment& segment) override;
23 | String generate(const OutputData::BulletListSegment& segment) override;
24 | String generate(const OutputData::NumberedListSegment& segment) override;
25 | String generate(const OutputData::BlockquoteSegment& segment) override;
26 | String generate(const OutputData::EnvironmentSegment& segment) override;
27 | String generate(const OutputData::TableSegment& segment) override;
28 | String generate(const OutputData::TexSegment& segment) override;
29 | String generate(const OutputData::TexPartSegment& segment) override;
30 | String generate(const OutputData::PdfSegment& segment) override;
31 |
32 | String escapeChar(uint32 c) override;
33 | String getSpanStart(const String& sequence) override;
34 | String getSpanEnd(const String& sequence) override;
35 | String getWordBreak(const char l, const char r) override;
36 | String getLink(const String& link, const String& name) override;
37 | String getLineBreak() override;
38 | String getInlineImage(const String& path) override;
39 | String getFootnote(const String& text) override;
40 | String getLatexFormula(const String& formula) override {return String();};
41 |
42 | private:
43 | class Number
44 | {
45 | public:
46 | Number() {}
47 | Number(const Array& number) : _number(number) {}
48 | Number(uint number) {_number.append(number);}
49 |
50 | String toString() const;
51 |
52 | private:
53 | Array _number;
54 | };
55 |
56 | struct LastNumbers
57 | {
58 | Array title;
59 | uint figure;
60 | uint table;
61 |
62 | LastNumbers() : figure(), table() {}
63 | };
64 |
65 | private:
66 | String _outputDir;
67 |
68 | HashMap _titleNumbers;
69 | HashMap _figureNumbers;
70 | HashMap _tableNumbers;
71 | HashMap _numbers;
72 |
73 | HashMap _elementIds;
74 | HashSet _usedElementIds;
75 |
76 | List _footnotes;
77 |
78 | private:
79 | String stripFormattingAndTranslate(const String& str);
80 | String escape(const String& str);
81 |
82 | void findNumbers(const List& segments, LastNumbers& lastNumbers);
83 | String getElementId(const OutputData::Segment& segment, const String& title, const Map& arguments);
84 | };
85 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 0.3.0
3 |
4 | * Added Ubuntu 22.04 build
5 | * Fixed crash caused by XML syntax errors
6 | * Fixed crash caused by code segments in environments or tables
7 |
8 | # 0.2.9
9 |
10 | * Fixed environment script execution
11 |
12 | # 0.2.8
13 |
14 | * Fixed issue with invalid characters in output files
15 |
16 | # 0.2.7
17 |
18 | * Fixed crash when merging incomplete table rows
19 | * Corrected table cell content parsing
20 | * Added .plain table style
21 |
22 | # 0.2.6
23 |
24 | * Fixed dead lock caused by non utf-8 characters
25 |
26 | # 0.2.5
27 |
28 | * Added support for placeholders in markdown files
29 | * Fixed issue where lines are accidentally interpreted as a horizontal rule
30 |
31 | # 0.2.4
32 |
33 | * Fixed table parsing issue
34 |
35 | # 0.2.3
36 |
37 | * Refactored table parsing
38 | * Build Windows builds with /MT
39 |
40 | # 0.2.2
41 |
42 | * Fixed configuration mapping issue with Visual Studio CDeploy package
43 | * Added support for grid table syntax
44 | * Fixed issues with paragraphs in tables
45 |
46 | # 0.2.1
47 |
48 | * Added support for older latex engines by translating non breaking space to ~
49 | * Switched to CDeploy to create packages
50 |
51 | # 0.2.0
52 |
53 | * Added support for custom environment command execution
54 | * Added support for mathematical LaTex expressions
55 | * Added HTML generator
56 | * Removed -t flag and recognize file format based on the output file extension
57 | * Added "xplain" environment for environment that span multiple pages
58 | * Switched to xelatex by default
59 | * Added "xtab" table style
60 | * Support figures width and height attributes
61 | * Introduced new syntax to insert a section number in cross document references
62 | * Changed handling of the character ~, its no longer equal to a non breaking space
63 | * Added support for HTML 1.0 entities and for
64 | * Switched to CMake and CPack
65 |
66 | # 0.1.6
67 |
68 | * Build Windows build with /MT
69 |
70 | # 0.1.5
71 |
72 | * Support XML comments in verbatim environments
73 | * Support cross references to figures and tables
74 | * Improved the user manual
75 |
76 | # 0.1.4
77 |
78 | * Added support for tilde character
79 |
80 | # 0.1.3
81 |
82 | * Added support for tables with caption
83 | * Added *tablesfigure* example, which demonstrations generating a list of figures or tables
84 | * Added support for grid tables
85 | * Fixed replacing placeholders in header tex files
86 |
87 | # 0.1.2
88 |
89 | * Added support for footnotes
90 | * Allow line breaks after or before some special characters
91 | * Fixed resource file including issue
92 | * Fixed issue with titlesec section numbering on Ubuntu 16.04
93 |
94 | # 0.1.1
95 |
96 | * Fixed not working conversion of Markdown to *PDF* without a configuration file
97 | * Fixed access to input file when *umdoc* should operate in the working directory
98 | * Added *md2tex* example
99 |
100 | # 0.1.0
101 |
102 | * The initial release of *umdoc* supports the following features:
103 | * Importing styles from *TEX* files
104 | * Inserting of *TEX* files
105 | * Inserting of *PDF* files
106 | * Inserting a table of contents
107 | * Creation of documents with multiple parts
108 | * A front end for *lualatex*/*pdflatex*
109 | * Support for custom *LaTeX* classes
110 | * Support for custom *LaTeX* environments
111 | * The following Markdown features are supported:
112 | * Simple paragraphs
113 | * Emphasis
114 | * Setext headers
115 | * Atx headers
116 | * Blockquotes
117 | * Bullet lists
118 | * Numbered lists
119 | * Horizontal rules
120 | * Inline links
121 | * Code spans
122 | * Inline images
123 | * Underscores in words
124 | * Fenced code blocks
125 | * Tables
126 | * Header attributes
127 | * Implicit figures
128 | * The package includes the following examples:
129 | * *coverpage* - A configuration, which includes a *PDF* page
130 | * *customenvironment* - A configuration, which defines a custom environment
131 | * *headerfooter* - A configuration, which uses the *LaTeX* package *fancyhdr*
132 | * *latexclass* - A configuration, which uses a custom *LaTeX* class
133 |
--------------------------------------------------------------------------------
/Examples/tablestyles/example.md:
--------------------------------------------------------------------------------
1 |
2 | # Normal Table
3 |
4 | | left {width=3cm} | right {width=7cm} |
5 | | ---------------- | ----------------- |
6 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
7 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
8 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
9 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
10 |
11 | # Grid Table
12 |
13 | | left {width=3cm} | right {width=7cm} | {.grid}
14 | | ---------------- | ----------------- |
15 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
16 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
17 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
18 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
19 |
20 | # XTab Table
21 |
22 | | left {width=3cm} | right {width=7cm} | {.xtab}
23 | | ---------------- | ----------------- |
24 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
25 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
26 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
27 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
28 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
29 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
30 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
31 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
32 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
33 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
34 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
35 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
37 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
38 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
39 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
40 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
41 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
42 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
43 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
44 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
45 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
46 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
47 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
48 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
49 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
51 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
52 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
53 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
54 |
55 | # XTab Grid Table
56 |
57 | | left {width=3cm} | right {width=7cm} | {.xtabgrid}
58 | | ---------------- | ----------------- |
59 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
60 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
61 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
62 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
63 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
64 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
65 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
66 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
67 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
68 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
69 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
70 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
71 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
72 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
73 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
74 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
75 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
76 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
77 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
78 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
79 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
80 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
81 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
82 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
83 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
84 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
85 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
86 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
87 | | Lorem ipsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
88 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
89 |
--------------------------------------------------------------------------------
/Src/Tests/Test.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "../Parser.hpp"
9 | #include "../InputData.hpp"
10 | #include "../Reader.hpp"
11 | #include "../TexGenerator.hpp"
12 |
13 | void test_Parser_translateHtmlEntities()
14 | {
15 | String test = Parser::translateHtmlEntities("a €b");
16 | ASSERT(Parser::translateHtmlEntities("a €b") == "a\302\240\302\240\342\202\254b");
17 | }
18 |
19 | void test_Parser_replacePlaceholderVariables()
20 | {
21 | HashMap vars;
22 | vars.append("ab", "12");
23 | vars.append("c", "34");
24 | vars.append("de", "56");
25 | ASSERT(Parser::replacePlaceholderVariables("%ab%-%c%-%de%", vars, true) == "12-34-56");
26 | ASSERT(Parser::replacePlaceholderVariables("%ab%-%c%-", vars, true) == "12-34-");
27 | ASSERT(Parser::replacePlaceholderVariables("-%c%-", vars, true) == "-34-");
28 | ASSERT(Parser::replacePlaceholderVariables("-%c%-%f%-", vars, true) == "-34-%f%-");
29 | ASSERT(Parser::replacePlaceholderVariables("-\\%c% %f%-", vars, true) == "-\\%c% %f%-");
30 | ASSERT(Parser::replacePlaceholderVariables("-\\%c%-%f%-", vars, false) == "-\\34-%f%-");
31 | }
32 |
33 | void test_Parser_TableCellParsing()
34 | {
35 | {
36 | File file;
37 | ASSERT(file.open("umdoc.xml", File::writeFlag));
38 | ASSERT(file.write("\n"));
39 | ASSERT(file.write("+-----+\n"));
40 | ASSERT(file.write("| * b |\n"));
41 | ASSERT(file.write("| c |\n"));
42 | ASSERT(file.write("+-----+\n"));
43 | ASSERT(file.write(""));
44 | }
45 |
46 | {
47 | InputData inputData;
48 | OutputData outputData;
49 | Reader reader;
50 | Parser parser;
51 | TexGenerator generator;
52 | ASSERT(reader.read("umdoc.xml", inputData));
53 | ASSERT(parser.parse(inputData, "test.tex", outputData));
54 | ASSERT(generator.generate(String(), outputData, "test.tex"));
55 | }
56 |
57 | {
58 | String data;
59 | ASSERT(File::readAll("test.tex", data));
60 | ASSERT(data.find("\\item \nb c \n\\end{itemize}\n"));
61 | }
62 |
63 | ASSERT(File::unlink("umdoc.xml"));
64 | ASSERT(File::unlink("test.tex"));
65 | }
66 |
67 | void test_Parser_TableCellParsing2()
68 | {
69 | {
70 | File file;
71 | ASSERT(file.open("umdoc.xml", File::writeFlag));
72 | ASSERT(file.write("\n"));
73 | ASSERT(file.write("| a |\n"));
74 | ASSERT(file.write(" * b \n"));
75 | ASSERT(file.write(" c\n"));
76 | ASSERT(file.write(""));
77 | }
78 |
79 | {
80 | InputData inputData;
81 | OutputData outputData;
82 | Reader reader;
83 | Parser parser;
84 | TexGenerator generator;
85 | ASSERT(reader.read("umdoc.xml", inputData));
86 | ASSERT(parser.parse(inputData, "test.tex", outputData));
87 | ASSERT(generator.generate(String(), outputData, "test.tex"));
88 | }
89 |
90 | {
91 | String data;
92 | ASSERT(File::readAll("test.tex", data));
93 | ASSERT(data.find("\\item \nb c\n\\end{itemize}\n"));
94 | }
95 |
96 | ASSERT(File::unlink("umdoc.xml"));
97 | ASSERT(File::unlink("test.tex"));
98 | }
99 |
100 | void test_Parser_TableCellParsing3()
101 | {
102 | {
103 | File file;
104 | ASSERT(file.open("umdoc.xml", File::writeFlag));
105 | ASSERT(file.write("\n"));
106 | ASSERT(file.write("| ```c |\n"));
107 | ASSERT(file.write(""));
108 | }
109 |
110 | {
111 | InputData inputData;
112 | OutputData outputData;
113 | Reader reader;
114 | Parser parser;
115 | TexGenerator generator;
116 | ASSERT(reader.read("umdoc.xml", inputData));
117 | ASSERT(parser.parse(inputData, "test.tex", outputData));
118 | ASSERT(generator.generate(String(), outputData, "test.tex"));
119 | }
120 |
121 | {
122 | String data;
123 | ASSERT(File::readAll("test.tex", data));
124 | ASSERT(data.find("\\begin{clanguage}"));
125 | }
126 |
127 | ASSERT(File::unlink("umdoc.xml"));
128 | ASSERT(File::unlink("test.tex"));
129 | }
130 |
131 | void test_umdoc_NonUtf8InputChar()
132 | { // test umdoc executable launching with non utf-8 char input
133 | {
134 | File file;
135 | ASSERT(file.open("umdoc.xml", File::writeFlag));
136 | ASSERT(file.write("Test�"));
137 | }
138 |
139 | {
140 | Process process;
141 | ASSERT(process.start(UMDOC_EXECUTABLE " -o test.tex"));
142 | uint32 exitCode;
143 | ASSERT(process.join(exitCode));
144 | ASSERT(exitCode == 0);
145 | }
146 |
147 | ASSERT(File::exists("test.tex"));
148 | ASSERT(File::unlink("umdoc.xml"));
149 | ASSERT(File::unlink("test.tex"));
150 | }
151 |
152 | void test_umdoc_UnclosedEnvironment()
153 | { // test umdoc executable launching with an unclosed environment
154 | {
155 | File file;
156 | ASSERT(file.open("umdoc.xml", File::writeFlag));
157 | ASSERT(file.write("a\n\n```test\n"));
158 | }
159 |
160 | {
161 | Process process;
162 | ASSERT(process.start(UMDOC_EXECUTABLE " -o test.tex"));
163 | uint32 exitCode;
164 | ASSERT(process.join(exitCode));
165 | ASSERT(exitCode == 0);
166 | }
167 |
168 | ASSERT(File::exists("test.tex"));
169 | ASSERT(File::unlink("umdoc.xml"));
170 | ASSERT(File::unlink("test.tex"));
171 | }
172 |
173 | int main(int argc, char* argv[])
174 | {
175 | test_Parser_translateHtmlEntities();
176 | test_Parser_replacePlaceholderVariables();
177 | test_Parser_TableCellParsing();
178 | test_Parser_TableCellParsing2();
179 | test_Parser_TableCellParsing3();
180 | test_umdoc_NonUtf8InputChar();
181 | test_umdoc_UnclosedEnvironment();
182 | return 0;
183 | }
184 |
--------------------------------------------------------------------------------
/Examples/codeblocks/example.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # c
4 |
5 | ```c "main.c"
6 | #include
7 |
8 | int main(void)
9 | {
10 | printf("Hello World\n");
11 | return 0;
12 | }
13 | ```
14 |
15 |
16 | # plain
17 |
18 | ```plain "Lorem ipsum.txt"
19 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque lacinia quam sed lectus viverra fringilla. Proin semper nunc vitae massa mollis volutpat. Nullam cursus ut libero vel lacinia. Sed placerat velit eleifend, convallis felis consectetur, viverra justo. Duis congue erat dolor, sit amet facilisis ipsum condimentum at. Aenean placerat at magna auctor dapibus. Quisque congue neque nec sem euismod, ornare tristique mi maximus. Sed finibus tellus urna, quis suscipit libero fringilla in. Pellentesque in imperdiet eros. Aliquam sit amet ipsum in felis rhoncus euismod at ut ante.
20 |
21 | Nullam tortor diam, vehicula id velit in, fringilla suscipit ligula. Suspendisse et diam et ex finibus facilisis. Nulla sed lobortis diam. Fusce nec velit pellentesque, interdum diam vel, ultrices purus. In blandit et dolor vel maximus. Sed sit amet imperdiet tellus. Cras interdum molestie dui eget sodales. Duis vulputate bibendum lorem id porta. Fusce dolor mauris, pellentesque vel mattis eu, vestibulum in leo. Morbi consequat cursus iaculis.
22 | ```
23 |
24 | # xplain
25 |
26 | ```xplain
27 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque lacinia quam sed lectus viverra fringilla. Proin semper nunc vitae massa mollis volutpat. Nullam cursus ut libero vel lacinia. Sed placerat velit eleifend, convallis felis consectetur, viverra justo. Duis congue erat dolor, sit amet facilisis ipsum condimentum at. Aenean placerat at magna auctor dapibus. Quisque congue neque nec sem euismod, ornare tristique mi maximus. Sed finibus tellus urna, quis suscipit libero fringilla in. Pellentesque in imperdiet eros. Aliquam sit amet ipsum in felis rhoncus euismod at ut ante.
28 |
29 | Nullam tortor diam, vehicula id velit in, fringilla suscipit ligula. Suspendisse et diam et ex finibus facilisis. Nulla sed lobortis diam. Fusce nec velit pellentesque, interdum diam vel, ultrices purus. In blandit et dolor vel maximus. Sed sit amet imperdiet tellus. Cras interdum molestie dui eget sodales. Duis vulputate bibendum lorem id porta. Fusce dolor mauris, pellentesque vel mattis eu, vestibulum in leo. Morbi consequat cursus iaculis.
30 |
31 | Nunc consectetur elit sit amet consectetur egestas. Phasellus ac congue arcu. Aliquam feugiat lacinia leo nec congue. Fusce vestibulum convallis molestie. Quisque in odio ac orci luctus rhoncus. Duis ultricies posuere mauris, ut pulvinar urna accumsan vel. Morbi faucibus nulla ipsum, at scelerisque leo rhoncus vitae. Etiam vulputate arcu ac pulvinar hendrerit. Proin tincidunt nisi eu elementum sollicitudin. Suspendisse magna ligula, accumsan a mattis quis, pellentesque ut lacus. Praesent sit amet magna urna. Sed a velit in erat lobortis dictum.
32 |
33 | Donec a rutrum nunc. Duis accumsan dignissim leo rhoncus dignissim. Vivamus in justo at odio hendrerit fermentum. Nam purus purus, fermentum sit amet iaculis in, convallis sed tortor. Donec orci risus, vulputate quis finibus quis, porttitor et mi. Curabitur ut ante sed magna vehicula tincidunt a ut augue. Proin scelerisque massa nec orci vehicula, et efficitur urna viverra.
34 |
35 | Proin rhoncus lorem non felis congue, ut varius eros cursus. Nullam auctor mi vel iaculis vestibulum. Phasellus varius nisi in nisl tincidunt pulvinar. Etiam vitae massa aliquet, auctor mi vel, lacinia dui. Cras pulvinar nulla ante, ut cursus metus mollis non. Mauris eget tempus nibh. Aliquam sagittis consectetur mauris in ultrices. Pellentesque erat ex, finibus sed lacinia in, dignissim a tellus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc tellus elit, convallis id eros quis, lobortis blandit felis. Sed vel mattis diam. In vitae urna id odio rhoncus bibendum et eu lacus. Ut tempus imperdiet augue eget placerat.
36 |
37 | Sed faucibus dignissim eros at convallis. Sed ut tincidunt lorem. Cras volutpat hendrerit lectus a placerat. Curabitur consectetur placerat est vitae iaculis. In ac nisl vitae libero pellentesque interdum. Sed elementum vehicula ipsum quis maximus. Curabitur hendrerit in nibh vitae ullamcorper. Quisque aliquet cursus nunc sed placerat.
38 |
39 | Duis a felis vehicula, suscipit lacus et, condimentum ex. Ut ut convallis lorem, quis molestie est. Etiam quis dui sem. Praesent neque neque, sollicitudin et ipsum at, gravida mattis lectus. Suspendisse lobortis, ante vel placerat pretium, felis ipsum accumsan risus, ut consequat nibh turpis a ipsum. Nunc porta congue leo, vitae egestas augue maximus ac. Nam quis pharetra lorem. Vivamus nec lobortis erat, at tempor mi. Aenean diam erat, aliquam suscipit lorem volutpat, mollis consequat sapien. Donec dictum eget ante auctor interdum. Duis rhoncus nisi vitae nunc eleifend laoreet. Mauris vel elementum sapien. Proin et purus vel lectus sollicitudin luctus at in risus. Ut scelerisque orci est, ac lacinia lacus dapibus sed. Phasellus mattis ac turpis ac porta.
40 |
41 | Donec nec justo quis urna vulputate condimentum. Sed tristique posuere dui, id dignissim est cursus id. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus imperdiet egestas eros sit amet commodo. Nullam ut quam nec justo gravida porta a eget quam. Sed lorem libero, aliquet in hendrerit ac, aliquet non massa. In hac habitasse platea dictumst. Proin faucibus, nunc et vestibulum tristique, purus tellus pellentesque dolor, sit amet consectetur risus nisl eu leo. Maecenas non leo pellentesque, molestie mauris vitae, laoreet tellus. Mauris eleifend, augue id rutrum ultrices, mauris mi egestas tortor, suscipit varius ante leo vitae orci.
42 |
43 | Maecenas vel erat ac turpis vulputate vehicula ut non orci. Sed vel auctor odio, et hendrerit elit. Vivamus rhoncus tincidunt ultricies. Pellentesque sit amet metus neque. Suspendisse vulputate ante id purus pharetra, nec sodales mi tincidunt. Integer pharetra vitae massa in scelerisque. Sed at condimentum sapien, eget tincidunt mauris. Duis dictum porta augue, a tristique nunc convallis at. Duis ac sapien porta, tincidunt sapien in, varius orci. Curabitur placerat dapibus lectus id vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Duis sit amet metus egestas, blandit ligula id, volutpat orci. Phasellus vel dignissim dui.
44 | ```
--------------------------------------------------------------------------------
/Src/Reader.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "Reader.hpp"
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "InputData.hpp"
9 |
10 | bool Reader::read(const String& inputFile, InputData& inputData)
11 | {
12 | inputData.inputFile = inputFile;
13 |
14 | if(File::extension(inputFile).compareIgnoreCase("md") == 0)
15 | {
16 | InputData::Component& component = inputData.document.append(InputData::Component());
17 | component.type = InputData::Component::mdType;
18 | component.filePath = inputFile;
19 | File file;
20 | if(!file.open(component.filePath))
21 | return _errorString = String::fromPrintf("Could not open file '%s': %s", (const char*)component.filePath, (const char*)Error::getErrorString()), false;
22 | if(!file.readAll(component.value))
23 | return _errorString = String::fromPrintf("Could not read file '%s': %s", (const char*)component.filePath, (const char*)Error::getErrorString()), false;
24 | return true;
25 | }
26 |
27 | Xml::Parser xmlParser;
28 | Xml::Element xmlFile;
29 | if(!xmlParser.load(inputFile, xmlFile))
30 | return _errorLine = xmlParser.getErrorLine(), _errorColumn = xmlParser.getErrorColumn(), _errorString = xmlParser.getErrorString(), false;
31 |
32 | if(xmlFile.type != "umdoc")
33 | return _errorLine = xmlParser.getErrorLine(), _errorColumn = xmlParser.getErrorColumn(), _errorString = "Expected element 'umdoc'", false;
34 |
35 | inputData.className = *xmlFile.attributes.find("class");
36 |
37 | bool documentRead = false;
38 | for(List::Iterator i = xmlFile.content.begin(), end = xmlFile.content.end(); i != end; ++i)
39 | {
40 | const Xml::Variant& variant = *i;
41 | if(!variant.isElement())
42 | continue;
43 | const Xml::Element& element = variant.toElement();
44 | if(documentRead)
45 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Unexpected element '%s'", (const char*)element.type), false;
46 |
47 | if(element.type == "tex")
48 | {
49 | String filePath = *element.attributes.find("file");
50 | File file;
51 | if(!filePath.isEmpty())
52 | {
53 | String data;
54 | if(!file.open(filePath))
55 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Could not open file '%s': %s", (const char*)filePath, (const char*)Error::getErrorString()), false;
56 | if(!file.readAll(data))
57 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Could not read file '%s': %s", (const char*)filePath, (const char*)Error::getErrorString()), false;
58 | inputData.headerTexFiles.append(data);
59 | }
60 | for(List::Iterator i = element.content.begin(), end = element.content.end(); i != end; ++i)
61 | inputData.headerTexFiles.append(i->toString());
62 | }
63 | else if(element.type == "set")
64 | inputData.variables.append(*element.attributes.find("name"), *element.attributes.find("value"));
65 | else if(element.type == "environment")
66 | {
67 | InputData::Environment& environment = inputData.environments.append(*element.attributes.find("name"), InputData::Environment());
68 | environment.verbatim = element.attributes.find("verbatim")->toBool();
69 | environment.command = *element.attributes.find("command");
70 | }
71 | else if(element.type == "document")
72 | {
73 | for(List::Iterator i = element.content.begin(), end = element.content.end(); i != end; ++i)
74 | {
75 | const Xml::Variant& variant = *i;
76 | if(!variant.isElement())
77 | continue;
78 | const Xml::Element& element = variant.toElement();
79 | if(element.type == "tex" || element.type == "md")
80 | {
81 | InputData::Component& component = inputData.document.append(InputData::Component());
82 | component.type = element.type == "md" ? InputData::Component::mdType : InputData::Component::texType;
83 | component.filePath = *element.attributes.find("file");
84 | if(!component.filePath.isEmpty())
85 | {
86 | File file;
87 | if(!file.open(component.filePath))
88 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Could not open file '%s': %s", (const char*)component.filePath, (const char*)Error::getErrorString()), false;
89 | if(!file.readAll(component.value))
90 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Could not read file '%s': %s", (const char*)component.filePath, (const char*)Error::getErrorString()), false;
91 | }
92 | for(List::Iterator i = element.content.begin(), end = element.content.end(); i != end; ++i)
93 | component.value.append(i->toString());
94 | }
95 | else if(element.type == "toc" || element.type == "tableOfContents")
96 | {
97 | InputData::Component& component = inputData.document.append(InputData::Component());
98 | component.type = InputData::Component::texTableOfContentsType;
99 | }
100 | else if(element.type == "lof" || element.type == "listOfFigures")
101 | {
102 | InputData::Component& component = inputData.document.append(InputData::Component());
103 | component.type = InputData::Component::texListOfFiguresType;
104 | }
105 | else if(element.type == "lot" || element.type == "listOfTables")
106 | {
107 | InputData::Component& component = inputData.document.append(InputData::Component());
108 | component.type = InputData::Component::texListOfTablesType;
109 | }
110 | else if(element.type == "break" || element.type == "newPage" || element.type == "pageBreak")
111 | {
112 | InputData::Component& component = inputData.document.append(InputData::Component());
113 | component.type = InputData::Component::texNewPageType;
114 | }
115 | else if(element.type == "pdf")
116 | {
117 | InputData::Component& component = inputData.document.append(InputData::Component());
118 | component.type = InputData::Component::pdfType;
119 | component.filePath = *element.attributes.find("file");
120 | }
121 | else if(element.type == "part")
122 | {
123 | InputData::Component& component = inputData.document.append(InputData::Component());
124 | component.type = InputData::Component::texPartType;
125 | component.value = *element.attributes.find("title");
126 | }
127 | else if(element.type == "environment")
128 | {
129 | InputData::Environment& environment = inputData.environments.append(*element.attributes.find("name"), InputData::Environment());
130 | environment.verbatim = element.attributes.find("verbatim")->toBool();
131 | environment.command = *element.attributes.find("command");
132 | }
133 | else if(element.type == "set")
134 | inputData.variables.append(*element.attributes.find("name"), *element.attributes.find("value"));
135 | else
136 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Unexpected element '%s'", (const char*)element.type), false;
137 | }
138 | documentRead = true;
139 | }
140 | else
141 | return _errorLine = element.line, _errorColumn = element.column, _errorString = String::fromPrintf("Unexpected element '%s'", (const char*)element.type), false;
142 | }
143 |
144 | return true;
145 | }
146 |
--------------------------------------------------------------------------------
/Src/OutputData.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | class Generator;
13 |
14 | struct OutputData
15 | {
16 | struct Info;
17 | class Segment;
18 | class BulletListSegment;
19 | class NumberedListSegment;
20 | class BlockquoteSegment;
21 | class ParagraphSegment;
22 |
23 | typedef RefCount::Ptr SegmentPtr;
24 | typedef RefCount::Ptr BulletListSegmentPtr;
25 | typedef RefCount::Ptr NumberedListSegmentPtr;
26 | typedef RefCount::Ptr BlockquoteSegmentPtr;
27 | typedef RefCount::Ptr ParagraphSegmentPtr;
28 |
29 | enum OutputFormat
30 | {
31 | plainFormat,
32 | texFormat,
33 | htmlFormat,
34 | };
35 |
36 | struct EnvironmentInfo
37 | {
38 | bool verbatim;
39 | String command;
40 | };
41 |
42 | class Segment : public RefCount::Object
43 | {
44 | public:
45 | Segment* _parent;
46 |
47 | public:
48 | Segment(int indent) : _parent(0), _valid(true), _indent(indent) {}
49 |
50 | public:
51 | virtual ~Segment() {};
52 | virtual bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) {return false;};
53 | virtual String generate(Generator& generator) const = 0;
54 | virtual bool process(const OutputData::Info& info, String& error) {return true;};
55 |
56 | int getIndent() const {return _indent;}
57 |
58 | bool isValid() const {return _valid;}
59 | void invalidate() {_valid = false;}
60 |
61 | protected:
62 | bool _valid;
63 | int _indent;
64 | };
65 |
66 | class ParagraphSegment : public Segment
67 | {
68 | public:
69 | String _text;
70 | public:
71 | ParagraphSegment(int indent, const String& text) : Segment(indent), _text(text) {}
72 | public:
73 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override;
74 | String generate(Generator& generator) const override;
75 | };
76 |
77 | class TitleSegment : public Segment
78 | {
79 | public:
80 | int _level;
81 | String _title;
82 | Map _arguments;
83 | public:
84 | TitleSegment(int indent, int level) : Segment(indent), _level(level) {}
85 | bool parseArguments(const String& title, String& error);
86 | public:
87 | String generate(Generator& generator) const override;
88 | };
89 |
90 | class SeparatorSegment : public Segment
91 | {
92 | public:
93 | SeparatorSegment(int indent) : Segment(indent) {}
94 | public:
95 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override;
96 | String generate(Generator& generator) const override;
97 | };
98 |
99 | class FigureSegment : public Segment
100 | {
101 | public:
102 | String _title;
103 | String _path;
104 | Map _arguments;
105 | public:
106 | FigureSegment(int indent, const String& title, const String& path) : Segment(indent), _title(title), _path(path) {}
107 | bool parseArguments(const String& line, String& error);
108 | public:
109 | String generate(Generator& generator) const override;
110 | };
111 |
112 | class RuleSegment : public Segment
113 | {
114 | public:
115 | RuleSegment(int indent) : Segment(indent) {}
116 | public:
117 | String generate(Generator& generator) const override;
118 | };
119 |
120 | class BulletListSegment : public Segment
121 | {
122 | public:
123 | List _siblingSegments;
124 | List _childSegments;
125 | char _symbol;
126 | int _childIndent;
127 | public:
128 | BulletListSegment(int indent, char symbol, uint childIndent) : Segment(indent), _symbol(symbol), _childIndent(childIndent) {}
129 | public:
130 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override;
131 | String generate(Generator& generator) const override;
132 | };
133 |
134 | class NumberedListSegment : public Segment
135 | {
136 | public:
137 | List _siblingSegments;
138 | List _childSegments;
139 | uint _number;
140 | int _childIndent;
141 | public:
142 | NumberedListSegment(int indent, uint number, uint childIndent) : Segment(indent), _number(number), _childIndent(childIndent) {}
143 | public:
144 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override;
145 | String generate(Generator& generator) const override;
146 | };
147 |
148 | class BlockquoteSegment : public Segment
149 | {
150 | public:
151 | List _siblingSegments;
152 | List _childSegments;
153 | int _childIndent;
154 | public:
155 | BlockquoteSegment(int indent, uint childIndent) : Segment(indent), _childIndent(childIndent) {}
156 | public:
157 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override;
158 | String generate(Generator& generator) const override;
159 | };
160 |
161 | class EnvironmentSegment : public Segment
162 | {
163 | public:
164 | int _backticks;
165 | bool _verbatim;
166 | String _command;
167 | String _language;
168 | Map _arguments;
169 | List _segments;
170 | List _lines;
171 | public:
172 | EnvironmentSegment(int indent, int backticks) : Segment(indent), _backticks(backticks), _verbatim(true) {}
173 | bool parseArguments(const String& line, const HashMap& knownEnvironments, String& error);
174 | public:
175 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override {return false;}
176 | String generate(Generator& generator) const override;
177 | bool process(const OutputData::Info& info, String& error) override;
178 | };
179 |
180 | class TableSegment : public Segment
181 | {
182 | public:
183 | struct CellData
184 | {
185 | List lines;
186 | List outputSegments2;
187 | };
188 | struct RowData
189 | {
190 | Array cellData;
191 | };
192 | struct ColumnInfo
193 | {
194 | enum Alignment
195 | {
196 | undefinedAlignment,
197 | leftAlignment,
198 | rightAlignment,
199 | centerAlignment
200 | };
201 |
202 | int indent;
203 | Alignment alignment;
204 | Map arguments;
205 | ColumnInfo(int indent) : indent(indent), alignment(undefinedAlignment) {}
206 | };
207 | enum Type
208 | {
209 | PipeTable,
210 | GridTable,
211 | };
212 | public:
213 | Type _tableType;
214 | bool _isSeparatorLine;
215 | Array _columns;
216 | List _rows;
217 | ParagraphSegmentPtr _captionSegment;
218 | Map _arguments;
219 | bool _forceNewRowNextMerge;
220 | public:
221 | TableSegment(int indent) : Segment(indent), _tableType(PipeTable), _isSeparatorLine(false), _forceNewRowNextMerge(false) {}
222 | bool parseArguments(const String& line, String& error);
223 | public:
224 | bool merge(Segment& segment, bool newParagraph, bool newLine, const String& line) override;
225 | bool process(const OutputData::Info& info, String& error) override;
226 | String generate(Generator& generator) const override;
227 | };
228 |
229 | class TexSegment : public Segment
230 | {
231 | public:
232 | String _content;
233 | public:
234 | TexSegment(const String& content) : Segment(0), _content(content) {}
235 | public:
236 | String generate(Generator& generator) const override;
237 | };
238 |
239 | class TexPartSegment : public Segment
240 | {
241 | public:
242 | String _title;
243 | public:
244 | TexPartSegment(const String& title) : Segment(0), _title(title) {}
245 | public:
246 | String generate(Generator& generator) const override;
247 | };
248 |
249 | class PdfSegment : public Segment
250 | {
251 | public:
252 | String _filePath;
253 | public:
254 | PdfSegment(const String& filePath) : Segment(0), _filePath(filePath) {}
255 | public:
256 | String generate(Generator& generator) const override;
257 | };
258 |
259 | struct Info
260 | {
261 | String sourceFile;
262 | OutputFormat format;
263 | String className;
264 | List headerTexFiles;
265 | bool hasPdfSegments;
266 | HashMap environments;
267 |
268 | Info () : format(plainFormat), hasPdfSegments(false) {}
269 | };
270 | Info info;
271 | List segments;
272 | };
273 |
--------------------------------------------------------------------------------
/Src/Generator.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "Generator.hpp"
3 |
4 | #include
5 | #include
6 |
7 | String Generator::translate(Generator& generator, const String& str)
8 | {
9 | String result(str.length());
10 | char c;
11 | String endSequence;
12 | List endSequenceStack;
13 | bool ignoreSingleBacktick = false;
14 | for(const char* start = str, * i = start, * end = start + str.length(); i < end;)
15 | {
16 | switch(c = *i)
17 | {
18 | case '\\':
19 | if(i + 1 < end && String::find("\\`*_{}[]()#+-.!$%", *(i + 1)))
20 | ++i;
21 | result.append(generator.escapeChar(*i));
22 | ++i;
23 | break;
24 | default:
25 | if(!endSequence.isEmpty() && String::compare(i, endSequence, endSequence.length()) == 0)
26 | {
27 | if(*(const char*)endSequence == '*' || *(const char*)endSequence == '_')
28 | {
29 | if((String::find(" \t", i[endSequence.length()]) && (i == start || String::find(" \t", i[-1]))) ||
30 | (*(const char*)endSequence == '_' && String::isAlphanumeric(i[endSequence.length()])))
31 | { // "[...] if you surround an * or _ with spaces, it will be treated as a literal asterisk or underscore."
32 | for(usize j = 0; j < endSequence.length(); ++j)
33 | result.append(generator.escapeChar(*(const char*)endSequence));
34 | i += endSequence.length();
35 | continue;
36 | }
37 | }
38 |
39 | if(*(const char*)endSequence == '`')
40 | while(!result.isEmpty() && String::find(" \t", ((const char*)result)[result.length() - 1]))
41 | result.resize(result.length() - 1);
42 |
43 | result.append(generator.getSpanEnd(endSequence));
44 | i += endSequence.length();
45 | if(endSequence == "``")
46 | ignoreSingleBacktick = false;
47 | if(endSequenceStack.isEmpty())
48 | endSequence.clear();
49 | else
50 | endSequence = endSequenceStack.back(), endSequenceStack.removeBack();
51 | continue;
52 | }
53 | if(c == '*' || c == '_' || c == '`')
54 | {
55 | String sequence;
56 | sequence.attach(i, i[1] == c ? 2 : 1);
57 |
58 | if(c == '*' || c == '_')
59 | {
60 | if((String::find(" \t", i[sequence.length()]) && (i == start || String::find(" \t", i[-1]))) ||
61 | (c == '_' && i != start && String::isAlphanumeric(i[-1])))
62 | { // "[...] if you surround an * or _ with spaces, it will be treated as a literal asterisk or underscore."
63 | for(usize j = 0; j < sequence.length(); ++j)
64 | result.append(generator.escapeChar(c));
65 | i += sequence.length();
66 | continue;
67 | }
68 | }
69 |
70 | if(c == '`' && ignoreSingleBacktick)
71 | {
72 | result.append(generator.escapeChar(c));
73 | ++i;
74 | continue;
75 | }
76 |
77 | i += sequence.length();
78 | if(c == '`')
79 | {
80 | result.append(generator.getSpanStart(sequence));
81 | if(sequence.length() > 1)
82 | ignoreSingleBacktick = true;
83 | while(String::find(" \t", *i))
84 | ++i;
85 | }
86 | else
87 | result.append(generator.getSpanStart(sequence));
88 | if(!endSequence.isEmpty())
89 | endSequenceStack.append(endSequence);
90 | endSequence = sequence;
91 | continue;
92 | }
93 | if(matchInlineLink(generator, i, end, i, result))
94 | continue;
95 | if(matchInlineImage(generator, i, end, i, result))
96 | continue;
97 | if(matchLineBreak(generator, i, end, i, result))
98 | continue;
99 | if(matchInlineFootnote(generator, i, end, i, result))
100 | continue;
101 | if(matchInlineLatexFormula(generator, i, end, i, result))
102 | continue;
103 | if(c == ':' && String::isAlpha(i[1]) && i > start && !String::isAlphanumeric(i[-1]))
104 | result.append(generator.escapeChar(c) + generator.getWordBreak(c, i[1])); // allow line breaks after e.g. "::"
105 | else if(String::isAlpha(c) && String::isLowerCase(c) && String::isAlpha(i[1]) && String::isUpperCase(i[1]))
106 | result.append(generator.escapeChar(c) + generator.getWordBreak(c, i[1])); // allow line breaks in camel case
107 | else if(String::find("<({[", c) && i > start && String::isAlphanumeric(i[-1]))
108 | result.append(generator.getWordBreak(i[-1], c) + generator.escapeChar(c)); // allow line breaks before <, (, { or [
109 | else
110 | {
111 | uint32 c = Unicode::fromString(i, end - i);
112 | result.append(generator.escapeChar(c));
113 | i += Math::max(Unicode::length(*i), (usize)1);
114 | continue;
115 | }
116 | ++i;
117 | break;
118 | }
119 | }
120 | while(!endSequenceStack.isEmpty())
121 | {
122 | result.append(generator.getSpanEnd(endSequenceStack.back()));
123 | endSequenceStack.removeBack();
124 | }
125 | if(!endSequence.isEmpty())
126 | result.append(generator.getSpanEnd(endSequence));
127 | return result;
128 | }
129 |
130 | String Generator::generate(Generator& generator, const OutputData& data)
131 | {
132 | String result(data.segments.size() * 256);
133 | for(List::Iterator i = data.segments.begin(), end = data.segments.end(); i != end; ++i)
134 | {
135 | const OutputData::SegmentPtr& segment = *i;
136 | if(!segment->isValid())
137 | continue;
138 | result.append(segment->generate(generator));
139 | }
140 | return result;
141 | }
142 |
143 | bool Generator::matchInlineLink(Generator& generator, const char* s, const char* end, const char*& pos, String& result)
144 | {
145 | if(*s != '[')
146 | return false;
147 | const char* nameStart = ++s;
148 | while(*s != ']')
149 | if(++s >= end)
150 | return false;
151 | const char* nameEnd = s++;
152 | if(*s != '(')
153 | return false;
154 | const char* linkStart = ++s;
155 | const char* linkEnd = 0;
156 | while(*s != ')')
157 | {
158 | if(*s == ' ' && !linkEnd)
159 | linkEnd = s;
160 | if(++s >= end)
161 | return false;
162 | }
163 | if(!linkEnd)
164 | linkEnd = s;
165 | ++s;
166 | String link;
167 | link.attach(linkStart, linkEnd - linkStart);
168 | String name;
169 | name.attach(nameStart, nameEnd - nameStart);
170 | result.append(generator.getLink(link, name));
171 | pos = s;
172 | return true;
173 | }
174 |
175 | bool Generator::matchInlineImage(Generator& generator, const char* s, const char* end, const char*& pos, String& result)
176 | {
177 | if(*s != '!')
178 | return false;
179 | if(*(++s) != '[')
180 | return false;
181 | ++s;
182 | while(*s != ']')
183 | if(++s >= end)
184 | return false;
185 | if(*(++s) != '(')
186 | return false;
187 | const char* pathStart = ++s;
188 | const char* pathEnd = 0;
189 | while(*s != ')')
190 | {
191 | if(*s == ' ' && !pathEnd)
192 | pathEnd = s;
193 | if(++s >= end)
194 | return false;
195 | }
196 | if(!pathEnd)
197 | pathEnd = s;
198 | ++s;
199 | String path;
200 | path.attach(pathStart, pathEnd - pathStart);
201 | result.append(generator.getInlineImage(path));
202 | pos = s;
203 | return true;
204 | }
205 |
206 | bool Generator::matchLineBreak(Generator& generator, const char* s, const char* end, const char*& pos, String& result)
207 | {
208 | if(*(s++) != '<')
209 | return false;
210 | while(String::isSpace(*s) && s < end)
211 | ++s;
212 | if(*(s++) != 'b')
213 | return false;
214 | if(*(s++) != 'r')
215 | return false;
216 | while(String::isSpace(*s) && s < end)
217 | ++s;
218 | if(*(s++) != '/')
219 | return false;
220 | if(*(s++) != '>')
221 | return false;
222 | pos = s;
223 | result.append(generator.getLineBreak());
224 | return true;
225 | }
226 |
227 | bool Generator::matchInlineFootnote(Generator& generator, const char* s, const char* end, const char*& pos, String& result)
228 | {
229 | if(*(s++) != '[')
230 | return false;
231 | if(*(s++) != '^')
232 | return false;
233 | const char* textStart = s;
234 | while(*s != ']')
235 | {
236 | if(++s >= end)
237 | return false;
238 | }
239 | const char* textEnd = s++;
240 | String text;
241 | text.attach(textStart, textEnd - textStart);
242 | pos = s;
243 | result.append(generator.getFootnote(text));
244 | return true;
245 | }
246 |
247 | bool Generator::matchInlineLatexFormula(Generator& generator, const char* s, const char* end, const char*& pos, String& result)
248 | {
249 | if(*(s++) != '$')
250 | return false;
251 | const char* formulaStart = s;
252 | while(*s != '$')
253 | {
254 | if(++s >= end)
255 | return false;
256 | }
257 | const char* formulaEnd = s++;
258 | String formula;
259 | formula.attach(formulaStart, formulaEnd - formulaStart);
260 | pos = s;
261 | result.append(generator.getLatexFormula(formula));
262 | return true;
263 | }
264 |
--------------------------------------------------------------------------------
/Src/Main.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "InputData.hpp"
9 | #include "OutputData.hpp"
10 | #include "Reader.hpp"
11 | #include "Parser.hpp"
12 | #include "TexGenerator.hpp"
13 | #include "HtmlGenerator.hpp"
14 |
15 | static bool latex2pdf(const String& texFile, const String& engine, const String& auxDirectory)
16 | {
17 | String tocFile;
18 | String auxFile;
19 | {
20 | const char* end = texFile.findLastOf("\\/.");
21 | if(end)
22 | {
23 | tocFile = texFile.substr(0, end - (const char*)texFile) + ".toc";
24 | auxFile = texFile.substr(0, end - (const char*)texFile) + ".aux";
25 | }
26 | else
27 | {
28 | tocFile = texFile + ".toc";
29 | auxFile = texFile + ".aux";
30 | }
31 | }
32 |
33 | for(int run = 0; run < 5; ++run)
34 | {
35 | bool tocFileExists = File::exists(tocFile);
36 | bool auxFileExists = File::exists(auxFile);
37 | bool rerun = false;
38 |
39 | Process process;
40 | if(process.open(engine + " --interaction=scrollmode --halt-on-error --file-line-error --output-directory=\"" + auxDirectory + "\" \"" + texFile + "\"") == 0)
41 | return false;
42 |
43 | // Well I tried to set textinfo.max_print_line in an lua init script. This does not work since it is overwritten from kpathsea when it is reading its config file (texmf.cnf).
44 | // I tried to create a texmf.cnf file to overwrite max_print_line just for this build, but this does not seem to work with MiKTeX. After inspecting some MiKTeX I came to the
45 | // conclusion that there is currently no way to set max_print_line without editing the global texmfapp.ini or hacking into MiKTeX's file IO operations.
46 |
47 | // Why the fuck are TeX engines still wrapping log messages after 79 characters? We have terminal emulators and text editors that will do this for you.
48 |
49 | // Now, lets try to unfuck lualatex's output:
50 |
51 | char buffer[32 * 1024];
52 | String unhandledData;
53 | String bufferedLine;
54 | for(ssize i;;)
55 | switch((i = process.read(buffer, sizeof(buffer))))
56 | {
57 | case -1:
58 | return false;
59 | case 0:
60 | if(!bufferedLine.isEmpty() || !unhandledData.isEmpty())
61 | Console::print(bufferedLine + unhandledData + "\n");
62 | goto done;
63 | default:
64 | unhandledData.append(buffer, i);
65 |
66 | for(;;)
67 | {
68 | const char* lineEnd = unhandledData.findOneOf("\r\n");
69 | if(lineEnd)
70 | {
71 | usize lineLen = lineEnd - (const char*)unhandledData;
72 | if(lineLen == 79)
73 | bufferedLine.append(unhandledData.substr(0, lineLen));
74 | else
75 | {
76 | String line = bufferedLine + unhandledData.substr(0, lineLen);
77 | if(line.find("Rerun to get") || line.find("run LaTeX again"))
78 | rerun = true;
79 |
80 | Console::print(line + "\n");
81 | bufferedLine.clear();
82 | }
83 | if(*lineEnd == '\r' && lineEnd[1] == '\n')
84 | ++lineLen;
85 | unhandledData = unhandledData.substr(lineLen + 1);
86 | }
87 | else
88 | break;
89 | }
90 | break;
91 | }
92 |
93 | done: ;
94 |
95 | uint32 exitCode;
96 | if(!process.join(exitCode))
97 | return false;
98 | if(exitCode != 0)
99 | return false;
100 |
101 | if((!tocFileExists && File::exists(tocFile)) ||
102 | (!auxFileExists && File::exists(auxFile)))
103 | rerun = true;
104 |
105 | if(!rerun)
106 | break;
107 | }
108 |
109 | return true;
110 | }
111 |
112 | int main(int argc, char* argv[])
113 | {
114 | String inputFile("umdoc.xml");
115 | String outputFile;
116 | String engine("xelatex");
117 | String auxDirectory;
118 | HashMap variables;
119 |
120 | {
121 | Process::Option options[] = {
122 | {'o', "output", Process::argumentFlag},
123 | {'e', "engine", Process::argumentFlag},
124 | {'a', "aux-directory", Process::argumentFlag},
125 | {'h', "help", Process::optionFlag},
126 | {1000, "version", Process::optionFlag},
127 | };
128 | Process::Arguments arguments(argc, argv, options);
129 | int character;
130 | String argument;
131 | while(arguments.read(character, argument))
132 | switch(character)
133 | {
134 | case 'o':
135 | outputFile = argument;
136 | break;
137 | case 'e':
138 | engine = argument;
139 | break;
140 | case 'a':
141 | auxDirectory = argument;
142 | break;
143 | case '?':
144 | if(argument.startsWith("--"))
145 | {
146 | const char* i = argument.find('=');
147 | if(i)
148 | {
149 | variables.append(argument.substr(2, i - (const char*)argument - 2), argument.substr(i - (const char*)argument + 1));
150 | continue;
151 | }
152 | }
153 | Console::errorf("Unknown option: %s.\n", (const char*)argument);
154 | return 1;
155 | case ':':
156 | Console::errorf("Option %s required an argument.\n", (const char*)argument);
157 | return 1;
158 | case 1000:
159 | Console::errorf("umdoc %s\n", VERSION);
160 | return 0;
161 | case '\0':
162 | inputFile = argument;
163 | break;
164 | default:
165 | Console::errorf("Usage: %s [] [-a ] [-e ]\n [-o ] [--=]\n", argv[0]);
166 | return 1;
167 | }
168 | }
169 |
170 | if(outputFile.isEmpty())
171 | {
172 | const char* end = inputFile.findLastOf("\\/.");
173 | if(end && *end == '.')
174 | outputFile = inputFile.substr(0, end - (const char*)inputFile) + ".pdf";
175 | else
176 | outputFile = inputFile + ".pdf";
177 | }
178 |
179 | if(auxDirectory.isEmpty())
180 | {
181 | const char* end = outputFile.findLastOf("\\/.");
182 | if(end && *end == '.')
183 | auxDirectory = outputFile.substr(0, end - (const char*)outputFile);
184 | else
185 | auxDirectory = outputFile;
186 | }
187 |
188 | String tmpTexFile;
189 | String tmpPdfFile;
190 | {
191 | String inputFileBasename = File::basename(inputFile);
192 | const char* end = inputFileBasename.findLast('.');
193 | if(end)
194 | inputFileBasename = inputFileBasename.substr(0, end - (const char*)inputFileBasename);
195 | tmpTexFile = auxDirectory + "/" + inputFileBasename + ".tex";
196 | tmpPdfFile = auxDirectory + "/" + inputFileBasename + ".pdf";
197 | }
198 |
199 | // change working directory to the directory of the input fle
200 | String inputFileDir = File::dirname(inputFile);
201 | if(inputFileDir != ".")
202 | {
203 | // convert input and output files to absolute paths
204 | inputFile = File::getAbsolutePath(inputFile);
205 | outputFile = File::getAbsolutePath(outputFile);
206 | auxDirectory = File::getAbsolutePath(auxDirectory);
207 | tmpTexFile = File::getAbsolutePath(tmpTexFile);
208 | tmpPdfFile = File::getAbsolutePath(tmpPdfFile);
209 |
210 | if(!Directory::change(inputFileDir))
211 | {
212 | Console::errorf("%s: error: %s\n", (const char*)inputFile, (const char*)Error::getErrorString());
213 | return 1;
214 | }
215 | }
216 |
217 | //
218 | InputData inputData;
219 | OutputData outputData;
220 |
221 | // read input file
222 | {
223 | Reader reader;
224 | if(!reader.read(inputFile, inputData))
225 | {
226 | Console::errorf("%s:%d:%d: error: %s\n", (const char*)inputFile, reader.getErrorLine(), reader.getErrorColumn(), (const char*)reader.getErrorString());
227 | return 1;
228 | }
229 | }
230 |
231 | // set variables
232 | for(HashMap::Iterator i = variables.begin(), end = variables.end(); i != end; ++i)
233 | {
234 | if(inputData.variables.find(i.key()) == inputData.variables.end())
235 | {
236 | Console::errorf("Unknown option: %s.\n", (const char*)i.key());
237 | return 1;
238 | }
239 | inputData.variables.append(i.key(), *i);
240 | }
241 |
242 | // parse input data
243 | {
244 | Parser parser;
245 | if(!parser.parse(inputData, outputFile, outputData))
246 | {
247 | Console::errorf("%s:%d: error: %s\n", (const char*)parser.getErrorFile(), parser.getErrorLine(), (const char*)parser.getErrorString());
248 | return 1;
249 | }
250 | }
251 |
252 | // generate html
253 | if(outputData.info.format == OutputData::htmlFormat)
254 | {
255 | HtmlGenerator generator;
256 | if(!generator.generate(outputData, outputFile))
257 | {
258 | Console::errorf("%s: error: %s\n", (const char*)tmpTexFile, (const char*)generator.getErrorString());
259 | return 1;
260 | }
261 | return 0;
262 | }
263 |
264 | // generate tmp tex file
265 | if(!Directory::exists(auxDirectory) && !Directory::create(auxDirectory))
266 | {
267 | Console::errorf("%s: error: %s\n", (const char*)auxDirectory, (const char*)Error::getErrorString());
268 | return 1;
269 | }
270 | {
271 | TexGenerator generator;
272 | if(!generator.generate(engine, outputData, outputFile.endsWith(".tex") ? outputFile : tmpTexFile))
273 | {
274 | Console::errorf("%s: error: %s\n", (const char*)tmpTexFile, (const char*)generator.getErrorString());
275 | return 1;
276 | }
277 | }
278 | if(outputFile.endsWith(".tex"))
279 | return 0;
280 |
281 | // covnert tmp tex file to tmp pdf
282 | if(!latex2pdf(tmpTexFile, engine, auxDirectory))
283 | return 1;
284 |
285 | // copy tmp pdf to output file
286 | if(tmpPdfFile != outputFile)
287 | {
288 | String pdfData;
289 | File pdfFile;
290 | if(!pdfFile.open(tmpPdfFile) ||
291 | !pdfFile.readAll(pdfData))
292 | {
293 | Console::errorf("%s: error: %s\n", (const char*)tmpPdfFile, (const char*)Error::getErrorString());
294 | return 1;
295 | }
296 | File file;
297 | if(!file.open(outputFile, File::writeFlag) ||
298 | !file.write(pdfData))
299 | {
300 | Console::errorf("%s: error: %s\n", (const char*)outputFile, (const char*)Error::getErrorString());
301 | return 1;
302 | }
303 | }
304 |
305 | return 0;
306 | }
307 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/Examples/latexclass/exampleclass.cls:
--------------------------------------------------------------------------------
1 | \NeedsTeXFormat{LaTeX2e}
2 | \ProvidesClass{exampleclass}
3 |
4 | \LoadClass[a4paper]{article}
5 |
6 | \usepackage[english]{babel}
7 |
8 | \usepackage{geometry}
9 | \geometry{
10 | a4paper,
11 | left=25mm,
12 | right=25mm,
13 | top=38mm,
14 | bottom=25mm,
15 | }
16 |
17 | \usepackage{hyperref}
18 | \hypersetup{%
19 | colorlinks,
20 | citecolor=black,
21 | filecolor=black,
22 | linkcolor=black,
23 | urlcolor=blue
24 | }
25 |
26 | \let\oldhyperref\hyperref
27 | \renewcommand{\hyperref}[2][]{\oldhyperref[#1]{\color{blue}#2}}
28 |
29 | \usepackage{graphicx}
30 |
31 | \usepackage{enumitem}
32 | \setlist{topsep=0pt}
33 |
34 | \usepackage{quoting}
35 |
36 | \usepackage[default,osf]{sourcesanspro}
37 | \usepackage[scaled=.8]{sourcecodepro}
38 |
39 | \setlength\parindent{0pt}
40 | \setlength\parskip{5pt}
41 |
42 | \usepackage{titlesec}
43 |
44 | \setcounter{secnumdepth}{4}
45 | \titleformat{\paragraph}{\normalfont\normalsize\bfseries}{\theparagraph}{1em}{}
46 | \titleformat{\subparagraph}{\normalfont\normalsize\bfseries}{\thesubparagraph}{1em}{}
47 | \usepackage{float}
48 |
49 | \usepackage{listings}
50 |
51 | \usepackage{xcolor}
52 | \usepackage{environ}
53 |
54 | \newcommand\HorizontalRule{\raisebox{3.5pt}[1.5ex]{\rule{\linewidth}{0.4pt}}}
55 |
56 | \newcommand\InlineImage[1]{\raisebox{-0.1em}{\includegraphics[height=0.9em]{#1}}}
57 |
58 | \usepackage{array}
59 | \renewcommand{\extrarowheight}{2pt}
60 |
61 | \definecolor{boxBackgroundColor}{RGB}{245,245,245}
62 | \definecolor{boxFrameColor}{RGB}{128,128,128}
63 | \definecolor{codeRedColor}{RGB}{163,21,21}
64 | \definecolor{codeBlueColor}{RGB}{0,0,255}
65 | \definecolor{codeGreenColor}{RGB}{0,128,0}
66 | \lstdefinelanguage{XML}{ basicstyle=\ttfamily\color{codeBlueColor}, morestring=[b]", moredelim=[s][\color{codeBlueColor}]{<}{\ }, moredelim=[s][\color{codeBlueColor}]{}{>}, morecomment=[s]{}{?>}, morecomment=[s]{}, commentstyle=\color{codeGreenColor}, stringstyle=\color{codeRedColor}, identifierstyle=\color{red}}
67 |
68 | \newcommand\EnvironmentCaption[1]{\parbox{\textwidth}{\textbf{#1}}}
69 |
70 | \lstset{frame=single,basicstyle=\ttfamily,breaklines=true,showstringspaces=false,backgroundcolor=\color{boxBackgroundColor},rulecolor=\color{boxFrameColor},keywordstyle=\color{codeBlueColor},stringstyle=\color{codeRedColor},commentstyle=\color{codeGreenColor}}
71 | \lstnewenvironment{plain}{\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
72 | \lstnewenvironment{abap}[1][]{\lstset{language=ABAP,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
73 | \lstnewenvironment{acm}[1][]{\lstset{language=ACM,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
74 | \lstnewenvironment{acmscript}[1][]{\lstset{language=ACMscript,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
75 | \lstnewenvironment{acsl}[1][]{\lstset{language=ACSL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
76 | \lstnewenvironment{ada}[1][]{\lstset{language=Ada,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
77 | \lstnewenvironment{algol}[1][]{\lstset{language=Algol,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
78 | \lstnewenvironment{ant}[1][]{\lstset{language=Ant,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
79 | \lstnewenvironment{assembler}[1][]{\lstset{language=Assembler,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
80 | \lstnewenvironment{awk}[1][]{\lstset{language=Awk,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
81 | \lstnewenvironment{bash}[1][]{\lstset{language=bash,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
82 | \lstnewenvironment{basic}[1][]{\lstset{language=Basic,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
83 | \lstnewenvironment{clanguage}[1][]{\lstset{language=C,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
84 | \lstnewenvironment{cplusplus}[1][]{\lstset{language=C++,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
85 | \lstnewenvironment{caml}[1][]{\lstset{language=Caml,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
86 | \lstnewenvironment{cil}[1][]{\lstset{language=CIL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
87 | \lstnewenvironment{clean}[1][]{\lstset{language=Clean,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
88 | \lstnewenvironment{cobol}[1][]{\lstset{language=Cobol,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
89 | \lstnewenvironment{comal80}[1][]{\lstset{language=Comal 80,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
90 | \lstnewenvironment{commandcom}[1][]{\lstset{language=command.com,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
91 | \lstnewenvironment{comsol}[1][]{\lstset{language=Comsol,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
92 | \lstnewenvironment{csh}[1][]{\lstset{language=csh,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
93 | \lstnewenvironment{delphi}[1][]{\lstset{language=Delphi,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
94 | \lstnewenvironment{eiffel}[1][]{\lstset{language=Eiffel,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
95 | \lstnewenvironment{elan}[1][]{\lstset{language=Elan,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
96 | \lstnewenvironment{erlang}[1][]{\lstset{language=erlang,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
97 | \lstnewenvironment{euphoria}[1][]{\lstset{language=Euphoria,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
98 | \lstnewenvironment{fortran}[1][]{\lstset{language=Fortran,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
99 | \lstnewenvironment{gcl}[1][]{\lstset{language=GCL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
100 | \lstnewenvironment{gnuplot}[1][]{\lstset{language=Gnuplot,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
101 | \lstnewenvironment{hansl}[1][]{\lstset{language=hansl,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
102 | \lstnewenvironment{haskell}[1][]{\lstset{language=Haskell,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
103 | \lstnewenvironment{html}[1][]{\lstset{language=HTML,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
104 | \lstnewenvironment{idl}[1][]{\lstset{language=IDL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
105 | \lstnewenvironment{inform}[1][]{\lstset{language=inform,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
106 | \lstnewenvironment{java}[1][]{\lstset{language=Java,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
107 | \lstnewenvironment{jvmis}[1][]{\lstset{language=JVMIS,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
108 | \lstnewenvironment{ksh}[1][]{\lstset{language=ksh,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
109 | \lstnewenvironment{lingo}[1][]{\lstset{language=Lingo,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
110 | \lstnewenvironment{lisp}[1][]{\lstset{language=Lisp,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
111 | \lstnewenvironment{llvm}[1][]{\lstset{language=LLVM,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
112 | \lstnewenvironment{logo}[1][]{\lstset{language=Logo,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
113 | \lstnewenvironment{lua}[1][]{\lstset{language=Lua,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
114 | \lstnewenvironment{make}[1][]{\lstset{language=make,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
115 | \lstnewenvironment{matlab}[1][]{\lstset{language=Matlab,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
116 | \lstnewenvironment{mathematica}[1][]{\lstset{language=Mathematica,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
117 | \lstnewenvironment{mercury}[1][]{\lstset{language=Mercury,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
118 | \lstnewenvironment{metapost}[1][]{\lstset{language=MetaPost,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
119 | \lstnewenvironment{miranda}[1][]{\lstset{language=Miranda,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
120 | \lstnewenvironment{mizar}[1][]{\lstset{language=Mizar,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
121 | \lstnewenvironment{ml}[1][]{\lstset{language=ML,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
122 | \lstnewenvironment{modula2}[1][]{\lstset{language=Modula-2,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
123 | \lstnewenvironment{mupad}[1][]{\lstset{language=MuPAD,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
124 | \lstnewenvironment{nastran}[1][]{\lstset{language=NASTRAN,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
125 | \lstnewenvironment{oberon2}[1][]{\lstset{language=Oberon-2,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
126 | \lstnewenvironment{ocl}[1][]{\lstset{language=OCL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
127 | \lstnewenvironment{octave}[1][]{\lstset{language=Octave,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
128 | \lstnewenvironment{oz}[1][]{\lstset{language=Oz,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
129 | \lstnewenvironment{perl}[1][]{\lstset{language=Perl,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
130 | \lstnewenvironment{pascal}[1][]{\lstset{language=Pascal,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
131 | \lstnewenvironment{php}[1][]{\lstset{language=PHP,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
132 | \lstnewenvironment{pli}[1][]{\lstset{language=PL/I,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
133 | \lstnewenvironment{plasm}[1][]{\lstset{language=Plasm,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
134 | \lstnewenvironment{postscript}[1][]{\lstset{language=PostScript,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
135 | \lstnewenvironment{pov}[1][]{\lstset{language=POV,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
136 | \lstnewenvironment{prolog}[1][]{\lstset{language=Prolog,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
137 | \lstnewenvironment{promela}[1][]{\lstset{language=Promela,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
138 | \lstnewenvironment{pstricks}[1][]{\lstset{language=PSTricks,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
139 | \lstnewenvironment{python}[1][]{\lstset{language=Python,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
140 | \lstnewenvironment{rlanguage}[1][]{\lstset{language=R,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
141 | \lstnewenvironment{reduce}[1][]{\lstset{language=Reduce,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
142 | \lstnewenvironment{rexx}[1][]{\lstset{language=Rexx,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
143 | \lstnewenvironment{rsl}[1][]{\lstset{language=RSL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
144 | \lstnewenvironment{ruby}[1][]{\lstset{language=Ruby,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
145 | \lstnewenvironment{slanguage}[1][]{\lstset{language=S,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
146 | \lstnewenvironment{sas}[1][]{\lstset{language=SAS,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
147 | \lstnewenvironment{scala}[1][]{\lstset{language=Scala,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
148 | \lstnewenvironment{scilab}[1][]{\lstset{language=Scilab,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
149 | \lstnewenvironment{sh}[1][]{\lstset{language=sh,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
150 | \lstnewenvironment{shelxl}[1][]{\lstset{language=SHELXL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
151 | \lstnewenvironment{sparql}[1][]{\lstset{language=SPARQL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
152 | \lstnewenvironment{simula}[1][]{\lstset{language=Simula,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
153 | \lstnewenvironment{sql}[1][]{\lstset{language=SQL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
154 | \lstnewenvironment{tcl}[1][]{\lstset{language=tcl,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
155 | \lstnewenvironment{tex}[1][]{\lstset{language=TeX,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
156 | \lstnewenvironment{vbscript}[1][]{\lstset{language=VBScript,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
157 | \lstnewenvironment{verilog}[1][]{\lstset{language=Verilog,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
158 | \lstnewenvironment{vhdl}[1][]{\lstset{language=VHDL,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
159 | \lstnewenvironment{vrml}[1][]{\lstset{language=VRML,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
160 | \lstnewenvironment{xml}[1][]{\lstset{language=XML,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
161 | \lstnewenvironment{xslt}[1][]{\lstset{language=XSLT,#1}\vspace{\parskip}\minipage{\linewidth}}{\endminipage}
162 |
163 | \NewEnviron{boxed}[1][]{\vspace{\parskip}\hspace{-3.4pt}\fcolorbox{boxFrameColor}{white}{\minipage{\linewidth}
164 | \vspace{3.3pt}\BODY
165 | \vspace{3.4pt}\endminipage}\vspace{3.3pt}}
166 |
167 | \let\oldtexttt\texttt
168 | \renewcommand{\texttt}[1]{\fcolorbox{boxFrameColor}{boxBackgroundColor}{\raisebox{0pt}[0.45em][0pt]{\oldtexttt{#1}}}}
169 |
--------------------------------------------------------------------------------
/CDeploy:
--------------------------------------------------------------------------------
1 |
2 | function(deploy_package package version)
3 |
4 | include(CMakeParseArguments)
5 |
6 | set(options NO_OS NO_ARCH NO_COMPILER NO_CACHE)
7 | set(oneValueArgs)
8 | set(multiValueArgs)
9 | cmake_parse_arguments(_ "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
10 |
11 | if(MSVC)
12 | foreach(config ${CMAKE_CONFIGURATION_TYPES})
13 | if(NOT "${config}" STREQUAL "Debug")
14 | string(TOUPPER "${config}" config_upper)
15 | set(CMAKE_MAP_IMPORTED_CONFIG_${config_upper} Release)
16 | endif()
17 | endforeach()
18 | endif()
19 |
20 | if(NOT __NO_CACHE)
21 | find_package(${package} ${version} EXACT QUIET CONFIG NO_DEFAULT_PATH)
22 | endif()
23 |
24 | if(NOT ${package}_FOUND)
25 |
26 | string(TOLOWER "${package}" filename)
27 | set(filename "${filename}-${version}")
28 | if(NOT __NO_OS)
29 | set(filename "${filename}-${CDEPLOY_OS}")
30 | endif()
31 | if(NOT __NO_ARCH)
32 | set(filename "${filename}-${CDEPLOY_ARCH}")
33 | endif()
34 | if(NOT __NO_COMPILER)
35 | set(filename "${filename}-${CDEPLOY_COMPILER}")
36 | endif()
37 | set(filename "${filename}.zip")
38 |
39 | if(NOT CDEPLOY_CACHE_DIR)
40 | set(CDEPLOY_CACHE_DIR "$ENV{CDEPLOY_CACHE_DIR}")
41 | if(NOT CDEPLOY_CACHE_DIR)
42 | if(WIN32)
43 | set(CDEPLOY_CACHE_DIR "$ENV{USERPROFILE}/.cmake/downloadcache")
44 | else()
45 | set(CDEPLOY_CACHE_DIR "$ENV{HOME}")
46 | if(NOT CDEPLOY_CACHE_DIR OR "${CDEPLOY_CACHE_DIR}" STREQUAL "/")
47 | set(CDEPLOY_CACHE_DIR "/tmp")
48 | else()
49 | set(CDEPLOY_CACHE_DIR "${CDEPLOY_CACHE_DIR}/.cmake/downloadcache")
50 | endif()
51 | endif()
52 | endif()
53 | endif()
54 |
55 | set(cache_file "${CDEPLOY_CACHE_DIR}/${filename}")
56 | if(NOT __NO_CACHE AND EXISTS "${cache_file}")
57 | message("-- Found ${filename} in cache")
58 | else()
59 |
60 | set(repository "${__UNPARSED_ARGUMENTS}")
61 | if(NOT repository)
62 | if(CDEPLOY_REPOSITORY)
63 | set(repository "${CDEPLOY_REPOSITORY}")
64 | else()
65 | set(repository "$ENV{CDEPLOY_REPOSITORY}")
66 | endif()
67 | endif()
68 |
69 | set(url "${repository}/${filename}")
70 | message("-- Downloading ${url}")
71 | file(DOWNLOAD "${url}" "${cache_file}.part" STATUS download_status)
72 | list(GET download_status 0 _download_result)
73 | if(NOT ${_download_result} EQUAL 0)
74 | list(GET download_status 1 _download_error)
75 | message(FATAL_ERROR "Could not download: ${_download_error} (${_download_result})")
76 | return()
77 | endif()
78 | file(RENAME "${cache_file}.part" "${cache_file}")
79 | endif()
80 |
81 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar tf "${cache_file}"
82 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
83 | OUTPUT_VARIABLE _unzip_output
84 | RESULT_VARIABLE _unzip_result)
85 | if(NOT ${_unzip_result} EQUAL 0)
86 | message(FATAL_ERROR "Could not extract file")
87 | return()
88 | endif()
89 | string(REGEX REPLACE "([^/]+).*" "\\1" _extract_dir "${_unzip_output}")
90 |
91 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${cache_file}"
92 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
93 | RESULT_VARIABLE _unzip_result)
94 | if(NOT ${_unzip_result} EQUAL 0)
95 | message(FATAL_ERROR "Could not extract file")
96 | return()
97 | endif()
98 |
99 | set(package_folder "${CMAKE_BINARY_DIR}/${_extract_dir}")
100 |
101 | find_package(${package} ${version} EXACT QUIET CONFIG REQUIRED
102 | PATHS "${package_folder}" NO_DEFAULT_PATH
103 | )
104 |
105 | endif()
106 | endfunction()
107 |
108 | function(get_target_arch var)
109 | set(${var} ${CMAKE_SYSTEM_PROCESSOR})
110 | if(WIN32)
111 | if(MSVC)
112 | if(CMAKE_CL_64)
113 | set(${var} x64)
114 | else()
115 | set(${var} x86)
116 | endif()
117 | elseif(CMAKE_COMPILER_IS_GNUCC) # CMAKE_CXX_COMPILER_ARCHITECTURE_ID does not provide anything with MinGW compilers
118 | execute_process(COMMAND "${CMAKE_CXX_COMPILER}" -v OUTPUT_VARIABLE GCC_VERSION_OUTPUT ERROR_VARIABLE GCC_VERSION_OUTPUT)
119 | if(GCC_VERSION_OUTPUT MATCHES ".*x86_64.*")
120 | set(${var} x64)
121 | else()
122 | set(${var} x86)
123 | endif()
124 | endif()
125 | endif()
126 | set(${var} ${${var}} PARENT_SCOPE)
127 | endfunction()
128 |
129 | function(get_target_compiler var)
130 | if(CMAKE_COMPILER_IS_GNUCC)
131 | string(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gcc_version "${CMAKE_CXX_COMPILER_VERSION}")
132 | if(NOT gcc_version VERSION_LESS "5.0")
133 | string(REGEX REPLACE "([0-9]+).*" "\\1" gcc_version "${gcc_version}")
134 | endif()
135 | set(${var} "gcc${gcc_version}")
136 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
137 | string(REGEX REPLACE "([0-9]+).*" "clang\\1" ${var} "${CMAKE_CXX_COMPILER_VERSION}")
138 | elseif(MSVC)
139 | set(MSVC_YEARS 2008 2010 2012 2013 2015 2017 2019)
140 | set(MSVC_MSC_VERS 150 160 170 180 190 191 192)
141 | string(SUBSTRING "${MSVC_VERSION}" 0 3 MSVC_VER_SHORT)
142 | list(FIND MSVC_MSC_VERS ${MSVC_VER_SHORT} MSVC_INDEX)
143 | list(GET MSVC_YEARS ${MSVC_INDEX} MSVS_YEAR)
144 | set(${var} vs${MSVS_YEAR})
145 | else()
146 | set(${var} ${CMAKE_CXX_COMPILER_ID}${CMAKE_CXX_COMPILER_VERSION})
147 | endif()
148 | set(${var} ${${var}} PARENT_SCOPE)
149 | endfunction()
150 |
151 | function(get_target_os var)
152 | if(WIN32)
153 | set(${var} windows)
154 | elseif(APPLE)
155 | set(${var} macos)
156 | else()
157 | file(GLOB ETC_RELEASE_FILES /etc/*-release)
158 | list(REMOVE_ITEM ETC_RELEASE_FILES /etc/lsb-release)
159 | list(GET ETC_RELEASE_FILES 0 ETC_RELEASE_FILE)
160 | file(READ "${ETC_RELEASE_FILE}" ETC_RELEASE_FILE_CONTENT)
161 | string(REGEX REPLACE "=|\n" ";" ETC_RELEASE_FILE_CONTENT "${ETC_RELEASE_FILE_CONTENT}")
162 | string(REGEX REPLACE "; +| +;" ";" ETC_RELEASE_FILE_CONTENT "${ETC_RELEASE_FILE_CONTENT}")
163 | list(FIND ETC_RELEASE_FILE_CONTENT "NAME" NAME_INDEX)
164 | if(NAME_INDEX GREATER -1)
165 | math(EXPR NAME_INDEX "${NAME_INDEX} + 1")
166 | list(GET ETC_RELEASE_FILE_CONTENT "${NAME_INDEX}" ETC_RELEASE_NAME)
167 | else()
168 | list(GET ETC_RELEASE_FILE_CONTENT 0 ETC_RELEASE_NAME)
169 | endif()
170 | string(REGEX MATCH "[^ \"]+" ETC_RELEASE_NAME "${ETC_RELEASE_NAME}")
171 | list(FIND ETC_RELEASE_FILE_CONTENT "VERSION_ID" VERSION_INDEX)
172 | if(NOT VERSION_INDEX GREATER -1)
173 | list(FIND ETC_RELEASE_FILE_CONTENT "VERSION" VERSION_INDEX)
174 | endif()
175 | if(VERSION_INDEX GREATER -1)
176 | math(EXPR VERSION_INDEX "${VERSION_INDEX} + 1")
177 | list(GET ETC_RELEASE_FILE_CONTENT "${VERSION_INDEX}" ETC_RELEASE_VERSION)
178 | string(REGEX MATCH "[^ \"]+" ETC_RELEASE_VERSION "${ETC_RELEASE_VERSION}")
179 | else()
180 | string(REGEX MATCH "[0-9]+" ETC_RELEASE_VERSION "${ETC_RELEASE_FILE_CONTENT}")
181 | endif()
182 | string(TOLOWER "${ETC_RELEASE_NAME}${ETC_RELEASE_VERSION}" ${var})
183 | endif()
184 | set(${var} ${${var}} PARENT_SCOPE)
185 | endfunction()
186 |
187 | get_target_arch(CDEPLOY_ARCH)
188 | get_target_compiler(CDEPLOY_COMPILER)
189 | get_target_os(CDEPLOY_OS)
190 |
191 | set(CPACK_GENERATOR "ZIP")
192 | string(TOLOWER "${PROJECT_NAME}" CPACK_PACKAGE_FILE_NAME)
193 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${PROJECT_VERSION}")
194 | if(NOT CDEPLOY_NO_OS)
195 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_OS}")
196 | endif()
197 | if(NOT CDEPLOY_NO_ARCH)
198 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_ARCH}")
199 | endif()
200 | if(NOT CDEPLOY_NO_COMPILER)
201 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_COMPILER}")
202 | endif()
203 |
204 | if(MSVC AND NOT CDEPLOY_NO_DEBUG_BUILD)
205 |
206 | set_property(GLOBAL PROPERTY USE_FOLDERS ON)
207 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER ".cmake")
208 |
209 | include(ExternalProject)
210 |
211 | ExternalProject_Add(DEBUG_BUILD
212 | SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
213 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build-debug"
214 | CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/install-debug" -DCDEPLOY_NO_DEBUG_BUILD=True
215 | BUILD_COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/build-debug" --config Debug
216 | INSTALL_COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/build-debug" --config Debug --target install
217 | BUILD_ALWAYS True
218 | )
219 | file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/install-debug")
220 | install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/install-debug/" DESTINATION . USE_SOURCE_PERMISSIONS)
221 |
222 | set_property(TARGET DEBUG_BUILD PROPERTY FOLDER ".cmake")
223 | if(NOT CDEPLOY_DEBUG_BUILD)
224 | set_property(TARGET DEBUG_BUILD PROPERTY EXCLUDE_FROM_DEFAULT_BUILD True)
225 | set_property(TARGET DEBUG_BUILD PROPERTY EXCLUDE_FROM_ALL True)
226 | endif()
227 |
228 | endif()
229 | if(CDEPLOY_DEBUG_BUILD)
230 | endif()
231 |
232 | if(MSVC)
233 | set(CMAKE_DEBUG_POSTFIX d)
234 | endif()
235 |
236 | file(REMOVE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in")
237 |
238 | function(deploy_export_init)
239 |
240 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in")
241 | file(WRITE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "@PACKAGE_INIT@\n\n")
242 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "set_and_check(INSTALL_DIR \"@PACKAGE_INSTALL_DIR@\")\n\n")
243 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "include(CMakeFindDependencyMacro)\n\n")
244 | endif()
245 |
246 | endfunction()
247 |
248 | function(deploy_export_dependency name)
249 | deploy_export_init()
250 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "find_dependency(${name} ${ARGN})\n\n")
251 | endfunction()
252 |
253 | function(deploy_export name)
254 |
255 | include(CMakeParseArguments)
256 |
257 | set(options INTERFACE LIBRARY STATIC SHARED EXECUTABLE)
258 | set(oneValueArgs CONFIGURATION IMPORTED_LOCATION IMPORTED_IMPLIB)
259 | set(multiValueArgs INTERFACE_INCLUDE_DIRECTORIES INTERFACE_SOURCES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES PROPERTIES)
260 | cmake_parse_arguments(_ "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
261 |
262 | deploy_export_init()
263 |
264 | set(target_flags)
265 | if(__STATIC)
266 | set(target_flags "${target_flags} STATIC")
267 | endif()
268 | if(__SHARED)
269 | set(target_flags "${target_flags} SHARED")
270 | endif()
271 | if(__INTERFACE)
272 | set(target_flags "${target_flags} INTERFACE")
273 | endif()
274 |
275 | set(target_config)
276 | if(__CONFIGURATION)
277 | string(TOUPPER "${__CONFIGURATION}" __CONFIGURATION)
278 | set(target_config "_${__CONFIGURATION}")
279 | endif()
280 |
281 | set(target_properties)
282 | if(__IMPORTED_LOCATION)
283 | set(target_properties "${target_properties} IMPORTED_LOCATION${target_config} \"\${INSTALL_DIR}/${__IMPORTED_LOCATION}\"")
284 | endif()
285 | if(__IMPORTED_IMPLIB)
286 | set(target_properties "${target_properties} IMPORTED_IMPLIB${target_config} \"\${INSTALL_DIR}/${__IMPORTED_IMPLIB}\"")
287 | endif()
288 | if(__INTERFACE_INCLUDE_DIRECTORIES)
289 | set(target_properties "${target_properties} INTERFACE_INCLUDE_DIRECTORIES \"")
290 | foreach(dir ${__INTERFACE_INCLUDE_DIRECTORIES})
291 | set(target_properties "${target_properties}\${INSTALL_DIR}/${dir};")
292 | endforeach()
293 | set(target_properties "${target_properties}\"")
294 | endif()
295 | if(__INTERFACE_SOURCES)
296 | set(target_properties "${target_properties} INTERFACE_SOURCES \"")
297 | foreach(file ${__INTERFACE_SOURCES})
298 | set(target_properties "${target_properties}\${INSTALL_DIR}/${file};")
299 | endforeach()
300 | set(target_properties "${target_properties}\"")
301 | endif()
302 | foreach(property INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES)
303 | if(__${property})
304 | set(target_properties "${target_properties} ${property} \"")
305 | foreach(arg ${__${property}})
306 | set(target_properties "${target_properties}${arg};")
307 | endforeach()
308 | set(target_properties "${target_properties}\"")
309 | endif()
310 | endforeach()
311 | if(__PROPERTIES)
312 | foreach(arg ${__PROPERTIES})
313 | set(target_properties "${target_properties} ${arg}")
314 | endforeach()
315 | endif()
316 |
317 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "if(NOT TARGET ${PROJECT_NAME}::${name})\n")
318 | if(__LIBRARY OR __INTERFACE)
319 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " add_library(${PROJECT_NAME}::${name} ${target_flags} IMPORTED GLOBAL)\n")
320 | else()
321 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " add_executable(${PROJECT_NAME}::${name} IMPORTED GLOBAL)\n")
322 | endif()
323 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "endif()\n")
324 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "set_target_properties(${PROJECT_NAME}::${name}\n")
325 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " PROPERTIES\n")
326 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " ${target_properties}\n")
327 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" ")\n")
328 | if(__CONFIGURATION)
329 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "set_property(TARGET ${PROJECT_NAME}::${name} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${__CONFIGURATION})\n")
330 | endif()
331 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "\n")
332 |
333 | endfunction()
334 |
335 | function(install_deploy_export)
336 | include(CMakePackageConfigHelpers)
337 | if(EXISTS "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in")
338 | set(INSTALL_DIR .)
339 | configure_package_config_file("${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "${PROJECT_NAME}Config.cmake"
340 | INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}"
341 | PATH_VARS
342 | INSTALL_DIR
343 | )
344 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake" DESTINATION "lib/cmake/${PROJECT_NAME}")
345 | else()
346 | install(EXPORT ${PROJECT_NAME}Config
347 | DESTINATION "lib/cmake/${PROJECT_NAME}"
348 | NAMESPACE ${PROJECT_NAME}::
349 | )
350 | endif()
351 | write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" COMPATIBILITY ExactVersion)
352 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION "lib/cmake/${PROJECT_NAME}")
353 | endfunction()
354 |
355 |
--------------------------------------------------------------------------------
/Src/HtmlGenerator.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "HtmlGenerator.hpp"
3 | #include "OutputData.hpp"
4 | #include "PlainGenerator.hpp"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | void HtmlGenerator::findNumbers(const List& segments, LastNumbers& lastNumbers)
12 | {
13 | for(List::Iterator i = segments.begin(), end = segments.end(); i != end; ++i)
14 | {
15 | const OutputData::SegmentPtr& segment = *i;
16 | if(!segment->isValid())
17 | continue;
18 | const OutputData::TitleSegment* titleSegment = dynamic_cast(&*segment);
19 | if(titleSegment)
20 | {
21 | if(titleSegment->_level >= 5 ||
22 | titleSegment->_arguments.contains(".unnumbered"))
23 | continue;
24 | if((usize)titleSegment->_level > lastNumbers.title.size())
25 | lastNumbers.title.resize(titleSegment->_level, 1);
26 | else
27 | {
28 | lastNumbers.title.resize(titleSegment->_level);
29 | ++lastNumbers.title.back();
30 | }
31 | _titleNumbers.append(titleSegment, lastNumbers.title);
32 | String label = titleSegment->_arguments.find("#")->toString();
33 | if(!label.isEmpty())
34 | _numbers.append(label, lastNumbers.title);
35 | }
36 | const OutputData::FigureSegment* figureSegment = dynamic_cast(&*segment);
37 | if(figureSegment)
38 | {
39 | _figureNumbers.append(figureSegment, ++lastNumbers.figure);
40 | String label = figureSegment->_arguments.find("#")->toString();
41 | if(!label.isEmpty())
42 | _numbers.append(label, lastNumbers.figure);
43 | }
44 | const OutputData::TableSegment* tableSegment = dynamic_cast(&*segment);
45 | if(tableSegment && tableSegment->_captionSegment)
46 | {
47 | _tableNumbers.append(tableSegment, ++lastNumbers.table);
48 | String label = tableSegment->_arguments.find("#")->toString();
49 | if(!label.isEmpty())
50 | _numbers.append(label, lastNumbers.table);
51 | }
52 | const OutputData::EnvironmentSegment* environmentSegment = dynamic_cast(&*segment);
53 | if(environmentSegment)
54 | findNumbers(environmentSegment->_segments, lastNumbers);
55 | }
56 | }
57 |
58 | bool HtmlGenerator::generate(const OutputData& outputData, const String& outputFile)
59 | {
60 | _outputDir = File::dirname(outputFile);
61 |
62 | {
63 | LastNumbers lastNumbers;
64 | findNumbers(outputData.segments, lastNumbers);
65 | }
66 |
67 | File file;
68 | if(!file.open(outputFile, File::writeFlag))
69 | return false;
70 |
71 | if(!file.write(String("\n")))
72 | return false;
73 |
74 | if(!file.write(String("\n")) ||
75 | !file.write(String("\n")) ||
76 | !file.write(String("\n")) ||
90 | !file.write(String("\n")))
91 | return false;
92 |
93 | if(!file.write(String("\n")) ||
94 | !file.write(Generator::generate(*this, outputData)))
95 | return false;
96 |
97 | if(!_footnotes.isEmpty())
98 | {
99 | if (!file.write("
\n"))
100 | return false;
101 | uint number = 1;
102 | for(List::Iterator i = _footnotes.begin(), end = _footnotes.end(); i != end; ++i, ++number)
103 | {
104 | if(i != _footnotes.begin())
105 | if(!file.write("
"))
106 | return false;
107 | if(!file.write(String("") + String::fromUInt(number) + " " + translate(*this, *i) + "\n"))
108 | return false;
109 | }
110 | if (!file.write("
\n"))
111 | return false;
112 | }
113 |
114 | if(!file.write(String("\n")) ||
115 | !file.write(String("\n")))
116 | return false;
117 |
118 | return true;
119 | }
120 |
121 | String HtmlGenerator::getErrorString() const
122 | {
123 | return Error::getErrorString();
124 | }
125 |
126 | String HtmlGenerator::generate(const OutputData::ParagraphSegment& segment)
127 | {
128 | return String("") + Generator::translate(*this, segment._text) + "
\n";
129 | }
130 |
131 | String HtmlGenerator::generate(const OutputData::TitleSegment& segment)
132 | {
133 | const Number& numberData = *_titleNumbers.find(&segment);
134 | String number = numberData.toString();
135 | String id = getElementId(segment, segment._title, segment._arguments);
136 | if(segment._level >= 1 && segment._level <= 6)
137 | {
138 | String levelStr = String::fromInt(segment._level);
139 | return String("" + number + " " + Generator::translate(*this, segment._title) + "\n";
140 | }
141 | return String("" + Generator::translate(*this, segment._title) + "
\n";
142 | }
143 |
144 | String HtmlGenerator::generate(const OutputData::SeparatorSegment& segment)
145 | {
146 | return String();
147 | }
148 |
149 | String HtmlGenerator::generate(const OutputData::FigureSegment& segment)
150 | {
151 | String number = _figureNumbers.find(&segment)->toString();
152 | String id = getElementId(segment, "", segment._arguments);
153 | String basename = File::basename(segment._path);
154 | String outputImageFile = _outputDir + "/" + basename;
155 | if(!File::copy(segment._path, outputImageFile, false))
156 | Console::errorf("error: Could not copy file '%s' to '%s': %s\n", (const char*)segment._path, (const char*)outputImageFile, (const char*)Error::getErrorString());
157 | List styleList;
158 | String width = segment._arguments.find("width")->toString();
159 | if(!width.isEmpty())
160 | styleList.append(String("width:") + width);
161 | String height = segment._arguments.find("height")->toString();
162 | if(!height.isEmpty())
163 | styleList.append(String("height:") + width);
164 | String style;
165 | style.join(styleList, ';');
166 | String result;
167 | result.append(String("" + "
");
168 | result.append(String("Figure ") + number + ": " + translate(*this, segment._title) + "
\n");
169 | return result;
170 |
171 | }
172 |
173 | String HtmlGenerator::generate(const OutputData::RuleSegment& segment)
174 | {
175 | return String("
\n");
176 | }
177 |
178 | String HtmlGenerator::generate(const OutputData::BulletListSegment& segment)
179 | {
180 | String result;
181 | result.append("- ");
182 | for(List::Iterator i = segment._childSegments.begin(), end = segment._childSegments.end(); i != end; ++i)
183 | {
184 | const OutputData::SegmentPtr& segment = *i;
185 | if(!segment->isValid())
186 | continue;
187 | result.append(segment->generate(*this));
188 | }
189 | result.append("
");
190 | for(List::Iterator i = segment._siblingSegments.begin(), end = segment._siblingSegments.end(); i != end; ++i)
191 | {
192 | const OutputData::BulletListSegmentPtr& siblingSegment = *i;
193 | if(!siblingSegment->isValid())
194 | continue;
195 | result.append("- ");
196 | for(List::Iterator i = siblingSegment->_childSegments.begin(), end = siblingSegment->_childSegments.end(); i != end; ++i)
197 | {
198 | const OutputData::SegmentPtr& segment = *i;
199 | if(!segment->isValid())
200 | continue;
201 | result.append(segment->generate(*this));
202 | }
203 | result.append("
");
204 | }
205 | result.append("
\n");
206 | return result;
207 | }
208 |
209 | String HtmlGenerator::generate(const OutputData::NumberedListSegment& segment)
210 | {
211 | String result;
212 | result.append("- ");
213 | for(List::Iterator i = segment._childSegments.begin(), end = segment._childSegments.end(); i != end; ++i)
214 | {
215 | const OutputData::SegmentPtr& segment = *i;
216 | if(!segment->isValid())
217 | continue;
218 | result.append(segment->generate(*this));
219 | }
220 | result.append("
");
221 | for(List::Iterator i = segment._siblingSegments.begin(), end = segment._siblingSegments.end(); i != end; ++i)
222 | {
223 | const OutputData::NumberedListSegmentPtr& siblingSegment = *i;
224 | if(!siblingSegment->isValid())
225 | continue;
226 | result.append("- ");
227 | for(List::Iterator i = siblingSegment->_childSegments.begin(), end = siblingSegment->_childSegments.end(); i != end; ++i)
228 | {
229 | const OutputData::SegmentPtr& segment = *i;
230 | if(!segment->isValid())
231 | continue;
232 | result.append(segment->generate(*this));
233 | }
234 | result.append("
");
235 | }
236 | result.append("
\n");
237 | return result;
238 | }
239 |
240 | String HtmlGenerator::generate(const OutputData::BlockquoteSegment& segment)
241 | {
242 | String result;
243 | result.append("");
244 | for(List::Iterator i = segment._childSegments.begin(), end = segment._childSegments.end(); i != end; ++i)
245 | {
246 | const OutputData::SegmentPtr& segment = *i;
247 | if(!segment->isValid())
248 | continue;
249 | result.append((*i)->generate(*this));
250 | }
251 | for(List::Iterator i = segment._siblingSegments.begin(), end = segment._siblingSegments.end(); i != end; ++i)
252 | {
253 | const OutputData::BlockquoteSegmentPtr& siblingSegment = *i;
254 | if(!siblingSegment->isValid())
255 | continue;
256 | for(List::Iterator i = siblingSegment->_childSegments.begin(), end = siblingSegment->_childSegments.end(); i != end; ++i)
257 | {
258 | const OutputData::SegmentPtr segment = *i;
259 | if(!segment->isValid())
260 | continue;
261 | result.append(segment->generate(*this));
262 | }
263 | }
264 | result.append("
\n");
265 | return result;
266 | }
267 |
268 | String HtmlGenerator::generate(const OutputData::EnvironmentSegment& segment)
269 | {
270 | String result;
271 | result.append("");
272 | if(segment._verbatim)
273 | {
274 | if(segment._command.isEmpty())
275 | result.append("
\n");
276 | for(List::Iterator i = segment._lines.begin(), end = segment._lines.end(); i != end; ++i)
277 | {
278 | result.append(escape(*i));
279 | result.append("\n");
280 | }
281 | if(segment._command.isEmpty())
282 | result.append("
");
283 | }
284 | else
285 | {
286 | for(List
::Iterator i = segment._segments.begin(), end = segment._segments.end(); i != end; ++i)
287 | {
288 | const OutputData::SegmentPtr& segment = *i;
289 | if(!segment->isValid())
290 | continue;
291 | result.append(segment->generate(*this));
292 | }
293 | }
294 | result.append(" \n");
295 | return result;
296 | }
297 |
298 | String HtmlGenerator::generate(const OutputData::TableSegment& segment)
299 | {
300 | String result;
301 | String id = getElementId(segment, "", segment._arguments);
302 | bool xtabGridStyle = segment._arguments.contains(".xtabgrid");
303 | bool gridStyle = segment._arguments.contains(".grid") || xtabGridStyle;
304 | bool plainStyle = segment._arguments.contains(".plain");
305 | String tableStyleSuffix;
306 | if(plainStyle)
307 | tableStyleSuffix = "_plain";
308 | else if(gridStyle)
309 | tableStyleSuffix = "_grid";
310 | result.append(String("");
311 | for(List::Iterator i = segment._rows.begin(), end = segment._rows.end(); i != end; ++i)
312 | {
313 | result.append("");
314 | OutputData::TableSegment::RowData& rowData = *i;
315 | usize columnIndex = 0;
316 | bool firstRow = i == segment._rows.begin();
317 | for(Array::Iterator i = rowData.cellData.begin(), end = rowData.cellData.end(); i != end; ++i, ++columnIndex)
318 | {
319 | const OutputData::TableSegment::ColumnInfo& columnInfo = segment._columns[columnIndex];
320 | List styleList;
321 | switch(columnInfo.alignment)
322 | {
323 | case OutputData::TableSegment::ColumnInfo::rightAlignment:
324 | styleList.append("text-align:right");
325 | break;
326 | case OutputData::TableSegment::ColumnInfo::centerAlignment:
327 | styleList.append("text-align:center");
328 | break;
329 | default:
330 | break;
331 | }
332 | String width = columnInfo.arguments.find("width")->toString();
333 | if(!width.isEmpty())
334 | styleList.append(String("width:") + width);
335 | String style;
336 | style.join(styleList, ';');
337 |
338 | OutputData::TableSegment::CellData& cellData = *i;
339 | String class_ = firstRow ? String("th") : String("td");
340 | result.append(String("<") + class_ + " class=\"" + class_ + tableStyleSuffix + "\" style=\"" + style + "\">");
341 | for(List::Iterator i = cellData.outputSegments2.begin(), end = cellData.outputSegments2.end(); i != end; ++i)
342 | {
343 | const OutputData::SegmentPtr& segment = *i;
344 | if(!segment->isValid())
345 | continue;
346 | result.append(segment->generate(*this));
347 | }
348 | if (firstRow)
349 | result.append("\n");
350 | else
351 | result.append("\n");
352 | }
353 | result.append("
\n");
354 | }
355 | result.append("
\n");
356 | if(segment._captionSegment)
357 | {
358 | String caption = segment._captionSegment->_text;
359 | if(caption.startsWith(":"))
360 | caption = caption.substr(1);
361 | else // Table:
362 | caption = caption.substr(6);
363 |
364 | String number = _tableNumbers.find(&segment)->toString();
365 | result.append(String("Table ") + number + ": ");
366 | result.append(translate(*this, caption));
367 | result.append("
\n");
368 | }
369 | return result;
370 | }
371 |
372 | String HtmlGenerator::generate(const OutputData::TexSegment& segment)
373 | {
374 | String result;
375 | if(segment._content == "\\tableofcontents")
376 | {
377 | result.append("Contents
\n");
378 | result.append("");
379 | result.append("
");
380 | int lastLevel = 1;
381 | for(HashMap::Iterator i = _titleNumbers.begin(), end = _titleNumbers.end(); i != end; ++i)
382 | {
383 | const OutputData::TitleSegment* segment = i.key();
384 | const Number& number = *i;
385 | if (segment->_level > 3)
386 | continue;
387 | for(; lastLevel < segment->_level; ++lastLevel)
388 | result.append(" | ");
389 | for(; lastLevel > segment->_level; --lastLevel)
390 | result.append(" |
\n");
391 | String id = getElementId(*segment, segment->_title, segment->_arguments);
392 | result.append(String("| _level) + "em\">");
393 | result.append(number.toString());
394 | result.append(" | ");
395 | result.append(String("");
396 | result.append(Generator::translate(*this, segment->_title));
397 | result.append(" |
\n");
398 | }
399 | for(; lastLevel > 1; --lastLevel)
400 | result.append("
\n");
401 | result.append("\n");
402 | result.append("\n");
403 | }
404 | return result;
405 | }
406 |
407 | String HtmlGenerator::generate(const OutputData::TexPartSegment& segment)
408 | {
409 | return String();
410 | }
411 |
412 | String HtmlGenerator::generate(const OutputData::PdfSegment& segment)
413 | {
414 | return String();
415 | }
416 |
417 | String HtmlGenerator::escapeChar(uint32 c)
418 | {
419 | switch (c)
420 | {
421 | case '"':
422 | return String(""");
423 | case '&':
424 | return String("&");
425 | case '\'':
426 | return String("'");
427 | case '<':
428 | return String("<");
429 | case '>':
430 | return String(">");
431 | default:
432 | return Unicode::toString(c);
433 | }
434 | }
435 |
436 | String HtmlGenerator::getSpanStart(const String& sequence)
437 | {
438 | if(sequence.startsWith("`"))
439 | return String("");
440 | if(sequence == "**" || sequence == "__")
441 | return String("");
442 | return String("");
443 | }
444 |
445 | String HtmlGenerator::getSpanEnd(const String& sequence)
446 | {
447 | if(sequence.startsWith("`"))
448 | return String("");
449 | if(sequence == "**" || sequence == "__")
450 | return String("");
451 | return String("");
452 | }
453 |
454 | String HtmlGenerator::getWordBreak(const char l, const char r)
455 | {
456 | return String();
457 | }
458 |
459 | String HtmlGenerator::getLink(const String& link, const String& name_)
460 | {
461 | String name = name_;
462 | if(name.isEmpty())
463 | name = link;
464 | else if(link.startsWith("#"))
465 | {
466 | if (name.endsWith("#"))
467 | name = name.substr(0, name.length() - 1) + _numbers.find(link.substr(1))->toString();
468 | }
469 | return String("" + Generator::translate(*this, name) + "";
470 | }
471 |
472 | String HtmlGenerator::getLineBreak()
473 | {
474 | return String("
");
475 | }
476 |
477 | String HtmlGenerator::getInlineImage(const String& path)
478 | {
479 | String basename = File::basename(path);
480 | String outputImageFile = _outputDir + "/" + basename;
481 | if(!File::copy(path, outputImageFile, false))
482 | Console::errorf("error: Could not copy file '%s' to '%s': %s\n", (const char*)path, (const char*)outputImageFile, (const char*)Error::getErrorString());
483 | return String("
";
484 | }
485 |
486 | String HtmlGenerator::getFootnote(const String& text)
487 | {
488 | _footnotes.append(text);
489 | return String("") + String::fromInt((int)_footnotes.size()) + "";
490 | }
491 |
492 | String HtmlGenerator::Number::toString() const
493 | {
494 | String result;
495 | for(Array::Iterator i = _number.begin(), end = _number.end(); i != end; ++i)
496 | {
497 | if(!result.isEmpty())
498 | result.append('.');
499 | result.append(String::fromUInt(*i));
500 | }
501 | return result;
502 | }
503 |
504 | String HtmlGenerator::stripFormattingAndTranslate(const String& str)
505 | {
506 | PlainGenerator plainGenerator;
507 | return escape(Generator::translate(plainGenerator, str));
508 | }
509 |
510 | String HtmlGenerator::escape(const String& str)
511 | {
512 | String result;
513 | result.reserve(str.length());
514 | for(const char* i = str; *i; ++i)
515 | result.append(escapeChar(*i));
516 | return result;
517 | }
518 |
519 | String HtmlGenerator::getElementId(const OutputData::Segment& segment, const String& title, const Map& arguments)
520 | {
521 | HashMap::Iterator it = _elementIds.find(&segment);
522 | if(it != _elementIds.end())
523 | return *it;
524 | String id = arguments.find("#")->toString();
525 | if(id.isEmpty())
526 | {
527 | id = title;
528 | id.toLowerCase();
529 | id = stripFormattingAndTranslate(id);
530 | id.replace(" ", "-");
531 | }
532 | String base = id;
533 | int index = 1;
534 | while(_usedElementIds.contains(id))
535 | id = base + "-" + String::fromInt(++index);
536 | _elementIds.append(&segment, id);
537 | return id;
538 | }
539 |
--------------------------------------------------------------------------------
/Src/TexGenerator.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "TexGenerator.hpp"
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "OutputData.hpp"
11 |
12 | const char* TexGenerator::_defaultListingsLanguages[] = {"ABAP", "ACM", "ACMscript", "ACSL", "Ada", "Algol", "Ant", "Assembler", "Awk", "bash", "Basic", "C", "C++", "Caml", "CIL", "Clean", "Cobol", "Comal 80", "command.com", "Comsol", "csh", "Delphi", "Eiffel", "Elan", "erlang", "Euphoria", "Fortran", "GCL", "Gnuplot", "hansl", "Haskell", "HTML", "IDL", "inform", "Java", "JVMIS", "ksh", "Lingo", "Lisp", "LLVM", "Logo", "Lua", "make", "Matlab", "Mathematica", "Mercury", "MetaPost", "Miranda", "Mizar", "ML", "Modula-2", "MuPAD", "NASTRAN", "Oberon-2", "OCL", "Octave", "Oz", "Perl", "Pascal", "PHP", "PL/I", "Plasm", "PostScript", "POV", "Prolog", "Promela", "PSTricks", "Python", "R", "Reduce", "Rexx", "RSL", "Ruby", "S", "SAS", "Scala", "Scilab", "sh", "SHELXL", "SPARQL", "Simula", "SQL", "tcl", "TeX", "VBScript", "Verilog", "VHDL", "VRML", "XML", "XSLT"};
13 | const usize TexGenerator::_numOfDefaultListingsLanguages = sizeof(_defaultListingsLanguages) / sizeof(*_defaultListingsLanguages);
14 |
15 | bool TexGenerator::generate(const String& engine, const OutputData& outputData, const String& outputFile)
16 | {
17 | //this->outputData = &outputData;
18 |
19 | File file;
20 | if(!file.open(outputFile, File::writeFlag))
21 | return false;
22 |
23 | if(!outputData.info.className.isEmpty())
24 | {
25 | if(!file.write(String("\\documentclass{") + outputData.info.className + "}\n\n"))
26 | return false;
27 | }
28 | else
29 | {
30 | if(!file.write(String("\\documentclass[a4paper]{article}\n\n")))
31 | return false;
32 |
33 | if(engine != "lualatex")
34 | if(!file.write("\\usepackage[utf8]{inputenc}\n\n"))
35 | return false;
36 |
37 | // inword wrap for english
38 | if(!file.write("\\usepackage[english]{babel}\n\n"))
39 | return false;
40 |
41 | // change page geometry
42 | if(!file.write("\\usepackage{geometry}\n") ||
43 | !file.write("\\geometry{\n") ||
44 | !file.write(" a4paper,\n") ||
45 | !file.write(" left=25mm,\n") ||
46 | !file.write(" right=25mm,\n") ||
47 | !file.write(" top=25mm,\n") ||
48 | !file.write(" bottom=25mm,\n") ||
49 | !file.write("}\n\n"))
50 | return false;
51 |
52 | // we need hyperlinks in toc and for \href
53 | if(!file.write("\\usepackage{hyperref}\n") ||
54 | !file.write("\\hypersetup{%\n") ||
55 | !file.write(" colorlinks,\n") ||
56 | !file.write(" citecolor=black,\n") ||
57 | !file.write(" filecolor=black,\n") ||
58 | !file.write(" linkcolor=black,\n") ||
59 | !file.write(" anchorcolor=black,\n") ||
60 | !file.write(" urlcolor=blue\n") ||
61 | !file.write("}\n\n"))
62 | return false;
63 |
64 | // package to include images
65 | if(!file.write("\\usepackage{graphicx}\n\n"))
66 | return false;
67 |
68 | // customized enumerations
69 | if(!file.write("\\usepackage{enumitem}\n") ||
70 | !file.write("\\setlist{topsep=0pt}\n\n"))
71 | return false;
72 |
73 | // customized blockquotes
74 | if(!file.write("\\usepackage{quoting}\n\n"))
75 | return false;
76 |
77 | // mordern fonts
78 | if(!file.write("\\usepackage[default,osf]{sourcesanspro}\n") ||
79 | !file.write("\\usepackage[scaled=.8]{sourcecodepro}\n\n"))
80 | return false;
81 |
82 | // change parindent and parskip
83 | if(!file.write("\\setlength\\parindent{0pt}\n") ||
84 | !file.write("\\setlength\\parskip{5pt}\n\n"))
85 | return false;
86 |
87 | // customized section titles
88 | if(!file.write("\\usepackage{titlesec}\n\n") ||
89 | !file.write("\\setcounter{secnumdepth}{4}\n") ||
90 | //!file.write("\\titleformat{\\section}{\\fontsize{7em}{7em}\\selectfont\\bfseries}{\\thesection}{0.1em}{\\LARGE\\scshape}\n") ||
91 | //!file.write("\\titleformat{\\part}{\\fontsize{5em}{5em}\\selectfont\\bfseries}{\\makebox[7.5cm][r]{\\thepart}}{0.8em}{\\LARGE\\scshape}\n") ||
92 | //!file.write("\\titleformat{\\subsection}{\\normalfont\\Large\\bfseries}{\\thesubsection}{1em}{}\n") ||
93 | //!file.write("\\titleformat{\\subsubsection}{\\normalfont\\large\\bfseries}{\\thesubsubsection}{1em}{}\n") ||
94 | !file.write("\\titleformat{\\paragraph}{\\normalfont\\normalsize\\bfseries}{\\theparagraph}{1em}{}\n") ||
95 | !file.write("\\titleformat{\\subparagraph}{\\normalfont\\normalsize\\bfseries}{\\thesubparagraph}{1em}{}\n\n") ||
96 | //!file.write("\\titlespacing*{\\part}{-1.5cm}{10pt}{5pt}\n") ||
97 | //!file.write("\\titlespacing*{\\section}{-1.5cm}{30pt}{5pt}\n") ||
98 | //!file.write("\\titlespacing*{\\subsection}{-1.5cm}{10pt}{5pt}\n") ||
99 | //!file.write("\\titlespacing*{\\subsubsection}{-1.5cm}{10pt}{5pt}\n") ||
100 | //!file.write("\\titlespacing*{\\paragraph}{-1.5cm}{10pt}{5pt}\n") ||
101 | //!file.write("\\titlespacing*{\\subparagraph}{-0cm}{10pt}{5pt}\n\n"))
102 | false)
103 | return false;
104 |
105 | // workaround for titlesec section numbering issue http://tex.stackexchange.com/questions/299969/titlesec-loss-of-section-numbering-with-the-new-update-2016-03-15
106 | if(!file.write("\\usepackage{etoolbox}\n") ||
107 | !file.write("\\makeatletter\n") ||
108 | !file.write("\\patchcmd{\\ttlh@hang}{\\parindent\\z@}{\\parindent\\z@\\leavevmode}{}{}\n") ||
109 | !file.write("\\patchcmd{\\ttlh@hang}{\\noindent}{}{}{}\n") ||
110 | !file.write("\\makeatother\n\n"))
111 | return false;
112 |
113 | // package insert graphics at the desired position
114 | if(!file.write("\\usepackage{float}\n\n"))
115 | return false;
116 |
117 | // package for pretty code and preformated blocks
118 | if(!file.write("\\usepackage{listings}\n\n"))
119 | return false;
120 |
121 | // package to use colored fonts
122 | if(!file.write("\\usepackage{xcolor}\n"))
123 | return false;
124 |
125 | // package for custom environments
126 | if(!file.write("\\usepackage{environ}\n\n"))
127 | return false;
128 |
129 | // command to insert a horizontal rule
130 | //if(!file.write("\\newcommand\\HorizontalRule{\\vspace{-3pt}\\rule{\\linewidth}{0.4pt}\\vspace{4pt}}\n\n"))
131 | if(!file.write("\\newcommand\\HorizontalRule{\\raisebox{3.5pt}[1.5ex]{\\rule{\\linewidth}{0.4pt}}}\n\n"))
132 | return false;
133 |
134 | // command to insert an image into text
135 | if(!file.write("\\newcommand\\InlineImage[1]{\\raisebox{-0.1em}{\\includegraphics[height=0.9em]{#1}}}\n\n"))
136 | return false;
137 |
138 | // configure table environment
139 | //if(!file.write("\\renewcommand{\\arraystretch}{1.2}\n\n"))
140 | // return false;
141 | //if(!file.write("\\usepackage{mdframed}\n\n"))
142 | // return false;
143 | if(!file.write("\\usepackage{array}\n"))
144 | return false;
145 | //if(!file.write("\\renewcommand{\\arraystretch}{1.5}\n\n"))
146 | // return false;
147 | if(!file.write("\\renewcommand{\\extrarowheight}{2pt}\n\n"))
148 | return false;
149 |
150 | // package for xtab tables
151 | if(!file.write("\\usepackage{xtab}\n\n"))
152 | return false;
153 |
154 | // prepare environments for syntax highlighting
155 | if(!file.write("\\definecolor{boxBackgroundColor}{RGB}{245,245,245}\n") ||
156 | !file.write("\\definecolor{boxFrameColor}{RGB}{128,128,128}\n") ||
157 | !file.write("\\definecolor{codeRedColor}{RGB}{163,21,21}\n") ||
158 | !file.write("\\definecolor{codeBlueColor}{RGB}{0,0,255}\n") ||
159 | !file.write("\\definecolor{codeGreenColor}{RGB}{0,128,0}\n"))
160 | return false;
161 | if(!file.write("\\lstdefinelanguage{XML}"
162 | "{"
163 | " basicstyle=\\ttfamily\\color{codeBlueColor},"
164 | " morestring=[b]\","
165 | " moredelim=[s][\\color{codeBlueColor}]{<}{\\ },"
166 | " moredelim=[s][\\color{codeBlueColor}]{}{>},"
167 | " morecomment=[s]{}{?>},"
168 | " morecomment=[s]{},"
169 | " commentstyle=\\color{codeGreenColor},"
170 | " stringstyle=\\color{codeRedColor},"
171 | " identifierstyle=\\color{red}"
172 | "}\n\n"))
173 | return false;
174 |
175 | if(!file.write("\\newcommand\\EnvironmentCaption[1]{\\parbox{\\textwidth}{\\textbf{#1}}}\n\n"))
176 | return false;
177 |
178 | if(!file.write("\\lstset{frame=single,basicstyle=\\ttfamily,breaklines=true,showstringspaces=false,backgroundcolor=\\color{boxBackgroundColor},rulecolor=\\color{boxFrameColor},keywordstyle=\\color{codeBlueColor},stringstyle=\\color{codeRedColor},commentstyle=\\color{codeGreenColor}}\n"))
179 | return false;
180 | if(!file.write("\\lstnewenvironment{plain}[1][]{\\lstset{#1}\\vspace{\\parskip}\\minipage{\\linewidth}}{\\endminipage}\n") ||
181 | !file.write("\\lstnewenvironment{xplain}{\\lstset{frame=none,backgroundcolor=}\\vspace{\\parskip}}{}\n"))
182 | return false;
183 | for(usize i = 0; i < _numOfDefaultListingsLanguages; ++i)
184 | {
185 | String language = String::fromCString(_defaultListingsLanguages[i]);
186 | //if(!file.write(String("\\lstnewenvironment{") + TexGenerator::getEnvironmentName(language) + "}{\\lstset{language=" + language + ",frame=single,basicstyle=\//\ttfamily,breaklines=true,showstringspaces=false,backgroundcolor=\\color{boxBackgroundColor},rulecolor=\\color{boxFrameColor}}\\vspace{\\parskip}\\minipage{\\linewidth}}{\\endminipage}\n"))
187 | // return false;
188 | if(!file.write(String("\\lstnewenvironment{") + TexGenerator::getEnvironmentName(language) + "}[1][]{\\lstset{language=" + language + ",#1}\\vspace{\\parskip}\\minipage{\\linewidth}}{\\endminipage}\n"))
189 | return false;
190 | }
191 | if(!file.write("\n"))
192 | return false;
193 |
194 | // create environment for latex examples
195 | //if(!file.write("\\newenvironment{boxed}{\\vspace{\\parskip}\\begin{minipage}{\\linewidth}\\HorizontalRule}{\n\\HorizontalRule\\end{minipage}}\n\n"))
196 | // return false;
197 | if(!file.write("\\NewEnviron{boxed}[1][]{\\vspace{\\parskip}\\hspace{-3.4pt}\\fcolorbox{boxFrameColor}{white}{\\minipage{\\linewidth}\n\\vspace{3.3pt}\\BODY\n\\vspace{3.4pt}\\endminipage}\\vspace{3.3pt}}\n\n"))
198 | return false;
199 | //if(!file.write("\\newenvironment{boxed}{\\vspace{\\parskip}\\begin{mdframed}[backgroundcolor=yellow!10]\\begin{minipage}{\\linewidth}}{\\end{minipage}\\end{mdframed}}\n\n"))
200 | // return false;
201 |
202 | // package to include pdf pages
203 | if(outputData.info.hasPdfSegments)
204 | if(!file.write("\\usepackage{pdfpages}\n\n"))
205 | return false;
206 |
207 | // customize inline code spans
208 | if(!file.write("\\let\\oldtexttt\\texttt\n") ||
209 | !file.write("\\renewcommand{\\texttt}[1]{\\fcolorbox{boxFrameColor}{boxBackgroundColor}{\\raisebox{0pt}[0.45em][0pt]{\\oldtexttt{#1}}}}\n\n"))
210 | return false;
211 | }
212 |
213 | if(!file.write("\n"))
214 | return false;
215 | for(List::Iterator i = outputData.info.headerTexFiles.begin(), end = outputData.info.headerTexFiles.end(); i != end; ++i)
216 | if(!file.write(*i) ||
217 | !file.write("\n"))
218 | return false;
219 | if(!file.write("\n\\begin{document}\n\n") ||
220 | !file.write(Generator::generate(*this, outputData)) ||
221 | !file.write("\n\\end{document}\n"))
222 | return false;
223 |
224 | return true;
225 | }
226 |
227 | String TexGenerator::getErrorString() const
228 | {
229 | return Error::getErrorString();
230 | }
231 |
232 | String TexGenerator::escapeChar(uint32 c)
233 | {
234 | switch(c)
235 | {
236 | case '\\':
237 | return String("{\\textbackslash}");
238 | case '<':
239 | return String("{\\textless}");
240 | case '>':
241 | return String("{\\textgreater}");
242 | case '_':
243 | return String("{\\_\\allowbreak}"); // allow line break after _
244 | case '-':
245 | return String("{-\\allowbreak}"); // do not merge -- into a long - // don't use \textendash!
246 | case '/':
247 | return String("{/\\allowbreak}"); // allow line break after /
248 | case '^':
249 | return String("\\textasciicircum");
250 | case '~':
251 | return String("{\\textasciitilde}");
252 | case '$':
253 | case '%':
254 | case '}':
255 | case '&':
256 | case '#':
257 | case '{':
258 | {
259 | String result("\\");
260 | result.append(c);
261 | return result;
262 | }
263 | case 160: //
264 | return String("~");
265 | default:
266 | return Unicode::toString(c);
267 | }
268 | }
269 |
270 | String TexGenerator::texTranslate(const String& str)
271 | {
272 | TexGenerator generator;
273 | return Generator::translate(generator, str);
274 | }
275 |
276 | String TexGenerator::getEnvironmentName(const String& language)
277 | {
278 | String result(language.length());
279 | for(const char* i = language; *i; ++i)
280 | switch(*i)
281 | {
282 | case '+':
283 | result.append("plus");
284 | break;
285 | default:
286 | if(String::isAlphanumeric(*i))
287 | result.append(String::toLowerCase(*i));
288 | }
289 | if(result.length() == 1)
290 | result.append("language");
291 | return result;
292 | }
293 |
294 | String TexGenerator::getTexSize(const String& size, bool width)
295 | {
296 | if(size.endsWith("%"))
297 | {
298 | double percentageSize = size.toDouble() / 100.0;
299 | return String::fromDouble(percentageSize) + (width ? String("\\textwidth") : String("\\textheight"));
300 | }
301 | else
302 | return size;
303 | }
304 |
305 | String TexGenerator::generate(const OutputData::SeparatorSegment& segment)
306 | {
307 | return String();
308 | }
309 |
310 | String TexGenerator::generate(const OutputData::FigureSegment& segment)
311 | {
312 | String path = segment._path;
313 | List flagsList;
314 | String label = segment._arguments.find("#")->toString();
315 | if(!label.isEmpty())
316 | label = String("\\label{") + label + "}";
317 | {
318 | String width = segment._arguments.find("width")->toString();
319 | if(!width.isEmpty())
320 | flagsList.append(String("width=") + TexGenerator::getTexSize(width));
321 | }
322 | {
323 | String height = segment._arguments.find("height")->toString();
324 | if(!height.isEmpty())
325 | flagsList.append(String("height=") + TexGenerator::getTexSize(height, false));
326 | }
327 | String flags;
328 | flags.join(flagsList, ',');
329 | return String("\n\\begin{figure}[H]\\centering\\includegraphics[") + flags + "]{" + path + "}\\caption{" + TexGenerator::texTranslate(segment._title) + "}" + label + "\\end{figure}\n";
330 | }
331 |
332 | String TexGenerator::generate(const OutputData::ParagraphSegment& segment)
333 | {
334 | return String("\n") + TexGenerator::texTranslate(segment._text) + "\n";
335 | }
336 |
337 | String TexGenerator::generate(const OutputData::TitleSegment& segment)
338 | {
339 | String result;
340 | switch(segment._level)
341 | {
342 | case 1:
343 | result = String("\n\\section{") + TexGenerator::texTranslate(segment._title) + "}\n";
344 | break;
345 | case 2:
346 | result = String("\n\\subsection{") + TexGenerator::texTranslate(segment._title) + "}\n";
347 | break;
348 | case 3:
349 | result = String("\n\\subsubsection{") + TexGenerator::texTranslate(segment._title) + "}\n";
350 | break;
351 | case 4:
352 | result = String("\n\\paragraph{") + TexGenerator::texTranslate(segment._title) + "}\n";
353 | break;
354 | case 5:
355 | default:
356 | result = String("\n\\subparagraph{") + TexGenerator::texTranslate(segment._title) + "}\n";
357 | break;
358 | }
359 | if(segment._arguments.contains(".unnumbered"))
360 | {
361 | const char* x = result.find('{');
362 | if(x)
363 | result = result.substr(0, x - (const char*)result) + "*" + result.substr(x - (const char*)result);
364 | }
365 | String label = segment._arguments.find("#")->toString();
366 | if(!label.isEmpty())
367 | {
368 | result.append("\\label{");
369 | result.append(label);
370 | result.append("}");
371 | }
372 | return result;
373 | }
374 |
375 | String TexGenerator::generate(const OutputData::RuleSegment& segment)
376 | {
377 | return String("\n\\HorizontalRule\n");
378 | }
379 |
380 | String TexGenerator::generate(const OutputData::BulletListSegment& segment)
381 | {
382 | String result("\n\\begin{itemize}\n\\item ");
383 | for(List::Iterator i = segment._childSegments.begin(), end = segment._childSegments.end(); i != end; ++i)
384 | {
385 | const OutputData::SegmentPtr& segment = *i;
386 | if(!segment->isValid())
387 | continue;
388 | result.append(segment->generate(*this));
389 | }
390 | for(List::Iterator i = segment._siblingSegments.begin(), end = segment._siblingSegments.end(); i != end; ++i)
391 | {
392 | OutputData::BulletListSegmentPtr& siblingSegment = *i;
393 | if(!siblingSegment->isValid())
394 | continue;
395 | result.append("\\item ");
396 | for(List::Iterator i = siblingSegment->_childSegments.begin(), end = siblingSegment->_childSegments.end(); i != end; ++i)
397 | {
398 | const OutputData::SegmentPtr& segment = *i;
399 | if(!segment->isValid())
400 | continue;
401 | result.append(segment->generate(*this));
402 | }
403 | }
404 | result.append("\\end{itemize}\n");
405 | return result;
406 | }
407 |
408 | String TexGenerator::generate(const OutputData::NumberedListSegment& segment)
409 | {
410 | String result("\n\\begin{enumerate}\n\\item ");
411 | for(List::Iterator i = segment._childSegments.begin(), end = segment._childSegments.end(); i != end; ++i)
412 | {
413 | const OutputData::SegmentPtr& segment = *i;
414 | if(!segment->isValid())
415 | continue;
416 | result.append((*i)->generate(*this));
417 | }
418 | for(List::Iterator i = segment._siblingSegments.begin(), end = segment._siblingSegments.end(); i != end; ++i)
419 | {
420 | const OutputData::NumberedListSegmentPtr& siblingSegment = *i;
421 | if(!siblingSegment->isValid())
422 | continue;
423 | result.append("\\item ");
424 | for(List::Iterator i = siblingSegment->_childSegments.begin(), end = siblingSegment->_childSegments.end(); i != end; ++i)
425 | {
426 | const OutputData::SegmentPtr& segment = *i;
427 | if(!segment->isValid())
428 | continue;
429 | result.append(segment->generate(*this));
430 | }
431 | }
432 | result.append("\\end{enumerate}\n");
433 | return result;
434 |
435 | }
436 |
437 | String TexGenerator::generate(const OutputData::BlockquoteSegment& segment)
438 | {
439 | String result("\n\\begin{quoting}\n");
440 | for(List::Iterator i = segment._childSegments.begin(), end = segment._childSegments.end(); i != end; ++i)
441 | {
442 | const OutputData::SegmentPtr& segment = *i;
443 | if(!segment->isValid())
444 | continue;
445 | result.append((*i)->generate(*this));
446 | }
447 | for(List::Iterator i = segment._siblingSegments.begin(), end = segment._siblingSegments.end(); i != end; ++i)
448 | {
449 | const OutputData::BlockquoteSegmentPtr& siblingSegment = *i;
450 | if(!siblingSegment->isValid())
451 | continue;
452 | for(List::Iterator i = siblingSegment->_childSegments.begin(), end = siblingSegment->_childSegments.end(); i != end; ++i)
453 | {
454 | const OutputData::SegmentPtr& segment = *i;
455 | if(!segment->isValid())
456 | continue;
457 | result.append(segment->generate(*this));
458 | }
459 | }
460 | result.append("\n\\end{quoting}\n");
461 | return result;
462 | }
463 |
464 | String TexGenerator::generate(const OutputData::EnvironmentSegment& segment)
465 | {
466 | String environment = segment._language;
467 | if(environment.isEmpty())
468 | environment = "plain";
469 | environment = TexGenerator::getEnvironmentName(environment);
470 |
471 | String caption = segment._arguments.find("caption")->toString();
472 | String flags;
473 | if(!caption.isEmpty())
474 | flags += String("title=\\EnvironmentCaption{") + TexGenerator::texTranslate(caption) + "}";
475 |
476 | String result;
477 | if (segment._command.isEmpty())
478 | result.append(String("\n\\begin{") + environment + "}");
479 | result.append(flags.isEmpty() ? String("\n") : String("[") + flags + "]\n");
480 | if(segment._verbatim)
481 | {
482 | for(List::Iterator i = segment._lines.begin(), end = segment._lines.end(); i != end; ++i)
483 | {
484 | result.append(*i);
485 | result.append("\n");
486 | }
487 | }
488 | else
489 | {
490 | for(List::Iterator i = segment._segments.begin(), end = segment._segments.end(); i != end; ++i)
491 | {
492 | const OutputData::SegmentPtr& segment = *i;
493 | if(!segment->isValid())
494 | continue;
495 | result.append(segment->generate(*this));
496 | }
497 | }
498 | if (segment._command.isEmpty())
499 | result.append(String("\\end{") + environment + "}\n");
500 | return result;
501 | }
502 |
503 | String TexGenerator::generate(const OutputData::TableSegment& segment)
504 | {
505 | String result;
506 | String caption;
507 | bool xtabGridStyle = segment._arguments.contains(".xtabgrid");
508 | bool gridStyle = segment._arguments.contains(".grid") || xtabGridStyle;
509 | bool xtabStyle = segment._arguments.contains(".xtab") || xtabGridStyle;
510 | bool plainStyle = segment._arguments.contains(".plain");
511 | if(segment._captionSegment)
512 | {
513 | caption = segment._captionSegment->_text;
514 | if(caption.startsWith(":"))
515 | caption = caption.substr(1);
516 | else // Table:
517 | caption = caption.substr(6);
518 | result.append("\n\\begin{table}[H]\\centering");
519 | }
520 | else
521 | result.append("\n\\begin{center}");
522 | if(xtabStyle)
523 | {
524 | result.append(String("\\tabletail{\\hline\\multicolumn{") + String::fromUInt((uint)segment._columns.size()) + "}{c}{\\vspace{-1cm}\\small\\emph{\\ldots}}\\\\}%\n");
525 | result.append(String("\\tablefirsthead{}%\n"));
526 | if (!gridStyle)
527 | result.append(String("\\tablehead{\\multicolumn{") + String::fromUInt((uint)segment._columns.size()) + "}{c}{\\small\\emph{\\ldots}}\\\\\\hline}%\n");
528 | else
529 | result.append(String("\\tablehead{\\multicolumn{") + String::fromUInt((uint)segment._columns.size()) + "}{c}{\\small\\emph{\\ldots}}\\\\}%\n");
530 | result.append("\\begin{xtabular}{");
531 | }
532 | else
533 | result.append("\\begin{tabular}{");
534 | if(gridStyle)
535 | result.append("|");
536 | for(Array::Iterator i = segment._columns.begin(), end = segment._columns.end(); i != end; ++i)
537 | {
538 | const OutputData::TableSegment::ColumnInfo& columnInfo = *i;
539 | String width = columnInfo.arguments.find("width")->toString();
540 | if(!width.isEmpty())
541 | result.append(String("p{") + TexGenerator::getTexSize(width) + "}");
542 | else
543 | {
544 | char a = columnInfo.alignment == OutputData::TableSegment::ColumnInfo::rightAlignment ? 'r' : (columnInfo.alignment == OutputData::TableSegment::ColumnInfo::centerAlignment ? 'c' : 'l');
545 | result.append(a);
546 | }
547 | if(gridStyle)
548 | result.append("|");
549 | }
550 | result.append("}\n");
551 | if (!plainStyle)
552 | result.append("\\hline\n");
553 | for(List::Iterator i = segment._rows.begin(), end = segment._rows.end(); i != end; ++i)
554 | {
555 | OutputData::TableSegment::RowData& rowData = *i;
556 | usize columnIndex = 0;
557 | {
558 | for(Array::Iterator begin = rowData.cellData.begin(), i = begin, end = rowData.cellData.end(); i != end; ++i, ++columnIndex)
559 | {
560 | //const ColumnInfo& columnInfo = columns[columnIndex];
561 | OutputData::TableSegment::CellData& cellData = *i;
562 | if(i != begin)
563 | result.append(" & ");
564 | //String width = columnInfo.arguments.find("width")->toString();
565 | //if(!width.isEmpty())
566 | // result.append(String("\\parbox[t][][t]{") + TexGenerator::getTexSize(width) + "}{");
567 | for(List::Iterator i = cellData.outputSegments2.begin(), end = cellData.outputSegments2.end(); i != end; ++i)
568 | {
569 | const OutputData::SegmentPtr& segment = *i;
570 | if(!segment->isValid())
571 | continue;
572 |
573 | result.append(segment->generate(*this));
574 | }
575 | //if(!width.isEmpty())
576 | // result.append("\\vspace{5pt}}");
577 | }
578 | }
579 | result.append(" \\\\\n");
580 | if (!plainStyle)
581 | if(i == segment._rows.begin() || gridStyle)
582 | result.append("\\hline\n");
583 | }
584 | if (!plainStyle)
585 | if(segment._rows.size() > 1 && !gridStyle)
586 | result.append("\\hline\n");
587 | if(xtabStyle)
588 | result.append("\\end{xtabular}");
589 | else
590 | result.append("\\end{tabular}");
591 | if(segment._captionSegment)
592 | {
593 | String label = segment._arguments.find("#")->toString();
594 | if(!label.isEmpty())
595 | label = String("\\label{") + label + "}";
596 | result.append(String("\\caption{") + TexGenerator::texTranslate(caption) + "}" + label + "\\end{table}");
597 | }
598 | else
599 | result.append("\\end{center}");
600 | return result;
601 | }
602 |
603 | String TexGenerator::generate(const OutputData::TexSegment& segment)
604 | {
605 | return String("\n") + segment._content + "\n";
606 | }
607 |
608 | String TexGenerator::generate(const OutputData::TexPartSegment& segment)
609 | {
610 | return String("\n\\clearpage\n\\part{") + TexGenerator::texTranslate(segment._title) + "}\n";
611 | }
612 |
613 | String TexGenerator::generate(const OutputData::PdfSegment& segment)
614 | {
615 | return String("\n\\includepdf[pages=-]{") + segment._filePath + "}\n";
616 | }
617 |
618 |
619 | String TexGenerator::getSpanStart(const String& sequence)
620 | {
621 | if(sequence.startsWith("`"))
622 | return String("\\texttt{");
623 | if(sequence == "**" || sequence == "__")
624 | return String("\\textbf{");
625 | return String("\\emph{");
626 | }
627 |
628 | String TexGenerator::getSpanEnd(const String& sequence)
629 | {
630 | return String("}");
631 | }
632 |
633 | String TexGenerator::getWordBreak(const char l, const char r)
634 | {
635 | if(String::isAlpha(l) && String::isAlpha(r))
636 | return String("\\-");
637 | return String("{\\allowbreak}");
638 | }
639 |
640 | String TexGenerator::getLink(const String& link, const String& name_)
641 | {
642 | String result;
643 | String name = name_;
644 | if(link.startsWith("#"))
645 | {
646 | if(name.isEmpty())
647 | {
648 | result.append("\\ref{");
649 | result.append(link.substr(1));
650 | result.append("}");
651 | }
652 | else
653 | {
654 | result.append("\\hyperref[");
655 | result.append(link.substr(1));
656 | result.append("]{");
657 | if (name.endsWith("#"))
658 | {
659 | name.resize(name.length() - 1);
660 | name = translate(*this, name);
661 | name.append(String("\\ref{") + link.substr(1) + "}");
662 | }
663 | else
664 | name = translate(*this, name);
665 | name.replace(' ', '~');
666 | result.append(name);
667 | result.append("}");
668 | }
669 | }
670 | else
671 | {
672 | result.append("\\href{");
673 | result.append(link);
674 | result.append("}{");
675 | if(name.isEmpty())
676 | {
677 | result.append("\\mbox{");
678 | result.append(translate(*this, link));
679 | result.append("}");
680 | }
681 | else
682 | result.append(translate(*this, name));
683 | result.append("}");
684 | }
685 | return result;
686 | }
687 |
688 | String TexGenerator::getLineBreak()
689 | {
690 | return String("\\newline ");
691 | }
692 |
693 | String TexGenerator::getInlineImage(const String& path)
694 | {
695 | String result;
696 | result.append("\\InlineImage{");
697 | result.append(path);
698 | result.append("}");
699 | return result;
700 | }
701 |
702 | String TexGenerator::getFootnote(const String& text)
703 | {
704 | String result;
705 | result.append("\\footnote{");
706 | result.append(translate(*this, text));
707 | result.append("}");
708 | return result;
709 | }
710 |
711 | String TexGenerator::getLatexFormula(const String& formula)
712 | {
713 | return String("$") + formula + "$";
714 | }
715 |
--------------------------------------------------------------------------------
/Doc/manual.md:
--------------------------------------------------------------------------------
1 |
2 | # Introduction
3 |
4 | *umdoc* is a Markdown to *LaTeX* to *PDF* converter.
5 | It is command line tool to convert a Markdown file or set of Markdown files into an input file (`.tex` file) for a *LaTeX* engine like `xelatex`, `lualatex` or `pdflatex`.
6 | The *LaTeX* engine is then launched to convert the generated file into a *PDF* document.
7 | Optional layout information written in *LaTeX* may be provided to customize the look of the generated document.
8 |
9 | There are three different modes to use the tool:
10 |
11 | 1. Converting a Markdown file (ending with `.md`) into a *LaTeX* or *PDF* document:
12 | ```
13 | umdoc example.md [-o example.tex]
14 | ```
15 | 2. Converting an *umdoc* *XML* file (see [section #](#umdoc-xml-file)), which provides Markdown input files and optional layout information, into a *LaTeX* or *PDF* document:
16 | ```
17 | umdoc example.xml [-o example.tex]
18 | ```
19 | 3. Processing the working directory where it expects to find an *umdoc* *XML* file with the name `umdoc.xml`. This file is converted into a *LaTeX* or *PDF* document in the same directory:
20 | ```
21 | umdoc [-o output.tex]
22 | ```
23 |
24 | *umdoc* creates a *PDF* document by default. The extension of the output file, which can be set with `-o`, tells *umdoc* to stop after creating the *LaTeX* file, or to generate an *HTML* output file. (See [section #](#usage) for a full list of accepted command line arguments.)
25 |
26 | ## About this Document
27 |
28 | This document describes the usage of the *umdoc* tool (see [section #](#usage)), the format of the *umdoc* *XML* file (see [section #](#umdoc-xml-file)) and it serves as a reference with examples of the supported Markdown features (see [section #](#supported-markdown-features)).
29 |
30 | Additionally, it describes how Markdown is converted into *LaTeX*, which is essential to know for customization.
31 | Some parts of these descriptions presume advanced knowledge of *LaTeX* and can be ignored by most users.
32 |
33 | ## Motivation
34 |
35 | There are various of options out there if you want to write documentation that can be delivered on paper or in form of a *PDF*.
36 | A WYSIWYG word processor can be used, you can use a sophisticated markup language like *LaTeX* or a anything in between.
37 | But, writing documentation should be simple and convenient.
38 | If you collaborate with others, you want to use something that can easily be merged and version controlled with version control systems like *Git*.
39 | Hence, using tools that rely on a binary or a more or less human unreadable format are not an option.
40 | The alternative - markup languages - have other disadvantages.
41 | You have to know them to use them.
42 | Markdown on the other hand can easily be learned simply by looking at a Markdown document.
43 | Hence, using Markdown and converting it into a neatly formatted printable document is the perfect solution if your documentation needs can be met with basic text formatting constructs.
44 | However, converting Markdown into a printable document is not as easy as it should be.
45 |
46 | One of the reasons for this is that Markdown was designed to be converted into inner text *HTML* and not into printable pages.
47 | To create a printable document, you will probably require:
48 | * The possibility to define the basic layout of a document (cover page, table of contents, etc.).
49 | * The possibility to define a page header and footer.
50 | * Figure and table environments.
51 | * Cross references within the generated document.
52 |
53 | Most of this cannot be accomplished with traditional Markdown without reverting to *HTML*.
54 | Hence, some extensions to the Markdown language and a way to define the layout of the document are required.
55 |
56 | *umdoc* tries to overcome the shortcomings of traditional Markdown with some Markdown extensions inspired by *[GitHub](http://github.com) flavored Markdown* and Markdown extensions from [Pandoc](http://pandoc.org).
57 | Layout information required to generate the document can be specified using an *umdoc* specific *XML* file (see [section #](#umdoc-xml-file)) and with customized *LaTex* classes and commands.
58 |
59 | ##### Project Goals
60 |
61 | The *umdoc* project tries to achieve the following goals:
62 |
63 | * Be easy to use.
64 | * Create a decent out of the box result.
65 | * Be easy to develop.
66 | * Be customizable.
67 | * Support user defined *LaTeX* environments.
68 |
69 | ##### Project Non-Goals
70 |
71 | In contrary, goals that the project does not try to achieve are:
72 |
73 | * Support of all Markdown features and dialects.
74 | * Create output in other formats than *LaTeX*, *PDF* or *HTML*.
75 |
76 | # Installation
77 |
78 | The *umdoc* tool is a single binary without any notable dependencies except a *LaTeX* engine.
79 | To install it, simply [download](https://github.com/craflin/umdoc/releases) the binary for your platform, place it somewhere on your system and ensure that it can be found with the *PATH* environment variable.
80 | You can also call the *umdoc* tool directly, if you do not want to touch your *PATH* variable.
81 |
82 | Additionally, you have to install a *TeX* distribution like [*MiKTeX*](http://miktex.org) on Windows or [*TeX Live*](http://www.tug.org/texlive/) on Linux or Windows.
83 | Most Linux distributions provide packages for *TeX Live* that you can be install using the packaging system of the distribution (*APT*, *RPM*, etc.).
84 |
85 | # Usage {#usage}
86 |
87 | The *umdoc* tool accepts the following command line arguments:
88 |
89 | ```
90 | umdoc [] [-a ] [-e ] [-o ]
91 | [--=]
92 | ```
93 |
94 | The default behavior of *umdoc* is to look in the working directory for a `umdoc.xml` file.
95 | This file is converted into a *LaTeX* and into a *PDF* in the same directory.
96 | Alternatively, input, output files and an output file format can be explicitly specified using the optional `` and `-o ` arguments.
97 |
98 | ##### Options
99 |
100 | * ``
101 |
102 | The input file, which can be an *umdoc* *XML* file (see [section #](#umdoc-xml-file)) or a Markdown (`.md`) file. The default is `umdoc.xml`.
103 |
104 | * `-o `, `--output=`
105 |
106 | The path to the output *PDF*, *LaTex* or *HTML* file. The default is derived from the input file's path where the file name extension is replaced by `.pdf`.
107 |
108 | If the output file ends with the extension `.tex`, *umdoc* will stop after the generation of this file and it will not be converted to *PDF*.
109 |
110 | If the output file ends with the extension `.htm` or `.html`, an *HTML* file will be created where some of the *umdoc* Markdown extensions (like section cross referencing for example) will be emulated.
111 |
112 | * `-e `, `--engine=`
113 |
114 | The launch command of the *LaTeX* engine to be used to convert the created *LaTeX* document into *PDF*.
115 | The default is `xelatex`.
116 |
117 | * `-a `, `--aux-directory=`
118 |
119 | A directory for intermediate files like the created *LaTeX* file or files created by the *LaTeX* engine.
120 | The default is the directory of the output file plus the base name of the output file.
121 |
122 | * `--version`
123 |
124 | Prints the version of *umdoc* and exits.
125 |
126 | * `--=`
127 |
128 | Overwrites the *umdoc* *XML* file placeholder `` with the value `` (see [section #](#umdoc-xml-file-placeholder)).
129 |
130 | ## The *umdoc* *XML* File {#umdoc-xml-file}
131 |
132 | The purpose of the *umdoc* *XML* file is to provide *umdoc* with everything it needs to create the output file.
133 | This includes: Markdown input files, *LaTeX* styling code, and basic layout information like where to insert the table of contents, page breaks or pages from other sources like `.pdf` or `.tex` files.
134 |
135 | The *XML* file defines an `` element in which the following elements can be used:
136 |
137 | * ``
138 |
139 | Defines a custom *LaTeX* environment (see [section #](#umdoc-xml-file-custom-environments)).
140 |
141 | * ``
142 |
143 | Defines a placeholder string (see [section #](#umdoc-xml-file-placeholder)), which can be used to insert a string into *LaTeX* styling code (see [section #](#umdoc-xml-tex-header)) and into inserted *LaTeX* code (see [section #](#umdoc-xml-tex-in-document)).
144 |
145 | * ``
146 |
147 | Inserts *LaTeX* styling code or a *LaTeX* file into the generated *LaTeX* document (see [section #](#umdoc-xml-tex-header)).
148 |
149 | * ``
150 |
151 | Defines the structure of the document to be generated. (see [section #](#umdoc-xml-document)).
152 |
153 | ##### Example
154 |
155 | An *umdoc* *XML* file might look like this:
156 |
157 | ```xml "umdoc.xml"
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | ```
172 |
173 | ### *LaTeX* Class Name {#latex-class}
174 |
175 | By default *umdoc* uses the *LaTeX* class `article` with various custom extensions, which define the default *umdoc* style.
176 | The *LaTeX* class used can be changed by specifying a class name within the `` element:
177 |
178 | ```xml
179 |
180 | ```
181 |
182 | Setting the class name disables all custom extensions.
183 | Hence, the *LaTeX* class `` has to provide an implementation of all the custom *LaTeX* commands and environments that are required for the Markdown features used in a Markdown file (see [section #](#supported-markdown-features)). Hence, the recommended way of creating a custom *LaTeX* class is to start with the default *umdoc* style and to copy the required extensions from the generated *LaTeX* file into the custom *LaTeX* class declaration.
184 |
185 | ### Custom Environments {#umdoc-xml-file-custom-environments}
186 |
187 | The spectrum of supported environments or "languages" in fenced code blocks (see [section #](#fenced-code-blocks)) can be extended.
188 | To do this, your *LaTeX* class (see [section #](#latex-class)) or one of the *LaTeX* styling code files (see [section #](#umdoc-xml-tex-header)) has to declare the *LaTeX* environment.
189 | *umdoc* has to be made aware of such an environment using the element ``:
190 |
191 | ```xml
192 |
193 | ```
194 |
195 | `language` corresponds the name of the fenced code block "language" to be used for this environment in the fenced code block syntax (see [section #](#fenced-code-blocks)).
196 |
197 | The optional attribute `verbatim` can be set to `true` or `false`. The default is `false`.
198 | If it is set to `true`, the code in the fenced code block will be copied unchanged into the *LaTeX* environment.
199 | If `verbatim` is set to `false`, the code will be considered to be Markdown and is converted to *LaTeX* as such.
200 |
201 | The optional attribute `command` specifies a script or executable that processes the unchanged content of the fenced code block.
202 | The first argument of the command is the output format `latex` or `html` of the generated output file (see [section #](#usage)).
203 | The output of the script or executable will be inserted into the generated document without any *LaTeX* or *HTML* environment start or end tags.
204 | But, if `verbatim` is not set to `true`, the output will be transformed from Markdown to *LaTeX* (or *HTML*).
205 |
206 | ### Placeholders {#umdoc-xml-file-placeholder}
207 |
208 | The *umdoc* *XML* file can define placeholders, which are replaced in inserted *LaTeX* files (see [section #](#umdoc-xml-tex-header) and [section #](#umdoc-xml-tex-in-document)) and in Markdown files or code (see [section #](#umdoc-xml-markdown)):
209 |
210 | ```xml
211 |
212 | ```
213 |
214 | For each defined placeholder the occurrence of `\%name\%` is replaced with `` in inserted *LaTeX* and Markdown files.
215 | The value of a placeholder can be overwritten using a `--=` command line argument when launching the *umdoc* executable.
216 |
217 | This feature is most useful to inject information like a version string or the name of the build platform at compile time.
218 |
219 | ### *LaTeX* Styling Code {#umdoc-xml-tex-header}
220 |
221 | A `` element inserts *LaTeX* code or the contents of a `.tex` file into the generated *LaTeX* file before the `\begin{document}` section in the generated file:
222 |
223 | ```xml
224 |
225 | %some latex code
226 |
227 | ```
228 |
229 | or
230 |
231 | ```xml
232 |
233 | ```
234 |
235 | The attribute `file` specifies the path to the *LaTeX* file.
236 | If the path is not absolute, it is considered to be relative to the location of the *umdoc* *XML* file.
237 | The code or the file may contain placeholders (see [section #](#umdoc-xml-file-placeholder)).
238 |
239 | ### Document Structure {#umdoc-xml-document}
240 |
241 | The `` element defines the structure of the document to be generated.
242 | The following elements can be used within this element:
243 |
244 | * ``
245 |
246 | Inserts a manual page break (see [section #](#umdoc-xml-page-break)).
247 |
248 | * ``
249 |
250 | Defines a custom *LaTeX* environment (see [section #](#umdoc-xml-file-custom-environments)).
251 |
252 | * ``
253 |
254 | Inserts Markdown or a Markdown file (see [section #](#umdoc-xml-markdown)).
255 |
256 | * ``
257 |
258 | Inserts a segment title into the generated document (see [section #](#umdoc-xml-part)).
259 |
260 | * ``
261 |
262 | Inserts a *PDF* document (see [section #](#umdoc-xml-pdf)).
263 |
264 | * ``
265 |
266 | Defines a placeholder string (see [section #](#umdoc-xml-file-placeholder)), which can be used to insert a string into *LaTeX* styling code (see [section #](#umdoc-xml-tex-header)) or other *LaTeX* code (see [section #](#umdoc-xml-tex-in-document)).
267 |
268 | * ``
269 |
270 | Inserts *LaTeX* code or a `.tex` file into the generated *LaTeX* document (see [section #](#umdoc-xml-tex-in-document)).
271 |
272 | * ``
273 |
274 | Inserts a table of contents (see [section #](#umdoc-xml-toc)).
275 |
276 | * ``
277 |
278 | Inserts a list of figures (see [section #](#umdoc-xml-lof-lot)).
279 |
280 | * ``
281 |
282 | Inserts a list of tables (see [section #](#umdoc-xml-lof-lot)).
283 |
284 | #### Manual Page Breaks {#umdoc-xml-page-break}
285 |
286 | A manual page break can be inserted using the `` element.
287 | This is most useful after inserting the table of contents (see [section #](#umdoc-xml-toc)) or after inserting a Markdown or *LaTeX* file.
288 |
289 | The `` element translates to the *LaTeX* command `\\clearpage`.
290 |
291 | #### Markdown Code {#umdoc-xml-markdown}
292 |
293 | Markdown or a Markdown file can be inserted with:
294 |
295 | ```xml
296 |
297 | some markdown here
298 |
299 | ```
300 |
301 | or
302 |
303 | ```xml
304 |
305 | ```
306 |
307 | The attribute `file` specifies the path to the Markdown (`.md`) file.
308 | If the path is not absolute, it is relative to the location of the *umdoc* *XML* file.
309 | The code or the content of the file is interpreted and converted to *LaTeX* respectively to the supported Markdown features (see [section #](#supported-markdown-features)).
310 |
311 | #### Segment Titles {#umdoc-xml-part}
312 |
313 | A segment title, which is hierarchically above the highest section title of a Markdown file (see [section #](#markdown-titles)), can be inserted with:
314 |
315 | ```xml
316 |
317 | ```
318 |
319 | The `title` attribute supports basic Markdown styling (see [section #](#markdown-styling)).
320 |
321 | The `` element translates to the *LaTeX* command `\\part`.
322 |
323 | #### *PDF* Files {#umdoc-xml-pdf}
324 |
325 | A *PDF* document can be inserted into the generated document with:
326 |
327 | ```xml
328 |
329 | ```
330 |
331 | The attribute `file` specifies the path to the *PDF* file.
332 | If the path is not absolute, it is relative to the location of the *umdoc* *XML* file.
333 |
334 | The `` element translates to the command `\\includepdf` from the *LaTeX* package *pdfpages*.
335 |
336 | #### *LaTeX* Code {#umdoc-xml-tex-in-document}
337 |
338 | A `` element inserts *LaTeX* code or the content of a `.tex` file into the generated *LaTeX* file:
339 |
340 | ```xml
341 |
342 | %some latex code
343 |
344 | ```
345 |
346 | or
347 |
348 | ```xml
349 |
350 | ```
351 |
352 | The attribute `file` specifies the path to the *LaTeX* file.
353 | If the path is not absolute, it is relative to the location of the *umdoc* *XML* file.
354 | The code or the file may contain placeholders (see [section #](#umdoc-xml-file-placeholder)).
355 |
356 | #### Table of Contents {#umdoc-xml-toc}
357 |
358 | An automatically generated table of contents can be inserted using the element ``.
359 |
360 | This translates to the *LaTeX* command `\\tableofcontents`.
361 |
362 | #### List of Figures or Tables {#umdoc-xml-lof-lot}
363 |
364 | Similar to the the automatically generated list of contents, a list of figures or tables can be inserted using the element `` or ``.
365 |
366 | This translates to the *LaTeX* commands `\\listoffigures` or `\\listoftables`.
367 |
368 | # Supported Markdown Features {#supported-markdown-features}
369 |
370 | ## Titles {#markdown-titles}
371 |
372 | Section titles are defined with one or multiple leading `#` characters (Atx-style) or by underlining the title in the following line with `=` or `-` characters (Setex-style).
373 |
374 | ```
375 | # Example Title
376 |
377 | ## Example Title Level 2
378 |
379 | ### Example Title Level 3
380 |
381 | Example text.
382 | ```
383 |
384 | or
385 |
386 | ```
387 | Example Title
388 | =============
389 |
390 | Example Title Level 2
391 | ---------------------
392 |
393 | ### Example Title Level 3
394 |
395 | Example text.
396 | ```
397 |
398 | results in:
399 | ```boxed
400 | 1 Example Title {-}
401 | =============
402 |
403 | 1.1 Example Title Level 2 {-}
404 | ---------------------
405 |
406 | ### 1.1.1 Example Title Level 3 {-}
407 |
408 | Example text.
409 | ```
410 |
411 | Depending on the title level, this translate to the *LaTeX* commands `\\section`, `\\subsection`, `\\subsubsection`, `\\paragraph`, and `\\subparagraph`.
412 | Titles of a level beyond level 5 will also be translated to `\\subparagraph`.
413 |
414 | A label for cross referencing may be added after the title (see [section #](#markdown-cross-references)).
415 |
416 | ## Basic Markdown Styling {#markdown-styling}
417 |
418 | ### Italic and Bold Text
419 |
420 | Placing the character `\*` or `\_` around some text will turn it to italic.
421 | Two consecutive `\*` or `\_` characters (`\*\*` or `\_\_`) will make the text to bold.
422 | But, `\*` and `\_` will be left unchanged when they are surrounded by spaces.
423 | Additionally, the `\_` character will be left unchanged if it is surrounded by letters.
424 |
425 | ```
426 | The following is *italic* and so is _this_. __This__ is bold and so is **this**. You can use * or _ characters surrounded by spaces and the _ character is left unchanged when it connects two_words. But, * or ** can be used for in**word**highlighting.
427 | ```
428 |
429 | results in:
430 | ```boxed
431 | The following is *italic* and so is _this_. __This__ is bold and so is **this**. You can use * or _ characters surrounded by spaces and the _ character is left unchanged when it connects two_words. But, * or ** can be used for in**word**highlighting.
432 | ```
433 |
434 | Italic text translates to the *LaTeX* command `\\emph` and bold text to `\\textbf`.
435 |
436 | The `\*` and `\_` character can be escaped with `\\` (see [section #](#character-escaping)) if it is not supposed to affect text styling.
437 |
438 | ### Code Spans
439 |
440 | Text can be formatted with a fixed width font by surrounding it with single or double back ticks `` ` ``.
441 |
442 | ```
443 | Some text with code like `printf()`. Code span with back ticks `` `test` ``.
444 | ```
445 |
446 | results in:
447 | ```boxed
448 | Some text with code like `printf()`. Code span with back ticks `` `test` ``.
449 | ```
450 |
451 | This translates to the *LaTeX* command `\\texttt`. The default *umdoc* style redefines `\\texttt` as:
452 | ```
453 | \let\oldtexttt\texttt
454 | \renewcommand{\texttt}[1]{\fcolorbox{boxFrameColor}{boxBackgroundColor}{\raisebox{0pt}[0.45em][0pt]{\oldtexttt{#1}}}}
455 | ```
456 |
457 | ### Inline Links {#markdown-inline-links}
458 |
459 | A link can be inserted using the syntax `()[]`.
460 |
461 | ```
462 | This is an [example link](https://github.com/craflin/umdoc). You can also create a link to a [email address](mailto:some@email.com) links. Links without a text look like this: [](https://github.com/craflin/umdoc)
463 | ```
464 |
465 | results in:
466 | ```boxed
467 | This is an [example link](https://github.com/craflin/umdoc). You can also create a link to a [email address](mailto:some@email.com) links. Links without a text look like this: [](https://github.com/craflin/umdoc)
468 | ```
469 |
470 |
471 |
472 | A link translates to the command `\\hyperref` from the *LaTeX* package *hyperref*.
473 |
474 | ### Cross References {#markdown-cross-references}
475 |
476 | Section titles (see [section #](#markdown-titles)), figures (see [section #](#figures)) and tables with captions (see [section #](#tables-caption)) can be referenced if they are labeled.
477 | The label of a title or figure is added with `{#