├── .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 | ![Example Figure 1](circle.png) 6 | 7 | ![Example Figure 2](circle.png){width=2cm} 8 | 9 | ![Example Figure 3](circle.png){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 | [![Build Status](http://xaws6t1emwa2m5pr.myfritz.net:8080/buildStatus/icon?job=craflin%2Fumdoc%2Fmaster)](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("
  1. "); 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("
  2. "); 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("
  3. "); 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("
  4. "); 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("\n"); 391 | String id = getElementId(*segment, segment->_title, segment->_arguments); 392 | result.append(String("\n"); 398 | } 399 | for(; lastLevel > 1; --lastLevel) 400 | result.append("
"); 389 | for(; lastLevel > segment->_level; --lastLevel) 390 | result.append("
_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"); 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 `{#