yomitoku 0.9.1__py3-none-any.whl → 0.9.3__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.
- yomitoku/cli/main.py +49 -8
- yomitoku/cli/{mcp.py → mcp_server.py} +37 -6
- yomitoku/constants.py +1 -1
- yomitoku/utils/searchable_pdf.py +119 -0
- {yomitoku-0.9.1.dist-info → yomitoku-0.9.3.dist-info}/METADATA +5 -3
- {yomitoku-0.9.1.dist-info → yomitoku-0.9.3.dist-info}/RECORD +8 -7
- {yomitoku-0.9.1.dist-info → yomitoku-0.9.3.dist-info}/entry_points.txt +1 -1
- {yomitoku-0.9.1.dist-info → yomitoku-0.9.3.dist-info}/WHEEL +0 -0
yomitoku/cli/main.py
CHANGED
@@ -9,6 +9,7 @@ from ..constants import SUPPORT_OUTPUT_FORMAT
|
|
9
9
|
from ..data.functions import load_image, load_pdf
|
10
10
|
from ..document_analyzer import DocumentAnalyzer
|
11
11
|
from ..utils.logger import set_logger
|
12
|
+
from ..utils.searchable_pdf import create_searchable_pdf
|
12
13
|
|
13
14
|
from ..export import save_csv, save_html, save_json, save_markdown
|
14
15
|
from ..export import convert_json, convert_csv, convert_html, convert_markdown
|
@@ -48,10 +49,15 @@ def merge_all_pages(results):
|
|
48
49
|
else:
|
49
50
|
out += "\n" + data
|
50
51
|
|
52
|
+
elif format == "pdf":
|
53
|
+
if out is None:
|
54
|
+
out = [data]
|
55
|
+
else:
|
56
|
+
out.append(data)
|
51
57
|
return out
|
52
58
|
|
53
59
|
|
54
|
-
def save_merged_file(out_path, args, out):
|
60
|
+
def save_merged_file(out_path, args, out, imgs):
|
55
61
|
if args.format == "json":
|
56
62
|
save_json(out, out_path, args.encoding)
|
57
63
|
elif args.format == "csv":
|
@@ -60,6 +66,13 @@ def save_merged_file(out_path, args, out):
|
|
60
66
|
save_html(out, out_path, args.encoding)
|
61
67
|
elif args.format == "md":
|
62
68
|
save_markdown(out, out_path, args.encoding)
|
69
|
+
elif args.format == "pdf":
|
70
|
+
create_searchable_pdf(
|
71
|
+
imgs,
|
72
|
+
out,
|
73
|
+
output_path=out_path,
|
74
|
+
font_path=args.font_path,
|
75
|
+
)
|
63
76
|
|
64
77
|
|
65
78
|
def validate_encoding(encoding):
|
@@ -80,7 +93,7 @@ def process_single_file(args, analyzer, path, format):
|
|
80
93
|
else:
|
81
94
|
imgs = load_image(path)
|
82
95
|
|
83
|
-
|
96
|
+
format_results = []
|
84
97
|
for page, img in enumerate(imgs):
|
85
98
|
result, ocr, layout = analyzer(img)
|
86
99
|
dirname = path.parent.name
|
@@ -130,7 +143,7 @@ def process_single_file(args, analyzer, path, format):
|
|
130
143
|
figure_dir=args.figure_dir,
|
131
144
|
)
|
132
145
|
|
133
|
-
|
146
|
+
format_results.append(
|
134
147
|
{
|
135
148
|
"format": format,
|
136
149
|
"data": json.model_dump(),
|
@@ -157,7 +170,7 @@ def process_single_file(args, analyzer, path, format):
|
|
157
170
|
figure_dir=args.figure_dir,
|
158
171
|
)
|
159
172
|
|
160
|
-
|
173
|
+
format_results.append(
|
161
174
|
{
|
162
175
|
"format": format,
|
163
176
|
"data": csv,
|
@@ -188,7 +201,7 @@ def process_single_file(args, analyzer, path, format):
|
|
188
201
|
encoding=args.encoding,
|
189
202
|
)
|
190
203
|
|
191
|
-
|
204
|
+
format_results.append(
|
192
205
|
{
|
193
206
|
"format": format,
|
194
207
|
"data": html,
|
@@ -219,20 +232,36 @@ def process_single_file(args, analyzer, path, format):
|
|
219
232
|
encoding=args.encoding,
|
220
233
|
)
|
221
234
|
|
222
|
-
|
235
|
+
format_results.append(
|
223
236
|
{
|
224
237
|
"format": format,
|
225
238
|
"data": md,
|
226
239
|
}
|
227
240
|
)
|
241
|
+
elif format == "pdf":
|
242
|
+
if not args.combine:
|
243
|
+
create_searchable_pdf(
|
244
|
+
[img],
|
245
|
+
[result],
|
246
|
+
output_path=out_path,
|
247
|
+
font_path=args.font_path,
|
248
|
+
)
|
249
|
+
|
250
|
+
format_results.append(
|
251
|
+
{
|
252
|
+
"format": format,
|
253
|
+
"data": result,
|
254
|
+
}
|
255
|
+
)
|
228
256
|
|
229
|
-
out = merge_all_pages(
|
257
|
+
out = merge_all_pages(format_results)
|
230
258
|
if args.combine:
|
231
259
|
out_path = os.path.join(args.outdir, f"{dirname}_{filename}.{format}")
|
232
260
|
save_merged_file(
|
233
261
|
out_path,
|
234
262
|
args,
|
235
263
|
out,
|
264
|
+
imgs,
|
236
265
|
)
|
237
266
|
|
238
267
|
|
@@ -349,7 +378,12 @@ def main():
|
|
349
378
|
type=str,
|
350
379
|
choices=["auto", "left2right", "top2bottom", "right2left"],
|
351
380
|
)
|
352
|
-
|
381
|
+
parser.add_argument(
|
382
|
+
"--font_path",
|
383
|
+
default=None,
|
384
|
+
type=str,
|
385
|
+
help="Path to the font file(.ttf) for PDF output",
|
386
|
+
)
|
353
387
|
args = parser.parse_args()
|
354
388
|
|
355
389
|
path = Path(args.arg1)
|
@@ -362,6 +396,13 @@ def main():
|
|
362
396
|
f"Invalid output format: {args.format}. Supported formats are {SUPPORT_OUTPUT_FORMAT}"
|
363
397
|
)
|
364
398
|
|
399
|
+
if (
|
400
|
+
args.font_path is not None
|
401
|
+
and not os.path.exists(args.font_path)
|
402
|
+
and format == "pdf"
|
403
|
+
):
|
404
|
+
raise FileNotFoundError(f"Font file not found: {args.font_path}")
|
405
|
+
|
365
406
|
validate_encoding(args.encoding)
|
366
407
|
|
367
408
|
if format == "markdown":
|
@@ -1,14 +1,20 @@
|
|
1
|
-
import json
|
2
|
-
import io
|
3
1
|
import csv
|
2
|
+
import io
|
3
|
+
import json
|
4
4
|
import os
|
5
|
+
from argparse import ArgumentParser
|
5
6
|
from pathlib import Path
|
6
7
|
|
7
8
|
from mcp.server.fastmcp import Context, FastMCP
|
8
9
|
|
9
10
|
from yomitoku import DocumentAnalyzer
|
10
11
|
from yomitoku.data.functions import load_image, load_pdf
|
11
|
-
from yomitoku.export import
|
12
|
+
from yomitoku.export import (
|
13
|
+
convert_csv,
|
14
|
+
convert_html,
|
15
|
+
convert_json,
|
16
|
+
convert_markdown,
|
17
|
+
)
|
12
18
|
|
13
19
|
try:
|
14
20
|
RESOURCE_DIR = os.environ["RESOURCE_DIR"]
|
@@ -154,12 +160,37 @@ async def get_file_list() -> list[str]:
|
|
154
160
|
return os.listdir(RESOURCE_DIR)
|
155
161
|
|
156
162
|
|
157
|
-
def run_mcp_server():
|
163
|
+
def run_mcp_server(transport="stdio", mount_path=None):
|
158
164
|
"""
|
159
165
|
Run the MCP server.
|
160
166
|
"""
|
161
|
-
|
167
|
+
|
168
|
+
if transport == "stdio":
|
169
|
+
mcp.run()
|
170
|
+
elif transport == "sse":
|
171
|
+
mcp.run(transport=transport, mount_path=mount_path)
|
172
|
+
|
173
|
+
|
174
|
+
def main():
|
175
|
+
parser = ArgumentParser(description="Run the MCP server.")
|
176
|
+
parser.add_argument(
|
177
|
+
"--transport",
|
178
|
+
"-t",
|
179
|
+
type=str,
|
180
|
+
default="stdio",
|
181
|
+
choices=["stdio", "sse"],
|
182
|
+
help="Transport method for the MCP server.",
|
183
|
+
)
|
184
|
+
parser.add_argument(
|
185
|
+
"--mount_path",
|
186
|
+
"-m",
|
187
|
+
type=str,
|
188
|
+
default=None,
|
189
|
+
help="Mount path for the MCP server (only used with SSE transport).",
|
190
|
+
)
|
191
|
+
args = parser.parse_args()
|
192
|
+
run_mcp_server(transport=args.transport, mount_path=args.mount_path)
|
162
193
|
|
163
194
|
|
164
195
|
if __name__ == "__main__":
|
165
|
-
|
196
|
+
main()
|
yomitoku/constants.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
4
|
-
SUPPORT_OUTPUT_FORMAT = ["json", "csv", "html", "markdown", "md"]
|
4
|
+
SUPPORT_OUTPUT_FORMAT = ["json", "csv", "html", "markdown", "md", "pdf"]
|
5
5
|
SUPPORT_INPUT_FORMAT = ["jpg", "jpeg", "png", "bmp", "tiff", "tif", "pdf"]
|
6
6
|
MIN_IMAGE_SIZE = 32
|
7
7
|
WARNING_IMAGE_SIZE = 720
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from PIL import Image
|
4
|
+
from io import BytesIO
|
5
|
+
|
6
|
+
from reportlab.pdfgen import canvas
|
7
|
+
from reportlab.pdfbase.ttfonts import TTFont
|
8
|
+
from reportlab.pdfbase import pdfmetrics
|
9
|
+
from reportlab.pdfbase.pdfmetrics import stringWidth
|
10
|
+
|
11
|
+
import numpy as np
|
12
|
+
import jaconv
|
13
|
+
|
14
|
+
from ..constants import ROOT_DIR
|
15
|
+
|
16
|
+
FONT_PATH = ROOT_DIR + "/resource/MPLUS1p-Medium.ttf"
|
17
|
+
|
18
|
+
|
19
|
+
def _poly2rect(points):
|
20
|
+
"""
|
21
|
+
Convert a polygon defined by its corner points to a rectangle.
|
22
|
+
The points should be in the format [[x1, y1], [x2, y2], [x3, y3], [x4, y4]].
|
23
|
+
"""
|
24
|
+
points = np.array(points, dtype=int)
|
25
|
+
x_min = points[:, 0].min()
|
26
|
+
x_max = points[:, 0].max()
|
27
|
+
y_min = points[:, 1].min()
|
28
|
+
y_max = points[:, 1].max()
|
29
|
+
|
30
|
+
return [x_min, y_min, x_max, y_max]
|
31
|
+
|
32
|
+
|
33
|
+
def _calc_font_size(content, bbox_height, bbox_width):
|
34
|
+
rates = np.arange(0.5, 1.0, 0.01)
|
35
|
+
|
36
|
+
min_diff = np.inf
|
37
|
+
best_font_size = None
|
38
|
+
for rate in rates:
|
39
|
+
font_size = bbox_height * rate
|
40
|
+
text_w = stringWidth(content, "MPLUS1p-Medium", font_size)
|
41
|
+
diff = abs(text_w - bbox_width)
|
42
|
+
if diff < min_diff:
|
43
|
+
min_diff = diff
|
44
|
+
best_font_size = font_size
|
45
|
+
|
46
|
+
return best_font_size
|
47
|
+
|
48
|
+
|
49
|
+
def to_full_width(text):
|
50
|
+
fw_map = {
|
51
|
+
"\u00a5": "\uffe5", # ¥ → ¥
|
52
|
+
"\u00b7": "\u30fb", # · → ・
|
53
|
+
" ": "\u3000", # 半角スペース→全角スペース
|
54
|
+
}
|
55
|
+
|
56
|
+
TO_FULLWIDTH = str.maketrans(fw_map)
|
57
|
+
|
58
|
+
jaconv_text = jaconv.h2z(text, kana=True, ascii=True, digit=True)
|
59
|
+
jaconv_text = jaconv_text.translate(TO_FULLWIDTH)
|
60
|
+
|
61
|
+
return jaconv_text
|
62
|
+
|
63
|
+
|
64
|
+
def create_searchable_pdf(images, ocr_results, output_path, font_path=None):
|
65
|
+
if font_path is None:
|
66
|
+
font_path = FONT_PATH
|
67
|
+
|
68
|
+
pdfmetrics.registerFont(TTFont("MPLUS1p-Medium", font_path))
|
69
|
+
|
70
|
+
packet = BytesIO()
|
71
|
+
c = canvas.Canvas(packet)
|
72
|
+
|
73
|
+
for i, (image, ocr_result) in enumerate(zip(images, ocr_results)):
|
74
|
+
image = Image.fromarray(image[:, :, ::-1]) # Convert BGR to RGB
|
75
|
+
pdfmetrics.registerFont(TTFont("MPLUS1p-Medium", FONT_PATH))
|
76
|
+
|
77
|
+
image_path = f"tmp_{i}.png"
|
78
|
+
image.save(image_path)
|
79
|
+
w, h = image.size
|
80
|
+
|
81
|
+
c.setPageSize((w, h))
|
82
|
+
c.drawImage(image_path, 0, 0, width=w, height=h)
|
83
|
+
os.remove(image_path) # Clean up temporary image file
|
84
|
+
|
85
|
+
for word in ocr_result.words:
|
86
|
+
text = word.content
|
87
|
+
bbox = _poly2rect(word.points)
|
88
|
+
direction = word.direction
|
89
|
+
|
90
|
+
x1, y1, x2, y2 = bbox
|
91
|
+
bbox_height = y2 - y1
|
92
|
+
bbox_width = x2 - x1
|
93
|
+
|
94
|
+
if direction == "vertical":
|
95
|
+
text = to_full_width(text)
|
96
|
+
|
97
|
+
if direction == "horizontal":
|
98
|
+
font_size = _calc_font_size(text, bbox_height, bbox_width)
|
99
|
+
else:
|
100
|
+
font_size = _calc_font_size(text, bbox_width, bbox_height)
|
101
|
+
|
102
|
+
c.setFont("MPLUS1p-Medium", font_size)
|
103
|
+
c.setFillColorRGB(1, 1, 1, alpha=0) # 透明
|
104
|
+
if direction == "vertical":
|
105
|
+
base_y = h - y2 + (bbox_height - font_size)
|
106
|
+
for j, ch in enumerate(text):
|
107
|
+
c.saveState()
|
108
|
+
c.translate(x1 + font_size * 0.5, base_y - (j - 1) * font_size)
|
109
|
+
c.rotate(-90)
|
110
|
+
c.drawString(0, 0, ch)
|
111
|
+
c.restoreState()
|
112
|
+
else:
|
113
|
+
base_y = h - y2 + (bbox_height - font_size) * 0.5
|
114
|
+
c.drawString(x1, base_y, text)
|
115
|
+
c.showPage()
|
116
|
+
c.save()
|
117
|
+
|
118
|
+
with open(output_path, "wb") as f:
|
119
|
+
f.write(packet.getvalue())
|
@@ -1,12 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: yomitoku
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.3
|
4
4
|
Summary: Yomitoku is an AI-powered document image analysis package designed specifically for the Japanese language.
|
5
5
|
Author-email: Kotaro Kinoshita <kotaro.kinoshita@mlism.com>
|
6
6
|
License: CC BY-NC-SA 4.0
|
7
7
|
Keywords: Deep Learning,Japanese,OCR
|
8
8
|
Requires-Python: <3.13,>=3.10
|
9
9
|
Requires-Dist: huggingface-hub>=0.26.1
|
10
|
+
Requires-Dist: jaconv>=0.4.0
|
10
11
|
Requires-Dist: lxml>=5.3.0
|
11
12
|
Requires-Dist: omegaconf>=2.3.0
|
12
13
|
Requires-Dist: onnx>=1.17.0
|
@@ -15,6 +16,7 @@ Requires-Dist: opencv-python>=4.10.0.84
|
|
15
16
|
Requires-Dist: pyclipper>=1.3.0.post6
|
16
17
|
Requires-Dist: pydantic>=2.9.2
|
17
18
|
Requires-Dist: pypdfium2>=4.30.0
|
19
|
+
Requires-Dist: reportlab>=4.4.1
|
18
20
|
Requires-Dist: shapely>=2.0.6
|
19
21
|
Requires-Dist: timm>=1.0.11
|
20
22
|
Requires-Dist: torch>=2.5.0
|
@@ -41,7 +43,7 @@ YomiToku は日本語に特化した AI 文章画像解析エンジン(Document
|
|
41
43
|
- 🤖 日本語データセットで学習した 4 種類(文字位置の検知、文字列認識、レイアウト解析、表の構造認識)の AI モデルを搭載しています。4 種類のモデルはすべて独自に学習されたモデルで日本語文書に対して、高精度に推論可能です。
|
42
44
|
- 🇯🇵 各モデルは日本語の文書画像に特化して学習されており、7000 文字を超える日本語文字の認識をサーポート、手書き文字、縦書きなど日本語特有のレイアウト構造の文書画像の解析も可能です。(日本語以外にも英語の文書に対しても対応しています)。
|
43
45
|
- 📈 レイアウト解析、表の構造解析, 読み順推定機能により、文書画像のレイアウトの意味的構造を壊さずに情報を抽出することが可能です。
|
44
|
-
- 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv
|
46
|
+
- 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。文書画像をサーチャブルPDFに変換する処理もサポートしています。
|
45
47
|
- ⚡ GPU 環境で高速に動作し、効率的に文書の文字起こし解析が可能です。また、VRAM も 8GB 以内で動作し、ハイエンドな GPU を用意する必要はありません。
|
46
48
|
|
47
49
|
## 🖼️ デモ
|
@@ -85,7 +87,7 @@ yomitoku ${path_data} -f md -o results -v --figure --lite
|
|
85
87
|
```
|
86
88
|
|
87
89
|
- `${path_data}` 解析対象の画像が含まれたディレクトリか画像ファイルのパスを直接して指定してください。ディレクトリを対象とした場合はディレクトリのサブディレクトリ内の画像も含めて処理を実行します。
|
88
|
-
- `-f`, `--format` 出力形式のファイルフォーマットを指定します。(json, csv, html, md をサポート)
|
90
|
+
- `-f`, `--format` 出力形式のファイルフォーマットを指定します。(json, csv, html, md, pdf(searchable-pdf) をサポート)
|
89
91
|
- `-o`, `--outdir` 出力先のディレクトリ名を指定します。存在しない場合は新規で作成されます。
|
90
92
|
- `-v`, `--vis` を指定すると解析結果を可視化した画像を出力します。
|
91
93
|
- `-l`, `--lite` を指定すると軽量モデルで推論を実行します。通常より高速に推論できますが、若干、精度が低下する可能性があります。
|
@@ -1,6 +1,6 @@
|
|
1
1
|
yomitoku/__init__.py,sha256=kXOM8RbpwwLABG3p3vPT3dJWBk4JX2MFGrOeBEW0hKM,543
|
2
2
|
yomitoku/base.py,sha256=9U3sfe69O6vuO430JzzKQQNkgPsLM9WdLfOUUhp3Ljs,3878
|
3
|
-
yomitoku/constants.py,sha256=
|
3
|
+
yomitoku/constants.py,sha256=2jya14UflDkMdYWMKc-ZllkWbJW2qh59Cnt2brrgNb4,693
|
4
4
|
yomitoku/document_analyzer.py,sha256=xliAelQdfsK64FtVuFvstDBr9uf2TwhqW31g2g91_CY,16888
|
5
5
|
yomitoku/layout_analyzer.py,sha256=VhNf1ZQFoozj6WUGk5ll1p2p1jk5X3j-JPcDbTAoSl4,1856
|
6
6
|
yomitoku/layout_parser.py,sha256=0MgbCsD90srQdsxkGEL0TgKm4rkmGzsQYx0sjKQ03yc,7718
|
@@ -10,8 +10,8 @@ yomitoku/table_structure_recognizer.py,sha256=tHjex6deT_FjRK5ePz9bUXA_QIhgv_vYtK
|
|
10
10
|
yomitoku/text_detector.py,sha256=6IwEJJKp_F8YH0Oki0QV-Mqi--P2LGbNKo-_kxBB_eo,4383
|
11
11
|
yomitoku/text_recognizer.py,sha256=eaxozNu-Ms6iv8efbKZzn8pJNW1Wo4f86bGhzSMtv3s,5992
|
12
12
|
yomitoku/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
yomitoku/cli/main.py,sha256=
|
14
|
-
yomitoku/cli/
|
13
|
+
yomitoku/cli/main.py,sha256=5An9usBfBYqNiBA6QqZTCaYI4b3W1j-efAsggK_HCss,13522
|
14
|
+
yomitoku/cli/mcp_server.py,sha256=WnWzxd13HaemC3b-5i9B9NVBGc3WGfum2nYhoBolEnk,5641
|
15
15
|
yomitoku/configs/__init__.py,sha256=x5-ccjGiP6xxRtDPT7f1Enl7SsE0hSk0G8f7eF9V85I,886
|
16
16
|
yomitoku/configs/cfg_layout_parser_rtdtrv2.py,sha256=8PRxB2Ar9UF7-DLtbgSokhrzdXb0veWI6Wc-X8qigRw,2329
|
17
17
|
yomitoku/configs/cfg_layout_parser_rtdtrv2_v2.py,sha256=nMrL3uvoVmyzZ909Bz2zmfp9b6AEBLKhIprOvQ5yiQE,2324
|
@@ -51,8 +51,9 @@ yomitoku/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
51
|
yomitoku/utils/graph.py,sha256=LKNB8ZhSQwOZMfeAimPMF5UCVVr2ZaUWoGDkz8z-uGU,456
|
52
52
|
yomitoku/utils/logger.py,sha256=uOmtQDr0A0JD7wyFshedL08BiNrQorHnpktRXba8bjU,424
|
53
53
|
yomitoku/utils/misc.py,sha256=r92x45kQR8lC5jO1MZaHBDtcCWBkQXg_WS9H4RXJzSY,4127
|
54
|
+
yomitoku/utils/searchable_pdf.py,sha256=7JQCFhwpBJVV1Fx9q4p6fFGlEsJ-SmR0arddI3NzEeo,3567
|
54
55
|
yomitoku/utils/visualizer.py,sha256=DjDwHiAu1iFRKh96H3Egq4vuI2s_-9dLCDeykhKi8jo,5251
|
55
|
-
yomitoku-0.9.
|
56
|
-
yomitoku-0.9.
|
57
|
-
yomitoku-0.9.
|
58
|
-
yomitoku-0.9.
|
56
|
+
yomitoku-0.9.3.dist-info/METADATA,sha256=0r3tOl0ohoegcYQXWM3ROCSOr5px3IK-0zwqyADc9Mc,8872
|
57
|
+
yomitoku-0.9.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
58
|
+
yomitoku-0.9.3.dist-info/entry_points.txt,sha256=n3c8bQSj5Be5GHAOv_NZ8cldJFmWeigQxSmteFTmu_k,96
|
59
|
+
yomitoku-0.9.3.dist-info/RECORD,,
|
File without changes
|