Compare commits
3 Commits
e1928564fa
...
2ca77b604b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ca77b604b | |||
| 27aed10a4a | |||
| e6e6d059ac |
@ -45,8 +45,36 @@ RUN apt-get update && \
|
|||||||
libreoffice \
|
libreoffice \
|
||||||
libreoffice-calc \
|
libreoffice-calc \
|
||||||
libreoffice-writer \
|
libreoffice-writer \
|
||||||
|
libreoffice-java-common \
|
||||||
|
fonts-ipafont \
|
||||||
|
fonts-ipafont-gothic \
|
||||||
|
fonts-ipafont-mincho \
|
||||||
|
language-pack-ja \
|
||||||
|
fontconfig \
|
||||||
|
locales \
|
||||||
python3-uno # LibreOffice Python バインディング
|
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 && \
|
RUN mkdir -p /app/docbase /tmp/libreoffice && \
|
||||||
chmod -R 777 /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
|
COPY ./requirements.txt /app/requirements.txt
|
||||||
|
|
||||||
|
RUN pip install boto3==1.26.137
|
||||||
|
|
||||||
# Install Gunicorn
|
# Install Gunicorn
|
||||||
RUN pip install gunicorn
|
RUN pip install gunicorn
|
||||||
|
|
||||||
|
|||||||
@ -305,6 +305,12 @@ class SumasenExcel:
|
|||||||
logging.error(f"Error in proceed_group: {str(e)}")
|
logging.error(f"Error in proceed_group: {str(e)}")
|
||||||
return {"status": False, "message": f"Exception in proceed_group : Error generating report: {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):
|
def format_cell_value(self, field_value, cell):
|
||||||
"""セルの値を適切な形式に変換する
|
"""セルの値を適切な形式に変換する
|
||||||
@ -370,6 +376,7 @@ class SumasenExcel:
|
|||||||
# ワークシートを取得
|
# ワークシートを取得
|
||||||
worksheet = cell.parent
|
worksheet = cell.parent
|
||||||
|
|
||||||
|
logging.info('step-1')
|
||||||
try:
|
try:
|
||||||
# 列の幅を取得(文字単位からピクセルに変換)
|
# 列の幅を取得(文字単位からピクセルに変換)
|
||||||
column_letter = get_column_letter(cell.column)
|
column_letter = get_column_letter(cell.column)
|
||||||
@ -378,6 +385,7 @@ class SumasenExcel:
|
|||||||
except Exception:
|
except Exception:
|
||||||
cell_width = 100 # デフォルト値
|
cell_width = 100 # デフォルト値
|
||||||
|
|
||||||
|
logging.info('step-2')
|
||||||
try:
|
try:
|
||||||
# 行の高さを取得(ポイント単位からピクセルに変換)
|
# 行の高さを取得(ポイント単位からピクセルに変換)
|
||||||
row_height = worksheet.row_dimensions[cell.row].height
|
row_height = worksheet.row_dimensions[cell.row].height
|
||||||
@ -385,6 +393,7 @@ class SumasenExcel:
|
|||||||
except Exception:
|
except Exception:
|
||||||
cell_height = 20 # デフォルト値
|
cell_height = 20 # デフォルト値
|
||||||
|
|
||||||
|
logging.info('step-3')
|
||||||
# マージンの設定(ピクセル単位)
|
# マージンの設定(ピクセル単位)
|
||||||
margin_horizontal = 4 # 左右のマージン
|
margin_horizontal = 4 # 左右のマージン
|
||||||
margin_vertical = 2 # 上下のマージン
|
margin_vertical = 2 # 上下のマージン
|
||||||
@ -393,6 +402,7 @@ class SumasenExcel:
|
|||||||
effective_cell_width = cell_width - (margin_horizontal * 2)
|
effective_cell_width = cell_width - (margin_horizontal * 2)
|
||||||
effective_cell_height = cell_height - (margin_vertical * 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_width = max(effective_cell_width, 92) # 100 - (4 * 2)
|
||||||
effective_cell_height = max(effective_cell_height, 16) # 20 - (2 * 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_width = min(effective_cell_width, max_width)
|
||||||
effective_cell_height = min(effective_cell_height, max_height)
|
effective_cell_height = min(effective_cell_height, max_height)
|
||||||
|
|
||||||
|
logging.info('step-5')
|
||||||
# アスペクト比を保持しながらリサイズ
|
# アスペクト比を保持しながらリサイズ
|
||||||
img_width, img_height = img.size
|
img_width, img_height = img.size
|
||||||
img_aspect = img_width / img_height
|
img_aspect = img_width / img_height
|
||||||
@ -415,6 +426,10 @@ class SumasenExcel:
|
|||||||
height = effective_cell_height
|
height = effective_cell_height
|
||||||
width = int(height * img_aspect)
|
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)
|
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_resized.save(img_byte_arr, format='JPEG', quality=85, optimize=True)
|
||||||
img_byte_arr.seek(0)
|
img_byte_arr.seek(0)
|
||||||
|
|
||||||
|
|
||||||
|
logging.info('step-7')
|
||||||
# OpenPyXLのImageオブジェクトを作成
|
# OpenPyXLのImageオブジェクトを作成
|
||||||
from openpyxl.drawing.image import Image as XLImage
|
|
||||||
excel_image = XLImage(img_byte_arr)
|
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.drawing.spreadsheet_drawing import OneCellAnchor, AnchorMarker
|
||||||
from openpyxl.utils.units import pixels_to_EMU
|
from openpyxl.drawing.xdr import XDRPositiveSize2D
|
||||||
|
|
||||||
# セルの左上を基準に、マージン分オフセットした位置に配置
|
logging.info('step-8')
|
||||||
col_offset = pixels_to_EMU(margin_horizontal)
|
marker = AnchorMarker(
|
||||||
row_offset = pixels_to_EMU(margin_vertical)
|
col=cell.column - 1,
|
||||||
|
colOff=pixels_to_EMU(margin_horizontal),
|
||||||
|
row=cell.row - 1,
|
||||||
|
rowOff=pixels_to_EMU(margin_vertical)
|
||||||
|
)
|
||||||
|
|
||||||
# マージンを考慮した配置
|
# XDRPositiveSize2Dを使用して画像サイズを設定
|
||||||
marker = AnchorMarker(col=cell.column - 1, colOff=col_offset,
|
size = XDRPositiveSize2D(
|
||||||
row=cell.row - 1, rowOff=row_offset)
|
cx=pixels_to_EMU(width),
|
||||||
anchor = OneCellAnchor(_from=marker, ext=None)
|
cy=pixels_to_EMU(height)
|
||||||
|
)
|
||||||
|
|
||||||
|
anchor = OneCellAnchor(_from=marker, ext=size)
|
||||||
excel_image.anchor = anchor
|
excel_image.anchor = anchor
|
||||||
|
|
||||||
|
|
||||||
|
logging.info('step-9')
|
||||||
# 画像をワークシートに追加
|
# 画像をワークシートに追加
|
||||||
worksheet.add_image(excel_image)
|
worksheet.add_image(excel_image)
|
||||||
|
#cell.parent.add_image(excel_image)
|
||||||
|
|
||||||
|
logging.info('step-A')
|
||||||
|
# メモリ解放
|
||||||
|
#img_byte_arr.close()
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@ -467,7 +500,28 @@ class SumasenExcel:
|
|||||||
# その他の場合は文字列に変換
|
# その他の場合は文字列に変換
|
||||||
return str(field_value)
|
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]):
|
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
|
import os
|
||||||
from aiohttp import ClientError
|
from botocore.exceptions import ClientError
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import logging
|
import logging
|
||||||
@ -193,12 +193,12 @@ class S3Bucket:
|
|||||||
s3_key = os.path.basename(file_path)
|
s3_key = os.path.basename(file_path)
|
||||||
|
|
||||||
# S3クライアントが指定されていない場合は新規作成
|
# S3クライアントが指定されていない場合は新規作成
|
||||||
if s3_client is None:
|
if self.s3_client is None:
|
||||||
s3_client = self.connect()
|
self.s3_client = self.connect()
|
||||||
|
|
||||||
# ファイルのアップロード
|
# ファイルのアップロード
|
||||||
logger.info(f"アップロード開始: {file_path} → s3://{self.bucket_name}/{s3_key}")
|
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("アップロード完了")
|
logger.info("アップロード完了")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -313,11 +313,11 @@ class S3Bucket:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# S3クライアントが指定されていない場合は新規作成
|
# S3クライアントが指定されていない場合は新規作成
|
||||||
if s3_client is None:
|
if self.s3_client is None:
|
||||||
s3_client = self.connect()
|
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)
|
pages = paginator.paginate(Bucket=self.bucket_name, Prefix=prefix)
|
||||||
|
|
||||||
for page in pages:
|
for page in pages:
|
||||||
@ -361,12 +361,12 @@ class S3Bucket:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# S3クライアントが指定されていない場合は新規作成
|
# S3クライアントが指定されていない場合は新規作成
|
||||||
if s3_client is None:
|
if self.s3_client is None:
|
||||||
s3_client = self.connect()
|
self.s3_client = self.connect()
|
||||||
|
|
||||||
# オブジェクトの削除
|
# オブジェクトの削除
|
||||||
logger.info(f"削除開始: s3://{self.bucket_name}/{s3_key}")
|
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("削除完了")
|
logger.info("削除完了")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
129
rog/views.py
129
rog/views.py
@ -2687,6 +2687,7 @@ def update_checkins_old(request):
|
|||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
def export_excel(request, zekken_number, event_code):
|
def export_excel(request, zekken_number, event_code):
|
||||||
temp_dir = None
|
temp_dir = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# パラメータを文字列型に変換
|
# パラメータを文字列型に変換
|
||||||
zekken_number = str(zekken_number)
|
zekken_number = str(zekken_number)
|
||||||
@ -2763,14 +2764,111 @@ def export_excel(request, zekken_number, event_code):
|
|||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
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)
|
# フォーマット指定(excel or pdf)
|
||||||
format_type = request.query_params.get('format', 'pdf')
|
format_type = request.query_params.get('format', 'pdf')
|
||||||
|
|
||||||
if format_type.lower() == 'pdf':
|
if format_type.lower() == 'pdf':
|
||||||
try:
|
try:
|
||||||
# パスとファイル名に分離
|
# パスとファイル名に分離
|
||||||
file_dir = os.path.dirname(excel_path) # パス部分の取得
|
file_dir = os.path.dirname(temp_excel_path) # パス部分の取得
|
||||||
file_name = os.path.basename(excel_path) # ファイル名部分の取得
|
file_name = os.path.basename(temp_excel_path) # ファイル名部分の取得
|
||||||
|
|
||||||
# ファイル名の拡張子をpdfに変更
|
# ファイル名の拡張子をpdfに変更
|
||||||
base_name = os.path.splitext(file_name)[0] # 拡張子を除いたファイル名
|
base_name = os.path.splitext(file_name)[0] # 拡張子を除いたファイル名
|
||||||
@ -2785,18 +2883,22 @@ def export_excel(request, zekken_number, event_code):
|
|||||||
conversion_command = [
|
conversion_command = [
|
||||||
'soffice', # LibreOfficeの代替コマンド
|
'soffice', # LibreOfficeの代替コマンド
|
||||||
'--headless',
|
'--headless',
|
||||||
'--convert-to',
|
'--convert-to', 'pdf:writer_pdf_Export',
|
||||||
'pdf',
|
'--outdir',file_dir,
|
||||||
'--outdir',
|
'-env:UserInstallation=file://' + libreoffice_config_dir,
|
||||||
file_dir,
|
temp_excel_path
|
||||||
excel_path
|
|
||||||
]
|
]
|
||||||
|
|
||||||
logger.debug(f"Running conversion command: {' '.join(conversion_command)}")
|
logger.debug(f"Running conversion command: {' '.join(conversion_command)}")
|
||||||
|
|
||||||
# 環境変数を設定
|
# 環境変数を設定
|
||||||
env = os.environ.copy()
|
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(
|
process = subprocess.run(
|
||||||
@ -2804,7 +2906,9 @@ def export_excel(request, zekken_number, event_code):
|
|||||||
env=env,
|
env=env,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
check=True
|
cwd=work_dir,
|
||||||
|
check=True,
|
||||||
|
timeout=30
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug(f"Conversion output: {process.stdout}")
|
logger.debug(f"Conversion output: {process.stdout}")
|
||||||
@ -2818,9 +2922,10 @@ def export_excel(request, zekken_number, event_code):
|
|||||||
)
|
)
|
||||||
|
|
||||||
s3 = S3Bucket('sumasenrogaining')
|
s3 = S3Bucket('sumasenrogaining')
|
||||||
s3.upload_file(pdf_path, f'{event_code}/scoreboard', f'certificate_{zekken_number}.pdf')
|
s3.upload_file(pdf_path, f'{event_code}/scoreboard/certificate_{zekken_number}.pdf')
|
||||||
s3.upload_file(excel_path, f'{event_code}/scoreboard_excel', f'certificate_{zekken_number}.xlsx')
|
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(excel_path)
|
||||||
os.remove(pdf_path)
|
os.remove(pdf_path)
|
||||||
|
|
||||||
@ -2832,7 +2937,7 @@ def export_excel(request, zekken_number, event_code):
|
|||||||
return Response(
|
return Response(
|
||||||
{"error": f"PDF conversion failed: {str(e)}"},
|
{"error": f"PDF conversion failed: {str(e)}"},
|
||||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
# 一時ディレクトリの削除
|
# 一時ディレクトリの削除
|
||||||
if temp_dir and os.path.exists(temp_dir):
|
if temp_dir and os.path.exists(temp_dir):
|
||||||
|
|||||||
@ -1196,67 +1196,9 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
// 確認ダイアログを表示
|
||||||
// Blobとしてレスポンスを取得
|
const userChoice = window.confirm('PDFを印刷に回しました。');
|
||||||
const blob = await response.blob();
|
return
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('エクスポート中にエラーが発生しました:', error);
|
console.error('エクスポート中にエラーが発生しました:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user