This commit is contained in:
Furqan-14
2026-06-17 11:39:58 +05:30
40 changed files with 2633 additions and 5 deletions
+101
View File
@@ -737,3 +737,104 @@ def export_document(document_id: str):
)
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
class TextObjectResponse(BaseModel):
text: str
fontName: str
fontSize: float
tm: list[float]
@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")
pdfengine = engine.require()
import os
# For StreamEditor we need a real file path. If it's loaded from memory, we need to save it to a temp file.
# In this MVP, we assume the file was saved somewhere, but actually document_store keeps it in memory.
# Wait, doc_store has doc_info["filename"] and bytes_data. Let's write bytes to a temp file.
import tempfile
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:
# obj["text"] is now returned as bytes from C++
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)
class UpdateTextObjectRequest(BaseModel):
new_text: str
@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")
pdfengine = engine.require()
import tempfile
import os
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)
# Convert frontend string back to exact bytes using latin-1
new_text_bytes = req.new_text.encode("latin-1")
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)")
# Update the document store with the new bytes
with open(out_path, "rb") as f:
new_bytes = f.read()
# Reload doc instance
doc_instance = pdfengine.PdfDocument.load_from_memory(new_bytes, "")
# Update the store
doc_info["bytes_data"] = new_bytes
doc_info["doc_instance"] = doc_instance
return {"success": True}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
if os.path.exists(tmp_path):
os.remove(tmp_path)
if os.path.exists(out_path):
os.remove(out_path)