From ff61bab382d132629a1a597f4f6f506a40904720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E4=BD=A9=E5=BA=AD?= Date: Sun, 7 Sep 2025 16:58:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E6=AA=94=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/app.cpython-312.pyc | Bin 0 -> 4032 bytes app.py | 91 +++++++++++++++++++++++--------- requirements.txt | 2 +- 3 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 __pycache__/app.cpython-312.pyc diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0feb2f4863721e05f916f7bb12b8ae66a0865a2 GIT binary patch literal 4032 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 literal 0 HcmV?d00001 diff --git a/app.py b/app.py index 64a3e4c..8f7f717 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,8 @@ from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from pydantic import BaseModel -from reportlab.pdfgen import canvas -from reportlab.lib.pagesizes import A4 -from reportlab.pdfbase.ttfonts import TTFont -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfbase.pdfmetrics import stringWidth +from openpyxl import Workbook +from openpyxl.styles import Font, Alignment import uuid, os app = FastAPI() @@ -13,37 +10,79 @@ app = FastAPI() os.makedirs("static", exist_ok=True) app.mount("/static", StaticFiles(directory="static"), name="static") -pdfmetrics.registerFont(TTFont("TWFont", "NotoSansTC-Medium.ttf")) class TextRequest(BaseModel): content: str + separator: str = "\t" # 預設使用 Tab 分隔符 + has_header: bool = False # 是否第一行是標題 -@app.post("/generate-pdf") -def generate_pdf(data: TextRequest, request: Request): - filename = f"{uuid.uuid4()}.pdf" +@app.post("/generate-excel") +def generate_excel(data: TextRequest, request: Request): + filename = f"{uuid.uuid4()}.xlsx" filepath = os.path.join("static", filename) - c = canvas.Canvas(filepath, pagesize=A4) - c.setFont("TWFont", 12) + # 創建新的工作簿 + wb = Workbook() + ws = wb.active + ws.title = "文字內容" - text_obj = c.beginText(50, 800) - text_obj.setFont("TWFont", 12) + # 將文字內容按行分割 + lines = data.content.splitlines() + + if not lines: + return {"error": "沒有提供文字內容"} - max_line_width = A4[0] - 50 * 2 # 頁寬扣左右邊距 + # 分析第一行來確定欄位數量 + first_line = lines[0] + columns = first_line.split(data.separator) + num_columns = len(columns) + + # 設置標題(如果有指定) + start_row = 1 + if data.has_header and len(lines) > 1: + # 第一行作為標題 + for col_idx, header in enumerate(columns, 1): + cell = ws.cell(row=1, column=col_idx, value=header.strip()) + cell.font = Font(bold=True, size=12) + cell.alignment = Alignment(horizontal='center') + start_row = 2 + lines = lines[1:] # 跳過標題行 + else: + # 沒有標題,第一行也是資料 + lines = lines - for line in data.content.splitlines(): - while stringWidth(line, "TWFont", 12) > max_line_width: - for i in range(len(line), 0, -1): - if stringWidth(line[:i], "TWFont", 12) <= max_line_width: - text_obj.textLine(line[:i]) - line = line[i:] - break - text_obj.textLine(line) + # 寫入資料 + for row_idx, line in enumerate(lines, start=start_row): + columns_data = line.split(data.separator) + + # 確保每行都有足夠的欄位 + while len(columns_data) < num_columns: + columns_data.append("") + + # 寫入每個欄位的資料 + for col_idx, cell_data in enumerate(columns_data[:num_columns], 1): + cell = ws.cell(row=row_idx, column=col_idx, value=cell_data.strip()) + cell.alignment = Alignment(wrap_text=True) - c.drawText(text_obj) - c.showPage() - c.save() + # 自動調整列寬 + for col_idx in range(1, num_columns + 1): + column_letter = ws.cell(row=1, column=col_idx).column_letter + max_length = 0 + + for row in ws.iter_rows(min_col=col_idx, max_col=col_idx): + for cell in row: + if cell.value: + max_length = max(max_length, len(str(cell.value))) + + # 設置列寬,最小10,最大50 + adjusted_width = min(max(max_length + 2, 10), 50) + ws.column_dimensions[column_letter].width = adjusted_width + + # 保存文件 + wb.save(filepath) return { - "download_url": str(request.base_url).rstrip("/") + f"/static/{filename}" + "download_url": str(request.base_url).rstrip("/") + f"/static/{filename}", + "columns": num_columns, + "rows": len(lines) + (1 if data.has_header else 0) } diff --git a/requirements.txt b/requirements.txt index e7caa4e..c90cf89 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ fastapi uvicorn pydantic -reportlab +openpyxl python-multipart