mirror of https://github.com/Nonannet/pyladoc.git
Compare commits
65 Commits
Author | SHA1 | Date |
---|---|---|
|
3f9cd56221 | |
|
13c2aaac8b | |
|
4c396cd5b3 | |
|
c7123e23d9 | |
|
df592aef6a | |
|
65b9a300b9 | |
|
3098226c75 | |
|
cb563a2df5 | |
|
1e7e7bfead | |
|
a90c411ff1 | |
|
726e6c7de0 | |
|
5f6147c2b9 | |
|
6120a38241 | |
|
c72a1e70c4 | |
|
001e5b8a10 | |
|
bd7d70a0b7 | |
|
cba2dd690f | |
|
a8ca6aec75 | |
|
83cf954e95 | |
|
01147ef648 | |
|
3ee02b73b8 | |
|
71f211022f | |
|
1f9f2f93e7 | |
|
10ace61abc | |
|
dfa450f05c | |
|
664a08b2dc | |
|
5ac7659f01 | |
|
ecc97bd9a6 | |
|
a65bb1c8e5 | |
|
a413d771b3 | |
|
727ab55b3d | |
|
b20773364c | |
|
ad6fe7c6e7 | |
|
37ad1231c8 | |
|
250885d44f | |
|
76944d1829 | |
|
c4fdb5040a | |
|
b3c2f5e384 | |
|
dba8f5997e | |
|
c908602657 | |
|
f8c59c3c4b | |
|
2b5fde630b | |
|
1abc883ee6 | |
|
53f7f3c828 | |
|
aacb99a40f | |
|
efe702a522 | |
|
f2588bb7d7 | |
|
932ead8ef2 | |
|
507d88bc38 | |
|
2083a6ed41 | |
|
c647bceafc | |
|
bdfaa08f37 | |
|
c6979f3c6a | |
|
39142a1e22 | |
|
2bb4e55dd0 | |
|
d9fbe49bcf | |
|
bda9d046ee | |
|
807cb96638 | |
|
98d40a21f6 | |
|
d2193c2526 | |
|
0d5c5c727c | |
|
a90d055d3e | |
|
59a53f1be2 | |
|
dda979487f | |
|
413be9b625 |
7
.flake8
7
.flake8
|
@ -12,9 +12,10 @@ exclude =
|
|||
__pycache__,
|
||||
build,
|
||||
dist,
|
||||
.conda
|
||||
.venv
|
||||
venv
|
||||
.conda,
|
||||
.venv,
|
||||
venv,
|
||||
docs/source/api
|
||||
|
||||
# Enable specific plugins or options
|
||||
# Example: Enabling flake8-docstrings
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
name: CI Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build-ubuntu-no-optional-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10", 3.13]
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install from source and install pytest
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -e .
|
||||
python -m pip install pytest lxml requests
|
||||
|
||||
- name: Run tests with pytest (no matplotlib, no pandas)
|
||||
run: |
|
||||
pytest tests/test_rendering_markdown.py::test_markdown_styling
|
||||
pytest tests/test_rendering_markdown.py::test_markdown_table
|
||||
|
||||
- name: Install matplotlib
|
||||
run: |
|
||||
python -m pip install matplotlib
|
||||
|
||||
- name: Run tests with pytest rendering equations (with matplotlib)
|
||||
run: |
|
||||
pytest tests/test_rendering_markdown.py::test_markdown_equations
|
||||
|
||||
build-ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10", 3.13]
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install LaTeX
|
||||
run: sudo apt-get install -y texlive-latex-extra texlive-fonts-recommended lmodern texlive-xetex texlive-science
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -e .[dev]
|
||||
|
||||
- name: Lint code with flake8
|
||||
run: flake8
|
||||
|
||||
- name: Type checking with mypy
|
||||
run: mypy
|
||||
|
||||
- name: Run tests with pytest
|
||||
run: pytest
|
||||
|
||||
- name: Upload rendered files
|
||||
uses: actions/upload-artifact@v4
|
||||
if: strategy.job-index == 0
|
||||
with:
|
||||
name: rendering-results-ubuntu
|
||||
path: tests/out/test_*_render*
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Cache MiKTeX Portable
|
||||
uses: actions/cache@v4
|
||||
id: miktex
|
||||
with:
|
||||
path: C:\tmp\cache
|
||||
key: miktex-portable-${{ runner.os }}-24.1-x64
|
||||
|
||||
- if: ${{ steps.miktex.outputs.cache-hit != 'true' }}
|
||||
name: Set up MiKTeX Portable
|
||||
run: |
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
Invoke-WebRequest https://www.nonan.net/w/files/miktex-portable-Win-x64.zip -OutFile miktex-portable-Win-x64.zip
|
||||
Expand-Archive miktex-portable-Win-x64.zip -DestinationPath C:\tmp\cache\
|
||||
|
||||
- name: Copy miktex directory
|
||||
run: |
|
||||
robocopy C:\tmp\cache\miktex-portable C:\tmp\test_miktex\miktex-portable /E /NFL /NDL
|
||||
if ($LASTEXITCODE -eq 1) { exit 0 }
|
||||
|
||||
- name: Add miktex to PATH
|
||||
run: |
|
||||
echo "PATH=$PATH;C:\tmp\test_miktex\miktex-portable\texmfs\install\miktex\bin\x64;C:\Program Files\Git\usr\bin" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
|
||||
- name: test xelatex
|
||||
run: xelatex --version
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install -e .[dev]
|
||||
|
||||
- name: Run tests with pytest
|
||||
run: pytest
|
||||
|
||||
- name: Upload rendered files
|
||||
uses: actions/upload-artifact@v4
|
||||
if: strategy.job-index == 0
|
||||
with:
|
||||
name: rendering-results-windows
|
||||
path: tests/out/test_*_render*.pdf
|
||||
|
||||
build-docs:
|
||||
if: github.event_name == 'push'
|
||||
needs: build-ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download rendering outputs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rendering-results-ubuntu
|
||||
path: docs/build/html/files/
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Install package and dependencies
|
||||
run: pip install .[doc_build]
|
||||
|
||||
- name: Generate Class List
|
||||
run: python ./docs/source/generate_class_list.py
|
||||
|
||||
- name: Build Docs
|
||||
run: |
|
||||
mkdir -p docs/source/media
|
||||
cp media/* docs/source/media/
|
||||
mkdir -p docs/source/tests
|
||||
cp tests/test_rendering_example*.py docs/source/tests/
|
||||
cp LICENSE docs/source/LICENSE.md
|
||||
cd docs
|
||||
sphinx-apidoc -o source/ ../src/ -M --no-toc
|
||||
rm ./source/*.rst
|
||||
make html
|
||||
touch ./build/html/.nojekyll
|
||||
mkdir -p ./build/html/_autogenerated
|
||||
cp ./build/html/api/* ./build/html/_autogenerated/
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs/build/html
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'push'
|
||||
needs: build-docs
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
|
@ -0,0 +1,39 @@
|
|||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Build and publish
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/project/${{ github.event.repository.name }}/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Ensure this is main branch
|
||||
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
||||
run: echo "Proceeding with publish"
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install build tools
|
||||
run: python -m pip install --upgrade build twine
|
||||
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
run: python -m twine upload dist/*
|
|
@ -69,7 +69,8 @@ instance/
|
|||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
docs/build/
|
||||
docs/source/api/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
|
@ -132,3 +133,4 @@ pyModbusTCP_old/
|
|||
test.py
|
||||
test_*.ipynb
|
||||
settings.json
|
||||
tests/out/test*
|
||||
|
|
76
README.md
76
README.md
|
@ -1,44 +1,50 @@
|
|||
# Pyladoc
|
||||
|
||||
## Description
|
||||
Pyladoc is a python package for programmatically generating HTML and
|
||||
PDF/LaTeX output. This package targets specifically applications where reports
|
||||
or results with Pandas-tables and Matplotlib-figures are generated programmatically
|
||||
to be displayed as website and as PDF document without any manual formatting
|
||||
steps.
|
||||
Pyladoc is a Python package for programmatically generating HTML and
|
||||
PDF/LaTeX output. This package specifically targets applications where reports
|
||||
or results with Pandas tables and Matplotlib figures are generated
|
||||
to be displayed as a website and as a PDF document without involving any manual
|
||||
formatting steps.
|
||||
|
||||
This package focuses on the "Document in Code" approach for cases
|
||||
where a lot of calculations and data handling is done but not a lot of
|
||||
document text needs to be displayed.
|
||||
document text needs to be displayed. The multiline string capability of Python
|
||||
handles this very well. In comparison to "Code in Document" templates,
|
||||
Python tools support this approach out of the box—similar to docstrings.
|
||||
|
||||
As backend for PDF generation LaTeX is used. There are excellent engines for
|
||||
rendering HTML to PDF available, but even if there is no requirement for an
|
||||
accurate typesetting, placing programmatically content of variable
|
||||
composition and element sizes on fixed size pages without manual intervention
|
||||
is a hard problem that LaTeX is very capable of.
|
||||
LaTeX is used as the backend for PDF generation. There are excellent engines for
|
||||
rendering HTML to PDF, but even if there is no requirement for
|
||||
accurate typesetting, placing programmatically generated content of variable
|
||||
composition and element sizes on fixed-size pages without manual intervention
|
||||
is a hard problem where LaTeX is superior.
|
||||
|
||||
## Example outputs
|
||||
The following documents are generated by [tests/test_rendering_example1_doc.py](tests/test_rendering_example1_doc.py):
|
||||
|
||||
- HTML: [test_html_render1.html](https://html-preview.github.io/?url=https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.html)
|
||||
- PDF: [test_latex_render1.pdf](https://raw.githubusercontent.com/Nonannet/pyladoc/refs/heads/main/tests/out/test_latex_render1.pdf)
|
||||
[](https://nonannet.github.io/pyladoc/files/test_latex_render1.pdf)
|
||||
|
||||
- HTML: [test_html_render1.html](https://nonannet.github.io/pyladoc/files/test_html_render1.html)
|
||||
- PDF: [test_latex_render1.pdf](https://nonannet.github.io/pyladoc/files/test_latex_render1.pdf) ([LaTeX](https://nonannet.github.io/pyladoc/files/test_html_render1.tex))
|
||||
|
||||
The documents are generated by the script [tests/test_rendering_example1_doc.py](tests/test_rendering_example1_doc.py).
|
||||
|
||||
### Supported primitives
|
||||
- Text (can be Markdown or HTML formatted)
|
||||
- Headings
|
||||
- Tables (Pandas, Markdown or HTML)
|
||||
- Matplotlib figures
|
||||
- LaTeX equations (Block or inline)
|
||||
- Named references for figures, tables and equation
|
||||
- LaTeX equations (block or inline)
|
||||
- Named references for figures, tables, and equations
|
||||
|
||||
### Key Features
|
||||
- HTML and PDF/LaTeX rendering of the same document
|
||||
- Single file output including figures
|
||||
- Figure and equation embedding in HTML by inline SVG, SVG in Base64 or PNG in Base64
|
||||
- Figure and equation embedding in HTML by inline SVG, SVG in Base64, or PNG in Base64
|
||||
- Figure embedding in LaTeX as PGF/TikZ
|
||||
- Tested on Linux and Windows
|
||||
|
||||
### Usage Scenarios
|
||||
- Webservices
|
||||
- Web services
|
||||
- Report generation for lab equipment
|
||||
|
||||
## Installation
|
||||
|
@ -48,8 +54,30 @@ It can be installed with pip:
|
|||
pip install pyladoc
|
||||
```
|
||||
|
||||
As well as with conda:
|
||||
```bash
|
||||
conda install conda-forge::pyladoc
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
Pyladoc depends on the markdown package.
|
||||
|
||||
Optional dependencies are:
|
||||
- Matplotlib Python package for rendering LaTeX equations for HTML output
|
||||
- LaTeX for exporting to PDF or exporting Matplotlib figures to LaTeX (PGF/TikZ rendering)
|
||||
- Pandas and Jinja2 for rendering Pandas tables
|
||||
- Matplotlib for rendering Matplotlib figures (obviously)
|
||||
|
||||
For the included template, the `miktex` LaTeX distribution works on Windows
|
||||
and the following LaTeX setup works on Ubuntu (both tested in CI):
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y texlive-latex-extra texlive-fonts-recommended lmodern texlive-xetex texlive-science
|
||||
```
|
||||
|
||||
## Usage
|
||||
It is easy to use as the following example code shows:
|
||||
It is easy to use, as the following example code shows:
|
||||
|
||||
```python
|
||||
import pyladoc
|
||||
|
@ -85,7 +113,7 @@ doc.to_pdf('test.pdf')
|
|||
```
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome, please open an issue or submit a pull request on GitHub.
|
||||
Contributions are welcome; please open an issue or submit a pull request on GitHub.
|
||||
|
||||
## Developer Guide
|
||||
To get started with developing the `pyladoc` package, follow these steps.
|
||||
|
@ -97,14 +125,14 @@ git clone https://github.com/Nonannet/pyladoc.git
|
|||
cd pyladoc
|
||||
```
|
||||
|
||||
It's recommended to setup an venv:
|
||||
It's recommended to set up a venv:
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows use `venv\Scripts\activate`
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # On Windows use `.venv\Scripts\activate`
|
||||
```
|
||||
|
||||
Install the package and dev-dependencies while keeping files in the
|
||||
Install the package and development dependencies while keeping files in the
|
||||
current directory:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -0,0 +1,35 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
|
@ -0,0 +1,33 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath("../src/"))
|
||||
|
||||
project = 'pyladoc'
|
||||
copyright = '2025, Nicolas Kruse'
|
||||
author = 'Nicolas Kruse'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx_autodoc_typehints", "myst_parser"]
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = []
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
# html_theme = 'alabaster'
|
||||
html_theme = 'pydata_sphinx_theme'
|
||||
html_static_path = ['_static']
|
||||
|
||||
autodoc_inherit_docstrings = True
|
||||
autoclass_content = 'both'
|
|
@ -0,0 +1,78 @@
|
|||
import importlib
|
||||
import inspect
|
||||
import fnmatch
|
||||
from io import TextIOWrapper
|
||||
import os
|
||||
|
||||
|
||||
def write_classes(f: TextIOWrapper, patterns: list[str], module_name: str, title: str, description: str = '', exclude: list[str] = []) -> None:
|
||||
"""Write the classes to the file."""
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
classes = [
|
||||
name for name, obj in inspect.getmembers(module, inspect.isclass)
|
||||
if (obj.__module__ == module_name and
|
||||
any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude) and
|
||||
obj.__doc__ and '(Automatic generated stub)' not in obj.__doc__)
|
||||
]
|
||||
|
||||
if description:
|
||||
f.write(f'{description}\n\n')
|
||||
|
||||
write_dochtree(f, title, classes)
|
||||
|
||||
for cls in classes:
|
||||
with open(f'docs/source/api/{cls}.md', 'w') as f2:
|
||||
f2.write(f'# {module_name}.{cls}\n')
|
||||
f2.write('```{eval-rst}\n')
|
||||
f2.write(f'.. autoclass:: {module_name}.{cls}\n')
|
||||
f2.write(' :members:\n')
|
||||
f2.write(' :undoc-members:\n')
|
||||
f2.write(' :show-inheritance:\n')
|
||||
f2.write(' :inherited-members:\n')
|
||||
f2.write('```\n\n')
|
||||
|
||||
|
||||
def write_functions(f: TextIOWrapper, patterns: list[str], module_name: str, title: str, description: str = '', exclude: list[str] = []) -> None:
|
||||
"""Write the classes to the file."""
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
functions = [
|
||||
name for name, obj in inspect.getmembers(module, inspect.isfunction)
|
||||
if (obj.__module__ == module_name and
|
||||
any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude))
|
||||
]
|
||||
|
||||
if description:
|
||||
f.write(f'{description}\n\n')
|
||||
|
||||
write_dochtree(f, title, functions)
|
||||
|
||||
for func in functions:
|
||||
if not func.startswith('_'):
|
||||
with open(f'docs/source/api/{func}.md', 'w') as f2:
|
||||
f2.write(f'# {module_name}.{func}\n')
|
||||
f2.write('```{eval-rst}\n')
|
||||
f2.write(f'.. autofunction:: {module_name}.{func}\n')
|
||||
f2.write('```\n\n')
|
||||
|
||||
|
||||
def write_dochtree(f: TextIOWrapper, title: str, items: list[str]):
|
||||
f.write('```{toctree}\n')
|
||||
f.write(':maxdepth: 1\n')
|
||||
f.write(f':caption: {title}:\n')
|
||||
for text in items:
|
||||
if not text.startswith('_'):
|
||||
f.write(f"{text}\n")
|
||||
f.write('```\n\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Ensure the output directory exists
|
||||
os.makedirs('docs/source/api', exist_ok=True)
|
||||
|
||||
with open('docs/source/api/index.md', 'w') as f:
|
||||
f.write('# Classes and functions\n\n')
|
||||
write_classes(f, ['DocumentWriter'], 'pyladoc', title='DocumentWriter Class')
|
||||
write_functions(f, ['*'], 'pyladoc', title='Functions')
|
||||
write_functions(f, ['*'], 'pyladoc.latex', title='Submodule latex')
|
|
@ -0,0 +1,9 @@
|
|||
```{toctree}
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
api/index
|
||||
repo
|
||||
```
|
||||
|
||||
```{include} ../../README.md
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
# Code repository
|
||||
|
||||
Code repository is on GitHub: [github.com/Nonannet/pyladoc](https://github.com/Nonannet/pyladoc).
|
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
|
@ -1,15 +1,15 @@
|
|||
[project]
|
||||
name = "pyladoc"
|
||||
version = "1.1.0"
|
||||
version = "1.2.4"
|
||||
authors = [
|
||||
{ name="Nicolas Kruse", email="nicolas.kruse@nonan.net" },
|
||||
]
|
||||
description = "Package for generating HTML and PDF/latex from python code"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.10"
|
||||
license = "MIT"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
dependencies = [
|
||||
|
@ -19,16 +19,23 @@ dependencies = [
|
|||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest", "flake8", "mypy",
|
||||
"lxml", "types-lxml",
|
||||
"lxml", "types-lxml", "types-Markdown", "pandas-stubs",
|
||||
"requests",
|
||||
"matplotlib>=3.1.1",
|
||||
"pandas>=2.0.0", "Jinja2",
|
||||
]
|
||||
doc_build = [
|
||||
"sphinx",
|
||||
"pydata_sphinx_theme",
|
||||
"sphinx-autodoc-typehints",
|
||||
"myst-parser"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/Nonannet/pyladoc"
|
||||
Repository = "https://github.com/Nonannet/pyladoc"
|
||||
Issues = "https://github.com/Nonannet/pyladoc/issues"
|
||||
documentation = "https://nonannet.github.io/pyladoc/"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0", "wheel"]
|
||||
|
@ -38,7 +45,7 @@ build-backend = "setuptools.build_meta"
|
|||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
pyladoc = ["templates/*"]
|
||||
pyladoc = ["templates/*", "py.typed"]
|
||||
|
||||
[tool.mypy]
|
||||
files = ["src"]
|
||||
|
|
|
@ -8,6 +8,7 @@ from . import latex
|
|||
import pkgutil
|
||||
from html.parser import HTMLParser
|
||||
from io import StringIO
|
||||
from . import svg_tools
|
||||
|
||||
HTML_OUTPUT = 0
|
||||
LATEX_OUTPUT = 1
|
||||
|
@ -32,6 +33,7 @@ else:
|
|||
Table = DataFrame | Styler
|
||||
except ImportError:
|
||||
Table = DataFrame
|
||||
Styler = None
|
||||
|
||||
try:
|
||||
import matplotlib.pyplot as plt
|
||||
|
@ -58,17 +60,6 @@ def _markdown_to_html(text: str) -> str:
|
|||
return html_text
|
||||
|
||||
|
||||
def _clean_svg(svg_text: str) -> str:
|
||||
# remove all tags not alllowd for inline svg from metadata:
|
||||
svg_text = re.sub(r'<metadata>.*?</metadata>', '', svg_text, flags=re.DOTALL)
|
||||
|
||||
# remove illegal path-tags without d attribute:
|
||||
return re.sub(r'<path(?![^>]*\sd=)\s.*?/>', '', svg_text, flags=re.DOTALL)
|
||||
|
||||
# def _get_templ_vars(template: str) -> list[str]:
|
||||
# return re.findall("<!---START (.+?)--->.*?<!---END .+?--->", template, re.DOTALL)
|
||||
|
||||
|
||||
def _drop_indent(text: str, amount: int) -> str:
|
||||
"""
|
||||
Drops a specific number of indentation spaces from a multiline text.
|
||||
|
@ -99,8 +90,9 @@ def _save_figure(fig: Figure, buff: io.BytesIO, figure_format: FFormat, font_fam
|
|||
yield ax.xaxis.label
|
||||
yield ax.yaxis.label
|
||||
yield from ax.get_xticklabels() + ax.get_yticklabels()
|
||||
legend: Mpl_Legend = ax.get_legend()
|
||||
legend = ax.get_legend()
|
||||
if legend:
|
||||
assert isinstance(legend, Mpl_Legend)
|
||||
yield from legend.get_texts()
|
||||
|
||||
# Store current figure settings
|
||||
|
@ -142,6 +134,7 @@ def escape_html(text: str) -> str:
|
|||
|
||||
|
||||
def figure_to_string(fig: Figure,
|
||||
unique_id: str,
|
||||
figure_format: FFormat = 'svg',
|
||||
font_family: str | None = None,
|
||||
scale: float = 1,
|
||||
|
@ -175,7 +168,7 @@ def figure_to_string(fig: Figure,
|
|||
elif figure_format == 'svg' and not base64:
|
||||
i = buff.read(2028).find(b'<svg') # skip xml and DOCTYPE header
|
||||
buff.seek(max(i, 0))
|
||||
return _clean_svg(buff.read().decode('utf-8'))
|
||||
return svg_tools.update_svg_ids(svg_tools.clean_svg(buff.read().decode('utf-8')), unique_id)
|
||||
|
||||
else:
|
||||
image_mime = {"png": "image/png", "svg": "image/svg+xml"}
|
||||
|
@ -218,7 +211,7 @@ def _fillin_reference_names(input_string: str, item_index: dict[str, int]) -> st
|
|||
for start, end, ref in replacements:
|
||||
assert ref in item_index, f"Reference {ref} does not exist in the document"
|
||||
ret.append(input_string[current_pos:start - 1])
|
||||
ret.append(str(item_index[ref]))
|
||||
ret.append(f'<a href="#pyld-ref-{latex.normalize_label_text(ref)}">{item_index[ref]}</a>')
|
||||
current_pos = end
|
||||
return ''.join(ret) + input_string[current_pos:]
|
||||
|
||||
|
@ -254,30 +247,40 @@ def _create_document_writer() -> 'DocumentWriter':
|
|||
return new_dwr
|
||||
|
||||
|
||||
def inject_to_template(content: str, template_path: str = '', internal_template: str = '') -> str:
|
||||
def inject_to_template(fields_dict: dict[str, str],
|
||||
template_path: str = '',
|
||||
internal_template: str = '',
|
||||
template_string: str = '',
|
||||
) -> str:
|
||||
"""
|
||||
injects a content string into a template. The placeholder <!--CONTENT-->
|
||||
injects content fields into a template. The placeholder <!--CONTENT-->
|
||||
will be replaced by the content. If the placeholder is prefixed with a
|
||||
'%' comment character, this character will be replaced as well.
|
||||
|
||||
Args:
|
||||
fields_dict: A dictionary with field names as keys and content as values
|
||||
template_path: Path to a template file
|
||||
internal_template: Path to a internal default template
|
||||
template_string: A template string to use directly
|
||||
|
||||
Returns:
|
||||
Template with included content
|
||||
"""
|
||||
assert isinstance(fields_dict, dict), 'fields_dict must be a dictionary'
|
||||
if template_path:
|
||||
with open(template_path, 'r') as f:
|
||||
template = f.read()
|
||||
elif internal_template:
|
||||
template = _get_pkgutil_string(internal_template)
|
||||
elif template_string:
|
||||
template = template_string
|
||||
else:
|
||||
raise Exception('No template provided')
|
||||
|
||||
assert '<!--CONTENT-->' in template, 'No <!--CONTENT--> expression in template located'
|
||||
prep_template = re.sub(r"\%?\s*<!--CONTENT-->", '<!--CONTENT-->', template)
|
||||
return prep_template.replace('<!--CONTENT-->', content)
|
||||
def replace_field(match: re.Match[str]) -> str:
|
||||
return fields_dict.get(match.group(1), match.group(0))
|
||||
|
||||
return re.sub(r"(?:\%+\s*)?\<!--(.*?)-->", replace_field, template, 0, re.MULTILINE)
|
||||
|
||||
|
||||
class DocumentWriter():
|
||||
|
@ -298,13 +301,13 @@ class DocumentWriter():
|
|||
self._item_index: dict[str, int] = {}
|
||||
self._fig_scale: float = 1
|
||||
|
||||
def _add_item(self, ref_id: str, ref_type: str, caption_prefix: str) -> str:
|
||||
def _add_item(self, ref_id: str, ref_type: str, caption_prefix: str) -> tuple[str, str]:
|
||||
current_index = self._item_count.get(ref_type, 0) + 1
|
||||
if not ref_id:
|
||||
ref_id = str(current_index)
|
||||
ref_id = f"auto{current_index}"
|
||||
self._item_index[f"{ref_type}:{ref_id}"] = current_index
|
||||
self._item_count[ref_type] = current_index
|
||||
return caption_prefix.format(current_index)
|
||||
return caption_prefix.format(current_index), latex.normalize_label_text(f"{ref_type}:{ref_id}")
|
||||
|
||||
def _equation_embedding_reescaping(self, text: str) -> str:
|
||||
"""
|
||||
|
@ -333,11 +336,10 @@ class DocumentWriter():
|
|||
parts = latex_label.split(':')
|
||||
ref_type = parts[0]
|
||||
ref_id = parts[1]
|
||||
caption = self._add_item(ref_id, ref_type, '({})')
|
||||
return (f'\n<latex type="block" ref_type="{ref_type}"'
|
||||
f' ref_id="{ref_id}" caption="{caption}">{content}</latex>\n')
|
||||
caption, reference = self._add_item(ref_id, ref_type, '({})')
|
||||
return (f'<latex type="block" reference="{reference}" caption="{caption}">{content}</latex>')
|
||||
else:
|
||||
return f'\n<latex type="block">{content}</latex>\n'
|
||||
return f'<latex type="block">{content}</latex>'
|
||||
|
||||
result = block_pattern.sub(block_repl, text)
|
||||
|
||||
|
@ -349,16 +351,16 @@ class DocumentWriter():
|
|||
|
||||
return inline_pattern.sub(inline_repl, result)
|
||||
|
||||
def _get_equation_html(self, latex_equation: str, caption: str, block: bool = False) -> str:
|
||||
def _get_equation_html(self, latex_equation: str, caption: str, reference: str, block: bool = False) -> str:
|
||||
fig = latex_to_figure(latex_equation)
|
||||
if block:
|
||||
ret = ('<div class="equation-container">'
|
||||
'<div class="equation">%s</div>'
|
||||
'<div class="equation-number">%s</div></div>') % (
|
||||
figure_to_string(fig, self._figure_format, base64=self._base64_svgs),
|
||||
caption)
|
||||
fig_str = figure_to_string(fig, reference, self._figure_format, base64=self._base64_svgs)
|
||||
ret = ('<div class="equation-container" '
|
||||
f'id="pyld-ref-{reference}">'
|
||||
f'<div class="equation">{fig_str}</div>'
|
||||
f'<div class="equation-number">{caption}</div></div>')
|
||||
else:
|
||||
ret = '<span class="inline-equation">' + figure_to_string(fig, self._figure_format, base64=self._base64_svgs) + '</span>'
|
||||
ret = '<span class="inline-equation">' + figure_to_string(fig, reference, self._figure_format, base64=self._base64_svgs) + '</span>'
|
||||
|
||||
plt.close(fig)
|
||||
return ret
|
||||
|
@ -372,34 +374,56 @@ class DocumentWriter():
|
|||
self.modified_html = StringIO()
|
||||
self.in_latex: bool = False
|
||||
self.eq_caption: str = ''
|
||||
self.reference: str = ''
|
||||
self.block: bool = False
|
||||
self.p_tags: int = 0
|
||||
self.dw = document_writer
|
||||
self.latex_count = 0
|
||||
self.self_closing = False
|
||||
|
||||
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
||||
if tag == 'hr':
|
||||
self.modified_html.write(f"<{tag}>")
|
||||
self.self_closing = True
|
||||
elif tag == 'latex':
|
||||
self.in_latex = True
|
||||
attr_dict = {k: v if v else '' for k, v in attrs}
|
||||
self.eq_caption = attr_dict.get('caption', '')
|
||||
if 'reference' in attr_dict:
|
||||
self.reference = attr_dict['reference']
|
||||
else:
|
||||
self.latex_count += 1
|
||||
self.reference = f"auto_id_{self.latex_count}"
|
||||
self.block = attr_dict.get('type') == 'block'
|
||||
elif not self.in_latex:
|
||||
tag_text = self.get_starttag_text()
|
||||
if tag_text:
|
||||
self.self_closing = tag_text.endswith('/>')
|
||||
self.modified_html.write(tag_text)
|
||||
if tag == 'p':
|
||||
self.p_tags += 1
|
||||
|
||||
def handle_data(self, data: str) -> None:
|
||||
if self.in_latex:
|
||||
self.modified_html.write(
|
||||
self.dw._get_equation_html(data, self.eq_caption, self.block))
|
||||
eq_html = self.dw._get_equation_html(data, self.eq_caption, self.reference, self.block)
|
||||
if self.p_tags > 0 and self.block:
|
||||
# If a block equation (with divs) is inside a p tag: close and reopen it
|
||||
self.modified_html.write(f"</p>{eq_html}<p>")
|
||||
else:
|
||||
self.modified_html.write(eq_html)
|
||||
|
||||
else:
|
||||
self.modified_html.write(data)
|
||||
|
||||
def handle_endtag(self, tag: str) -> None:
|
||||
if tag == 'latex':
|
||||
self.in_latex = False
|
||||
elif self.self_closing:
|
||||
self.self_closing = False
|
||||
else:
|
||||
self.modified_html.write(f"</{tag}>")
|
||||
if tag == 'p' and self.p_tags > 0:
|
||||
self.p_tags -= 1
|
||||
|
||||
parser = HTMLPostProcessor(self)
|
||||
parser.feed(html_code)
|
||||
|
@ -431,18 +455,19 @@ class DocumentWriter():
|
|||
"""
|
||||
|
||||
def render_to_html() -> str:
|
||||
caption_prefix = self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
return '<div class="figure">%s%s</div>' % (
|
||||
figure_to_string(fig, self._figure_format, base64=self._base64_svgs, scale=self._fig_scale),
|
||||
caption_prefix, reference = self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
return '<div id="pyld-ref-%s" class="figure">%s%s</div>' % (
|
||||
reference,
|
||||
figure_to_string(fig, reference, self._figure_format, base64=self._base64_svgs, scale=self._fig_scale),
|
||||
'<br>' + caption_prefix + escape_html(caption) if caption else '')
|
||||
|
||||
def render_to_latex() -> str:
|
||||
self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
_, reference = self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
return '\\begin{figure}%s\n%s\n\\caption{%s}\n%s\\end{figure}' % (
|
||||
'\n\\centering' if centered else '',
|
||||
figure_to_string(fig, 'pgf', self._font_family, scale=self._fig_scale),
|
||||
figure_to_string(fig, reference, 'pgf', self._font_family, scale=self._fig_scale),
|
||||
latex.escape_text(caption),
|
||||
'\\label{%s}\n' % latex.normalize_label_text(ref_type + ':' + ref_id) if ref_id else '')
|
||||
'\\label{%s}\n' % latex.normalize_label_text(reference) if ref_id else '')
|
||||
|
||||
self._doc.append([render_to_html, render_to_latex])
|
||||
|
||||
|
@ -462,27 +487,27 @@ class DocumentWriter():
|
|||
centered: Whether to center the table in LaTeX output
|
||||
"""
|
||||
assert Table and isinstance(table, Table), 'Table has to be a pandas DataFrame oder DataFrame Styler'
|
||||
styler = table if isinstance(table, Styler) else getattr(table, 'style', None)
|
||||
assert isinstance(styler, Styler), 'Jinja2 package is required for rendering tables'
|
||||
styler = table if Styler and isinstance(table, Styler) else getattr(table, 'style', None) # type: ignore[truthy-function]
|
||||
assert Styler and isinstance(styler, Styler), 'Jinja2 package is required for rendering pandas tables' # type: ignore[truthy-function]
|
||||
|
||||
def render_to_html() -> str:
|
||||
caption_prefix = self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
caption_prefix, reference = self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
|
||||
html_string = styler.to_html(table_uuid=ref_id, caption=caption_prefix + escape_html(caption))
|
||||
return re.sub(r'<style.*?>.*?</style>', '', html_string, flags=re.DOTALL)
|
||||
return f'<div id="pyld-ref-{reference}">' + re.sub(r'<style.*?>.*?</style>', '', html_string, flags=re.DOTALL) + '</div>'
|
||||
|
||||
def render_to_latex() -> str:
|
||||
self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
ref_label = latex.normalize_label_text(ref_type + ':' + ref_id)
|
||||
_, reference = self._add_item(ref_id, ref_type, prefix_pattern)
|
||||
if self._table_renderer == 'pandas':
|
||||
return styler.to_latex(
|
||||
label=ref_label,
|
||||
label=reference,
|
||||
hrules=True,
|
||||
convert_css=True,
|
||||
siunitx=True,
|
||||
caption=latex.escape_text(caption),
|
||||
position_float='centering' if centered else None)
|
||||
else:
|
||||
return latex.render_pandas_styler_table(styler, caption, ref_label, centered)
|
||||
return latex.render_pandas_styler_table(styler, caption, reference, centered)
|
||||
|
||||
self._doc.append([render_to_html, render_to_latex])
|
||||
|
||||
|
@ -581,12 +606,12 @@ class DocumentWriter():
|
|||
"""
|
||||
|
||||
def render_to_html() -> str:
|
||||
caption = self._add_item(ref_id, ref_type, '({})')
|
||||
return self._get_equation_html(latex_equation, caption)
|
||||
caption, reference = self._add_item(ref_id, ref_type, '({})')
|
||||
return self._get_equation_html(latex_equation, caption, reference, block=True)
|
||||
|
||||
def render_to_latex() -> str:
|
||||
self._add_item(ref_id, ref_type, '')
|
||||
return latex.get_equation_code(latex_equation, ref_type, ref_id)
|
||||
_, reference = self._add_item(ref_id, ref_type, '')
|
||||
return latex.get_equation_code(latex_equation, reference, block=True)
|
||||
|
||||
self._doc.append([render_to_html, render_to_latex])
|
||||
|
||||
|
@ -596,20 +621,19 @@ class DocumentWriter():
|
|||
|
||||
Args:
|
||||
text: The markdown text to add
|
||||
section_class: The class for the text section
|
||||
section_class: The HTML-class and LaTeX-environment name for the text section
|
||||
"""
|
||||
norm_text = _normalize_text_indent(str(text))
|
||||
|
||||
def render_to_html() -> str:
|
||||
html = self._html_post_processing(_markdown_to_html(self._equation_embedding_reescaping(norm_text)))
|
||||
html = _markdown_to_html(self._equation_embedding_reescaping(norm_text))
|
||||
if section_class:
|
||||
return '<div class="' + section_class + '">' + html + '</div>'
|
||||
else:
|
||||
return html
|
||||
|
||||
def render_to_latex() -> str:
|
||||
html = _markdown_to_html(
|
||||
self._equation_embedding_reescaping(norm_text))
|
||||
html = render_to_html()
|
||||
return latex.from_html(html)
|
||||
|
||||
self._doc.append([render_to_html, render_to_latex])
|
||||
|
@ -635,7 +659,7 @@ class DocumentWriter():
|
|||
self._base64_svgs = base64_svgs
|
||||
self._fig_scale = figure_scale
|
||||
|
||||
return _fillin_reference_names(self._render_doc(HTML_OUTPUT), self._item_index)
|
||||
return self._html_post_processing(_fillin_reference_names(self._render_doc(HTML_OUTPUT), self._item_index))
|
||||
|
||||
def to_latex(self, font_family: Literal[None, 'serif', 'sans-serif'] = None,
|
||||
table_renderer: TRenderer = 'simple', figure_scale: float = 1) -> str:
|
||||
|
@ -660,27 +684,37 @@ class DocumentWriter():
|
|||
def to_pdf(self, file_path: str,
|
||||
font_family: Literal[None, 'serif', 'sans-serif'] = None,
|
||||
table_renderer: TRenderer = 'simple',
|
||||
latex_template_path: str = '') -> bool:
|
||||
latex_template_path: str = '',
|
||||
fields_dict: dict[str, str] = {},
|
||||
figure_scale: float = 1,
|
||||
engine: latex.LatexEngine = 'pdflatex') -> bool:
|
||||
"""
|
||||
Export the document to a PDF file using LaTeX.
|
||||
|
||||
Args:
|
||||
file_path: The path to save the PDF file to
|
||||
font_family: Overwrites the front family for figures and the template
|
||||
table_renderer: The renderer for tables (simple: renderer with column type
|
||||
guessing for text and numbers; pandas: using the internal pandas LaTeX renderer)
|
||||
latex_template_path: Path to a LaTeX template file. The
|
||||
expression <!--CONTENT--> will be replaced by the generated content.
|
||||
If no path is provided a default template is used.
|
||||
fields_dict: A dictionary with field names as keys and content as values
|
||||
replacing the placeholders <!--KEY--> in the template.
|
||||
figure_scale: Scaling factor for the figure size
|
||||
engine: LaTeX engine (pdflatex, lualatex, xelatex or tectonic)
|
||||
|
||||
Returns:
|
||||
True if the PDF file was successfully created
|
||||
"""
|
||||
latex_code = inject_to_template(self.to_latex(font_family, table_renderer),
|
||||
content = self.to_latex(font_family, table_renderer, figure_scale)
|
||||
latex_code = inject_to_template({'CONTENT': content} | fields_dict,
|
||||
latex_template_path,
|
||||
'templates/default_template.tex')
|
||||
internal_template='templates/default_template.tex')
|
||||
|
||||
if font_family == 'sans-serif':
|
||||
latex_code = latex.inject_latex_command(latex_code, '\\renewcommand{\\familydefault}{\\sfdefault}')
|
||||
success, errors, warnings = latex.compile(latex_code, file_path)
|
||||
success, errors, warnings = latex.compile(latex_code, file_path, engine=engine)
|
||||
|
||||
if not success:
|
||||
print('Errors:')
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from html.parser import HTMLParser
|
||||
from typing import Generator, Any
|
||||
from pandas.io.formats.style import Styler
|
||||
from typing import Generator, Any, Literal, get_args, TYPE_CHECKING
|
||||
import re
|
||||
import os
|
||||
import shutil
|
||||
|
@ -8,9 +7,15 @@ import subprocess
|
|||
import tempfile
|
||||
from .latex_escaping import unicode_to_latex_dict, latex_escape_dict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pandas.io.formats.style import Styler
|
||||
else:
|
||||
try:
|
||||
from pandas.io.formats.style import Styler
|
||||
except ImportError:
|
||||
Styler = None
|
||||
|
||||
def basic_formatter(value: Any) -> str:
|
||||
return escape_text(str(value))
|
||||
LatexEngine = Literal['pdflatex', 'lualatex', 'xelatex', 'tectonic']
|
||||
|
||||
|
||||
def to_ascii(text: str) -> str:
|
||||
|
@ -82,19 +87,18 @@ def escape_text(text: str) -> str:
|
|||
return ''.join(ret)
|
||||
|
||||
|
||||
def get_equation_code(equation: str, ref_id: str, ref_type: str, block: bool = False) -> str:
|
||||
def get_equation_code(equation: str, reference: str | None, block: bool = False) -> str:
|
||||
"""
|
||||
Converts an equation string to LaTeX code.
|
||||
|
||||
Args:
|
||||
equation: The LaTeX equation string.
|
||||
ref_id: The reference ID for the equation.
|
||||
ref_type: The type of reference (e.g., 'eq', 'fig', etc.).
|
||||
reference: The reference type and ID for the equation separated by a ':'.
|
||||
"""
|
||||
if block:
|
||||
if ref_id:
|
||||
return '\\begin{equation}\\label{%s:%s}%s\\end{equation}' % (
|
||||
normalize_label_text(ref_type), normalize_label_text(ref_id), equation)
|
||||
if reference:
|
||||
return '\\begin{equation}\\label{%s}%s\\end{equation}' % (
|
||||
normalize_label_text(reference), equation)
|
||||
else:
|
||||
return '\\[%s\\]' % equation
|
||||
else:
|
||||
|
@ -114,13 +118,16 @@ def render_pandas_styler_table(df_style: Styler, caption: str = '', label: str =
|
|||
Returns:
|
||||
The LaTeX code.
|
||||
"""
|
||||
assert Styler, 'Jinja2 package is required for rendering pandas tables' # type: ignore[truthy-function]
|
||||
assert isinstance(df_style, Styler), 'df_style has to be of type Styler'
|
||||
|
||||
def iter_table(table: dict[str, Any]) -> Generator[str, None, None]:
|
||||
yield '\\begin{table}\n'
|
||||
if centering:
|
||||
yield '\\centering\n'
|
||||
|
||||
# Guess column type
|
||||
numeric = re.compile(r'^[<>]?\s*(?:\d+,?)+(?:\.\d+)?(?:\s\D.*)?$')
|
||||
numeric = re.compile('^[<>\\-\u2212]?\\s*(?:\\d+,?)+(?:\\.\\d+)?(?:\\s\\D.*)?$')
|
||||
formats = ['S' if all(
|
||||
(numeric.match(line[ci]['display_value'].strip()) for line in table['body'])
|
||||
) else 'l' for ci in range(len(table['body'][0])) if table['body'][0][ci]['is_visible']]
|
||||
|
@ -184,6 +191,7 @@ def from_html(html_code: str) -> str:
|
|||
self.header_flag = False
|
||||
self.attr_dict: dict[str, str] = {}
|
||||
self.equation_flag = False
|
||||
self.class_name: str = ''
|
||||
|
||||
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
||||
self.attr_dict = {k: v if v else '' for k, v in attrs}
|
||||
|
@ -210,6 +218,10 @@ def from_html(html_code: str) -> str:
|
|||
self.latex_code.append("\n\n\\noindent\\rule[0.5ex]{\\linewidth}{1pt}\n\n")
|
||||
elif tag == 'latex':
|
||||
self.equation_flag = True
|
||||
elif tag == 'div':
|
||||
self.class_name = self.attr_dict.get('class', '')
|
||||
if self.class_name:
|
||||
self.latex_code.append(f"\n\\begin{{{self.class_name}}}\n")
|
||||
|
||||
def handle_endtag(self, tag: str) -> None:
|
||||
if tag in html_to_latex:
|
||||
|
@ -235,13 +247,15 @@ def from_html(html_code: str) -> str:
|
|||
self.latex_code.append("}")
|
||||
elif tag == 'latex':
|
||||
self.equation_flag = False
|
||||
elif tag == 'div':
|
||||
if self.class_name:
|
||||
self.latex_code.append(f"\n\\end{{{self.class_name}}}\n")
|
||||
|
||||
def handle_data(self, data: str) -> None:
|
||||
if self.equation_flag:
|
||||
block = self.attr_dict.get('type') == 'block'
|
||||
ref_id = self.attr_dict.get('ref_id', '')
|
||||
ref_type = self.attr_dict.get('ref_type', 'eq')
|
||||
self.latex_code.append(get_equation_code(data, ref_id, ref_type, block))
|
||||
reference = self.attr_dict.get('reference')
|
||||
self.latex_code.append(get_equation_code(data, reference, block))
|
||||
elif data.strip():
|
||||
self.latex_code.append(escape_text(data))
|
||||
|
||||
|
@ -250,7 +264,7 @@ def from_html(html_code: str) -> str:
|
|||
return ''.join(parser.latex_code)
|
||||
|
||||
|
||||
def compile(latex_code: str, output_file: str = '', encoding: str = 'utf-8') -> tuple[bool, list[str], list[str]]:
|
||||
def compile(latex_code: str, output_file: str = '', encoding: str = 'utf-8', engine: LatexEngine = 'pdflatex') -> tuple[bool, list[str], list[str]]:
|
||||
"""
|
||||
Compiles LaTeX code to a PDF file.
|
||||
|
||||
|
@ -258,6 +272,7 @@ def compile(latex_code: str, output_file: str = '', encoding: str = 'utf-8') ->
|
|||
latex_code: The LaTeX code to compile.
|
||||
output_file: The output file path.
|
||||
encoding: The encoding of the LaTeX code.
|
||||
engine: LaTeX engine (pdflatex, lualatex, xelatex or tectonic)
|
||||
|
||||
Returns:
|
||||
A tuple with three elements:
|
||||
|
@ -266,8 +281,13 @@ def compile(latex_code: str, output_file: str = '', encoding: str = 'utf-8') ->
|
|||
- A list of warnings.
|
||||
"""
|
||||
|
||||
assert engine in get_args(LatexEngine), "engine must be pdflatex, lualatex, xelatex or tectonic"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_path:
|
||||
command = ['pdflatex', '-halt-on-error', '--output-directory', tmp_path]
|
||||
if engine == 'tectonic':
|
||||
command = ['tectonic', '--outdir', tmp_path, '-']
|
||||
else:
|
||||
command = [engine, '--halt-on-error', '--output-directory', tmp_path]
|
||||
|
||||
errors: list[str] = []
|
||||
warnings: list[str] = []
|
||||
|
@ -305,6 +325,17 @@ def compile(latex_code: str, output_file: str = '', encoding: str = 'utf-8') ->
|
|||
|
||||
|
||||
def inject_latex_command(text: str, command: str) -> str:
|
||||
"""
|
||||
Injects a provided LaTeX code under the last line
|
||||
starting with \\usepackage.
|
||||
|
||||
Args:
|
||||
text: input LaTeX code
|
||||
command: code to inject
|
||||
|
||||
Returns:
|
||||
LaTeX code with injected command
|
||||
"""
|
||||
lines = text.splitlines()
|
||||
|
||||
last_package_index = -1
|
||||
|
|
|
@ -68,7 +68,8 @@ unicode_to_latex_dict = {
|
|||
'£': r'{\pounds}',
|
||||
'¥': r'{\yen}',
|
||||
'\u00A0': r'~', # Non-breaking space
|
||||
'\u2007': ' ' # Figure space
|
||||
'\u2007': ' ', # Figure space
|
||||
'\u2212': '-' # Unicode minus sign
|
||||
}
|
||||
|
||||
latex_escape_dict = {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import re
|
||||
from re import Match
|
||||
|
||||
|
||||
def update_svg_ids(input_svg: str, unique_id: str) -> str:
|
||||
"""Add a unique ID part to all svg IDs and update references ti these IDs"""
|
||||
id_mapping: dict[str, str] = {}
|
||||
|
||||
def update_ids(match: Match[str]) -> str:
|
||||
old_id = match.group(1)
|
||||
new_id = f"svg-{unique_id}-{old_id}"
|
||||
id_mapping[old_id] = new_id
|
||||
return f' id="{new_id}"'
|
||||
|
||||
def update_references(match: Match[str]) -> str:
|
||||
old_ref = match.group(1)
|
||||
new_ref = id_mapping.get(old_ref, old_ref)
|
||||
if match.group(0).startswith('xlink:href'):
|
||||
return f'xlink:href="#{new_ref}"'
|
||||
else:
|
||||
return f'url(#{new_ref})'
|
||||
|
||||
# Update IDs
|
||||
svg_string = re.sub(r'\sid="(.*?)"', update_ids, input_svg)
|
||||
|
||||
# Update references to IDs
|
||||
svg_string = re.sub(r'url\(#([^\)]+)\)', update_references, svg_string)
|
||||
svg_string = re.sub(r'xlink:href="#([^\"]+)"', update_references, svg_string)
|
||||
|
||||
return svg_string
|
||||
|
||||
|
||||
def clean_svg(svg_text: str) -> str:
|
||||
# remove all tags not alllowd for inline svg from metadata:
|
||||
svg_text = re.sub(r'<metadata>.*?</metadata>', '', svg_text, flags=re.DOTALL)
|
||||
|
||||
# remove illegal path-tags without d attribute:
|
||||
return re.sub(r'<path(?![^>]*\sd=)\s.*?/>', '', svg_text, flags=re.DOTALL)
|
|
@ -16,11 +16,17 @@
|
|||
\usepackage{booktabs} % For professional-looking tables
|
||||
\usepackage{pgf} % For using pgf grafics
|
||||
\usepackage{textcomp, gensymb} % provides \degree symbol
|
||||
\usepackage{xcolor} % For colored text
|
||||
|
||||
\sisetup{
|
||||
table-align-text-post = false
|
||||
}
|
||||
|
||||
% Define fine print environment
|
||||
\newenvironment{fineprint}
|
||||
{\par\vspace{0.5\baselineskip}\noindent\footnotesize\color{gray}}
|
||||
{\par\vspace{0.5\baselineskip}}
|
||||
|
||||
% Geometry Settings
|
||||
\geometry{margin=1in} % 1-inch margins
|
||||
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
div.fineprint
|
||||
{
|
||||
font-size: smaller;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
div h1
|
||||
{
|
||||
font-size: 32px;
|
||||
|
|
|
@ -2,10 +2,7 @@ from typing import Generator, Any
|
|||
from lxml import etree
|
||||
from lxml.etree import _Element as EElement # type: ignore
|
||||
import requests
|
||||
|
||||
|
||||
with open('src/pyladoc/templates/test_template.html', mode='rt', encoding='utf-8') as f:
|
||||
html_test_template = f.read()
|
||||
import pyladoc
|
||||
|
||||
|
||||
def add_line_numbers(multiline_string: str) -> str:
|
||||
|
@ -53,7 +50,7 @@ def validate_html(html_string: str, validate_online: bool = False, check_for: li
|
|||
assert tag_type in tags, f"Tag {tag_type} not found in the html code"
|
||||
|
||||
if validate_online:
|
||||
test_page = html_test_template.replace('<!--CONTENT-->', html_string)
|
||||
test_page = pyladoc.inject_to_template({'CONTENT': html_string}, internal_template='templates/test_template.html')
|
||||
validation_result = validate_html_with_w3c(test_page)
|
||||
assert 'messages' in validation_result, 'Validate request failed'
|
||||
if validation_result['messages']:
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Rendering Test Outputs
|
||||
|
||||
This is the target directory for the test renderings.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,613 +0,0 @@
|
|||
\section{Thermal Conductivity of Mixtures}
|
||||
The determination of the thermal conductivity of gas mixtures is a central aspect of modeling
|
||||
transport phenomena, particularly in high-temperature and high-pressure processes. Among the
|
||||
most established approaches is the empirical equation introduced by Wassiljewa, which was
|
||||
subsequently refined by Mason and Saxena to improve its applicability to multicomponent systems.
|
||||
This model offers a reliable means of estimating the thermal conductivity of gas mixtures based
|
||||
on the properties of the pure components and their molar interactions.
|
||||
|
||||
The thermal conductivity of a gas mixture, denoted by \(\lambda_{\text{mix}}\), can expressed as
|
||||
shown in equation \ref{eq:lambda-mixture}.
|
||||
\begin{equation}\label{eq:lambda-mixture}\lambda_{ ext{mix}} = \sum_{i=1}^{n} \frac{x_i \lambda_i}{\sum_{j=1}^{n} x_j \Phi_{ij}}\end{equation}
|
||||
In this equation, \(x_i\) represents the molar fraction of component \(i\) within the mixture,
|
||||
while \(\lambda_i\) denotes the thermal conductivity of the pure substance \(i\). The denominator
|
||||
contains the interaction parameter \(\Phi_{ij}\), which describes the influence of component
|
||||
\(j\) on the transport properties of component \(i\).
|
||||
|
||||
The interaction parameter \(\Phi_{ij}\) is given by the relation shown in equation \ref{eq:interaction-parameter}.
|
||||
\begin{equation}\label{eq:interaction-parameter}\Phi_{ij} = \frac{1}{\sqrt{8}} \left(1 + \frac{M_i}{M_j} \right)^{-1/2} \left[ 1 + \left( \frac{\lambda_i}{\lambda_j} \right)^{1/2} \left( \frac{M_j}{M_i} \right)^{1/4} \right]^2\end{equation}
|
||||
Here, \(M_i\) and \(M_j\) are the molar masses of the components \(i\) and \(j\), respectively.
|
||||
Molar masses and thermal conductivity of the pure substances are listed in table \ref{table:gas-probs}.
|
||||
The structure of this expression illustrates the nonlinear dependence of the interaction term on
|
||||
both the molar mass ratio and the square root of the conductivity ratio of the involved species.
|
||||
|
||||
\begin{table}
|
||||
\centering
|
||||
\caption{Properties of some gases}
|
||||
\label{table:gas-probs}
|
||||
\begin{tabular}{lSS}
|
||||
\toprule
|
||||
\text{Gas} & \text{Molar mass in g/mol} & \text{Thermal conductivity in W/m/K} \\
|
||||
\midrule
|
||||
H2 & 2.016 & 0.1805 \\
|
||||
O2 & 32.00 & 0.0263 \\
|
||||
N2 & 28.02 & 0.0258 \\
|
||||
CO2 & 44.01 & 0.0166 \\
|
||||
CH4 & 16.04 & 0.0341 \\
|
||||
Ar & 39.95 & 0.0177 \\
|
||||
He & 4.0026 & 0.1513 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}This formulation acknowledges that the transport properties of a gas mixture are not a simple
|
||||
linear combination of the individual conductivities. Rather, they are governed by intermolecular
|
||||
interactions, which affect the energy exchange and diffusion behavior of each component. These
|
||||
interactions are particularly significant at elevated pressures or in cases where the gas components
|
||||
exhibit widely differing molecular masses or transport properties.
|
||||
|
||||
The equation proposed by Wassiljewa and refined by Mason and Saxena assumes that binary interactions
|
||||
dominate the behavior of the mixture, while higher-order (three-body or more) interactions are
|
||||
neglected. It also presumes that the gases approximate ideal behavior, although in practical
|
||||
applications, moderate deviations from ideality are tolerated without significant loss of accuracy.
|
||||
In figure \ref{fig:mixture} the resulting thermal conductivity of an H2/CO2-mixture is shown.
|
||||
|
||||
\begin{figure}
|
||||
\centering
|
||||
\begingroup%
|
||||
\makeatletter%
|
||||
\begin{pgfpicture}%
|
||||
\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{6.400000in}{4.800000in}}%
|
||||
\pgfusepath{use as bounding box, clip}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetmiterjoin%
|
||||
\definecolor{currentfill}{rgb}{1.000000,1.000000,1.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.000000pt}%
|
||||
\definecolor{currentstroke}{rgb}{1.000000,1.000000,1.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{6.400000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{6.400000in}{4.800000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{4.800000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathclose%
|
||||
\pgfusepath{fill}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetmiterjoin%
|
||||
\definecolor{currentfill}{rgb}{1.000000,1.000000,1.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.000000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetstrokeopacity{0.000000}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{0.800000in}{0.528000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.760000in}{0.528000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.760000in}{4.224000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.800000in}{4.224000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.800000in}{0.528000in}}%
|
||||
\pgfpathclose%
|
||||
\pgfusepath{fill}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{1.025455in}{0.528000in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=1.025455in,y=0.430778in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{1.927273in}{0.528000in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=1.927273in,y=0.430778in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}20}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{2.829091in}{0.528000in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=2.829091in,y=0.430778in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}40}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{3.730909in}{0.528000in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=3.730909in,y=0.430778in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}60}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{4.632727in}{0.528000in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=4.632727in,y=0.430778in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}80}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{0.000000in}{-0.048611in}}{\pgfqpoint{0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.000000in}{-0.048611in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{5.534545in}{0.528000in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=5.534545in,y=0.430778in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}100}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=3.280000in,y=0.240809in,,top]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}H2 molar fraction / %}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{0.868203in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=0.815441in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.025}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{1.380710in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=1.327949in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.050}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{1.893218in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=1.840456in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.075}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{2.405725in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=2.352964in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.100}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{2.918233in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=2.865472in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.125}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{3.430741in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=3.377979in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.150}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetbuttcap%
|
||||
\pgfsetroundjoin%
|
||||
\definecolor{currentfill}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetfillcolor{currentfill}%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfsys@defobject{currentmarker}{\pgfqpoint{-0.048611in}{0.000000in}}{\pgfqpoint{-0.000000in}{0.000000in}}{%
|
||||
\pgfpathmoveto{\pgfqpoint{-0.000000in}{0.000000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{-0.048611in}{0.000000in}}%
|
||||
\pgfusepath{stroke,fill}%
|
||||
}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsys@transformshift{0.800000in}{3.943248in}%
|
||||
\pgfsys@useobject{currentmarker}{}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.305168in, y=3.890487in, left, base]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}0.175}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\definecolor{textcolor}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{textcolor}%
|
||||
\pgfsetfillcolor{textcolor}%
|
||||
\pgftext[x=0.249612in,y=2.376000in,,bottom,rotate=90.000000]{\color{textcolor}{\sffamily\fontsize{10.000000}{12.000000}\selectfont\catcode`\^=\active\def^{\ifmmode\sp\else\^{}\fi}\catcode`\%=\active\def%{\%}Thermal Conductivity / (W/m·K)}}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfpathrectangle{\pgfqpoint{0.800000in}{0.528000in}}{\pgfqpoint{4.960000in}{3.696000in}}%
|
||||
\pgfusepath{clip}%
|
||||
\pgfsetrectcap%
|
||||
\pgfsetroundjoin%
|
||||
\pgfsetlinewidth{1.505625pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{1.025455in}{0.696000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.071001in}{0.697319in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.116547in}{0.698664in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.162094in}{0.700036in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.207640in}{0.701436in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.253186in}{0.702865in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.298733in}{0.704324in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.344279in}{0.705814in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.389826in}{0.707335in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.435372in}{0.708889in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.480918in}{0.710476in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.526465in}{0.712098in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.572011in}{0.713757in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.617557in}{0.715452in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.663104in}{0.717186in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.708650in}{0.718960in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.754197in}{0.720775in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.799743in}{0.722632in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.845289in}{0.724534in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.890836in}{0.726482in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.936382in}{0.728476in}}%
|
||||
\pgfpathlineto{\pgfqpoint{1.981928in}{0.730520in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.027475in}{0.732615in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.073021in}{0.734763in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.118567in}{0.736966in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.164114in}{0.739226in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.209660in}{0.741545in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.255207in}{0.743926in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.300753in}{0.746371in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.346299in}{0.748883in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.391846in}{0.751464in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.437392in}{0.754118in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.482938in}{0.756847in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.528485in}{0.759656in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.574031in}{0.762546in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.619578in}{0.765523in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.665124in}{0.768589in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.710670in}{0.771749in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.756217in}{0.775008in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.801763in}{0.778370in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.847309in}{0.781840in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.892856in}{0.785423in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.938402in}{0.789124in}}%
|
||||
\pgfpathlineto{\pgfqpoint{2.983949in}{0.792951in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.029495in}{0.796909in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.075041in}{0.801005in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.120588in}{0.805247in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.166134in}{0.809642in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.211680in}{0.814198in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.257227in}{0.818926in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.302773in}{0.823834in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.348320in}{0.828933in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.393866in}{0.834235in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.439412in}{0.839752in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.484959in}{0.845496in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.530505in}{0.851484in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.576051in}{0.857729in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.621598in}{0.864249in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.667144in}{0.871063in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.712691in}{0.878191in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.758237in}{0.885655in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.803783in}{0.893479in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.849330in}{0.901690in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.894876in}{0.910317in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.940422in}{0.919392in}}%
|
||||
\pgfpathlineto{\pgfqpoint{3.985969in}{0.928951in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.031515in}{0.939034in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.077062in}{0.949685in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.122608in}{0.960953in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.168154in}{0.972893in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.213701in}{0.985566in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.259247in}{0.999042in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.304793in}{1.013399in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.350340in}{1.028727in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.395886in}{1.045125in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.441433in}{1.062710in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.486979in}{1.081614in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.532525in}{1.101991in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.578072in}{1.124018in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.623618in}{1.147903in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.669164in}{1.173889in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.714711in}{1.202263in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.760257in}{1.233369in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.805803in}{1.267615in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.851350in}{1.305499in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.896896in}{1.347625in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.942443in}{1.394741in}}%
|
||||
\pgfpathlineto{\pgfqpoint{4.987989in}{1.447778in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.033535in}{1.507912in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.079082in}{1.576651in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.124628in}{1.655955in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.170174in}{1.748426in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.215721in}{1.857582in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.261267in}{1.988306in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.306814in}{2.147564in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.352360in}{2.345643in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.397906in}{2.598377in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.443453in}{2.931436in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.488999in}{3.389276in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.534545in}{4.056000in}}%
|
||||
\pgfusepath{stroke}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetrectcap%
|
||||
\pgfsetmiterjoin%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{0.800000in}{0.528000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{0.800000in}{4.224000in}}%
|
||||
\pgfusepath{stroke}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetrectcap%
|
||||
\pgfsetmiterjoin%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{5.760000in}{0.528000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.760000in}{4.224000in}}%
|
||||
\pgfusepath{stroke}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetrectcap%
|
||||
\pgfsetmiterjoin%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{0.800000in}{0.528000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.760000in}{0.528000in}}%
|
||||
\pgfusepath{stroke}%
|
||||
\end{pgfscope}%
|
||||
\begin{pgfscope}%
|
||||
\pgfsetrectcap%
|
||||
\pgfsetmiterjoin%
|
||||
\pgfsetlinewidth{0.803000pt}%
|
||||
\definecolor{currentstroke}{rgb}{0.000000,0.000000,0.000000}%
|
||||
\pgfsetstrokecolor{currentstroke}%
|
||||
\pgfsetdash{}{0pt}%
|
||||
\pgfpathmoveto{\pgfqpoint{0.800000in}{4.224000in}}%
|
||||
\pgfpathlineto{\pgfqpoint{5.760000in}{4.224000in}}%
|
||||
\pgfusepath{stroke}%
|
||||
\end{pgfscope}%
|
||||
\end{pgfpicture}%
|
||||
\makeatother%
|
||||
\endgroup%
|
||||
|
||||
\caption{Thermal Conductivity of H2/CO2 mixtures}
|
||||
\label{fig:mixture}
|
||||
\end{figure}In engineering practice, the accurate determination of \(\lambda_{\text{mix}}\) is essential
|
||||
for the prediction of heat transfer in systems such as membrane modules, chemical reactors, and
|
||||
combustion chambers. In the context of membrane-based gas separation, for instance, the thermal
|
||||
conductivity of the gas mixture influences the local temperature distribution, which in turn affects
|
||||
both the permeation behavior and the structural stability of the membrane.
|
||||
|
||||
It is important to note that the calculated mixture conductivity reflects only the gas phase
|
||||
behavior. In porous systems such as carbon membranes, additional effects must be considered.
|
||||
These include the solid-phase thermal conduction through the membrane matrix, radiative transport
|
||||
in pore channels at high temperatures, and transport in the Knudsen regime for narrow pores.
|
||||
To account for these complexities, models based on effective medium theory, such as those of
|
||||
Maxwell-Eucken or Bruggeman, are frequently employed. These models combine the conductivities of
|
||||
individual phases (gas and solid) with geometrical factors that reflect the morphology of the
|
||||
porous structure.
|
||||
|
||||
|
||||
|
||||
\noindent\rule[0.5ex]{\linewidth}{1pt}
|
||||
|
||||
Expanded by more or less sensible AI jabbering; based on: \href{https://doi.org/10.14279/depositonce-7390}{doi:10.14279/depositonce-7390}
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
|
@ -1,5 +0,0 @@
|
|||
<h1>Special caracters</h1>
|
||||
<p>Umlaute: ÖÄÜ öäü</p>
|
||||
<p>Other: ß, €, @, $, %, ~, µ</p>
|
||||
<p>Units: m³, cm²</p>
|
||||
<p>Controll characters: <, >, ", ', &, |, /, \</p>
|
|
@ -1,39 +0,0 @@
|
|||
<h1>Source Equations</h1>
|
||||
<ol>
|
||||
<li>$4(3x + 2) - 5(x - 1) = 3x + 14$</li>
|
||||
<li>$
|
||||
rac{2y + 5}{4} +
|
||||
rac{3y - 1}{2} = 5$</li>
|
||||
<li>$
|
||||
rac{5}{x + 2} +
|
||||
rac{2}{x - 2} = 3$</li>
|
||||
<li>$8(3b - 5) + 4(b + 2) = 60$</li>
|
||||
<li>$2c^2 - 3c - 5 = 0$</li>
|
||||
<li>$4(2d - 1) + 5(3d + 2) = 7d + 28$</li>
|
||||
<li>$q^2 + 6q + 9 = 16$</li>
|
||||
</ol>
|
||||
<h1>Result Equations</h1>
|
||||
<ol>
|
||||
<li>$x =
|
||||
rac{1}{4}$</li>
|
||||
<li>$y =
|
||||
rac{17}{8}$</li>
|
||||
<li>$z =
|
||||
rac{7}{3}$</li>
|
||||
<li>$x = 1$ or $x = -6$</li>
|
||||
<li>$a =
|
||||
rac{1}{3}$ or $a = 2$</li>
|
||||
<li>$x = -
|
||||
rac{2}{3}$ or $x = 3$</li>
|
||||
<li>$b =
|
||||
rac{23}{7}$</li>
|
||||
</ol>
|
||||
<h1>Step by Step</h1>
|
||||
<ol>
|
||||
<li>Distribute: $12x + 8 - 5x + 5 = 3x + 14$</li>
|
||||
<li>Combine like terms: $7x + 13 = 3x + 14$</li>
|
||||
<li>Subtract $3x$: $4x + 13 = 14$</li>
|
||||
<li>Subtract $13$: $4x = 1$</li>
|
||||
<li>Divide by $4$: $x =
|
||||
rac{1}{4}$</li>
|
||||
</ol>
|
|
@ -1,44 +0,0 @@
|
|||
<p>Below is an in-depth explanation of the AArch64 (ARM64)
|
||||
unconditional branch instruction—often simply called the
|
||||
“B” instruction—and how its 26‐bit immediate field (imm26)
|
||||
is laid out and later relocated during linking.</p>
|
||||
<hr></hr>
|
||||
<h2>Instruction Layout</h2>
|
||||
<p>The unconditional branch in AArch64 is encoded in a 32‑bit
|
||||
instruction. Its layout is as follows:</p>
|
||||
<pre><code>Bits: 31 26 25 0
|
||||
+-------------+------------------------------+
|
||||
| Opcode | imm26 |
|
||||
+-------------+------------------------------+
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><strong>Opcode (bits 31:26):</strong></li>
|
||||
<li>For a plain branch (<code>B</code>), the opcode is <code>000101</code>.</li>
|
||||
<li>
|
||||
<p>For a branch with link (<code>BL</code>), which saves the return
|
||||
address (i.e., a call), the opcode is <code>100101</code>.
|
||||
These 6 bits determine the instruction type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Immediate Field (imm26, bits 25:0):</strong></p>
|
||||
</li>
|
||||
<li>This 26‑bit field holds a signed immediate value.</li>
|
||||
<li>
|
||||
<p><strong>Offset Calculation:</strong> At runtime, the processor:</p>
|
||||
<ol>
|
||||
<li><strong>Shifts</strong> the 26‑bit immediate left by 2 bits.
|
||||
(Because instructions are 4-byte aligned,
|
||||
the two least-significant bits are always zero.)</li>
|
||||
<li><strong>Sign-extends</strong> the resulting 28‑bit value to
|
||||
the full register width (typically 64 bits).</li>
|
||||
<li><strong>Adds</strong> this value to the program counter
|
||||
(PC) to obtain the branch target.</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Reach:</strong></p>
|
||||
</li>
|
||||
<li>With a 26‑bit signed field that’s effectively 28 bits
|
||||
after the shift, the branch can cover a range
|
||||
of approximately ±128 MB from the current instruction.</li>
|
||||
</ul>
|
|
@ -1,77 +0,0 @@
|
|||
<h2>Klemmen</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: right;">Anz.</th>
|
||||
<th>Typ</th>
|
||||
<th>Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: right;">12</td>
|
||||
<td>BK9050</td>
|
||||
<td>Buskoppler</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">2</td>
|
||||
<td>KL1104</td>
|
||||
<td>4 Digitaleingänge</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">2</td>
|
||||
<td>KL2404</td>
|
||||
<td>4 Digitalausgänge (0,5 A)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">3</td>
|
||||
<td>KL2424</td>
|
||||
<td>4 Digitalausgänge (2 A)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">2</td>
|
||||
<td>KL4004</td>
|
||||
<td>4 Analogausgänge</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">1</td>
|
||||
<td>KL4002</td>
|
||||
<td>2 Analogausgänge</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">22</td>
|
||||
<td>KL9188</td>
|
||||
<td>Potenzialverteilungsklemme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">1</td>
|
||||
<td>KL9100</td>
|
||||
<td>Potenzialeinspeiseklemme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">3</td>
|
||||
<td>KL3054</td>
|
||||
<td>4 Analogeingänge</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">5</td>
|
||||
<td>KL3214</td>
|
||||
<td>PT100 4 Temperatureingänge (3-Leiter)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">3</td>
|
||||
<td>KL3202</td>
|
||||
<td>PT100 2 Temperatureingänge (3-Leiter)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">1</td>
|
||||
<td>KL2404</td>
|
||||
<td>4 Digitalausgänge</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: right;">2</td>
|
||||
<td>KL9010</td>
|
||||
<td>Endklemme</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -22,9 +22,7 @@ def test_latex_embedding2():
|
|||
contains the interaction parameter <latex>\Phi_{ij}</latex>, which describes the influence of component
|
||||
<latex>j</latex> on the transport properties of component <latex>i</latex>.
|
||||
|
||||
The interaction parameter <latex>\Phi_{ij}</latex> is given by the relation shown in @eq:ExampleFormula2.
|
||||
<latex type="block" ref_type="eq" ref_id="ExampleFormula2" caption="(1)">\Phi_{ij} = \frac{1}{\sqrt{8}} \left(1 + \frac{M_i}{M_j} \right)^{-1/2} \left[ 1 + \left( \frac{\lambda_i}{\lambda_j} \right)^{1/2} \left( \frac{M_j}{M_i} \right)^{1/4} \right]^2</latex>
|
||||
""")
|
||||
The interaction parameter <latex>\Phi_{ij}</latex> is given by the relation shown in @eq:ExampleFormula2.<latex type="block" reference="eq:ExampleFormula2" caption="(1)">\Phi_{ij} = \frac{1}{\sqrt{8}} \left(1 + \frac{M_i}{M_j} \right)^{-1/2} \left[ 1 + \left( \frac{\lambda_i}{\lambda_j} \right)^{1/2} \left( \frac{M_j}{M_i} \right)^{1/4} \right]^2</latex>""")
|
||||
|
||||
dummy = pyladoc.DocumentWriter()
|
||||
result_string = dummy._equation_embedding_reescaping(test_input)
|
||||
|
@ -44,9 +42,7 @@ def test_latex_embedding():
|
|||
""")
|
||||
|
||||
expected_output = pyladoc._normalize_text_indent(r"""
|
||||
# Test
|
||||
<latex type="block" ref_type="eq" ref_id="ExampleFormula2" caption="(1)">\Phi_{ij} = \frac{1}{\sqrt{8}}</latex>
|
||||
This <latex>i</latex> is inline LaTeX.
|
||||
# Test<latex type="block" reference="eq:ExampleFormula2" caption="(1)">\Phi_{ij} = \frac{1}{\sqrt{8}}</latex>This <latex>i</latex> is inline LaTeX.
|
||||
""")
|
||||
|
||||
dummy = pyladoc.DocumentWriter()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import pyladoc
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def test_readme_example():
|
||||
doc = pyladoc.DocumentWriter()
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import pyladoc
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
import document_validation
|
||||
from . import document_validation
|
||||
import numpy as np
|
||||
|
||||
VALIDATE_HTML_CODE_ONLINE = False
|
||||
VALIDATE_HTML_CODE_ONLINE = True
|
||||
WRITE_RESULT_FILES = True
|
||||
|
||||
|
||||
|
@ -158,7 +158,7 @@ def test_html_render():
|
|||
|
||||
if WRITE_RESULT_FILES:
|
||||
with open('tests/out/test_html_render1.html', 'w', encoding='utf-8') as f:
|
||||
f.write(pyladoc.inject_to_template(html_code, internal_template='templates/test_template.html'))
|
||||
f.write(pyladoc.inject_to_template({'CONTENT': html_code}, internal_template='templates/test_template.html'))
|
||||
|
||||
|
||||
def test_latex_render():
|
||||
|
@ -168,7 +168,9 @@ def test_latex_render():
|
|||
with open('tests/out/test_html_render1.tex', 'w', encoding='utf-8') as f:
|
||||
f.write(doc.to_latex())
|
||||
|
||||
assert doc.to_pdf('tests/out/test_latex_render1.pdf', font_family='serif')
|
||||
assert doc.to_pdf('tests/out/test_latex_render1.pdf', font_family='serif')
|
||||
else:
|
||||
assert doc.to_pdf('', font_family='serif') # Write only to temp folder
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pyladoc
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
import document_validation
|
||||
from . import document_validation
|
||||
|
||||
VALIDATE_HTML_CODE_ONLINE = False
|
||||
WRITE_RESULT_FILES = True
|
||||
|
@ -71,16 +71,23 @@ def make_document():
|
|||
mydataset = {
|
||||
'Row1': ["Line1", "Line2", "Line3", "Line4", "Line5"],
|
||||
'Row2': [120, '95 km/h', 110, '105 km/h', 130],
|
||||
'Row3': ['12 g/km', '> 150 g/km', '110 g/km', '1140 g/km', '13.05 g/km'],
|
||||
'Row3': ['12 g/km', '> 150 g/km', '-110 g/km', '1140 g/km', '\u221213.05 g/km'],
|
||||
'Row4': ['5 stars', '4 stars', '5 stars', '4.5 stars', '5 stars'],
|
||||
'Row5': [3.5, 7.8, 8.5, 6.9, 4.2],
|
||||
'Row6': ['1850 kg', '1500 kg', '1400 kg', '1600 kg', '1700 kg'],
|
||||
'Row7': ['600 Nm', '250 Nm', '280 Nm', '320 Nm', '450 Nm']
|
||||
'Row6': ['1850 kg', '150 kg', '140 kg', '1600 kg', '17.55 kg'],
|
||||
'Row7': ['600 Nm', '250 Nm', '280,8 Nm', '320 Nm', '450 Nm']
|
||||
}
|
||||
df = pd.DataFrame(mydataset)
|
||||
|
||||
doc.add_table(df.style.hide(axis="index"), 'This is a example table', 'example1')
|
||||
|
||||
doc.add_text("This is a fine print test text section. It uses smaller text and uses grey color. This is a fine print test"
|
||||
"text section. It uses smaller text and uses grey color.", section_class='fineprint')
|
||||
|
||||
doc.add_text("Standard text section. This is normal text without any special formatting. It uses the default text size and color.")
|
||||
|
||||
doc.add_markdown("This is a **fine print** test text section. It uses **smaller text** and uses **grey** color.", section_class='fineprint')
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
|
@ -92,15 +99,23 @@ def test_html_render():
|
|||
|
||||
if WRITE_RESULT_FILES:
|
||||
with open('tests/out/test_html_render2.html', 'w', encoding='utf-8') as f:
|
||||
f.write(pyladoc.inject_to_template(html_code, internal_template='templates/test_template.html'))
|
||||
f.write(pyladoc.inject_to_template({'CONTENT': html_code}, internal_template='templates/test_template.html'))
|
||||
|
||||
|
||||
def test_latex_render():
|
||||
doc = make_document()
|
||||
|
||||
# print(doc.to_latex())
|
||||
latex_code = doc.to_latex()
|
||||
|
||||
assert doc.to_pdf('tests/out/test_latex_render2.pdf', font_family='serif')
|
||||
assert r'\begin{tabular}{lSSSSSS}' in latex_code, "Table format not correct in LaTeX output"
|
||||
|
||||
if WRITE_RESULT_FILES:
|
||||
with open('tests/out/test_html_render2.tex', 'w', encoding='utf-8') as f:
|
||||
f.write(latex_code)
|
||||
|
||||
assert doc.to_pdf('tests/out/test_latex_render2.pdf', font_family='serif')
|
||||
else:
|
||||
assert doc.to_pdf('', font_family='serif') # Write only to temp folder
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import pyladoc
|
||||
import document_validation
|
||||
from . import document_validation
|
||||
|
||||
VALIDATE_HTML_CODE_ONLINE = False
|
||||
WRITE_RESULT_FILES = True
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
import pyladoc
|
||||
|
||||
|
||||
def test_update_svg_ids():
|
||||
test_str = r"""
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 15.0336
|
||||
L 24.570183 15.0336
|
||||
L 24.570183 0
|
||||
L 0 0
|
||||
z
|
||||
" style="fill: #ffffff"/>
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="text_1">
|
||||
<!-- $\lambda_{\text{mix}}$ -->
|
||||
<g transform="translate(3.042219 10.351343) scale(0.1 -0.1)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Oblique-3bb" d="M 2350 4316
|
||||
|
||||
|
||||
" clip-path="url(#p8dcad2f367)" style="fill: none; stroke: #000000; stroke-width: 1.5; stroke-linecap: square"/>
|
||||
|
||||
|
||||
<clipPath id="p8dcad2f367">
|
||||
<rect x="57.6" y="41.472" width="357.12" height="266.112"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
||||
<path id="DejaVuSans-Oblique-78" d="M 3841 3500
|
||||
L 2234 1784
|
||||
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Oblique-78" transform="translate(0 0.3125)"/>
|
||||
<use xlink:href="#DejaVuSans-Oblique-69" transform="translate(59.179688 -16.09375) scale(0.7)"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="24.570183pt" height="15.0336pt" viewBox="0 0 24.570183 15.0336" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
|
||||
<defs>
|
||||
<style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style>
|
||||
</defs>
|
||||
<g id="figure_1">
|
||||
<g id="patch_1">
|
||||
<path d="M 0 15.0336
|
||||
L 24.570183 15.0336
|
||||
L 24.570183 0
|
||||
L 0 0
|
||||
z
|
||||
" style="fill: #ffffff"/>
|
||||
</g>
|
||||
<g id="axes_1">
|
||||
<g id="text_1">
|
||||
<!-- $\lambda_{\text{mix}}$ -->
|
||||
<g transform="translate(3.042219 10.351343) scale(0.1 -0.1)">
|
||||
<defs>
|
||||
<path id="DejaVuSans-Oblique-3bb" d="M 2350 4316
|
||||
L 3125 0
|
||||
L 2516 0
|
||||
L 2038 2588
|
||||
L 328 0
|
||||
L -281 0
|
||||
L 1903 3356
|
||||
L 1794 3975
|
||||
Q 1725 4369 1391 4369
|
||||
L 1091 4369
|
||||
L 1184 4863
|
||||
L 1550 4856
|
||||
Q 2253 4847 2350 4316
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
<path id="DejaVuSans-6d" d="M 3328 2828
|
||||
Q 3544 3216 3844 3400
|
||||
Q 4144 3584 4550 3584
|
||||
Q 5097 3584 5394 3201
|
||||
Q 5691 2819 5691 2113
|
||||
L 5691 0
|
||||
L 5113 0
|
||||
L 5113 2094
|
||||
Q 5113 2597 4934 2840
|
||||
Q 4756 3084 4391 3084
|
||||
Q 3944 3084 3684 2787
|
||||
Q 3425 2491 3425 1978
|
||||
L 3425 0
|
||||
L 2847 0
|
||||
L 2847 2094
|
||||
Q 2847 2600 2669 2842
|
||||
Q 2491 3084 2119 3084
|
||||
Q 1678 3084 1418 2786
|
||||
Q 1159 2488 1159 1978
|
||||
L 1159 0
|
||||
L 581 0
|
||||
L 581 3500
|
||||
L 1159 3500
|
||||
L 1159 2956
|
||||
Q 1356 3278 1631 3431
|
||||
Q 1906 3584 2284 3584
|
||||
Q 2666 3584 2933 3390
|
||||
Q 3200 3197 3328 2828
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
<path id="DejaVuSans-69" d="M 603 3500
|
||||
L 1178 3500
|
||||
L 1178 0
|
||||
L 603 0
|
||||
L 603 3500
|
||||
z
|
||||
M 603 4863
|
||||
L 1178 4863
|
||||
L 1178 4134
|
||||
L 603 4134
|
||||
L 603 4863
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
<path id="DejaVuSans-78" d="M 3513 3500
|
||||
L 2247 1797
|
||||
L 3578 0
|
||||
L 2900 0
|
||||
L 1881 1375
|
||||
L 863 0
|
||||
L 184 0
|
||||
L 1544 1831
|
||||
L 300 3500
|
||||
L 978 3500
|
||||
L 1906 2253
|
||||
L 2834 3500
|
||||
L 3513 3500
|
||||
z
|
||||
" transform="scale(0.015625)"/>
|
||||
</defs>
|
||||
<use xlink:href="#DejaVuSans-Oblique-3bb" transform="translate(0 0.015625)"/>
|
||||
<use xlink:href="#DejaVuSans-6d" transform="translate(59.179688 -16.390625) scale(0.7)"/>
|
||||
<use xlink:href="#DejaVuSans-69" transform="translate(127.368164 -16.390625) scale(0.7)"/>
|
||||
<use xlink:href="#DejaVuSans-78" transform="translate(146.816406 -16.390625) scale(0.7)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
unique_id = 'xx-rgerergre-yy-trhsrthrst--xx'
|
||||
|
||||
result = pyladoc.svg_tools.update_svg_ids(test_str, unique_id)
|
||||
|
||||
print(result)
|
||||
|
||||
assert result.replace(f"svg-{unique_id}-", '') == test_str
|
|
@ -0,0 +1,86 @@
|
|||
import pyladoc
|
||||
|
||||
|
||||
def test_inject_to_template_html():
|
||||
template = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><!--TITLE--></title>
|
||||
</head>
|
||||
<!-- some comment -->
|
||||
<body>
|
||||
<!--CONTENT-->
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
content = "Hello, World!"
|
||||
title = "Test Title"
|
||||
|
||||
result = pyladoc.inject_to_template({'CONTENT': content, 'TITLE': title}, template_string=template)
|
||||
|
||||
print(result)
|
||||
|
||||
assert "Hello, World!" in result
|
||||
assert "<!-- some comment -->" in result # Keep unrelated HTML comments
|
||||
assert "<title>Test Title</title>" in result
|
||||
|
||||
|
||||
def test_inject_to_template_latex():
|
||||
template = """
|
||||
\\documentclass[a4paper,12pt]{article}
|
||||
|
||||
% Packages
|
||||
\\usepackage[utf8]{inputenc}
|
||||
\\usepackage[T1]{fontenc}
|
||||
\\usepackage{lmodern} % Load Latin Modern font
|
||||
\\usepackage{graphicx} % For including images
|
||||
\\usepackage{amsmath} % For mathematical symbols
|
||||
\\usepackage{amssymb} % For additional symbols
|
||||
\\usepackage{hyperref} % For hyperlinks
|
||||
\\usepackage{caption} % For customizing captions
|
||||
\\usepackage{geometry} % To set margins
|
||||
\\usepackage{natbib} % For citations
|
||||
\\usepackage{float} % For fixing figure positions
|
||||
\\usepackage{siunitx} % For scientific units
|
||||
\\usepackage{booktabs} % For professional-looking tables
|
||||
\\usepackage{pgf} % For using pgf grafics
|
||||
\\usepackage{textcomp, gensymb} % provides \\degree symbol
|
||||
|
||||
\\sisetup{
|
||||
table-align-text-post = false
|
||||
}
|
||||
|
||||
% Geometry Settings
|
||||
\\geometry{margin=1in} % 1-inch margins
|
||||
|
||||
% Title and Author Information
|
||||
\\title{<!--PROJECT-->}
|
||||
<!--AUTHOR-->
|
||||
\\date{\\today}
|
||||
|
||||
\begin{document}
|
||||
|
||||
% Title Page
|
||||
\\maketitle
|
||||
|
||||
% <!--CONTENT-->
|
||||
\\end{document}
|
||||
"""
|
||||
|
||||
content = "Hello, World!"
|
||||
project_name = "Test Project"
|
||||
author_name = "Otto"
|
||||
|
||||
result = pyladoc.inject_to_template(
|
||||
{'CONTENT': content, 'PROJECT': project_name, 'AUTHOR': author_name},
|
||||
template_string=template)
|
||||
|
||||
print(result)
|
||||
|
||||
assert "\nOtto\n" in result
|
||||
assert "\\title{Test Project}\n" in result
|
||||
assert "Hello, World!" in result
|
Loading…
Reference in New Issue