Skip to content

Commit

Permalink
add script odfdo-headers
Browse files Browse the repository at this point in the history
  • Loading branch information
jdum committed Mar 2, 2024
1 parent 99cdcad commit 0994418
Show file tree
Hide file tree
Showing 9 changed files with 759 additions and 8 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ There is no detailed documentation or tutorial, but:
- the `recipes` folder contains more than 50 working sample scripts,
- the `doc` folder contains an auto generated documentation.

When installing odfdo, 5 scripts are installed:
When installing odfdo, a few scripts are installed:

- `odfdo-diff`: show a *diff* between two .odt document.
- `odfdo-folder`: convert standard ODF file to folder and files, and reverse.
- `odfdo-show`: dump text from an ODF file to the standard output, and optionally styles and meta informations.
- `odfdo-styles`: command line interface tool to manipulate styles of ODF files.
- `odfdo-replace`: find a pattern (regex) in an ODF file and replace by some string.
- `odfdo-highlight`: highlight the text matching a pattern (regex) in an ODF file.
- `odfdo-headers`: print the headers of an ODF file.

About styles: the best way to apply style is by merging styles from a template
document into your generated document (See `odfdo-styles` script).
Expand Down
4 changes: 2 additions & 2 deletions doc/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31281,7 +31281,7 @@ <h2 id="arguments">Arguments</h2>
del level_indexes[idx]
number_str = &#34;.&#34;.join(number) + &#34;.&#34;
# Make the title with &#34;1.2.3. Title&#34; format
header_title = f&#34;{number_str} {str(header)}&#34;
header_title = f&#34;{number_str} {header}&#34;
paragraph = Paragraph(header_title)
if use_default_styles:
paragraph.style = TOC_ENTRY_STYLE_PATTERN % level
Expand Down Expand Up @@ -31575,7 +31575,7 @@ <h2 id="arguments">Arguments</h2>
del level_indexes[idx]
number_str = &#34;.&#34;.join(number) + &#34;.&#34;
# Make the title with &#34;1.2.3. Title&#34; format
header_title = f&#34;{number_str} {str(header)}&#34;
header_title = f&#34;{number_str} {header}&#34;
paragraph = Paragraph(header_title)
if use_default_styles:
paragraph.style = TOC_ENTRY_STYLE_PATTERN % level
Expand Down
385 changes: 385 additions & 0 deletions doc/scripts/headers.html

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions doc/scripts/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ <h2 class="section-title" id="header-submodules">Sub-modules</h2>
<dd>
<div class="desc"></div>
</dd>
<dt><code class="name"><a title="odfdo.scripts.headers" href="headers.html">odfdo.scripts.headers</a></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt><code class="name"><a title="odfdo.scripts.highlight" href="highlight.html">odfdo.scripts.highlight</a></code></dt>
<dd>
<div class="desc"></div>
Expand Down Expand Up @@ -74,6 +78,7 @@ <h1>Index</h1>
<ul>
<li><code><a title="odfdo.scripts.diff" href="diff.html">odfdo.scripts.diff</a></code></li>
<li><code><a title="odfdo.scripts.folder" href="folder.html">odfdo.scripts.folder</a></code></li>
<li><code><a title="odfdo.scripts.headers" href="headers.html">odfdo.scripts.headers</a></code></li>
<li><code><a title="odfdo.scripts.highlight" href="highlight.html">odfdo.scripts.highlight</a></code></li>
<li><code><a title="odfdo.scripts.replace" href="replace.html">odfdo.scripts.replace</a></code></li>
<li><code><a title="odfdo.scripts.show" href="show.html">odfdo.scripts.show</a></code></li>
Expand Down
6 changes: 3 additions & 3 deletions doc/toc.html
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ <h1 class="title">Module <code>odfdo.toc</code></h1>
del level_indexes[idx]
number_str = &#34;.&#34;.join(number) + &#34;.&#34;
# Make the title with &#34;1.2.3. Title&#34; format
header_title = f&#34;{number_str} {str(header)}&#34;
header_title = f&#34;{number_str} {header}&#34;
paragraph = Paragraph(header_title)
if use_default_styles:
paragraph.style = TOC_ENTRY_STYLE_PATTERN % level
Expand Down Expand Up @@ -1390,7 +1390,7 @@ <h2 id="arguments">Arguments</h2>
del level_indexes[idx]
number_str = &#34;.&#34;.join(number) + &#34;.&#34;
# Make the title with &#34;1.2.3. Title&#34; format
header_title = f&#34;{number_str} {str(header)}&#34;
header_title = f&#34;{number_str} {header}&#34;
paragraph = Paragraph(header_title)
if use_default_styles:
paragraph.style = TOC_ENTRY_STYLE_PATTERN % level
Expand Down Expand Up @@ -1684,7 +1684,7 @@ <h2 id="arguments">Arguments</h2>
del level_indexes[idx]
number_str = &#34;.&#34;.join(number) + &#34;.&#34;
# Make the title with &#34;1.2.3. Title&#34; format
header_title = f&#34;{number_str} {str(header)}&#34;
header_title = f&#34;{number_str} {header}&#34;
paragraph = Paragraph(header_title)
if use_default_styles:
paragraph.style = TOC_ENTRY_STYLE_PATTERN % level
Expand Down
137 changes: 137 additions & 0 deletions odfdo/scripts/headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright 2018-2024 Jérôme Dumonteil
# Copyright (c) 2009-2013 Ars Aperta, Itaapy, Pierlis, Talend.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Authors (odfdo project): jerome.dumonteil@gmail.com
# The odfdo project is a derivative work of the lpod-python project:
# https://github.com/lpod/lpod-python
from __future__ import annotations

import io
import selectors
import sys
from argparse import ArgumentParser, Namespace

from odfdo import Document, Header, __version__

PROG = "odfdo-headers"
STDIN_TIMEOUT = 0.5


def configure_parser() -> ArgumentParser:
description = "Print the headers of the document."
parser = ArgumentParser(prog=PROG, description=description)
parser.add_argument(
"-d",
"--depth",
action="store",
dest="depth",
type=int,
default=999,
required=False,
help="depth of headers hierarchy to print",
)
parser.add_argument(
"--version",
action="version",
version=f"{PROG} v{__version__}",
)
parser.add_argument(
"document",
nargs="?",
default=None,
action="store",
help="input document. if not present, read from stdin",
)
return parser


def error(message: str) -> str:
return f"{PROG}: error: {message}"


def detect_stdin_timeout() -> None:
selector = selectors.DefaultSelector()
selector.register(sys.stdin, selectors.EVENT_READ)
something = selector.select(timeout=STDIN_TIMEOUT)
if not something:
raise SystemExit(error("timeout reading from stdin"))
selector.close()


def read_document(input_path: str | None) -> Document:
if input_path:
return Document(input_path)
detect_stdin_timeout()
content = io.BytesIO(sys.stdin.buffer.read())
document = Document(content)
content.close()
return document


def header_numbering(
header: Header,
level_indexes: dict[int, int],
depth: int,
) -> str | None:
level = header.get_attribute_integer("text:outline-level") or 0
if level is None or level > depth:
return None
number = []
# < level
for idx in range(1, level):
index = level_indexes.setdefault(idx, 1)
number.append(str(index))
# == level
index = level_indexes.setdefault(level, 0) + 1
level_indexes[level] = index
number.append(str(index))
# 3. l > level
for idx in range(level + 1, depth + 1):
if idx in level_indexes:
del level_indexes[idx]
return ".".join(number) + "."


def headers_document(document: Document, depth: int) -> None:
body = document.body
level_indexes: dict[int, int] = {}
for header in body.get_headers():
number_str = header_numbering(header, level_indexes, depth)
if number_str is None:
continue
print(f"{number_str} {header}")


def headers(args: Namespace) -> None:
document = read_document(args.document)
depth = args.depth
headers_document(document, depth)


def main() -> int:
parser = configure_parser()
args = parser.parse_args()
try:
headers(args)
except Exception:
parser.print_help()
print()
raise
return 0


if __name__ == "__main__": # pragma: no cover
raise SystemExit(main())
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "odfdo"
version = "3.6.0"
version = "3.7.0"
description = "Python library for OpenDocument Format"
license = "Apache-2.0"
keywords = ["python", "library", "ODF", "OpenDocument"]
Expand Down Expand Up @@ -47,6 +47,7 @@ odfdo-highlight = 'odfdo.scripts.highlight:main'
odfdo-show = 'odfdo.scripts.show:main'
odfdo-styles = 'odfdo.scripts.styles:main'
odfdo-replace = 'odfdo.scripts.replace:main'
odfdo-headers = 'odfdo.scripts.headers:main'

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
2 changes: 1 addition & 1 deletion recipes/create_a_basic_text_document_with_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def make_annotations(document):
paragraph.insert_annotation(
after=some_word, # The word after what the annotation is inserted.
body="It's so easy!", # The annotation itself, at the end of the page.
creator="Bob" # The author of the annotation.
creator="Bob", # The author of the annotation.
# date= xxx A datetime value, by default datetime.now().
)

Expand Down
Loading

0 comments on commit 0994418

Please sign in to comment.