mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
New PDF engine, still incomplete (outlines and links have to be added, lots of testing needed)
This commit is contained in:
parent
955ace2687
commit
ccc6449df3
@ -14,50 +14,32 @@ import os
|
|||||||
from calibre.customize.conversion import OutputFormatPlugin, \
|
from calibre.customize.conversion import OutputFormatPlugin, \
|
||||||
OptionRecommendation
|
OptionRecommendation
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.constants import iswindows
|
|
||||||
|
|
||||||
UNITS = [
|
UNITS = ['millimeter', 'centimeter', 'point', 'inch' , 'pica' , 'didot',
|
||||||
'millimeter',
|
'cicero', 'devicepixel']
|
||||||
'point',
|
|
||||||
'inch' ,
|
|
||||||
'pica' ,
|
|
||||||
'didot',
|
|
||||||
'cicero',
|
|
||||||
'devicepixel',
|
|
||||||
]
|
|
||||||
|
|
||||||
PAPER_SIZES = ['b2',
|
PAPER_SIZES = ['b2', 'b4', 'b5', 'b6', 'b0', 'b1', 'letter', 'b3', 'a3', 'a1',
|
||||||
'a9',
|
'a0', 'legal', 'a6', 'a2', 'a5', 'a4']
|
||||||
'executive',
|
|
||||||
'tabloid',
|
|
||||||
'b4',
|
|
||||||
'b5',
|
|
||||||
'b6',
|
|
||||||
'b7',
|
|
||||||
'b0',
|
|
||||||
'b1',
|
|
||||||
'letter',
|
|
||||||
'b3',
|
|
||||||
'a7',
|
|
||||||
'a8',
|
|
||||||
'b8',
|
|
||||||
'b9',
|
|
||||||
'a3',
|
|
||||||
'a1',
|
|
||||||
'folio',
|
|
||||||
'c5e',
|
|
||||||
'dle',
|
|
||||||
'a0',
|
|
||||||
'ledger',
|
|
||||||
'legal',
|
|
||||||
'a6',
|
|
||||||
'a2',
|
|
||||||
'b10',
|
|
||||||
'a5',
|
|
||||||
'comm10e',
|
|
||||||
'a4']
|
|
||||||
|
|
||||||
ORIENTATIONS = ['portrait', 'landscape']
|
class PDFMetadata(object): # {{{
|
||||||
|
def __init__(self, oeb_metadata=None):
|
||||||
|
from calibre import force_unicode
|
||||||
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
|
self.title = _(u'Unknown')
|
||||||
|
self.author = _(u'Unknown')
|
||||||
|
self.tags = u''
|
||||||
|
|
||||||
|
if oeb_metadata != None:
|
||||||
|
if len(oeb_metadata.title) >= 1:
|
||||||
|
self.title = oeb_metadata.title[0].value
|
||||||
|
if len(oeb_metadata.creator) >= 1:
|
||||||
|
self.author = authors_to_string([x.value for x in oeb_metadata.creator])
|
||||||
|
if oeb_metadata.subject:
|
||||||
|
self.tags = u', '.join(map(unicode, oeb_metadata.subject))
|
||||||
|
|
||||||
|
self.title = force_unicode(self.title)
|
||||||
|
self.author = force_unicode(self.author)
|
||||||
|
# }}}
|
||||||
|
|
||||||
class PDFOutput(OutputFormatPlugin):
|
class PDFOutput(OutputFormatPlugin):
|
||||||
|
|
||||||
@ -66,9 +48,14 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
file_type = 'pdf'
|
file_type = 'pdf'
|
||||||
|
|
||||||
options = set([
|
options = set([
|
||||||
|
OptionRecommendation(name='override_profile_size', recommended_value=False,
|
||||||
|
help=_('Normally, the PDF page size is set by the output profile'
|
||||||
|
' chosen under page options. This option will cause the '
|
||||||
|
' page size settings under PDF Output to override the '
|
||||||
|
' size specified by the output profile.')),
|
||||||
OptionRecommendation(name='unit', recommended_value='inch',
|
OptionRecommendation(name='unit', recommended_value='inch',
|
||||||
level=OptionRecommendation.LOW, short_switch='u', choices=UNITS,
|
level=OptionRecommendation.LOW, short_switch='u', choices=UNITS,
|
||||||
help=_('The unit of measure. Default is inch. Choices '
|
help=_('The unit of measure for page sizes. Default is inch. Choices '
|
||||||
'are %s '
|
'are %s '
|
||||||
'Note: This does not override the unit for margins!') % UNITS),
|
'Note: This does not override the unit for margins!') % UNITS),
|
||||||
OptionRecommendation(name='paper_size', recommended_value='letter',
|
OptionRecommendation(name='paper_size', recommended_value='letter',
|
||||||
@ -80,10 +67,6 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
help=_('Custom size of the document. Use the form widthxheight '
|
help=_('Custom size of the document. Use the form widthxheight '
|
||||||
'EG. `123x321` to specify the width and height. '
|
'EG. `123x321` to specify the width and height. '
|
||||||
'This overrides any specified paper-size.')),
|
'This overrides any specified paper-size.')),
|
||||||
OptionRecommendation(name='orientation', recommended_value='portrait',
|
|
||||||
level=OptionRecommendation.LOW, choices=ORIENTATIONS,
|
|
||||||
help=_('The orientation of the page. Default is portrait. Choices '
|
|
||||||
'are %s') % ORIENTATIONS),
|
|
||||||
OptionRecommendation(name='preserve_cover_aspect_ratio',
|
OptionRecommendation(name='preserve_cover_aspect_ratio',
|
||||||
recommended_value=False,
|
recommended_value=False,
|
||||||
help=_('Preserve the aspect ratio of the cover, instead'
|
help=_('Preserve the aspect ratio of the cover, instead'
|
||||||
@ -108,6 +91,11 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
OptionRecommendation(name='pdf_mono_font_size',
|
OptionRecommendation(name='pdf_mono_font_size',
|
||||||
recommended_value=16, help=_(
|
recommended_value=16, help=_(
|
||||||
'The default font size for monospaced text')),
|
'The default font size for monospaced text')),
|
||||||
|
OptionRecommendation(name='uncompressed_pdf',
|
||||||
|
recommended_value=False, help=_(
|
||||||
|
'Generate an uncompressed PDF (useful for debugging)')),
|
||||||
|
OptionRecommendation(name='old_pdf_engine', recommended_value=False,
|
||||||
|
help=_('Use the old, less capable engine to generate the PDF')),
|
||||||
])
|
])
|
||||||
|
|
||||||
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
||||||
@ -200,32 +188,17 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
if k in family_map:
|
if k in family_map:
|
||||||
val[i].value = family_map[k]
|
val[i].value = family_map[k]
|
||||||
|
|
||||||
def remove_font_specification(self):
|
|
||||||
# Qt produces image based pdfs on windows when non-generic fonts are specified
|
|
||||||
# This might change in Qt WebKit 2.3+ you will have to test.
|
|
||||||
for item in self.oeb.manifest:
|
|
||||||
if not hasattr(item.data, 'cssRules'): continue
|
|
||||||
for i, rule in enumerate(item.data.cssRules):
|
|
||||||
if rule.type != rule.STYLE_RULE: continue
|
|
||||||
ff = rule.style.getProperty('font-family')
|
|
||||||
if ff is None: continue
|
|
||||||
val = ff.propertyValue
|
|
||||||
for i in xrange(val.length):
|
|
||||||
k = icu_lower(val[i].value)
|
|
||||||
if k not in {'serif', 'sans', 'sans-serif', 'sansserif',
|
|
||||||
'monospace', 'cursive', 'fantasy'}:
|
|
||||||
val[i].value = ''
|
|
||||||
|
|
||||||
def convert_text(self, oeb_book):
|
def convert_text(self, oeb_book):
|
||||||
|
if self.opts.old_pdf_engine:
|
||||||
from calibre.ebooks.pdf.writer import PDFWriter
|
from calibre.ebooks.pdf.writer import PDFWriter
|
||||||
|
PDFWriter
|
||||||
|
else:
|
||||||
|
from calibre.ebooks.pdf.render.from_html import PDFWriter
|
||||||
from calibre.ebooks.metadata.opf2 import OPF
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
|
|
||||||
self.log.debug('Serializing oeb input to disk for processing...')
|
self.log.debug('Serializing oeb input to disk for processing...')
|
||||||
self.get_cover_data()
|
self.get_cover_data()
|
||||||
|
|
||||||
if iswindows:
|
|
||||||
self.remove_font_specification()
|
|
||||||
else:
|
|
||||||
self.handle_embedded_fonts()
|
self.handle_embedded_fonts()
|
||||||
|
|
||||||
with TemporaryDirectory('_pdf_out') as oeb_dir:
|
with TemporaryDirectory('_pdf_out') as oeb_dir:
|
||||||
@ -240,7 +213,6 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
'toc', None))
|
'toc', None))
|
||||||
|
|
||||||
def write(self, Writer, items, toc):
|
def write(self, Writer, items, toc):
|
||||||
from calibre.ebooks.pdf.writer import PDFMetadata
|
|
||||||
writer = Writer(self.opts, self.log, cover_data=self.cover_data,
|
writer = Writer(self.opts, self.log, cover_data=self.cover_data,
|
||||||
toc=toc)
|
toc=toc)
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ inch = 72.0
|
|||||||
cm = inch / 2.54
|
cm = inch / 2.54
|
||||||
mm = cm * 0.1
|
mm = cm * 0.1
|
||||||
pica = 12.0
|
pica = 12.0
|
||||||
|
didot = 0.375 * mm
|
||||||
|
cicero = 12 * didot
|
||||||
|
|
||||||
_W, _H = (21*cm, 29.7*cm)
|
_W, _H = (21*cm, 29.7*cm)
|
||||||
|
|
||||||
@ -41,6 +43,10 @@ B3 = (_BH*2, _BW)
|
|||||||
B2 = (_BW*2, _BH*2)
|
B2 = (_BW*2, _BH*2)
|
||||||
B1 = (_BH*4, _BW*2)
|
B1 = (_BH*4, _BW*2)
|
||||||
B0 = (_BW*4, _BH*4)
|
B0 = (_BW*4, _BH*4)
|
||||||
|
|
||||||
|
PAPER_SIZES = {k:globals()[k.upper()] for k in ('a0 a1 a2 a3 a4 a5 a6 b0 b1 b2'
|
||||||
|
' b3 b4 b5 b6 letter legal').split()}
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Basic PDF datatypes {{{
|
# Basic PDF datatypes {{{
|
||||||
@ -79,19 +85,12 @@ class String(unicode):
|
|||||||
raw = codecs.BOM_UTF16_BE + s.encode('utf-16-be')
|
raw = codecs.BOM_UTF16_BE + s.encode('utf-16-be')
|
||||||
stream.write(b'('+raw+b')')
|
stream.write(b'('+raw+b')')
|
||||||
|
|
||||||
class GlyphIndex(object):
|
class GlyphIndex(int):
|
||||||
|
|
||||||
def __init__(self, code, compress):
|
|
||||||
self.code = code
|
|
||||||
self.compress = compress
|
|
||||||
|
|
||||||
def pdf_serialize(self, stream):
|
def pdf_serialize(self, stream):
|
||||||
if self.compress:
|
byts = bytearray(pack(b'>H', self))
|
||||||
stream.write(pack(b'>sHs', b'(', self.code, b')'))
|
|
||||||
else:
|
|
||||||
byts = bytearray(pack(b'>H', self.code))
|
|
||||||
stream.write('<%s>'%''.join(map(
|
stream.write('<%s>'%''.join(map(
|
||||||
lambda x: bytes(hex(int(x))[2:]).rjust(2, b'0'), byts)))
|
lambda x: bytes(hex(x)[2:]).rjust(2, b'0'), byts)))
|
||||||
|
|
||||||
class Dictionary(dict):
|
class Dictionary(dict):
|
||||||
|
|
||||||
|
@ -14,17 +14,13 @@ from functools import wraps
|
|||||||
|
|
||||||
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
|
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
|
||||||
QTransform, QPainterPath, QTextOption, QTextLayout,
|
QTransform, QPainterPath, QTextOption, QTextLayout,
|
||||||
QImage, QByteArray, QBuffer, qRgba)
|
QImage, QByteArray, QBuffer, qRgba, QRectF)
|
||||||
|
|
||||||
from calibre.constants import DEBUG
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
XDPI = 1200
|
|
||||||
YDPI = 1200
|
|
||||||
|
|
||||||
Point = namedtuple('Point', 'x y')
|
Point = namedtuple('Point', 'x y')
|
||||||
ColorState = namedtuple('ColorState', 'color opacity do')
|
ColorState = namedtuple('ColorState', 'color opacity do')
|
||||||
|
|
||||||
@ -35,7 +31,8 @@ def store_error(func):
|
|||||||
try:
|
try:
|
||||||
func(self, *args, **kwargs)
|
func(self, *args, **kwargs)
|
||||||
except:
|
except:
|
||||||
self.errors.append(traceback.format_exc())
|
self.errors_occurred = True
|
||||||
|
self.errors(traceback.format_exc())
|
||||||
|
|
||||||
return errh
|
return errh
|
||||||
|
|
||||||
@ -115,7 +112,7 @@ class GraphicsState(object): # {{{
|
|||||||
elif flags & QPaintEngine.DirtyClipRegion:
|
elif flags & QPaintEngine.DirtyClipRegion:
|
||||||
path = QPainterPath()
|
path = QPainterPath()
|
||||||
for rect in state.clipRegion().rects():
|
for rect in state.clipRegion().rects():
|
||||||
path.addRect(rect)
|
path.addRect(QRectF(rect))
|
||||||
self.ops['clip'] = (state.clipOperation(), path)
|
self.ops['clip'] = (state.clipOperation(), path)
|
||||||
|
|
||||||
def __call__(self, engine):
|
def __call__(self, engine):
|
||||||
@ -215,9 +212,11 @@ class Font(FontMetrics):
|
|||||||
class PdfEngine(QPaintEngine):
|
class PdfEngine(QPaintEngine):
|
||||||
|
|
||||||
def __init__(self, file_object, page_width, page_height, left_margin,
|
def __init__(self, file_object, page_width, page_height, left_margin,
|
||||||
top_margin, right_margin, bottom_margin, width, height):
|
top_margin, right_margin, bottom_margin, width, height,
|
||||||
|
errors=print, debug=print, compress=True):
|
||||||
QPaintEngine.__init__(self, self.features)
|
QPaintEngine.__init__(self, self.features)
|
||||||
self.file_object = file_object
|
self.file_object = file_object
|
||||||
|
self.compress = compress
|
||||||
self.page_height, self.page_width = page_height, page_width
|
self.page_height, self.page_width = page_height, page_width
|
||||||
self.left_margin, self.top_margin = left_margin, top_margin
|
self.left_margin, self.top_margin = left_margin, top_margin
|
||||||
self.right_margin, self.bottom_margin = right_margin, bottom_margin
|
self.right_margin, self.bottom_margin = right_margin, bottom_margin
|
||||||
@ -242,18 +241,20 @@ class PdfEngine(QPaintEngine):
|
|||||||
self.scale = sqrt(sy**2 + sx**2)
|
self.scale = sqrt(sy**2 + sx**2)
|
||||||
self.xscale, self.yscale = sx, sy
|
self.xscale, self.yscale = sx, sy
|
||||||
self.graphics_state = GraphicsState()
|
self.graphics_state = GraphicsState()
|
||||||
self.errors, self.debug = [], []
|
self.errors_occurred = False
|
||||||
|
self.errors, self.debug = errors, debug
|
||||||
self.text_option = QTextOption()
|
self.text_option = QTextOption()
|
||||||
self.text_option.setWrapMode(QTextOption.NoWrap)
|
self.text_option.setWrapMode(QTextOption.NoWrap)
|
||||||
self.fonts = {}
|
self.fonts = {}
|
||||||
i = QImage(1, 1, QImage.Format_ARGB32)
|
i = QImage(1, 1, QImage.Format_ARGB32)
|
||||||
i.fill(qRgba(0, 0, 0, 255))
|
i.fill(qRgba(0, 0, 0, 255))
|
||||||
self.alpha_bit = i.constBits().asstring(4).find(b'\xff')
|
self.alpha_bit = i.constBits().asstring(4).find(b'\xff')
|
||||||
|
self.current_page_num = 1
|
||||||
|
|
||||||
def init_page(self):
|
def init_page(self):
|
||||||
self.pdf.transform(self.pdf_system)
|
self.pdf.transform(self.pdf_system)
|
||||||
self.pdf.set_rgb_colorspace()
|
self.pdf.set_rgb_colorspace()
|
||||||
width = self.painter.pen().widthF() if self.isActive() else 0
|
width = self.painter().pen().widthF() if self.isActive() else 0
|
||||||
self.pdf.set_line_width(width)
|
self.pdf.set_line_width(width)
|
||||||
self.do_stroke = True
|
self.do_stroke = True
|
||||||
self.do_fill = False
|
self.do_fill = False
|
||||||
@ -271,7 +272,7 @@ class PdfEngine(QPaintEngine):
|
|||||||
try:
|
try:
|
||||||
self.pdf = PDFStream(self.file_object, (self.page_width,
|
self.pdf = PDFStream(self.file_object, (self.page_width,
|
||||||
self.page_height),
|
self.page_height),
|
||||||
compress=not DEBUG)
|
compress=self.compress)
|
||||||
self.init_page()
|
self.init_page()
|
||||||
except:
|
except:
|
||||||
self.errors.append(traceback.format_exc())
|
self.errors.append(traceback.format_exc())
|
||||||
@ -281,6 +282,7 @@ class PdfEngine(QPaintEngine):
|
|||||||
def end_page(self, start_new=True):
|
def end_page(self, start_new=True):
|
||||||
self.pdf.restore_stack()
|
self.pdf.restore_stack()
|
||||||
self.pdf.end_page()
|
self.pdf.end_page()
|
||||||
|
self.current_page_num += 1
|
||||||
if start_new:
|
if start_new:
|
||||||
self.init_page()
|
self.init_page()
|
||||||
|
|
||||||
@ -488,7 +490,7 @@ class PdfEngine(QPaintEngine):
|
|||||||
glyph_map[g[0]] = string
|
glyph_map[g[0]] = string
|
||||||
break
|
break
|
||||||
if not found:
|
if not found:
|
||||||
self.debug.append(
|
self.debug(
|
||||||
'Failed to find glyph->unicode mapping for text: %s'%text)
|
'Failed to find glyph->unicode mapping for text: %s'%text)
|
||||||
break
|
break
|
||||||
ipos += 1
|
ipos += 1
|
||||||
@ -546,6 +548,9 @@ class PdfEngine(QPaintEngine):
|
|||||||
self.pdf.draw_path(p, stroke=True, fill_rule=fill_rule,
|
self.pdf.draw_path(p, stroke=True, fill_rule=fill_rule,
|
||||||
fill=(mode in (self.OddEvenMode, self.WindingMode, self.ConvexMode)))
|
fill=(mode in (self.OddEvenMode, self.WindingMode, self.ConvexMode)))
|
||||||
|
|
||||||
|
def set_metadata(self, *args, **kwargs):
|
||||||
|
self.pdf.set_metadata(*args, **kwargs)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.pdf.save_stack()
|
self.pdf.save_stack()
|
||||||
self.saved_ps = (self.do_stroke, self.do_fill)
|
self.saved_ps = (self.do_stroke, self.do_fill)
|
||||||
@ -558,23 +563,26 @@ class PdfDevice(QPaintDevice): # {{{
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, file_object, page_size=A4, left_margin=inch,
|
def __init__(self, file_object, page_size=A4, left_margin=inch,
|
||||||
top_margin=inch, right_margin=inch, bottom_margin=inch):
|
top_margin=inch, right_margin=inch, bottom_margin=inch,
|
||||||
|
xdpi=1200, ydpi=1200, errors=print, debug=print, compress=True):
|
||||||
QPaintDevice.__init__(self)
|
QPaintDevice.__init__(self)
|
||||||
|
self.xdpi, self.ydpi = xdpi, ydpi
|
||||||
self.page_width, self.page_height = page_size
|
self.page_width, self.page_height = page_size
|
||||||
self.body_width = self.page_width - left_margin - right_margin
|
self.body_width = self.page_width - left_margin - right_margin
|
||||||
self.body_height = self.page_height - top_margin - bottom_margin
|
self.body_height = self.page_height - top_margin - bottom_margin
|
||||||
self.engine = PdfEngine(file_object, self.page_width, self.page_height,
|
self.engine = PdfEngine(file_object, self.page_width, self.page_height,
|
||||||
left_margin, top_margin, right_margin,
|
left_margin, top_margin, right_margin,
|
||||||
bottom_margin, self.width(), self.height())
|
bottom_margin, self.width(), self.height(),
|
||||||
|
errors=errors, debug=debug, compress=compress)
|
||||||
|
|
||||||
def paintEngine(self):
|
def paintEngine(self):
|
||||||
return self.engine
|
return self.engine
|
||||||
|
|
||||||
def metric(self, m):
|
def metric(self, m):
|
||||||
if m in (self.PdmDpiX, self.PdmPhysicalDpiX):
|
if m in (self.PdmDpiX, self.PdmPhysicalDpiX):
|
||||||
return XDPI
|
return self.xdpi
|
||||||
if m in (self.PdmDpiY, self.PdmPhysicalDpiY):
|
if m in (self.PdmDpiY, self.PdmPhysicalDpiY):
|
||||||
return YDPI
|
return self.ydpi
|
||||||
if m == self.PdmDepth:
|
if m == self.PdmDepth:
|
||||||
return 32
|
return 32
|
||||||
if m == self.PdmNumColors:
|
if m == self.PdmNumColors:
|
||||||
@ -584,10 +592,32 @@ class PdfDevice(QPaintDevice): # {{{
|
|||||||
if m == self.PdmHeightMM:
|
if m == self.PdmHeightMM:
|
||||||
return int(round(self.body_height * 0.35277777777778))
|
return int(round(self.body_height * 0.35277777777778))
|
||||||
if m == self.PdmWidth:
|
if m == self.PdmWidth:
|
||||||
return int(round(self.body_width * XDPI / 72.0))
|
return int(round(self.body_width * self.xdpi / 72.0))
|
||||||
if m == self.PdmHeight:
|
if m == self.PdmHeight:
|
||||||
return int(round(self.body_height * YDPI / 72.0))
|
return int(round(self.body_height * self.ydpi / 72.0))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def end_page(self, start_new=True):
|
||||||
|
self.engine.end_page(start_new=start_new)
|
||||||
|
|
||||||
|
def init_page(self):
|
||||||
|
self.engine.init_page()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_page_num(self):
|
||||||
|
return self.engine.current_page_num
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors_occurred(self):
|
||||||
|
return self.engine.errors_occurred
|
||||||
|
|
||||||
|
def to_px(self, pt, vertical=True):
|
||||||
|
return pt * (self.height()/self.page_height if vertical else
|
||||||
|
self.width()/self.page_width)
|
||||||
|
|
||||||
|
def set_metadata(self, *args, **kwargs):
|
||||||
|
self.engine.set_metadata(*args, **kwargs)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@ -596,7 +626,7 @@ if __name__ == '__main__':
|
|||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
p = QPainter()
|
p = QPainter()
|
||||||
with open('/tmp/painter.pdf', 'wb') as f:
|
with open('/tmp/painter.pdf', 'wb') as f:
|
||||||
dev = PdfDevice(f)
|
dev = PdfDevice(f, compress=False)
|
||||||
p.begin(dev)
|
p.begin(dev)
|
||||||
xmax, ymax = p.viewport().width(), p.viewport().height()
|
xmax, ymax = p.viewport().width(), p.viewport().height()
|
||||||
try:
|
try:
|
||||||
@ -642,9 +672,6 @@ if __name__ == '__main__':
|
|||||||
# p.drawText(QPoint(100, 300), 'Some text ū --- Д AV ff ff')
|
# p.drawText(QPoint(100, 300), 'Some text ū --- Д AV ff ff')
|
||||||
finally:
|
finally:
|
||||||
p.end()
|
p.end()
|
||||||
for line in dev.engine.debug:
|
if dev.engine.errors_occurred:
|
||||||
print (line)
|
|
||||||
if dev.engine.errors:
|
|
||||||
for err in dev.engine.errors: print (err)
|
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
284
src/calibre/ebooks/pdf/render/from_html.py
Normal file
284
src/calibre/ebooks/pdf/render/from_html.py
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import json
|
||||||
|
from future_builtins import map
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QObject, QPainter, Qt, QSize, QString, QTimer,
|
||||||
|
pyqtProperty, QEventLoop, QPixmap, QRect)
|
||||||
|
from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings
|
||||||
|
|
||||||
|
from calibre import fit_image
|
||||||
|
from calibre.ebooks.oeb.display.webview import load_html
|
||||||
|
from calibre.ebooks.pdf.render.engine import PdfDevice
|
||||||
|
from calibre.ebooks.pdf.render.common import (inch, cm, mm, pica, cicero,
|
||||||
|
didot, PAPER_SIZES)
|
||||||
|
from calibre.ebooks.pdf.outline_writer import Outline
|
||||||
|
|
||||||
|
def get_page_size(opts, for_comic=False): # {{{
|
||||||
|
use_profile = not (opts.override_profile_size or
|
||||||
|
opts.output_profile.short_name == 'default')
|
||||||
|
if use_profile:
|
||||||
|
w = (opts.output_profile.comic_screen_size[0] if for_comic else
|
||||||
|
opts.output_profile.width)
|
||||||
|
h = (opts.output_profile.comic_screen_size[1] if for_comic else
|
||||||
|
opts.output_profile.height)
|
||||||
|
dpi = opts.output_profile.dpi
|
||||||
|
factor = 72.0 / dpi
|
||||||
|
page_size = (factor * w, factor * h)
|
||||||
|
else:
|
||||||
|
page_size = None
|
||||||
|
if opts.custom_size != None:
|
||||||
|
width, sep, height = opts.custom_size.partition('x')
|
||||||
|
if height:
|
||||||
|
try:
|
||||||
|
width = float(width)
|
||||||
|
height = float(height)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if opts.unit == 'devicepixel':
|
||||||
|
factor = 72.0 / opts.output_profile.dpi
|
||||||
|
else:
|
||||||
|
{'point':1.0, 'inch':inch, 'cicero':cicero,
|
||||||
|
'didot':didot, 'pica':pica, 'millimeter':mm,
|
||||||
|
'centimeter':cm}[opts.unit]
|
||||||
|
page_size = (factor*width, factor*height)
|
||||||
|
if page_size is None:
|
||||||
|
page_size = PAPER_SIZES[opts.paper_size]
|
||||||
|
return page_size
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class Page(QWebPage): # {{{
|
||||||
|
|
||||||
|
def __init__(self, opts, log):
|
||||||
|
self.log = log
|
||||||
|
QWebPage.__init__(self)
|
||||||
|
settings = self.settings()
|
||||||
|
settings.setFontSize(QWebSettings.DefaultFontSize,
|
||||||
|
opts.pdf_default_font_size)
|
||||||
|
settings.setFontSize(QWebSettings.DefaultFixedFontSize,
|
||||||
|
opts.pdf_mono_font_size)
|
||||||
|
settings.setFontSize(QWebSettings.MinimumLogicalFontSize, 8)
|
||||||
|
settings.setFontSize(QWebSettings.MinimumFontSize, 8)
|
||||||
|
|
||||||
|
std = {'serif':opts.pdf_serif_family, 'sans':opts.pdf_sans_family,
|
||||||
|
'mono':opts.pdf_mono_family}.get(opts.pdf_standard_font,
|
||||||
|
opts.pdf_serif_family)
|
||||||
|
if std:
|
||||||
|
settings.setFontFamily(QWebSettings.StandardFont, std)
|
||||||
|
if opts.pdf_serif_family:
|
||||||
|
settings.setFontFamily(QWebSettings.SerifFont, opts.pdf_serif_family)
|
||||||
|
if opts.pdf_sans_family:
|
||||||
|
settings.setFontFamily(QWebSettings.SansSerifFont,
|
||||||
|
opts.pdf_sans_family)
|
||||||
|
if opts.pdf_mono_family:
|
||||||
|
settings.setFontFamily(QWebSettings.FixedFont, opts.pdf_mono_family)
|
||||||
|
|
||||||
|
def javaScriptConsoleMessage(self, msg, lineno, msgid):
|
||||||
|
self.log.debug(u'JS:', unicode(msg))
|
||||||
|
|
||||||
|
def javaScriptAlert(self, frame, msg):
|
||||||
|
self.log(unicode(msg))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def draw_image_page(page_rect, painter, p, preserve_aspect_ratio=True):
|
||||||
|
if preserve_aspect_ratio:
|
||||||
|
aspect_ratio = float(p.width())/p.height()
|
||||||
|
nw, nh = page_rect.width(), page_rect.height()
|
||||||
|
if aspect_ratio > 1:
|
||||||
|
nh = int(page_rect.width()/aspect_ratio)
|
||||||
|
else: # Width is smaller than height
|
||||||
|
nw = page_rect.height()*aspect_ratio
|
||||||
|
__, nnw, nnh = fit_image(nw, nh, page_rect.width(),
|
||||||
|
page_rect.height())
|
||||||
|
dx = int((page_rect.width() - nnw)/2.)
|
||||||
|
dy = int((page_rect.height() - nnh)/2.)
|
||||||
|
page_rect.moveTo(dx, dy)
|
||||||
|
page_rect.setHeight(nnh)
|
||||||
|
page_rect.setWidth(nnw)
|
||||||
|
painter.drawPixmap(page_rect, p, p.rect())
|
||||||
|
|
||||||
|
class PDFWriter(QObject):
|
||||||
|
|
||||||
|
def _pass_json_value_getter(self):
|
||||||
|
val = json.dumps(self.bridge_value)
|
||||||
|
return QString(val)
|
||||||
|
|
||||||
|
def _pass_json_value_setter(self, value):
|
||||||
|
self.bridge_value = json.loads(unicode(value))
|
||||||
|
|
||||||
|
_pass_json_value = pyqtProperty(QString, fget=_pass_json_value_getter,
|
||||||
|
fset=_pass_json_value_setter)
|
||||||
|
|
||||||
|
def __init__(self, opts, log, cover_data=None, toc=None):
|
||||||
|
from calibre.gui2 import is_ok_to_use_qt
|
||||||
|
if not is_ok_to_use_qt():
|
||||||
|
raise Exception('Not OK to use Qt')
|
||||||
|
QObject.__init__(self)
|
||||||
|
|
||||||
|
self.logger = self.log = log
|
||||||
|
self.opts = opts
|
||||||
|
self.cover_data = cover_data
|
||||||
|
self.paged_js = None
|
||||||
|
self.toc = toc
|
||||||
|
|
||||||
|
self.loop = QEventLoop()
|
||||||
|
self.view = QWebView()
|
||||||
|
self.page = Page(opts, self.log)
|
||||||
|
self.view.setPage(self.page)
|
||||||
|
self.view.setRenderHints(QPainter.Antialiasing|
|
||||||
|
QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
|
||||||
|
self.view.loadFinished.connect(self.render_html,
|
||||||
|
type=Qt.QueuedConnection)
|
||||||
|
for x in (Qt.Horizontal, Qt.Vertical):
|
||||||
|
self.view.page().mainFrame().setScrollBarPolicy(x,
|
||||||
|
Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
def dump(self, items, out_stream, pdf_metadata):
|
||||||
|
opts = self.opts
|
||||||
|
self.outline = Outline(self.toc, items)
|
||||||
|
page_size = get_page_size(self.opts)
|
||||||
|
dpi = min(self.opts.input_profile.dpi, 150)
|
||||||
|
ml, mr = opts.margin_left, opts.margin_right
|
||||||
|
margin_side = min(ml, mr)
|
||||||
|
ml, mr = ml - margin_side, mr - margin_side
|
||||||
|
self.doc = PdfDevice(out_stream, page_size=page_size, left_margin=ml,
|
||||||
|
top_margin=0, right_margin=mr, bottom_margin=0,
|
||||||
|
xdpi=dpi, ydpi=dpi, errors=self.log.error,
|
||||||
|
debug=self.log.debug, compress=not
|
||||||
|
opts.uncompressed_pdf)
|
||||||
|
|
||||||
|
self.page.setViewportSize(QSize(self.doc.width(), self.doc.height()))
|
||||||
|
self.render_queue = items
|
||||||
|
self.first_page = True
|
||||||
|
|
||||||
|
# TODO: Test margins
|
||||||
|
mt, mb = map(self.doc.to_px, (opts.margin_top, opts.margin_bottom))
|
||||||
|
ms = self.doc.to_px(margin_side, vertical=False)
|
||||||
|
self.margin_top, self.margin_size, self.margin_bottom = map(
|
||||||
|
lambda x:int(floor(x)), (mt, ms, mb))
|
||||||
|
|
||||||
|
self.painter = QPainter(self.doc)
|
||||||
|
self.doc.set_metadata(title=pdf_metadata.title,
|
||||||
|
author=pdf_metadata.author,
|
||||||
|
tags=pdf_metadata.tags)
|
||||||
|
self.painter.save()
|
||||||
|
try:
|
||||||
|
if self.cover_data is not None:
|
||||||
|
p = QPixmap()
|
||||||
|
p.loadFromData(self.cover_data)
|
||||||
|
if not p.isNull():
|
||||||
|
draw_image_page(QRect(0, 0, self.doc.width(), self.doc.height()),
|
||||||
|
self.painter, p,
|
||||||
|
preserve_aspect_ratio=self.opts.preserve_cover_aspect_ratio)
|
||||||
|
self.doc.end_page()
|
||||||
|
finally:
|
||||||
|
self.painter.restore()
|
||||||
|
|
||||||
|
QTimer.singleShot(0, self.render_book)
|
||||||
|
self.loop.exec_()
|
||||||
|
|
||||||
|
# TODO: Outline and links
|
||||||
|
self.painter.end()
|
||||||
|
|
||||||
|
if self.doc.errors_occurred:
|
||||||
|
raise Exception('PDF Output failed, see log for details')
|
||||||
|
|
||||||
|
def render_book(self):
|
||||||
|
if self.doc.errors_occurred:
|
||||||
|
return self.loop.exit(1)
|
||||||
|
try:
|
||||||
|
if not self.render_queue:
|
||||||
|
self.loop.exit()
|
||||||
|
else:
|
||||||
|
self.render_next()
|
||||||
|
except:
|
||||||
|
self.logger.exception('Rendering failed')
|
||||||
|
self.loop.exit(1)
|
||||||
|
|
||||||
|
def render_next(self):
|
||||||
|
item = unicode(self.render_queue.pop(0))
|
||||||
|
|
||||||
|
self.logger.debug('Processing %s...' % item)
|
||||||
|
self.current_item = item
|
||||||
|
load_html(item, self.view)
|
||||||
|
|
||||||
|
def render_html(self, ok):
|
||||||
|
if ok:
|
||||||
|
try:
|
||||||
|
self.do_paged_render()
|
||||||
|
except:
|
||||||
|
self.log.exception('Rendering failed')
|
||||||
|
self.loop.exit(1)
|
||||||
|
else:
|
||||||
|
# The document is so corrupt that we can't render the page.
|
||||||
|
self.logger.error('Document cannot be rendered.')
|
||||||
|
self.loop.exit(1)
|
||||||
|
return
|
||||||
|
self.render_book()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_page_num(self):
|
||||||
|
return self.doc.current_page_num
|
||||||
|
|
||||||
|
def do_paged_render(self):
|
||||||
|
if self.paged_js is None:
|
||||||
|
from calibre.utils.resources import compiled_coffeescript
|
||||||
|
self.paged_js = compiled_coffeescript('ebooks.oeb.display.utils')
|
||||||
|
self.paged_js += compiled_coffeescript('ebooks.oeb.display.indexing')
|
||||||
|
self.paged_js += compiled_coffeescript('ebooks.oeb.display.paged')
|
||||||
|
|
||||||
|
self.view.page().mainFrame().addToJavaScriptWindowObject("py_bridge", self)
|
||||||
|
evaljs = self.view.page().mainFrame().evaluateJavaScript
|
||||||
|
evaljs(self.paged_js)
|
||||||
|
evaljs('''
|
||||||
|
py_bridge.__defineGetter__('value', function() {
|
||||||
|
return JSON.parse(this._pass_json_value);
|
||||||
|
});
|
||||||
|
py_bridge.__defineSetter__('value', function(val) {
|
||||||
|
this._pass_json_value = JSON.stringify(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.style.backgroundColor = "white";
|
||||||
|
paged_display.set_geometry(1, %d, %d, %d);
|
||||||
|
paged_display.layout();
|
||||||
|
paged_display.fit_images();
|
||||||
|
'''%(self.margin_top, self.margin_size, self.margin_bottom))
|
||||||
|
|
||||||
|
mf = self.view.page().mainFrame()
|
||||||
|
start_page = self.current_page_num
|
||||||
|
while True:
|
||||||
|
if not self.first_page:
|
||||||
|
self.doc.init_page()
|
||||||
|
self.first_page = False
|
||||||
|
self.painter.save()
|
||||||
|
try:
|
||||||
|
mf.render(self.painter)
|
||||||
|
nsl = evaljs('paged_display.next_screen_location()').toInt()
|
||||||
|
if not nsl[1] or nsl[0] <= 0:
|
||||||
|
break
|
||||||
|
evaljs('window.scrollTo(%d, 0)'%nsl[0])
|
||||||
|
self.doc.end_page()
|
||||||
|
finally:
|
||||||
|
self.painter.restore()
|
||||||
|
if self.doc.errors_occurred:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.bridge_value = tuple(self.outline.anchor_map[self.current_item])
|
||||||
|
evaljs('py_bridge.value = book_indexing.anchor_positions(py_bridge.value)')
|
||||||
|
amap = self.bridge_value
|
||||||
|
if not isinstance(amap, dict):
|
||||||
|
amap = {} # Some javascript error occurred
|
||||||
|
self.outline.set_pos(self.current_item, None, start_page, 0)
|
||||||
|
for anchor, x in amap.iteritems():
|
||||||
|
pagenum, ypos = x
|
||||||
|
self.outline.set_pos(self.current_item, anchor, start_page + pagenum, ypos)
|
||||||
|
|
@ -303,6 +303,14 @@ class PDFStream(object):
|
|||||||
def catalog(self):
|
def catalog(self):
|
||||||
return self.objects[1]
|
return self.objects[1]
|
||||||
|
|
||||||
|
def set_metadata(self, title=None, author=None, tags=None):
|
||||||
|
if title:
|
||||||
|
self.info['Title'] = String(title)
|
||||||
|
if author:
|
||||||
|
self.info['Author'] = String(author)
|
||||||
|
if tags:
|
||||||
|
self.info['Keywords'] = String(tags)
|
||||||
|
|
||||||
def write_line(self, byts=b''):
|
def write_line(self, byts=b''):
|
||||||
byts = byts if isinstance(byts, bytes) else byts.encode('ascii')
|
byts = byts if isinstance(byts, bytes) else byts.encode('ascii')
|
||||||
self.stream.write(byts + EOL)
|
self.stream.write(byts + EOL)
|
||||||
@ -409,7 +417,7 @@ class PDFStream(object):
|
|||||||
self.current_page.write('%s Tm '%' '.join(map(type(u''), transform)))
|
self.current_page.write('%s Tm '%' '.join(map(type(u''), 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.compress), self.current_page)
|
serialize(GlyphIndex(glyph_id), self.current_page)
|
||||||
self.current_page.write(' Tj ')
|
self.current_page.write(' Tj ')
|
||||||
self.current_page.write_line(b' ET')
|
self.current_page.write_line(b' ET')
|
||||||
|
|
||||||
|
@ -9,18 +9,16 @@ Write content to PDF.
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
import os, shutil, json
|
import os, shutil, json
|
||||||
from future_builtins import map
|
|
||||||
|
|
||||||
from PyQt4.Qt import (QEventLoop, QObject, QPrinter, QSizeF, Qt, QPainter,
|
from PyQt4.Qt import (QEventLoop, QObject, QPrinter, QSizeF, Qt, QPainter,
|
||||||
QPixmap, QTimer, pyqtProperty, QString, QSize)
|
QPixmap, QTimer, pyqtProperty, QString, QSize)
|
||||||
from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings
|
from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings
|
||||||
|
|
||||||
from calibre.ptempfile import PersistentTemporaryDirectory
|
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||||
from calibre.ebooks.pdf.pageoptions import (unit, paper_size, orientation)
|
from calibre.ebooks.pdf.pageoptions import (unit, paper_size)
|
||||||
from calibre.ebooks.pdf.outline_writer import Outline
|
from calibre.ebooks.pdf.outline_writer import Outline
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre import (__appname__, __version__, fit_image, isosx, force_unicode)
|
from calibre import (__appname__, __version__, fit_image, isosx)
|
||||||
from calibre.ebooks.oeb.display.webview import load_html
|
from calibre.ebooks.oeb.display.webview import load_html
|
||||||
|
|
||||||
def get_custom_size(opts):
|
def get_custom_size(opts):
|
||||||
@ -72,7 +70,6 @@ def get_pdf_printer(opts, for_comic=False, output_file_name=None): # {{{
|
|||||||
else:
|
else:
|
||||||
printer.setPageMargins(opts.margin_left, opts.margin_top,
|
printer.setPageMargins(opts.margin_left, opts.margin_top,
|
||||||
opts.margin_right, opts.margin_bottom, QPrinter.Point)
|
opts.margin_right, opts.margin_bottom, QPrinter.Point)
|
||||||
printer.setOrientation(orientation(opts.orientation))
|
|
||||||
printer.setOutputFormat(QPrinter.PdfFormat)
|
printer.setOutputFormat(QPrinter.PdfFormat)
|
||||||
printer.setFullPage(for_comic)
|
printer.setFullPage(for_comic)
|
||||||
if output_file_name:
|
if output_file_name:
|
||||||
@ -103,24 +100,6 @@ def draw_image_page(printer, painter, p, preserve_aspect_ratio=True):
|
|||||||
painter.drawPixmap(page_rect, p, p.rect())
|
painter.drawPixmap(page_rect, p, p.rect())
|
||||||
|
|
||||||
|
|
||||||
class PDFMetadata(object): # {{{
|
|
||||||
def __init__(self, oeb_metadata=None):
|
|
||||||
self.title = _(u'Unknown')
|
|
||||||
self.author = _(u'Unknown')
|
|
||||||
self.tags = u''
|
|
||||||
|
|
||||||
if oeb_metadata != None:
|
|
||||||
if len(oeb_metadata.title) >= 1:
|
|
||||||
self.title = oeb_metadata.title[0].value
|
|
||||||
if len(oeb_metadata.creator) >= 1:
|
|
||||||
self.author = authors_to_string([x.value for x in oeb_metadata.creator])
|
|
||||||
if oeb_metadata.subject:
|
|
||||||
self.tags = u', '.join(map(unicode, oeb_metadata.subject))
|
|
||||||
|
|
||||||
self.title = force_unicode(self.title)
|
|
||||||
self.author = force_unicode(self.author)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
class Page(QWebPage): # {{{
|
class Page(QWebPage): # {{{
|
||||||
|
|
||||||
def __init__(self, opts, log):
|
def __init__(self, opts, log):
|
||||||
|
@ -18,16 +18,17 @@ class PluginWidget(Widget, Ui_Form):
|
|||||||
ICON = I('mimetypes/pdf.png')
|
ICON = I('mimetypes/pdf.png')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, ['paper_size', 'custom_size',
|
Widget.__init__(self, parent, [
|
||||||
'orientation', 'preserve_cover_aspect_ratio', 'pdf_serif_family',
|
'override_profile_size', 'paper_size', 'custom_size',
|
||||||
|
'preserve_cover_aspect_ratio', 'pdf_serif_family', 'unit',
|
||||||
'pdf_sans_family', 'pdf_mono_family', 'pdf_standard_font',
|
'pdf_sans_family', 'pdf_mono_family', 'pdf_standard_font',
|
||||||
'pdf_default_font_size', 'pdf_mono_font_size'])
|
'pdf_default_font_size', 'pdf_mono_font_size'])
|
||||||
self.db, self.book_id = db, book_id
|
self.db, self.book_id = db, book_id
|
||||||
|
|
||||||
for x in get_option('paper_size').option.choices:
|
for x in get_option('paper_size').option.choices:
|
||||||
self.opt_paper_size.addItem(x)
|
self.opt_paper_size.addItem(x)
|
||||||
for x in get_option('orientation').option.choices:
|
for x in get_option('unit').option.choices:
|
||||||
self.opt_orientation.addItem(x)
|
self.opt_unit.addItem(x)
|
||||||
for x in get_option('pdf_standard_font').option.choices:
|
for x in get_option('pdf_standard_font').option.choices:
|
||||||
self.opt_pdf_standard_font.addItem(x)
|
self.opt_pdf_standard_font.addItem(x)
|
||||||
|
|
||||||
|
@ -14,7 +14,27 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item row="1" column="0">
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Note:</b> The paper size settings below only take effect if you enable the "Override" checkbox below. Otherwise the size from the output profile will be used.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_override_profile_size">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Override paper size set in output profile</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Paper Size:</string>
|
<string>&Paper Size:</string>
|
||||||
@ -24,21 +44,8 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QComboBox" name="opt_paper_size"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Orientation:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_orientation</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QComboBox" name="opt_orientation"/>
|
<widget class="QComboBox" name="opt_paper_size"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
@ -51,8 +58,25 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
<widget class="QLineEdit" name="opt_custom_size"/>
|
<widget class="QLineEdit" name="opt_custom_size"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Unit:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_unit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="opt_unit"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="opt_preserve_cover_aspect_ratio">
|
<widget class="QCheckBox" name="opt_preserve_cover_aspect_ratio">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -60,19 +84,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="11" column="0">
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>213</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -159,15 +170,18 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="11" column="0">
|
||||||
<widget class="QLabel" name="label_10">
|
<spacer name="verticalSpacer">
|
||||||
<property name="text">
|
<property name="orientation">
|
||||||
<string><b>Note:</b> The paper size settings below only take effect if you have set the output profile to the default output profile. Otherwise the output profile will override these settings.</string>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="sizeHint" stdset="0">
|
||||||
<bool>true</bool>
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>213</height>
|
||||||
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user