Path construction interpreter (m l c re S f B)

This commit is contained in:
saqib mir
2026-06-22 15:21:39 +05:30
parent ac5a1cc516
commit d290e65685
13 changed files with 146 additions and 9 deletions
+1
View File
@@ -13,6 +13,7 @@ add_library(pdfengine STATIC
src/core/engine_info.cpp
src/core/graphics_state.cpp
src/core/display_list.cpp
src/core/path_interpreter.cpp
src/core/skia_renderer.cpp
src/parser/pdfium_loader.cpp
src/parser/pdfium_document.cpp
@@ -72,6 +72,7 @@ public:
Path path;
PathPaintOp paintOp = PathPaintOp::Stroke;
FillRule fillRule = FillRule::NonZero;
Matrix transform;
};
+12 -2
View File
@@ -48,7 +48,8 @@ struct DrawTextCommand : public Command {
struct FillPathCommand : public Command {
Path path;
explicit FillPathCommand(Path path) : path(std::move(path)) {}
FillRule rule;
explicit FillPathCommand(Path path, FillRule rule = FillRule::NonZero) : path(std::move(path)), rule(rule) {}
void accept(CommandVisitor& visitor) const override;
};
@@ -58,6 +59,13 @@ struct StrokePathCommand : public Command {
void accept(CommandVisitor& visitor) const override;
};
struct FillStrokePathCommand : public Command {
Path path;
FillRule rule;
explicit FillStrokePathCommand(Path path, FillRule rule = FillRule::NonZero) : path(std::move(path)), rule(rule) {}
void accept(CommandVisitor& visitor) const override;
};
struct DrawImageCommand : public Command {
ImageInfo image;
Matrix matrix;
@@ -82,6 +90,7 @@ public:
virtual void visit(const DrawTextCommand& cmd) = 0;
virtual void visit(const FillPathCommand& cmd) = 0;
virtual void visit(const StrokePathCommand& cmd) = 0;
virtual void visit(const FillStrokePathCommand& cmd) = 0;
virtual void visit(const DrawImageCommand& cmd) = 0;
};
@@ -104,8 +113,9 @@ public:
void setTransform(const Matrix& m);
void fillRect(float x, float y, float w, float h);
void drawText(const std::string& text, float x, float y);
void fillPath(const Path& path);
void fillPath(const Path& path, FillRule rule = FillRule::NonZero);
void strokePath(const Path& path);
void fillStrokePath(const Path& path, FillRule rule = FillRule::NonZero);
void drawImage(const ImageInfo& image, const Matrix& m, float opacity = 1.0f);
[[nodiscard]] size_t size() const noexcept { return m_commands.size(); }
+5
View File
@@ -4,6 +4,11 @@
namespace pdfengine {
enum class FillRule {
NonZero,
EvenOdd
};
// Basic point structure
struct Point {
float x = 0.0f;
@@ -0,0 +1,14 @@
#pragma once
#include <pdfengine/content_object.hpp>
#include <pdfengine/display_list.hpp>
namespace pdfengine {
// PathObjectInterpreter converts a PathObject into DisplayList commands
class PathObjectInterpreter {
public:
static void interpret(const PathObject& pathObj, DisplayList& displayList);
};
} // namespace pdfengine
@@ -21,6 +21,7 @@ public:
void visit(const DrawTextCommand& cmd) override;
void visit(const FillPathCommand& cmd) override;
void visit(const StrokePathCommand& cmd) override;
void visit(const FillStrokePathCommand& cmd) override;
void visit(const DrawImageCommand& cmd) override;
// Renders the entire display list to the canvas
void render(const DisplayList& displayList);
+7 -2
View File
@@ -9,6 +9,7 @@ void FillRectCommand::accept(CommandVisitor& visitor) const { visitor.visit(*thi
void DrawTextCommand::accept(CommandVisitor& visitor) const { visitor.visit(*this); }
void FillPathCommand::accept(CommandVisitor& visitor) const { visitor.visit(*this); }
void StrokePathCommand::accept(CommandVisitor& visitor) const { visitor.visit(*this); }
void FillStrokePathCommand::accept(CommandVisitor& visitor) const { visitor.visit(*this); }
void DrawImageCommand::accept(CommandVisitor& visitor) const { visitor.visit(*this); }
void DisplayList::addCommand(std::unique_ptr<Command> cmd) {
@@ -43,14 +44,18 @@ void DisplayList::drawText(const std::string& text, float x, float y) {
addCommand(std::make_unique<DrawTextCommand>(text, x, y));
}
void DisplayList::fillPath(const Path& path) {
addCommand(std::make_unique<FillPathCommand>(path));
void DisplayList::fillPath(const Path& path, FillRule rule) {
addCommand(std::make_unique<FillPathCommand>(path, rule));
}
void DisplayList::strokePath(const Path& path) {
addCommand(std::make_unique<StrokePathCommand>(path));
}
void DisplayList::fillStrokePath(const Path& path, FillRule rule) {
addCommand(std::make_unique<FillStrokePathCommand>(path, rule));
}
void DisplayList::drawImage(const ImageInfo& image, const Matrix& m, float opacity) {
addCommand(std::make_unique<DrawImageCommand>(image, m, opacity));
}
+28
View File
@@ -0,0 +1,28 @@
#include <pdfengine/path_interpreter.hpp>
namespace pdfengine {
void PathObjectInterpreter::interpret(const PathObject& pathObj, DisplayList& displayList) {
if (pathObj.path.empty()) {
return;
}
displayList.saveState();
displayList.setTransform(pathObj.transform);
switch (pathObj.paintOp) {
case PathPaintOp::Stroke:
displayList.strokePath(pathObj.path);
break;
case PathPaintOp::Fill:
displayList.fillPath(pathObj.path, pathObj.fillRule);
break;
case PathPaintOp::FillStroke:
displayList.fillStrokePath(pathObj.path, pathObj.fillRule);
break;
}
displayList.restoreState();
}
} // namespace pdfengine
+64
View File
@@ -129,6 +129,12 @@ void SkiaRenderer::visit(const FillPathCommand& cmd) {
}
}
if (cmd.rule == FillRule::EvenOdd) {
skPath.setFillType(SkPathFillType::kEvenOdd);
} else {
skPath.setFillType(SkPathFillType::kWinding);
}
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kFill_Style);
@@ -186,6 +192,64 @@ void SkiaRenderer::visit(const StrokePathCommand& cmd) {
#endif
}
void SkiaRenderer::visit(const FillStrokePathCommand& cmd) {
(void)cmd;
#ifdef PDFENGINE_WITH_SKIA
if (!m_canvas || cmd.path.empty()) return;
SkPath skPath;
for (const auto& segment : cmd.path.segments()) {
switch (segment.verb) {
case Path::Verb::MoveTo:
skPath.moveTo(segment.points[0].x, segment.points[0].y);
break;
case Path::Verb::LineTo:
skPath.lineTo(segment.points[0].x, segment.points[0].y);
break;
case Path::Verb::CubicBezierTo:
skPath.cubicTo(
segment.points[0].x, segment.points[0].y,
segment.points[1].x, segment.points[1].y,
segment.points[2].x, segment.points[2].y
);
break;
case Path::Verb::Close:
skPath.close();
break;
}
}
if (cmd.rule == FillRule::EvenOdd) {
skPath.setFillType(SkPathFillType::kEvenOdd);
} else {
skPath.setFillType(SkPathFillType::kWinding);
}
const auto& color = m_stateStack.current().fillColor;
// First fill
SkPaint fillPaint;
fillPaint.setAntiAlias(true);
fillPaint.setStyle(SkPaint::kFill_Style);
fillPaint.setColor(SkColorSetARGB(255,
static_cast<uint8_t>(color.r * 255),
static_cast<uint8_t>(color.g * 255),
static_cast<uint8_t>(color.b * 255)));
m_canvas->drawPath(skPath, fillPaint);
// Then stroke
SkPaint strokePaint;
strokePaint.setAntiAlias(true);
strokePaint.setStyle(SkPaint::kStroke_Style);
strokePaint.setColor(SkColorSetARGB(255,
static_cast<uint8_t>(color.r * 255),
static_cast<uint8_t>(color.g * 255),
static_cast<uint8_t>(color.b * 255)));
strokePaint.setStrokeWidth(1.0f);
m_canvas->drawPath(skPath, strokePaint);
#endif
}
void SkiaRenderer::visit(const DrawImageCommand& cmd) {
(void)cmd;
#ifdef PDFENGINE_WITH_SKIA
+9 -4
View File
@@ -46,11 +46,14 @@ void ContentBuilder::processOperation(const Operation& op, std::vector<std::uniq
} else if (op.op == "s") {
handlePathPaint(PathPaintOp::Stroke, outObjects, true);
} else if (op.op == "f" || op.op == "F" || op.op == "f*") {
handlePathPaint(PathPaintOp::Fill, outObjects);
FillRule rule = (op.op == "f*") ? FillRule::EvenOdd : FillRule::NonZero;
handlePathPaint(PathPaintOp::Fill, outObjects, false, rule);
} else if (op.op == "B" || op.op == "B*") {
handlePathPaint(PathPaintOp::FillStroke, outObjects);
FillRule rule = (op.op == "B*") ? FillRule::EvenOdd : FillRule::NonZero;
handlePathPaint(PathPaintOp::FillStroke, outObjects, false, rule);
} else if (op.op == "b" || op.op == "b*") {
handlePathPaint(PathPaintOp::FillStroke, outObjects, true);
FillRule rule = (op.op == "b*") ? FillRule::EvenOdd : FillRule::NonZero;
handlePathPaint(PathPaintOp::FillStroke, outObjects, true, rule);
} else if (op.op == "n") {
currentPath_.clear();
}
@@ -287,7 +290,8 @@ void ContentBuilder::handlePathConstruction(const Operation& op) {
void ContentBuilder::handlePathPaint(PathPaintOp paintOp,
std::vector<std::unique_ptr<ContentObject>>& outObjects,
bool closePath) {
bool closePath,
FillRule fillRule) {
if (closePath) {
if (!currentPath_.empty()) {
currentPath_.close();
@@ -300,6 +304,7 @@ void ContentBuilder::handlePathPaint(PathPaintOp paintOp,
auto pathObj = std::make_unique<PathObject>();
pathObj->path = currentPath_;
pathObj->paintOp = paintOp;
pathObj->fillRule = fillRule;
pathObj->transform = state_.ctm;
outObjects.push_back(std::move(pathObj));
+1 -1
View File
@@ -39,7 +39,7 @@ private:
void handleCm(const Operation& op);
void handleDo(const Operation& op, std::vector<std::unique_ptr<ContentObject>>& outObjects);
void handlePathConstruction(const Operation& op);
void handlePathPaint(PathPaintOp paintOp, std::vector<std::unique_ptr<ContentObject>>& outObjects, bool closePath = false);
void handlePathPaint(PathPaintOp paintOp, std::vector<std::unique_ptr<ContentObject>>& outObjects, bool closePath = false, FillRule fillRule = FillRule::NonZero);
};
} // namespace pdfengine
+2
View File
@@ -951,6 +951,7 @@ std::expected<std::vector<GlyphBounds>, EngineError> PdfiumPage::extractTextWith
}
namespace {
#ifdef PDFENGINE_WITH_PDFIUM
inline void repagSetParaId(FPDF_DOCUMENT doc, FPDF_PAGEOBJECT obj, const std::string& paraId) {
if (paraId.empty() || !obj) return;
FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(obj, "PDFPARA");
@@ -982,6 +983,7 @@ inline std::string repagGetParaId(FPDF_PAGEOBJECT obj) {
}
return "";
}
#endif
}
std::expected<PageModel, EngineError> PdfiumPage::extractDocumentModel() const {
+1
View File
@@ -17,6 +17,7 @@ public:
void visit(const DrawTextCommand& cmd) override { calls.push_back("DrawText(" + cmd.text + ")"); }
void visit(const FillPathCommand&) override { calls.push_back("FillPath"); }
void visit(const StrokePathCommand&) override { calls.push_back("StrokePath"); }
void visit(const FillStrokePathCommand&) override { calls.push_back("FillStrokePath"); }
void visit(const DrawImageCommand&) override { calls.push_back("DrawImage"); }
};