Skip to content

Commit ce28c00

Browse files
committed
Implement covered text handling.
1 parent 9c0b2a8 commit ce28c00

19 files changed

Lines changed: 368 additions & 8 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ set(PDF2HTMLEX_SRC ${PDF2HTMLEX_SRC}
161161
src/Base64Stream.cc
162162
src/Color.h
163163
src/Color.cc
164+
src/CoveredTextHandler.h
165+
src/CoveredTextHandler.cc
164166
src/HTMLState.h
165167
src/HTMLTextLine.h
166168
src/HTMLTextLine.cc

pdf2htmlEX.1.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ If set to 0, pdf2htmlEX would try its best to balance the two methods above.
242242
.B --optimize-text <0|1> (Default: 0)
243243
If set to 1, pdf2htmlEX will try to reduce the number of HTML elements used for text. Turn it off if anything goes wrong.
244244

245+
.TP
246+
.B --process-covered-text <0|1> (Default: 0)
247+
If set to 1, pdf2htmlEX will try to detect texts covered by other graphics and properly arrange them,
248+
i.e. covered texts are made transparent in text layer, and are drawn on background layer.
249+
245250
.SS Background Image
246251

247252
.TP

src/BackgroundRenderer/CairoBackgroundRenderer.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ void CairoBackgroundRenderer::drawChar(GfxState *state, double x, double y,
6363
{
6464
CairoOutputDev::drawChar(state,x,y,dx,dy,originX,originY,code,nBytes,u,uLen);
6565
}
66+
// If a char is treated as image, it is not subject to cover test
67+
// (see HTMLRenderer::drawString), so don't increase drawn_char_count.
68+
else if (param.process_covered_text) {
69+
if (html_renderer->get_chars_covered()[drawn_char_count])
70+
CairoOutputDev::drawChar(state,x,y,dx,dy,originX,originY,code,nBytes,u,uLen);
71+
drawn_char_count++;
72+
}
6673
}
6774

6875
void CairoBackgroundRenderer::beginTextObject(GfxState *state)
@@ -97,6 +104,7 @@ static GBool annot_cb(Annot *, void * pflag) {
97104

98105
bool CairoBackgroundRenderer::render_page(PDFDoc * doc, int pageno)
99106
{
107+
drawn_char_count = 0;
100108
double page_width;
101109
double page_height;
102110
if(param.use_cropbox)

src/BackgroundRenderer/CairoBackgroundRenderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class CairoBackgroundRenderer : public BackgroundRenderer, CairoOutputDev
6666
std::unordered_map<int, int> bitmaps_ref_count;
6767
// id of bitmaps' stream used by current page
6868
std::vector<int> bitmaps_in_current_page;
69+
int drawn_char_count;
6970
};
7071

7172
}

src/CoveredTextHandler.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* CoveredTextHandler.cc
3+
*
4+
* Created on: 2014-6-14
5+
* Author: duanyao
6+
*/
7+
8+
#include "CoveredTextHandler.h"
9+
10+
#include "util/math.h"
11+
12+
namespace pdf2htmlEX {
13+
14+
CoveredTextHandler::CoveredTextHandler()
15+
{
16+
// TODO Auto-generated constructor stub
17+
18+
}
19+
20+
CoveredTextHandler::~CoveredTextHandler()
21+
{
22+
// TODO Auto-generated destructor stub
23+
}
24+
25+
void CoveredTextHandler::reset()
26+
{
27+
char_bboxes.clear();
28+
chars_covered.clear();
29+
}
30+
31+
void CoveredTextHandler::add_char_bbox(double * bbox)
32+
{
33+
for (int i = 0; i < 4; i++)
34+
char_bboxes.push_back(bbox[i]);
35+
chars_covered.push_back(false);
36+
}
37+
38+
void CoveredTextHandler::add_non_char_bbox(double * bbox, int index)
39+
{
40+
if (index < 0)
41+
index = chars_covered.size();
42+
for (int i = 0; i < index; i++)
43+
{
44+
if (chars_covered[i])
45+
continue;
46+
double * cbbox = &char_bboxes[i * 4];
47+
if (bbox_intersect(cbbox, bbox))
48+
{
49+
chars_covered[i] = true;
50+
add_non_char_bbox(cbbox, i);
51+
}
52+
}
53+
}
54+
55+
}

src/CoveredTextHandler.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* CoveredTextHandler.h
3+
*
4+
* Created on: 2014-6-14
5+
* Author: duanyao
6+
*/
7+
8+
#ifndef COVEREDTEXTHANDLER_H__
9+
#define COVEREDTEXTHANDLER_H__
10+
11+
#include <vector>
12+
13+
namespace pdf2htmlEX {
14+
15+
/**
16+
* Detect characters that are covered by non-char graphics on a page.
17+
*/
18+
class CoveredTextHandler
19+
{
20+
public:
21+
CoveredTextHandler();
22+
virtual ~CoveredTextHandler();
23+
24+
/**
25+
* Reset to initial state. Should be called when start drawing a page.
26+
*/
27+
void reset();
28+
29+
/**
30+
* Add a drawn character's bounding box.
31+
* @param bbox (x0, y0, x1, y1)
32+
*/
33+
void add_char_bbox(double * bbox);
34+
35+
/**
36+
* Add a drawn non-char graphics' bounding box.
37+
* If it intersects any previously drawn char's bbox, the char is marked as covered
38+
* and treated as an non-char.
39+
* @param bbox (x0, y0, x1, y1)
40+
* @param index this graphics' drawing order: assume it is drawn after (index-1)th
41+
* char. -1 means after the last char.
42+
*/
43+
void add_non_char_bbox(double * bbox, int index = -1);
44+
45+
/**
46+
* An array of flags indicating whether a char is covered by any non-char graphics.
47+
* Index by the order that these chars are added.
48+
* This vector grows as add_char_bbox() is called, so its size is the count
49+
* of currently drawn chars.
50+
*/
51+
const std::vector<bool> & get_chars_covered() { return chars_covered; }
52+
53+
private:
54+
//covered text test
55+
std::vector<bool> chars_covered;
56+
// x00, y00, x01, y01; x10, y10, x11, y11;...
57+
std::vector<double> char_bboxes;
58+
};
59+
60+
}
61+
62+
#endif /* COVEREDTEXTHANDLER_H__ */

src/HTMLRenderer/HTMLRenderer.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "HTMLTextPage.h"
3232

3333
#include "BackgroundRenderer/BackgroundRenderer.h"
34+
#include "CoveredTextHandler.h"
3435

3536
#include "util/const.h"
3637
#include "util/misc.h"
@@ -125,6 +126,15 @@ class HTMLRenderer : public OutputDev
125126

126127
virtual void drawImage(GfxState * state, Object * ref, Stream * str, int width, int height, GfxImageColorMap * colorMap, GBool interpolate, int *maskColors, GBool inlineImg);
127128

129+
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
130+
int width, int height,
131+
GfxImageColorMap *colorMap,
132+
GBool interpolate,
133+
Stream *maskStr,
134+
int maskWidth, int maskHeight,
135+
GfxImageColorMap *maskColorMap,
136+
GBool maskInterpolate);
137+
128138
virtual void stroke(GfxState *state) { css_do_path(state, false); }
129139
virtual void fill(GfxState *state) { css_do_path(state, true); }
130140
virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax);
@@ -135,6 +145,8 @@ class HTMLRenderer : public OutputDev
135145
bool can_stroke(GfxState *state) { return css_do_path(state, false, true); }
136146
bool can_fill(GfxState *state) { return css_do_path(state, true, true); }
137147

148+
const std::vector<bool> & get_chars_covered() { return covered_text_handler.get_chars_covered(); }
149+
138150
protected:
139151
////////////////////////////////////////////////////
140152
// misc
@@ -215,6 +227,19 @@ class HTMLRenderer : public OutputDev
215227
const GfxRGB * line_color, const GfxRGB * fill_color,
216228
void (*style_function)(void *, std::ostream &) = nullptr, void * style_function_data = nullptr );
217229

230+
////////////////////////////////////////////////////
231+
// Covered text handling
232+
////////////////////////////////////////////////////
233+
/*
234+
* Cue CoveredTextHandler that a character is drawn
235+
* x, y: glyph-drawing position, in PDF text object space.
236+
* ax, ay: glyph advance, in glyph space.
237+
*/
238+
void add_char_bbox(GfxState *state, double x, double y, double ax, double ay);
239+
/*
240+
* Cue CoveredTextHandler that an image is drawn
241+
*/
242+
void add_image_bbox(GfxState *state);
218243

219244
////////////////////////////////////////////////////
220245
// PDF stuffs
@@ -338,6 +363,8 @@ class HTMLRenderer : public OutputDev
338363
std::string cur_page_filename;
339364

340365
static const std::string MANIFEST_FILENAME;
366+
367+
CoveredTextHandler covered_text_handler;
341368
};
342369

343370
} //namespace pdf2htmlEX

src/HTMLRenderer/general.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,10 @@ void HTMLRenderer::process(PDFDoc *doc)
133133
cur_page_filename = filled_template_filename;
134134
}
135135

136-
if(param.process_nontext)
137-
{
138-
fallback_bg_required = !bg_renderer->render_page(doc, i);
139-
if (fallback_bg_required && fallback_bg_renderer != nullptr)
140-
fallback_bg_renderer->render_page(doc, i);
141-
}
142-
136+
// We handle covered texts during doc->displayPage(this...),
137+
// and bg_renderer->render_page() depends on the result, so it must be called after
138+
// doc->displayPage(this...).
139+
covered_text_handler.reset();
143140
doc->displayPage(this, i,
144141
text_zoom_factor() * DEFAULT_DPI, text_zoom_factor() * DEFAULT_DPI,
145142
0,
@@ -148,6 +145,13 @@ void HTMLRenderer::process(PDFDoc *doc)
148145
false, // printing
149146
nullptr, nullptr, nullptr, nullptr);
150147

148+
if(param.process_nontext)
149+
{
150+
fallback_bg_required = !bg_renderer->render_page(doc, i);
151+
if (fallback_bg_required && fallback_bg_renderer != nullptr)
152+
fallback_bg_renderer->render_page(doc, i);
153+
}
154+
151155
if(param.split_pages)
152156
{
153157
delete f_curpage;

src/HTMLRenderer/image.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ namespace pdf2htmlEX {
1414

1515
void HTMLRenderer::drawImage(GfxState * state, Object * ref, Stream * str, int width, int height, GfxImageColorMap * colorMap, GBool interpolate, int *maskColors, GBool inlineImg)
1616
{
17+
add_image_bbox(state);
18+
1719
return OutputDev::drawImage(state,ref,str,width,height,colorMap,interpolate,maskColors,inlineImg);
1820

1921
#if 0
@@ -62,4 +64,30 @@ void HTMLRenderer::drawImage(GfxState * state, Object * ref, Stream * str, int w
6264
#endif
6365
}
6466

67+
void HTMLRenderer::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
68+
int width, int height,
69+
GfxImageColorMap *colorMap,
70+
GBool interpolate,
71+
Stream *maskStr,
72+
int maskWidth, int maskHeight,
73+
GfxImageColorMap *maskColorMap,
74+
GBool maskInterpolate)
75+
{
76+
add_image_bbox(state);
77+
78+
return OutputDev::drawSoftMaskedImage(state,ref,str,
79+
width,height,colorMap,interpolate,
80+
maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
81+
}
82+
83+
void HTMLRenderer::add_image_bbox(GfxState *state)
84+
{
85+
if (!param.process_covered_text)
86+
return;
87+
auto ctm = state->getCTM();
88+
double bbox[4] {0, 0, 1, 1};
89+
tm_transform_bbox(ctm, bbox);
90+
covered_text_handler.add_non_char_bbox(bbox);
91+
}
92+
6593
} // namespace pdf2htmlEX

src/HTMLRenderer/state.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ void HTMLRenderer::reset_state()
119119
cur_line_state.y = 0;
120120
memcpy(cur_line_state.transform_matrix, ID_MATRIX, sizeof(cur_line_state.transform_matrix));
121121

122+
if (param.process_covered_text)
123+
cur_line_state.chars_covered = &covered_text_handler.get_chars_covered();
124+
122125
cur_clip_state.xmin = 0;
123126
cur_clip_state.xmax = 0;
124127
cur_clip_state.ymin = 0;
@@ -502,6 +505,8 @@ void HTMLRenderer::prepare_text_line(GfxState * state)
502505
double rise_x, rise_y;
503506
state->textTransformDelta(0, state->getRise(), &rise_x, &rise_y);
504507
state->transform(state->getCurX() + rise_x, state->getCurY() + rise_y, &cur_line_state.x, &cur_line_state.y);
508+
if (param.process_covered_text)
509+
cur_line_state.first_char_index = covered_text_handler.get_chars_covered().size();
505510
html_text_page.open_new_line(cur_line_state);
506511

507512
cur_text_state.vertical_align = 0;

0 commit comments

Comments
 (0)