Skip to content

biblesfmconverter

Convert bible sfm (usfm) files to the Giella xml format.

add_text(paragraph, text)

Add text to a p element, one verse per line.

Parameters:

Name Type Description Default
paragraph Element

a Giella xml p element.

required
text str

the text that should be added.

required
Source code in corpustools/biblesfmconverter.py
101
102
103
104
105
106
107
108
109
110
111
def add_text(paragraph, text):
    """Add text to a p element, one verse per line.

    Args:
        paragraph (lxml.etree.Element): a Giella xml p element.
        text (str): the text that should be added.
    """
    if not text:
        return

    paragraph.text = f"{paragraph.text}\n{text}" if paragraph.text else text

clean_text(text)

Normalise the whitespace and the macron letters of a text string.

Parameters:

Name Type Description Default
text str

a text string.

required

Returns:

Type Description
str

the normalised string.

Source code in corpustools/biblesfmconverter.py
68
69
70
71
72
73
74
75
76
77
def clean_text(text):
    """Normalise the whitespace and the macron letters of a text string.

    Args:
        text (str): a text string.

    Returns:
        (str): the normalised string.
    """
    return macron_to_caron(" ".join(text.split()))

convert2intermediate(filename)

Convert an sfm file to the intermediate Giella xml format.

Source code in corpustools/biblesfmconverter.py
179
180
181
182
183
def convert2intermediate(filename):
    """Convert an sfm file to the intermediate Giella xml format."""

    with open(filename, encoding="utf-8") as sfm_file:
        return parse_sfm(sfm_file)

macron_to_caron(text)

Replace letters written with a macron with their caron equivalents.

The 1895 bible writes č, š and ǯ as c, s and ʒ with a combining macron.

Parameters:

Name Type Description Default
text str

a text string.

required

Returns:

Type Description
str

the string with carons instead of macrons.

Source code in corpustools/biblesfmconverter.py
54
55
56
57
58
59
60
61
62
63
64
65
def macron_to_caron(text):
    """Replace letters written with a macron with their caron equivalents.

    The 1895 bible writes č, š and ǯ as c, s and ʒ with a combining macron.

    Args:
        text (str): a text string.

    Returns:
        (str): the string with carons instead of macrons.
    """
    return MACRON_RE.sub(lambda match: CARON_LETTERS[match.group(1)], text)

parse_line(line)

Split an sfm line into its marker and its text.

Footnotes and cross references are removed from the text.

Parameters:

Name Type Description Default
line str

a line from an sfm file.

required

Returns:

Type Description
tuple[str | None, str]

the name of the marker (None if the line does not start with a marker) and the text of the line.

Source code in corpustools/biblesfmconverter.py
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def parse_line(line):
    """Split an sfm line into its marker and its text.

    Footnotes and cross references are removed from the text.

    Args:
        line (str): a line from an sfm file.

    Returns:
        (tuple[str | None, str]): the name of the marker (None if the line
            does not start with a marker) and the text of the line.
    """
    line = NOTE_RE.sub("", line)
    match = LINE_MARKER_RE.match(line)

    if match is None:
        return None, clean_text(line)

    return match.group("name"), clean_text(line[match.end() :])

parse_sfm(lines)

Convert the lines of an sfm file to a Giella xml document.

Each chapter becomes a section holding one p element, where each verse is a line. Titles end the p element, so a chapter with titles in the middle of it gets one p element per title.

Parameters:

Name Type Description Default
lines Iterable[str]

the lines of an sfm file.

required

Returns:

Type Description
Element

a Giella xml document element.

Source code in corpustools/biblesfmconverter.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def parse_sfm(lines):
    """Convert the lines of an sfm file to a Giella xml document.

    Each chapter becomes a section holding one p element, where each verse
    is a line. Titles end the p element, so a chapter with titles in the
    middle of it gets one p element per title.

    Args:
        lines (collections.abc.Iterable[str]): the lines of an sfm file.

    Returns:
        (lxml.etree.Element): a Giella xml document element.
    """
    document = etree.Element("document")
    body = etree.SubElement(document, "body")
    section = etree.SubElement(body, "section")
    paragraph = None

    for line in lines:
        marker, text = parse_line(line)

        if marker in IGNORED_MARKERS:
            continue

        if marker == "c":
            section = etree.SubElement(body, "section")
            paragraph = None
        elif marker in TITLE_MARKERS:
            title = etree.SubElement(section, "p")
            title.set("type", "title")
            add_text(title, text)
            paragraph = None
        elif marker == SUMMARY_MARKER:
            summary = etree.SubElement(section, "p")
            add_text(summary, text)
            paragraph = None
        elif marker in TEXT_MARKERS or marker == "v" or marker is None:
            if marker == "v":
                text = VERSE_NUMBER_RE.sub("", text)
            if paragraph is None:
                paragraph = etree.SubElement(section, "p")
            add_text(paragraph, text)
        else:
            raise UserWarning(f"Unknown sfm marker: \\{marker}")

    remove_empty(body)

    return document

remove_empty(body)

Remove p and section elements without text from the body.

Parameters:

Name Type Description Default
body Element

a Giella xml body element.

required
Source code in corpustools/biblesfmconverter.py
114
115
116
117
118
119
120
121
122
123
124
125
126
def remove_empty(body):
    """Remove p and section elements without text from the body.

    Args:
        body (lxml.etree.Element): a Giella xml body element.
    """
    for paragraph in body.findall(".//p"):
        if paragraph.text is None:
            paragraph.getparent().remove(paragraph)

    for section in body.findall("section"):
        if not len(section):
            body.remove(section)