txt2ebook 0.1.154__tar.gz → 0.1.156__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.154 → txt2ebook-0.1.156}/PKG-INFO +18 -18
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/pyproject.toml +12 -5
- txt2ebook-0.1.156/setup.cfg +4 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/epub.py +40 -7
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/tex.py +40 -15
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/typ.py +41 -8
- txt2ebook-0.1.156/src/txt2ebook.egg-info/PKG-INFO +153 -0
- txt2ebook-0.1.156/src/txt2ebook.egg-info/SOURCES.txt +61 -0
- txt2ebook-0.1.156/src/txt2ebook.egg-info/dependency_links.txt +1 -0
- txt2ebook-0.1.156/src/txt2ebook.egg-info/entry_points.txt +3 -0
- txt2ebook-0.1.156/src/txt2ebook.egg-info/requires.txt +13 -0
- txt2ebook-0.1.156/src/txt2ebook.egg-info/top_level.txt +1 -0
- txt2ebook-0.1.156/tests/test_header_number_flag.py +46 -0
- txt2ebook-0.1.156/tests/test_input_file_arg.py +28 -0
- txt2ebook-0.1.156/tests/test_language_option.py +33 -0
- txt2ebook-0.1.156/tests/test_output_file_arg.py +34 -0
- txt2ebook-0.1.156/tests/test_overwrite_flag.py +23 -0
- txt2ebook-0.1.156/tests/test_parser.py +94 -0
- txt2ebook-0.1.156/tests/test_purge_flag.py +49 -0
- txt2ebook-0.1.156/tests/test_quiet_flag.py +24 -0
- txt2ebook-0.1.156/tests/test_sort_volume_and_chapter_flag.py +44 -0
- txt2ebook-0.1.156/tests/test_split_volume_and_chapter_flag.py +51 -0
- txt2ebook-0.1.156/tests/test_test_parsing_flag.py +28 -0
- txt2ebook-0.1.156/tests/test_tokenizer.py +127 -0
- txt2ebook-0.1.156/tests/test_txt2ebook.py +21 -0
- txt2ebook-0.1.156/tests/test_verbose_flag.py +92 -0
- txt2ebook-0.1.156/tests/test_volume_page_flag.py +23 -0
- txt2ebook-0.1.154/src/txt2ebook/formats/templates/epub/clean.css +0 -41
- txt2ebook-0.1.154/src/txt2ebook/formats/templates/epub/condense.css +0 -42
- txt2ebook-0.1.154/src/txt2ebook/formats/templates/epub/noindent.css +0 -42
- txt2ebook-0.1.154/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
- txt2ebook-0.1.154/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -31
- txt2ebook-0.1.154/src/txt2ebook/locales/txt2ebook.pot +0 -31
- txt2ebook-0.1.154/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
- txt2ebook-0.1.154/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -31
- txt2ebook-0.1.154/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
- txt2ebook-0.1.154/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -31
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/LICENSE.md +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/README.md +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/__main__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/cli.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/exceptions.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/base.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/epub.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/gmi.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/md.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/pdf.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/templates/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/tex.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/txt.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/typ.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/helpers/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/en.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/zh_cn.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/zh_tw.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/book.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/chapter.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/volume.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/parser.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/__init__.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/env.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/gmi.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/massage.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/md.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/parse.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/pdf.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/tokenizer.py +0 -0
- {txt2ebook-0.1.154 → txt2ebook-0.1.156}/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.156
|
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,23 @@ 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
|
-
Project-URL: Repository, https://github.com/kianmeng/txt2ebook
|
42
|
+
Dynamic: license-file
|
42
43
|
|
43
44
|
# txt2ebook
|
44
45
|
|
@@ -150,4 +151,3 @@ The fish logo used in the documentation generated by Sphinx is a public domain
|
|
150
151
|
drawing of Troschel's parrotfish (Chlorurus troschelii Var. A.) from
|
151
152
|
<https://commons.wikimedia.org/entity/M18506436>.
|
152
153
|
18506436>.
|
153
|
-
|
@@ -1,11 +1,12 @@
|
|
1
1
|
[project]
|
2
2
|
name = "txt2ebook"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.156"
|
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",
|
@@ -83,5 +83,12 @@ dev = [
|
|
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"]
|
@@ -38,11 +38,10 @@ def build_subparser(subparsers) -> None:
|
|
38
38
|
|
39
39
|
epub_parser.add_argument(
|
40
40
|
"input_file",
|
41
|
-
nargs=
|
41
|
+
nargs="*",
|
42
42
|
type=argparse.FileType("rb"),
|
43
|
-
|
44
|
-
|
45
|
-
metavar="TXT_FILENAME",
|
43
|
+
help="source text filenames (can use glob patterns)",
|
44
|
+
metavar="TXT_FILENAMES",
|
46
45
|
)
|
47
46
|
|
48
47
|
epub_parser.add_argument(
|
@@ -124,6 +123,40 @@ def run(args: argparse.Namespace) -> None:
|
|
124
123
|
Returns:
|
125
124
|
None
|
126
125
|
"""
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
input_sources = []
|
127
|
+
|
128
|
+
if not sys.stdin.isatty():
|
129
|
+
# piped input, use stdin as the single input source
|
130
|
+
input_sources.append(sys.stdin)
|
131
|
+
elif args.input_file:
|
132
|
+
# multiple file(s)
|
133
|
+
input_sources.extend(args.input_file)
|
134
|
+
else:
|
135
|
+
logger.error("No input files provided.")
|
136
|
+
sys.exit(1)
|
137
|
+
|
138
|
+
if len(input_sources) > 1 and args.output_file:
|
139
|
+
logger.error(
|
140
|
+
"Cannot specify a single output file when "
|
141
|
+
"processing multiple input files."
|
142
|
+
)
|
143
|
+
sys.exit(1)
|
144
|
+
|
145
|
+
for i, current_input_stream in enumerate(input_sources):
|
146
|
+
# ensures that `input_file` and `output_file` are correctly isolated
|
147
|
+
current_file_args = argparse.Namespace(**vars(args))
|
148
|
+
current_file_args.input_file = current_input_stream
|
149
|
+
|
150
|
+
# if an explicit output_file was provided, it must apply to the first
|
151
|
+
# input
|
152
|
+
if i > 0 and args.output_file:
|
153
|
+
current_file_args.output_file = None
|
154
|
+
|
155
|
+
book = parse_txt(current_file_args)
|
156
|
+
writer = EpubWriter(book, current_file_args)
|
157
|
+
writer.write()
|
158
|
+
|
159
|
+
# close the file stream if it was opened by argparse.FileType and is
|
160
|
+
# not sys.stdin.
|
161
|
+
if current_input_stream is not sys.stdin:
|
162
|
+
current_input_stream.close()
|
@@ -33,19 +33,10 @@ def build_subparser(subparsers) -> None:
|
|
33
33
|
|
34
34
|
tex_parser.add_argument(
|
35
35
|
"input_file",
|
36
|
-
nargs=
|
36
|
+
nargs="*",
|
37
37
|
type=argparse.FileType("rb"),
|
38
|
-
|
39
|
-
|
40
|
-
metavar="TXT_FILENAME",
|
41
|
-
)
|
42
|
-
|
43
|
-
tex_parser.add_argument(
|
44
|
-
"output_file",
|
45
|
-
nargs="?",
|
46
|
-
default=None,
|
47
|
-
help="converted ebook filename (default: 'TXT_FILENAME.pdf')",
|
48
|
-
metavar="EBOOK_FILENAME",
|
38
|
+
help="source text filenames (can use glob patterns)",
|
39
|
+
metavar="TXT_FILENAMES",
|
49
40
|
)
|
50
41
|
|
51
42
|
tex_parser.add_argument(
|
@@ -121,6 +112,40 @@ def run(args: argparse.Namespace) -> None:
|
|
121
112
|
Returns:
|
122
113
|
None
|
123
114
|
"""
|
124
|
-
|
125
|
-
|
126
|
-
|
115
|
+
input_sources = []
|
116
|
+
|
117
|
+
if not sys.stdin.isatty():
|
118
|
+
# piped input, use stdin as the single input source
|
119
|
+
input_sources.append(sys.stdin)
|
120
|
+
elif args.input_file:
|
121
|
+
# multiple file(s)
|
122
|
+
input_sources.extend(args.input_file)
|
123
|
+
else:
|
124
|
+
logger.error("No input files provided.")
|
125
|
+
sys.exit(1)
|
126
|
+
|
127
|
+
if len(input_sources) > 1 and args.output_file:
|
128
|
+
logger.error(
|
129
|
+
"Cannot specify a single output file when "
|
130
|
+
"processing multiple input files."
|
131
|
+
)
|
132
|
+
sys.exit(1)
|
133
|
+
|
134
|
+
for i, current_input_stream in enumerate(input_sources):
|
135
|
+
# ensures that `input_file` and `output_file` are correctly isolated
|
136
|
+
current_file_args = argparse.Namespace(**vars(args))
|
137
|
+
current_file_args.input_file = current_input_stream
|
138
|
+
|
139
|
+
# if an explicit output_file was provided, it must apply to the first
|
140
|
+
# input
|
141
|
+
if i > 0 and args.output_file:
|
142
|
+
current_file_args.output_file = None
|
143
|
+
|
144
|
+
book = parse_txt(current_file_args)
|
145
|
+
writer = TexWriter(book, current_file_args)
|
146
|
+
writer.write()
|
147
|
+
|
148
|
+
# close the file stream if it was opened by argparse.FileType and is
|
149
|
+
# not sys.stdin.
|
150
|
+
if current_input_stream is not sys.stdin:
|
151
|
+
current_input_stream.close()
|
@@ -36,11 +36,10 @@ def build_subparser(subparsers) -> None:
|
|
36
36
|
|
37
37
|
typ_parser.add_argument(
|
38
38
|
"input_file",
|
39
|
-
nargs=
|
39
|
+
nargs="*",
|
40
40
|
type=argparse.FileType("rb"),
|
41
|
-
|
42
|
-
|
43
|
-
metavar="TXT_FILENAME",
|
41
|
+
help="source text filenames (can use glob patterns)",
|
42
|
+
metavar="TXT_FILENAMES",
|
44
43
|
)
|
45
44
|
|
46
45
|
typ_parser.add_argument(
|
@@ -126,11 +125,45 @@ def run(args: argparse.Namespace) -> None:
|
|
126
125
|
"""Run typ subcommand.
|
127
126
|
|
128
127
|
Args:
|
129
|
-
|
128
|
+
args (argparse.Namespace): Config from command line arguments
|
130
129
|
|
131
130
|
Returns:
|
132
131
|
None
|
133
132
|
"""
|
134
|
-
|
135
|
-
|
136
|
-
|
133
|
+
input_sources = []
|
134
|
+
|
135
|
+
if not sys.stdin.isatty():
|
136
|
+
# piped input, use stdin as the single input source
|
137
|
+
input_sources.append(sys.stdin)
|
138
|
+
elif args.input_file:
|
139
|
+
# multiple file(s)
|
140
|
+
input_sources.extend(args.input_file)
|
141
|
+
else:
|
142
|
+
logger.error("No input files provided.")
|
143
|
+
sys.exit(1)
|
144
|
+
|
145
|
+
if len(input_sources) > 1 and args.output_file:
|
146
|
+
logger.error(
|
147
|
+
"Cannot specify a single output file when "
|
148
|
+
"processing multiple input files."
|
149
|
+
)
|
150
|
+
sys.exit(1)
|
151
|
+
|
152
|
+
for i, current_input_stream in enumerate(input_sources):
|
153
|
+
# ensures that `input_file` and `output_file` are correctly isolated
|
154
|
+
current_file_args = argparse.Namespace(**vars(args))
|
155
|
+
current_file_args.input_file = current_input_stream
|
156
|
+
|
157
|
+
# if an explicit output_file was provided, it must apply to the first
|
158
|
+
# input
|
159
|
+
if i > 0 and args.output_file:
|
160
|
+
current_file_args.output_file = None
|
161
|
+
|
162
|
+
book = parse_txt(current_file_args)
|
163
|
+
writer = TypWriter(book, current_file_args)
|
164
|
+
writer.write()
|
165
|
+
|
166
|
+
# close the file stream if it was opened by argparse.FileType and is
|
167
|
+
# not sys.stdin.
|
168
|
+
if current_input_stream is not sys.stdin:
|
169
|
+
current_input_stream.close()
|
@@ -0,0 +1,153 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: txt2ebook
|
3
|
+
Version: 0.1.156
|
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
|
+
Dynamic: license-file
|
43
|
+
|
44
|
+
# txt2ebook
|
45
|
+
|
46
|
+
A console tool to convert txt file to different ebook formats.
|
47
|
+
|
48
|
+
## Installation
|
49
|
+
|
50
|
+
Stable version From PyPI:
|
51
|
+
|
52
|
+
```console
|
53
|
+
uv tool install txt2ebook
|
54
|
+
```
|
55
|
+
|
56
|
+
Upgrade to latest stable version:
|
57
|
+
|
58
|
+
```console
|
59
|
+
uv tool upgrade txt2ebook
|
60
|
+
```
|
61
|
+
|
62
|
+
## Usage
|
63
|
+
|
64
|
+
Showing help message of command-line options:
|
65
|
+
|
66
|
+
```console
|
67
|
+
txt2ebook --help
|
68
|
+
```
|
69
|
+
|
70
|
+
<!--help !-->
|
71
|
+
|
72
|
+
```console
|
73
|
+
usage: txt2ebook [-of OUTPUT_FOLDER] [-p] [-l LANGUAGE] [-rw] [-q] [-v] [-d]
|
74
|
+
[-h] [-V]
|
75
|
+
{env,epub,gmi,massage,md,parse,pdf,tex,typ} ...
|
76
|
+
|
77
|
+
txt2ebook/tte is a cli tool to convert txt file to ebook format.
|
78
|
+
|
79
|
+
website: https://github.com/kianmeng/txt2ebook
|
80
|
+
changelog: https://github.com/kianmeng/txt2ebook/blob/master/CHANGELOG.md
|
81
|
+
issues: https://github.com/kianmeng/txt2ebook/issues
|
82
|
+
|
83
|
+
positional arguments:
|
84
|
+
{env,epub,gmi,massage,md,parse,pdf,tex,typ}
|
85
|
+
sub-command help
|
86
|
+
env
|
87
|
+
print environment information for bug reporting
|
88
|
+
epub
|
89
|
+
generate ebook in EPUB format
|
90
|
+
gmi
|
91
|
+
generate ebook in Gemtext format
|
92
|
+
massage
|
93
|
+
massage the source txt file
|
94
|
+
md
|
95
|
+
generate ebook in Markdown format
|
96
|
+
parse
|
97
|
+
parse and validate the txt file
|
98
|
+
pdf
|
99
|
+
generate ebook in Markdown format
|
100
|
+
tex
|
101
|
+
generate ebook in TeX/PDF format
|
102
|
+
typ
|
103
|
+
generate ebook in Typst format
|
104
|
+
|
105
|
+
options:
|
106
|
+
-of, --output-folder OUTPUT_FOLDER
|
107
|
+
set default output folder (default: 'output')
|
108
|
+
-p, --purge
|
109
|
+
remove converted ebooks specified by --output-folder option (default: 'False')
|
110
|
+
-l, --language LANGUAGE
|
111
|
+
language of the ebook (default: 'None')
|
112
|
+
-rw, --raise-on-warning
|
113
|
+
raise exception and stop parsing upon warning
|
114
|
+
-q, --quiet
|
115
|
+
suppress all logging
|
116
|
+
-v, --verbose
|
117
|
+
show verbosity of debugging log, use -vv, -vvv for more details
|
118
|
+
-d, --debug
|
119
|
+
show debugging log and stacktrace
|
120
|
+
-h, --help
|
121
|
+
show this help message and exit
|
122
|
+
-V, --version
|
123
|
+
show program's version number and exit
|
124
|
+
```
|
125
|
+
|
126
|
+
<!--help !-->
|
127
|
+
|
128
|
+
Convert a txt file into epub:
|
129
|
+
|
130
|
+
```console
|
131
|
+
txt2ebook ebook.txt
|
132
|
+
```
|
133
|
+
|
134
|
+
## Copyright and License
|
135
|
+
|
136
|
+
Copyright (c) 2021,2022,2023,2024,2025 Kian-Meng Ang
|
137
|
+
|
138
|
+
This program is free software: you can redistribute it and/or modify it under
|
139
|
+
the terms of the GNU Affero General Public License as published by the Free
|
140
|
+
Software Foundation, either version 3 of the License, or (at your option) any
|
141
|
+
later version.
|
142
|
+
|
143
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
144
|
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
145
|
+
PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
146
|
+
|
147
|
+
You should have received a copy of the GNU Affero General Public License along
|
148
|
+
with this program. If not, see <https://www.gnu.org/licenses/>.
|
149
|
+
|
150
|
+
The fish logo used in the documentation generated by Sphinx is a public domain
|
151
|
+
drawing of Troschel's parrotfish (Chlorurus troschelii Var. A.) from
|
152
|
+
<https://commons.wikimedia.org/entity/M18506436>.
|
153
|
+
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,13 @@
|
|
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
|
@@ -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
|