Merge branch 'dev' of https://gitea.maskantech.in/gitea_admin/pdf into furqan
This commit is contained in:
@@ -17,6 +17,7 @@ add_library(pdfengine STATIC
|
||||
src/parser/pdfium_loader.cpp
|
||||
src/parser/pdfium_document.cpp
|
||||
src/parser/content_stream_parser.cpp
|
||||
src/parser/decoration_builder.cpp
|
||||
src/text/selection.cpp
|
||||
src/fonts/face/font_face.cpp
|
||||
src/fonts/face/free_type_manager.cpp
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "decoration_builder.hpp"
|
||||
|
||||
namespace pdfengine {
|
||||
|
||||
Path DecorationBuilder::buildUnderline(float x, float y, float width, float thickness, float offset) {
|
||||
Path p;
|
||||
// An underline is essentially a thin filled rectangle at (y + offset).
|
||||
// The caller is responsible for supplying the correctly signed offset.
|
||||
p.addRect(x, y + offset, width, thickness);
|
||||
return p;
|
||||
}
|
||||
|
||||
Path DecorationBuilder::buildStrikeout(float x, float y, float width, float thickness, float offset) {
|
||||
Path p;
|
||||
// A strikeout is similarly a thin filled rectangle, positioned higher up.
|
||||
p.addRect(x, y + offset, width, thickness);
|
||||
return p;
|
||||
}
|
||||
|
||||
Path DecorationBuilder::buildSquiggly(float x, float y, float width, float amplitude, float frequency) {
|
||||
Path p;
|
||||
if (width <= 0.0f) {
|
||||
return p;
|
||||
}
|
||||
|
||||
p.moveTo(x, y);
|
||||
|
||||
float currentX = x;
|
||||
float endX = x + width;
|
||||
|
||||
// Create a jagged squiggly line using line segments.
|
||||
// This is drawn as a stroked path rather than a filled rect.
|
||||
bool up = true;
|
||||
while (currentX < endX) {
|
||||
float nextX = currentX + (frequency / 2.0f);
|
||||
if (nextX > endX) {
|
||||
nextX = endX;
|
||||
// Adjust the final Y to keep the slope somewhat consistent if chopped early
|
||||
float ratio = (nextX - currentX) / (frequency / 2.0f);
|
||||
float nextY = y + (up ? amplitude : -amplitude) * ratio;
|
||||
p.lineTo(nextX, nextY);
|
||||
break;
|
||||
}
|
||||
|
||||
float nextY = y + (up ? amplitude : -amplitude);
|
||||
p.lineTo(nextX, nextY);
|
||||
|
||||
currentX = nextX;
|
||||
up = !up;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace pdfengine
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <pdfengine/path.hpp>
|
||||
|
||||
namespace pdfengine {
|
||||
|
||||
// A utility class to generate vector paths for text markup and decorations
|
||||
class DecorationBuilder {
|
||||
public:
|
||||
// Builds a path representing a straight underline.
|
||||
// x, y: starting coordinates (usually the baseline origin)
|
||||
// width: length of the underline
|
||||
// thickness: thickness of the line (used to build a thin rectangle)
|
||||
// offset: vertical offset from y
|
||||
static Path buildUnderline(float x, float y, float width, float thickness = 1.0f, float offset = -2.0f);
|
||||
|
||||
// Builds a path representing a strikeout line.
|
||||
// x, y: starting coordinates (baseline)
|
||||
// width: length of the strikeout
|
||||
// thickness: thickness of the line
|
||||
// offset: vertical offset from y (typically goes up through the text)
|
||||
static Path buildStrikeout(float x, float y, float width, float thickness = 1.0f, float offset = 4.0f);
|
||||
|
||||
// Builds a path representing a squiggly underline (often used for spelling or grammar highlights).
|
||||
// x, y: starting coordinates
|
||||
// width: length of the squiggly
|
||||
// amplitude: height of the squiggly waves
|
||||
// frequency: horizontal width of a single wave cycle
|
||||
static Path buildSquiggly(float x, float y, float width, float amplitude = 2.0f, float frequency = 4.0f);
|
||||
};
|
||||
|
||||
} // namespace pdfengine
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "fonts/pdf_fonts/font_fallback.hpp"
|
||||
#include "fonts/pdf_fonts/font_subset.hpp"
|
||||
#include "fonts/shaping/hb_shaper.hpp"
|
||||
#include "decoration_builder.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
@@ -2261,6 +2262,80 @@ std::expected<void, EngineError> PdfiumDocument::applyEdits(const std::string& e
|
||||
return std::unexpected(EngineError::Unknown);
|
||||
}
|
||||
|
||||
FPDF_ClosePage(page);
|
||||
} else if (type == "underline" || type == "strikeout" || type == "squiggly") {
|
||||
if (!op.contains("data") || !op["data"].is_object()) {
|
||||
spdlog::error("{} operation missing 'data' object", type);
|
||||
return std::unexpected(EngineError::InvalidFormat);
|
||||
}
|
||||
auto data = op["data"];
|
||||
double x = data.value("x", 0.0);
|
||||
double y = data.value("y", 0.0);
|
||||
double width = data.value("width", 0.0);
|
||||
double thickness = data.value("thickness", 1.0);
|
||||
std::string color = data.value("color", "#000000");
|
||||
|
||||
spdlog::info("Parsed {} operation: x={}, y={}, width={}", type, x, y, width);
|
||||
|
||||
FPDF_PAGE page = FPDF_LoadPage(doc_, pageIndex);
|
||||
if (!page) {
|
||||
spdlog::error("Failed to load page index {} for decoration", pageIndex);
|
||||
return std::unexpected(EngineError::Unknown);
|
||||
}
|
||||
|
||||
pdfengine::Path path;
|
||||
if (type == "underline") {
|
||||
path = DecorationBuilder::buildUnderline(x, y, width, thickness);
|
||||
} else if (type == "strikeout") {
|
||||
path = DecorationBuilder::buildStrikeout(x, y, width, thickness);
|
||||
} else if (type == "squiggly") {
|
||||
path = DecorationBuilder::buildSquiggly(x, y, width);
|
||||
}
|
||||
|
||||
if (!path.empty()) {
|
||||
const auto& segments = path.segments();
|
||||
float startX = segments[0].points[0].x;
|
||||
float startY = segments[0].points[0].y;
|
||||
FPDF_PAGEOBJECT pathObj = FPDFPageObj_CreateNewPath(startX, startY);
|
||||
|
||||
bool isFill = (type != "squiggly");
|
||||
bool isStroke = (type == "squiggly");
|
||||
|
||||
for (size_t i = 1; i < segments.size(); ++i) { // Start from 1 to skip first MoveTo
|
||||
const auto& seg = segments[i];
|
||||
if (seg.verb == Path::Verb::MoveTo) {
|
||||
FPDFPath_MoveTo(pathObj, seg.points[0].x, seg.points[0].y);
|
||||
} else if (seg.verb == Path::Verb::LineTo) {
|
||||
FPDFPath_LineTo(pathObj, seg.points[0].x, seg.points[0].y);
|
||||
} else if (seg.verb == Path::Verb::CubicBezierTo) {
|
||||
FPDFPath_BezierTo(pathObj, seg.points[0].x, seg.points[0].y,
|
||||
seg.points[1].x, seg.points[1].y,
|
||||
seg.points[2].x, seg.points[2].y);
|
||||
} else if (seg.verb == Path::Verb::Close) {
|
||||
FPDFPath_Close(pathObj);
|
||||
}
|
||||
}
|
||||
|
||||
FPDFPath_SetDrawMode(pathObj, isFill ? FPDF_FILLMODE_ALTERNATE : FPDF_FILLMODE_NONE, isStroke);
|
||||
|
||||
unsigned int r = 0, g = 0, b = 0;
|
||||
parseHexColor(color, r, g, b);
|
||||
|
||||
if (isFill) {
|
||||
FPDFPageObj_SetFillColor(pathObj, r, g, b, 255);
|
||||
}
|
||||
if (isStroke) {
|
||||
FPDFPageObj_SetStrokeColor(pathObj, r, g, b, 255);
|
||||
FPDFPageObj_SetStrokeWidth(pathObj, thickness);
|
||||
}
|
||||
|
||||
FPDFPage_InsertObject(page, pathObj);
|
||||
|
||||
if (!FPDFPage_GenerateContent(page)) {
|
||||
spdlog::error("Failed to generate page content after adding decoration");
|
||||
}
|
||||
}
|
||||
|
||||
FPDF_ClosePage(page);
|
||||
} else if (type == "redaction") {
|
||||
if (!op.contains("data") || !op["data"].is_object()) {
|
||||
|
||||
Reference in New Issue
Block a user