Sync to trunk.

This commit is contained in:
John Schember 2009-06-11 06:09:07 -04:00
commit f334cd786e
5 changed files with 387 additions and 91 deletions

View File

@ -61,8 +61,9 @@ def roman(num):
def fmt_sidx(i, fmt='%.2f', use_roman=False):
if i is None:
if i is None or i == '':
i = 1
i = float(i)
if int(i) == float(i):
return roman(int(i)) if use_roman else '%d'%int(i)
try:

View File

@ -88,6 +88,7 @@ def encode(data):
DECINT_FORWARD = 0
DECINT_BACKWARD = 1
def decint(value, direction):
# Encode vwi
bytes = []
while True:
b = value & 0x7f
@ -324,6 +325,8 @@ class MobiWriter(object):
self._imagemax = imagemax or OTHER_MAX_IMAGE_SIZE
self._prefer_author_sort = prefer_author_sort
self._primary_index_record = None
self._HTMLRecords = []
self._tbSequence = ""
@classmethod
def generate(cls, opts):
@ -358,7 +361,8 @@ class MobiWriter(object):
def _generate_content(self):
self._map_image_names()
self._generate_text()
if INDEXING and not self.opts.no_mobi_index:
#if INDEXING and not self.opts.no_mobi_index:
if INDEXING :
try:
self._generate_index()
except:
@ -403,6 +407,130 @@ class MobiWriter(object):
text.seek(npos)
return data, overlap
def _build_HTMLRecords_Data_List(self):
# Assemble a HTMLRecordData instance for each HTML record
numberOfHTMLRecords = ( self._content_length // RECORD_SIZE ) + 1
# Create a list of HTMLRecordData class instances
x = numberOfHTMLRecords
while x:
self._HTMLRecords.append(HTMLRecordData())
x -= 1
toc = self._oeb.toc
myIndex = 0
myEndingRecord = 0
entries = list(toc.iter())[1:]
# borrowed from _generate_indxt
for i, child in enumerate(entries):
if not child.title or not child.title.strip():
continue
h = child.href
if h not in self._id_offsets:
self._oeb.log.warning('Could not find TOC entry:', child.title)
continue
offset = self._id_offsets[h]
length = None
for sibling in entries[i+1:]:
h2 = sibling.href
if h2 in self._id_offsets:
offset2 = self._id_offsets[h2]
if offset2 > offset:
length = offset2 - offset
break
if length is None:
length = self._content_length - offset
# Calculate the HTML record for this entry
myStartingRecord = offset // RECORD_SIZE
# If no one has taken the openingNode slot, it must be us
if self._HTMLRecords[myStartingRecord].openingNode == -1 :
self._HTMLRecords[myStartingRecord].openingNode = myIndex
# Bump the node count for this HTML record
# Special case if we're the first so we get a true node count
if self._HTMLRecords[myStartingRecord].currentSectionNodeCount == -1:
self._HTMLRecords[myStartingRecord].currentSectionNodeCount = 1
else:
self._HTMLRecords[myStartingRecord].currentSectionNodeCount += 1
# Calculate the ending HTMLRecord of this entry
myEndingRecord = (offset + length) // RECORD_SIZE
# Tell the future HTML records about us
if myEndingRecord > myStartingRecord :
interimSpanRecord = myStartingRecord + 1
while interimSpanRecord <= myEndingRecord :
self._HTMLRecords[interimSpanRecord].continuingNode = myIndex
self._HTMLRecords[interimSpanRecord].currentSectionNodeCount = 1
interimSpanRecord += 1
ctoc_offset = self._ctoc_map[child]
last_name = "%04d" % myIndex
myIndex += 1
'''
# Dump the accumulated HTML Record Data
x = 0
while x < len(self._HTMLRecords):
self._HTMLRecords[x].dumpData(x, self._oeb)
x += 1
'''
def _build_TBS_Book(self, nrecords, lastrecord):
# Variables for trailing byte sequence
tbsType = 0x00
tbSequence = ""
#print "_build_TBS_Book: nrecords = %d, lastrecord = %d" % (nrecords, lastrecord)
# Generate TBS for type 0x002 - mobi_book
if nrecords == 0 :
# First HTML record is a special case
if self._HTMLRecords[nrecords].currentSectionNodeCount == 1 :
tbsType = 2
else :
tbsType = 6
tbSequence = decint(tbsType, DECINT_BACKWARD)
tbSequence += decint(0x00, DECINT_BACKWARD)
# Don't write a nodecount for opening type 2 record
if tbsType != 2 :
tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
tbSequence += decint(len(tbSequence) + 1, DECINT_BACKWARD)
else :
# Determine tbsType for HTMLRecords > 0
if nrecords == lastrecord and self._HTMLRecords[nrecords].currentSectionNodeCount == 1 :
# Ending record with singleton node
tbsType = 2
elif self._HTMLRecords[nrecords].continuingNode > 0 and self._HTMLRecords[nrecords].openingNode == -1 :
# This is a span-only record
tbsType = 3
# Zero out the nodeCount with a pre-formed vwi
self._HTMLRecords[nrecords].currentSectionNodeCount = 0x80
else :
tbsType = 6
# Shift the openingNode index << 3
shiftedNCXEntry = self._HTMLRecords[nrecords].continuingNode << 3
# Add the TBS type
shiftedNCXEntry |= tbsType
# Assemble the TBS
tbSequence = decint(shiftedNCXEntry, DECINT_BACKWARD)
tbSequence += decint(0x00, DECINT_BACKWARD)
# Don't write a nodecount for terminating type 2 record
if tbsType != 2 :
tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
tbSequence += decint(len(tbSequence) + 1, DECINT_BACKWARD)
# print "record %d: tbsType %d" % (nrecords, tbsType)
self._tbSequence = tbSequence
def _generate_text(self):
self._oeb.logger.info('Serializing markup content...')
serializer = Serializer(self._oeb, self._images)
@ -414,10 +542,25 @@ class MobiWriter(object):
text = StringIO(text)
buf = []
nrecords = 0
lastrecord = (self._content_length // RECORD_SIZE )
offset = 0
if self._compression != UNCOMPRESSED:
self._oeb.logger.info('Compressing markup content...')
data, overlap = self._read_text_record(text)
# GR borrowed this from generate_index
# We seem to need it before calling self._build_HTMLRecords_Data_List()
ctoc = self._generate_ctoc()
# Build the HTMLRecords list so we can assemble the trailing bytes sequences in the following while loop
toc = self._oeb.toc
entries = list(toc.iter())[1:]
hasNCXEntries = True if len(entries) else False
if hasNCXEntries :
self._build_HTMLRecords_Data_List()
while len(data) > 0:
if self._compression == PALMDOC:
data = compress_doc(data)
@ -428,24 +571,59 @@ class MobiWriter(object):
nextra = 0
pbreak = 0
running = offset
while breaks and (breaks[0] - offset) < RECORD_SIZE:
pbreak = (breaks.pop(0) - running) >> 3
encoded = decint(pbreak, DECINT_FORWARD)
record.write(encoded)
running += pbreak << 3
nextra += len(encoded)
lsize = 1
while True:
size = decint(nextra + lsize, DECINT_BACKWARD)
if len(size) == lsize:
break
lsize += 1
record.write(size)
# Write Trailing Byte Sequence
if INDEXING and hasNCXEntries :
# Dispatch to different TBS generators based upon publication type
booktype = 0x101 if self.opts.mobi_periodical else 0x002
if booktype == 0x002 :
self._build_TBS_Book(nrecords, lastrecord)
#elif booktype == flatPeriodical :
# tbSequence = self._build_TBS_FlatPeriodicalTBS()
#elif booktype == structuredPeriodical :
# tbSequence = self._build_TBS_StructuredPeriodicalTBS()
else :
raise NotImplementedError('Indexing for periodicals not implemented')
# Dump the current HTML Record Data / TBS
# GR diagnostics
if False :
self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
outstr = ''
for eachbyte in self._tbSequence:
outstr += '0x%02X ' % ord(eachbyte)
self._oeb.logger.info(' Trailing Byte Sequence: %s\n' % outstr)
# Write the sequence
record.write(self._tbSequence)
else :
# Marshall's original code
while breaks and (breaks[0] - offset) < RECORD_SIZE:
# .pop returns item, removes it from list
pbreak = (breaks.pop(0) - running) >> 3
self._oeb.logger.info('pbreak = 0x%X' % pbreak )
encoded = decint(pbreak, DECINT_FORWARD)
record.write(encoded)
running += pbreak << 3
nextra += len(encoded)
lsize = 1
while True:
size = decint(nextra + lsize, DECINT_BACKWARD)
if len(size) == lsize:
break
lsize += 1
# Writing vwi length byte here
record.write(size)
self._records.append(record.getvalue())
buf.append(self._records[-1])
nrecords += 1
offset += RECORD_SIZE
data, overlap = self._read_text_record(text)
if INDEXING:
extra = sum(map(len, buf))%4
if extra == 0:
@ -455,6 +633,7 @@ class MobiWriter(object):
self._text_nrecords = nrecords
def _generate_indxt(self, ctoc):
if self.opts.mobi_periodical:
raise NotImplementedError('Indexing for periodicals not implemented')
toc = self._oeb.toc
@ -471,7 +650,7 @@ class MobiWriter(object):
pos = 0xc0 + indxt.tell()
indices.write(pack('>H', pos))
name = "%4d"%count
name = "%04d"%count
indxt.write(chr(len(name)) + name)
indxt.write(INDXT['chapter'])
indxt.write(decint(offset, DECINT_FORWARD))
@ -502,7 +681,7 @@ class MobiWriter(object):
add_node(child, offset, length, c)
ctoc_offset = self._ctoc_map[child]
last_name = "%4d"%c
last_name = "%04d"%c
c += 1
return align_block(indxt.getvalue()), c, \
@ -510,7 +689,7 @@ class MobiWriter(object):
def _generate_index(self):
self._oeb.log('Generating index...')
self._oeb.log('Generating primary index...')
self._primary_index_record = None
ctoc = self._generate_ctoc()
indxt, indxt_count, indices, last_name = \
@ -519,6 +698,8 @@ class MobiWriter(object):
self._oeb.log.warn('Input document has no TOC. No index generated.')
return
# GR: indx0 => INDX0[0]
# indx1 => INDX1[0]
indx1 = StringIO()
indx1.write('INDX'+pack('>I', 0xc0)) # header length
@ -575,7 +756,9 @@ class MobiWriter(object):
header.write(pack('>I', 1))
# 0x1c - 0x1f : Text encoding ?
header.write(pack('>I', 650001))
# header.write(pack('>I', 650001))
# GR: This needs to be either 0xFDE9 or 0x4E4
header.write(pack('>I', 0xFDE9))
# 0x20 - 0x23 : Language code?
header.write(iana2mobi(str(self._oeb.metadata.language[0])))
@ -615,56 +798,58 @@ class MobiWriter(object):
self._primary_index_record = len(self._records)
self._records.extend([indx0, indx1, ctoc])
# Write secondary index records
tagx = TAGX['secondary_'+\
('periodical' if self.opts.mobi_periodical else 'book')]
tagx_len = 8 + len(tagx)
# Turn this off for now
if False:
# Write secondary index records
tagx = TAGX['secondary_'+\
('periodical' if self.opts.mobi_periodical else 'book')]
tagx_len = 8 + len(tagx)
indx0 = StringIO()
indx0.write('INDX'+pack('>I', 0xc0)+'\0'*8)
indx0.write(pack('>I', 0x02))
indx0.write(pack('>I', 0xc0+tagx_len+4))
indx0.write(pack('>I', 1))
indx0.write(pack('>I', 65001))
indx0.write('\xff'*4)
indx0.write(pack('>I', 1))
indx0.write('\0'*4)
indx0.write('\0'*136)
indx0.write(pack('>I', 0xc0))
indx0.write('\0'*8)
indx0.write('TAGX'+pack('>I', tagx_len)+tagx)
if self.opts.mobi_periodical:
raise NotImplementedError
else:
indx0.write('\0'*3 + '\x01' + 'IDXT' + '\0\xd4\0\0')
indx1 = StringIO()
indx1.write('INDX' + pack('>I', 0xc0) + '\0'*4)
indx1.write(pack('>I', 1))
extra = 0xf0 if self.opts.mobi_periodical else 4
indx1.write('\0'*4 + pack('>I', 0xc0+extra))
num = 4 if self.opts.mobi_periodical else 1
indx1.write(pack('>I', num))
indx1.write('\xff'*8)
indx1.write('\0'*(0xc0-indx1.tell()))
if self.opts.mobi_periodical:
raise NotImplementedError
else:
indx1.write('\0\x01\x80\0')
indx1.write('IDXT')
if self.opts.mobi_periodical:
raise NotImplementedError
else:
indx1.write('\0\xc0\0\0')
indx0 = StringIO()
indx0.write('INDX'+pack('>I', 0xc0)+'\0'*8)
indx0.write(pack('>I', 0x02))
indx0.write(pack('>I', 0xc0+tagx_len+4))
indx0.write(pack('>I', 1))
indx0.write(pack('>I', 65001))
indx0.write('\xff'*4)
indx0.write(pack('>I', 1))
indx0.write('\0'*4)
indx0.write('\0'*136)
indx0.write(pack('>I', 0xc0))
indx0.write('\0'*8)
indx0.write('TAGX'+pack('>I', tagx_len)+tagx)
if self.opts.mobi_periodical:
raise NotImplementedError
else:
indx0.write('\0'*3 + '\x01' + 'IDXT' + '\0\xd4\0\0')
indx1 = StringIO()
indx1.write('INDX' + pack('>I', 0xc0) + '\0'*4)
indx1.write(pack('>I', 1))
extra = 0xf0 if self.opts.mobi_periodical else 4
indx1.write('\0'*4 + pack('>I', 0xc0+extra))
num = 4 if self.opts.mobi_periodical else 1
indx1.write(pack('>I', num))
indx1.write('\xff'*8)
indx1.write('\0'*(0xc0-indx1.tell()))
if self.opts.mobi_periodical:
raise NotImplementedError
else:
indx1.write('\0\x01\x80\0')
indx1.write('IDXT')
if self.opts.mobi_periodical:
raise NotImplementedError
else:
indx1.write('\0\xc0\0\0')
indx0, indx1 = indx0.getvalue(), indx1.getvalue()
self._records.extend((indx0, indx1))
if self.opts.verbose > 3:
from tempfile import mkdtemp
import os
t = mkdtemp()
for i, n in enumerate(['sindx1', 'sindx0', 'ctoc', 'indx0', 'indx1']):
open(os.path.join(t, n+'.bin'), 'wb').write(self._records[-(i+1)])
self._oeb.log.debug('Index records dumped to', t)
indx0, indx1 = indx0.getvalue(), indx1.getvalue()
self._records.extend((indx0, indx1))
if self.opts.verbose > 3:
from tempfile import mkdtemp
import os
t = mkdtemp()
for i, n in enumerate(['sindx1', 'sindx0', 'ctoc', 'indx0', 'indx1']):
open(os.path.join(t, n+'.bin'), 'wb').write(self._records[-(i+1)])
self._oeb.log.debug('Index records dumped to', t)
@ -714,16 +899,22 @@ class MobiWriter(object):
self._first_image_record = len(self._records)-1
def _generate_end_records(self):
self._flis_number = len(self._records)
self._records.append(
'FLIS\0\0\0\x08\0\x41\0\0\0\0\0\0\xff\xff\xff\xff\0\x01\0\x03\0\0\0\x03\0\0\0\x01'+
'\xff'*4)
fcis = 'FCIS\x00\x00\x00\x14\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00'
fcis += pack('>I', self._text_length)
fcis += '\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x08\x00\x01\x00\x01\x00\x00\x00\x00'
self._fcis_number = len(self._records)
self._records.append(fcis)
self._records.append('\xE9\x8E\x0D\x0A')
if True:
self._flis_number = len(self._records)
self._records.append('\xE9\x8E\x0D\x0A')
else:
# This adds the binary blobs of FLIS and FCIS, which don't seem to be necessary
self._flis_number = len(self._records)
self._records.append(
'FLIS\0\0\0\x08\0\x41\0\0\0\0\0\0\xff\xff\xff\xff\0\x01\0\x03\0\0\0\x03\0\0\0\x01'+
'\xff'*4)
fcis = 'FCIS\x00\x00\x00\x14\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00'
fcis += pack('>I', self._text_length)
fcis += '\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x08\x00\x01\x00\x01\x00\x00\x00\x00'
self._fcis_number = len(self._records)
self._records.append(fcis)
self._records.append('\xE9\x8E\x0D\x0A')
def _generate_record0(self):
metadata = self._oeb.metadata
@ -760,9 +951,14 @@ class MobiWriter(object):
# 0x18 - 0x1f : Unknown
record0.write('\xff' * 8)
# 0x20 - 0x23 : Secondary index record
record0.write(pack('>I', 0xffffffff if self._primary_index_record is
None else self._primary_index_record+3))
# Turned off as it seems unnecessary
if True:
record0.write(pack('>I', 0xffffffff))
else:
record0.write(pack('>I', 0xffffffff if self._primary_index_record is
None else self._primary_index_record+3))
# 0x24 - 0x3f : Unknown
record0.write('\xff' * 28)
@ -819,16 +1015,33 @@ class MobiWriter(object):
record0.write('\0\0\0\x01')
# 0xb8 - 0xbb : FCIS record number
record0.write(pack('>I', self._fcis_number))
# Turned off, these are optional and not understood yet
if True:
# 0xb8 - 0xbb : FCIS record number
record0.write(pack('>I', 0xffffffff))
# 0xbc - 0xbf : Unknown (FCIS record count?)
record0.write(pack('>I', 1))
# 0xbc - 0xbf : Unknown (FCIS record count?)
record0.write(pack('>I', 0xffffffff))
# 0xc0 - 0xc3 : FLIS record number
record0.write(pack('>I', self._flis_number))
# 0xc0 - 0xc3 : FLIS record number
record0.write(pack('>I', 0xffffffff))
# 0xc4 - 0xc7 : Unknown (FLIS record count?)
record0.write(pack('>I', 1))
# 0xc4 - 0xc7 : Unknown (FLIS record count?)
record0.write(pack('>I', 1))
else:
# Write these if FCIS/FLIS turned on
# 0xb8 - 0xbb : FCIS record number
record0.write(pack('>I', self._fcis_number))
# 0xbc - 0xbf : Unknown (FCIS record count?)
record0.write(pack('>I', 1))
# 0xc0 - 0xc3 : FLIS record number
record0.write(pack('>I', self._flis_number))
# 0xc4 - 0xc7 : Unknown (FLIS record count?)
record0.write(pack('>I', 1))
# 0xc8 - 0xcf : Unknown
record0.write('\0'*8)
@ -839,9 +1052,14 @@ class MobiWriter(object):
# 0xe0 - 0xe3 : Extra record data
# The '5' is a bitmask of extra record data at the end:
# - 0x1: <extra multibyte bytes><size> (?)
# - 0x2: <indexing description of this HTML record><size> GR
# - 0x4: <uncrossable breaks><size>
# Of course, the formats aren't quite the same.
record0.write(pack('>I', 5))
# GR: Use 2 for indexed files
if INDEXING :
record0.write(pack('>I', 2))
else:
record0.write(pack('>I', 5))
# 0xe4 - 0xe7 : Primary index record
record0.write(pack('>I', 0xffffffff if self._primary_index_record is
@ -891,11 +1109,14 @@ class MobiWriter(object):
if index is not None:
exth.write(pack('>III', 0xca, 0x0c, index - 1))
nrecs += 1
if INDEXING:
# Not sure what these are, but not needed for indexing
if False :
# Write unknown EXTH records as 0s
for code, size in [(204,4), (205,4), (206,4), (207,4), (300,40)]:
exth.write(pack('>II', code, 8+size)+'\0'*size)
nrecs += 1
exth = exth.getvalue()
trail = len(exth) % 4
pad = '\0' * (4 - trail) # Always pad w/ at least 1 byte
@ -935,3 +1156,73 @@ class MobiWriter(object):
self._write(record)
class HTMLRecordData(object):
def __init__(self):
self._continuingNode = -1
self._continuingNodeParent = -1
self._openingNode = -1
self._openingNodeParent = -1
self._currentSectionNodeCount = -1
self._nextSectionNumber = -1
self._nextSectionOpeningNode = -1
self._nextSectionNodeCount = -1
def getContinuingNode(self):
return self._continuingNode
def setContinuingNode(self, value):
self._continuingNode = value
continuingNode = property(getContinuingNode, setContinuingNode, None, None)
def getContinuingNodeParent(self):
return self._continuingNodeParent
def setContinuingNodeParent(self, value):
self._continuingNodeParent = value
continuingNodeParent = property(getContinuingNodeParent, setContinuingNodeParent, None, None)
def getOpeningNode(self):
return self._openingNode
def setOpeningNode(self, value):
self._openingNode = value
openingNode = property(getOpeningNode, setOpeningNode, None, None)
def getOpeningNodeParent(self):
return self._openingNodeParent
def setOpeningNodeParent(self, value):
self._openingNodeParent = value
openingNodeParent = property(getOpeningNodeParent, setOpeningNodeParent, None, None)
def getCurrentSectionNodeCount(self):
return self._currentSectionNodeCount
def setCurrentSectionNodeCount(self, value):
self._currentSectionNodeCount = value
currentSectionNodeCount = property(getCurrentSectionNodeCount, setCurrentSectionNodeCount, None, None)
def getNextSectionNumber(self):
return self._nextSectionNumber
def setNextSectionNumber(self, value):
self._nextSectionNumber = value
nextSectionNumber = property(getNextSectionNumber, setNextSectionNumber, None, None)
def getNextSectionOpeningNode(self):
return self._nextSectionOpeningNode
def setNextSectionOpeningNode(self, value):
self._nextSectionOpeningNode = value
nextSectionOpeningNode = property(getNextSectionOpeningNode, setNextSectionOpeningNode, None, None)
def getNextSectionNodeCount(self):
return self._nextSectionNodeCount
def setNextSectionNodeCount(self, value):
self._nextSectionNodeCount = value
nextSectionNodeCount = property(getNextSectionNodeCount, setNextSectionNodeCount, None, None)
def dumpData(self, recordNumber, oeb):
oeb.logger.info( "--- Summary of HTML Record 0x%x [%d] indexing ---" % (recordNumber, recordNumber) )
oeb.logger.info( " continuingNode: %03d" % self.continuingNode )
oeb.logger.info( " continuingNodeParent: %03d" % self.continuingNodeParent )
oeb.logger.info( " openingNode: %03d" % self.openingNode )
oeb.logger.info( " openingNodeParent: %03d" % self.openingNodeParent )
oeb.logger.info( " currentSectionNodeCount: %03d" % self.currentSectionNodeCount )
oeb.logger.info( " nextSectionNumber: %03d" % self.nextSectionNumber )
oeb.logger.info( " nextSectionOpeningNode: %03d" % self.nextSectionOpeningNode )
oeb.logger.info( " nextSectionNodeCount: %03d" % self.nextSectionNodeCount )

View File

@ -279,7 +279,7 @@ class Stylizer(object):
class Style(object):
UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|px|mm|cm|in|pt|pc)$')
UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|ex|en|px|mm|cm|in|pt|pc)$')
def __init__(self, element, stylizer):
self._element = element
@ -362,6 +362,11 @@ class Style(object):
elif unit == 'em':
font = font or self.fontSize
result = value * font
elif unit in ('ex', 'en'):
# This is a hack for ex since we have no way to know
# the x-height of the font
font = font or self.fontSize
result = value * font * 0.5
elif unit == 'pc':
result = value * 12.0
elif unit == 'mm':

View File

@ -23,7 +23,6 @@ entry_points = {
'calibre-server = calibre.library.server:main',
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
'isbndb = calibre.ebooks.metadata.isbndb:main',
'librarything = calibre.ebooks.metadata.library_thing:main',
'calibre-debug = calibre.debug:main',
'calibredb = calibre.library.cli:main',

View File

@ -11,12 +11,12 @@ from math import ceil
from threading import Thread, RLock
from Queue import Queue, Empty
from multiprocessing.connection import Listener
from multiprocessing import cpu_count
from collections import deque
from binascii import hexlify
from calibre.utils.ipc.launch import Worker
from calibre.utils.ipc.worker import PARALLEL_FUNCS
from calibre import detect_ncpus as cpu_count
_counter = 0