Skip to content

Commit

Permalink
Citation definitions in body (#271, fixes #269)
Browse files Browse the repository at this point in the history
- Citations can be displayed in body of document
- Style citations independent of footnotes
  • Loading branch information
alexfargus committed Jul 6, 2021
1 parent 21b20fb commit bc5aefa
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 24 deletions.
20 changes: 15 additions & 5 deletions src/rinoh/data/stylesheets/sphinx.rts
Original file line number Diff line number Diff line change
Expand Up @@ -606,11 +606,6 @@ base=linked reference
position=SUPERSCRIPT
number_format=number

[citation marker]
label_prefix='['
label_suffix=']'
custom_label=True

[footnote paragraph]
base=default
font_size=8pt
Expand All @@ -621,6 +616,21 @@ line_spacing=Fixed(10pt)
base=footnote paragraph
text_align=RIGHT

[citation]
base=header_footer
as_footnote=True

[citation marker]
label_prefix='['
label_suffix=']'
custom_label=True

[citation paragraph]
base=footnote paragraph

[citation label]
base=citation paragraph
text_align=RIGHT

[image]
space_above=10pt
Expand Down
19 changes: 15 additions & 4 deletions src/rinoh/frontend/rst/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@


import re

from datetime import datetime

import rinoh as rt
Expand Down Expand Up @@ -419,6 +418,8 @@ def build_flowable(self):


class Footnote(DocutilsBodyNode):
style = 'footnote'

def flowables(self):
note, = super().flowables()
yield rt.RegisterNote(note)
Expand All @@ -441,13 +442,23 @@ def build_styled_text(self):
style=self.style)


class Citation(Footnote):
pass
class Citation(DocutilsBodyNode):
style = 'citation'

def build_flowable(self):
return rt.CitationNote(
rt.StaticGroupedFlowables(self.children_flowables(1)),
style=self.style)

class Citation_Reference(Footnote_Reference):

class Citation_Reference(DocutilsInlineNode):
style = 'citation'

def build_styled_text(self):
return rt.CitationMarker(self['refid'],
custom_label=self.process_content(),
style=self.style)


class Substitution_Definition(DocutilsBodyNode):
def build_flowable(self):
Expand Down
60 changes: 48 additions & 12 deletions src/rinoh/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,27 @@


import re

from copy import copy
from itertools import chain, zip_longest

from .annotation import NamedDestinationLink
from .attribute import Attribute, Bool, OptionSet, OverrideDefault
from .flowable import Flowable, LabeledFlowable, DummyFlowable
from .flowable import (Flowable, LabeledFlowable, DummyFlowable,
LabeledFlowableStyle)
from .layout import ContainerOverflow
from .number import NumberStyle, Label, format_number
from .paragraph import Paragraph, ParagraphStyle, ParagraphBase
from .strings import StringCollection, StringField
from .style import HasClass, HasClasses, Styled
from .text import (InlineStyled, StyledText, TextStyle, SingleStyledText,
from .text import (StyledText, TextStyle, SingleStyledText,
MixedStyledTextBase, MixedStyledText, ErrorText)
from .util import NotImplementedAttribute


__all__ = ['Reference', 'ReferenceField', 'ReferenceText', 'ReferenceType',
'ReferencingParagraph', 'ReferencingParagraphStyle',
'Note', 'RegisterNote', 'NoteMarkerBase', 'NoteMarkerByID',
'NoteMarkerWithNote', 'NoteMarkerStyle',
'Field', 'PAGE_NUMBER', 'NUMBER_OF_PAGES', 'SECTION_NUMBER',
'Note', 'CitationNote', 'RegisterNote',
'NoteMarkerBase', 'NoteMarkerByID', 'NoteMarkerWithNote',
'NoteMarkerStyle', 'CitationMarker', 'Field',
'PAGE_NUMBER', 'NUMBER_OF_PAGES', 'SECTION_NUMBER',
'SECTION_TITLE', 'DOCUMENT_TITLE', 'DOCUMENT_SUBTITLE']


Expand Down Expand Up @@ -100,7 +99,7 @@ def children(self, container, type=None):
self.warn(f"Target '{target_id}' has no '{type}' reference",
container)
text = ErrorText('??', parent=self)
if type == ReferenceType.TITLE: # prevent infinite recursion
if type == ReferenceType.TITLE: # prevent infinite recursion
document.title_targets.update(self.referenceable_ids)
if target_id in document.title_targets:
self.warn("Circular 'title' reference replaced with "
Expand Down Expand Up @@ -215,8 +214,14 @@ def __eq__(self, type_name):
return type_name == type(self.styled).__name__


class NoteStyle(LabeledFlowableStyle):
as_footnote = Attribute(Bool, True,
'Display the Note as a footnote')


class Note(LabeledFlowable):
category = 'Note'
style_class = NoteStyle

def __init__(self, flowable, id=None, style=None, parent=None):
label = Paragraph(DirectReference(self))
Expand All @@ -232,6 +237,25 @@ def prepare(self, flowable_target):
self.note.prepare(flowable_target)


class CitationNote(Note):
def __init__(self, flowable, id=None, style=None, parent=None):
super().__init__(flowable, id, style, parent)

def as_footnote(self):
return CitationFootnote(self.flowable, style=self.style,
parent=self.parent,
id=self.id)

def flow(self, container, last_descender, state=None, **kwargs):
if self.get_style('as_footnote', container):
return 0, 0, last_descender
return super().flow(container, last_descender, state, **kwargs)


class CitationFootnote(Note):
pass


class NoteMarkerStyle(ReferenceStyle):
type = OverrideDefault(ReferenceType.NUMBER)

Expand Down Expand Up @@ -261,10 +285,22 @@ def prepare(self, flowable_target):
document.set_reference(target_id, 'number', formatted_label)

def before_placing(self, container):
self.add_footnote(container)
super().before_placing(container)

def add_footnote(self, container):
note = container.document.elements[self.target_id(container.document)]
if not container._footnote_space.add_footnote(note):
raise ContainerOverflow
super().before_placing(container)


class CitationMarker(Reference, NoteMarkerBase):
def add_footnote(self, container):
note = container.document.elements[self.target_id(container.document)]
if note.get_style('as_footnote', container):
footnote = note.as_footnote()
if not container._footnote_space.add_footnote(footnote):
raise ContainerOverflow


class NoteMarkerByID(Reference, NoteMarkerBase):
Expand Down Expand Up @@ -428,8 +464,8 @@ def children(self, container):

RE_FIELD = re.compile('{(' + '|'.join(chain(FieldType.all,
(r'{}\(\d+\)'.format(name)
for name
in SectionFieldType.all)))
for name
in SectionFieldType.all)))
+ '|' + RE_STRINGFIELD
+ ')}', re.IGNORECASE)

Expand Down
12 changes: 9 additions & 3 deletions src/rinoh/stylesheets/matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,20 @@
matcher('header', Header)
matcher('footer', Footer)


# footnotes & citations
# footnotes

matcher('footnote marker', NoteMarkerBase.like('footnote'))
matcher('citation marker', NoteMarkerBase.like('citation'))
matcher('footnote paragraph', Note / GroupedFlowables / Paragraph)
matcher('footnote label', Note / Paragraph)

# citations

matcher('citation', Note.like('citation'))
matcher('citation marker', NoteMarkerBase.like('citation'))
matcher('citation paragraph',
Note.like('citation') / GroupedFlowables / Paragraph)
matcher('citation label', Note.like('citation') / Paragraph)


# images & figures

Expand Down
Binary file added tests_regression/rst/citation_in_body.pdf
Binary file not shown.
9 changes: 9 additions & 0 deletions tests_regression/rst/citation_in_body.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
test-markup-citation
=====================

This is a citation ref; [CITE1]_ and [CITE2]_.

.. [CITE1] This is a citation
.. [CITE2] This is
a multiline citation
8 changes: 8 additions & 0 deletions tests_regression/rst/citation_in_body.rts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[STYLESHEET]
base = sphinx_base14

[citation]
as_footnote = False

[citation paragraph]
font_slant=ITALIC
File renamed without changes.
File renamed without changes.

0 comments on commit bc5aefa

Please sign in to comment.