Fix serialization of floating point numbers

This commit is contained in:
Kovid Goyal 2012-12-30 00:22:23 +05:30
parent 40539ca292
commit 836a623b5f
3 changed files with 33 additions and 9 deletions

View File

@ -10,6 +10,7 @@ __docformat__ = 'restructuredtext en'
import codecs, zlib import codecs, zlib
from io import BytesIO from io import BytesIO
from struct import pack from struct import pack
from decimal import Decimal
EOL = b'\n' EOL = b'\n'
@ -51,13 +52,30 @@ PAPER_SIZES = {k:globals()[k.upper()] for k in ('a0 a1 a2 a3 a4 a5 a6 b0 b1 b2'
# Basic PDF datatypes {{{ # Basic PDF datatypes {{{
def format_float(f):
if abs(f) < 1e-7:
return '0'
places = 6
a, b = type(u'')(Decimal(f).quantize(Decimal(10)**-places)).partition('.')[0::2]
b = b.rstrip('0')
if not b:
return '0' if a == '-0' else a
return '%s.%s'%(a, b)
def fmtnum(o):
if isinstance(o, (int, long)):
return type(u'')(o)
return format_float(o)
def serialize(o, stream): def serialize(o, stream):
if hasattr(o, 'pdf_serialize'): if hasattr(o, 'pdf_serialize'):
o.pdf_serialize(stream) o.pdf_serialize(stream)
elif isinstance(o, bool): elif isinstance(o, bool):
stream.write(b'true' if o else b'false') stream.write(b'true' if o else b'false')
elif isinstance(o, (int, long, float)): elif isinstance(o, (int, long)):
stream.write(type(u'')(o).encode('ascii')) stream.write(type(u'')(o).encode('ascii'))
elif isinstance(o, float):
stream.write(format_float(o).encode('ascii'))
elif o is None: elif o is None:
stream.write(b'null') stream.write(b'null')
else: else:

View File

@ -10,6 +10,7 @@ __docformat__ = 'restructuredtext en'
import sys, traceback import sys, traceback
from collections import namedtuple from collections import namedtuple
from functools import wraps, partial from functools import wraps, partial
from future_builtins import map
import sip import sip
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter, from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
@ -18,13 +19,17 @@ from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
from calibre.constants import plugins from calibre.constants import plugins
from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path) from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path)
from calibre.ebooks.pdf.render.common import inch, A4 from calibre.ebooks.pdf.render.common import inch, A4, fmtnum
from calibre.utils.fonts.sfnt.container import Sfnt from calibre.utils.fonts.sfnt.container import Sfnt
from calibre.utils.fonts.sfnt.metrics import FontMetrics from calibre.utils.fonts.sfnt.metrics import FontMetrics
Point = namedtuple('Point', 'x y') Point = namedtuple('Point', 'x y')
ColorState = namedtuple('ColorState', 'color opacity do') ColorState = namedtuple('ColorState', 'color opacity do')
def repr_transform(t):
vals = map(fmtnum, (t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy()))
return '[%s]'%' '.join(vals)
def store_error(func): def store_error(func):
@wraps(func) @wraps(func)

View File

@ -15,7 +15,7 @@ from collections import namedtuple
from calibre.constants import (__appname__, __version__) from calibre.constants import (__appname__, __version__)
from calibre.ebooks.pdf.render.common import ( from calibre.ebooks.pdf.render.common import (
Reference, EOL, serialize, Stream, Dictionary, String, Name, Array, Reference, EOL, serialize, Stream, Dictionary, String, Name, Array,
GlyphIndex) GlyphIndex, fmtnum)
from calibre.ebooks.pdf.render.fonts import FontManager from calibre.ebooks.pdf.render.fonts import FontManager
from calibre.ebooks.pdf.render.links import Links from calibre.ebooks.pdf.render.links import Links
@ -180,7 +180,7 @@ class Text(object):
stream.write_line('BT ') stream.write_line('BT ')
serialize(Name(font_name), stream) serialize(Name(font_name), stream)
stream.write(' %g Tf '%self.size) stream.write(' %g Tf '%self.size)
stream.write(' '.join(map(type(u''), self.transform)) + ' Tm ') stream.write(' '.join(map(fmtnum, self.transform)) + ' Tm ')
if self.horizontal_scale != self.default_horizontal_scale: if self.horizontal_scale != self.default_horizontal_scale:
stream.write('%g Tz '%self.horizontal_scale) stream.write('%g Tz '%self.horizontal_scale)
if self.word_spacing != self.default_word_spacing: if self.word_spacing != self.default_word_spacing:
@ -331,7 +331,7 @@ class PDFStream(object):
vals = [m.m11(), m.m12(), m.m21(), m.m22(), m.dx(), m.dy()] vals = [m.m11(), m.m12(), m.m21(), m.m22(), m.dx(), m.dy()]
else: else:
vals = args vals = args
cm = ' '.join(map(type(u''), vals)) cm = ' '.join(map(fmtnum, vals))
self.current_page.write_line(cm + ' cm') self.current_page.write_line(cm + ' cm')
def set_rgb_colorspace(self): def set_rgb_colorspace(self):
@ -355,7 +355,8 @@ class PDFStream(object):
if i != 0: if i != 0:
self.current_page.write_line() self.current_page.write_line()
for x in op: for x in op:
self.current_page.write(type(u'')(x) + ' ') self.current_page.write(
(fmtnum(x) if isinstance(x, (int, long, float)) else x) + ' ')
def draw_path(self, path, stroke=True, fill=False, fill_rule='winding'): def draw_path(self, path, stroke=True, fill=False, fill_rule='winding'):
if not path.ops: return if not path.ops: return
@ -394,7 +395,7 @@ class PDFStream(object):
op = Dictionary({'Type':Name('ExtGState'), 'CA': opacity}) op = Dictionary({'Type':Name('ExtGState'), 'CA': opacity})
self.stroke_opacities[opacity] = self.objects.add(op) self.stroke_opacities[opacity] = self.objects.add(op)
self.current_page.set_opacity(self.stroke_opacities[opacity]) self.current_page.set_opacity(self.stroke_opacities[opacity])
self.current_page.write_line(' '.join(map(type(u''), color[:3])) + ' SC') self.current_page.write_line(' '.join(map(fmtnum, color[:3])) + ' SC')
def set_fill_color(self, color): def set_fill_color(self, color):
opacity = color.opacity opacity = color.opacity
@ -402,7 +403,7 @@ class PDFStream(object):
op = Dictionary({'Type':Name('ExtGState'), 'ca': opacity}) op = Dictionary({'Type':Name('ExtGState'), 'ca': opacity})
self.fill_opacities[opacity] = self.objects.add(op) self.fill_opacities[opacity] = self.objects.add(op)
self.current_page.set_opacity(self.fill_opacities[opacity]) self.current_page.set_opacity(self.fill_opacities[opacity])
self.current_page.write_line(' '.join(map(type(u''), color[:3])) + ' sc') self.current_page.write_line(' '.join(map(fmtnum, color[:3])) + ' sc')
def end_page(self): def end_page(self):
pageref = self.current_page.end(self.objects, self.stream) pageref = self.current_page.end(self.objects, self.stream)
@ -424,7 +425,7 @@ class PDFStream(object):
self.current_page.write(b'BT ') self.current_page.write(b'BT ')
serialize(Name(name), self.current_page) serialize(Name(name), self.current_page)
self.current_page.write(' %g Tf '%size) self.current_page.write(' %g Tf '%size)
self.current_page.write('%s Tm '%' '.join(map(type(u''), transform))) self.current_page.write('%s Tm '%' '.join(map(fmtnum, transform)))
for x, y, glyph_id in glyphs: for x, y, glyph_id in glyphs:
self.current_page.write('%g %g Td '%(x, y)) self.current_page.write('%g %g Td '%(x, y))
serialize(GlyphIndex(glyph_id), self.current_page) serialize(GlyphIndex(glyph_id), self.current_page)