Repository navigation
Expand file tree
/
Copy pathjson3pdf.py
More file actions
662 lines (574 loc) · 31 KB
/
Copy pathjson3pdf.py
File metadata and controls
662 lines (574 loc) · 31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
import os
import io
import argparse
from pathlib import Path
import logging
import glob
from datetime import datetime
import sys
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A3, A4, A5, A6, B4, B5, B6, B7, letter
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import Paragraph
from reportlab.lib.colors import Color
import json
import powerlog
from powerlog import logger,verbose_print, info_print, error_print, variable_str, debug_print,warning_print
from PyPDF2 import PdfReader
import math
import re
from shapely.geometry import Polygon
from difflib import SequenceMatcher
try:
import pytesseract
except ImportError:
error_print('pytesseract is not installed. All ajusting functions using pytesseract will be disabled.')
pytesseract_exists = False
from PIL import Image
import pandas as pd
from colorama import Fore, Style
# 透明色を定義(赤、緑、青、アルファ)
transparent_color = Color(0, 0, 0, alpha=0)
# 文字が日本語かどうかを判断する関数
def is_japanese(text):
return bool(re.search(r'[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]', text))
# ページサイズの辞書を作成
page_sizes = {
"A3": (842, 1191),
"A4": (595, 842),
"A5": (420, 595),
"A6": (298, 420),
"B4": (729, 1032),
"B5": (516, 729),
"B6": (363, 516),
"B7": (258, 363),
"Tabloid": (792, 1224), # タブロイド判のサイズ(11 x 17インチをポイントに変換)
"Blanket": (4320, 6480) # ブランケット判のサイズ(60 x 90インチをポイントに変換)
}
# コマンドライン引数を解析する
parser = powerlog.create_parser()
parser = argparse.ArgumentParser(description='PDFファイルにテキストを書き込む')
parser.add_argument('--log-level', '-log', default='INFO', choices=['DEBUG', 'VERBOSE', 'INFO', 'WARNING'],
help='Set the logging level (default: INFO)')
parser.add_argument('-debug', action='store_const', const='DEBUG', dest='log_level',
help='Set the logging level to DEBUG')
parser.add_argument('-s', '--size', type=int, help='フォントのサイズの調整(デフォルトは100)単位は%')
# 排他的なオプショングループを作成
font_threshold = parser.add_mutually_exclusive_group()
font_threshold.add_argument('--font-threshold','-t',default=None, type=int, help='連続した単語や行のフォントサイズ変更の閾値を指定します。単位は%')
font_threshold.add_argument('--individual', action='store_true', help='各単語のフォントサイズを個別に設定します')
parser.add_argument('--hfont','-hf', default='NotoSansJP-Regular.ttf', help='Sets horizontal font (default is NotoSansJP-Regular.ttf)')
parser.add_argument('--vfont','-vf', default='NotoSansJP-Regular.ttf', help='Sets vertical font (default is NotoSansJP-Regular.ttf)')
parser.add_argument('--dpi','-d', type=int, default=600, help='文書のDPIを指定します(デフォルトは600)')
parser.add_argument('--page','-p', choices=list(page_sizes.keys()), help='The page size of the PDF.')
parser.add_argument('--layout', choices=['word', 'line', 'paragraph'], default='line', help='Choose the level of text to draw: word, line, or paragraph(at the monent paragraph is unusable).')
parser.add_argument('--area','-ar', type=float, default=80,
help='area threshold for counting lines in a paragraph. default is 80')
parser.add_argument('--similarity', '-st', type=float, default=0.1,
help='Set the similarity threshold for adding lines to a paragraph. Default is 0.1')
parser.add_argument('--adjust', '-ad', action='store_true', help='adjust the layout of lines and paragraphs. Experimental!!!Default is False')
parser.add_argument('--coordinate', '-ct', type=float, default=80,help='Set the coordinate threshold for coordinate adjustment for lines and paragraph. Default is 80')
parser.add_argument('--clear','-c', action='store_true', help='output clear text PDF')
parser.add_argument('--search','-se', nargs=2, type=int, default=[50,2], help='search page limit and ignore character number for layout "line" main text direction detection. Default:(50,2)')
args = parser.parse_args()
powerlog.set_log_level(args)
if args.adjust and not pytesseract_exists:
error_print('pytesseract is not installed. --adjust option will be ignored.')
args.adjust = False
# area/coordinate/similarity thresholdの値をパーセンテージから小数に変換
args.area /= 100.0
args.coordinate /= 100.0
args.similarity /= 100.0
# tesseractが使えるか確認
try:
# pytesseractのget_tesseract_version関数を使用してTesseractのバージョンを取得
pytesseract.get_tesseract_version()
verbose_print(Fore.GREEN+'Tesseract is available.'+Style.RESET_ALL)
except pytesseract.TesseractNotFoundError:
# Tesseractが見つからない場合、--adjustオプションを無効化
if args.adjust:
print("Warning: Tesseract not found. The --adjust option will be disabled.")
args.adjust = False
# OSに適した改行文字を取得
newline = os.linesep
# 文字列の類似度を計算する関数pyt
def similarity(a, b):
return SequenceMatcher(None, a, b).ratio()
# DPI変換のための係数を設定
DPI_CONVERSION_FACTOR = args.dpi / 72
# ポイントをインチに変換する係数
INCH_TO_POINT = 72
# 単位系からポイントへの変換係数を定義
unit_to_point_conversion_factors = {
'inch': 72, # 1インチ = 72ポイント
# 必要に応じて他の単位系を追加
}
# レイアウトオプションをtesseractレベルに変換
level_dict = {'word': 5, 'line': 4, 'paragraph': 3}
level = level_dict[args.layout]
# フォントのパスを取得する関数
def get_font_path(get_font_name, font_type):
font_path = './data/fonts/' + get_font_name
if not os.path.splitext(get_font_name)[1]: # 拡張子がない場合
# デフォルトの拡張子を追加
font_path += '.ttf'
return font_path
# check origin and direction of polygon
def determine_origin(item_polygon_coords):
# バウンディングボックスの4つの点の座標を取得
bbox1, bbox2, bbox3, bbox4 = item_polygon_coords
# ポリゴンを作成
poly = Polygon(item_polygon_coords)
# ポリゴンの重心を計算
centroid = poly.centroid
angles = []
positions = []
for bbox in item_polygon_coords:
position = None
# 重心とbbox1との角度を計算
angle = math.atan2(bbox[1] - centroid.y, bbox[0] - centroid.x)
angles.append(angle)
# 角度から起点を判断
if 0 < angle <= math.pi /2:
position = "top_right"
elif math.pi /2 < angle <= math.pi or angle == - math.pi:
origin = "top_left"
elif - math.pi < angle <= - math.pi/2:
position = "bottom_left"
elif - math.pi/2 < angle <= 0:
position = "bottom_right"
else:
position = "unknown"
positions.append(position)
angle_diffs = [(angles[i] - angles[i-1] + math.pi) % (2*math.pi) - math.pi for i in range(1, len(angles))]
if all(angle_diffs) > 0:
polygon_direction = "clockwise"
elif all(angle_diffs) < 0:
polygon_direction = "counterclockwise"
else:
polygon_direction = "diagonal"
origin = positions [0]
return origin, polygon_direction
# 本文テキストの方向を取得
search_limit = args.search
def get_standard_text_direction(pages, search_limit):
vertical_count = 0
horizontal_count = 0
japanese_count = 0
english_count = 0
bottom_left_count = 0
bottom_right_count = 0
top_right_count = 0
top_left_count = 0
# 各ページを処理
for i, page in enumerate(analyze_result['pages'][:search_limit[0]]):
page_number = page['pageNumber']
# Document Intelligenceの単位系を取得
di_unit = page['unit']
# 単位系からポイントへの変換係数を取得
unit_to_point_conversion_factor = unit_to_point_conversion_factors.get(di_unit, 1) # デフォルトは1
# ページサイズを取得
page_width = page['width'] * unit_to_point_conversion_factor
page_height = page['height'] * unit_to_point_conversion_factor
c.setPageSize((page_width, page_height))
items = page['lines']
#テスト走査
for item in items:
text = item['content']
polygon = item['polygon']
# itemのpolygonを座標のペアのリストに変換
x1, y1, x2, y2, x3, y3, x4, y4 = [v * unit_to_point_conversion_factor for v in polygon]
item_polygon_coords = [(x1,y1),(x2,y2),(x3,y3),(x4,y4)]
origin, polygon_direction = determine_origin(item_polygon_coords)
# only count text with known origin and non-diagonal polygon, with number of characters greater than search_limit[1]
if origin != "unknown" and polygon_direction != "diagonal" and len(text) > search_limit[1]:
if origin == "bottom_left":
bottom_left_count += 1
elif origin == "bottom_right":
bottom_left_count += 1
elif origin == "top_right":
top_right_count += 1
elif origin == "top_left":
top_left_count += 1
if origin == "bottom_left":
origin_offset = 0
elif origin == "bottom_right":
origin_offset = 1
elif origin == "top_right":
origin_offset = 2
elif origin == "top_left":
origin_offset = 3
# 長辺と短辺が近似的に等しいかどうかをチェック
if abs(y3 - y1) < abs(x3 - x1):
rotation = math.degrees(math.atan2(y1 - y2, x2 - x1)) + 90 * origin_offset
else:
rotation = math.degrees(math.atan2(y2 - y3, x3 - x2)) + 90 * origin_offset
if -180 <= rotation <= -135 or -45 <= rotation <= 45 or 135 <= rotation <= 180:
#script_direction = 'horizontal'
horizontal_count += 1
else:
#script_direction = 'vertical'
vertical_count += 1
if is_japanese(text):
japanese_count += 1
else:
english_count += 1
if vertical_count > horizontal_count:
main_text_direction = "vertical"
else:
main_text_direction = "horizontal"
if japanese_count > english_count:
main_text_language = "japanese"
else:
main_text_language = "english"
origin_counts = {("bottom_left", 0): bottom_left_count, ("bottom_right", 1): bottom_right_count, ("top_right", 2): top_right_count, ("top_left", 3): top_left_count}
main_text_origin_tuple = max(origin_counts, key=origin_counts.get)
main_text_origin, main_text_offset = main_text_origin_tuple
return main_text_origin, main_text_offset, main_text_direction, main_text_language
# 横書き用のフォントを登録
h_font_name = args.hfont
h_font_path = get_font_path(h_font_name, 'h')
info_print(f'Horizontal font: {h_font_name}')
pdfmetrics.registerFont(TTFont(h_font_name, h_font_path))
# 縦書き用のフォントを登録
v_font_name = args.vfont
v_font_path = get_font_path(v_font_name, 'v')
info_print(f'Vertical font: {v_font_name}')
pdfmetrics.registerFont(TTFont(v_font_name, v_font_path))
# フォントサイズの係数を取得(デフォルトは1.0)
font_size_factor = 1.0 if args.size is None else args.size / 100.0
# フォントサイズ変化の閾値を取得(デフォルトはNone)
font_size_change_threshold = None if args.font_threshold is None else args.font_threshold / 100.0
# 座標間の距離を計算
def calculate_distance(x1, y1, x2, y2):
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
# 入力フォルダと出力フォルダのパスを設定
json_folder = './DIjson'
if not os.path.exists(json_folder):
os.makedirs(json_folder,exist_ok=True)
print(f'Created {json_folder} folder')
else:
print(f'{json_folder} folder already exists')
if not args.clear:
output_folder = './OCRtextPDF'
if not os.path.exists(output_folder):
os.makedirs(output_folder,exist_ok=True)
print(f'Created {output_folder} folder')
else:
print(f'{output_folder} folder already exists')
else:
output_folder = './OCRclearPDF'
if not os.path.exists(output_folder):
os.makedirs(output_folder,exist_ok=True)
print(f'Created {output_folder} folder')
else:
print(f'{output_folder} folder already exists')
optpdf_folder = Path('./OptimizedPDF')
optimized_folder = Path('./TEMP/optimized')
# 入力フォルダ内の全てのJSONファイルを取得
json_files = [f for f in os.listdir(json_folder) if f.endswith('.pdf.json')]
# JSONファイルの総数を取得し、コンソールに表示
total_json_files = len(json_files)
info_print(f'Total JSON files: {total_json_files}')
# PDFファイルのカウンターを初期化
pdf_counter = 0
for json_file in json_files:
# OCR結果のJSONファイル名を設定
ocr_json_path = os.path.join(json_folder, json_file)
# JSONファイルが存在する場合のみ処理を実行
if os.path.exists(ocr_json_path):
with open(ocr_json_path, 'r', encoding='utf-8') as f:
ocr_data = json.load(f)
# OCR元のPDFファイル名を設定
ocr_pdf_path = optpdf_folder / json_file.replace('.json', '')
# PDFファイルが存在する場合のみページサイズを読み取る
if args.page is not None:
page_size = page_sizes[args.page]
else:
if ocr_pdf_path.exists():
with open(ocr_pdf_path, 'rb') as f:
pdf = PdfReader(f)
page = pdf.pages[0]
width_pt = page.mediabox[2]
height_pt = page.mediabox[3]
# ページサイズを辞書から探す
for size, (w, h) in page_sizes.items():
if abs(width_pt - w) < 1 and abs(height_pt - h) < 1:
page_size = size
break
# ページサイズが見つからない場合はA5に設定
else:
page_size = 'A5'
#元画像のフォルダ
image_folder = optimized_folder / json_file.replace('.pdf.json', '')
# フォルダが存在することを確認
if not image_folder.exists():
args.adjust = False
warning_print(f"Warning: Folder {image_folder} does not exist.--adjust option will be disabled.")
# image_folder内の画像ファイルをアルファベット順にソート
image_files = sorted(image_folder.glob('*.jp2'))
# 画像ファイルが存在することを確認
if not image_files:
args.adjust = False
print(f"Error: No image files found in {image_folder}.--adjust option will be disabled.")
# Check if 'analyzeResult' key exists: json downloaded from web has ''key, json created from API does not.
if 'analyzeResult' in ocr_data:
analyze_result = ocr_data['analyzeResult']
else:
analyze_result = ocr_data # Treat the whole JSON as the content of 'analyzeResult'
# 新しいPDFファイル名を設定('.pdf' を削除してから '_TextOnly.pdf' を追加)
base_filename = os.path.splitext(json_file)[0]
base_filename = base_filename.replace('.pdf', '') # '.pdf' を削除
if args.clear:
new_pdf_filename = base_filename + '_ClearText.pdf'
else:
new_pdf_filename = base_filename + '_TextOnly.pdf'
new_pdf_path = os.path.join(output_folder, new_pdf_filename)
# ReportLabのキャンバスを作成
c = canvas.Canvas(new_pdf_path, pagesize=page_size)
info_print(f'Creating PDF file {new_pdf_filename}...')
info_print(f'Layout: {args.layout}')
#本文テキストの方向を取得
main_text_origin, main_text_offset, main_text_direction, main_text_language = get_standard_text_direction(analyze_result['pages'], search_limit)
# 各ページを処理
for i, page in enumerate(analyze_result['pages']):
page_number = page['pageNumber']
# Document Intelligenceの単位系を取得
di_unit = page['unit']
# 単位系からポイントへの変換係数を取得
unit_to_point_conversion_factor = unit_to_point_conversion_factors.get(di_unit, 1) # デフォルトは1
# ページサイズを取得
page_width = page['width'] * unit_to_point_conversion_factor
page_height = page['height'] * unit_to_point_conversion_factor
c.setPageSize((page_width, page_height))
verbose_print(f'Processing page {page_number}...')
# iがimage_filesの長さを超えないようにする
if args.adjust and i<len(image_files):
tesseract_ocr = pytesseract.image_to_data(Image.open(image_files[i]))
image = Image.open(image_files[i])
verbose_print(f'performing_osd for page {page_number} and {i} with tesseract: {tesseract_ocr}')
# 画像のサイズ(ピクセル単位)を取得
image_width, image_height = image.size
debug_print(f'image size: {image.size}')
elif args.adjust:
error_print(f"Image file not found for page {page_number}.in {image_folder}")
# 各単語を処理
if args.layout == 'word':
items = page['words']
elif args.layout == 'line':
items = page['lines']
elif args.layout == 'paragraph':
items = [p for p in analyze_result['paragraphs'] if p['boundingRegions'][0]['pageNumber'] == page_number]
lines = page['lines']
# Create a copy of lines list to manipulate it
lines_copy = lines.copy()
if args.adjust and i<len(image_files):
tesseract_bboxes = []
# pandasを使用してデータを解析
read_tess_data = pd.read_csv(io.StringIO(tesseract_ocr), sep='\t', quotechar='"', on_bad_lines='skip', engine='python')
# 'bbox'情報が含まれる行を抽出
bbox_data = read_tess_data[read_tess_data['level'] == level]
# 各行のbbox情報を取得
for index, row in bbox_data.iterrows():
bbox = [row['left'], row['top'], row['width'], row['height']]
tesseract_bboxes.append(bbox)
debug_print(f'tesseract_bboxes: {tesseract_bboxes}')
# bboxリストをcoordsリストに変換
bbox_coords = []
# tesseractのピクセル単位の座標系をDocument Intelligenceの座標系(以前に取取得)に変換
tess_to_di_conversion_factor = unit_to_point_conversion_factor / (args.dpi*max(image_width, image_height))
for tesseract_bbox in tesseract_bboxes:
x, y, width, height = tesseract_bbox
x /= image_width
y /= image_height
width /= image_width
height /= image_height
bbox_to_polygon_coords = [(x, y), (x + width, y), (x + width, y + height), (x, y + height), (x, y)]
bbox_coords.append(bbox_to_polygon_coords)
#メイン処理
prev_font_size = None
for item in items:
if args.layout == 'paragraph':
paragraph_text = item['content']
debug_print(f'paragraph_text: {paragraph_text}')
text = ''
polygon = item['boundingRegions'][0]['polygon']
line_count = 0
x1, y1, x2, y2, x3, y3, x4, y4 = [v * unit_to_point_conversion_factor for v in polygon]
item_polygon_coords = [(x1,y1),(x2,y2),(x3,y3),(x4,y4)]
item_polygon = Polygon(item_polygon_coords)
prev_line_bottom = None
for line in lines_copy:
debug_print(f'checking line: {line}')
line_polygon_coords = [(line['polygon'][i] * unit_to_point_conversion_factor, line['polygon'][i + 1] * unit_to_point_conversion_factor) for i in range(0, len(line['polygon']), 2)]
line_polygon = Polygon(line_polygon_coords)
line_bottom = min(line_polygon_coords, key=lambda coord: coord[1])[1]
if prev_line_bottom is not None and line_bottom < prev_line_bottom:
text += newline
# Only add line content to text if it matches the similarity threshold or more with a part of the paragraph text
if similarity(line['content'], paragraph_text) >= args.similarity:
text += line['content']
debug_print(f'Added line content: {line["content"]}')
# Remove the line from lines_copy list after adding its content to text
lines_copy.remove(line)
line_count += 1
debug_print(f'line_count: {line_count}')
# Remove the same content from paragraph_text
paragraph_text = paragraph_text.replace(line['content'], '', 1)
debug_print(f'Remaining paragraph_text: {paragraph_text}')
# If paragraph_text is empty, break the loop to move to the next item
if not paragraph_text:
debug_print('No more content in paragraph_text. Moving to the next item.')
break
else:
error_print(f'Line content does not match the similarity threshold: {line["content"]}')
error_print(f'similarity threshold: {args.similarity}')
prev_line_bottom = line_bottom
else:
text = item['content']
polygon = item['polygon']
# itemのpolygonを座標のペアのリストに変換
x1, y1, x2, y2, x3, y3, x4, y4 = [v * unit_to_point_conversion_factor for v in polygon]
item_polygon_coords = [(x1,y1),(x2,y2),(x3,y3),(x4,y4)]
item_polygon = Polygon(item_polygon_coords)
debug_print(f'item_polygon: {item_polygon_coords}')
if args.adjust and i<len(image_files):
# tesseract_の座標とitemのpolygonを比較
for tesseract_polygon_coord in bbox_coords:
tesseract_polygon = Polygon(tesseract_polygon_coord)
debug_print(f'tesseract_polygon coord: {tesseract_polygon_coord}')
# tesseractの座標とitemのpolygonが重なっている割合を計算
ocr_intersection = item_polygon.intersection(tesseract_polygon).area
ocr_overlap = ocr_intersection / item_polygon.area
debug_print(f'ocr_overlap: {ocr_overlap}for item {text}')
# 重なっている割合が閾値を超えている場合、tesseractの座標を使用
if ocr_overlap > args.coordinate:
item_polygon = tesseract_polygon
verbose_print(f'Using osd polygon for item {text}')
break
origin, polygon_direction = determine_origin(item_polygon_coords)
if origin == "unknown" or polygon_direction == "diagonal":
error_print(f'Unknown origin or diagonal polygon for item {text}. Layout may be incorrect.')
# 座標間の距離を計算
distances = [
(calculate_distance(x1, y1, x2, y2), ((x1, y1), (x2, y2))),
(calculate_distance(x2, y2, x3, y3), ((x2, y2), (x3, y3))),
(calculate_distance(x3, y3, x4, y4), ((x3, y3), (x4, y4))),
(calculate_distance(x4, y4, x1, y1), ((x4, y4), (x1, y1))),
(calculate_distance(x1, y1, x3, y3), ((x1, y1), (x3, y3))),
(calculate_distance(x2, y2, x4, y4), ((x2, y2), (x4, y4)))
]
# 長さ順に並べ替え
distances.sort(key=lambda x: x[0])
# 最後の2つが対角線、次の2つが長辺、最初の2つが短辺
diagonals = distances[-2:]
long_sides = distances[2:4]
short_sides = distances[:2]
# 長辺と短辺の長さを取得
long_side_length = (long_sides[0][0]+long_sides[1][0])/2
short_side_length = (short_sides[0][0]+short_sides[1][0])/2
if origin == "bottom_left":
origin_offset = 0
elif origin == "bottom_right":
origin_offset = 1
elif origin == "top_right":
origin_offset = 2
elif origin == "top_left":
origin_offset = 3
else:
origin_offset = 0
#one character text recognitions is usually off by 90 degrees due to the ratio.
#if len(text) == 1:
# origin_offset += 1
# 長辺と短辺が近似的に等しいかどうかをチェック
if abs(y3 - y1) < abs(x3 - x1):
rotation = math.degrees(math.atan2(y1 - y2, x2 - x1)) + 90 * origin_offset
else:
rotation = math.degrees(math.atan2(y2 - y3, x3 - x2)) + 90 * origin_offset
if -180 <= rotation <= -135 or -45 <= rotation <= 45 or 135 <= rotation <= 180:
script_direction = 'horizontal'
else:
script_direction = 'vertical'
if len(text) == 1:
script_direction = main_text_direction
rotation = 90 * main_text_offset
debug_print(f'text' + text + 'is' + script_direction)
debug_print(f'Is Japanese?:{is_japanese(text)}')
debug_print(f'Origin and rotation is:{determine_origin(item_polygon_coords)}')
# フォントサイズを計算(高さに係数を適用)
font_size = short_side_length * font_size_factor
if args.layout == 'line':
font_size *= 0.9 # 行のフォントサイズのデフォルト係数、テスト環境で決定した数値
font_size *= (1 - 0.1 * (len(text) / 100)) # フォントサイズを微調整
elif args.layout == 'paragraph':
if line_count > 0:
font_size /= line_count
else:
error_print("No lines were counted. Cannot calculate font size.")
font_size += 0 # 段落のフォントサイズを行数で調整
# 前の単語と比較してフォントサイズが閾値以上変化した場合にのみフォントサイズを変更
if not args.individual and prev_font_size is not None and font_size_change_threshold is not None and abs(font_size - prev_font_size) / prev_font_size > font_size_change_threshold:
font_size = prev_font_size
if script_direction == 'horizontal':
font_name = h_font_name
else:
font_name = v_font_name
prev_font_size = font_size
if args.layout == 'word' or args.layout == 'line':
c.setFont(font_name, font_size)
string_width = c.stringWidth(text, font_name, font_size)
scale = long_side_length / string_width
else:
c.setFont(font_name, font_size)
string_width = c.stringWidth(text, font_name, font_size)
scale = 1
# フォントの上昇と下降を取得
font = pdfmetrics.getFont(font_name)
ascent = font.face.ascent * (font_size / 1000.0)
descent = font.face.descent * (font_size / 1000.0)
# 描画原点を設定
x = x1
y = page_height - y1 - ascent
# オフセットを補正
if origin_offset == 0:
offset_correction_x = 0
offset_correction_y = 0
elif origin_offset == 1:
offset_correction_x = -short_side_length
offset_correction_y = + short_side_length
elif origin_offset == 2:
offset_correction_x = 0
offset_correction_y = 0
elif origin_offset == 3:
offset_correction_x = + short_side_length
offset_correction_y = - short_side_length
c.saveState() # 現在の状態を保存
c.translate(x + offset_correction_x, y + offset_correction_y) # 描画原点を移動
if script_direction == 'vertical':
if is_japanese(text): # 文字が日本語の場合
c.scale(1,scale )
c.rotate(rotation)
else: # 文字が英語の場合
c.scale(1, scale)
c.rotate(rotation)
else:
c.scale(scale, 1)
c.rotate(rotation)
if args.clear:
c.setFillColor(transparent_color)
c.drawString(0, 0, text) # 描画原点から文字を描画
c.restoreState()
# 次のページに移動
c.showPage()
# PDFファイルを保存
c.save()
# PDFファイルのカウンターを増やす
pdf_counter += 1
# PDFファイルが作成されたことを表示(何個目/総数の形で表示)
info_print(f'PDF file {new_pdf_filename} has been created. ({pdf_counter}/{total_json_files})')
else:
# JSONファイルが存在しないことを表示
info_print(f'No JSON files found, so no PDF file was created.')
info_print('All PDF file processing is complete.')
info_print(f'font_threshold:{font_size_change_threshold}, individual:{args.individual}, layout:{args.layout}')