txt2ebook 0.1.145__tar.gz → 0.1.147__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/.pre-commit-config.yaml +4 -1
  2. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/CHANGELOG.md +16 -0
  3. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/PKG-INFO +7 -13
  4. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/README.md +5 -11
  5. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/noxfile.py +15 -4
  6. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/pyproject.toml +6 -1
  7. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/__init__.py +6 -2
  8. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/base.py +9 -2
  9. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/epub.py +18 -6
  10. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/gmi.py +7 -2
  11. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/md.py +7 -2
  12. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/pdf.py +3 -1
  13. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/tex.py +6 -2
  14. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/txt.py +6 -2
  15. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/typ.py +4 -1
  16. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/languages/en.py +3 -2
  17. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/models/book.py +3 -1
  18. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/parser.py +10 -5
  19. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/__init__.py +2 -1
  20. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/gmi.py +3 -1
  21. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/massage.py +46 -14
  22. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/md.py +3 -1
  23. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/pdf.py +3 -1
  24. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/tex.py +3 -1
  25. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/typ.py +3 -1
  26. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/tokenizer.py +10 -3
  27. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/zh_utils.py +4 -1
  28. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/conftest.py +25 -0
  29. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_input_file_arg.py +4 -1
  30. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_output_file_arg.py +3 -1
  31. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_purge_flag.py +3 -1
  32. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_split_volume_and_chapter_flag.py +3 -1
  33. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_subcommand_massage.py +10 -3
  34. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/uv.lock +210 -185
  35. txt2ebook-0.1.145/tests/test_epub_writer.py +0 -28
  36. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/.coveragerc +0 -0
  37. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/.gitignore +0 -0
  38. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/.python-version +0 -0
  39. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/CONTRIBUTING.md +0 -0
  40. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/LICENSE.md +0 -0
  41. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/Makefile +0 -0
  42. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/make.bat +0 -0
  43. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/CHANGELOG.md +0 -0
  44. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/CONTRIBUTING.md +0 -0
  45. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/LICENSE.md +0 -0
  46. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/README.md +0 -0
  47. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/_static/logo.png +0 -0
  48. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/conf.py +0 -0
  49. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/index.rst +0 -0
  50. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/txt2ebook.formats.rst +0 -0
  51. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/txt2ebook.helpers.rst +0 -0
  52. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/txt2ebook.models.rst +0 -0
  53. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/txt2ebook.parsers.rst +0 -0
  54. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/docs/source/txt2ebook.rst +0 -0
  55. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/__main__.py +0 -0
  56. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/cli.py +0 -0
  57. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/exceptions.py +0 -0
  58. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/__init__.py +0 -0
  59. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/templates/__init__.py +0 -0
  60. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
  61. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/templates/epub/clean.css +0 -0
  62. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/templates/epub/condense.css +0 -0
  63. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/formats/templates/epub/noindent.css +0 -0
  64. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/helpers/__init__.py +0 -0
  65. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/languages/__init__.py +0 -0
  66. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/languages/zh_cn.py +0 -0
  67. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/languages/zh_tw.py +0 -0
  68. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
  69. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -0
  70. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/txt2ebook.pot +0 -0
  71. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
  72. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -0
  73. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
  74. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -0
  75. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/models/__init__.py +0 -0
  76. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/models/chapter.py +0 -0
  77. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/models/volume.py +0 -0
  78. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/env.py +0 -0
  79. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/epub.py +0 -0
  80. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/src/txt2ebook/subcommands/parse.py +0 -0
  81. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/__init__.py +0 -0
  82. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/empty_file.txt +0 -0
  83. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/missing_chapters.txt +0 -0
  84. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample.txt +0 -0
  85. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample_all_headers.txt +0 -0
  86. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample_long_headers.txt +0 -0
  87. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample_remove_wrapping.txt +0 -0
  88. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample_unsorted_headers.txt +0 -0
  89. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample_with_issues.txt +0 -0
  90. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/fixtures/sample_with_metadata.txt +0 -0
  91. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_filename_format_flag.py +0 -0
  92. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_format_option.py +0 -0
  93. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_header_number_flag.py +0 -0
  94. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_language_option.py +0 -0
  95. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_overwrite_flag.py +0 -0
  96. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_parser.py +0 -0
  97. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_quiet_flag.py +0 -0
  98. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_raise_warnings.py +0 -0
  99. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_sort_volume_and_chapter_flag.py +0 -0
  100. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_subcommand_env.py +0 -0
  101. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_subcommand_epub.py +0 -0
  102. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_test_parsing_flag.py +0 -0
  103. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_tokenizer.py +0 -0
  104. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_translator_option.py +0 -0
  105. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_txt2ebook.py +0 -0
  106. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_verbose_flag.py +0 -0
  107. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_volume_page_flag.py +0 -0
  108. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_zh_utils_zh_halfwidth_to_fullwidth.py +0 -0
  109. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_zh_utils_zh_numeric.py +0 -0
  110. {txt2ebook-0.1.145 → txt2ebook-0.1.147}/tests/test_zh_utils_zh_words_to_numbers.py +0 -0
@@ -35,12 +35,15 @@ repos:
35
35
  exclude: (poetry.lock)
36
36
 
37
37
  - repo: https://github.com/astral-sh/ruff-pre-commit
38
- rev: v0.12.0
38
+ rev: v0.12.2
39
39
  hooks:
40
40
  - id: ruff-check
41
41
  args:
42
42
  - --fix
43
43
  - --exit-non-zero-on-fix
44
+ - id: ruff-format
45
+ args:
46
+ - --line-length=79
44
47
 
45
48
  - repo: https://github.com/pre-commit/mirrors-mypy
46
49
  rev: v1.16.1
@@ -7,6 +7,22 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.1.147 (2025-07-06)
11
+
12
+ - Bump deps and `pre-commit` hook for `ruff`
13
+ - Enable `ruff format` and code format
14
+ - Resync `uv.lock` file during release `nox` job
15
+
16
+ ## v0.1.146 (2025-06-29)
17
+
18
+ - Bump deps and `pre-commit` hook for `ruff`
19
+ - Fix `massage` subcommand did not retain the original content
20
+ - Fix missing regex for translator for en language
21
+ - Move fixture to the right file
22
+ - Update help message in readme
23
+ - Update installation step using `uv` tool
24
+ - Update project classifier keywords
25
+
10
26
  ## v0.1.145 (2025-06-22)
11
27
 
12
28
  - Bump deps and `pre-commit` hooks
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: txt2ebook
3
- Version: 0.1.145
3
+ Version: 0.1.147
4
4
  Summary: CLI tool to convert txt file to ebook format
5
5
  Project-URL: Homepage, https://github.com/kianmeng/txt2ebook
6
6
  Project-URL: Repository, https://github.com/kianmeng/txt2ebook
7
7
  Author-email: Kian-Meng Ang <kianmeng@cpan.org>
8
8
  License-Expression: AGPL-3.0-or-later
9
9
  License-File: LICENSE.md
10
- Keywords: cjk,ebook,epub,txt
10
+ Keywords: cjk,ebook,epub,gmi,latex,md,pdf,txt,typst
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Environment :: Console
13
13
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
@@ -50,19 +50,13 @@ A console tool to convert txt file to different ebook formats.
50
50
  Stable version From PyPI:
51
51
 
52
52
  ```console
53
- python3 -m pip install txt2ebook
53
+ uv tool install txt2ebook
54
54
  ```
55
55
 
56
56
  Upgrade to latest stable version:
57
57
 
58
58
  ```console
59
- python3 -m pip install txt2ebook --upgrade
60
- ```
61
-
62
- Latest development version from GitHub:
63
-
64
- ```console
65
- python3 -m pip install -e git+https://github.com/kianmeng/txt2ebook.git
59
+ uv tool upgrade txt2ebook
66
60
  ```
67
61
 
68
62
  ## Usage
@@ -108,12 +102,12 @@ positional arguments:
108
102
  typ
109
103
  generate ebook in Typst format
110
104
 
111
- optional arguments:
112
- -of OUTPUT_FOLDER, --output-folder OUTPUT_FOLDER
105
+ options:
106
+ -of, --output-folder OUTPUT_FOLDER
113
107
  set default output folder (default: 'output')
114
108
  -p, --purge
115
109
  remove converted ebooks specified by --output-folder option (default: 'False')
116
- -l LANGUAGE, --language LANGUAGE
110
+ -l, --language LANGUAGE
117
111
  language of the ebook (default: 'None')
118
112
  -rw, --raise-on-warning
119
113
  raise exception and stop parsing upon warning
@@ -7,19 +7,13 @@ A console tool to convert txt file to different ebook formats.
7
7
  Stable version From PyPI:
8
8
 
9
9
  ```console
10
- python3 -m pip install txt2ebook
10
+ uv tool install txt2ebook
11
11
  ```
12
12
 
13
13
  Upgrade to latest stable version:
14
14
 
15
15
  ```console
16
- python3 -m pip install txt2ebook --upgrade
17
- ```
18
-
19
- Latest development version from GitHub:
20
-
21
- ```console
22
- python3 -m pip install -e git+https://github.com/kianmeng/txt2ebook.git
16
+ uv tool upgrade txt2ebook
23
17
  ```
24
18
 
25
19
  ## Usage
@@ -65,12 +59,12 @@ positional arguments:
65
59
  typ
66
60
  generate ebook in Typst format
67
61
 
68
- optional arguments:
69
- -of OUTPUT_FOLDER, --output-folder OUTPUT_FOLDER
62
+ options:
63
+ -of, --output-folder OUTPUT_FOLDER
70
64
  set default output folder (default: 'output')
71
65
  -p, --purge
72
66
  remove converted ebooks specified by --output-folder option (default: 'False')
73
- -l LANGUAGE, --language LANGUAGE
67
+ -l, --language LANGUAGE
74
68
  language of the ebook (default: 'None')
75
69
  -rw, --raise-on-warning
76
70
  raise exception and stop parsing upon warning
@@ -74,7 +74,9 @@ def cov(session: nox.Session) -> None:
74
74
  def doc(session: nox.Session) -> None:
75
75
  """Build doc with sphinx."""
76
76
  _uv_install(session)
77
- session.run("sphinx-build", "docs/source/", "docs/build/html", *session.posargs)
77
+ session.run(
78
+ "sphinx-build", "docs/source/", "docs/build/html", *session.posargs
79
+ )
78
80
 
79
81
 
80
82
  @nox.session(python="3.13")
@@ -192,17 +194,26 @@ def release(session: nox.Session) -> None:
192
194
  """Bump release."""
193
195
  _uv_install(session)
194
196
 
195
- before_version = session.run("uv", "version", "--short", silent=True).strip()
197
+ before_version = session.run(
198
+ "uv", "version", "--short", silent=True
199
+ ).strip()
196
200
  session.run("uv", "version", "--bump", "patch")
197
- after_version = session.run("uv", "version", "--short", silent=True).strip()
201
+ after_version = session.run(
202
+ "uv", "version", "--short", silent=True
203
+ ).strip()
198
204
 
199
- _search_and_replace("src/txt2ebook/__init__.py", before_version, after_version)
205
+ _search_and_replace(
206
+ "src/txt2ebook/__init__.py", before_version, after_version
207
+ )
200
208
 
201
209
  date = datetime.date.today().strftime("%Y-%m-%d")
202
210
  before_header = "## [Unreleased]\n\n"
203
211
  after_header = f"## [Unreleased]\n\n## v{after_version} ({date})\n\n"
204
212
  _search_and_replace("CHANGELOG.md", before_header, after_header)
205
213
 
214
+ # resync to update the bumped version to uv.lock
215
+ _uv_install(session)
216
+
206
217
  session.run(
207
218
  "git",
208
219
  "commit",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "txt2ebook"
3
- version = "0.1.145"
3
+ version = "0.1.147"
4
4
  description = "CLI tool to convert txt file to ebook format"
5
5
  authors = [{ name = "Kian-Meng Ang", email = "kianmeng@cpan.org" }]
6
6
  requires-python = "~=3.9"
@@ -10,7 +10,12 @@ keywords = [
10
10
  "cjk",
11
11
  "ebook",
12
12
  "epub",
13
+ "gmi",
14
+ "latex",
15
+ "md",
16
+ "pdf",
13
17
  "txt",
18
+ "typst",
14
19
  ]
15
20
  classifiers = [
16
21
  "Development Status :: 4 - Beta",
@@ -38,7 +38,9 @@ def setup_logger(config: argparse.Namespace) -> None:
38
38
  return
39
39
 
40
40
  log_level = logging.DEBUG if config.debug else logging.INFO
41
- log_format = "%(levelname)5s: %(message)s" if config.debug else "%(message)s"
41
+ log_format = (
42
+ "%(levelname)5s: %(message)s" if config.debug else "%(message)s"
43
+ )
42
44
 
43
45
  logging.basicConfig(
44
46
  level=log_level,
@@ -48,7 +50,9 @@ def setup_logger(config: argparse.Namespace) -> None:
48
50
  )
49
51
 
50
52
 
51
- def log_or_raise_on_warning(message: str, raise_on_warning: bool = False) -> None:
53
+ def log_or_raise_on_warning(
54
+ message: str, raise_on_warning: bool = False
55
+ ) -> None:
52
56
  """Logs a warning message or raises an exception.
53
57
 
54
58
  Args:
@@ -126,7 +126,9 @@ class BaseWriter(ABC):
126
126
  # do not create to output folder when we explicit set the output path
127
127
  # and file
128
128
  if self.config.output_file:
129
- return Path(file.parent, lower_underscore(file.stem)).with_suffix(extension)
129
+ return Path(file.parent, lower_underscore(file.stem)).with_suffix(
130
+ extension
131
+ )
130
132
 
131
133
  return Path(
132
134
  file.parent, self.config.output_folder, lower_underscore(file.stem)
@@ -139,7 +141,12 @@ class BaseWriter(ABC):
139
141
  self._("translator:") + ",".join(self.book.translators),
140
142
  self._("tag:") + ",".join(self.book.tags),
141
143
  ]
142
- return "---\n" + "\n".join(metadata) + "\n---" + self.config.paragraph_separator
144
+ return (
145
+ "---\n"
146
+ + "\n".join(metadata)
147
+ + "\n---"
148
+ + self.config.paragraph_separator
149
+ )
143
150
 
144
151
  def _to_toc(self, list_symbol, header_symbol="") -> str:
145
152
  toc = ""
@@ -81,7 +81,9 @@ class EpubWriter(BaseWriter):
81
81
  logger.debug("Create separate volume page: %s", section)
82
82
  book.toc.append((html_volume, html_chapters))
83
83
  else:
84
- book.toc.append((epub.Section(section.title), html_chapters))
84
+ book.toc.append(
85
+ (epub.Section(section.title), html_chapters)
86
+ )
85
87
 
86
88
  if isinstance(section, Chapter):
87
89
  html_chapter = self._build_chapter(section)
@@ -113,12 +115,16 @@ class EpubWriter(BaseWriter):
113
115
  book.add_item(book_css)
114
116
 
115
117
  nav = epub.EpubNav()
116
- nav.add_link(href="style/book.css", rel="stylesheet", type="text/css")
118
+ nav.add_link(
119
+ href="style/book.css", rel="stylesheet", type="text/css"
120
+ )
117
121
  book.add_item(nav)
118
122
  book.spine.append("nav")
119
123
 
120
124
  except FileNotFoundError as error:
121
- logger.error("Unknown EPUB template name: %s", self.config.epub_template)
125
+ logger.error(
126
+ "Unknown EPUB template name: %s", self.config.epub_template
127
+ )
122
128
  raise SystemExit() from error
123
129
 
124
130
  def _gen_id(self) -> str:
@@ -143,7 +149,9 @@ class EpubWriter(BaseWriter):
143
149
  lang=self.book.language,
144
150
  content=html,
145
151
  )
146
- cover.add_link(href="style/book.css", rel="stylesheet", type="text/css")
152
+ cover.add_link(
153
+ href="style/book.css", rel="stylesheet", type="text/css"
154
+ )
147
155
  return cover
148
156
 
149
157
  def _build_volume(self, volume: Volume) -> epub.EpubHtml:
@@ -166,7 +174,9 @@ class EpubWriter(BaseWriter):
166
174
  lang=self.book.language,
167
175
  content=html,
168
176
  )
169
- epub_html.add_link(href="style/book.css", rel="stylesheet", type="text/css")
177
+ epub_html.add_link(
178
+ href="style/book.css", rel="stylesheet", type="text/css"
179
+ )
170
180
 
171
181
  return epub_html
172
182
 
@@ -192,6 +202,8 @@ class EpubWriter(BaseWriter):
192
202
  lang=self.book.language,
193
203
  content=html,
194
204
  )
195
- epub_html.add_link(href="style/book.css", rel="stylesheet", type="text/css")
205
+ epub_html.add_link(
206
+ href="style/book.css", rel="stylesheet", type="text/css"
207
+ )
196
208
 
197
209
  return epub_html
@@ -96,7 +96,9 @@ class GmiWriter(BaseWriter):
96
96
  export_filename.parent.mkdir(parents=True, exist_ok=True)
97
97
  logger.info("Creating %s", export_filename)
98
98
  with open(export_filename, "w", encoding="utf8") as file:
99
- file.write(self._to_volume_chapter_txt(section, chapter))
99
+ file.write(
100
+ self._to_volume_chapter_txt(section, chapter)
101
+ )
100
102
  ct_seq = ct_seq + 1
101
103
  if isinstance(section, Chapter):
102
104
  filename = lower_underscore(
@@ -145,7 +147,10 @@ class GmiWriter(BaseWriter):
145
147
  f"# {volume.title}"
146
148
  + self.config.paragraph_separator
147
149
  + self.config.paragraph_separator.join(
148
- [self._to_chapter_txt(chapter, True) for chapter in volume.chapters]
150
+ [
151
+ self._to_chapter_txt(chapter, True)
152
+ for chapter in volume.chapters
153
+ ]
149
154
  )
150
155
  )
151
156
 
@@ -95,7 +95,9 @@ class MdWriter(BaseWriter):
95
95
  export_filename.parent.mkdir(parents=True, exist_ok=True)
96
96
  logger.info("Creating %s", export_filename)
97
97
  with open(export_filename, "w", encoding="utf8") as file:
98
- file.write(self._to_volume_chapter_txt(section, chapter))
98
+ file.write(
99
+ self._to_volume_chapter_txt(section, chapter)
100
+ )
99
101
  ct_seq = ct_seq + 1
100
102
  if isinstance(section, Chapter):
101
103
  filename = lower_underscore(
@@ -144,7 +146,10 @@ class MdWriter(BaseWriter):
144
146
  f"# {volume.title}"
145
147
  + self.config.paragraph_separator
146
148
  + self.config.paragraph_separator.join(
147
- [self._to_chapter_txt(chapter, True) for chapter in volume.chapters]
149
+ [
150
+ self._to_chapter_txt(chapter, True)
151
+ for chapter in volume.chapters
152
+ ]
148
153
  )
149
154
  )
150
155
 
@@ -153,7 +153,9 @@ class PdfWriter(BaseWriter):
153
153
  canvas.restoreState()
154
154
 
155
155
  def _get_pagesize(self) -> Tuple:
156
- page_size = self.config.page_size or self.langconf.DEFAULT_PDF_PAGE_SIZE
156
+ page_size = (
157
+ self.config.page_size or self.langconf.DEFAULT_PDF_PAGE_SIZE
158
+ )
157
159
  return portrait(getattr(reportlab.lib.pagesizes, page_size.upper()))
158
160
 
159
161
  def _init_styles(self) -> None:
@@ -100,7 +100,9 @@ class TexWriter(BaseWriter):
100
100
  doc.append(NoEsc(r"\maketitle"))
101
101
  doc.append(NoEsc(r"\thispagestyle{empty}"))
102
102
  doc.append(NoEsc(r"\addtocontents{toc}{\protect\pagestyle{empty}}"))
103
- doc.append(NoEsc(r"\addtocontents{toc}{\protect\thispagestyle{empty}}"))
103
+ doc.append(
104
+ NoEsc(r"\addtocontents{toc}{\protect\thispagestyle{empty}}")
105
+ )
104
106
  doc.append(NoEsc(r"\tableofcontents"))
105
107
  doc.append(NoEsc(r"\pagestyle{empty}"))
106
108
  doc.append(NoEsc(r"\cleardoublepage"))
@@ -126,7 +128,9 @@ class TexWriter(BaseWriter):
126
128
 
127
129
  filename = str(new_filename.parent / new_filename.stem)
128
130
  pdf_filename = Path(filename).with_suffix(".pdf")
129
- doc.generate_pdf(filename, compiler="latexmk", clean_tex=self.config.clean_tex)
131
+ doc.generate_pdf(
132
+ filename, compiler="latexmk", clean_tex=self.config.clean_tex
133
+ )
130
134
  logger.info("Generate PDF file: %s", pdf_filename.resolve())
131
135
 
132
136
  if self.config.open:
@@ -105,7 +105,9 @@ class TxtWriter(BaseWriter):
105
105
  export_filename.parent.mkdir(parents=True, exist_ok=True)
106
106
  logger.info("Creating %s", export_filename)
107
107
  with open(export_filename, "w", encoding="utf8") as file:
108
- file.write(self._to_volume_chapter_txt(section, chapter))
108
+ file.write(
109
+ self._to_volume_chapter_txt(section, chapter)
110
+ )
109
111
  ct_seq = ct_seq + 1
110
112
  if isinstance(section, Chapter):
111
113
  filename = lower_underscore(
@@ -132,7 +134,9 @@ class TxtWriter(BaseWriter):
132
134
  ymd_hms = dt.now().strftime("%Y%m%d_%H%M%S")
133
135
  new_filename = Path(
134
136
  txt_filename.resolve().parent.joinpath(
135
- lower_underscore(txt_filename.stem + "_" + ymd_hms + ".txt")
137
+ lower_underscore(
138
+ txt_filename.stem + "_" + ymd_hms + ".txt"
139
+ )
136
140
  )
137
141
  )
138
142
 
@@ -175,7 +175,10 @@ class TypWriter(BaseWriter):
175
175
  f"= {volume.title}"
176
176
  + self.config.paragraph_separator
177
177
  + self.config.paragraph_separator.join(
178
- [self._to_chapter_txt(chapter, True) for chapter in volume.chapters]
178
+ [
179
+ self._to_chapter_txt(chapter, True)
180
+ for chapter in volume.chapters
181
+ ]
179
182
  )
180
183
  )
181
184
 
@@ -17,7 +17,8 @@
17
17
 
18
18
  DEFAULT_RE_TITLE = r"Title:(.*)"
19
19
  DEFAULT_RE_AUTHOR = r"Author:(.*)"
20
- DEFAULT_RE_TAG = r"Tags(.*)"
21
- DEFAULT_RE_INDEX = r"Index(.*)"
20
+ DEFAULT_RE_TAG = r"Tags:(.*)"
21
+ DEFAULT_RE_INDEX = r"Index:(.*)"
22
22
  DEFAULT_RE_VOLUME = r"(Volume.*)"
23
23
  DEFAULT_RE_CHAPTER = r"(Chapter.*)"
24
+ DEFAULT_RE_TRANSLATOR = r"Translator:(.*)"
@@ -60,7 +60,9 @@ class Book:
60
60
  try:
61
61
  return format_options[filename_format]
62
62
  except KeyError:
63
- raise AttributeError(f"Invalid filename format: '{filename_format}'!")
63
+ raise AttributeError(
64
+ f"Invalid filename format: '{filename_format}'!"
65
+ )
64
66
 
65
67
  def debug(self, verbosity: int = 1) -> None:
66
68
  """Dump debug log of sections in self.toc."""
@@ -57,8 +57,8 @@ class Parser:
57
57
  """
58
58
  tokenizer = Tokenizer(self.raw_content, self.config)
59
59
 
60
- (book_title, authors, translators, tags, index, toc) = self.parse_tokens(
61
- tokenizer
60
+ (book_title, authors, translators, tags, index, toc) = (
61
+ self.parse_tokens(tokenizer)
62
62
  )
63
63
 
64
64
  book = Book(
@@ -103,13 +103,17 @@ class Parser:
103
103
  match = re.match(rf"第([{self.langconf.HALFWIDTH_NUMS}]*)", words)
104
104
  if match and match.group(1) != "":
105
105
  header_nums = match.group(1)
106
- return words.replace(header_nums, str(header_nums).rjust(length, "0"))
106
+ return words.replace(
107
+ header_nums, str(header_nums).rjust(length, "0")
108
+ )
107
109
 
108
110
  # left pad the section number if found as fullwidth integer
109
111
  match = re.match(rf"第([{self.langconf.FULLWIDTH_NUMS}]*)", words)
110
112
  if match and match.group(1) != "":
111
113
  header_nums = match.group(1)
112
- return words.replace(header_nums, str(header_nums).rjust(length, "0"))
114
+ return words.replace(
115
+ header_nums, str(header_nums).rjust(length, "0")
116
+ )
113
117
 
114
118
  replaced_words = zh_words_to_numbers(words, length=length)
115
119
 
@@ -148,7 +152,8 @@ class Parser:
148
152
  if (
149
153
  token.type not in ["CHAPTER", "PARAGRAPH"]
150
154
  or (
151
- token.type == "CHAPTER" and self.config.verbose >= chapter_verbosity
155
+ token.type == "CHAPTER"
156
+ and self.config.verbose >= chapter_verbosity
152
157
  )
153
158
  or (
154
159
  token.type == "PARAGRAPH"
@@ -24,7 +24,8 @@ def build_subparser(subparsers):
24
24
  iter_namespace = pkgutil.iter_modules(__path__, __name__ + ".")
25
25
 
26
26
  subcommands = {
27
- name: importlib.import_module(name) for finder, name, ispkg in iter_namespace
27
+ name: importlib.import_module(name)
28
+ for finder, name, ispkg in iter_namespace
28
29
  }
29
30
 
30
31
  for subcommand in subcommands.values():
@@ -27,7 +27,9 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
  def build_subparser(subparsers) -> None:
29
29
  """Build the subparser."""
30
- gmi_parser = subparsers.add_parser("gmi", help="generate ebook in Gemtext format")
30
+ gmi_parser = subparsers.add_parser(
31
+ "gmi", help="generate ebook in Gemtext format"
32
+ )
31
33
 
32
34
  gmi_parser.set_defaults(func=run)
33
35
 
@@ -19,6 +19,7 @@ import argparse
19
19
  import logging
20
20
  import sys
21
21
  from importlib import import_module
22
+ from pathlib import Path
22
23
 
23
24
  import cjkwrap
24
25
  import regex as re
@@ -201,21 +202,48 @@ def run(args: argparse.Namespace) -> None:
201
202
  None
202
203
  """
203
204
  massaged_txt = massage_txt(args)
204
- args.language = detect_and_expect_language(massaged_txt, args.language)
205
- config_lang = args.language.replace("-", "_")
206
- langconf = import_module(f"txt2ebook.languages.{config_lang}")
207
- args.with_toc = False
208
- parser = Parser(massaged_txt, args, langconf)
209
- book = parser.parse()
205
+ if args.overwrite:
206
+ _overwrite_file(args, massaged_txt)
207
+ else:
208
+ _new_file(args, massaged_txt)
209
+
210
+ # args.language = detect_and_expect_language(massaged_txt, args.language)
211
+ # config_lang = args.language.replace("-", "_")
212
+ # langconf = import_module(f"txt2ebook.languages.{config_lang}")
213
+ # args.with_toc = False
214
+ # parser = Parser(massaged_txt, args, langconf)
215
+ # book = parser.parse()
216
+
217
+ # if args.debug:
218
+ # book.debug(args.verbose)
219
+
220
+ # if args.header_number:
221
+ # book = header_number(args, book)
222
+
223
+ # writer = TxtWriter(book, args)
224
+ # writer.write()
225
+
210
226
 
211
- if args.debug:
212
- book.debug(args.verbose)
227
+ def _overwrite_file(args, massaged_txt) -> None:
228
+ txt_filename = Path(args.input_file.name)
213
229
 
214
- if args.header_number:
215
- book = header_number(args, book)
230
+ with open(txt_filename, "w", encoding="utf8") as file:
231
+ file.write(massaged_txt)
232
+ logger.info("Overwrite txt file: %s", txt_filename.resolve())
216
233
 
217
- writer = TxtWriter(book, args)
218
- writer.write()
234
+
235
+ def _new_file(args, massaged_txt) -> None:
236
+ txt_filename = Path(args.input_file.name)
237
+ export_filename = Path(
238
+ txt_filename.resolve().parent.joinpath(
239
+ args.output_folder, txt_filename.name
240
+ )
241
+ )
242
+ export_filename.parent.mkdir(parents=True, exist_ok=True)
243
+
244
+ with open(export_filename, "w", encoding="utf8") as file:
245
+ file.write(massaged_txt)
246
+ logger.info("New txt file: %s", export_filename.resolve())
219
247
 
220
248
 
221
249
  def header_number(args: argparse.Namespace, book: Book) -> Book:
@@ -230,7 +258,9 @@ def header_number(args: argparse.Namespace, book: Book) -> Book:
230
258
  for toc_item in book.toc:
231
259
  toc_type = type(toc_item).__name__
232
260
  if toc_type in seq_lengths:
233
- toc_item.title = words_to_nums(args, toc_item.title, seq_lengths[toc_type])
261
+ toc_item.title = words_to_nums(
262
+ args, toc_item.title, seq_lengths[toc_type]
263
+ )
234
264
 
235
265
  return book
236
266
 
@@ -337,7 +367,9 @@ def do_delete_regex(args, content: str) -> str:
337
367
  str: The formatted book content.
338
368
  """
339
369
  for delete_regex in args.re_delete:
340
- content = re.sub(re.compile(rf"{delete_regex}", re.MULTILINE), "", content)
370
+ content = re.sub(
371
+ re.compile(rf"{delete_regex}", re.MULTILINE), "", content
372
+ )
341
373
  return content
342
374
 
343
375
 
@@ -27,7 +27,9 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
  def build_subparser(subparsers) -> None:
29
29
  """Build the subparser."""
30
- md_parser = subparsers.add_parser("md", help="generate ebook in Markdown format")
30
+ md_parser = subparsers.add_parser(
31
+ "md", help="generate ebook in Markdown format"
32
+ )
31
33
 
32
34
  md_parser.set_defaults(func=run)
33
35
 
@@ -28,7 +28,9 @@ logger = logging.getLogger(__name__)
28
28
 
29
29
  def build_subparser(subparsers) -> None:
30
30
  """Build the subparser."""
31
- pdf_parser = subparsers.add_parser("pdf", help="generate ebook in Markdown format")
31
+ pdf_parser = subparsers.add_parser(
32
+ "pdf", help="generate ebook in Markdown format"
33
+ )
32
34
 
33
35
  pdf_parser.set_defaults(func=run)
34
36
 
@@ -27,7 +27,9 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
  def build_subparser(subparsers) -> None:
29
29
  """Build the subparser."""
30
- tex_parser = subparsers.add_parser("tex", help="generate ebook in TeX/PDF format")
30
+ tex_parser = subparsers.add_parser(
31
+ "tex", help="generate ebook in TeX/PDF format"
32
+ )
31
33
 
32
34
  tex_parser.add_argument(
33
35
  "input_file",
@@ -28,7 +28,9 @@ logger = logging.getLogger(__name__)
28
28
 
29
29
  def build_subparser(subparsers) -> None:
30
30
  """Build the subparser."""
31
- typ_parser = subparsers.add_parser("typ", help="generate ebook in Typst format")
31
+ typ_parser = subparsers.add_parser(
32
+ "typ", help="generate ebook in Typst format"
33
+ )
32
34
 
33
35
  typ_parser.set_defaults(func=run)
34
36