109 lines
4.1 KiB
Python
109 lines
4.1 KiB
Python
import os
|
|
import tempfile
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
|
|
from app.schemas.text_object import TextObjectResponse, UpdateTextObjectRequest
|
|
from app.services import engine
|
|
from app.services.store import document_store
|
|
|
|
router = APIRouter(tags=["documents"])
|
|
|
|
|
|
@router.get(
|
|
"/{document_id}/pages/{page_index}/text_objects",
|
|
response_model=list[TextObjectResponse],
|
|
)
|
|
def get_text_objects(document_id: str, page_index: int) -> list[TextObjectResponse]:
|
|
if not engine.is_available():
|
|
raise HTTPException(status_code=501, detail="Engine not available")
|
|
|
|
doc_info = document_store.get_document(document_id)
|
|
if not doc_info:
|
|
raise HTTPException(status_code=404, detail="Document not found")
|
|
|
|
_perms = doc_info.get("permissions") or {}
|
|
if _perms.get("canModify", True) is False:
|
|
raise HTTPException(status_code=403, detail="Raw Text editing is not permitted by this document's restrictions (canModify).")
|
|
|
|
pdfengine = engine.require()
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
|
|
tmp.write(doc_info["bytes_data"])
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
editor = pdfengine.StreamEditor(tmp_path)
|
|
objects = editor.extract_text_objects(page_index)
|
|
|
|
result = []
|
|
for obj in objects:
|
|
text_str = obj["text"].decode("latin-1") if isinstance(obj["text"], bytes) else obj["text"]
|
|
result.append(TextObjectResponse(
|
|
text=text_str,
|
|
fontName=obj["fontName"],
|
|
fontSize=obj["fontSize"],
|
|
tm=obj["tm"]
|
|
))
|
|
return result
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
finally:
|
|
if os.path.exists(tmp_path):
|
|
os.remove(tmp_path)
|
|
|
|
|
|
@router.put("/{document_id}/pages/{page_index}/text_objects/{object_index}")
|
|
def replace_text_object(document_id: str, page_index: int, object_index: int, req: UpdateTextObjectRequest) -> dict:
|
|
if not engine.is_available():
|
|
raise HTTPException(status_code=501, detail="Engine not available")
|
|
|
|
doc_info = document_store.get_document(document_id)
|
|
if not doc_info:
|
|
raise HTTPException(status_code=404, detail="Document not found")
|
|
|
|
_perms = doc_info.get("permissions") or {}
|
|
if _perms.get("canModify", True) is False:
|
|
raise HTTPException(status_code=403, detail="Raw Text editing is not permitted by this document's restrictions (canModify).")
|
|
|
|
pdfengine = engine.require()
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
|
|
tmp.write(doc_info["bytes_data"])
|
|
tmp_path = tmp.name
|
|
|
|
out_path = tmp_path + ".out.pdf"
|
|
|
|
try:
|
|
editor = pdfengine.StreamEditor(tmp_path)
|
|
try:
|
|
new_text_bytes = req.new_text.encode("latin-1")
|
|
except UnicodeEncodeError as enc_err:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Some characters can't be encoded in this run's font. Raw Text supports same-charset edits only — use Edit text to add new characters.",
|
|
) from enc_err
|
|
success = editor.replace_text_object(page_index, object_index, new_text_bytes, out_path)
|
|
if not success:
|
|
raise HTTPException(status_code=400, detail="Failed to replace text object (not found or identical)")
|
|
|
|
with open(out_path, "rb") as f:
|
|
new_bytes = f.read()
|
|
new_doc = pdfengine.PdfDocument.load_from_memory(new_bytes, "")
|
|
new_info = document_store.add_document(
|
|
filename=doc_info["filename"], bytes_data=new_bytes, doc_instance=new_doc,
|
|
permissions=doc_info.get("permissions"),
|
|
)
|
|
if "remote_context" in doc_info:
|
|
new_info["remote_context"] = doc_info["remote_context"]
|
|
return {"success": True, "newDocumentId": new_info["id"]}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e)) from e
|
|
finally:
|
|
if os.path.exists(tmp_path):
|
|
os.remove(tmp_path)
|
|
if os.path.exists(out_path):
|
|
os.remove(out_path)
|