Compare commits
3 Commits
e1928564fa
...
2ca77b604b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ca77b604b | |||
| 27aed10a4a | |||
| e6e6d059ac |
@ -45,8 +45,36 @@ RUN apt-get update && \
|
||||
libreoffice \
|
||||
libreoffice-calc \
|
||||
libreoffice-writer \
|
||||
libreoffice-java-common \
|
||||
fonts-ipafont \
|
||||
fonts-ipafont-gothic \
|
||||
fonts-ipafont-mincho \
|
||||
language-pack-ja \
|
||||
fontconfig \
|
||||
locales \
|
||||
python3-uno # LibreOffice Python バインディング
|
||||
|
||||
|
||||
# 日本語ロケールの設定
|
||||
RUN locale-gen ja_JP.UTF-8
|
||||
ENV LANG=ja_JP.UTF-8
|
||||
ENV LC_ALL=ja_JP.UTF-8
|
||||
ENV LANGUAGE=ja_JP:ja
|
||||
|
||||
# フォント設定ファイルをコピー
|
||||
COPY config/fonts.conf /etc/fonts/local.conf
|
||||
|
||||
# フォントキャッシュの更新
|
||||
RUN fc-cache -f -v
|
||||
|
||||
# LibreOfficeの作業ディレクトリを作成
|
||||
RUN mkdir -p /var/cache/libreoffice && \
|
||||
chmod 777 /var/cache/libreoffice
|
||||
|
||||
# フォント設定の権限を設定
|
||||
RUN chmod 644 /etc/fonts/local.conf
|
||||
|
||||
|
||||
# 作業ディレクトリとパーミッションの設定
|
||||
RUN mkdir -p /app/docbase /tmp/libreoffice && \
|
||||
chmod -R 777 /app/docbase /tmp/libreoffice
|
||||
@ -66,6 +94,8 @@ RUN apt-get update
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install boto3==1.26.137
|
||||
|
||||
# Install Gunicorn
|
||||
RUN pip install gunicorn
|
||||
|
||||
|
||||
@ -305,6 +305,12 @@ class SumasenExcel:
|
||||
logging.error(f"Error in proceed_group: {str(e)}")
|
||||
return {"status": False, "message": f"Exception in proceed_group : Error generating report: {str(e)}"}
|
||||
|
||||
def pixels_to_EMU(pixels):
|
||||
"""
|
||||
Convert pixels to EMU (English Metric Units)
|
||||
EMU = pixels * 9525
|
||||
"""
|
||||
return int(pixels * 9525)
|
||||
|
||||
def format_cell_value(self, field_value, cell):
|
||||
"""セルの値を適切な形式に変換する
|
||||
@ -370,6 +376,7 @@ class SumasenExcel:
|
||||
# ワークシートを取得
|
||||
worksheet = cell.parent
|
||||
|
||||
logging.info('step-1')
|
||||
try:
|
||||
# 列の幅を取得(文字単位からピクセルに変換)
|
||||
column_letter = get_column_letter(cell.column)
|
||||
@ -378,6 +385,7 @@ class SumasenExcel:
|
||||
except Exception:
|
||||
cell_width = 100 # デフォルト値
|
||||
|
||||
logging.info('step-2')
|
||||
try:
|
||||
# 行の高さを取得(ポイント単位からピクセルに変換)
|
||||
row_height = worksheet.row_dimensions[cell.row].height
|
||||
@ -385,6 +393,7 @@ class SumasenExcel:
|
||||
except Exception:
|
||||
cell_height = 20 # デフォルト値
|
||||
|
||||
logging.info('step-3')
|
||||
# マージンの設定(ピクセル単位)
|
||||
margin_horizontal = 4 # 左右のマージン
|
||||
margin_vertical = 2 # 上下のマージン
|
||||
@ -393,6 +402,7 @@ class SumasenExcel:
|
||||
effective_cell_width = cell_width - (margin_horizontal * 2)
|
||||
effective_cell_height = cell_height - (margin_vertical * 2)
|
||||
|
||||
logging.info('step-4')
|
||||
# 最小サイズを設定(マージンを考慮)
|
||||
effective_cell_width = max(effective_cell_width, 92) # 100 - (4 * 2)
|
||||
effective_cell_height = max(effective_cell_height, 16) # 20 - (2 * 2)
|
||||
@ -403,6 +413,7 @@ class SumasenExcel:
|
||||
effective_cell_width = min(effective_cell_width, max_width)
|
||||
effective_cell_height = min(effective_cell_height, max_height)
|
||||
|
||||
logging.info('step-5')
|
||||
# アスペクト比を保持しながらリサイズ
|
||||
img_width, img_height = img.size
|
||||
img_aspect = img_width / img_height
|
||||
@ -415,6 +426,10 @@ class SumasenExcel:
|
||||
height = effective_cell_height
|
||||
width = int(height * img_aspect)
|
||||
|
||||
# 画像処理部分の修正
|
||||
#from openpyxl.utils.units import pixels_to_EMU
|
||||
|
||||
logging.info('step-6')
|
||||
# 画像をリサイズ
|
||||
img_resized = img.resize((width, height), Image.BICUBIC)
|
||||
|
||||
@ -423,27 +438,45 @@ class SumasenExcel:
|
||||
img_resized.save(img_byte_arr, format='JPEG', quality=85, optimize=True)
|
||||
img_byte_arr.seek(0)
|
||||
|
||||
|
||||
logging.info('step-7')
|
||||
# OpenPyXLのImageオブジェクトを作成
|
||||
from openpyxl.drawing.image import Image as XLImage
|
||||
excel_image = XLImage(img_byte_arr)
|
||||
|
||||
# 画像のオフセット位置を設定(マージンを適用)
|
||||
# EMUユニットでのサイズを設定
|
||||
excel_image.width = pixels_to_EMU(width)
|
||||
excel_image.height = pixels_to_EMU(height)
|
||||
|
||||
# 正しいアンカー設定
|
||||
from openpyxl.drawing.spreadsheet_drawing import OneCellAnchor, AnchorMarker
|
||||
from openpyxl.utils.units import pixels_to_EMU
|
||||
from openpyxl.drawing.xdr import XDRPositiveSize2D
|
||||
|
||||
# セルの左上を基準に、マージン分オフセットした位置に配置
|
||||
col_offset = pixels_to_EMU(margin_horizontal)
|
||||
row_offset = pixels_to_EMU(margin_vertical)
|
||||
logging.info('step-8')
|
||||
marker = AnchorMarker(
|
||||
col=cell.column - 1,
|
||||
colOff=pixels_to_EMU(margin_horizontal),
|
||||
row=cell.row - 1,
|
||||
rowOff=pixels_to_EMU(margin_vertical)
|
||||
)
|
||||
|
||||
# マージンを考慮した配置
|
||||
marker = AnchorMarker(col=cell.column - 1, colOff=col_offset,
|
||||
row=cell.row - 1, rowOff=row_offset)
|
||||
anchor = OneCellAnchor(_from=marker, ext=None)
|
||||
# XDRPositiveSize2Dを使用して画像サイズを設定
|
||||
size = XDRPositiveSize2D(
|
||||
cx=pixels_to_EMU(width),
|
||||
cy=pixels_to_EMU(height)
|
||||
)
|
||||
|
||||
anchor = OneCellAnchor(_from=marker, ext=size)
|
||||
excel_image.anchor = anchor
|
||||
|
||||
|
||||
logging.info('step-9')
|
||||
# 画像をワークシートに追加
|
||||
worksheet.add_image(excel_image)
|
||||
#cell.parent.add_image(excel_image)
|
||||
|
||||
logging.info('step-A')
|
||||
# メモリ解放
|
||||
#img_byte_arr.close()
|
||||
|
||||
return ""
|
||||
|
||||
@ -467,7 +500,28 @@ class SumasenExcel:
|
||||
# その他の場合は文字列に変換
|
||||
return str(field_value)
|
||||
|
||||
def verify_image_insertion(self, worksheet, cell, image):
|
||||
"""画像の挿入を検証するためのヘルパーメソッド"""
|
||||
try:
|
||||
# 画像が実際にワークシートに追加されているか確認
|
||||
images_in_sheet = worksheet._images
|
||||
if not images_in_sheet:
|
||||
logging.warning(f"No images found in worksheet at cell {cell.coordinate}")
|
||||
return False
|
||||
|
||||
# 画像のアンカー位置を確認
|
||||
last_image = images_in_sheet[-1]
|
||||
anchor = last_image.anchor
|
||||
if not anchor:
|
||||
logging.warning(f"Image anchor not set properly at cell {cell.coordinate}")
|
||||
return False
|
||||
|
||||
logging.info(f"Image successfully inserted at cell {cell.coordinate}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error verifying image insertion: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
def proceed_one_record(self,table:str,where:str,group_range:str,variables: Dict[str, Any]):
|
||||
|
||||
69
config/fonts.conf
Normal file
69
config/fonts.conf
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<dir>/usr/share/fonts</dir>
|
||||
|
||||
<!-- デフォルトのサンセリフフォントをIPAexGothicに設定 -->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>IPAexGothic</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- デフォルトのセリフフォントをIPAexMinchoに設定 -->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>IPAexMincho</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- MS Gothic の代替としてIPAexGothicを使用 -->
|
||||
<match target="pattern">
|
||||
<test name="family">
|
||||
<string>MS Gothic</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>IPAexGothic</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- MS Mincho の代替としてIPAexMinchoを使用 -->
|
||||
<match target="pattern">
|
||||
<test name="family">
|
||||
<string>MS Mincho</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>IPAexMincho</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- ビットマップフォントを無効化 -->
|
||||
<match target="font">
|
||||
<edit name="embeddedbitmap" mode="assign">
|
||||
<bool>false</bool>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- フォントのヒンティング設定 -->
|
||||
<match target="font">
|
||||
<edit name="hintstyle" mode="assign">
|
||||
<const>hintslight</const>
|
||||
</edit>
|
||||
<edit name="rgba" mode="assign">
|
||||
<const>rgb</const>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- アンチエイリアス設定 -->
|
||||
<match target="font">
|
||||
<edit name="antialias" mode="assign">
|
||||
<bool>true</bool>
|
||||
</edit>
|
||||
</match>
|
||||
</fontconfig>
|
||||
20
rog/utils.py
20
rog/utils.py
@ -1,5 +1,5 @@
|
||||
import os
|
||||
from aiohttp import ClientError
|
||||
from botocore.exceptions import ClientError
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
import logging
|
||||
@ -193,12 +193,12 @@ class S3Bucket:
|
||||
s3_key = os.path.basename(file_path)
|
||||
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if s3_client is None:
|
||||
s3_client = self.connect()
|
||||
if self.s3_client is None:
|
||||
self.s3_client = self.connect()
|
||||
|
||||
# ファイルのアップロード
|
||||
logger.info(f"アップロード開始: {file_path} → s3://{self.bucket_name}/{s3_key}")
|
||||
s3_client.upload_file(file_path, self.bucket_name, s3_key)
|
||||
self.s3_client.upload_file(file_path, self.bucket_name, s3_key)
|
||||
logger.info("アップロード完了")
|
||||
|
||||
return True
|
||||
@ -313,11 +313,11 @@ class S3Bucket:
|
||||
|
||||
try:
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if s3_client is None:
|
||||
s3_client = self.connect()
|
||||
if self.s3_client is None:
|
||||
self.s3_client = self.connect()
|
||||
|
||||
# プレフィックスに一致するオブジェクトをリスト
|
||||
paginator = s3_client.get_paginator('list_objects_v2')
|
||||
paginator = self.s3_client.get_paginator('list_objects_v2')
|
||||
pages = paginator.paginate(Bucket=self.bucket_name, Prefix=prefix)
|
||||
|
||||
for page in pages:
|
||||
@ -361,12 +361,12 @@ class S3Bucket:
|
||||
|
||||
try:
|
||||
# S3クライアントが指定されていない場合は新規作成
|
||||
if s3_client is None:
|
||||
s3_client = self.connect()
|
||||
if self.s3_client is None:
|
||||
self.s3_client = self.connect()
|
||||
|
||||
# オブジェクトの削除
|
||||
logger.info(f"削除開始: s3://{self.bucket_name}/{s3_key}")
|
||||
s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key)
|
||||
self.s3_client.delete_object(Bucket=self.bucket_name, Key=s3_key)
|
||||
logger.info("削除完了")
|
||||
|
||||
return True
|
||||
|
||||
129
rog/views.py
129
rog/views.py
@ -2687,6 +2687,7 @@ def update_checkins_old(request):
|
||||
@api_view(['GET'])
|
||||
def export_excel(request, zekken_number, event_code):
|
||||
temp_dir = None
|
||||
|
||||
try:
|
||||
# パラメータを文字列型に変換
|
||||
zekken_number = str(zekken_number)
|
||||
@ -2763,14 +2764,111 @@ def export_excel(request, zekken_number, event_code):
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
|
||||
# 一時ディレクトリを作成(ASCII文字のみのパスを使用)
|
||||
temp_dir = tempfile.mkdtemp(prefix='lo-')
|
||||
logger.debug(f"Created temp directory: {temp_dir}")
|
||||
|
||||
# ASCII文字のみの作業ディレクトリを作成
|
||||
work_dir = os.path.join(temp_dir, 'work')
|
||||
output_dir = os.path.join(temp_dir, 'output')
|
||||
os.makedirs(work_dir, exist_ok=True)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# すべてのディレクトリに適切な権限を設定
|
||||
for directory in [temp_dir, work_dir, output_dir]:
|
||||
os.chmod(directory, 0o777)
|
||||
logger.debug(f"Set permissions for directory: {directory}")
|
||||
|
||||
# ASCII文字のみの一時ファイル名を使用
|
||||
temp_excel_name = f"certificate_{zekken_number}.xlsx"
|
||||
temp_excel_path = os.path.join(work_dir, temp_excel_name)
|
||||
|
||||
# 元のExcelファイルを作業ディレクトリにコピー
|
||||
shutil.copy2(excel_path, temp_excel_path)
|
||||
os.chmod(temp_excel_path, 0o666)
|
||||
logger.debug(f"Copied Excel file to: {temp_excel_path}")
|
||||
|
||||
|
||||
# LibreOffice設定ディレクトリを作成
|
||||
libreoffice_config_dir = os.path.join(temp_dir, 'libreoffice')
|
||||
os.makedirs(libreoffice_config_dir, exist_ok=True)
|
||||
|
||||
# フォント設定ディレクトリを作成
|
||||
font_conf_dir = os.path.join(temp_dir, 'fonts')
|
||||
os.makedirs(font_conf_dir, exist_ok=True)
|
||||
|
||||
# フォント設定ファイルを作成
|
||||
fonts_conf_content = '''<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<dir>/usr/share/fonts</dir>
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>IPAexGothic</string>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>IPAexMincho</string>
|
||||
</edit>
|
||||
</match>
|
||||
</fontconfig>'''
|
||||
|
||||
font_conf_path = os.path.join(libreoffice_config_dir, 'fonts.conf')
|
||||
with open(font_conf_path, 'w') as f:
|
||||
f.write(fonts_conf_content)
|
||||
|
||||
|
||||
# LibreOfficeのフォント設定を作成
|
||||
registry_dir = os.path.join(libreoffice_config_dir, 'registry')
|
||||
os.makedirs(registry_dir, exist_ok=True)
|
||||
|
||||
# フォント埋め込み設定を作成
|
||||
pdf_export_config = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<oor:data xmlns:oor="http://openoffice.org/2001/registry">
|
||||
<oor:component-data oor:package="org.openoffice.Office.Common" oor:name="Filter">
|
||||
<node oor:name="PDF">
|
||||
<prop oor:name="EmbedFonts" oor:op="fuse">
|
||||
<value>true</value>
|
||||
</prop>
|
||||
<prop oor:name="ExportFormFields" oor:op="fuse">
|
||||
<value>true</value>
|
||||
</prop>
|
||||
<prop oor:name="UseTaggedPDF" oor:op="fuse">
|
||||
<value>true</value>
|
||||
</prop>
|
||||
</node>
|
||||
</oor:component-data>
|
||||
</oor:data>'''
|
||||
|
||||
pdf_config_path = os.path.join(registry_dir, 'pdf_export.xcu')
|
||||
with open(pdf_config_path, 'w') as f:
|
||||
f.write(pdf_export_config)
|
||||
|
||||
# すべてのディレクトリに適切な権限を設定
|
||||
for directory in [temp_dir, work_dir, output_dir,registry_dir]:
|
||||
os.chmod(directory, 0o777)
|
||||
logger.debug(f"Set permissions for directory: {directory}")
|
||||
|
||||
os.chmod(temp_excel_path, 0o666)
|
||||
os.chmod(font_conf_path, 0o666)
|
||||
os.chmod(pdf_config_path, 0o666)
|
||||
|
||||
# フォーマット指定(excel or pdf)
|
||||
format_type = request.query_params.get('format', 'pdf')
|
||||
|
||||
if format_type.lower() == 'pdf':
|
||||
try:
|
||||
# パスとファイル名に分離
|
||||
file_dir = os.path.dirname(excel_path) # パス部分の取得
|
||||
file_name = os.path.basename(excel_path) # ファイル名部分の取得
|
||||
file_dir = os.path.dirname(temp_excel_path) # パス部分の取得
|
||||
file_name = os.path.basename(temp_excel_path) # ファイル名部分の取得
|
||||
|
||||
# ファイル名の拡張子をpdfに変更
|
||||
base_name = os.path.splitext(file_name)[0] # 拡張子を除いたファイル名
|
||||
@ -2785,18 +2883,22 @@ def export_excel(request, zekken_number, event_code):
|
||||
conversion_command = [
|
||||
'soffice', # LibreOfficeの代替コマンド
|
||||
'--headless',
|
||||
'--convert-to',
|
||||
'pdf',
|
||||
'--outdir',
|
||||
file_dir,
|
||||
excel_path
|
||||
'--convert-to', 'pdf:writer_pdf_Export',
|
||||
'--outdir',file_dir,
|
||||
'-env:UserInstallation=file://' + libreoffice_config_dir,
|
||||
temp_excel_path
|
||||
]
|
||||
|
||||
logger.debug(f"Running conversion command: {' '.join(conversion_command)}")
|
||||
|
||||
# 環境変数を設定
|
||||
env = os.environ.copy()
|
||||
env['HOME'] = temp_dir # LibreOfficeの設定ディレクトリを一時ディレクトリに設定
|
||||
#env['HOME'] = temp_dir # LibreOfficeの設定ディレクトリを一時ディレクトリに設定
|
||||
env['HOME'] = temp_dir
|
||||
env['LANG'] = 'ja_JP.UTF-8' # 日本語環境を設定
|
||||
env['LC_ALL'] = 'ja_JP.UTF-8'
|
||||
env['FONTCONFIG_FILE'] = font_conf_path
|
||||
env['FONTCONFIG_PATH'] = font_conf_dir
|
||||
|
||||
# 変換プロセスを実行
|
||||
process = subprocess.run(
|
||||
@ -2804,7 +2906,9 @@ def export_excel(request, zekken_number, event_code):
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
cwd=work_dir,
|
||||
check=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
logger.debug(f"Conversion output: {process.stdout}")
|
||||
@ -2818,9 +2922,10 @@ def export_excel(request, zekken_number, event_code):
|
||||
)
|
||||
|
||||
s3 = S3Bucket('sumasenrogaining')
|
||||
s3.upload_file(pdf_path, f'{event_code}/scoreboard', f'certificate_{zekken_number}.pdf')
|
||||
s3.upload_file(excel_path, f'{event_code}/scoreboard_excel', f'certificate_{zekken_number}.xlsx')
|
||||
s3.upload_file(pdf_path, f'{event_code}/scoreboard/certificate_{zekken_number}.pdf')
|
||||
s3.upload_file(excel_path, f'{event_code}/scoreboard_excel/certificate_{zekken_number}.xlsx')
|
||||
|
||||
os.remove(temp_excel_path)
|
||||
os.remove(excel_path)
|
||||
os.remove(pdf_path)
|
||||
|
||||
@ -2832,7 +2937,7 @@ def export_excel(request, zekken_number, event_code):
|
||||
return Response(
|
||||
{"error": f"PDF conversion failed: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
)
|
||||
finally:
|
||||
# 一時ディレクトリの削除
|
||||
if temp_dir and os.path.exists(temp_dir):
|
||||
|
||||
@ -1196,67 +1196,9 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
// Blobとしてレスポンスを取得
|
||||
const blob = await response.blob();
|
||||
|
||||
// BlobをURLに変換
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
// 印刷方法の選択を提供する関数
|
||||
const printPDF = () => {
|
||||
// IEとその他のブラウザで異なる処理を行う
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
// IEの場合
|
||||
window.navigator.msSaveOrOpenBlob(blob, `通過証明書_${zekkenNumber}_${eventCode}.pdf`);
|
||||
} else {
|
||||
// その他のブラウザの場合
|
||||
// iframeを作成して印刷用のコンテナとして使用
|
||||
const printFrame = document.createElement('iframe');
|
||||
printFrame.style.display = 'none';
|
||||
printFrame.src = url;
|
||||
|
||||
printFrame.onload = () => {
|
||||
try {
|
||||
// iframe内のPDFを印刷
|
||||
printFrame.contentWindow.print();
|
||||
} catch (error) {
|
||||
console.error('印刷プロセス中にエラーが発生しました:', error);
|
||||
// 印刷に失敗した場合、新しいタブでPDFを開く
|
||||
window.open(url, '_blank');
|
||||
} finally {
|
||||
// 少し遅延してからクリーンアップ
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(printFrame);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
document.body.appendChild(printFrame);
|
||||
}
|
||||
};
|
||||
|
||||
// 確認ダイアログを表示
|
||||
const userChoice = window.confirm('PDFを印刷しますか?\n「キャンセル」を選択すると保存できます。');
|
||||
|
||||
if (userChoice) {
|
||||
// 印刷を実行
|
||||
printPDF();
|
||||
} else {
|
||||
// PDFを保存
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `通過証明書_${zekkenNumber}_${eventCode}.pdf`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
// クリーンアップ
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
}
|
||||
// 確認ダイアログを表示
|
||||
const userChoice = window.confirm('PDFを印刷に回しました。');
|
||||
return
|
||||
|
||||
} catch (error) {
|
||||
console.error('エクスポート中にエラーが発生しました:', error);
|
||||
|
||||
Reference in New Issue
Block a user