txt2ebook 0.1.155__tar.gz → 0.1.157__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.
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/PKG-INFO +34 -18
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/pyproject.toml +26 -19
- txt2ebook-0.1.157/setup.cfg +4 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/massage.py +30 -9
- txt2ebook-0.1.157/src/txt2ebook.egg-info/PKG-INFO +169 -0
- txt2ebook-0.1.157/src/txt2ebook.egg-info/SOURCES.txt +61 -0
- txt2ebook-0.1.157/src/txt2ebook.egg-info/dependency_links.txt +1 -0
- txt2ebook-0.1.157/src/txt2ebook.egg-info/entry_points.txt +3 -0
- txt2ebook-0.1.157/src/txt2ebook.egg-info/requires.txt +32 -0
- txt2ebook-0.1.157/src/txt2ebook.egg-info/top_level.txt +1 -0
- txt2ebook-0.1.157/tests/test_header_number_flag.py +46 -0
- txt2ebook-0.1.157/tests/test_input_file_arg.py +28 -0
- txt2ebook-0.1.157/tests/test_language_option.py +33 -0
- txt2ebook-0.1.157/tests/test_output_file_arg.py +34 -0
- txt2ebook-0.1.157/tests/test_overwrite_flag.py +23 -0
- txt2ebook-0.1.157/tests/test_parser.py +94 -0
- txt2ebook-0.1.157/tests/test_purge_flag.py +49 -0
- txt2ebook-0.1.157/tests/test_quiet_flag.py +24 -0
- txt2ebook-0.1.157/tests/test_sort_volume_and_chapter_flag.py +44 -0
- txt2ebook-0.1.157/tests/test_split_volume_and_chapter_flag.py +51 -0
- txt2ebook-0.1.157/tests/test_test_parsing_flag.py +28 -0
- txt2ebook-0.1.157/tests/test_tokenizer.py +127 -0
- txt2ebook-0.1.157/tests/test_txt2ebook.py +21 -0
- txt2ebook-0.1.157/tests/test_verbose_flag.py +92 -0
- txt2ebook-0.1.157/tests/test_volume_page_flag.py +23 -0
- txt2ebook-0.1.155/src/txt2ebook/formats/templates/epub/clean.css +0 -41
- txt2ebook-0.1.155/src/txt2ebook/formats/templates/epub/condense.css +0 -42
- txt2ebook-0.1.155/src/txt2ebook/formats/templates/epub/noindent.css +0 -42
- txt2ebook-0.1.155/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
- txt2ebook-0.1.155/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -31
- txt2ebook-0.1.155/src/txt2ebook/locales/txt2ebook.pot +0 -31
- txt2ebook-0.1.155/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
- txt2ebook-0.1.155/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -31
- txt2ebook-0.1.155/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
- txt2ebook-0.1.155/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -31
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/LICENSE.md +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/README.md +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/__main__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/cli.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/exceptions.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/base.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/epub.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/gmi.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/md.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/pdf.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/templates/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/tex.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/txt.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/typ.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/helpers/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/en.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/zh_cn.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/zh_tw.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/book.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/chapter.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/volume.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/parser.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/__init__.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/env.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/epub.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/gmi.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/md.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/parse.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/pdf.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/tex.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/typ.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/tokenizer.py +0 -0
- {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/zh_utils.py +0 -0
@@ -1,14 +1,14 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: txt2ebook
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.157
|
4
4
|
Summary: CLI tool to convert txt file to ebook format
|
5
|
-
Keywords: cjk,ebook,epub,gmi,latex,md,pdf,txt,typst
|
6
5
|
Author-email: Kian-Meng Ang <kianmeng@cpan.org>
|
7
|
-
|
8
|
-
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
7
|
+
Project-URL: Homepage, https://github.com/kianmeng/txt2ebook
|
8
|
+
Project-URL: Repository, https://github.com/kianmeng/txt2ebook
|
9
|
+
Keywords: cjk,ebook,epub,gmi,latex,md,pdf,txt,typst
|
9
10
|
Classifier: Development Status :: 4 - Beta
|
10
11
|
Classifier: Environment :: Console
|
11
|
-
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
12
12
|
Classifier: Natural Language :: Chinese (Simplified)
|
13
13
|
Classifier: Natural Language :: Chinese (Traditional)
|
14
14
|
Classifier: Programming Language :: Python
|
@@ -23,22 +23,39 @@ Classifier: Topic :: Text Processing :: Filters
|
|
23
23
|
Classifier: Topic :: Text Processing :: General
|
24
24
|
Classifier: Topic :: Text Processing :: Markup :: HTML
|
25
25
|
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
26
|
+
Requires-Python: ~=3.9
|
27
|
+
Description-Content-Type: text/markdown
|
26
28
|
License-File: LICENSE.md
|
27
29
|
Requires-Dist: CJKwrap~=2.2
|
28
|
-
Requires-Dist: EbookLib
|
29
|
-
Requires-Dist: bs4
|
30
|
-
Requires-Dist: importlib-resources
|
31
|
-
Requires-Dist: jieba
|
32
|
-
Requires-Dist: langdetect
|
33
|
-
Requires-Dist: lxml
|
34
|
-
Requires-Dist: pylatex
|
30
|
+
Requires-Dist: EbookLib<0.18,>=0.17.1
|
31
|
+
Requires-Dist: bs4<0.0.2,>=0.0.1
|
32
|
+
Requires-Dist: importlib-resources<7,>=6.1.1
|
33
|
+
Requires-Dist: jieba<0.43,>=0.42.1
|
34
|
+
Requires-Dist: langdetect<2,>=1.0.9
|
35
|
+
Requires-Dist: lxml<6,>=5.2.2
|
36
|
+
Requires-Dist: pylatex<2,>=1.4.2
|
35
37
|
Requires-Dist: pypandoc~=1.11
|
36
|
-
Requires-Dist: regex
|
37
|
-
Requires-Dist: reportlab
|
38
|
-
Requires-Dist: typing-extensions
|
38
|
+
Requires-Dist: regex<2022,>=2021.11.10
|
39
|
+
Requires-Dist: reportlab<5,>=4.0.0
|
40
|
+
Requires-Dist: typing-extensions<5,>=4.5.0
|
39
41
|
Requires-Dist: typst>=0.13.0
|
40
|
-
|
41
|
-
|
42
|
+
Provides-Extra: test
|
43
|
+
Requires-Dist: pytest; extra == "test"
|
44
|
+
Requires-Dist: pytest-cov; extra == "test"
|
45
|
+
Requires-Dist: pytest-randomly; extra == "test"
|
46
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
47
|
+
Requires-Dist: scripttest; extra == "test"
|
48
|
+
Provides-Extra: doc
|
49
|
+
Requires-Dist: myst-parser; extra == "doc"
|
50
|
+
Requires-Dist: sphinx; extra == "doc"
|
51
|
+
Requires-Dist: sphinx-autobuild; extra == "doc"
|
52
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "doc"
|
53
|
+
Requires-Dist: sphinx-copybutton; extra == "doc"
|
54
|
+
Provides-Extra: lint
|
55
|
+
Requires-Dist: pre-commit; extra == "lint"
|
56
|
+
Requires-Dist: ruff; extra == "lint"
|
57
|
+
Requires-Dist: mypy; extra == "lint"
|
58
|
+
Dynamic: license-file
|
42
59
|
|
43
60
|
# txt2ebook
|
44
61
|
|
@@ -150,4 +167,3 @@ The fish logo used in the documentation generated by Sphinx is a public domain
|
|
150
167
|
drawing of Troschel's parrotfish (Chlorurus troschelii Var. A.) from
|
151
168
|
<https://commons.wikimedia.org/entity/M18506436>.
|
152
169
|
18506436>.
|
153
|
-
|
@@ -1,11 +1,12 @@
|
|
1
1
|
[project]
|
2
2
|
name = "txt2ebook"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.157"
|
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"
|
7
7
|
readme = "README.md"
|
8
|
-
license =
|
8
|
+
license = "AGPL-3.0-or-later"
|
9
|
+
license-files = ["LICENSE.md"]
|
9
10
|
keywords = [
|
10
11
|
"cjk",
|
11
12
|
"ebook",
|
@@ -20,7 +21,6 @@ keywords = [
|
|
20
21
|
classifiers = [
|
21
22
|
"Development Status :: 4 - Beta",
|
22
23
|
"Environment :: Console",
|
23
|
-
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
24
24
|
"Natural Language :: Chinese (Simplified)",
|
25
25
|
"Natural Language :: Chinese (Traditional)",
|
26
26
|
"Programming Language :: Python",
|
@@ -60,28 +60,35 @@ Repository = "https://github.com/kianmeng/txt2ebook"
|
|
60
60
|
txt2ebook = "txt2ebook.cli:main"
|
61
61
|
tte = "txt2ebook.cli:main"
|
62
62
|
|
63
|
-
[
|
64
|
-
|
65
|
-
"
|
66
|
-
"bandit",
|
67
|
-
"flake8-simplify",
|
68
|
-
"mypy",
|
69
|
-
"myst-parser",
|
70
|
-
"nox",
|
71
|
-
"pep8-naming",
|
72
|
-
"pre-commit",
|
63
|
+
[project.optional-dependencies]
|
64
|
+
test = [
|
65
|
+
"pytest",
|
73
66
|
"pytest-cov",
|
74
67
|
"pytest-randomly",
|
75
68
|
"pytest-xdist",
|
76
|
-
"pytest",
|
77
|
-
"ruff",
|
78
69
|
"scripttest",
|
70
|
+
]
|
71
|
+
doc = [
|
72
|
+
"myst-parser",
|
73
|
+
"sphinx",
|
74
|
+
"sphinx-autobuild",
|
79
75
|
"sphinx-autodoc-typehints",
|
80
76
|
"sphinx-copybutton",
|
81
|
-
|
82
|
-
|
77
|
+
]
|
78
|
+
|
79
|
+
lint = [
|
80
|
+
"pre-commit",
|
81
|
+
"ruff",
|
82
|
+
"mypy",
|
83
83
|
]
|
84
84
|
|
85
85
|
[build-system]
|
86
|
-
requires = ["
|
87
|
-
build-backend = "
|
86
|
+
requires = ["setuptools>=61.0"]
|
87
|
+
build-backend = "setuptools.build_meta"
|
88
|
+
|
89
|
+
# verify through: uv run ruff check --show-settings
|
90
|
+
[tool.ruff]
|
91
|
+
line-length = 79
|
92
|
+
|
93
|
+
[tool.setuptools.packages.find]
|
94
|
+
where = ["src"]
|
@@ -210,7 +210,7 @@ def run(args: argparse.Namespace) -> None:
|
|
210
210
|
"""Run massage subcommand.
|
211
211
|
|
212
212
|
Args:
|
213
|
-
args (argparse.Namespace):
|
213
|
+
args (argparse.Namespace): arguments from command line arguments
|
214
214
|
|
215
215
|
Returns:
|
216
216
|
None
|
@@ -472,7 +472,6 @@ def extract_metadata_and_body(_args, content: str) -> tuple:
|
|
472
472
|
metadata = match.group(0).strip()
|
473
473
|
body = content.replace(metadata, "", 1)
|
474
474
|
|
475
|
-
|
476
475
|
metadata_block = metadata.split("---")[1]
|
477
476
|
|
478
477
|
metadata_dict = {}
|
@@ -492,33 +491,38 @@ def extract_metadata_and_body(_args, content: str) -> tuple:
|
|
492
491
|
return (meta_str, body)
|
493
492
|
|
494
493
|
|
495
|
-
def do_single_newline(args, content: str) -> str:
|
494
|
+
def do_single_newline(args: argparse.Namespace, content: str) -> str:
|
496
495
|
"""Set single newline.
|
497
496
|
|
498
497
|
Args:
|
499
|
-
|
498
|
+
args (argparse.Namespace): arguments from command line arguments
|
499
|
+
content (str): The formatted book content
|
500
500
|
|
501
501
|
Returns:
|
502
502
|
str: The formatted book content.
|
503
503
|
"""
|
504
|
-
|
504
|
+
unwrap_content = _unwrap_content(args, content)
|
505
|
+
modified_content = re.sub(r"\n+", "\n\n", unwrap_content)
|
505
506
|
return modified_content
|
506
507
|
|
507
508
|
|
508
|
-
def do_wrapping(args, content: str) -> str:
|
509
|
+
def do_wrapping(args: argparse.Namespace, content: str) -> str:
|
509
510
|
"""Wrap or fill CJK text.
|
510
511
|
|
511
512
|
Args:
|
512
|
-
|
513
|
+
args (argparse.Namespace): arguments from command line arguments
|
514
|
+
content (str): The formatted book content
|
513
515
|
|
514
516
|
Returns:
|
515
517
|
str: The formatted book content.
|
516
518
|
"""
|
517
519
|
logger.info("Wrapping paragraph to width: %s", args.width)
|
518
520
|
|
521
|
+
unwrap_content = _unwrap_content(args, content)
|
522
|
+
|
523
|
+
# don't remove empty line and keep all formatting as it
|
519
524
|
paragraphs = []
|
520
|
-
|
521
|
-
for paragraph in content.split("\n"):
|
525
|
+
for paragraph in unwrap_content.split("\n"):
|
522
526
|
paragraph = paragraph.strip()
|
523
527
|
|
524
528
|
lines = cjkwrap.wrap(paragraph, width=args.width)
|
@@ -527,3 +531,20 @@ def do_wrapping(args, content: str) -> str:
|
|
527
531
|
|
528
532
|
wrapped_content = "\n".join(paragraphs)
|
529
533
|
return wrapped_content
|
534
|
+
|
535
|
+
|
536
|
+
def _unwrap_content(args: argparse.Namespace, content: str) -> str:
|
537
|
+
"""
|
538
|
+
Args:
|
539
|
+
args (argparse.Namespace): arguments from command line arguments
|
540
|
+
content (str): The formatted book content
|
541
|
+
|
542
|
+
Returns:
|
543
|
+
str: The formatted book content.
|
544
|
+
"""
|
545
|
+
paragraphs = content.split(args.paragraph_separator)
|
546
|
+
processed_paragraphs = []
|
547
|
+
for paragraph in paragraphs:
|
548
|
+
single_line_paragraph = " ".join(paragraph.splitlines())
|
549
|
+
processed_paragraphs.append(single_line_paragraph.strip())
|
550
|
+
return args.paragraph_separator.join(processed_paragraphs)
|
@@ -0,0 +1,169 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: txt2ebook
|
3
|
+
Version: 0.1.157
|
4
|
+
Summary: CLI tool to convert txt file to ebook format
|
5
|
+
Author-email: Kian-Meng Ang <kianmeng@cpan.org>
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
7
|
+
Project-URL: Homepage, https://github.com/kianmeng/txt2ebook
|
8
|
+
Project-URL: Repository, https://github.com/kianmeng/txt2ebook
|
9
|
+
Keywords: cjk,ebook,epub,gmi,latex,md,pdf,txt,typst
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
11
|
+
Classifier: Environment :: Console
|
12
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
13
|
+
Classifier: Natural Language :: Chinese (Traditional)
|
14
|
+
Classifier: Programming Language :: Python
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
21
|
+
Classifier: Topic :: Text Processing
|
22
|
+
Classifier: Topic :: Text Processing :: Filters
|
23
|
+
Classifier: Topic :: Text Processing :: General
|
24
|
+
Classifier: Topic :: Text Processing :: Markup :: HTML
|
25
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
26
|
+
Requires-Python: ~=3.9
|
27
|
+
Description-Content-Type: text/markdown
|
28
|
+
License-File: LICENSE.md
|
29
|
+
Requires-Dist: CJKwrap~=2.2
|
30
|
+
Requires-Dist: EbookLib<0.18,>=0.17.1
|
31
|
+
Requires-Dist: bs4<0.0.2,>=0.0.1
|
32
|
+
Requires-Dist: importlib-resources<7,>=6.1.1
|
33
|
+
Requires-Dist: jieba<0.43,>=0.42.1
|
34
|
+
Requires-Dist: langdetect<2,>=1.0.9
|
35
|
+
Requires-Dist: lxml<6,>=5.2.2
|
36
|
+
Requires-Dist: pylatex<2,>=1.4.2
|
37
|
+
Requires-Dist: pypandoc~=1.11
|
38
|
+
Requires-Dist: regex<2022,>=2021.11.10
|
39
|
+
Requires-Dist: reportlab<5,>=4.0.0
|
40
|
+
Requires-Dist: typing-extensions<5,>=4.5.0
|
41
|
+
Requires-Dist: typst>=0.13.0
|
42
|
+
Provides-Extra: test
|
43
|
+
Requires-Dist: pytest; extra == "test"
|
44
|
+
Requires-Dist: pytest-cov; extra == "test"
|
45
|
+
Requires-Dist: pytest-randomly; extra == "test"
|
46
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
47
|
+
Requires-Dist: scripttest; extra == "test"
|
48
|
+
Provides-Extra: doc
|
49
|
+
Requires-Dist: myst-parser; extra == "doc"
|
50
|
+
Requires-Dist: sphinx; extra == "doc"
|
51
|
+
Requires-Dist: sphinx-autobuild; extra == "doc"
|
52
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "doc"
|
53
|
+
Requires-Dist: sphinx-copybutton; extra == "doc"
|
54
|
+
Provides-Extra: lint
|
55
|
+
Requires-Dist: pre-commit; extra == "lint"
|
56
|
+
Requires-Dist: ruff; extra == "lint"
|
57
|
+
Requires-Dist: mypy; extra == "lint"
|
58
|
+
Dynamic: license-file
|
59
|
+
|
60
|
+
# txt2ebook
|
61
|
+
|
62
|
+
A console tool to convert txt file to different ebook formats.
|
63
|
+
|
64
|
+
## Installation
|
65
|
+
|
66
|
+
Stable version From PyPI:
|
67
|
+
|
68
|
+
```console
|
69
|
+
uv tool install txt2ebook
|
70
|
+
```
|
71
|
+
|
72
|
+
Upgrade to latest stable version:
|
73
|
+
|
74
|
+
```console
|
75
|
+
uv tool upgrade txt2ebook
|
76
|
+
```
|
77
|
+
|
78
|
+
## Usage
|
79
|
+
|
80
|
+
Showing help message of command-line options:
|
81
|
+
|
82
|
+
```console
|
83
|
+
txt2ebook --help
|
84
|
+
```
|
85
|
+
|
86
|
+
<!--help !-->
|
87
|
+
|
88
|
+
```console
|
89
|
+
usage: txt2ebook [-of OUTPUT_FOLDER] [-p] [-l LANGUAGE] [-rw] [-q] [-v] [-d]
|
90
|
+
[-h] [-V]
|
91
|
+
{env,epub,gmi,massage,md,parse,pdf,tex,typ} ...
|
92
|
+
|
93
|
+
txt2ebook/tte is a cli tool to convert txt file to ebook format.
|
94
|
+
|
95
|
+
website: https://github.com/kianmeng/txt2ebook
|
96
|
+
changelog: https://github.com/kianmeng/txt2ebook/blob/master/CHANGELOG.md
|
97
|
+
issues: https://github.com/kianmeng/txt2ebook/issues
|
98
|
+
|
99
|
+
positional arguments:
|
100
|
+
{env,epub,gmi,massage,md,parse,pdf,tex,typ}
|
101
|
+
sub-command help
|
102
|
+
env
|
103
|
+
print environment information for bug reporting
|
104
|
+
epub
|
105
|
+
generate ebook in EPUB format
|
106
|
+
gmi
|
107
|
+
generate ebook in Gemtext format
|
108
|
+
massage
|
109
|
+
massage the source txt file
|
110
|
+
md
|
111
|
+
generate ebook in Markdown format
|
112
|
+
parse
|
113
|
+
parse and validate the txt file
|
114
|
+
pdf
|
115
|
+
generate ebook in Markdown format
|
116
|
+
tex
|
117
|
+
generate ebook in TeX/PDF format
|
118
|
+
typ
|
119
|
+
generate ebook in Typst format
|
120
|
+
|
121
|
+
options:
|
122
|
+
-of, --output-folder OUTPUT_FOLDER
|
123
|
+
set default output folder (default: 'output')
|
124
|
+
-p, --purge
|
125
|
+
remove converted ebooks specified by --output-folder option (default: 'False')
|
126
|
+
-l, --language LANGUAGE
|
127
|
+
language of the ebook (default: 'None')
|
128
|
+
-rw, --raise-on-warning
|
129
|
+
raise exception and stop parsing upon warning
|
130
|
+
-q, --quiet
|
131
|
+
suppress all logging
|
132
|
+
-v, --verbose
|
133
|
+
show verbosity of debugging log, use -vv, -vvv for more details
|
134
|
+
-d, --debug
|
135
|
+
show debugging log and stacktrace
|
136
|
+
-h, --help
|
137
|
+
show this help message and exit
|
138
|
+
-V, --version
|
139
|
+
show program's version number and exit
|
140
|
+
```
|
141
|
+
|
142
|
+
<!--help !-->
|
143
|
+
|
144
|
+
Convert a txt file into epub:
|
145
|
+
|
146
|
+
```console
|
147
|
+
txt2ebook ebook.txt
|
148
|
+
```
|
149
|
+
|
150
|
+
## Copyright and License
|
151
|
+
|
152
|
+
Copyright (c) 2021,2022,2023,2024,2025 Kian-Meng Ang
|
153
|
+
|
154
|
+
This program is free software: you can redistribute it and/or modify it under
|
155
|
+
the terms of the GNU Affero General Public License as published by the Free
|
156
|
+
Software Foundation, either version 3 of the License, or (at your option) any
|
157
|
+
later version.
|
158
|
+
|
159
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
160
|
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
161
|
+
PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
162
|
+
|
163
|
+
You should have received a copy of the GNU Affero General Public License along
|
164
|
+
with this program. If not, see <https://www.gnu.org/licenses/>.
|
165
|
+
|
166
|
+
The fish logo used in the documentation generated by Sphinx is a public domain
|
167
|
+
drawing of Troschel's parrotfish (Chlorurus troschelii Var. A.) from
|
168
|
+
<https://commons.wikimedia.org/entity/M18506436>.
|
169
|
+
18506436>.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
LICENSE.md
|
2
|
+
README.md
|
3
|
+
pyproject.toml
|
4
|
+
src/txt2ebook/__init__.py
|
5
|
+
src/txt2ebook/__main__.py
|
6
|
+
src/txt2ebook/cli.py
|
7
|
+
src/txt2ebook/exceptions.py
|
8
|
+
src/txt2ebook/parser.py
|
9
|
+
src/txt2ebook/tokenizer.py
|
10
|
+
src/txt2ebook/zh_utils.py
|
11
|
+
src/txt2ebook.egg-info/PKG-INFO
|
12
|
+
src/txt2ebook.egg-info/SOURCES.txt
|
13
|
+
src/txt2ebook.egg-info/dependency_links.txt
|
14
|
+
src/txt2ebook.egg-info/entry_points.txt
|
15
|
+
src/txt2ebook.egg-info/requires.txt
|
16
|
+
src/txt2ebook.egg-info/top_level.txt
|
17
|
+
src/txt2ebook/formats/__init__.py
|
18
|
+
src/txt2ebook/formats/base.py
|
19
|
+
src/txt2ebook/formats/epub.py
|
20
|
+
src/txt2ebook/formats/gmi.py
|
21
|
+
src/txt2ebook/formats/md.py
|
22
|
+
src/txt2ebook/formats/pdf.py
|
23
|
+
src/txt2ebook/formats/tex.py
|
24
|
+
src/txt2ebook/formats/txt.py
|
25
|
+
src/txt2ebook/formats/typ.py
|
26
|
+
src/txt2ebook/formats/templates/__init__.py
|
27
|
+
src/txt2ebook/formats/templates/epub/__init__.py
|
28
|
+
src/txt2ebook/helpers/__init__.py
|
29
|
+
src/txt2ebook/languages/__init__.py
|
30
|
+
src/txt2ebook/languages/en.py
|
31
|
+
src/txt2ebook/languages/zh_cn.py
|
32
|
+
src/txt2ebook/languages/zh_tw.py
|
33
|
+
src/txt2ebook/models/__init__.py
|
34
|
+
src/txt2ebook/models/book.py
|
35
|
+
src/txt2ebook/models/chapter.py
|
36
|
+
src/txt2ebook/models/volume.py
|
37
|
+
src/txt2ebook/subcommands/__init__.py
|
38
|
+
src/txt2ebook/subcommands/env.py
|
39
|
+
src/txt2ebook/subcommands/epub.py
|
40
|
+
src/txt2ebook/subcommands/gmi.py
|
41
|
+
src/txt2ebook/subcommands/massage.py
|
42
|
+
src/txt2ebook/subcommands/md.py
|
43
|
+
src/txt2ebook/subcommands/parse.py
|
44
|
+
src/txt2ebook/subcommands/pdf.py
|
45
|
+
src/txt2ebook/subcommands/tex.py
|
46
|
+
src/txt2ebook/subcommands/typ.py
|
47
|
+
tests/test_header_number_flag.py
|
48
|
+
tests/test_input_file_arg.py
|
49
|
+
tests/test_language_option.py
|
50
|
+
tests/test_output_file_arg.py
|
51
|
+
tests/test_overwrite_flag.py
|
52
|
+
tests/test_parser.py
|
53
|
+
tests/test_purge_flag.py
|
54
|
+
tests/test_quiet_flag.py
|
55
|
+
tests/test_sort_volume_and_chapter_flag.py
|
56
|
+
tests/test_split_volume_and_chapter_flag.py
|
57
|
+
tests/test_test_parsing_flag.py
|
58
|
+
tests/test_tokenizer.py
|
59
|
+
tests/test_txt2ebook.py
|
60
|
+
tests/test_verbose_flag.py
|
61
|
+
tests/test_volume_page_flag.py
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
CJKwrap~=2.2
|
2
|
+
EbookLib<0.18,>=0.17.1
|
3
|
+
bs4<0.0.2,>=0.0.1
|
4
|
+
importlib-resources<7,>=6.1.1
|
5
|
+
jieba<0.43,>=0.42.1
|
6
|
+
langdetect<2,>=1.0.9
|
7
|
+
lxml<6,>=5.2.2
|
8
|
+
pylatex<2,>=1.4.2
|
9
|
+
pypandoc~=1.11
|
10
|
+
regex<2022,>=2021.11.10
|
11
|
+
reportlab<5,>=4.0.0
|
12
|
+
typing-extensions<5,>=4.5.0
|
13
|
+
typst>=0.13.0
|
14
|
+
|
15
|
+
[doc]
|
16
|
+
myst-parser
|
17
|
+
sphinx
|
18
|
+
sphinx-autobuild
|
19
|
+
sphinx-autodoc-typehints
|
20
|
+
sphinx-copybutton
|
21
|
+
|
22
|
+
[lint]
|
23
|
+
pre-commit
|
24
|
+
ruff
|
25
|
+
mypy
|
26
|
+
|
27
|
+
[test]
|
28
|
+
pytest
|
29
|
+
pytest-cov
|
30
|
+
pytest-randomly
|
31
|
+
pytest-xdist
|
32
|
+
scripttest
|
@@ -0,0 +1 @@
|
|
1
|
+
txt2ebook
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# # Copyright (c) 2021,2022,2023,2024,2025 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Affero General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Affero General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
from textwrap import dedent
|
17
|
+
|
18
|
+
import pytest
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.mark.parametrize("option", ["-hn", "--header-number"])
|
22
|
+
def test_header_to_numbers_conversion(cli_runner, infile, option):
|
23
|
+
txtfile = infile("sample_long_headers.txt")
|
24
|
+
|
25
|
+
output = cli_runner("-d", option, txtfile)
|
26
|
+
expected_output1 = dedent(
|
27
|
+
"""\
|
28
|
+
DEBUG: Convert header to numbers: 第一卷 -> 第1卷
|
29
|
+
DEBUG: Convert header to numbers: 第一章 月既不解饮 -> 第1章 月既不解饮
|
30
|
+
DEBUG: Convert header to numbers: 第二章 影徒随我身 -> 第2章 影徒随我身
|
31
|
+
"""
|
32
|
+
)
|
33
|
+
assert expected_output1 in output.stdout
|
34
|
+
|
35
|
+
output = cli_runner("-d", "--header-number", txtfile)
|
36
|
+
expected_output2 = dedent(
|
37
|
+
"""\
|
38
|
+
DEBUG: Convert header to numbers: 第二卷 -> 第2卷
|
39
|
+
DEBUG: Convert header to numbers: 第三章 暂伴月将影 -> 第3章 暂伴月将影
|
40
|
+
DEBUG: Convert header to numbers: 第二百章 暂伴月将影 -> 第200章 暂伴月将
|
41
|
+
DEBUG: Convert header to numbers: 第九百九十九章 暂伴 -> 第999章 暂伴月将
|
42
|
+
DEBUG: Convert header to numbers: 第九千百九十九章 暂 -> 第9919章 暂伴月
|
43
|
+
"""
|
44
|
+
)
|
45
|
+
|
46
|
+
assert expected_output2 in output.stdout
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# # Copyright (c) 2021,2022,2023,2024,2025 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Affero General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Affero General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
|
17
|
+
def test_nonexistent_filename(cli_runner):
|
18
|
+
output = cli_runner("parse", "nonexistent.txt")
|
19
|
+
assert (
|
20
|
+
"[Errno 2] No such file or directory: 'nonexistent.txt'"
|
21
|
+
in output.stderr
|
22
|
+
)
|
23
|
+
|
24
|
+
|
25
|
+
def test_empty_file_content(cli_runner, infile):
|
26
|
+
txt = infile("empty_file.txt")
|
27
|
+
output = cli_runner("parse", str(txt))
|
28
|
+
assert f"error: Empty file content in {str(txt)}" in output.stdout
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# # Copyright (c) 2021,2022,2023,2024,2025 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Affero General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Affero General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
import pytest
|
17
|
+
|
18
|
+
|
19
|
+
def test_auto_detect_language(cli_runner, infile):
|
20
|
+
txtfile = infile("sample.txt")
|
21
|
+
output = cli_runner(txtfile)
|
22
|
+
assert "Detect language: zh-cn" in output.stdout
|
23
|
+
|
24
|
+
|
25
|
+
@pytest.mark.parametrize("option", ["-l", "--language"])
|
26
|
+
def test_warning_log_for_mismatch_configured_and_detect_language(
|
27
|
+
cli_runner, infile, option
|
28
|
+
):
|
29
|
+
txtfile = infile("missing_chapters.txt")
|
30
|
+
output = cli_runner(txtfile, option, "en")
|
31
|
+
assert "Config language: en" in output.stdout
|
32
|
+
assert "Detect language: ko" in output.stdout
|
33
|
+
assert "Config (en) and detect (ko) language mismatch" in output.stdout
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# # Copyright (c) 2021,2022,2023,2024,2025 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Affero General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Affero General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
|
17
|
+
def test_output_basename_default_to_input_basename(
|
18
|
+
tte, infile, outfile
|
19
|
+
):
|
20
|
+
txt = infile("sample.txt")
|
21
|
+
epub = outfile("output/sample.epub")
|
22
|
+
output = tte("epub", str(txt))
|
23
|
+
|
24
|
+
assert epub.exists()
|
25
|
+
assert f"Generate EPUB file: {str(epub)}" in output.stdout
|
26
|
+
|
27
|
+
|
28
|
+
def test_set_output_filename(tte, infile, outfile):
|
29
|
+
txt = infile("sample.txt")
|
30
|
+
epub = outfile("foobar.epub")
|
31
|
+
output = tte("epub", str(txt), str(epub))
|
32
|
+
|
33
|
+
assert epub.exists()
|
34
|
+
assert f"Generate EPUB file: {str(epub)}" in output.stdout
|