2009-03-28 17:23:53 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
import sys
|
2009-05-05 12:26:29 +00:00
|
|
|
from utils import apply_matrix_norm
|
2009-05-04 08:29:36 +00:00
|
|
|
INF = sys.maxint
|
2009-03-28 17:23:53 +00:00
|
|
|
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
## pick
|
2009-05-04 08:59:27 +00:00
|
|
|
##
|
2009-05-05 12:26:29 +00:00
|
|
|
def pick(seq, func, maxobj=None):
|
|
|
|
maxscore = None
|
|
|
|
for obj in seq:
|
|
|
|
score = func(obj)
|
|
|
|
if maxscore == None or maxscore < score:
|
|
|
|
(maxscore,maxobj) = (score,obj)
|
|
|
|
return maxobj
|
2009-05-04 08:59:27 +00:00
|
|
|
|
|
|
|
|
2009-05-04 08:29:36 +00:00
|
|
|
## bsearch
|
|
|
|
##
|
|
|
|
## Finds objects whose coordinates overlap with [v0,v1].
|
|
|
|
## It performs binary search so that the processing time
|
|
|
|
## should be around O(log n).
|
2009-03-28 17:23:53 +00:00
|
|
|
##
|
|
|
|
def bsearch(objs, v0, v1):
|
2009-05-04 08:29:36 +00:00
|
|
|
if v1 <= v0: return []
|
2009-03-28 17:23:53 +00:00
|
|
|
i0 = 0
|
|
|
|
i1 = len(objs)-1
|
|
|
|
while i0 <= i1:
|
|
|
|
i = (i0+i1)/2
|
|
|
|
assert 0 <= i and i < len(objs)
|
|
|
|
(v, obj) = objs[i]
|
|
|
|
if v < v0:
|
|
|
|
i0 = i+1
|
|
|
|
elif v1 < v:
|
|
|
|
i1 = i-1
|
|
|
|
else:
|
|
|
|
i0 = i
|
|
|
|
while 0 < i0:
|
|
|
|
(v,_) = objs[i0-1]
|
|
|
|
if v < v0: break
|
|
|
|
i0 -= 1
|
|
|
|
i1 = i
|
|
|
|
while i1 < len(objs)-1:
|
|
|
|
(v,_) = objs[i1+1]
|
|
|
|
if v1 < v: break
|
|
|
|
i1 += 1
|
|
|
|
return [ obj for (_,obj) in objs[i0:i1+1] ]
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
2009-05-04 08:29:36 +00:00
|
|
|
## reorder_hv, reorder_vh
|
2009-05-15 10:10:30 +00:00
|
|
|
## chop_hv, chop_vh
|
2009-05-04 08:29:36 +00:00
|
|
|
##
|
|
|
|
## Reorders objects according to its writing direction.
|
|
|
|
##
|
2009-05-05 12:26:29 +00:00
|
|
|
def reorder_vh(objs, hdir):
|
2009-05-04 08:29:36 +00:00
|
|
|
if 0 < hdir:
|
|
|
|
hkey = (lambda obj: obj.x0)
|
2009-05-05 12:26:29 +00:00
|
|
|
vkey = (lambda obj: -obj.y1)
|
2009-05-04 08:29:36 +00:00
|
|
|
else:
|
|
|
|
hkey = (lambda obj: -obj.x1)
|
2009-05-05 12:26:29 +00:00
|
|
|
vkey = (lambda obj: -obj.y1)
|
2009-05-04 08:29:36 +00:00
|
|
|
r = []
|
|
|
|
line = []
|
2009-05-05 12:26:29 +00:00
|
|
|
for obj in sorted(objs, key=vkey):
|
|
|
|
if line and not line[-1].voverlap(obj):
|
2009-05-04 08:29:36 +00:00
|
|
|
line.sort(key=hkey)
|
|
|
|
r.append(line)
|
|
|
|
line = []
|
2009-05-05 12:26:29 +00:00
|
|
|
line.append(obj)
|
2009-05-04 08:29:36 +00:00
|
|
|
line.sort(key=hkey)
|
|
|
|
r.append(line)
|
|
|
|
return r
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def reorder_hv(objs, hdir):
|
2009-05-04 08:29:36 +00:00
|
|
|
if 0 < hdir:
|
|
|
|
hkey = (lambda obj: obj.x0)
|
2009-05-05 12:26:29 +00:00
|
|
|
vkey = (lambda obj: -obj.y1)
|
2009-05-04 08:29:36 +00:00
|
|
|
else:
|
|
|
|
hkey = (lambda obj: -obj.x1)
|
2009-05-05 12:26:29 +00:00
|
|
|
vkey = (lambda obj: -obj.y1)
|
2009-05-04 08:29:36 +00:00
|
|
|
r = []
|
|
|
|
line = []
|
2009-05-05 12:26:29 +00:00
|
|
|
for obj in sorted(objs, key=hkey):
|
|
|
|
if line and not line[-1].hoverlap(obj):
|
2009-05-04 08:29:36 +00:00
|
|
|
line.sort(key=vkey)
|
|
|
|
r.append(line)
|
|
|
|
line = []
|
2009-05-05 12:26:29 +00:00
|
|
|
line.append(obj)
|
2009-05-04 08:29:36 +00:00
|
|
|
line.sort(key=vkey)
|
|
|
|
r.append(line)
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
2009-03-28 17:23:53 +00:00
|
|
|
## Plane
|
|
|
|
##
|
2009-05-04 08:29:36 +00:00
|
|
|
## A data structure for objects placed on a plane.
|
|
|
|
## Can efficiently find objects in a certain rectangular area.
|
|
|
|
## It maintains two parallel lists of objects, each of
|
|
|
|
## which is sorted by its x or y coordinate.
|
|
|
|
##
|
2009-03-28 17:23:53 +00:00
|
|
|
class Plane(object):
|
|
|
|
|
2009-05-04 08:29:36 +00:00
|
|
|
def __init__(self, objs):
|
2009-03-28 17:23:53 +00:00
|
|
|
self.xobjs = []
|
|
|
|
self.yobjs = []
|
2009-05-04 08:29:36 +00:00
|
|
|
for obj in objs:
|
|
|
|
self.place(obj)
|
|
|
|
self.fixate()
|
2009-03-28 17:23:53 +00:00
|
|
|
return
|
|
|
|
|
2009-05-04 08:29:36 +00:00
|
|
|
# place(obj): place an object in a certain area.
|
|
|
|
def place(self, obj):
|
2009-05-05 12:26:29 +00:00
|
|
|
assert isinstance(obj, LayoutItem)
|
2009-05-04 08:29:36 +00:00
|
|
|
self.xobjs.append((obj.x0, obj))
|
|
|
|
self.xobjs.append((obj.x1, obj))
|
|
|
|
self.yobjs.append((obj.y0, obj))
|
|
|
|
self.yobjs.append((obj.y1, obj))
|
2009-03-28 17:23:53 +00:00
|
|
|
return
|
|
|
|
|
2009-05-04 08:29:36 +00:00
|
|
|
# fixate(): you must call this after adding all objects.
|
|
|
|
def fixate(self):
|
2009-03-28 17:23:53 +00:00
|
|
|
self.xobjs.sort()
|
|
|
|
self.yobjs.sort()
|
|
|
|
return
|
|
|
|
|
2009-05-04 08:29:36 +00:00
|
|
|
# find(): finds objects that are in a certain area.
|
2009-03-28 17:23:53 +00:00
|
|
|
def find(self, (x0,y0,x1,y1)):
|
|
|
|
xobjs = set(bsearch(self.xobjs, x0, x1))
|
|
|
|
yobjs = set(bsearch(self.yobjs, y0, y1))
|
|
|
|
objs = xobjs.intersection(yobjs)
|
|
|
|
return objs
|
|
|
|
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
## ClusterSet
|
2009-05-04 08:29:36 +00:00
|
|
|
##
|
2009-05-15 14:25:32 +00:00
|
|
|
## Maintains a set of LTTextBox objects.
|
|
|
|
## It incrementally constructs LTTextBox objects
|
2009-05-05 12:26:29 +00:00
|
|
|
## and group them when necessary. It gives
|
2009-05-15 14:25:32 +00:00
|
|
|
## a sequence of LTTextBox objects that represent
|
2009-05-05 12:26:29 +00:00
|
|
|
## the text stream of that page.
|
2009-05-04 08:29:36 +00:00
|
|
|
##
|
2009-05-05 12:26:29 +00:00
|
|
|
class ClusterSet(object):
|
2009-05-04 08:29:36 +00:00
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def __init__(self, klass):
|
|
|
|
self.clusters = {}
|
|
|
|
self.klass = klass
|
2009-05-15 14:25:32 +00:00
|
|
|
self.i = 0
|
2009-05-05 12:26:29 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# add(objs): groups text objects if necessary.
|
|
|
|
def add(self, objs):
|
2009-05-15 14:25:32 +00:00
|
|
|
group = self.klass(objs, self.i)
|
|
|
|
self.i += 1
|
2009-05-05 12:26:29 +00:00
|
|
|
for obj in objs:
|
|
|
|
if obj in self.clusters:
|
|
|
|
group.merge(self.clusters[obj])
|
|
|
|
for obj in group:
|
|
|
|
self.clusters[obj] = group
|
|
|
|
return
|
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
# finish(): returns all the LTTextBoxes in a page.
|
2009-05-05 12:26:29 +00:00
|
|
|
def finish(self):
|
|
|
|
r = set(self.clusters.itervalues())
|
|
|
|
for group in r:
|
|
|
|
group.fixate()
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
|
|
|
## LayoutItem
|
|
|
|
##
|
|
|
|
class LayoutItem(object):
|
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
def __init__(self, bbox):
|
2009-05-05 12:26:29 +00:00
|
|
|
#assert x0 <= x1 and y0 <= y1
|
|
|
|
self.set_bbox(bbox)
|
|
|
|
return
|
|
|
|
|
|
|
|
def set_bbox(self, (x0,y0,x1,y1)):
|
|
|
|
self.x0 = x0
|
|
|
|
self.y0 = y0
|
|
|
|
self.x1 = x1
|
|
|
|
self.y1 = y1
|
|
|
|
self.width = x1-x0
|
|
|
|
self.height = y1-y0
|
2009-05-04 08:29:36 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
def __repr__(self):
|
2009-05-05 12:26:29 +00:00
|
|
|
return ('<pageitem bbox=%s>' % (self.get_bbox()))
|
|
|
|
|
|
|
|
def hoverlap(self, obj):
|
|
|
|
assert isinstance(obj, LayoutItem)
|
|
|
|
if self.x1 <= obj.x0 or obj.x1 <= self.x0:
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
return min(abs(self.x0-obj.x1), abs(self.x1-obj.x0))
|
|
|
|
|
|
|
|
def voverlap(self, obj):
|
|
|
|
assert isinstance(obj, LayoutItem)
|
|
|
|
if self.y1 <= obj.y0 or obj.y1 <= self.y0:
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
return min(abs(self.y0-obj.y1), abs(self.y1-obj.y0))
|
|
|
|
|
|
|
|
def get_bbox(self):
|
|
|
|
return '%.3f,%.3f,%.3f,%.3f' % (self.x0, self.y0, self.x1, self.y1)
|
|
|
|
|
|
|
|
def get_margin(self, ratio):
|
|
|
|
return 0
|
2009-05-04 08:29:36 +00:00
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def get_weight(self):
|
|
|
|
return 0
|
2009-05-04 08:29:36 +00:00
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def get_direction(self):
|
2009-05-15 10:10:30 +00:00
|
|
|
return None
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
## LayoutContainer
|
|
|
|
##
|
|
|
|
class LayoutContainer(LayoutItem):
|
|
|
|
|
|
|
|
def __init__(self, id, bbox, objs=None):
|
2009-05-15 14:25:32 +00:00
|
|
|
LayoutItem.__init__(self, bbox)
|
|
|
|
self.id = id
|
2009-05-05 12:26:29 +00:00
|
|
|
if objs:
|
|
|
|
self.objs = set(objs)
|
|
|
|
else:
|
|
|
|
self.objs = set()
|
|
|
|
self.weight = None
|
2009-05-04 08:29:36 +00:00
|
|
|
return
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def __repr__(self):
|
2009-05-15 10:10:30 +00:00
|
|
|
return ('<group %s>' % (self.get_bbox()))
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return iter(self.objs)
|
|
|
|
|
|
|
|
def add(self, obj):
|
|
|
|
self.objs.add(obj)
|
|
|
|
return
|
|
|
|
|
|
|
|
def merge(self, group):
|
|
|
|
self.objs.update(iter(group))
|
|
|
|
return
|
|
|
|
|
|
|
|
# fixate(): determines its boundery and writing direction.
|
|
|
|
def fixate(self):
|
|
|
|
if not self.width and self.objs:
|
|
|
|
(bx0, by0, bx1, by1) = (INF, INF, -INF, -INF)
|
|
|
|
for obj in self.objs:
|
|
|
|
bx0 = min(bx0, obj.x0)
|
|
|
|
by0 = min(by0, obj.y0)
|
|
|
|
bx1 = max(bx1, obj.x1)
|
|
|
|
by1 = max(by1, obj.y1)
|
|
|
|
self.set_bbox((bx0, by0, bx1, by1))
|
|
|
|
self.weight = sum( obj.get_weight() for obj in self.objs )
|
|
|
|
return
|
|
|
|
|
|
|
|
def group_objs(self, ratio, klass):
|
|
|
|
plane = Plane(self.objs)
|
|
|
|
cset = ClusterSet(klass)
|
2009-05-04 08:29:36 +00:00
|
|
|
for obj in self.objs:
|
2009-05-05 12:26:29 +00:00
|
|
|
margin = abs(obj.get_margin(ratio))
|
|
|
|
neighbors = plane.find((obj.x0-margin, obj.y0-margin, obj.x1+margin, obj.y1+margin))
|
|
|
|
cset.add(neighbors)
|
|
|
|
self.objs = cset.finish()
|
|
|
|
return
|
|
|
|
|
|
|
|
def get_weight(self):
|
|
|
|
return self.weight
|
|
|
|
|
|
|
|
def get_direction(self):
|
2009-05-15 10:10:30 +00:00
|
|
|
if not self.objs: return None
|
|
|
|
d = {}
|
|
|
|
for obj in self.objs:
|
|
|
|
k = obj.get_direction()
|
|
|
|
if k not in d: d[k] = 0
|
|
|
|
d[k] += 1
|
|
|
|
(direction,_) = sorted(d.iteritems(), key=lambda (k,v):v)[0]
|
|
|
|
return direction
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
## LTLine
|
|
|
|
##
|
|
|
|
class LTLine(LayoutItem):
|
|
|
|
|
|
|
|
def __init__(self, linewidth, direction, bbox):
|
|
|
|
LayoutItem.__init__(self, bbox)
|
|
|
|
self.linewidth = linewidth
|
|
|
|
self.direction = direction
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
## LTRect
|
2009-05-05 12:26:29 +00:00
|
|
|
##
|
2009-05-15 14:25:32 +00:00
|
|
|
class LTRect(LayoutItem):
|
|
|
|
|
|
|
|
def __init__(self, linewidth, bbox):
|
|
|
|
LayoutItem.__init__(self, bbox)
|
|
|
|
self.linewidth = linewidth
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
## LTFigure
|
|
|
|
##
|
|
|
|
class LTFigure(LayoutContainer):
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return ('<figure id=%r bbox=%s>' % (self.id, self.get_bbox()))
|
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
|
|
|
|
## LTText
|
2009-05-05 12:26:29 +00:00
|
|
|
##
|
2009-05-15 14:25:32 +00:00
|
|
|
class LTText(LayoutItem):
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
def __init__(self, matrix, font, fontsize, charspace, scaling, chars):
|
|
|
|
assert chars
|
|
|
|
self.matrix = matrix
|
|
|
|
self.font = font
|
|
|
|
(_,_,_,_,tx,ty) = self.matrix
|
|
|
|
self.vertical = self.font.is_vertical()
|
|
|
|
self.text = ''.join( char for (char,_) in chars )
|
|
|
|
adv = sum( font.char_width(cid) for (_,cid) in chars )
|
|
|
|
adv = (adv * fontsize + len(chars)*charspace) * scaling * .01
|
|
|
|
size = (font.get_ascent() - font.get_descent()) * fontsize
|
|
|
|
if not self.vertical:
|
|
|
|
# horizontal text
|
|
|
|
self.vertical = False
|
|
|
|
(dx,dy) = apply_matrix_norm(self.matrix, (adv,size))
|
|
|
|
(_,descent) = apply_matrix_norm(self.matrix, (0,font.get_descent() * fontsize))
|
|
|
|
ty += descent
|
|
|
|
self.adv = (dx, 0)
|
|
|
|
bbox = (tx, ty, tx+dx, ty+dy)
|
|
|
|
else:
|
|
|
|
# vertical text
|
|
|
|
(_,cid) = chars[0]
|
|
|
|
(_,disp) = apply_matrix_norm(self.matrix, (0, (1000-font.char_disp(cid))*fontsize*.001))
|
|
|
|
(dx,dy) = apply_matrix_norm(self.matrix, (size,adv))
|
|
|
|
tx -= dx/2
|
|
|
|
ty += disp
|
|
|
|
self.adv = (0, dy)
|
|
|
|
bbox = (tx, ty+dy, tx+dx, ty)
|
|
|
|
self.fontsize = max(apply_matrix_norm(self.matrix, (size,size)))
|
2009-05-15 14:25:32 +00:00
|
|
|
LayoutItem.__init__(self, bbox)
|
2009-05-05 12:26:29 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return ('<text matrix=%s font=%r fontsize=%.1f bbox=%s adv=%s text=%r>' %
|
|
|
|
('[%.1f, %.1f, %.1f, %.1f, (%.1f, %.1f)]' % self.matrix,
|
|
|
|
self.font, self.fontsize, self.get_bbox(),
|
|
|
|
'(%.1f, %.1f)' % self.adv,
|
|
|
|
self.text))
|
|
|
|
|
|
|
|
def get_margin(self, ratio):
|
|
|
|
return self.fontsize * ratio
|
|
|
|
|
|
|
|
def get_weight(self):
|
|
|
|
return len(self.text)
|
|
|
|
|
2009-05-15 10:10:30 +00:00
|
|
|
def is_vertical(self):
|
2009-05-05 12:26:29 +00:00
|
|
|
return self.vertical
|
|
|
|
|
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
## LTTextBox
|
2009-05-05 12:26:29 +00:00
|
|
|
##
|
2009-05-15 14:25:32 +00:00
|
|
|
## A set of text objects that are grouped within
|
2009-05-05 12:26:29 +00:00
|
|
|
## a certain rectangular area.
|
|
|
|
##
|
2009-05-15 14:25:32 +00:00
|
|
|
class LTTextBox(LayoutContainer):
|
2009-05-05 12:26:29 +00:00
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
def __init__(self, id, objs):
|
|
|
|
LayoutContainer.__init__(self, id, (0,0,0,0), objs)
|
2009-05-15 10:10:30 +00:00
|
|
|
self.direction = None
|
2009-05-05 12:26:29 +00:00
|
|
|
return
|
|
|
|
|
2009-05-15 10:10:30 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return ('<textbox %s(%s)>' % (self.get_bbox(), self.direction))
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def fixate(self):
|
|
|
|
LayoutContainer.fixate(self)
|
2009-05-15 10:10:30 +00:00
|
|
|
self.direction = 'H'
|
2009-05-04 08:29:36 +00:00
|
|
|
for obj in self.objs:
|
2009-05-15 10:10:30 +00:00
|
|
|
if obj.is_vertical():
|
|
|
|
self.direction = 'V'
|
2009-05-04 08:29:36 +00:00
|
|
|
break
|
|
|
|
if 2 <= len(self.objs):
|
|
|
|
objs = sorted(self.objs, key=lambda obj: -obj.x1-obj.y1)
|
2009-05-05 12:26:29 +00:00
|
|
|
if objs[0].get_weight() == 1 and objs[1].get_weight() == 1:
|
2009-05-04 08:29:36 +00:00
|
|
|
h = objs[0].voverlap(objs[1])
|
|
|
|
v = objs[0].hoverlap(objs[1])
|
2009-05-15 10:10:30 +00:00
|
|
|
if h < v:
|
|
|
|
self.direction = 'V'
|
|
|
|
if self.direction == 'H':
|
|
|
|
self.lines = reorder_vh(self.objs, +1)
|
|
|
|
else:
|
|
|
|
self.lines = reorder_hv(self.objs, -1)
|
|
|
|
self.objs = []
|
|
|
|
for line in self.lines:
|
|
|
|
self.objs.extend(line)
|
2009-05-04 08:29:36 +00:00
|
|
|
return
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def get_direction(self):
|
2009-05-15 10:10:30 +00:00
|
|
|
return self.direction
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
def get_lines(self, ratio):
|
2009-05-15 10:10:30 +00:00
|
|
|
if self.get_direction() == 'H':
|
|
|
|
for line in self.lines:
|
2009-05-04 08:29:36 +00:00
|
|
|
s = ''
|
2009-05-15 10:10:30 +00:00
|
|
|
x1 = INF
|
2009-05-04 08:29:36 +00:00
|
|
|
for obj in line:
|
2009-05-15 14:25:32 +00:00
|
|
|
if not isinstance(obj, LTText): continue
|
2009-05-14 14:25:20 +00:00
|
|
|
margin = obj.get_margin(ratio)
|
2009-05-15 10:10:30 +00:00
|
|
|
if x1 < obj.x0-margin:
|
2009-05-04 08:29:36 +00:00
|
|
|
s += ' '
|
|
|
|
s += obj.text
|
2009-05-15 10:10:30 +00:00
|
|
|
x1 = obj.x1
|
2009-05-04 08:29:36 +00:00
|
|
|
yield s
|
|
|
|
else:
|
2009-05-15 10:10:30 +00:00
|
|
|
for line in self.lines:
|
2009-05-04 08:29:36 +00:00
|
|
|
s = ''
|
2009-05-15 10:10:30 +00:00
|
|
|
y0 = -INF
|
2009-05-04 08:29:36 +00:00
|
|
|
for obj in line:
|
2009-05-15 14:25:32 +00:00
|
|
|
if not isinstance(obj, LTText): continue
|
2009-05-14 14:25:20 +00:00
|
|
|
margin = obj.get_margin(ratio)
|
2009-05-15 10:10:30 +00:00
|
|
|
if obj.y1+margin < y0:
|
2009-05-04 08:29:36 +00:00
|
|
|
s += ' '
|
|
|
|
s += obj.text
|
2009-05-15 10:10:30 +00:00
|
|
|
y0 = obj.y0
|
2009-05-04 08:29:36 +00:00
|
|
|
yield s
|
|
|
|
return
|
|
|
|
|
|
|
|
|
2009-05-15 14:25:32 +00:00
|
|
|
## LTPage
|
2009-05-04 08:29:36 +00:00
|
|
|
##
|
2009-05-15 14:25:32 +00:00
|
|
|
class LTPage(LayoutContainer):
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
def __init__(self, id, bbox, rotate=0):
|
|
|
|
LayoutContainer.__init__(self, id, bbox)
|
|
|
|
self.rotate = rotate
|
2009-03-28 17:23:53 +00:00
|
|
|
return
|
2009-05-05 12:26:29 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return ('<page id=%r bbox=%s rotate=%r>' % (self.id, self.get_bbox(), self.rotate))
|
2009-03-28 17:23:53 +00:00
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def fixate(self):
|
2009-03-28 17:23:53 +00:00
|
|
|
return
|
|
|
|
|
2009-05-05 12:26:29 +00:00
|
|
|
def group_text(self, ratio):
|
2009-05-15 14:25:32 +00:00
|
|
|
self.group_objs(ratio, LTTextBox)
|
2009-05-15 10:10:30 +00:00
|
|
|
if self.get_direction() == 'H':
|
2009-05-05 12:26:29 +00:00
|
|
|
lines = reorder_vh(self.objs, +1)
|
2009-05-15 10:10:30 +00:00
|
|
|
else:
|
|
|
|
lines = reorder_hv(self.objs, -1)
|
2009-05-05 12:26:29 +00:00
|
|
|
self.objs = []
|
|
|
|
for line in lines:
|
|
|
|
self.objs.extend(line)
|
|
|
|
return
|