Skip to content

Instantly share code, notes, and snippets.

@tk0miya
Last active October 20, 2015 15:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tk0miya/e36dfcff629cd34a4f58 to your computer and use it in GitHub Desktop.
Save tk0miya/e36dfcff629cd34a4f58 to your computer and use it in GitHub Desktop.
new_numfig.py
new_numfig.py: yet another numfig.py
-------------------------------------
This is Sphinx extension for numbering figures and tables automatically.
what's difference?
-------------------
- Support numbering tables
- Rename :num: role to :ref:num: and support HTML format
- Numbering over multiple .rst files (in HTML)
- Support branch numbering (cf. 1-1, 1-2, 2-1) with numbered toctree (in HTML)
- Add extensivility for third party extensions
Note
-----
this is implementation for experimentation (I'll send feature request to sphinx itself).
it does not handle any errors.
it's absolutely no warranty
LICENSE
--------
Apache 2.0
from docutils import nodes
from collections import defaultdict
from sphinx.roles import XRefRole
from sphinx.ext.graphviz import graphviz
counter = defaultdict(int)
class num_ref(nodes.reference):
pass
def latex_visit_num_ref(self, node):
domain = self.builder.env.domains['std']
target_docname, labelid = domain.data['anonlabels'].get(node['reftarget'])
self.body.append('\\ref{%s:%s}' % (target_docname, labelid))
raise nodes.SkipNode
def default_caption_updater(node, prefix, secnum, fignum):
for caption in node.traverse(nodes.caption):
if secnum:
caption[0] = nodes.Text("%s%s-%s: %s" % (prefix, secnum, fignum, caption[0]))
else:
caption[0] = nodes.Text("%s%s: %s" % (prefix, fignum, caption[0]))
return True
def graphviz_caption_updater(node, prefix, secnum, fignum):
if secnum:
node['caption'] = "%s%s-%d: %s" % (prefix, secnum, fignum, node['caption'])
else:
node['caption'] = "%s%d: %s" % (prefix, fignum, node['caption'])
return True
def table_title_updater(node, prefix, secnum, fignum):
if isinstance(node[0], nodes.title):
if secnum:
node[0][0] = nodes.Text("%s%s-%d: %s" % (prefix, secnum, fignum, node[0][0]))
else:
node[0][0] = nodes.Text("%s%d: %s" % (prefix, fignum, node[0][0]))
return True
else:
return False
def html_visit_num_ref(self, node):
builder = self.builder
domain = builder.env.domains['std']
node['refexplicit'] = True
xref = domain.resolve_xref(builder.env, builder.current_docname, builder,
'ref', node['reftarget'], node, node)
if xref is None:
builder.warn('Unknown label: %s' % node['reftarget'])
node.parent.remove(node)
else:
target_docname, labelid = domain.data['anonlabels'].get(node['reftarget'])
fignum = builder.env.doc_fignum[target_docname]
secnumbers = builder.env.toc_secnumbers.get(target_docname)
if secnumbers:
secnum = secnumbers[''][0]
else:
secnum = ''
target_doctree = builder.env.get_doctree(target_docname)
assign_fignumbers(builder, target_doctree, builder.current_docname, secnum, fignum)
for target in target_doctree.traverse(nodes.Element):
if node['reftarget'] in target['names']:
while 'fignum' not in target:
target = target.parent
xref[0] = nodes.emphasis(text=target['fignum'])
break
else:
builder.warn('Could not resolve fignumber: %s' % node['reftarget'])
xref[0] = nodes.emphasis(text='??')
node.replace_self(xref)
xref.walkabout(self)
raise nodes.SkipNode
def assign_fignumbers(app, doctree, docname, secnum, fignum):
config = app.env.config
for node in doctree.traverse(nodes.Node):
if node.__class__ in config.number_caption_rules:
type, caption_updater = config.number_caption_rules[node.__class__]
prefix = config.numbered_caption_prefixes[type]
if caption_updater is None:
caption_updater = default_caption_updater
if caption_updater(node, prefix, secnum, fignum):
if secnum:
node['fignum'] = "%s-%d" % (secnum, fignum)
else:
node['fignum'] = fignum
fignum += 1
return fignum
def on_doctree_resolved(app, doctree, docname):
if app.builder.name == 'latex':
return
secnumbers = app.env.toc_secnumbers.get(docname)
if secnumbers:
secnum = secnumbers[''][0]
else:
secnum = ''
if secnum in app.env.fignum:
fignum = app.env.fignum[secnum]
else:
fignum = app.env.fignum[secnum] = 1
app.env.doc_fignum[docname] = fignum
next_fignum = assign_fignumbers(app, doctree, docname, secnum, fignum)
app.env.fignum[fignum] = next_fignum
def on_builder_inited(app):
app.builder.env.fignum = {}
app.builder.env.doc_fignum = {}
def setup(app):
app.add_node(num_ref,
html=(html_visit_num_ref, None),
latex=(latex_visit_num_ref, None))
app.add_role('ref:num', XRefRole(nodeclass=num_ref))
app.connect("builder-inited", on_builder_inited)
app.connect('doctree-resolved', on_doctree_resolved)
# config values
number_caption_rules = {nodes.figure: ('image', None),
graphviz: ('image', graphviz_caption_updater),
nodes.table: ('table', table_title_updater)}
numbered_caption_prefixes = {'image': 'Fig.',
'table': 'Table '}
app.add_config_value('number_caption_rules', number_caption_rules, 'env')
app.add_config_value('numbered_caption_prefixes', numbered_caption_prefixes, 'env')
@mickours
Copy link

Thanks, it works great!

One thing that you should explain is that the reference no longer takes the # before the label:
like this :ref:num:my-fig-ref``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment