This commit is contained in:
saqib mir
2026-07-10 11:20:16 +05:30
22 changed files with 720 additions and 54 deletions
@@ -212,6 +212,7 @@ public:
std::string content;
std::string timestamp;
int pageIndex = 0;
double thickness = 0.0;
std::vector<std::vector<Point2D>> paths;
std::vector<std::array<Point2D, 4>> quadPoints;
+1
View File
@@ -166,6 +166,7 @@ private:
std::expected<void, EngineError> applyOp_replaceText(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_reflow(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_textOverlay(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_stamp(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_decoration(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_redaction(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_updateField(const nlohmann::json& op, int pageIndex);
+3 -1
View File
@@ -44,7 +44,7 @@ std::expected<std::vector<InvalidatedRegion>, EngineError> PdfiumDocument::apply
}
static const std::set<std::string> kContentOps = {
"replace_text", "reflow_paragraph", "text_overlay", "add_text",
"replace_text", "reflow_paragraph", "text_overlay", "add_text", "stamp",
"underline", "strikeout", "squiggly", "redaction",
"image_overlay", "highlight", "free_text", "comment", "freehand"};
if (kContentOps.count(type)) markEdited(pageIndex);
@@ -56,6 +56,8 @@ std::expected<std::vector<InvalidatedRegion>, EngineError> PdfiumDocument::apply
r = applyOp_reflow(op, pageIndex);
} else if (type == "text_overlay" || type == "add_text") {
r = applyOp_textOverlay(op, pageIndex);
} else if (type == "stamp") {
r = applyOp_stamp(op, pageIndex);
} else if (type == "underline" || type == "strikeout" || type == "squiggly") {
r = applyOp_decoration(op, pageIndex);
} else if (type == "redaction") {
@@ -1,4 +1,7 @@
#include "parser/pdfium_internal.hpp"
#include <chrono>
#include <iomanip>
#include <sstream>
namespace pdfengine::parser {
@@ -111,6 +114,101 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_textOverlay(const nlohm
#endif
}
std::expected<void, EngineError> PdfiumDocument::applyOp_stamp(const nlohmann::json& op, int pageIndex) {
#ifdef PDFENGINE_WITH_PDFIUM
if (!op.contains("data") || !op["data"].is_object()) {
spdlog::error("stamp operation missing 'data' object");
return std::unexpected(EngineError::InvalidFormat);
}
auto data = op["data"];
std::string text = data.value("text", "");
double x = data.value("x", 0.0);
double y = data.value("y", 0.0);
double width = data.value("width", 100.0);
double height = data.value("height", 30.0);
double fontSize = data.value("fontSize", 18.0);
std::string textColor = data.value("textColor", "#000000");
std::string bgColor = data.value("backgroundColor", "#ffffff");
std::string borderColor = data.value("borderColor", "#000000");
bool includeDate = data.value("includeDate", false);
if (includeDate) {
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %H:%M");
text += "\n" + ss.str();
}
FPDF_PAGE page = FPDF_LoadPage(doc_, pageIndex);
if (!page) {
spdlog::error("Failed to load page index {} for stamp", pageIndex);
return std::unexpected(EngineError::Unknown);
}
// Background rect
FPDF_PAGEOBJECT bgRect = FPDFPageObj_CreateNewRect(
static_cast<float>(x), static_cast<float>(y),
static_cast<float>(width), static_cast<float>(height)
);
unsigned int r=0, g=0, b=0;
parseHexColor(bgColor, r, g, b);
FPDFPageObj_SetFillColor(bgRect, r, g, b, 255);
FPDFPath_SetDrawMode(bgRect, FPDF_FILLMODE_WINDING, 0);
FPDFPage_InsertObject(page, bgRect);
// Border rect
FPDF_PAGEOBJECT borderRect = FPDFPageObj_CreateNewRect(
static_cast<float>(x), static_cast<float>(y),
static_cast<float>(width), static_cast<float>(height)
);
parseHexColor(borderColor, r, g, b);
FPDFPageObj_SetStrokeColor(borderRect, r, g, b, 255);
FPDFPageObj_SetStrokeWidth(borderRect, 2.5f);
FPDFPath_SetDrawMode(borderRect, 0, 1);
FPDFPage_InsertObject(page, borderRect);
FPDF_FONT font = FPDFText_LoadStandardFont(doc_, "Helvetica-Bold");
std::vector<std::string> lines;
std::stringstream textStream(text);
std::string line;
while(std::getline(textStream, line, '\n')) {
lines.push_back(line);
}
float startY = static_cast<float>(y + height - fontSize * 1.1);
parseHexColor(textColor, r, g, b);
for (size_t i = 0; i < lines.size(); i++) {
float currentFontSize = static_cast<float>(i == 0 ? fontSize : fontSize * 0.5);
FPDF_PAGEOBJECT textObj = FPDFPageObj_CreateTextObj(doc_, font, currentFontSize);
FPDFPageObj_SetFillColor(textObj, r, g, b, 255);
auto utf16 = utf8_to_utf16le(lines[i]);
FPDFText_SetText(textObj, reinterpret_cast<FPDF_WIDESTRING>(utf16.data()));
float left=0, bottom=0, right=0, top=0;
FPDFPageObj_GetBounds(textObj, &left, &bottom, &right, &top);
float textWidth = right - left;
float textX = static_cast<float>(x + width / 2.0 - textWidth / 2.0);
float textY = startY - (i * fontSize * 0.7f);
FPDFPageObj_Transform(textObj, 1.0, 0.0, 0.0, 1.0, textX, textY);
FPDFPage_InsertObject(page, textObj);
}
if (!FPDFPage_GenerateContent(page)) {
spdlog::error("Failed to generate page content after stamp");
}
FPDF_ClosePage(page);
return {};
#else
(void)op; (void)pageIndex;
return std::unexpected(EngineError::Unknown);
#endif
}
std::expected<void, EngineError> PdfiumDocument::applyOp_decoration(const nlohmann::json& op, int pageIndex) {
#ifdef PDFENGINE_WITH_PDFIUM
std::string type = op.value("type", "");
@@ -129,6 +227,7 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_decoration(const nlohma
int subtype = FPDF_ANNOT_UNDERLINE;
if (type == "strikeout") subtype = FPDF_ANNOT_STRIKEOUT;
else if (type == "squiggly") subtype = FPDF_ANNOT_SQUIGGLY;
else if (type == "highlight") subtype = FPDF_ANNOT_HIGHLIGHT;
FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, subtype);
if (!annot) {
@@ -383,6 +482,10 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_freehand(const nlohmann
float thickness = static_cast<float>(data.value("thickness", 2.0));
FPDFAnnot_SetBorder(annot, 0.0f, 0.0f, thickness);
std::string tStr = std::to_string(thickness);
auto tUtf16 = utf8_to_utf16le(tStr);
FPDFAnnot_SetStringValue(annot, "CustomThickness", reinterpret_cast<FPDF_WIDESTRING>(tUtf16.data()));
float minX = 1e9f, minY = 1e9f, maxX = -1e9f, maxY = -1e9f;
bool anyPoints = false;
@@ -549,6 +652,10 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_updateAnnotation(const
if (data.contains("thickness")) {
float thickness = static_cast<float>(data["thickness"].get<double>());
FPDFAnnot_SetBorder(targetAnnot, 0.0f, 0.0f, thickness);
std::string tStr = std::to_string(thickness);
auto tUtf16 = utf8_to_utf16le(tStr);
FPDFAnnot_SetStringValue(targetAnnot, "CustomThickness", reinterpret_cast<FPDF_WIDESTRING>(tUtf16.data()));
}
if (data.contains("text")) {
+15
View File
@@ -600,6 +600,21 @@ std::expected<std::vector<PdfPage::AnnotationInfo>, EngineError> PdfiumPage::ext
}
if (subtype == FPDF_ANNOT_INK) {
float h_radius = 0, v_radius = 0, border_width = 1.0f;
info.thickness = 2.0; // default
unsigned long ctLen = FPDFAnnot_GetStringValue(annot, "CustomThickness", nullptr, 0);
if (ctLen > 2) {
std::vector<char16_t> ctBuf(ctLen / 2);
FPDFAnnot_GetStringValue(annot, "CustomThickness", reinterpret_cast<FPDF_WCHAR*>(ctBuf.data()), ctLen);
std::string ctStr = utf16le_to_utf8(ctBuf.data(), ctBuf.size());
try {
info.thickness = std::stod(ctStr);
} catch (...) {}
} else if (FPDFAnnot_GetBorder(annot, &h_radius, &v_radius, &border_width)) {
info.thickness = border_width;
}
const double pageH = height();
unsigned long strokeCount = FPDFAnnot_GetInkListCount(annot);
for (unsigned long s = 0; s < strokeCount; ++s) {