Skip to content

Add support for two sets of footnotes (alphabetical and numbered). #5

Merged
merged 13 commits into from Apr 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
261 changes: 256 additions & 5 deletions Skripten/EOAconvert.py
Expand Up @@ -802,6 +802,34 @@ def cleanup():
dictFootnotes[strUID] = str(intNoteNumber)
intNoteNumber += 1


# the new-style footnotes that use LaTeX bigfoot show up in the following order:
footnote_groups = ["decimal", "lower-latin"]

def get_bigfoot_data(chapter):
"""
footnotes are per-chapter
footnote numbers reset for each chapter
this helper takes a chapter and returns a collection containing its new-style footnotes that use LaTeX bigfoot
the result is an association list: a list of key-value pairs
the values are, for each type of footnote, a list of the footnotes of that type, in the order in which they appear in the chapter
"""
xmlBigfootNotes = list(chapter.findall(".//EOAbigfoot"))
return [ # a list
( # of tuples
grouping, # the key
[ # the value: a filter of the above list
note
for note
in xmlBigfootNotes
if grouping == note.get("list-style-type")
],
)
for grouping
in footnote_groups # the types we support
]


print ("-----------------------------------------------------")
print ("Numbering of Lists per Chapter")
for xmlChapter in xmlChapters:
Expand Down Expand Up @@ -1486,14 +1514,166 @@ def cleanup():

print ("-----------------------------------------------------")
print ("Preparing Footnotes")

def alph_footnote_index(fndex):
"""
lowercase Latin footnotes need to support more than 26 values
These are zero-indexed.

>>> alph_footnote_index(0)
'a'
>>> alph_footnote_index(1)
'b'
>>> alph_footnote_index(24)
'y'
>>> alph_footnote_index(25)
'z'
>>> alph_footnote_index(26)
'aa'
>>> alph_footnote_index(27)
'ab'
"""
alphabet = "abcdefghijklmnopqrstuvwxyz"
quotient, remainder = divmod(fndex, len(alphabet))
if not quotient: return alphabet[fndex]
return alph_footnote_index(quotient - 1) + alph_footnote_index(remainder)


def replace_footnote_equations(footnote):
"""
captures reusable behavior from the existing code
potentially, some of the old code could be replaced by calls to this helper

usage: contentopf = replace_footnote_equations(my_footnote)
unfortunately, returning the result seemed like a better idea than mutating the global variable
"""
result = contentopf
for equation in footnote.findall(".//EOAequationnonumber"):
filename = equation.get("filename")
equation.clear()
equation.tag = "p"
img = etree.Element("img", src="images/%s" % filename, alt="")
equation.append(img)
cwd = os.getcwd()
shutil.copy("%s/items/%s" % (cwd, filename), "%s/CONVERT/epub/DEBPS/images/%s" % (cwd, filename))
result = addToContentopf(result, "images/" + filename, filename, "png")
return result


def replace_footnote_with_sup(note):
"""
captures reusable behavior from the existing code
potentially, some of the old code could be replaced by calls to this helper

this behavior showed up in a few places
I thought I would be able to extract a little more, but this was all that was actually common
"""
tail = note.tail
note.clear()
note.tail = tail
note.tag = "sup"


def bring_footnote_down_epub(footnote, footnote_name, destination):
"""
captures reusable behavior from the existing code
potentially, some of the old code could be replaced by calls to this helper

usage: contentopf = bring_footnote_down_epub(my_footnote, "1", xmlNewFootnotes)
unfortunately, returning the result seemed like a better idea than mutating the global variable
"""

contentopf = replace_footnote_equations(footnote) # see usage note
kids = list(footnote.getchildren())
prefix = "[%s]" % footnote_name

# we would like to prepend this footnote identifier to the footnote element
if footnote.text is not None:
# if the element starts with some text anyway, prepend it there
footnote.text = "%s %s" % (prefix, footnote.text)
else:
# if, however, the element begins with a child, prepend the text at the beginning of the first child instead
if len(kids):
first_child = kids[0]
child_text = prefix
# separate them with a space, unless the child had no text to begin with
child_suffix = first_child.text
if child_suffix is None:
child_suffix = ""
else:
child_text += " "
child_text += child_suffix
first_child.text = child_text
else:
# a totally empty footnote is weird, but who am I to judge?
footnote.text = prefix
footnote_text = footnote.text or ""
replace_footnote_with_sup(footnote)
footnote.text = "[%s] " % footnote_name
# append any text the footnote used to have to the destination
destkids = list(destination.getchildren())
if len(destkids):
# if the destination has children, append after the last one's tail
last_kid = destkids[-1]
prefix = last_kid.tail
if prefix is None:
prefix = ""
else:
prefix += " "
last_kid.tail = prefix + footnote_text
else:
# if the destination has no children, append to its text
prefix = destination.text
if prefix is None:
prefix = ""
else:
prefix += " "
destination.text = prefix + footnote_text
for kid in kids:
destination.append(kid)
return contentopf


class FootnoteError(Exception):
"""
we only support one type of footnote per chapter
don't try to mix-and-match
"""
pass


for xmlChapter in xmlChapters:
xmlFootnotes = xmlChapter.findall(".//note")
if len(xmlFootnotes) == 0:
continue
groupings = get_bigfoot_data(xmlChapter)
xmlFootnotes = list(xmlChapter.findall(".//note"))
has_old = 0 != len(xmlFootnotes)
has_new = 0 != len(
[ # flatten the association list whose values are lists, so we can take the length
note
for grouping, notes in groupings
for note in notes
]
)

# the XOR case falls through, the AND is an error, and the NOR skips to the next chapter
if has_old:
if has_new:
raise FootnoteError("Chapter %s contains both \\EOAfn and footnotes in the style of \\EOAfnalph" % xmlChapter.get("id-text"))
else:
if not has_new:
continue
xmlNewFootnotes = etree.Element("div")
xmlNewFootnotesHeader = etree.Element("h3")
xmlNewFootnotesHeader.text = dictLangFootnotes[xmlChapter.get("language")]
xmlNewFootnotes.append(xmlNewFootnotesHeader)
for grouping, notes in groupings:
# do for the new-style footnotes what was being done for the old
for index, note in enumerate(notes):
footnote_name = str(index + 1)
if "lower-latin" == grouping:
footnote_name = alph_footnote_index(index)
para = etree.Element("p")
contentopf = bring_footnote_down_epub(note, footnote_name, para)
xmlNewFootnotes.append(para)
intFootnoteNumber = 1
for xmlFootnote in xmlFootnotes:
# Not numbered Equations may appear in a footnote, need to be treated differently
Expand Down Expand Up @@ -2702,10 +2882,70 @@ def djangoParseHeadline(xmlElement):
print ("----------------------------------------------")
print ("Processing and linking Footnotes for django")

def bring_footnote_down_django(footnote, fragment, footnote_number, object_number, unique_id, destination):
"""
captures reusable behavior from the existing code
potentially, some of the old code could be replaced by calls to this helper

usage: intObjectNumber = bring_footnote_down_django(xmlFootnote, "fn"+str(intFootnoteNumber), str(intFootnoteNumber), intObjectNumber, tmpStrUID, xmlResult)
unfortunately, returning the result seemed like a better idea than mutating the global variable
"""

kids = list(footnote.getchildren())
footnote_text = footnote.text or ""
replace_footnote_with_sup(footnote)
footnote.set("class", "footnote")
anchor = etree.Element("a")
anchor.set("href", "#" + fragment) # "fn" + str(intFootnoteNumber)
anchor.text = footnote_number # str(intFootnoteNumber)
footnote.append(anchor)
foot = etree.Element("EOAfootnote")
foot.set("order", str(object_number))
object_number += 1
foot.set("number", footnote_number)
anchor_number = next(
iter(
(
parent.get("order")
for parent
in footnote.iterancestors()
if parent.get("order") is not None
)
)
)
foot.set("anchor", anchor_number)
foot.set("id", unique_id)
foot.text = footnote_text
for kid in kids:
if "EOAequationnonumber" == kid.tag:
cwd = os.getcwd()
shutil.copy(
"%s/items/%s" % (cwd, kid.get("filename")),
"%s/CONVERT/django/images/" % cwd,
)
foot.append(kid)
destination.append(foot)
return object_number


xmlEOAchapters = xmlEOAdocument.findall(".//EOAchapter")
for xmlEOAchapter in xmlEOAchapters:
if len(xmlEOAchapter.findall(".//note")) == 0:
continue
groupings = get_bigfoot_data(xmlEOAchapter)
has_old = 0 != len(xmlEOAchapter.findall(".//note"))
has_new = 0 != len(
[ # flatten
note
for grouping, notes in groupings
for note in notes
]
)
# XOR falls through, AND is an error (that should have already been thrown during the epub phase), and NOR skips to the next chapter
if has_old:
if has_new:
raise FootnoteError("This chapter contains both old-style footnotes and new-style footnotes")
else:
if not has_new:
continue
# Find out running order of last item the chapter
# Hier pro FN zunächst die EOAequationnonumber in <p> korrigieren
# Dann pro FN die Kindelemente abarbeiten und an die neue FN dran hängen
Expand All @@ -2722,6 +2962,17 @@ def djangoParseHeadline(xmlElement):
xmlHead.text = dictLangFootnotes[xmlEOAchapter.get("language")]
xmlEOAsection.append(xmlHead)
xmlResult.append(xmlEOAsection)

for grouping, notes in groupings:
for index, note in enumerate(notes):
# do for the new-style notes what the old code did for the other footnotes
fntext = str(index+1)
if "lower-latin" == grouping:
fntext = alph_footnote_index(index)
unique_id = "fn%s" % fntext
intObjectNumber = bring_footnote_down_django(note, unique_id, fntext, intObjectNumber, unique_id, xmlResult)

intFootnoteNumber = 1
xmlFootnotes = xmlEOAchapter.findall(".//note")
for xmlFootnote in xmlFootnotes:
xmlFootnoteContent = xmlFootnote.getchildren()
Expand Down
28 changes: 25 additions & 3 deletions TeX/pre_eoa.tex
@@ -1,6 +1,5 @@
% Version: 1.5
% 14/07/2015
% Last modifier: Georg
% Version: 1.5+
% Last modified: March 2016

\documentclass[10pt,openright,twoside]{scrbook}

Expand Down Expand Up @@ -372,6 +371,8 @@
}
% New command for numbered chapters
\newcommand{\EOAchapter}[2]{
\setcounter{footnotealph}{0}
\setcounter{footnotearabic}{0}
\chapter[#2]{#2}
\chaptermark{#1}
\ifthenelse{\boolean{anthology}}{
Expand Down Expand Up @@ -611,3 +612,24 @@
\setlength\emergencystretch{3em}

\usepackage[german=quotes]{csquotes}




%%%%%%%%%%%% Footnote Sets
\usepackage{etex}
\usepackage{bigfoot}
\usepackage{alphalph}
% Allocate some extra registers so that we don't run out when
% declaring footnotes
\reserveinserts{4}

\DeclareNewFootnote{arabic}[arabic]
\DeclareNewFootnote{alph}[alph]
%allow for more than 26 alpha-numbered footnotes per chater
%(aa,ab,ac,...)
\renewcommand{\thefootnotealph}{\emph{\alphalph{\value{footnotealph}}}}

\newcommand{\EOAfnalph}[1]{\protect\footnotealph{#1}}
\newcommand{\EOAfnarabic}[1]{\protect\footnotearabic{#1}}
%%%%%%%%%%%% Footnote Sets --end
3 changes: 3 additions & 0 deletions TeX/pre_xml.tex
Expand Up @@ -268,3 +268,6 @@

%EOAtablehead
\newcommand*{\EOAtablehead}[2][1]{\begin{xmlelement}{tableheader}TRUE\end{xmlelement}#2\\}

\newcommand*{\EOAfnarabic}[1]{\begin{xmlelement}{EOAbigfoot}\AddAttToCurrent{list-style-type}{decimal}#1\end{xmlelement}}
\newcommand*{\EOAfnalph}[1]{\begin{xmlelement}{EOAbigfoot}\AddAttToCurrent{list-style-type}{lower-latin}#1\end{xmlelement}}