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.
Files changed (73) hide show
  1. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/PKG-INFO +34 -18
  2. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/pyproject.toml +26 -19
  3. txt2ebook-0.1.157/setup.cfg +4 -0
  4. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/massage.py +30 -9
  5. txt2ebook-0.1.157/src/txt2ebook.egg-info/PKG-INFO +169 -0
  6. txt2ebook-0.1.157/src/txt2ebook.egg-info/SOURCES.txt +61 -0
  7. txt2ebook-0.1.157/src/txt2ebook.egg-info/dependency_links.txt +1 -0
  8. txt2ebook-0.1.157/src/txt2ebook.egg-info/entry_points.txt +3 -0
  9. txt2ebook-0.1.157/src/txt2ebook.egg-info/requires.txt +32 -0
  10. txt2ebook-0.1.157/src/txt2ebook.egg-info/top_level.txt +1 -0
  11. txt2ebook-0.1.157/tests/test_header_number_flag.py +46 -0
  12. txt2ebook-0.1.157/tests/test_input_file_arg.py +28 -0
  13. txt2ebook-0.1.157/tests/test_language_option.py +33 -0
  14. txt2ebook-0.1.157/tests/test_output_file_arg.py +34 -0
  15. txt2ebook-0.1.157/tests/test_overwrite_flag.py +23 -0
  16. txt2ebook-0.1.157/tests/test_parser.py +94 -0
  17. txt2ebook-0.1.157/tests/test_purge_flag.py +49 -0
  18. txt2ebook-0.1.157/tests/test_quiet_flag.py +24 -0
  19. txt2ebook-0.1.157/tests/test_sort_volume_and_chapter_flag.py +44 -0
  20. txt2ebook-0.1.157/tests/test_split_volume_and_chapter_flag.py +51 -0
  21. txt2ebook-0.1.157/tests/test_test_parsing_flag.py +28 -0
  22. txt2ebook-0.1.157/tests/test_tokenizer.py +127 -0
  23. txt2ebook-0.1.157/tests/test_txt2ebook.py +21 -0
  24. txt2ebook-0.1.157/tests/test_verbose_flag.py +92 -0
  25. txt2ebook-0.1.157/tests/test_volume_page_flag.py +23 -0
  26. txt2ebook-0.1.155/src/txt2ebook/formats/templates/epub/clean.css +0 -41
  27. txt2ebook-0.1.155/src/txt2ebook/formats/templates/epub/condense.css +0 -42
  28. txt2ebook-0.1.155/src/txt2ebook/formats/templates/epub/noindent.css +0 -42
  29. txt2ebook-0.1.155/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
  30. txt2ebook-0.1.155/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -31
  31. txt2ebook-0.1.155/src/txt2ebook/locales/txt2ebook.pot +0 -31
  32. txt2ebook-0.1.155/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
  33. txt2ebook-0.1.155/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -31
  34. txt2ebook-0.1.155/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
  35. txt2ebook-0.1.155/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -31
  36. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/LICENSE.md +0 -0
  37. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/README.md +0 -0
  38. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/__init__.py +0 -0
  39. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/__main__.py +0 -0
  40. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/cli.py +0 -0
  41. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/exceptions.py +0 -0
  42. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/__init__.py +0 -0
  43. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/base.py +0 -0
  44. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/epub.py +0 -0
  45. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/gmi.py +0 -0
  46. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/md.py +0 -0
  47. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/pdf.py +0 -0
  48. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/templates/__init__.py +0 -0
  49. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
  50. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/tex.py +0 -0
  51. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/txt.py +0 -0
  52. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/formats/typ.py +0 -0
  53. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/helpers/__init__.py +0 -0
  54. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/__init__.py +0 -0
  55. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/en.py +0 -0
  56. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/zh_cn.py +0 -0
  57. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/languages/zh_tw.py +0 -0
  58. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/__init__.py +0 -0
  59. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/book.py +0 -0
  60. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/chapter.py +0 -0
  61. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/models/volume.py +0 -0
  62. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/parser.py +0 -0
  63. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/__init__.py +0 -0
  64. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/env.py +0 -0
  65. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/epub.py +0 -0
  66. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/gmi.py +0 -0
  67. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/md.py +0 -0
  68. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/parse.py +0 -0
  69. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/pdf.py +0 -0
  70. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/tex.py +0 -0
  71. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/subcommands/typ.py +0 -0
  72. {txt2ebook-0.1.155 → txt2ebook-0.1.157}/src/txt2ebook/tokenizer.py +0 -0
  73. {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.155
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
- 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,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>=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
+ 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.155"
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 = {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",
@@ -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
- [dependency-groups]
64
- dev = [
65
- "babel",
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
- "sphinx",
82
- "vulture",
77
+ ]
78
+
79
+ lint = [
80
+ "pre-commit",
81
+ "ruff",
82
+ "mypy",
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
+
@@ -210,7 +210,7 @@ def run(args: argparse.Namespace) -> None:
210
210
  """Run massage subcommand.
211
211
 
212
212
  Args:
213
- args (argparse.Namespace): args.from command line arguments
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
- content (str): The formatted book content.
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
- modified_content = re.sub(r"\n+", "\n\n", content)
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
- content (str): The formatted book content.
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
- # We don't remove empty line and keep all formatting as it.
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,3 @@
1
+ [console_scripts]
2
+ tte = txt2ebook.cli:main
3
+ txt2ebook = txt2ebook.cli:main
@@ -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