建立檔案
This commit is contained in:
BIN
__pycache__/app.cpython-312.pyc
Normal file
BIN
__pycache__/app.cpython-312.pyc
Normal file
Binary file not shown.
91
app.py
91
app.py
@@ -1,11 +1,8 @@
|
|||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from reportlab.pdfgen import canvas
|
from openpyxl import Workbook
|
||||||
from reportlab.lib.pagesizes import A4
|
from openpyxl.styles import Font, Alignment
|
||||||
from reportlab.pdfbase.ttfonts import TTFont
|
|
||||||
from reportlab.pdfbase import pdfmetrics
|
|
||||||
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
||||||
import uuid, os
|
import uuid, os
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -13,37 +10,79 @@ app = FastAPI()
|
|||||||
os.makedirs("static", exist_ok=True)
|
os.makedirs("static", exist_ok=True)
|
||||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
|
|
||||||
pdfmetrics.registerFont(TTFont("TWFont", "NotoSansTC-Medium.ttf"))
|
|
||||||
|
|
||||||
class TextRequest(BaseModel):
|
class TextRequest(BaseModel):
|
||||||
content: str
|
content: str
|
||||||
|
separator: str = "\t" # 預設使用 Tab 分隔符
|
||||||
|
has_header: bool = False # 是否第一行是標題
|
||||||
|
|
||||||
@app.post("/generate-pdf")
|
@app.post("/generate-excel")
|
||||||
def generate_pdf(data: TextRequest, request: Request):
|
def generate_excel(data: TextRequest, request: Request):
|
||||||
filename = f"{uuid.uuid4()}.pdf"
|
filename = f"{uuid.uuid4()}.xlsx"
|
||||||
filepath = os.path.join("static", filename)
|
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 row_idx, line in enumerate(lines, start=start_row):
|
||||||
for i in range(len(line), 0, -1):
|
columns_data = line.split(data.separator)
|
||||||
if stringWidth(line[:i], "TWFont", 12) <= max_line_width:
|
|
||||||
text_obj.textLine(line[:i])
|
# 確保每行都有足夠的欄位
|
||||||
line = line[i:]
|
while len(columns_data) < num_columns:
|
||||||
break
|
columns_data.append("")
|
||||||
text_obj.textLine(line)
|
|
||||||
|
# 寫入每個欄位的資料
|
||||||
|
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()
|
for col_idx in range(1, num_columns + 1):
|
||||||
c.save()
|
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 {
|
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)
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
fastapi
|
fastapi
|
||||||
uvicorn
|
uvicorn
|
||||||
pydantic
|
pydantic
|
||||||
reportlab
|
openpyxl
|
||||||
python-multipart
|
python-multipart
|
||||||
|
Reference in New Issue
Block a user