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.
Files changed (73) hide show
  1. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/PKG-INFO +18 -18
  2. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/pyproject.toml +12 -5
  3. txt2ebook-0.1.156/setup.cfg +4 -0
  4. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/epub.py +40 -7
  5. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/tex.py +40 -15
  6. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/typ.py +41 -8
  7. txt2ebook-0.1.156/src/txt2ebook.egg-info/PKG-INFO +153 -0
  8. txt2ebook-0.1.156/src/txt2ebook.egg-info/SOURCES.txt +61 -0
  9. txt2ebook-0.1.156/src/txt2ebook.egg-info/dependency_links.txt +1 -0
  10. txt2ebook-0.1.156/src/txt2ebook.egg-info/entry_points.txt +3 -0
  11. txt2ebook-0.1.156/src/txt2ebook.egg-info/requires.txt +13 -0
  12. txt2ebook-0.1.156/src/txt2ebook.egg-info/top_level.txt +1 -0
  13. txt2ebook-0.1.156/tests/test_header_number_flag.py +46 -0
  14. txt2ebook-0.1.156/tests/test_input_file_arg.py +28 -0
  15. txt2ebook-0.1.156/tests/test_language_option.py +33 -0
  16. txt2ebook-0.1.156/tests/test_output_file_arg.py +34 -0
  17. txt2ebook-0.1.156/tests/test_overwrite_flag.py +23 -0
  18. txt2ebook-0.1.156/tests/test_parser.py +94 -0
  19. txt2ebook-0.1.156/tests/test_purge_flag.py +49 -0
  20. txt2ebook-0.1.156/tests/test_quiet_flag.py +24 -0
  21. txt2ebook-0.1.156/tests/test_sort_volume_and_chapter_flag.py +44 -0
  22. txt2ebook-0.1.156/tests/test_split_volume_and_chapter_flag.py +51 -0
  23. txt2ebook-0.1.156/tests/test_test_parsing_flag.py +28 -0
  24. txt2ebook-0.1.156/tests/test_tokenizer.py +127 -0
  25. txt2ebook-0.1.156/tests/test_txt2ebook.py +21 -0
  26. txt2ebook-0.1.156/tests/test_verbose_flag.py +92 -0
  27. txt2ebook-0.1.156/tests/test_volume_page_flag.py +23 -0
  28. txt2ebook-0.1.154/src/txt2ebook/formats/templates/epub/clean.css +0 -41
  29. txt2ebook-0.1.154/src/txt2ebook/formats/templates/epub/condense.css +0 -42
  30. txt2ebook-0.1.154/src/txt2ebook/formats/templates/epub/noindent.css +0 -42
  31. txt2ebook-0.1.154/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
  32. txt2ebook-0.1.154/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -31
  33. txt2ebook-0.1.154/src/txt2ebook/locales/txt2ebook.pot +0 -31
  34. txt2ebook-0.1.154/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
  35. txt2ebook-0.1.154/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -31
  36. txt2ebook-0.1.154/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
  37. txt2ebook-0.1.154/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -31
  38. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/LICENSE.md +0 -0
  39. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/README.md +0 -0
  40. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/__init__.py +0 -0
  41. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/__main__.py +0 -0
  42. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/cli.py +0 -0
  43. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/exceptions.py +0 -0
  44. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/__init__.py +0 -0
  45. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/base.py +0 -0
  46. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/epub.py +0 -0
  47. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/gmi.py +0 -0
  48. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/md.py +0 -0
  49. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/pdf.py +0 -0
  50. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/templates/__init__.py +0 -0
  51. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
  52. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/tex.py +0 -0
  53. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/txt.py +0 -0
  54. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/formats/typ.py +0 -0
  55. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/helpers/__init__.py +0 -0
  56. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/__init__.py +0 -0
  57. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/en.py +0 -0
  58. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/zh_cn.py +0 -0
  59. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/languages/zh_tw.py +0 -0
  60. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/__init__.py +0 -0
  61. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/book.py +0 -0
  62. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/chapter.py +0 -0
  63. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/models/volume.py +0 -0
  64. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/parser.py +0 -0
  65. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/__init__.py +0 -0
  66. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/env.py +0 -0
  67. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/gmi.py +0 -0
  68. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/massage.py +0 -0
  69. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/md.py +0 -0
  70. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/parse.py +0 -0
  71. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/subcommands/pdf.py +0 -0
  72. {txt2ebook-0.1.154 → txt2ebook-0.1.156}/src/txt2ebook/tokenizer.py +0 -0
  73. {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.154
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
- Requires-Python: ~=3.9
8
- Description-Content-Type: text/markdown
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>=0.17.1,<0.18
29
- Requires-Dist: bs4>=0.0.1,<0.0.2
30
- Requires-Dist: importlib-resources>=6.1.1,<7
31
- Requires-Dist: jieba>=0.42.1,<0.43
32
- Requires-Dist: langdetect>=1.0.9,<2
33
- Requires-Dist: lxml>=5.2.2,<6
34
- Requires-Dist: pylatex>=1.4.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
35
37
  Requires-Dist: pypandoc~=1.11
36
- Requires-Dist: regex>=2021.11.10,<2022
37
- Requires-Dist: reportlab>=4.0.0,<5
38
- Requires-Dist: typing-extensions>=4.5.0,<5
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
- Project-URL: Homepage, https://github.com/kianmeng/txt2ebook
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.154"
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 = {file = "LICENSE.md"}
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 = ["flit_core"]
87
- build-backend = "flit_core.buildapi"
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"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -38,11 +38,10 @@ def build_subparser(subparsers) -> None:
38
38
 
39
39
  epub_parser.add_argument(
40
40
  "input_file",
41
- nargs=None if sys.stdin.isatty() else "?", # type: ignore
41
+ nargs="*",
42
42
  type=argparse.FileType("rb"),
43
- default=None if sys.stdin.isatty() else sys.stdin,
44
- help="source text filename",
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
- book = parse_txt(args)
128
- writer = EpubWriter(book, args)
129
- writer.write()
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=None if sys.stdin.isatty() else "?", # type: ignore
36
+ nargs="*",
37
37
  type=argparse.FileType("rb"),
38
- default=None if sys.stdin.isatty() else sys.stdin,
39
- help="source text filename",
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
- book = parse_txt(args)
125
- writer = TexWriter(book, args)
126
- writer.write()
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=None if sys.stdin.isatty() else "?", # type: ignore
39
+ nargs="*",
40
40
  type=argparse.FileType("rb"),
41
- default=None if sys.stdin.isatty() else sys.stdin,
42
- help="source text filename",
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
- config (argparse.Namespace): Config from command line arguments
128
+ args (argparse.Namespace): Config from command line arguments
130
129
 
131
130
  Returns:
132
131
  None
133
132
  """
134
- book = parse_txt(args)
135
- writer = TypWriter(book, args)
136
- writer.write()
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,3 @@
1
+ [console_scripts]
2
+ tte = txt2ebook.cli:main
3
+ txt2ebook = txt2ebook.cli:main
@@ -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