pdf2htmlEX/src/BackgroundRenderer/SplashBackgroundRenderer.cc

262 lines
7.7 KiB
C++

/*
* SplashBackgroundRenderer.cc
*
* Copyright (C) 2012,2013 Lu Wang <coolwanglu@gmail.com>
*/
#include <fstream>
#include <vector>
#include <memory>
#include <poppler-config.h>
#include <PDFDoc.h>
#include <goo/ImgWriter.h>
#include <goo/PNGWriter.h>
#include <goo/JpegWriter.h>
#include "Base64Stream.h"
#include "util/const.h"
#include "SplashBackgroundRenderer.h"
namespace pdf2htmlEX {
using std::string;
using std::ifstream;
using std::vector;
using std::unique_ptr;
const SplashColor SplashBackgroundRenderer::white = {255,255,255};
SplashBackgroundRenderer::SplashBackgroundRenderer(const string & imgFormat, HTMLRenderer * html_renderer, const Param & param)
: SplashOutputDev(splashModeRGB8, 4, gFalse, (SplashColorPtr)(&white), gTrue)
, html_renderer(html_renderer)
, param(param)
, format(imgFormat)
{
bool supported = false;
#ifdef ENABLE_LIBPNG
if (format.empty())
format = "png";
supported = supported || format == "png";
#endif
#ifdef ENABLE_LIBJPEG
if (format.empty())
format = "jpg";
supported = supported || format == "jpg";
#endif
if (!supported)
{
throw string("Image format not supported: ") + format;
}
}
/*
* SplashOutputDev::startPage would paint the whole page with the background color
* And thus have modified region set to the whole page area
* We do not want that.
*/
void SplashBackgroundRenderer::startPage(int pageNum, GfxState *state, XRef *xrefA)
{
SplashOutputDev::startPage(pageNum, state, xrefA);
clearModRegion();
}
void SplashBackgroundRenderer::drawChar(GfxState *state, double x, double y,
double dx, double dy,
double originX, double originY,
CharCode code, int nBytes, Unicode *u, int uLen)
{
// draw characters as image when
// - in fallback mode
// - OR there is special filling method
// - OR using a writing mode font
// - OR using a Type 3 font while param.process_type3 is not enabled
// - OR the text is used as path
if((param.fallback || param.proof)
|| ( (state->getFont())
&& ( (state->getFont()->getWMode())
|| ((state->getFont()->getType() == fontType3) && (!param.process_type3))
|| (state->getRender() >= 4)
)
)
)
{
SplashOutputDev::drawChar(state,x,y,dx,dy,originX,originY,code,nBytes,u,uLen);
}
// If a char is treated as image, it is not subject to cover test
// (see HTMLRenderer::drawString), so don't increase drawn_char_count.
else if (param.correct_text_visibility) {
if (html_renderer->is_char_covered(drawn_char_count))
SplashOutputDev::drawChar(state,x,y,dx,dy,originX,originY,code,nBytes,u,uLen);
drawn_char_count++;
}
}
void SplashBackgroundRenderer::beginTextObject(GfxState *state)
{
if (param.proof == 2)
proof_begin_text_object(state, this);
SplashOutputDev::beginTextObject(state);
}
void SplashBackgroundRenderer::beginString(GfxState *state, GooString * str)
{
if (param.proof == 2)
proof_begin_string(state, this);
SplashOutputDev::beginString(state, str);
}
void SplashBackgroundRenderer::endTextObject(GfxState *state)
{
if (param.proof == 2)
proof_end_text_object(state, this);
SplashOutputDev::endTextObject(state);
}
void SplashBackgroundRenderer::updateRender(GfxState *state)
{
if (param.proof == 2)
proof_update_render(state, this);
SplashOutputDev::updateRender(state);
}
void SplashBackgroundRenderer::init(PDFDoc * doc)
{
startDoc(doc);
}
static GBool annot_cb(Annot *, void * pflag) {
return (*((bool*)pflag)) ? gTrue : gFalse;
};
bool SplashBackgroundRenderer::render_page(PDFDoc * doc, int pageno)
{
drawn_char_count = 0;
bool process_annotation = param.process_annotation;
doc->displayPage(this, pageno, param.h_dpi, param.v_dpi,
0,
(!(param.use_cropbox)),
false, false,
nullptr, nullptr, &annot_cb, &process_annotation);
return true;
}
void SplashBackgroundRenderer::embed_image(int pageno)
{
// xmin->xmax is top->bottom
int xmin, xmax, ymin, ymax;
getModRegion(&xmin, &ymin, &xmax, &ymax);
// dump the background image only when it is not empty
if((xmin <= xmax) && (ymin <= ymax))
{
{
auto fn = html_renderer->str_fmt("%s/bg%x.%s", (param.embed_image ? param.tmp_dir : param.dest_dir).c_str(), pageno, format.c_str());
if(param.embed_image)
html_renderer->tmp_files.add((char*)fn);
dump_image((char*)fn, xmin, ymin, xmax, ymax);
}
double h_scale = html_renderer->text_zoom_factor() * DEFAULT_DPI / param.h_dpi;
double v_scale = html_renderer->text_zoom_factor() * DEFAULT_DPI / param.v_dpi;
auto & f_page = *(html_renderer->f_curpage);
auto & all_manager = html_renderer->all_manager;
f_page << "<img class=\"" << CSS::BACKGROUND_IMAGE_CN
<< " " << CSS::LEFT_CN << all_manager.left.install(((double)xmin) * h_scale)
<< " " << CSS::BOTTOM_CN << all_manager.bottom.install(((double)getBitmapHeight() - 1 - ymax) * v_scale)
<< " " << CSS::WIDTH_CN << all_manager.width.install(((double)(xmax - xmin + 1)) * h_scale)
<< " " << CSS::HEIGHT_CN << all_manager.height.install(((double)(ymax - ymin + 1)) * v_scale)
<< "\" alt=\"\" src=\"";
if(param.embed_image)
{
auto path = html_renderer->str_fmt("%s/bg%x.%s", param.tmp_dir.c_str(), pageno, format.c_str());
ifstream fin((char*)path, ifstream::binary);
if(!fin)
throw string("Cannot read background image ") + (char*)path;
auto iter = FORMAT_MIME_TYPE_MAP.find(format);
if(iter == FORMAT_MIME_TYPE_MAP.end())
throw string("Image format not supported: ") + format;
string mime_type = iter->second;
f_page << "data:" << mime_type << ";base64," << Base64Stream(fin);
}
else
{
f_page << (char*)html_renderer->str_fmt("bg%x.%s", pageno, format.c_str());
}
f_page << "\"/>";
}
}
// There might be mem leak when exception is thrown !
void SplashBackgroundRenderer::dump_image(const char * filename, int x1, int y1, int x2, int y2)
{
int width = x2 - x1 + 1;
int height = y2 - y1 + 1;
if((width <= 0) || (height <= 0))
throw "Bad metric for background image";
FILE * f = fopen(filename, "wb");
if(!f)
throw string("Cannot open file for background image " ) + filename;
// use unique_ptr to auto delete the object upon exception
unique_ptr<ImgWriter> writer;
if(false) { }
#ifdef ENABLE_LIBPNG
else if(format == "png")
{
writer = unique_ptr<ImgWriter>(new PNGWriter);
}
#endif
#ifdef ENABLE_LIBJPEG
else if(format == "jpg")
{
writer = unique_ptr<ImgWriter>(new JpegWriter);
}
#endif
else
{
throw string("Image format not supported: ") + format;
}
if(!writer->init(f, width, height, param.h_dpi, param.v_dpi))
throw "Cannot initialize image writer";
auto * bitmap = getBitmap();
assert(bitmap->getMode() == splashModeRGB8);
SplashColorPtr data = bitmap->getDataPtr();
int row_size = bitmap->getRowSize();
vector<unsigned char*> pointers;
pointers.reserve(height);
SplashColorPtr p = data + y1 * row_size + x1 * 3;
for(int i = 0; i < height; ++i)
{
pointers.push_back(p);
p += row_size;
}
if(!writer->writePointers(pointers.data(), height))
{
throw "Cannot write background image";
}
if(!writer->close())
{
throw "Cannot finish background image";
}
fclose(f);
}
} // namespace pdf2htmlEX