diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3d6dbe7..e6e82bb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -27,12 +27,25 @@ jobs: # 3. Install Sphinx and dependencies - name: Install Sphinx and dependencies - run: pip install sphinx sphinx-rtd-theme + run: pip install sphinx sphinx-rtd-theme cairosvg pillow # 4. Build Sphinx documentation - name: Generate Sphinx docs run: sphinx-build -b html docs docs/_build/html + # 4b. Install LaTeX and build the PDF (Yubico-style, pdflatex) + - name: Install LaTeX for PDF + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + latexmk texlive-latex-recommended texlive-latex-extra \ + texlive-fonts-recommended texlive-fonts-extra tex-gyre + + - name: Build PDF (pdflatex) + run: | + sphinx-build -M latexpdf docs docs/_build + cp docs/_build/latex/cryptnox-cli.pdf docs/_build/html/cryptnox-cli.pdf + # 5. Deploy HTML documentation to GitHub Pages - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 diff --git a/.gitignore b/.gitignore index 8909dcb..29331b5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ signature.sig cardpub.pub docs/_build/ *.lock +docs/_static/cryptnox-logo-dark.png diff --git a/dev-requirements.txt b/dev-requirements.txt index ec683d3..d511bba 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -22,6 +22,8 @@ pywin32-ctypes==0.2.3; sys_platform == 'win32' Sphinx>=7.3.7 sphinx-rtd-theme>=2.0.0 sphinx-autodoc-typehints>=2.1.0 +cairosvg>=2.7.0 +Pillow>=10.0.0 setuptools==78.1.1; python_version >= '3.9' tomlkit==0.13.2; python_version >= '3.8' typing-extensions==4.13.2; python_version >= '3.8' diff --git a/docs/_static/cryptnox-logo.png b/docs/_static/cryptnox-logo.png deleted file mode 100644 index b5d3148..0000000 Binary files a/docs/_static/cryptnox-logo.png and /dev/null differ diff --git a/docs/_static/cryptnox-logo.svg b/docs/_static/cryptnox-logo.svg new file mode 100644 index 0000000..9fed9ce --- /dev/null +++ b/docs/_static/cryptnox-logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/docs/conf.py b/docs/conf.py index 55811e9..143b696 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,6 +11,28 @@ sys.path.insert(0, os.path.abspath("..")) +# Derive the navy PDF cover logo from the white HTML SVG at build time, so only the +# SVG is a committed source asset (needs cairosvg + Pillow, both in the docs requirements). +try: + import io as _io + import cairosvg + from PIL import Image as _Image +except ImportError as _e: + raise RuntimeError( + "Docs build requires cairosvg and Pillow to generate the PDF cover logo; " + "install them (pip install cairosvg pillow)." + ) from _e +_static = os.path.join(os.path.dirname(__file__), "_static") +with open(os.path.join(_static, "cryptnox-logo.svg"), encoding="utf-8") as _f: + _svg = _f.read() +_png = cairosvg.svg2png( + bytestring=_svg.replace('fill="white"', 'fill="#101f2e"').encode(), + output_width=1200, output_height=226, +) +_Image.open(_io.BytesIO(_png)).save( + os.path.join(_static, "cryptnox-logo-dark.png"), dpi=(400, 400) +) + project = 'cryptnox-cli' copyright = '2025, Cryptnox' author = 'Cryptnox' @@ -29,6 +51,11 @@ # Disable autosummary generation to prevent hangs autosummary_generate = False +# Show default args as written in source (e.g. wordlist=wordlist_english) instead +# of expanding them — the mnemonic module defaults to the full 2048-word BIP39 list, +# which otherwise floods signatures and forces near-blank pages in the PDF. +autodoc_preserve_defaults = True + templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] @@ -115,7 +142,7 @@ html_show_sphinx = False # Logo configuration -html_logo = "_static/cryptnox-logo.png" +html_logo = "_static/cryptnox-logo.svg" # Custom CSS and JS html_css_files = [ @@ -141,3 +168,101 @@ 'includehidden': True, 'titles_only': False } + +# -- Options for PDF (LaTeX) output ------------------------------------------ +# Built by CI with pdflatex, same as Yubico's tech manual. Output: cryptnox-cli.pdf + +today = 'June 20, 2026' # fixed doc date on the cover + +latex_engine = 'pdflatex' +latex_logo = '_static/cryptnox-logo-dark.png' # white logo is invisible on white PDF title page +latex_domain_indices = False # no Python Module Index in the PDF (kept in HTML) +latex_documents = [ + ('index', 'cryptnox-cli.tex', 'Cryptnox CLI Manual', author, 'manual'), +] +latex_elements = { + 'papersize': 'a4paper', + 'pointsize': '11pt', + 'figure_align': 'H', + 'sphinxsetup': 'pre_border-radius=0pt', # sharp rectangle corners on code-block (CLI command) frames + 'extraclassoptions': 'oneside,openany', # no blank filler pages (web PDF) + 'printindex': '', # drop the general Index from the PDF (kept in HTML) + 'fncychap': '', # no fancy chapter rules; titlesec styles chapters instead + 'preamble': r''' +% pdflatex can't render colour emoji; map the ones used in the docs to safe glyphs +\usepackage{amssymb} +\DeclareUnicodeCharacter{1F4B3}{}% credit card -> drop +\DeclareUnicodeCharacter{FE0F}{}% variation selector -> drop +\DeclareUnicodeCharacter{26A0}{\textbf{!}}% warning sign +\DeclareUnicodeCharacter{2705}{\ensuremath{\checkmark}}% check mark +% CLI commands / literals (\ttfamily) in Inconsolata +\usepackage{inconsolata} +% Left-align body text (ragged right instead of justified) +\usepackage[document]{ragged2e} +% Drop the "(continues on next page)" / "(continued from previous page)" labels (parens included) on code blocks +\AtBeginDocument{\renewcommand*\sphinxstylecodecontinued[1]{}\renewcommand*\sphinxstylecodecontinues[1]{}} +% Whole document in the sans font (TeX Gyre Heros) +\renewcommand{\familydefault}{\sfdefault} +% Sans-serif TOC entries +\AtBeginDocument{\addtocontents{toc}{\protect\sffamily}} +% Left-aligned chapter headings +\usepackage{titlesec} +\titleformat{\chapter}[hang]{\sffamily\bfseries\huge}{\thechapter}{1em}{} +\titlespacing*{\chapter}{0pt}{0pt}{20pt} +% Centered page header (manual title, no release) + copyright footer +\usepackage{fancyhdr} +\def\headruleskip{4pt}\def\footruleskip{4pt}% gap between header/footer text and rule +\AtBeginDocument{% + \fancypagestyle{normal}{% + \fancyhf{}% + \fancyhead[C]{\sffamily\nouppercase{Cryptnox CLI Manual}}% + \fancyfoot[L]{\sffamily\copyright{} 2026 Cryptnox SA}% + \fancyfoot[R]{\sffamily\thepage}% + \renewcommand{\headrulewidth}{0.4pt}% + \renewcommand{\footrulewidth}{0.4pt}% + }% + \fancypagestyle{plain}{% + \fancyhf{}% + \fancyfoot[L]{\sffamily\copyright{} 2026 Cryptnox SA}% + \fancyfoot[R]{\sffamily\thepage}% + \renewcommand{\headrulewidth}{0pt}% + \renewcommand{\footrulewidth}{0.4pt}% + }% + \pagestyle{normal}% +} +% Centered title page (default is right-aligned); author line removed (logo brands it) +\makeatletter +\renewcommand{\sphinxmaketitle}{% + \let\sphinxrestorepageanchorsetting\relax + \ifHy@pageanchor\def\sphinxrestorepageanchorsetting{\Hy@pageanchortrue}\fi + \hypersetup{pageanchor=false}% + \begin{titlepage}% + \let\footnotesize\small \let\footnoterule\relax + \begingroup + \def\endgraf{ }\def\and{\& }% + \pdfstringdefDisableCommands{\def\\{, }}% + \hypersetup{pdfauthor={\@author}, pdftitle={\@title}}% + \endgroup + \noindent\rule{\textwidth}{1pt}\par + \begin{flushright}% + \vskip 1em% + \includegraphics[width=7cm]{cryptnox-logo-dark}\par + \vskip 2em% + {\LARGE\py@HeaderFamily \@title \par}% + \vskip 0.5em% + {\large\itshape \py@release\releaseinfo \par}% + \vfill + {\large \@date \par}% + \end{flushright}% + \@thanks + \end{titlepage}% + \setcounter{footnote}{0}% + \let\thanks\relax\let\maketitle\relax + \clearpage + \ifdefined\sphinxbackoftitlepage\sphinxbackoftitlepage\fi + \if@openright\cleardoublepage\else\clearpage\fi + \sphinxrestorepageanchorsetting +} +\makeatother +''', +} diff --git a/docs/index.rst b/docs/index.rst index fc19abd..f27d032 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,11 @@ Cryptnox CLI Documentation ========================== -Welcome to the technical documentation of Cryptnox CLI. +.. only:: html + + Welcome to the technical documentation of Cryptnox CLI. + + 📄 `Download this documentation as PDF `__ .. toctree:: :maxdepth: 2