173 lines
8.4 KiB
C++
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
|
|
|
|
} |