FOSSredder 0.5.0
Loading...
Searching...
No Matches
PopplerCore.cpp
Go to the documentation of this file.
1
5
6#include "pdf-rendering/pch.h"
9#include <poppler-document.h>
10#include <poppler-page.h>
11#include <poppler-image.h>
12#include <poppler-page-renderer.h>
13#include <nlohmann/json.hpp>
14#include <filesystem>
15#include <fstream>
16#include <opencv2/imgcodecs.hpp>
17#include <opencv2/imgproc.hpp>
18
19using json = nlohmann::json;
21
22static void safeWriteText(const std::shared_ptr<core::ports::diagnostics::IDiagnostics>& debugger, const std::string& path, const std::string& text) {
23 if (!debugger || !debugger->enabled()) return;
24 try {
25 debugger->writeText(path, text);
26 } catch (...) {
27 return;
28 }
29}
30
31static void safeWriteBytes(const std::shared_ptr<core::ports::diagnostics::IDiagnostics>& debugger, const std::string& path, const std::vector<uint8_t>& data) {
32 if (!debugger || !debugger->enabled()) return;
33 try {
34 debugger->writeBytes(path, data);
35 } catch (...) {
36 return;
37 }
38}
39
40static ports::RenderedPage extractPageMeta(poppler::page* page, int pageIndex, double dpi, std::shared_ptr<core::ports::diagnostics::IDiagnostics> debugger) {
42 double pageWidthPts = 0.0, pageHeightPts = 0.0;
43 try {
44 auto rect = page->page_rect();
45 pageWidthPts = rect.width();
46 pageHeightPts = rect.height();
47 } catch (...) {
48 pageWidthPts = 595.2; pageHeightPts = 841.8;
49 }
50
51 rp.pageWidthPts = pageWidthPts;
52 rp.pageHeightPts = pageHeightPts;
53 rp.dpiX = dpi; rp.dpiY = dpi;
54
55 try {
56 auto tbl = page->text_list();
57 for (const auto& tb : tbl) {
59 try {
60 auto u = tb.text();
61 auto ba = u.to_utf8();
62 if (ba.size() > 0 && ba.data() != nullptr)
63 te.text.assign(reinterpret_cast<const char*>(ba.data()), ba.size());
64 } catch (...) { te.text.clear(); }
65
66 try {
67 auto b = tb.bbox();
68 try { te.x = b.x(); te.y = b.y(); te.width = b.width(); te.height = b.height(); }
69 catch (...) {
70 try { te.x = b.left(); te.y = b.top(); te.width = b.right() - b.left(); te.height = b.bottom() - b.top(); }
71 catch (...) { te.x = te.y = te.width = te.height = 0.0; }
72 }
73 } catch (...) { te.x = te.y = te.width = te.height = 0.0; }
74
75 rp.textElements.push_back(te);
76 }
77 } catch (...) { rp.textElements.clear(); }
78
79 json j;
80 j["dpi_x"] = dpi; j["dpi_y"] = dpi;
81 j["page_width_pts"] = pageWidthPts; j["page_height_pts"] = pageHeightPts;
82 double scaleX = dpi / 72.0; double scaleY = dpi / 72.0;
83 json tj = json::array();
84 for (const auto& te : rp.textElements) {
85 json it;
86 it["text"] = te.text;
87 it["bbox_pts"] = { te.x, te.y, te.width, te.height };
88 it["bbox_px"] = { static_cast<int>(std::round(te.x * scaleX)), static_cast<int>(std::round(te.y * scaleY)), static_cast<int>(std::round(te.width * scaleX)), static_cast<int>(std::round(te.height * scaleY)) };
89 tj.push_back(it);
90 }
91 j["text_elements"] = tj;
92 j["embedded_images"] = json::array();
93 rp.metadataJson = j.dump();
94
95 if (debugger && debugger->enabled()) {
96 std::string stem = std::to_string(pageIndex + 1);
97 safeWriteText(debugger, std::string("poppler/meta/") + stem + ".json", rp.metadataJson);
98 }
99
100 return rp;
101}
102
103static std::filesystem::path resolveOutputDir(const std::filesystem::path& outputDir) {
104 if (outputDir.empty()) return {};
105 std::filesystem::create_directories(outputDir);
106 return outputDir;
107}
108
109static std::vector<uint8_t> encodePngBytes(poppler::image& img) {
110 std::vector<uint8_t> out;
111 if (!img.is_valid()) return out;
112
113 const int w = img.width();
114 const int h = img.height();
115 if (w <= 0 || h <= 0) return out;
116
117 const int stride = img.bytes_per_row();
118 unsigned char* data = reinterpret_cast<unsigned char*>(img.data());
119 if (!data) return out;
120
121 cv::Mat rgb(h, w, CV_8UC3, data, static_cast<size_t>(stride));
122 cv::Mat bgr;
123 cv::cvtColor(rgb, bgr, cv::COLOR_RGB2BGR);
124
125 std::vector<int> params;
126 params.push_back(cv::IMWRITE_PNG_COMPRESSION);
127 params.push_back(3);
128
129 cv::imencode(".png", bgr, out, params);
130 return out;
131}
132
133static std::string prefix_base(const std::string& uniqIdPrefix, const std::string& filePrefix)
134{
135 if (!uniqIdPrefix.empty() && !filePrefix.empty()) return uniqIdPrefix + "_" + filePrefix;
136 if (!uniqIdPrefix.empty()) return uniqIdPrefix;
137 if (!filePrefix.empty()) return filePrefix;
138 return "poppler";
139}
140
141std::vector<ports::RenderedPage> PopplerCore::extractDocumentMeta(const std::string& pdfPath,
142 double dpi,
143 const std::filesystem::path& outputDir,
144 const std::string& uniqIdPrefix,
145 const std::string& filePrefix,
146 std::shared_ptr<core::ports::diagnostics::IDiagnostics> debugger,
147 std::shared_ptr<std::atomic<bool>> cancelFlag) {
148 std::vector<ports::RenderedPage> result;
149 if (!std::filesystem::exists(pdfPath)) throw std::runtime_error("PDF file does not exist: " + pdfPath);
150
151 std::unique_ptr<poppler::document> doc(poppler::document::load_from_file(pdfPath));
152 if (!doc) throw std::runtime_error("Failed to open PDF: " + pdfPath);
153
154 (void)uniqIdPrefix;
155 (void)filePrefix;
156
157 int numPages = doc->pages();
158 for (int i = 0; i < numPages; ++i) {
159 if (cancelFlag && cancelFlag->load()) break;
160 std::unique_ptr<poppler::page> page(doc->create_page(i));
161 if (!page) continue;
162 result.push_back(extractPageMeta(page.get(), i, dpi, debugger));
163 }
164 return result;
165}
166
167std::vector<ports::RenderedPage> PopplerCore::renderDocument(const std::string& pdfPath,
168 double dpi,
169 const std::filesystem::path& outputDir,
170 const std::string& uniqIdPrefix,
171 const std::string& filePrefix,
172 std::shared_ptr<core::ports::diagnostics::IDiagnostics> debugger,
173 std::shared_ptr<std::atomic<bool>> cancelFlag) {
174 std::vector<ports::RenderedPage> result;
175 if (!std::filesystem::exists(pdfPath)) throw std::runtime_error("PDF file does not exist: " + pdfPath);
176
177 std::unique_ptr<poppler::document> doc(poppler::document::load_from_file(pdfPath));
178 if (!doc) throw std::runtime_error("Failed to open PDF: " + pdfPath);
179
180 poppler::page_renderer renderer;
181 renderer.set_render_hint(poppler::page_renderer::antialiasing, true);
182 renderer.set_render_hint(poppler::page_renderer::text_antialiasing, true);
183 renderer.set_image_format(poppler::image::format_enum::format_rgb24);
184
185 int numPages = doc->pages();
186
187 std::filesystem::path outDir = resolveOutputDir(outputDir);
188 const bool writeFiles = !outDir.empty();
189 const std::string base = prefix_base(uniqIdPrefix, filePrefix.empty() ? std::string("poppler_render") : filePrefix);
190
191 for (int i = 0; i < numPages; ++i) {
192 if (cancelFlag && cancelFlag->load()) break;
193 std::unique_ptr<poppler::page> page(doc->create_page(i));
194 if (!page) continue;
195
196 poppler::image img = renderer.render_page(page.get(), dpi, dpi);
197 if (!img.is_valid()) continue;
198
200 rp.dpiX = dpi;
201 rp.dpiY = dpi;
202 rp.imageWidthPx = img.width();
203 rp.imageHeightPx = img.height();
204 rp.imageBytes = encodePngBytes(img);
205
206 if (writeFiles) {
207 std::ostringstream fname;
208 fname << outDir.string() << "/" << base << "_page" << (i + 1) << ".png";
209 rp.imagePath = fname.str();
210 if (!img.save(rp.imagePath, "png")) throw std::runtime_error("Failed to save image: " + rp.imagePath);
211 }
212
213 if (debugger && debugger->enabled() && !rp.imageBytes.empty()) {
214 std::string stem = base + std::string("_page") + std::to_string(i + 1);
215 safeWriteBytes(debugger, std::string("poppler/output/") + stem + std::string(".png"), rp.imageBytes);
216 }
217
218 result.push_back(std::move(rp));
219 }
220 return result;
221}
Declares the diagnostics artifact writing port.
static void safeWriteBytes(const std::shared_ptr< core::ports::diagnostics::IDiagnostics > &debugger, const std::string &path, const std::vector< uint8_t > &data)
static std::vector< uint8_t > encodePngBytes(poppler::image &img)
nlohmann::json json
static std::filesystem::path resolveOutputDir(const std::filesystem::path &outputDir)
static std::string prefix_base(const std::string &uniqIdPrefix, const std::string &filePrefix)
static ports::RenderedPage extractPageMeta(poppler::page *page, int pageIndex, double dpi, std::shared_ptr< core::ports::diagnostics::IDiagnostics > debugger)
static void safeWriteText(const std::shared_ptr< core::ports::diagnostics::IDiagnostics > &debugger, const std::string &path, const std::string &text)
Declares the Poppler implementation helpers used by the import workflow.
static std::vector< core::ports::pdf_rendering::RenderedPage > renderDocument(const std::string &path, double dpi, const std::filesystem::path &outputDir=std::filesystem::path(), const std::string &uniqIdPrefix="", const std::string &filePrefix="", std::shared_ptr< core::ports::diagnostics::IDiagnostics > dbg=nullptr, std::shared_ptr< std::atomic< bool > > cancelFlag=nullptr)
Extracts document metadata and page text elements from a PDF file.
static std::vector< core::ports::pdf_rendering::RenderedPage > extractDocumentMeta(const std::string &path, double dpi, const std::filesystem::path &outputDir=std::filesystem::path(), const std::string &uniqIdPrefix="", const std::string &filePrefix="", std::shared_ptr< core::ports::diagnostics::IDiagnostics > dbg=nullptr, std::shared_ptr< std::atomic< bool > > cancelFlag=nullptr)
Extracts page metadata without rendering the page bitmap.
Precompiled header for the pdf-rendering infrastructure module.