Files
pdf/engine/src/parser/pdfium_edit_pages.cpp
T

173 lines
8.4 KiB
C++

#include "parser/pdfium_internal.hpp"
namespace pdfengine::parser {
std::expected<void, EngineError> PdfiumDocument::applyOp_updateField(const nlohmann::json& op, int pageIndex) {
#ifdef PDFENGINE_WITH_PDFIUM
if (!op.contains("data") || !op["data"].is_object()) {
spdlog::error("update_field operation missing 'data' object");
return std::unexpected(EngineError::InvalidFormat);
}
auto data = op["data"];
const bool isBool = data["value"].is_boolean();
const bool boolVal = isBool && data["value"].get<bool>();
std::string strVal;
if (data["value"].is_string()) strVal = data["value"].get<std::string>();
else if (!isBool) {
spdlog::error("update_field value must be a string or boolean");
return std::unexpected(EngineError::InvalidFormat);
}
std::string targetId = data.value("annotationId", op.value("id", ""));
FPDF_PAGE page = FPDF_LoadPage(doc_, pageIndex);
if (!page) {
spdlog::error("Failed to load page index {} for update_field", pageIndex);
return std::unexpected(EngineError::Unknown);
}
FPDF_FORMFILLINFO formInfo{};
formInfo.version = 2;
FPDF_FORMHANDLE form = FPDFDOC_InitFormFillEnvironment(doc_, &formInfo);
int count = FPDFPage_GetAnnotCount(page);
FPDF_ANNOTATION target = nullptr;
for (int i = 0; i < count; ++i) {
FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, i);
if (!annot) continue;
std::string id;
unsigned long len = FPDFAnnot_GetStringValue(annot, "NM", nullptr, 0);
if (len > 2) {
std::vector<uint8_t> buf(len);
FPDFAnnot_GetStringValue(annot, "NM", reinterpret_cast<FPDF_WCHAR*>(buf.data()), len);
id = utf16le_to_utf8(reinterpret_cast<const char16_t*>(buf.data()), len / sizeof(char16_t));
while (!id.empty() && id.back() == '\0') id.pop_back();
}
if (id.empty()) id = "anno_" + std::to_string(pageIndex) + "_" + std::to_string(i);
if (id == targetId) { target = annot; break; }
FPDFPage_CloseAnnot(annot);
}
if (target) {
int fieldType = form ? FPDFAnnot_GetFormFieldType(form, target) : -1;
if (form) FORM_OnAfterLoadPage(page, form);
if (fieldType == 2 || fieldType == 3) {
std::string state = boolVal ? "Yes" : "Off";
auto u = utf8_to_utf16le(state);
FPDFAnnot_SetStringValue(target, "V", reinterpret_cast<FPDF_WIDESTRING>(u.data()));
FPDFAnnot_SetStringValue(target, "AS", reinterpret_cast<FPDF_WIDESTRING>(u.data()));
} else if (form && FORM_SetFocusedAnnot(form, target)) {
if (fieldType == 4 || fieldType == 5) {
int optCount = FPDFAnnot_GetOptionCount(form, target);
int sel = -1;
for (int o = 0; o < optCount; ++o) {
unsigned long ol = FPDFAnnot_GetOptionLabel(form, target, o, nullptr, 0);
if (ol <= 2) continue;
std::vector<FPDF_WCHAR> ob(ol / 2);
FPDFAnnot_GetOptionLabel(form, target, o, ob.data(), ol);
std::string label = utf16le_to_utf8(reinterpret_cast<const char16_t*>(ob.data()), ob.size());
while (!label.empty() && label.back() == '\0') label.pop_back();
if (label == strVal) { sel = o; break; }
}
if (sel >= 0) {
FORM_SetIndexSelected(form, page, sel, 1);
} else {
auto u = utf8_to_utf16le(strVal);
FPDFAnnot_SetStringValue(target, "V", reinterpret_cast<FPDF_WIDESTRING>(u.data()));
}
} else {
FORM_SelectAllText(form, page);
auto u = utf8_to_utf16le(strVal);
FORM_ReplaceSelection(form, page, reinterpret_cast<FPDF_WIDESTRING>(u.data()));
}
FORM_ForceToKillFocus(form);
} else {
std::string v = isBool ? (boolVal ? "Yes" : "Off") : strVal;
auto u = utf8_to_utf16le(v);
FPDFAnnot_SetStringValue(target, "V", reinterpret_cast<FPDF_WIDESTRING>(u.data()));
}
if (form) FORM_OnBeforeClosePage(page, form);
FPDFPage_CloseAnnot(target);
} else {
spdlog::warn("update_field: field '{}' not found on page {}", targetId, pageIndex);
}
if (form) FPDFDOC_ExitFormFillEnvironment(form);
FPDF_ClosePage(page);
return {};
#else
(void)op; (void)pageIndex;
return std::unexpected(EngineError::Unknown);
#endif
}
std::expected<void, EngineError> PdfiumDocument::applyOp_pageRotation(const nlohmann::json& op, int pageIndex) {
#ifdef PDFENGINE_WITH_PDFIUM
if (!op.contains("data") || !op["data"].is_object()) {
spdlog::error("page_rotation operation missing 'data' object");
return std::unexpected(EngineError::InvalidFormat);
}
auto data = op["data"];
int rotation = data.value("rotation", 0);
FPDF_PAGE page = FPDF_LoadPage(doc_, pageIndex);
if (!page) {
spdlog::error("Failed to load page index {} for page rotation", pageIndex);
return std::unexpected(EngineError::Unknown);
}
int currentCode = FPDFPage_GetRotation(page);
int currentDegrees = currentCode * 90;
int newDegrees = currentDegrees + rotation;
newDegrees = (newDegrees % 360 + 360) % 360;
int newCode = newDegrees / 90;
FPDFPage_SetRotation(page, newCode);
FPDF_ClosePage(page);
return {};
#else
(void)op; (void)pageIndex;
return std::unexpected(EngineError::Unknown);
#endif
}
#ifdef PDFENGINE_WITH_PDFIUM
std::expected<void, EngineError> PdfiumDocument::applyOp_pageDeletion(const nlohmann::json& op, int pageIndex) {
if (pageCount() <= 1) {
spdlog::error("Cannot delete the only page in the document");
return std::unexpected(EngineError::Unknown);
}
FPDFPage_Delete(doc_, pageIndex);
return {};
}
#endif
#ifdef PDFENGINE_WITH_PDFIUM
std::expected<void, EngineError> PdfiumDocument::applyOp_pageReorder(const nlohmann::json& op, int pageIndex) {
if (!op.contains("data") || !op["data"].is_object()) {
spdlog::error("page_reorder operation missing 'data' object");
return std::unexpected(EngineError::InvalidFormat);
}
auto data = op["data"];
if (!data.contains("destPageIndex")) {
spdlog::error("page_reorder data missing 'destPageIndex'");
return std::unexpected(EngineError::InvalidFormat);
}
int destPageIndex = data["destPageIndex"];
if (destPageIndex < 0 || destPageIndex >= pageCount()) {
spdlog::error("Destination page index {} out of bounds (total pages: {})", destPageIndex, pageCount());
return std::unexpected(EngineError::PageOutOfBounds);
}
int fromIndex = pageIndex;
if (!FPDF_MovePages(doc_, &fromIndex, 1, destPageIndex)) {
spdlog::error("FPDF_MovePages failed from {} to {}", fromIndex, destPageIndex);
return std::unexpected(EngineError::Unknown);
}
return {};
}
#endif
}