txt2ebook 0.1.65__py3-none-any.whl → 0.1.67__py3-none-any.whl
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/__init__.py +1 -1
- txt2ebook/formats/__init__.py +3 -1
- txt2ebook/formats/typ.py +165 -0
- {txt2ebook-0.1.65.dist-info → txt2ebook-0.1.67.dist-info}/METADATA +6 -3
- {txt2ebook-0.1.65.dist-info → txt2ebook-0.1.67.dist-info}/RECORD +8 -7
- {txt2ebook-0.1.65.dist-info → txt2ebook-0.1.67.dist-info}/LICENSE.md +0 -0
- {txt2ebook-0.1.65.dist-info → txt2ebook-0.1.67.dist-info}/WHEEL +0 -0
- {txt2ebook-0.1.65.dist-info → txt2ebook-0.1.67.dist-info}/entry_points.txt +0 -0
txt2ebook/__init__.py
CHANGED
txt2ebook/formats/__init__.py
CHANGED
@@ -25,10 +25,11 @@ from txt2ebook.formats.gmi import GmiWriter
|
|
25
25
|
from txt2ebook.formats.md import MdWriter
|
26
26
|
from txt2ebook.formats.pdf import PdfWriter
|
27
27
|
from txt2ebook.formats.txt import TxtWriter
|
28
|
+
from txt2ebook.formats.typ import TypWriter
|
28
29
|
from txt2ebook.helpers import load_class, to_classname
|
29
30
|
from txt2ebook.models import Book
|
30
31
|
|
31
|
-
EBOOK_FORMATS = ["epub", "gmi", "md", "pdf", "txt"]
|
32
|
+
EBOOK_FORMATS = ["epub", "gmi", "md", "pdf", "txt", "typ"]
|
32
33
|
PAGE_SIZES = [
|
33
34
|
"a0",
|
34
35
|
"a1",
|
@@ -82,4 +83,5 @@ __all__ = [
|
|
82
83
|
"PAGE_SIZES",
|
83
84
|
"PdfWriter",
|
84
85
|
"TxtWriter",
|
86
|
+
"TypWriter",
|
85
87
|
]
|
txt2ebook/formats/typ.py
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# Copyright (C) 2021,2022,2023 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
|
+
"""Convert and back source text file into text as well."""
|
17
|
+
|
18
|
+
import importlib.resources as importlib_res
|
19
|
+
import logging
|
20
|
+
import textwrap
|
21
|
+
|
22
|
+
import importlib_resources
|
23
|
+
import typst
|
24
|
+
|
25
|
+
from txt2ebook.formats.base import BaseWriter
|
26
|
+
from txt2ebook.models import Chapter, Volume
|
27
|
+
|
28
|
+
# workaround for Python 3.8
|
29
|
+
# see https://github.com/messense/typst-py/issues/12#issuecomment-1812956252
|
30
|
+
setattr(importlib_res, "files", importlib_resources.files)
|
31
|
+
setattr(importlib_res, "as_file", importlib_resources.as_file)
|
32
|
+
|
33
|
+
logger = logging.getLogger(__name__)
|
34
|
+
|
35
|
+
|
36
|
+
class TypWriter(BaseWriter):
|
37
|
+
"""Module for writing ebook in Typst (typ) format."""
|
38
|
+
|
39
|
+
def write(self) -> None:
|
40
|
+
"""Generate Typst files."""
|
41
|
+
self._new_file()
|
42
|
+
|
43
|
+
def _new_file(self) -> None:
|
44
|
+
new_filename = self._output_filename(".typ")
|
45
|
+
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
46
|
+
|
47
|
+
with open(new_filename, "w", encoding="utf8") as file:
|
48
|
+
logger.info("Generate Typst file: %s", new_filename.resolve())
|
49
|
+
file.write(self._to_typ())
|
50
|
+
|
51
|
+
pdf_filename = new_filename.with_suffix(".pdf")
|
52
|
+
logger.info("Generate PDF file: %s", pdf_filename.resolve())
|
53
|
+
typst.compile(new_filename, output=pdf_filename)
|
54
|
+
|
55
|
+
def _to_typ(self) -> str:
|
56
|
+
return (
|
57
|
+
self._to_metadata_typ()
|
58
|
+
+ self._to_cover()
|
59
|
+
+ self._to_outline()
|
60
|
+
+ '#set page(numbering: "1")'
|
61
|
+
+ "\n"
|
62
|
+
+ "#counter(page).update(1)"
|
63
|
+
+ "\n"
|
64
|
+
+ self._to_body_txt()
|
65
|
+
)
|
66
|
+
|
67
|
+
def _to_cover(self) -> str:
|
68
|
+
return textwrap.dedent(
|
69
|
+
f"""
|
70
|
+
#set page(paper: "a5", numbering: none)
|
71
|
+
#align(center, text(17pt)[{self.book.title}])
|
72
|
+
#pagebreak()
|
73
|
+
|
74
|
+
"""
|
75
|
+
)
|
76
|
+
|
77
|
+
def _to_outline(self) -> str:
|
78
|
+
return (
|
79
|
+
textwrap.dedent(
|
80
|
+
"""
|
81
|
+
#set page(paper: "a5", numbering: none)
|
82
|
+
#outline(title: [目录], indent: 2em,)
|
83
|
+
#pagebreak()
|
84
|
+
"""
|
85
|
+
)
|
86
|
+
if self.config.with_toc
|
87
|
+
else ""
|
88
|
+
)
|
89
|
+
|
90
|
+
def _to_metadata_typ(self) -> str:
|
91
|
+
return textwrap.dedent(
|
92
|
+
"""
|
93
|
+
#set page(
|
94
|
+
paper: "a5",
|
95
|
+
margin: (x: 2.5cm, y: 2.5cm),
|
96
|
+
numbering: "1",
|
97
|
+
number-align: right,
|
98
|
+
)
|
99
|
+
#show heading.where(
|
100
|
+
level: 1
|
101
|
+
): it => block(width: 100%)[
|
102
|
+
#set align(center)
|
103
|
+
#set text(12pt, weight: "regular")
|
104
|
+
#smallcaps(it.body)
|
105
|
+
]
|
106
|
+
|
107
|
+
#show heading.where(
|
108
|
+
level: 2
|
109
|
+
): it => block(width: 100%)[
|
110
|
+
#set align(center)
|
111
|
+
#set text(12pt, weight: "regular")
|
112
|
+
#smallcaps(it.body)
|
113
|
+
]
|
114
|
+
|
115
|
+
#set par(
|
116
|
+
justify: true,
|
117
|
+
leading: 1em,
|
118
|
+
first-line-indent: 2em,
|
119
|
+
)
|
120
|
+
#set text(
|
121
|
+
font: "Noto Serif CJK SC",
|
122
|
+
size: 11pt,
|
123
|
+
)
|
124
|
+
|
125
|
+
"""
|
126
|
+
)
|
127
|
+
|
128
|
+
def _to_body_txt(self) -> str:
|
129
|
+
content = []
|
130
|
+
for section in self.book.toc:
|
131
|
+
if isinstance(section, Volume):
|
132
|
+
content.append(self._to_volume_txt(section))
|
133
|
+
if isinstance(section, Chapter):
|
134
|
+
content.append(self._to_chapter_txt(section))
|
135
|
+
|
136
|
+
return f"{self.config.paragraph_separator}".join(content)
|
137
|
+
|
138
|
+
def _to_volume_txt(self, volume) -> str:
|
139
|
+
return (
|
140
|
+
f"= {volume.title}"
|
141
|
+
+ self.config.paragraph_separator
|
142
|
+
+ self.config.paragraph_separator.join(
|
143
|
+
[
|
144
|
+
self._to_chapter_txt(chapter, True)
|
145
|
+
for chapter in volume.chapters
|
146
|
+
]
|
147
|
+
)
|
148
|
+
)
|
149
|
+
|
150
|
+
def _to_chapter_txt(self, chapter, part_of_volume=False) -> str:
|
151
|
+
header = "==" if part_of_volume else "="
|
152
|
+
return (
|
153
|
+
f"{header} {chapter.title}"
|
154
|
+
+ self.config.paragraph_separator
|
155
|
+
+ self.config.paragraph_separator.join(chapter.paragraphs)
|
156
|
+
+ "#pagebreak()"
|
157
|
+
)
|
158
|
+
|
159
|
+
def _to_volume_chapter_txt(self, volume, chapter) -> str:
|
160
|
+
return (
|
161
|
+
f"= {volume.title} {chapter.title}"
|
162
|
+
+ self.config.paragraph_separator
|
163
|
+
+ self.config.paragraph_separator.join(chapter.paragraphs)
|
164
|
+
+ "#pagebreak()"
|
165
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: txt2ebook
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.67
|
4
4
|
Summary: CLI tool to convert txt file to ebook format
|
5
5
|
Home-page: https://github.com/kianmeng/txt2ebook
|
6
6
|
License: AGPL-3.0-or-later
|
@@ -29,11 +29,13 @@ Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
29
29
|
Requires-Dist: CJKwrap (>=2.2,<3.0)
|
30
30
|
Requires-Dist: EbookLib (>=0.17.1,<0.18.0)
|
31
31
|
Requires-Dist: bs4 (>=0.0.1,<0.0.2)
|
32
|
+
Requires-Dist: importlib-resources (>=6.1.1,<7.0.0)
|
32
33
|
Requires-Dist: langdetect (>=1.0.9,<2.0.0)
|
33
34
|
Requires-Dist: pypandoc (>=1.11,<2.0)
|
34
35
|
Requires-Dist: regex (>=2021.11.10,<2022.0.0)
|
35
36
|
Requires-Dist: reportlab (>=4.0.0,<5.0.0)
|
36
37
|
Requires-Dist: typing-extensions (>=4.5.0,<5.0.0)
|
38
|
+
Requires-Dist: typst (>=0.10.0,<0.11.0)
|
37
39
|
Project-URL: Repository, https://github.com/kianmeng/txt2ebook
|
38
40
|
Description-Content-Type: text/markdown
|
39
41
|
|
@@ -64,7 +66,8 @@ txt2ebook --help
|
|
64
66
|
```
|
65
67
|
|
66
68
|
```console
|
67
|
-
|
69
|
+
|
70
|
+
usage: txt2ebook [-of OUTPUT_FOLDER] [-p] [-f {epub,gmi,md,pdf,txt,typ
|
68
71
|
[-t TITLE] [-l LANGUAGE] [-a AUTHOR] [-tr TRANSLATOR]
|
69
72
|
[-c IMAGE_FILENAME] [-w WIDTH] [-ff FILENAME_FORMAT]
|
70
73
|
[-ps SEPARATOR] [-pz PAGE_SIZE] [-rd REGEX] [-rvc REGEX]
|
@@ -91,7 +94,7 @@ optional arguments:
|
|
91
94
|
set default output folder (default: 'output')
|
92
95
|
-p, --purge
|
93
96
|
remove converted ebooks specified by --output-folder option (default: 'False')
|
94
|
-
-f {epub,gmi,md,pdf,txt}, --format {epub,gmi,md,pdf,txt}
|
97
|
+
-f {epub,gmi,md,pdf,txt,typ}, --format {epub,gmi,md,pdf,txt,typ}
|
95
98
|
ebook format (default: 'epub')
|
96
99
|
-t TITLE, --title TITLE
|
97
100
|
title of the ebook (default: 'None')
|
@@ -1,7 +1,7 @@
|
|
1
|
-
txt2ebook/__init__.py,sha256=
|
1
|
+
txt2ebook/__init__.py,sha256=XoYe79YmaVaV6agUP-YeNdSwBUa6OXzPa3Q8g6v5ooM,1760
|
2
2
|
txt2ebook/__main__.py,sha256=69yaheH-Fv2VIPS0T9kiVmiMluSgLGvwo1fa7badX2I,845
|
3
3
|
txt2ebook/exceptions.py,sha256=Y6rqjXhiKIxNeKWVqvAqsUAOaBFKxlUE5Tm5EBcoi9w,837
|
4
|
-
txt2ebook/formats/__init__.py,sha256=
|
4
|
+
txt2ebook/formats/__init__.py,sha256=s-d0hdkCpAshcJNxa_7PHUroeFd-p5o1PY6ix5fxjx4,2390
|
5
5
|
txt2ebook/formats/base.py,sha256=4LC8tY2Vw6KjwH-BBDWV2plPjPLCAv6kjphekYx_ofg,5438
|
6
6
|
txt2ebook/formats/epub.py,sha256=M9el5NfM9NEoGb41tg-Fn8BUuyze7L2Umm-rdrSOc9w,6856
|
7
7
|
txt2ebook/formats/gmi.py,sha256=lNHC7m3DMcLNSUZzfDjNUcOVtk6QI_c0pUI1ls7NhJQ,6706
|
@@ -13,6 +13,7 @@ txt2ebook/formats/templates/epub/clean.css,sha256=AnEwMckzUSKcjKsDiWtJW1oaceuklt
|
|
13
13
|
txt2ebook/formats/templates/epub/condense.css,sha256=Fz80ZqkPsFRmGdURduAxqMV8drD0CCUlrv41P8rUsm8,477
|
14
14
|
txt2ebook/formats/templates/epub/noindent.css,sha256=_O5Tv90TKyyPBRdgjuNKFwtKFbdheh2V9PtDhgRUg3U,483
|
15
15
|
txt2ebook/formats/txt.py,sha256=mSTFj3X9_iEKjCCgaUflCSKA5V96TUJ1WFcwbSzIU7Y,7385
|
16
|
+
txt2ebook/formats/typ.py,sha256=v3qAYatehMFCxFBPDtexK7A4s2bbAu2AWaYQDHi_DNg,5017
|
16
17
|
txt2ebook/helpers/__init__.py,sha256=5EFTheYifNYk24rniaehnxd0NRR4K3aakSldZTVot1c,1966
|
17
18
|
txt2ebook/languages/__init__.py,sha256=-c9-YQEkXnb9LKLoi7EtNlQLAw2SqDKVw2UXlWt9Dl0,753
|
18
19
|
txt2ebook/languages/en.py,sha256=xCIhL431eUgoGwci4LCBkso8bR2LBhxKqWCPczKKoJQ,915
|
@@ -33,8 +34,8 @@ txt2ebook/parser.py,sha256=I4g4gOxdc1CyocH7nafhJnA5MGKg8De7w4uSVZLHVKk,11818
|
|
33
34
|
txt2ebook/tokenizer.py,sha256=kzUW-xtAU4zgDxyUmPZZ7HNbjGDeOQX0MYr3_eOuByM,9313
|
34
35
|
txt2ebook/txt2ebook.py,sha256=DJhiEznSEBQVBaDJMm3ArQpRrTXRs7Emb01BFglv1W4,12527
|
35
36
|
txt2ebook/zh_utils.py,sha256=CM6SnsQhJWzSevcJWQ_9Pj028JUGbG3v0AG6xK_KgBY,4877
|
36
|
-
txt2ebook-0.1.
|
37
|
-
txt2ebook-0.1.
|
38
|
-
txt2ebook-0.1.
|
39
|
-
txt2ebook-0.1.
|
40
|
-
txt2ebook-0.1.
|
37
|
+
txt2ebook-0.1.67.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
|
38
|
+
txt2ebook-0.1.67.dist-info/METADATA,sha256=Fo9V8zk7SgJLgWmqqu9gJ9YbzUIMEqvUec-pCg6KaE4,7218
|
39
|
+
txt2ebook-0.1.67.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
40
|
+
txt2ebook-0.1.67.dist-info/entry_points.txt,sha256=IQHyIIhd0MHjSSRVC1a6tMeIoLus8D06KHL_cumvEbg,83
|
41
|
+
txt2ebook-0.1.67.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|