Skip to content

Commit

Permalink
adi_doctools: Implemented PDF export method
Browse files Browse the repository at this point in the history
- This method converts directly to PDF using the .rst files
(rst2pdf method)
- Build by running 'sphinx-build -b pdf' in the docs folder
and choose the output folder
- New parameter called ADOC_MEDIA_PRINT; This parameters is used to
signal the PDF export mode and can be either 1 or 0
- Tables export as they should now (wihtout empty columns)
- Collapsibles are converted into tables
- Dropdowns which contain tables like the ones found in
'Generic AXI ADC' && 'Generic AXI DAC' wouldn't render
properly, but now they are fixed
- Fixed larger tables, usually appear under 'Configuration Parameters'
section, which contain a larger number of columns
- Added a new section called 'Exporting to PDF' where the PDF
export mode is explained
- Added in requirments.txt the three libraries needed to build
the PDF documentation
- Fixed conflicts

Signed-off-by: Cristian Mihai Popa <cristianmihai.popa@analog.com>
  • Loading branch information
cristianmihaipopa committed Sep 17, 2024
1 parent 09d2d4d commit b2f8caf
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 35 deletions.
12 changes: 11 additions & 1 deletion adi_doctools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,22 @@ def config_inited(app, config):
# Inject version value, config entry has higher precedence
if 'version' not in config:
doc_version = getenv("ADOC_DOC_VERSION", default="")
doc_version = getenv("ADOC_DOC_VERSION", default="")

# parameter used for PDF output mode
media_print = getenv("ADOC_MEDIA_PRINT", default=0)
media_print = True if media_print == '1' else False
config.media_print = media_print

if 'version' not in config or config.version == "":
try:
doc_version = str(Version(doc_version))
except Exception as err:
pass
config.version = doc_version

elif doc_version != "":
logger.warning("ADOC_DOC_VERSION set but ignored due to "
"conf.py version entry")

def builder_inited(app):
if app.builder.format == 'html':
Expand Down
14 changes: 10 additions & 4 deletions adi_doctools/directive/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,25 +101,32 @@ def column_entries(self, rows, items, uid: Optional[str]=None):
uid=uid)
rows.append(row)

def generic_table(self, description, uid: Optional[str]=None):
def generic_table(self, description, uid: Optional[str]=None, media_print=False):
tgroup = nodes.tgroup(cols=2)
for _ in range(2):
colspec = nodes.colspec(colwidth=1)
tgroup.append(colspec)
table = nodes.table()
table += tgroup

# example: self.table_header(tgroup, ["Bear with me", "Description"])
self.table_header(tgroup, ["Name", "Description"])

rows = []
for key in description:
row = nodes.row()

entry = nodes.entry()
entry += nodes.literal(text="{:s}".format(key))
if not media_print: # Check if not in PDF mode
entry += nodes.literal(text="{:s}".format(key))
else:
entry += nodes.paragraph(text="{:s}".format(key)) # Use paragraph for PDF mode
row += entry

entry = nodes.entry()
entry += parse_rst(self.state, description[key], uid=uid)
row += entry

rows.append(row)

tbody = nodes.tbody()
Expand Down Expand Up @@ -154,8 +161,7 @@ def table_header(tgroup, columns):

thead.append(row)

def collapsible(self, section,
text: [str, Tuple[str, str]] = "", node=None):
def collapsible(self, section, text: [str, Tuple[str, str]] = "", node=None):
"""
Creates a collapsible content.
text: When a tuple, the first string is a unique id, useful when the
Expand Down
78 changes: 49 additions & 29 deletions adi_doctools/directive/hdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import re
from os import path, walk
from os import pardir, makedirs
from typing import Final
from math import ceil
from lxml import etree

Expand All @@ -17,6 +18,7 @@
from ..parser.hdl import parse_hdl_build_status
from ..writer.hdl_component import hdl_component


log = {
'signal': "{lib}/component.xml: Signal {signal} defined in the hdl-interfaces directive does not exist in the IP-XACT!", # noqa: E501
'path': "Inconsistent paths, {cwd} not in {doc}",
Expand All @@ -39,11 +41,11 @@ def pretty_dep(string, parent):
else:
return string.replace("'MODELPARAM_VALUE.", '').replace("'", '')

def tables(self, subnode, content, component, lib_name):
def tables(self, subnode, content, component, lib_name, media_print = False):
description = self.get_descriptions(content)

if component is None:
subnode += self.generic_table(description)
subnode += self.generic_table(description, media_print = media_print)
return subnode

bs = component['bus_interface']
Expand Down Expand Up @@ -79,14 +81,17 @@ def tables(self, subnode, content, component, lib_name):

self.table_header(tgroup, ["Physical Port", "Logical Port", "Direction", "Dependency"]) # noqa: E501

# change type of tables entries when outputing to PDF
literal_ = 'literal' if not media_print else 'paragraph'

rows = []
pm = bs[tag]['port_map']
for key in pm:
self.column_entries(rows, [
[key, 'literal'],
[pm[key]['logical_port'], 'literal'],
[key, literal_ ],
[pm[key]['logical_port'], literal_],
[pm[key]['direction'], 'paragraph'],
[self.pretty_dep(pm[key]['dependency'], key), 'literal'],
[self.pretty_dep(pm[key]['dependency'], key), literal_],
])

tbody = nodes.tbody()
Expand All @@ -108,15 +113,18 @@ def tables(self, subnode, content, component, lib_name):

self.table_header(tgroup, ["Physical Port", "Direction", "Dependency", "Description"]) # noqa: E501

# change type of tables entries when outputing to PDF
literal_ = 'literal' if not media_print else 'paragraph'

rows = []
pr = component['ports']
dm = component['bus_domain']
for key in pr:
row = nodes.row()
self.column_entry(row, key, 'literal')
self.column_entry(row, key, literal_)
self.column_entry(row, pr[key]['direction'], 'paragraph')
self.column_entry(row, self.pretty_dep(pr[key]['dependency'], key),
'literal')
self.column_entry(row, self.pretty_dep(pr[key]['dependency'], key), literal_)

if 'clk' in key or 'clock' in key:
domain = 'clock domain'
elif 'reset':
Expand Down Expand Up @@ -154,6 +162,8 @@ def tables(self, subnode, content, component, lib_name):
def run(self):
env = self.state.document.settings.env

media_print = env.config.media_print

node = node_div()

if 'path' in self.options:
Expand All @@ -163,9 +173,9 @@ def run(self):

discover_hdl_component(env, lib_name)
if lib_name in env.component:
self.tables(node, self.content, env.component[lib_name], lib_name)
self.tables(node, self.content, env.component[lib_name], lib_name, media_print)
else:
self.tables(node, self.content, None, lib_name)
self.tables(node, self.content, None, lib_name, media_print)

return [node]

Expand All @@ -187,7 +197,7 @@ def get_hex_addr(addr: int, addr_incr: int):

return (dword, byte)

def tables(self, subnode, obj, key):
def tables(self, subnode, obj, key, media_print = False):
uid = "hdl-regmap-" + key
section = nodes.section(ids=[uid])

Expand All @@ -202,14 +212,17 @@ def tables(self, subnode, obj, key):
self.table_header(tgroup, ["DWORD", "BYTE", ["Reg Name", 3], "Description"]) # noqa: E501
self.table_header(tgroup, [["", 1], "BITS", "Field Name", "Type", "Default Value", "Description"]) # noqa: E501

# change type of tables entries when outputing to PDF
literal_ = 'literal' if not media_print else 'paragraph'

rows = []
for reg in obj['regmap']:
dword, byte = self.get_hex_addr(reg['address'], reg['addr_incr'])
self.column_entries(rows, [
[dword, 'literal', ['bold']],
[byte, 'literal', ['bold']],
[reg['name'], 'literal', ['bold'], 3],
[reg['description'], 'reST', ['description', 'bold']],
[dword, literal_, ['bold']],
[byte, literal_, ['bold']],
[reg['name'], literal_, ['bold'], 3],
[reg['description'], 'reST' if not media_print else 'paragraph', ['description', 'bold']],
], uid=uid)

for field in reg['fields']:
Expand All @@ -234,12 +247,12 @@ def tables(self, subnode, obj, key):
bits = f"{bits[0]}:{bits[1]}"

self.column_entries(rows, [
["", 'literal', [''], 1],
[f"[{bits}]", 'literal'],
[field['name'], 'literal'],
[field['rw'], 'literal'],
[default, 'literal', ['default']],
[field['description'], 'reST', ['description']],
["", literal_, [''], 1],
[f"[{bits}]", literal_],
[field['name'], literal_],
[field['rw'], literal_],
[default, 'default_value' if not media_print else 'paragraph', ['default']],
[field['description'], 'reST' if not media_print else 'paragraph', ['description']],
], uid=uid)

tbody = nodes.tbody()
Expand Down Expand Up @@ -280,6 +293,8 @@ def run(self):
env = self.state.document.settings.env
owner = env.docname

media_print = env.config.media_print

node = node_div()

if 'name' in self.options:
Expand All @@ -303,7 +318,7 @@ def run(self):

if owner not in env.regmaps[f]['owners']:
env.regmaps[f]['owners'].append(owner)
self.tables(subnode, env.regmaps[f]['subregmap'][lib_name], lib_name)
self.tables(subnode, env.regmaps[f]['subregmap'][lib_name], lib_name, media_print)

node += subnode
return [node]
Expand All @@ -320,11 +335,11 @@ def dot_fix(self, string):
else:
return string

def tables(self, content, parameter, lib_name):
def tables(self, content, parameter, lib_name, media_print = False):
description = self.get_descriptions(content)

if parameter is None:
return self.generic_table(description)
return self.generic_table(description, media_print = media_print)

tgroup = nodes.tgroup(cols=5)
for _ in range(5):
Expand All @@ -334,11 +349,14 @@ def tables(self, content, parameter, lib_name):
table += tgroup

self.table_header(tgroup, ["Name", "Description", "Default Value", "Choices/Range"]) # noqa: E501

# change type of tables entries when outputing to PDF
literal_ = 'literal' if not media_print else 'paragraph'

rows = []
for key in parameter:
row = nodes.row()
self.column_entry(row, "{:s}".format(key), 'literal')
self.column_entry(row, "{:s}".format(key), literal_)
if key in description:
self.column_entry(row, description[key],
'reST', classes=['description'])
Expand All @@ -348,7 +366,7 @@ def tables(self, content, parameter, lib_name):
classes=['description'])
for tag, ty in zip(['default'], ['literal']):
if parameter[key][tag] is not None:
self.column_entry(row, parameter[key][tag], ty,
self.column_entry(row, parameter[key][tag], ty if not media_print else 'paragraph',
classes=[tag])
else:
logger.warning(f"Got empty {tag} at parameter {key}!")
Expand All @@ -375,7 +393,9 @@ def tables(self, content, parameter, lib_name):
def run(self):
env = self.state.document.settings.env

node = node_div()
media_print = env.config.media_print

node = node_div()

if 'path' not in self.options:
self.options['path'] = env.docname.replace('/index', '')
Expand All @@ -386,9 +406,9 @@ def run(self):
if lib_name in env.component:
subnode += self.tables(self.content,
env.component[lib_name]['parameters'],
lib_name)
lib_name, media_print)
else:
subnode += self.tables(self.content, None, lib_name)
subnode += self.tables(self.content, None, lib_name, media_print)

node += subnode

Expand Down
10 changes: 9 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
project = 'Doctools'
copyright = '2024, Analog Devices, Inc.'
author = 'Analog Devices, Inc.'
version = '0.3'

locale_dirs = ['locales/'] # path is relative to the source directory
language = 'en'

# -- General configuration ---------------------------------------------------

extensions = [
"adi_doctools",
'adi_doctools',
'rst2pdf.pdfbuilder'
]

needs_extensions = {
Expand All @@ -24,6 +29,9 @@

export_metadata = True

# -- Options for PDF output --------------------------------------------------
# draft comment, future options for exporting to PDF

# -- Options for HTML output --------------------------------------------------

html_theme = 'cosmic'
Expand Down
23 changes: 23 additions & 0 deletions docs/docs_guidelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,29 @@ if the target directory is:
* *./prs/1234*: ``2``
* *./staging/user/branch*: ``3``

Exporting to PDF
--------------------------------------------------------------------------------

The whole documentation can be exported to a PDF document for a more compact
format. This is done by setting the enviroment variable called
``ADOC_MEDIA_PRINT`` to 1 (default it's 0) and building the documentation using
this command:

.. code-block::
user@analog:~/doctools/docs$ sphinx-build -b pdf . _build/pdfbuild
In the output folder, you’ll find a PDF document named after the repository
(e.g. Doctools.pdf). This document includes an auto-generated cover, followed by
the remaining pages. Note that an HTML build of the documentation is not
required for the PDF build.

.. warning::

The enviroment variable ``ADOC_MEDIA_PRINT`` should be set to 0 when building
the HTML pages of documentation. If not set, some components of the pages
may not render properly.

References
--------------------------------------------------------------------------------

Expand Down
3 changes: 3 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
sphinx
matplotlib
rst2pdf
svglib
https://github.com/analogdevicesinc/doctools/releases/download/latest/adi-doctools.tar.gz

0 comments on commit b2f8caf

Please sign in to comment.