159 lines
6.1 KiB
C++
159 lines
6.1 KiB
C++
#include <emscripten/emscripten.h>
|
|
#include <pdfengine/pdf_document.hpp>
|
|
#include <spdlog/spdlog.h>
|
|
#include <unordered_map>
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <cstdint>
|
|
|
|
namespace {
|
|
struct DocEntry {
|
|
std::shared_ptr<pdfengine::PdfDocument> doc;
|
|
std::vector<uint8_t> bytes;
|
|
};
|
|
std::unordered_map<int, DocEntry> g_docs;
|
|
int g_nextHandle = 1;
|
|
|
|
std::vector<uint8_t> g_lastPng;
|
|
int g_lastW = 0, g_lastH = 0;
|
|
std::string g_lastLayout;
|
|
|
|
struct PreviewRegion { std::vector<uint8_t> rgba; int w = 0, h = 0; int pageIndex = 0; double yTopPt = 0.0; };
|
|
std::vector<PreviewRegion> g_regions;
|
|
|
|
int renderInto(pdfengine::PdfDocument& doc, int pageIndex, int dpi) {
|
|
auto page = doc.getPage(pageIndex);
|
|
if (!page) return -1;
|
|
auto img = (*page)->render(dpi);
|
|
if (!img) return -1;
|
|
g_lastPng = std::move(img->data);
|
|
g_lastW = img->width;
|
|
g_lastH = img->height;
|
|
return static_cast<int>(g_lastPng.size());
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
EMSCRIPTEN_KEEPALIVE int loadDocument(const uint8_t* buffer, int size) {
|
|
static const bool logInit = [] { spdlog::set_level(spdlog::level::err); return true; }();
|
|
(void)logInit;
|
|
std::vector<uint8_t> bytes(buffer, buffer + size);
|
|
auto res = pdfengine::PdfDocument::loadFromMemory(bytes, "");
|
|
if (!res) return -1;
|
|
int h = g_nextHandle++;
|
|
g_docs[h] = DocEntry{*res, std::move(bytes)};
|
|
return h;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int pageCount(int handle) {
|
|
auto it = g_docs.find(handle);
|
|
return it == g_docs.end() ? -1 : it->second.doc->pageCount();
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int renderPagePng(int handle, int pageIndex, int dpi) {
|
|
auto it = g_docs.find(handle);
|
|
if (it == g_docs.end()) return -1;
|
|
return renderInto(*it->second.doc, pageIndex, dpi);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int previewRender(int handle, int pageIndex, int dpi, const char* editsJson) {
|
|
auto it = g_docs.find(handle);
|
|
if (it == g_docs.end()) return -1;
|
|
auto fresh = pdfengine::PdfDocument::loadFromMemory(it->second.bytes, "");
|
|
if (!fresh) return -1;
|
|
g_lastLayout.clear();
|
|
if (editsJson && editsJson[0]) {
|
|
auto r = (*fresh)->applyEdits(editsJson);
|
|
if (!r) return -1;
|
|
g_lastLayout = (*fresh)->lastReflowLayout();
|
|
}
|
|
return renderInto(**fresh, pageIndex, dpi);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int previewRenderRegion(int handle, int pageIndex, int dpi,
|
|
const char* editsJson, double yTopPt, double heightPt) {
|
|
auto it = g_docs.find(handle);
|
|
if (it == g_docs.end()) return -1;
|
|
auto fresh = pdfengine::PdfDocument::loadFromMemory(it->second.bytes, "");
|
|
if (!fresh) return -1;
|
|
g_lastLayout.clear();
|
|
if (editsJson && editsJson[0]) {
|
|
auto r = (*fresh)->applyEdits(editsJson);
|
|
if (!r) return -1;
|
|
g_lastLayout = (*fresh)->lastReflowLayout();
|
|
}
|
|
auto page = (*fresh)->getPage(pageIndex);
|
|
if (!page) return -1;
|
|
auto img = (*page)->renderRegionRaw(dpi, yTopPt, heightPt);
|
|
if (!img) return -1;
|
|
g_lastPng = std::move(img->data);
|
|
g_lastW = img->width;
|
|
g_lastH = img->height;
|
|
return static_cast<int>(g_lastPng.size());
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int previewRenderPaginated(int handle, int pageIndex, int dpi,
|
|
const char* editsJson, double yTopPt, int maxFollow) {
|
|
auto it = g_docs.find(handle);
|
|
if (it == g_docs.end()) return -1;
|
|
auto fresh = pdfengine::PdfDocument::loadFromMemory(it->second.bytes, "");
|
|
if (!fresh) return -1;
|
|
g_lastLayout.clear();
|
|
g_regions.clear();
|
|
bool overflowed = false;
|
|
if (editsJson && editsJson[0]) {
|
|
auto r = (*fresh)->applyEdits(editsJson);
|
|
if (!r) return -1;
|
|
g_lastLayout = (*fresh)->lastReflowLayout();
|
|
overflowed = (*fresh)->lastReflowOverflowed();
|
|
}
|
|
{
|
|
auto page = (*fresh)->getPage(pageIndex);
|
|
if (!page) return -1;
|
|
auto img = (*page)->renderRegionRaw(dpi, yTopPt, 0.0);
|
|
if (!img) return -1;
|
|
g_regions.push_back({std::move(img->data), img->width, img->height, pageIndex, yTopPt});
|
|
}
|
|
if (overflowed) {
|
|
int total = (*fresh)->pageCount();
|
|
int last = pageIndex + (maxFollow > 0 ? maxFollow : 4);
|
|
for (int pg = pageIndex + 1; pg < total && pg <= last; ++pg) {
|
|
auto page = (*fresh)->getPage(pg);
|
|
if (!page) break;
|
|
auto img = (*page)->renderRegionRaw(dpi, 0.0, 0.0);
|
|
if (!img) break;
|
|
g_regions.push_back({std::move(img->data), img->width, img->height, pg, 0.0});
|
|
}
|
|
}
|
|
return static_cast<int>(g_regions.size());
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int lastRegionCount() { return static_cast<int>(g_regions.size()); }
|
|
EMSCRIPTEN_KEEPALIVE const uint8_t* lastRegionPtr(int i) {
|
|
return (i >= 0 && i < static_cast<int>(g_regions.size())) ? g_regions[i].rgba.data() : nullptr;
|
|
}
|
|
EMSCRIPTEN_KEEPALIVE int lastRegionW(int i) { return (i >= 0 && i < static_cast<int>(g_regions.size())) ? g_regions[i].w : 0; }
|
|
EMSCRIPTEN_KEEPALIVE int lastRegionH(int i) { return (i >= 0 && i < static_cast<int>(g_regions.size())) ? g_regions[i].h : 0; }
|
|
EMSCRIPTEN_KEEPALIVE int lastRegionPage(int i) { return (i >= 0 && i < static_cast<int>(g_regions.size())) ? g_regions[i].pageIndex : -1; }
|
|
EMSCRIPTEN_KEEPALIVE double lastRegionYTop(int i) { return (i >= 0 && i < static_cast<int>(g_regions.size())) ? g_regions[i].yTopPt : 0.0; }
|
|
|
|
EMSCRIPTEN_KEEPALIVE const uint8_t* lastRenderPtr() { return g_lastPng.data(); }
|
|
EMSCRIPTEN_KEEPALIVE int lastRenderW() { return g_lastW; }
|
|
EMSCRIPTEN_KEEPALIVE int lastRenderH() { return g_lastH; }
|
|
EMSCRIPTEN_KEEPALIVE const char* lastLayoutJson() { return g_lastLayout.c_str(); }
|
|
|
|
EMSCRIPTEN_KEEPALIVE void registerAuxFont(int handle, const char* fid, const uint8_t* data, int size) {
|
|
auto it = g_docs.find(handle);
|
|
if (it == g_docs.end() || !fid || !data || size <= 0) return;
|
|
std::vector<uint8_t> bytes(data, data + size);
|
|
it->second.doc->registerAuxFont(std::string(fid), bytes);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void freeDocument(int handle) { g_docs.erase(handle); }
|
|
|
|
EMSCRIPTEN_KEEPALIVE const char* engineBuildInfo() { return "pdfengine-wasm+pdfium"; }
|
|
|
|
}
|