pdf2htmlEX/src/HTMLRenderer/general.cc

593 lines
17 KiB
C++
Raw Normal View History

2012-08-14 08:23:15 +00:00
/*
* general.cc
*
2012-09-25 11:29:59 +00:00
* Handling general stuffs
2012-08-14 08:23:15 +00:00
*
2014-11-16 14:04:02 +00:00
* Copyright (C) 2012,2013,2014 Lu Wang <coolwanglu@gmail.com>
2012-08-14 08:23:15 +00:00
*/
2012-09-09 17:40:37 +00:00
#include <cstdio>
2012-09-12 15:26:14 +00:00
#include <ostream>
2012-09-22 14:47:44 +00:00
#include <cmath>
2012-09-26 16:17:56 +00:00
#include <algorithm>
#include <vector>
#include <functional>
2012-09-05 10:43:37 +00:00
2013-01-28 10:16:01 +00:00
#include <GlobalParams.h>
2012-09-10 05:08:47 +00:00
#include "pdf2htmlEX-config.h"
2013-04-06 08:45:01 +00:00
#include "HTMLRenderer.h"
2013-04-06 15:51:33 +00:00
#include "HTMLTextLine.h"
2013-04-06 15:41:58 +00:00
#include "Base64Stream.h"
2013-04-06 08:45:01 +00:00
2013-09-16 08:30:00 +00:00
#include "BackgroundRenderer/BackgroundRenderer.h"
2012-11-29 09:28:05 +00:00
#include "util/namespace.h"
#include "util/ffw.h"
2012-11-29 10:16:05 +00:00
#include "util/math.h"
#include "util/path.h"
2013-02-27 18:11:34 +00:00
#include "util/css_const.h"
#include "util/encoding.h"
2012-08-14 08:23:15 +00:00
2012-09-12 15:26:14 +00:00
namespace pdf2htmlEX {
using std::fixed;
2012-08-14 12:30:18 +00:00
using std::flush;
2012-09-12 15:26:14 +00:00
using std::ostream;
2012-09-22 14:47:44 +00:00
using std::max;
2012-09-26 16:17:56 +00:00
using std::min_element;
using std::vector;
2012-09-26 16:25:41 +00:00
using std::abs;
2012-11-29 10:28:07 +00:00
using std::cerr;
using std::endl;
2012-08-14 12:30:18 +00:00
2013-04-06 09:01:05 +00:00
HTMLRenderer::HTMLRenderer(const Param & param)
2012-09-16 10:30:34 +00:00
:OutputDev()
,param(param)
,html_text_page(param, all_manager)
2012-09-22 06:41:29 +00:00
,preprocessor(param)
,tmp_files(param)
,tracer(param)
2012-08-14 08:23:15 +00:00
{
2013-04-06 09:01:05 +00:00
if(!(param.debug))
2012-09-17 12:40:10 +00:00
{
2013-01-28 10:16:01 +00:00
//disable error messages of poppler
globalParams->setErrQuiet(gTrue);
2012-09-17 12:40:10 +00:00
}
2012-09-03 13:59:39 +00:00
2013-04-06 09:01:05 +00:00
ffw_init(param.debug);
2014-11-16 14:04:02 +00:00
cur_mapping.resize(0x10000);
cur_mapping2.resize(0x100);
width_list.resize(0x10000);
2013-02-05 05:57:11 +00:00
2013-04-04 10:17:29 +00:00
/*
* For these states, usually the error will not be accumulated
* or may be handled well (whitespace_manager)
* So we can set a large eps here
*/
2013-04-06 09:01:05 +00:00
all_manager.vertical_align.set_eps(param.v_eps);
all_manager.whitespace .set_eps(param.h_eps);
all_manager.left .set_eps(param.h_eps);
2013-04-04 10:17:29 +00:00
/*
2014-07-13 23:59:30 +00:00
* For other states, we need accurate values
2013-04-04 10:17:29 +00:00
* optimization will be done separately
*/
all_manager.font_size .set_eps(EPS);
all_manager.letter_space.set_eps(EPS);
all_manager.word_space .set_eps(EPS);
all_manager.height .set_eps(EPS);
all_manager.width .set_eps(EPS);
all_manager.bottom .set_eps(EPS);
tracer.on_char_drawn =
2014-11-16 14:04:02 +00:00
[this](double * box) { covered_text_detector.add_char_bbox(box); };
tracer.on_char_clipped =
2014-11-16 14:04:02 +00:00
[this](double * box, bool partial) { covered_text_detector.add_char_bbox_clipped(box, partial); };
tracer.on_non_char_drawn =
2014-11-16 14:04:02 +00:00
[this](double * box) { covered_text_detector.add_non_char_bbox(box); };
}
HTMLRenderer::~HTMLRenderer()
{
ffw_finalize();
}
void HTMLRenderer::process(PDFDoc *doc)
{
cur_doc = doc;
2013-01-28 10:31:02 +00:00
cur_catalog = doc->getCatalog();
xref = doc->getXRef();
2012-10-24 14:24:33 +00:00
pre_process(doc);
2012-09-26 16:17:56 +00:00
2013-01-28 13:01:02 +00:00
///////////////////
// Process pages
2013-04-06 09:01:05 +00:00
if(param.process_nontext)
2012-08-14 08:23:15 +00:00
{
2013-09-18 10:01:56 +00:00
bg_renderer = BackgroundRenderer::getBackgroundRenderer(param.bg_format, this, param);
if(!bg_renderer)
throw "Cannot initialize background renderer, unsupported format";
bg_renderer->init(doc);
fallback_bg_renderer = BackgroundRenderer::getFallbackBackgroundRenderer(this, param);
if (fallback_bg_renderer)
fallback_bg_renderer->init(doc);
2012-08-14 12:30:18 +00:00
}
2012-08-14 08:23:15 +00:00
2013-04-06 09:01:05 +00:00
int page_count = (param.last_page - param.first_page + 1);
for(int i = param.first_page; i <= param.last_page ; ++i)
2012-08-14 12:30:18 +00:00
{
if (param.tmp_file_size_limit != -1 && tmp_files.get_total_size() > param.tmp_file_size_limit * 1024) {
cerr << "Stop processing, reach max size\n";
break;
}
cerr << "Working: " << (i-param.first_page) << "/" << page_count << '\r' << flush;
2012-10-24 14:30:52 +00:00
2013-04-06 09:01:05 +00:00
if(param.split_pages)
2012-09-12 15:26:14 +00:00
{
2014-11-16 14:04:02 +00:00
// copy the string out, since we will reuse the buffer soon
string filled_template_filename = (char*)str_fmt(param.page_filename.c_str(), i);
2013-04-26 07:19:12 +00:00
auto page_fn = str_fmt("%s/%s", param.dest_dir.c_str(), filled_template_filename.c_str());
f_curpage = new ofstream((char*)page_fn, ofstream::binary);
2013-06-09 14:19:12 +00:00
if(!(*f_curpage))
2012-09-17 12:07:50 +00:00
throw string("Cannot open ") + (char*)page_fn + " for writing";
2013-06-09 14:19:12 +00:00
set_stream_flags((*f_curpage));
2013-06-09 14:19:12 +00:00
cur_page_filename = filled_template_filename;
2012-09-12 15:26:14 +00:00
}
doc->displayPage(this, i,
2012-10-02 06:19:20 +00:00
text_zoom_factor() * DEFAULT_DPI, text_zoom_factor() * DEFAULT_DPI,
0,
2013-04-06 09:01:05 +00:00
(!(param.use_cropbox)),
true, // crop
false, // printing
2012-08-14 12:30:18 +00:00
nullptr, nullptr, nullptr, nullptr);
2013-04-06 09:01:05 +00:00
if(param.split_pages)
2012-09-12 15:26:14 +00:00
{
2013-06-09 14:19:12 +00:00
delete f_curpage;
f_curpage = nullptr;
2012-09-12 15:26:14 +00:00
}
2012-08-14 08:23:15 +00:00
}
if(page_count >= 0)
cerr << "Working: " << page_count << "/" << page_count;
cerr << endl;
2012-09-12 15:26:14 +00:00
2013-01-28 13:01:02 +00:00
////////////////////////
// Process Outline
2013-04-06 09:01:05 +00:00
if(param.process_outline)
process_outline();
2013-01-28 13:01:02 +00:00
2012-08-14 12:30:18 +00:00
post_process();
2014-11-16 14:04:02 +00:00
bg_renderer = nullptr;
fallback_bg_renderer = nullptr;
2012-08-14 12:30:18 +00:00
cerr << endl;
2012-08-14 08:23:15 +00:00
}
2012-09-16 10:30:34 +00:00
void HTMLRenderer::setDefaultCTM(double *ctm)
{
memcpy(default_ctm, ctm, sizeof(default_ctm));
}
void HTMLRenderer::startPage(int pageNum, GfxState *state, XRef * xref)
2012-09-16 10:30:34 +00:00
{
2014-11-16 14:04:02 +00:00
covered_text_detector.reset();
tracer.reset(state);
2013-02-06 12:29:48 +00:00
this->pageNum = pageNum;
html_text_page.set_page_size(state->getPageWidth(), state->getPageHeight());
2013-05-04 11:26:26 +00:00
reset_state();
}
void HTMLRenderer::endPage() {
long long wid = all_manager.width.install(html_text_page.get_width());
long long hid = all_manager.height.install(html_text_page.get_height());
2013-05-04 11:26:26 +00:00
2013-06-09 14:19:12 +00:00
(*f_curpage)
<< "<div id=\"" << CSS::PAGE_FRAME_CN << pageNum
2013-02-27 18:52:00 +00:00
<< "\" class=\"" << CSS::PAGE_FRAME_CN
2013-11-04 15:53:38 +00:00
<< " " << CSS::WIDTH_CN << wid
<< " " << CSS::HEIGHT_CN << hid
2013-02-27 18:52:00 +00:00
<< "\" data-page-no=\"" << pageNum << "\">"
<< "<div class=\"" << CSS::PAGE_CONTENT_BOX_CN
2013-02-28 14:37:15 +00:00
<< " " << CSS::PAGE_CONTENT_BOX_CN << pageNum
2013-07-16 15:42:02 +00:00
<< " " << CSS::WIDTH_CN << wid
<< " " << CSS::HEIGHT_CN << hid
<< "\">";
2012-09-16 10:30:34 +00:00
2013-06-09 14:19:12 +00:00
/*
* When split_pages is on, f_curpage points to the current page file
* and we want to output empty frames in f_pages.fs
*/
if(param.split_pages)
{
f_pages.fs
<< "<div id=\"" << CSS::PAGE_FRAME_CN << pageNum
2013-06-09 14:19:12 +00:00
<< "\" class=\"" << CSS::PAGE_FRAME_CN
2013-11-04 15:53:38 +00:00
<< " " << CSS::WIDTH_CN << wid
<< " " << CSS::HEIGHT_CN << hid
<< "\" data-page-no=\"" << pageNum
2013-06-09 14:19:12 +00:00
<< "\" data-page-url=\"";
2013-10-18 08:31:59 +00:00
writeAttribute(f_pages.fs, cur_page_filename);
2013-06-09 14:19:12 +00:00
f_pages.fs << "\">";
}
2013-04-06 09:01:05 +00:00
if(param.process_nontext)
2012-09-16 10:30:34 +00:00
{
if (bg_renderer->render_page(cur_doc, pageNum))
2014-11-16 14:04:02 +00:00
{
bg_renderer->embed_image(pageNum);
2014-11-16 14:04:02 +00:00
}
else if (fallback_bg_renderer)
{
if (fallback_bg_renderer->render_page(cur_doc, pageNum))
fallback_bg_renderer->embed_image(pageNum);
}
2012-09-16 10:30:34 +00:00
}
2012-09-17 07:28:52 +00:00
// dump all text
2013-06-09 14:19:12 +00:00
html_text_page.dump_text(*f_curpage);
html_text_page.dump_css(f_css.fs);
html_text_page.clear();
2014-08-11 14:36:35 +00:00
// process form
2014-11-14 19:28:17 +00:00
if(param.process_form)
2014-08-11 14:36:35 +00:00
process_form(*f_curpage);
// process links before the page is closed
cur_doc->processLinks(this, pageNum);
2012-09-12 15:26:14 +00:00
2012-09-25 11:29:59 +00:00
// close box
2013-06-09 14:19:12 +00:00
(*f_curpage) << "</div>";
2012-09-16 12:47:42 +00:00
2012-09-25 11:29:59 +00:00
// dump info for js
// TODO: create a function for this
// BE CAREFUL WITH ESCAPES
2012-09-16 12:47:42 +00:00
{
2014-11-16 14:04:02 +00:00
(*f_curpage) << "<div class=\"" << CSS::PAGE_DATA_CN << "\" data-data='{";
2012-09-16 12:47:42 +00:00
2014-11-16 14:04:02 +00:00
//default CTM
(*f_curpage) << "\"ctm\":[";
for(int i = 0; i < 6; ++i)
{
if(i > 0) (*f_curpage) << ",";
(*f_curpage) << round(default_ctm[i]);
}
(*f_curpage) << "]";
(*f_curpage) << "}'></div>";
}
2012-09-25 11:29:59 +00:00
// close page
2013-11-04 15:53:38 +00:00
(*f_curpage) << "</div>" << endl;
2013-06-09 14:19:12 +00:00
if(param.split_pages)
{
2013-11-04 15:53:38 +00:00
f_pages.fs << "</div>" << endl;
2013-06-09 14:19:12 +00:00
}
2012-08-14 08:23:15 +00:00
}
2012-10-24 14:24:33 +00:00
void HTMLRenderer::pre_process(PDFDoc * doc)
{
2012-10-24 14:24:33 +00:00
preprocessor.process(doc);
/*
* determine scale factors
*/
{
vector<double> zoom_factors;
2013-04-06 09:01:05 +00:00
if(is_positive(param.zoom))
2012-10-24 14:24:33 +00:00
{
2013-04-06 09:01:05 +00:00
zoom_factors.push_back(param.zoom);
2012-10-24 14:24:33 +00:00
}
2013-04-06 09:01:05 +00:00
if(is_positive(param.fit_width))
2012-10-24 14:24:33 +00:00
{
2013-04-06 09:01:05 +00:00
zoom_factors.push_back((param.fit_width) / preprocessor.get_max_width());
2012-10-24 14:24:33 +00:00
}
2013-04-06 09:01:05 +00:00
if(is_positive(param.fit_height))
2012-10-24 14:24:33 +00:00
{
2013-04-06 09:01:05 +00:00
zoom_factors.push_back((param.fit_height) / preprocessor.get_max_height());
2012-10-24 14:24:33 +00:00
}
2013-04-04 07:31:15 +00:00
double zoom = (zoom_factors.empty() ? 1.0 : (*min_element(zoom_factors.begin(), zoom_factors.end())));
text_scale_factor1 = max<double>(zoom, param.font_size_multiplier);
2012-10-24 14:24:33 +00:00
text_scale_factor2 = zoom / text_scale_factor1;
}
// we may output utf8 characters, so always use binary
{
/*
* If embed-css
* we have to keep the generated css file into a temporary place
* and embed it into the main html later
*
* otherwise
2013-04-06 09:01:05 +00:00
* leave it in param.dest_dir
*/
auto fn = (param.embed_css)
2013-04-06 09:01:05 +00:00
? str_fmt("%s/__css", param.tmp_dir.c_str())
: str_fmt("%s/%s", param.dest_dir.c_str(), param.css_filename.c_str());
if(param.embed_css)
2012-11-26 21:38:13 +00:00
tmp_files.add((char*)fn);
2013-01-28 12:00:20 +00:00
f_css.path = (char*)fn;
2013-01-28 10:46:44 +00:00
f_css.fs.open(f_css.path, ofstream::binary);
if(!f_css.fs)
2012-09-17 12:07:50 +00:00
throw string("Cannot open ") + (char*)fn + " for writing";
2013-01-28 10:46:44 +00:00
set_stream_flags(f_css.fs);
}
2013-04-06 09:01:05 +00:00
if (param.process_outline)
2013-01-28 12:00:20 +00:00
{
/*
* The logic for outline is similar to css
*/
auto fn = (param.embed_outline)
2013-04-06 09:01:05 +00:00
? str_fmt("%s/__outline", param.tmp_dir.c_str())
: str_fmt("%s/%s", param.dest_dir.c_str(), param.outline_filename.c_str());
2013-01-28 12:00:20 +00:00
if(param.embed_outline)
2013-01-28 12:00:20 +00:00
tmp_files.add((char*)fn);
f_outline.path = (char*)fn;
f_outline.fs.open(f_outline.path, ofstream::binary);
if(!f_outline.fs)
throw string("Cannot open") + (char*)fn + " for writing";
// might not be necessary
set_stream_flags(f_outline.fs);
}
{
/*
2013-01-28 12:00:20 +00:00
* we have to keep the html file for pages into a temporary place
* because we'll have to embed css before it
*
* Otherwise just generate it
*/
2013-04-06 09:01:05 +00:00
auto fn = str_fmt("%s/__pages", param.tmp_dir.c_str());
2012-11-26 21:38:13 +00:00
tmp_files.add((char*)fn);
2013-01-28 10:46:44 +00:00
f_pages.path = (char*)fn;
f_pages.fs.open(f_pages.path, ofstream::binary);
2013-01-28 10:46:44 +00:00
if(!f_pages.fs)
2012-09-17 12:07:50 +00:00
throw string("Cannot open ") + (char*)fn + " for writing";
2013-01-28 10:46:44 +00:00
set_stream_flags(f_pages.fs);
}
2013-06-09 14:19:12 +00:00
if(param.split_pages)
{
f_curpage = nullptr;
}
else
{
f_curpage = &f_pages.fs;
}
}
2013-02-06 12:29:48 +00:00
void HTMLRenderer::post_process(void)
{
2013-02-06 12:29:48 +00:00
dump_css();
2014-08-14 08:35:27 +00:00
// close files if they opened
2013-04-06 09:01:05 +00:00
if (param.process_outline)
{
f_outline.fs.close();
}
f_pages.fs.close();
2013-01-28 10:46:44 +00:00
f_css.fs.close();
// build the main HTML file
2012-09-17 12:07:50 +00:00
ofstream output;
{
2013-04-06 09:01:05 +00:00
auto fn = str_fmt("%s/%s", param.dest_dir.c_str(), param.output_filename.c_str());
2012-09-17 12:07:50 +00:00
output.open((char*)fn, ofstream::binary);
if(!output)
throw string("Cannot open ") + (char*)fn + " for writing";
2012-11-29 12:39:30 +00:00
set_stream_flags(output);
2012-09-17 12:07:50 +00:00
}
// apply manifest
2013-04-06 09:01:05 +00:00
ifstream manifest_fin((char*)str_fmt("%s/%s", param.data_dir.c_str(), MANIFEST_FILENAME.c_str()), ifstream::binary);
if(!manifest_fin)
throw "Cannot open the manifest file";
bool embed_string = false;
string line;
long line_no = 0;
while(getline(manifest_fin, line))
{
// trim space at both sides
{
static const char * whitespaces = " \t\n\v\f\r";
auto idx1 = line.find_first_not_of(whitespaces);
if(idx1 == string::npos)
{
line.clear();
}
else
{
auto idx2 = line.find_last_not_of(whitespaces);
assert(idx2 >= idx1);
line = line.substr(idx1, idx2 - idx1 + 1);
}
}
++line_no;
if(line == "\"\"\"")
{
embed_string = !embed_string;
continue;
}
if(embed_string)
{
output << line << endl;
continue;
}
if(line.empty() || line[0] == '#')
continue;
if(line[0] == '@')
{
2013-04-06 09:01:05 +00:00
embed_file(output, param.data_dir + "/" + line.substr(1), "", true);
continue;
}
if(line[0] == '$')
{
if(line == "$css")
{
2013-01-28 10:46:44 +00:00
embed_file(output, f_css.path, ".css", false);
}
2013-01-28 12:00:20 +00:00
else if (line == "$outline")
{
if (param.process_outline && param.embed_outline)
{
ifstream fin(f_outline.path, ifstream::binary);
if(!fin)
2013-02-22 09:11:27 +00:00
throw "Cannot open outline for reading";
output << fin.rdbuf();
output.clear(); // output will set fail big if fin is empty
}
2013-01-28 12:00:20 +00:00
}
else if (line == "$pages")
{
2013-06-09 14:19:12 +00:00
ifstream fin(f_pages.path, ifstream::binary);
if(!fin)
throw "Cannot open pages for reading";
output << fin.rdbuf();
output.clear(); // output will set fail bit if fin is empty
}
else
{
cerr << "Warning: manifest line " << line_no << ": Unknown content \"" << line << "\"" << endl;
}
continue;
}
cerr << "Warning: unknown line in manifest: " << line << endl;
}
}
2012-11-29 12:39:30 +00:00
void HTMLRenderer::set_stream_flags(std::ostream & out)
{
2012-10-01 17:59:04 +00:00
// we output all ID's in hex
// browsers are not happy with scientific notations
out << hex << fixed;
}
2013-02-06 12:29:48 +00:00
void HTMLRenderer::dump_css (void)
{
all_manager.transform_matrix.dump_css(f_css.fs);
all_manager.vertical_align .dump_css(f_css.fs);
all_manager.letter_space .dump_css(f_css.fs);
all_manager.stroke_color .dump_css(f_css.fs);
all_manager.word_space .dump_css(f_css.fs);
all_manager.whitespace .dump_css(f_css.fs);
all_manager.fill_color .dump_css(f_css.fs);
all_manager.font_size .dump_css(f_css.fs);
all_manager.bottom .dump_css(f_css.fs);
all_manager.height .dump_css(f_css.fs);
all_manager.width .dump_css(f_css.fs);
all_manager.left .dump_css(f_css.fs);
all_manager.bgimage_size .dump_css(f_css.fs);
2013-02-06 12:29:48 +00:00
// print css
2013-04-30 11:07:55 +00:00
if(param.printing)
{
double ps = print_scale();
f_css.fs << CSS::PRINT_ONLY << "{" << endl;
all_manager.transform_matrix.dump_print_css(f_css.fs, ps);
all_manager.vertical_align .dump_print_css(f_css.fs, ps);
all_manager.letter_space .dump_print_css(f_css.fs, ps);
all_manager.stroke_color .dump_print_css(f_css.fs, ps);
all_manager.word_space .dump_print_css(f_css.fs, ps);
all_manager.whitespace .dump_print_css(f_css.fs, ps);
all_manager.fill_color .dump_print_css(f_css.fs, ps);
all_manager.font_size .dump_print_css(f_css.fs, ps);
all_manager.bottom .dump_print_css(f_css.fs, ps);
all_manager.height .dump_print_css(f_css.fs, ps);
all_manager.width .dump_print_css(f_css.fs, ps);
all_manager.left .dump_print_css(f_css.fs, ps);
all_manager.bgimage_size .dump_print_css(f_css.fs, ps);
f_css.fs << "}" << endl;
}
2013-02-06 12:29:48 +00:00
}
void HTMLRenderer::embed_file(ostream & out, const string & path, const string & type, bool copy)
2012-09-12 15:26:14 +00:00
{
string fn = get_filename(path);
string suffix = (type == "") ? get_suffix(fn) : type;
auto iter = EMBED_STRING_MAP.find(suffix);
2012-09-12 15:26:14 +00:00
if(iter == EMBED_STRING_MAP.end())
{
cerr << "Warning: unknown suffix: " << suffix << endl;
2012-09-12 15:26:14 +00:00
return;
}
const auto & entry = iter->second;
if(param.*(entry.embed_flag))
2012-09-12 15:26:14 +00:00
{
ifstream fin(path, ifstream::binary);
if(!fin)
2012-09-17 12:07:50 +00:00
throw string("Cannot open file ") + path + " for embedding";
2013-09-27 11:48:40 +00:00
out << entry.prefix_embed;
2013-09-27 11:48:40 +00:00
if(entry.base64_encode)
{
out << Base64Stream(fin);
}
else
{
out << endl << fin.rdbuf();
}
2013-01-28 13:56:22 +00:00
out.clear(); // out will set fail big if fin is empty
out << entry.suffix_embed << endl;
2012-09-12 15:26:14 +00:00
}
else
{
out << entry.prefix_external;
2013-10-18 08:31:59 +00:00
writeAttribute(out, fn);
out << entry.suffix_external << endl;
if(copy)
{
ifstream fin(path, ifstream::binary);
if(!fin)
throw string("Cannot copy file: ") + path;
2013-04-06 09:01:05 +00:00
auto out_path = param.dest_dir + "/" + fn;
2012-09-17 12:07:50 +00:00
ofstream out(out_path, ofstream::binary);
if(!out)
throw string("Cannot open file ") + path + " for embedding";
out << fin.rdbuf();
2013-01-28 13:56:22 +00:00
out.clear(); // out will set fail big if fin is empty
}
2012-09-12 15:26:14 +00:00
}
}
const std::string HTMLRenderer::MANIFEST_FILENAME = "manifest";
}// namespace pdf2htmlEX