from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtPrintSupport import *
from PySide2.QtUiTools import *
from .Util import mmToPixels, pixelsToMm, picaToPixels, marginsToString, print_document_data, print_preview_data, print_printer_data, loadPixMapData, dumpPixMapData, fileToPixMap, dataPixMapToImage, timed
from .PreviewPrinter import previewPrinter
from .Config import _, ARTWORK, ICONS
from copy import deepcopy
import os.path
USE_FAKE_HEADER = True
# QT BUG 85164
# DEFAULT_TRANSFORMATION_MODE=Qt.SmoothTransformation
DEFAULT_TRANSFORMATION_MODE=Qt.FastTransformation
# Helper class with pdf stuff related things
class helperPDF():
header_table_cols = 3
header_table_rows = 3
def __init__(self, parent=None, debug=False):
self.debug = False or debug
self.parent = parent
if self.parent and getattr(parent,'debug',None):
self.debug = getattr(parent,'debug',None)
self.pageMargins = QMarginsF(10,10,10,10)
self.orientation = QPageLayout.Orientation.Portrait
self.layout = None
self.printer = None
self.resolution_type = QPrinter.HighResolution
self.dpi = QPrinter(self.resolution_type).resolution()
self.constPaperScreen = None
self.document = None
self.cursor = None
self.styles = None
self.widget = None
self.printer, self.dpi, self.constPaperScreen, self.layout = self.initPrinter(printer=self.printer, resolution=self.resolution_type, margins=self.pageMargins, orientation=self.orientation)
self.widget = None
self.headerData = None
self.preview = False
self.last_cursor_ack = None
self.last_page_ack = 1
self.numberedPages = True
self.headerWithFrame = False
self.splitWithFrame = False
self.examData = None
if self.debug:
self.headerWithFrame = True
self.splitWithFrame = True
def setCustomizations(self,numberedPages, splitWithFrames, headerWithFrame):
self.splitWithFrame = splitWithFrame
self.numberedPages = numberedPages
self.headerWithFrame = headerWithFrame
@timed
def initPrinter(self, printer=None, resolution=QPrinter.HighResolution, margins=None, orientation=None):
if not resolution:
if not self.resolution_type:
self.resolution_type = QPrinter.HighResolution
# resolution = QPrinter(self.resolution_type).resolution()
if isinstance(resolution,QPrinter.PrinterMode):
default_printer = QPrinter(resolution)
resolution = default_printer.resolution()
else:
default_printer = QPrinter()
default_printer.setResolution(resolution)
self.dpi = resolution
if not printer:
if self.printer:
printer = self.printer
else:
self.printer = default_printer
printer = default_printer
if printer.resolution() != resolution:
printer.setResolution(resolution)
current_layout = self.printer.pageLayout()
changed_layout = False
if current_layout.units() != QPageLayout.Millimeter:
current_layout.setUnits(QPageLayout.Millimeter)
changed_layout = True
if margins is not None and isinstance(margins,QMarginsF):
if self.debug:
qDebug("{} {}".format(_('Setting margins to'),marginsToString(margins)))
current_layout.setMargins(margins)
changed_layout = True
else:
self.pageMargins = current_layout.margins()
if orientation is not None and isinstance(orientation, QPageLayout.Orientation):
if self.debug:
qDebug("{} {}".format(_('Setting orientation to'),orientation.name.decode()))
current_layout.setOrientation(orientation)
changed_layout = True
if changed_layout:
printer.setPageLayout(current_layout)
# PaperToScreen = int( resolution / QPrinter(QPrinter.ScreenResolution).resolution() )
PaperToScreen = int( resolution / 96 )
self.constPaperScreen = PaperToScreen
if self.debug:
qDebug("{}: {}".format(_('Setting constant'),int(self.constPaperScreen)))
relTextToA4 = int(self.printer.pageSizeMM().width()/210.0)
self.relTextToA4 = relTextToA4
if self.debug:
qDebug("{}: {}".format(_('Setting text multiplier size'),relTextToA4))
return printer, resolution, PaperToScreen, current_layout
@timed
def initDocument(self, printer=None, document=None):
if not printer:
printer = self.printer
if not document:
document = ExamDocument()
if not self.document:
self.document = document
document.setHeadersSize(16)
document.setPage(printer=printer)
self.initStyles(printer=printer)
return document
@timed
def initWidget(self, parent=None, printer=None,fn=None):
widget = previewPrinter(parent=parent,printer=printer)
widget.paintRequested.connect(fn)
return widget
@timed
def initSystem(self,printer=None,filename=None):
self.last_cursor_ack = None
self.last_page_ack = 1
if printer:
self.printer = printer
if not self.printer:
self.printer, self.dpi, self.constPaperScreen, self.layout = self.initPrinter(printer=self.printer, resolution=self.resolution_type, margins=self.pageMargins)
if not self.preview:
self.printer.setOutputFormat(QPrinter.PdfFormat)
if filename:
self.printer.setOutputFileName(filename)
else:
self.printer.setOutputFileName('out.pdf')
# Not needed to change printer orientation due: self.printer.Orientation() != self.printer.pageLayout().orientation()
#if self.printer.paperRect().height() > self.printer.paperRect().width():
# self.printer.setOrientation(QPrinter.Orientation.Portrait)
#else:
# self.printer.setOrientation(QPrinter.Orientation.Landscape)
@timed
def openWidget(self,answermode=False):
self.preview = True
self.document = self.completeDocument(answermode)
self.widget = self.initWidget(parent=self, printer=self.printer, fn=self.paintRequest )
self.widget.exec_()
@timed
def writePDF(self,filename=None,answermode=False):
self.preview = False
self.document = self.completeDocument(answermode)
dialog = QMessageBox()
hspacer = QSpacerItem(300,0,QSizePolicy.Minimum,QSizePolicy.Expanding)
dialog.setProperty('icon',QMessageBox.Information)
dialog.setStandardButtons(QMessageBox.Ok)
dialog.setText("{}".format(_('Pdf file generated')))
self.paintRequest(filename=filename)
dialog.setInformativeText(filename)
dialog.setStyleSheet('QMessageBox QLabel#qt_msgbox_label{ font-size: 12pt; } QMessageBox QLabel#qt_msgbox_informativelabel{ font-size: 10pt; }')
dialog.layout().addItem(hspacer,dialog.layout().rowCount(),0,1,dialog.layout().columnCount())
dialog.exec_()
@timed
def initStyles(self, styles=None, printer=None):
if printer and isinstance(printer,QPrinter):
resolution = printer.resolution()
else:
if self.dpi:
resolution = self.dpi
elif self.resolution_type:
resolution = QPrinter(self.resolution_type).resolution()
else:
resolution = QPrinter(QPrinter.HighResolution).resolution()
if not styles:
styles = {}
self.styles = styles
if self.debug:
tableborder = 1.0
tableborderstyle = QTextTableFormat.BorderStyle_Solid
else:
tableborder = 0.0
tableborderstyle = QTextTableFormat.BorderStyle_None
styles['line'] = QTextFrameFormat()
styles['line'].setHeight(1*self.constPaperScreen*self.relTextToA4)
styles['line'].setBackground(Qt.black)
styles['pagebreak'] = QTextBlockFormat()
styles['pagebreak'].setPageBreakPolicy(QTextFormat.PageBreak_AlwaysBefore)
x = 'table.common'
styles[x] = QTextTableFormat()
styles[x].setBorderStyle(QTextTableFormat.BorderStyle_Solid)
styles[x].setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
styles[x].setBorder(2.0)
styles[x].setMargin(0.0)
styles[x].setCellSpacing(0.0)
styles[x].setCellPadding(mmToPixels(2,resolution=resolution))
x = 'header.table'
styles[x] = QTextTableFormat()
styles[x].setBorderStyle(QTextTableFormat.BorderStyle_Solid)
styles[x].setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
styles[x].setBorder(2.0)
styles[x].setMargin(0.0)
styles[x].setCellSpacing(0.0)
styles[x].setCellPadding(mmToPixels(2,resolution=resolution))
styles['header.table'].setColumnWidthConstraints(
[
QTextLength(QTextLength.PercentageLength, 95/self.header_table_cols)
] * self.header_table_cols)
x = 'title.table'
styles[x] = QTextTableFormat()
styles[x].setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
styles[x].setMargin(0.0)
styles[x].setCellSpacing(0.0)
styles[x].setBorderStyle(tableborderstyle)
styles[x].setBorder(tableborder)
styles[x].setCellPadding(mmToPixels(5,resolution=resolution))
styles[x].setColumnWidthConstraints(
[
QTextLength(QTextLength.PercentageLength, 75),
QTextLength(QTextLength.PercentageLength, 25)
] )
x = 'title.table.text'
styles[x] = QTextTableFormat()
styles[x].setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
styles[x].setMargin(0.0)
styles[x].setCellSpacing(0.0)
styles[x].setBorderStyle(tableborderstyle)
styles[x].setBorder(tableborder)
styles[x].setCellPadding(0.0)
styles[x].setColumnWidthConstraints(
[
QTextLength(QTextLength.PercentageLength, 99),
QTextLength(QTextLength.PercentageLength, 0)
] )
x = 'option.table'
styles[x] = QTextTableFormat()
styles[x].setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
styles[x].setMargin(0.0)
styles[x].setCellSpacing(0.0)
styles[x].setBorderStyle(tableborderstyle)
styles[x].setBorder(tableborder)
styles[x].setCellPadding(0.0)
styles[x].setLeftMargin(mmToPixels(10,resolution=resolution))
styles[x].setColumnWidthConstraints(
[
QTextLength(QTextLength.PercentageLength, 5),
QTextLength(),
QTextLength()
] )
x = 'option.table.join'
styles[x] = QTextTableFormat()
styles[x].setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
styles[x].setMargin(0.0)
styles[x].setCellSpacing(0.0)
styles[x].setBorderStyle(tableborderstyle)
styles[x].setBorder(tableborder)
styles[x].setCellPadding(0.0)
# styles[x].setColumnWidthConstraints(
# [
# QTextLength(QTextLength.PercentageLength, 95 / 7)
# ] * 7
# )
styles[x].setAlignment(Qt.AlignCenter)
styles['centerH'] = QTextBlockFormat()
styles['centerH'].setAlignment(Qt.AlignCenter)
styles['centerV'] = QTextCharFormat()
styles['centerV'].setVerticalAlignment(QTextCharFormat.VerticalAlignment.AlignMiddle)
styles['body'] = QTextBlockFormat()
styles['body'].setAlignment(Qt.AlignJustify)
styles['double'] = QTextBlockFormat()
styles['double'].setLineHeight(200,QTextBlockFormat.ProportionalHeight)
styles['single'] = QTextBlockFormat()
styles['single'].setLineHeight(100,QTextBlockFormat.ProportionalHeight)
if self.debug:
qDebug("{}: {}".format(_('Using text multiplier size'),int(self.relTextToA4)))
# styles['defaultfont'] = QFont("Times",10 * self.constPaperScreen * self.relTextToA4)
# styles['bigfont'] = QFont("Times",30 * self.constPaperScreen * self.relTextToA4)
styles['defaultfont'] = QFont("Times", picaToPixels(10))
styles['bigfont'] = QFont("Times", picaToPixels(30))
styles['answer_ok'] = QFont("Times", picaToPixels(14),QFont.Bold)
styles['answer_fail'] = styles['defaultfont']
styles['answer_fail'].setWeight(QFont.Light)
styles['text'] = QTextCharFormat()
styles['bigtext'] = QTextCharFormat()
styles['text.bold'] = QTextCharFormat()
styles['text'].setFont(styles['defaultfont'])
styles['bigtext'].setFont(styles['bigfont'])
styles['text.bold'].setFont(styles['defaultfont'])
styles['text.bold'].setFontWeight(QFont.Bold)
return styles
def print_cursor_position_y(self,document,cursor=None):
if not cursor:
cursor = self.initCursor(document)
page_h = document.pageSize().height()
layout = document.documentLayout()
rect = layout.blockBoundingRect(cursor.block())
current_h = rect.y() + rect.height()
# page_h -= 2 * mmToPixels(document.headersSize,1200)
# current_h2 = document.documentLayout().blockBoundingRect(cursor.block()).y()
pagenumber = int(current_h/page_h)+1
pagecount = document.pageCount()
# if self.debug:
# qDebug('Cursor at: {} page={}'.format(current_h,pagenumber,pagecount))
pct = int(current_h*100/page_h)%100
return pagenumber,pct
@Slot(QPrinter)
def paintRequest(self, printer=None, filename=None):
if self.debug:
qDebug("***** {} ! *****".format(_('Repaint Event')))
self.initSystem(printer,filename)
if self.debug:
# print_document_data(self.document)
print_printer_data(self.printer)
#self.document = self.completeDocument(answermode)
#self.document = self.makeTestDocument(self.document)
if self.document.orientation != self.printer.pageLayout().orientation():
self.document = self.completeDocument(self.document.mode)
self.document.printExamModel(self.printer,numbered=self.numberedPages,framed=self.headerWithFrame)
def makeTestDocument(self,document):
#Init cursor
cursor = QTextCursor(document)
# cursor.movePosition(QTextCursor.End)
# if cursor.hasSelection():
# cursor.clearSelection()
t={}
for l in list('abcdefghijk'):
t[l] = ''
for i in range(1,20):
t[l] += '{0:s}_{1:03d}_'.format(l*5,i*10)
style_t = QTextCharFormat()
font = QFont("Courier",10 * self.constPaperScreen * self.relTextToA4)
style_t.setFont(font)
style_b = QTextBlockFormat()
style_f = QTextFrameFormat()
cursor.insertBlock(style_b)
a = cursor.block()
a.setUserData(BData('a'))
cursor.insertText(t.get('a'),style_t)
cursor.movePosition(QTextCursor.End)
cursor.insertBlock(style_b)
b = cursor.block()
b.setUserData(BData('b'))
bpos = b.blockNumber()
cursor.insertText(t.get('b'),style_t)
cursor.movePosition(QTextCursor.Start)
cursor.insertBlock(style_b)
c = cursor.block()
cursor.insertText(t.get('c'),style_t)
cursor = QTextCursor(b)
pos = cursor.position()
cursor.insertBlock(style_b)
d = cursor.block()
cursor.setPosition(pos)
cursor.insertText(t.get('d'),style_t)
cursor = QTextCursor(b)
pos2 = cursor.position()
cursor.insertBlock(style_b)
e = cursor.block()
cursor.setPosition(pos2)
cursor.insertText(t.get('e'),style_t)
cursor.movePosition(QTextCursor.Start)
cursor.movePosition(QTextCursor.NextBlock,QTextCursor.MoveAnchor,b.blockNumber())
pos3 = cursor.position()
cursor.insertBlock(style_b)
f = cursor.block()
cursor.setPosition(pos3)
cursor.insertText(t.get('f'),style_t)
cursor.movePosition(QTextCursor.Start)
while cursor.block().userData() is None or cursor.block().userData().data != 'b':
cursor.movePosition(QTextCursor.NextBlock,QTextCursor.MoveAnchor,1)
cursor.setPosition(cursor.position()-1)
cursor.insertBlock(style_b)
g = cursor.block()
cursor.insertText(t.get('g'),style_t)
cursor.movePosition(QTextCursor.Start)
tb = cursor.block()
tb = tb.next().next().next()
cursor = QTextCursor(tb)
style_cr = QTextBlockFormat()
style_cr.setPageBreakPolicy(QTextFormat.PageBreak_AlwaysBefore)
cursor.insertBlock(style_cr)
cursor.movePosition(QTextCursor.Start)
cursor = document.find('bbb')
if cursor.isNull():
qDebug('null')
else:
cursor.movePosition(QTextCursor.StartOfBlock)
cursor.insertBlock(style_cr)
qDebug('Total {} blocks'.format(document.blockCount()))
qDebug('{}'.format(document.toHtml()))
return document
@timed
def completeDocument(self, answermode=False):
document = self.initDocument(printer = self.printer)
document = self.writeExamData(document, answermode)
document.mode = answermode
return document
def buildFakeHeaderInfo(self):
from random import randint
a = randint(3,20)
b = randint(3,20)
header_info = {
'west' : {
'type': 'image',
'data': dumpPixMapData(fileToPixMap(ARTWORK['left'])),
'content': QUrl(ARTWORK['left']).toString()
},
'north' : {
'type': 'image',
'data': dumpPixMapData(fileToPixMap(ARTWORK['center'])),
'content': QUrl(ARTWORK['center']).toString()
},
'south' : {
'type': 'text',
'content': "Lorem ipsum " * a
},
'east' : {
'type': 'text',
'content': "Lorem ipsum " * b
}
}
return header_info
def setupCell(self, table, row=0, col=0, centerH=True, centerV=True):
cell = table.cellAt(row,col)
cursor = cell.firstCursorPosition()
if centerH:
cursor.setBlockFormat(self.styles['centerH'])
if centerV:
cell.setFormat(self.styles['centerV'])
return cursor,cell
def imageResized(self, name, max_image_height, max_image_width):
image = QImage(name)
sw = max_image_width / image.width()
sh = max_image_height / image.height()
s = min(sw,sh)
return image.scaled(s*image.width(),s*image.height(),Qt.KeepAspectRatio,DEFAULT_TRANSFORMATION_MODE)
def makeHeaderTable(self, document=None, headerdata=None, answermode=False):
printer = self.printer
config = self.headerData.get('config')
cursor = self.initCursor(document)
tableformat = QTextTableFormat()
configborder = None
bordersize = 2 # pixels
paddingsize = 1 # mm
if config:
configborder = config.get('border')
if configborder:
tableformat.setBorderStyle(QTextTableFormat.BorderStyle_Solid)
tableformat.setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
tableformat.setBorder(bordersize)
elif configborder is False:
tableformat.setBorderStyle(QTextTableFormat.BorderStyle_None)
else:
tableformat.setBorderStyle(QTextTableFormat.BorderStyle_Solid)
tableformat.setBorderBrush(QBrush(Qt.black,Qt.SolidPattern))
tableformat.setBorder(bordersize)
tableformat.setMargin(0.0)
tableformat.setCellSpacing(0.0)
paddingsize = mmToPixels(paddingsize,resolution=document.resolution)
tableformat.setCellPadding(paddingsize)
nrows = headerdata.get('nrows')
ncols = headerdata.get('ncols')
tablecolumnsize = document.pageSize().width() / ncols - (ncols + 1) * bordersize
qt_tablecolumnsize = QTextLength(QTextLength.FixedLength,tablecolumnsize)
tableformat.setColumnWidthConstraints([qt_tablecolumnsize] * ncols)
table = cursor.insertTable(nrows,headerdata.get('ncols'),tableformat)
joins = headerdata.get('joins')
if joins:
for k,v in joins.items():
y,x = [int(x) for x in k.split('_')]
sy,sx = v
table.mergeCells(y,x,sy,sx)
content = headerdata.get('content')
if content:
for k,v in content.items():
y,x = [ int(x) for x in k.split('_') ]
align = v.get('align')
pix = v.get('pix')
txt = v.get('txt')
if txt:
isField = True if '_@[' in txt.get('value') and ']@_' in txt.get('value') else False
if align:
if align == 'left':
align = Qt.AlignLeft|Qt.AlignVCenter
elif align == 'right':
align = Qt.AlignRight|Qt.AlignVCenter
else:
align = Qt.AlignCenter
else:
align = Qt.AlignCenter
cell = table.cellAt(y,x)
cursor = cell.firstCursorPosition()
blockformat = QTextBlockFormat()
blockformat.setAlignment(align)
cursor.setBlockFormat(blockformat)
charformat = QTextCharFormat()
charformat.setVerticalAlignment(QTextCharFormat.VerticalAlignment.AlignMiddle)
cell.setFormat(charformat)
if txt:
if not isField:
cursor.insertText(txt.get('show'),self.styles['text'])
else:
txt = txt.get('show')
cursor.insertBlock(blockformat,self.styles['text.bold'])
width = tablecolumnsize*cell.columnSpan()-2*paddingsize
fm = QFontMetrics(self.styles['text.bold'].font())
if bordersize:
sub = ' '
else:
sub = '_'
cursor.insertText("{}:{}".format(txt,sub*int((width-fm.size(Qt.TextSingleLine,txt+':').width())/fm.size(Qt.TextSingleLine,sub).width())))
if pix:
pix = dataPixMapToImage(pix)
max_image_height = ( tablecolumnsize / pix.width() ) * pix.height()
pix = self.imageResized(pix,max_image_height*cell.rowSpan()-2*paddingsize,tablecolumnsize*cell.columnSpan()-2*paddingsize)
cursor.insertImage(pix)
return document
cursor,cell = self.setupCell(table,2,1,centerH=False,centerV=True)
cursor.insertText('Name',self.styles['text.bold'])
cursor,cell = self.setupCell(table,2,2,centerH=False,centerV=True)
cursor.insertText('Group',self.styles['text.bold'])
self.writeSeparator(document, single=True)
return document
def makeTitleTable(self, document, rows, cols, cursor=None):
if not cursor:
cursor = self.initCursor(document)
if cols == 2:
table = cursor.insertTable(rows,cols,self.styles['title.table'])
elif cols == 1:
table = cursor.insertTable(rows,cols,self.styles['title.table.text'])
return table
def initCursor(self,document):
cursor = QTextCursor(document)
cursor.movePosition(QTextCursor.End)
if cursor.hasSelection():
cursor.clearSelection()
return cursor
def writeSeparator(self, document, cursor=None, number=None , single=False):
if not cursor:
cursor = self.initCursor(document)
if single:
style = self.styles['single']
else:
style = self.styles['double']
cursor.insertBlock(style,self.styles['text'])
txt = ""
if number:
txt = '{}'.format(number)
cursor.insertText(txt)
return cursor
def setExamData(self,examData):
self.examData = deepcopy(examData)
def writeImage(self, document, image, cursor=None):
if not cursor:
cursor = self.initCursor(document)
cursor.insertImage(image)
def debug_document_blocklayout(self,document):
ret=[]
if not self.debug:
return
blockCount = document.blockCount()
for i in range(blockCount):
r = document.documentLayout().blockBoundingRect(document.findBlockByNumber(i))
ret.append('{} #{} {} ({},{}) {} {}x{}'.format(_('Block'),i,_('on'),r.x(),r.y(),_('with'),r.width(),r.height()))
qDebug("\n".join(ret))
return ret
def writePageBreak(self, document, cursorpos=None):
cursor = QTextCursor(document)
if not cursorpos:
cursor = self.initCursor(document)
cursor.insertBlock(self.styles['pagebreak'])
else:
cursor.setPosition(cursorpos)
page1,pct1 = self.print_cursor_position_y(document,cursor)
cursor.movePosition(QTextCursor.StartOfBlock)
cursor.blockFormat().setPageBreakPolicy(QTextFormat.PageBreak_AlwaysBefore)
cursor.insertBlock(self.styles['pagebreak'])
page2,pct2 = self.print_cursor_position_y(document,cursor)
if self.debug:
qDebug('{}: {} {} {}% {} {} {}%'.format(_('Break page'),_('From'),page1,pct1,_('to'),page2,pct2))
def writeLine(self, document, cursor=None):
if not cursor:
cursor = self.initCursor(document)
if not self.splitWithFrame:
cursor.insertFrame(QTextFrameFormat())
return cursor
pagenum,pct = self.print_cursor_position_y(document,cursor)
cursor.insertFrame(self.styles['line'])
ret = cursor.movePosition(QTextCursor.Down)
return cursor
@timed
def writeExamData(self,document,answermode=False):
if not self.examData:
return document
for model in self.examData:
if model[0] == '_':
continue
document.setInitModel(model)
if self.headerData:
nrows = self.headerData.get('nrows')
ncols = self.headerData.get('ncols')
spans = self.headerData.get('joins')
content = self.headerData.get('content')
#self.styles['header.table'] = self.makeTableStyle(rows=nrows, cols=ncols, spans=spans, content=content, border=True)
document = self.makeHeaderTable(document=document,headerdata=self.headerData,answermode=answermode)
# if not answermode:
# document = self.writeTitleName(document)
# else:
self.writeSeparator(document,single=True)
self.writeSeparator(document,single=True)
self.writeSeparator(document,single=True)
data = self.examData[model]
self.pagequestion = {}
question_num = 0
for row in data:
question_num += 1
title = row.get('title')
title = title.capitalize()
typeq = row.get('type')
title_pic = row.get('title_pic')
cursor = self.initCursor(document)
cursor_ini = cursor.position()
pagestart,pct = self.print_cursor_position_y(document)
if self.debug:
qDebug("{} {} {} {} {}%".format(_("Starting question"),question_num,_('at page'),pagestart,pct))
if title or title_pic:
cols = 0
if title:
cols += 1
if title_pic:
cols += 1
table = self.makeTitleTable(document, rows=1, cols=cols, cursor=cursor)
if title:
cursor1,cell = self.setupCell(table,0,0,centerV=False)
if title_pic:
self.writeSeparator(document,cursor=cursor1,single=True)
if answermode:
self.writeTitle(document,title, cursor=cursor1,qnumber=question_num, html=True)
else:
self.writeTitle(document,title, cursor=cursor1,qnumber=question_num)
if title_pic:
cursor2,cell = self.setupCell(table,0,1,centerV=False)
image = dataPixMapToImage(title_pic)
max_image_width = (document.pageSize() / 4).width()
image = image.scaledToWidth(max_image_width,DEFAULT_TRANSFORMATION_MODE)
self.writeImage(document, image, cursor=cursor2)
self.writeSeparator(document,single=True)
if self.debug:
page,pct = self.print_cursor_position_y(document)
qDebug("{} {} {} {} {}%, {}={}...".format(_('End title from question'),question_num,_('at page'),pagestart,pct,_('title'),title[:30]))
self.writeSeparator(document,single=True)
nlines = 0
options = None
if typeq == 'single_question':
nlines = row.get('empty_lines')
if not nlines:
nlines = 0
else:
options = row.get('options')
if typeq == 'test_question':
if answermode:
self.writeTest(document,options, html=True)
else:
self.writeTest(document,options, html=False)
elif typeq == 'join_activity':
if answermode:
row_mapping = row.get('join_mapping')
self.writeJoinActivity(document,options, html=True, mapping=row_mapping)
else:
self.writeJoinActivity(document,options, html=False)
self.writeSeparator(document,single=False)
for i in range(1,nlines+1):
if self.debug:
self.writeSeparator(document,number=i)
page,pct = self.print_cursor_position_y(document)
qDebug('{} {} {} {} {} {} {}%'.format(_('Space'),i,_('from question'),question_num,_('at page'),page,pct))
else:
self.writeSeparator(document)
if self.debug:
page,pct = self.print_cursor_position_y(document)
qDebug("{} {} {} {} {} {}%".format(_('End options/lines'),_('from question'),question_num,_('at page'),page,pct))
cursor_end = self.writeLine(document)
cursor_end = cursor_end.position()
pageend,pct = self.print_cursor_position_y(document)
self.pagequestion.setdefault(pagestart,[])
if pagestart == pageend:
self.pagequestion[pagestart].append(question_num)
self.last_cursor_ack = cursor_end
if self.debug:
qDebug('{} {} {} {} {}%'.format(_('Question'),question_num,_('write success until page'),pageend,pct))
else:
if len(self.pagequestion[pagestart]) > 0:
if self.debug:
qDebug('{} {} {}'.format(_('Question'),question_num,_('goes next page, breaking page on last question')))
self.writePageBreak(document,cursor_ini)
else:
if self.debug:
qDebug('{} {} {}'.format(_('Question'),question_num,_('sizes more than one page, skipping break, write sucess')))
pageend,pct = self.print_cursor_position_y(document)
self.pagequestion.setdefault(pageend,[])
self.pagequestion[pageend].append(question_num)
self.last_cursor_ack = cursor_end
if self.debug:
qDebug('{} {}, {} {} {}%'.format(_('End processing question'),question_num,_('ended on page'),pageend,pct))
document.setEndModel(breakPage=False)
return document
def writeTest(self, document, options, cursor=None, html=False, color='red'):
if not cursor:
cursor = self.initCursor(document)
if options:
rows = len(options)
table = cursor.insertTable(rows,3,self.styles['option.table'])
i=0
tf = table.format()
tf.setCellSpacing(mmToPixels(2,1200))
table.setFormat(tf)
for opt in options:
text = opt.get('text1')
text = text.capitalize()
pic = opt.get('pic1')
is_valid = opt.get('valid')
iconbox = ICONS['option']
size = None
family = None
color = None
weight = None
style = 'defaultfont'
if html:
if is_valid:
style = 'answer_ok'
color = 'darkgreen'
iconbox = ICONS['boxok']
family = self.styles[style].family()
size = int(self.styles[style].pointSize())
weight = int(self.styles[style].weight()*8)
decoration = 'none'
else:
style = 'answer_fail'
color = 'darksalmon'
iconbox = ICONS['boxfail']
family = self.styles[style].family()
size = int(self.styles[style].pointSize())
weight = int(self.styles[style].weight()*8)
decoration = 'line-through'
c,cell = self.setupCell(table,i,0,centerV=True,centerH=False)
img = QImage(iconbox)
img = img.scaledToHeight(QFontMetrics(self.styles[style]).height(),DEFAULT_TRANSFORMATION_MODE)
c.insertImage(img)
if pic:
c,cell = self.setupCell(table,i,1,centerV=True,centerH=False)
img = dataPixMapToImage(pic)
if img.isNull():
qDebug(_('Error: Invalid image detected'))
else:
img = img.scaledToHeight(QFontMetrics(self.styles['defaultfont']).height()*5,DEFAULT_TRANSFORMATION_MODE)
c.insertImage(img)
if text:
c,cell = self.setupCell(table,i,2,centerV=True,centerH=False)
if html:
c.insertHtml('{}'.format(decoration,size,family,color,weight,text))
else:
c.setCharFormat(self.styles['text'])
c.insertText(text)
i += 1
return self.writeSeparator(document,single=True)
def writeJoinActivity(self, document, options, cursor=None, html=False, color='red', mapping=None):
if not cursor:
cursor = self.initCursor(document)
if not options:
return self.writeSeparator(document,single=True)
rows = len(options)
table = cursor.insertTable(rows,7,self.styles['option.table.join'])
tf = table.format()
tf.setCellSpacing(mmToPixels(2,1200))
table.setFormat(tf)
i=-1
for opt in options:
i += 1
text1 = opt.get('text1')
text1 = text1.capitalize()
pic1 = opt.get('pic1')
text2 = opt.get('text2')
text2 = text2.capitalize()
pic2 = opt.get('pic2')
inc = 50
if pic1:
inc -= 10
if pic2:
inc -= 10
if text1:
inc -= 10
if text2:
inc -= 10
separator = " " * inc
space = " " * 3
if text1:
c,cell = self.setupCell(table,i,0,centerV=True,centerH=False)
if html:
family = self.styles['text'].fontFamily()
weight = int(self.styles['text'].fontWeight()*8)
size = int(self.styles['text'].fontPointSize())
color = 'dimgray'
c.insertHtml('{}'.format(size,family,color,weight,text1+space))
else:
c.setCharFormat(self.styles['text'])
c.insertText(text1+space)
if pic1:
c,cell = self.setupCell(table,i,1,centerV=False,centerH=False)
img = dataPixMapToImage(pic1)
if img.isNull():
qDebug(_('Error: Invalid image detected'))
else:
img = img.scaledToHeight(QFontMetrics(self.styles['defaultfont']).height()*5,DEFAULT_TRANSFORMATION_MODE)
c.insertImage(img)
c,cell = self.setupCell(table,i,2,centerV=True,centerH=False)
if html and mapping:
text = chr(i+65)
family = self.styles['answer_ok'].family()
weight = int(self.styles['answer_ok'].weight()*8)
size = int(self.styles['answer_ok'].pointSize())
color = 'black'
c.insertHtml(' {}'.format(size,family,color,weight,text))
else:
c.setCharFormat(self.styles['text'])
c.insertText(space)
img = QImage(ICONS['option'])
img = img.scaledToHeight(QFontMetrics(self.styles['defaultfont']).height()*0.9,DEFAULT_TRANSFORMATION_MODE)
c.insertImage(img)
c,cell = self.setupCell(table,i,3,centerV=False,centerH=False)
c.setCharFormat(self.styles['text'])
c.insertText(separator)
c,cell = self.setupCell(table,i,4,centerV=True,centerH=False)
if html and mapping:
text = chr(mapping[i]+65)
family = self.styles['answer_ok'].family()
weight = int(self.styles['answer_ok'].weight()*8)
size = int(self.styles['answer_ok'].pointSize())
color = 'black'
c.insertHtml('{} '.format(size,family,color,weight,text))
else:
img = QImage(ICONS['option'])
img = img.scaledToHeight(QFontMetrics(self.styles['defaultfont']).height()*0.9,DEFAULT_TRANSFORMATION_MODE)
c.insertImage(img)
c.setCharFormat(self.styles['text'])
c.insertText(space)
if pic2:
c,cell = self.setupCell(table,i,5,centerV=False,centerH=False)
img = dataPixMapToImage(pic2)
if img.isNull():
qDebug(_('Error: Invalid image detected'))
else:
img = img.scaledToHeight(QFontMetrics(self.styles['defaultfont']).height()*5,DEFAULT_TRANSFORMATION_MODE)
c.insertImage(img)
if text2:
c,cell = self.setupCell(table,i,6,centerV=True,centerH=False)
if html:
family = self.styles['text'].fontFamily()
weight = int(self.styles['text'].fontWeight()*8)
size = int(self.styles['text'].fontPointSize())
color = 'dimgray'
c.insertHtml('{}'.format(size,family,color,weight,space+text2))
else:
c.setCharFormat(self.styles['text'])
c.insertText(space+text2)
return self.writeSeparator(document,single=True)
def writeTitleName(self,document,cursor=None):
if not cursor:
cursor = self.initCursor(document)
cursor.insertBlock(self.styles['body'],self.styles['text.bold'])
width = document.idealWidth()
fm = QFontMetrics(self.styles['text.bold'].font())
cursor.insertText("{}: {}".format(_('Name'),'_'*int((width-fm.size(Qt.TextSingleLine,_('Name')+':').width())/fm.size(Qt.TextSingleLine,'_').width())))
return document
def writeTitle(self,document,text, cursor=None, qnumber=None, html=False, color='red'):
if document and text:
if not cursor:
cursor = self.initCursor(document)
cursor.insertBlock(self.styles['body'],self.styles['text'])
family = self.styles['text'].fontFamily()
weight = int(self.styles['text'].fontWeight()*8)
size = int(self.styles['text'].fontPointSize())
bfamily = self.styles['text.bold'].fontFamily()
bweight = int(self.styles['text.bold'].fontWeight()*8)
bsize = int(self.styles['text.bold'].fontPointSize())
if qnumber:
if html:
color = 'dimgray'
cursor.insertHtml('{} {}: '.format(bsize,bfamily,color,900,_('Question'),qnumber))
else:
cursor.insertText('{} {}: '.format(_('Question'),qnumber),self.styles['text.bold'])
if html:
color = 'lightgray'
cursor.insertHtml('{}'.format(size,family,color,weight,text))
else:
cursor.insertText(text,self.styles['text'])
def setHeaderInfo(self,headerData):
self.headerData = deepcopy(headerData)
# class BData(QTextBlockUserData):
# def __init__(self,data=None):
# super().__init__()
# self.data = data
class ExamDocument(QTextDocument):
def __init__(self,*args,**kwargs):
self.headersSize = 16
self.resolution = None
self.modelOrder = []
self.models = {}
self.orientation = None
self.mode = None
super().__init__(*args,**kwargs)
def setHeadersSize(self, value):
self.headersSize = value
def setInitModel(self,model):
self.setEndModel()
self.modelOrder.append(model)
self.models.setdefault(model,{'ini': self.pageCount(), 'end':None, 'started': True})
def setEndModel(self,breakPage=True):
keys = self.models.keys()
if len(self.modelOrder) != len(keys) or len(keys) < 1:
return None
last = self.modelOrder[-1]
if last not in keys:
raise ValueError()
if self.models[last].get('started'):
self.models[last]['started'] = False
self.models[last]['end'] = self.pageCount()
if breakPage:
cursor = QTextCursor(self)
cursor.movePosition(QTextCursor.End)
pb = QTextBlockFormat()
pb.setPageBreakPolicy(QTextFormat.PageBreak_AlwaysBefore)
cursor.insertBlock(pb)
def setPage(self,pageSize=None,printer=None):
if not pageSize:
if printer:
self.resolution = printer.resolution()
pageSize = printer.pageRect().size()
if self.headersSize:
pageSize.setHeight(pageSize.height()-mmToPixels(self.headersSize,resolution=self.resolution))
else:
return
if pageSize.height() > pageSize.width():
self.orientation = QPrinter.Orientation.Portrait
else:
self.orientation = QPrinter.Orientation.Landscape
self.setPageSize(pageSize)
def printPageWithHeaders(self,index,painter,document,body,printer,header="HEADER",footer="FOOTER",framed=True):
fontHeaders = QFont('courier',self.headersSize,QFont.Black)
printer_height = printer.height()
paper_height = body.height()
offset_document = (index-1) * paper_height
free_space = printer_height - paper_height
header_height = free_space / 2
footer_height = free_space / 2
# header rect
# Coordinates into one page
headerPageRect = QRectF(0,0, printer.width(), header_height)
# text rect
# Coordinates into one page
# textPageRect = QRectF(0, header_height, printer.width(), body.height())
# view rect
# Coordinates into full document
viewRect = QRectF(QPointF(0,offset_document), body.size())
# footer rect
# Coordinates into one page
footerPageRect = QRectF(0,printer_height-footer_height, printer.width(), header_height)
offset_line = mmToPixels(2,resolution=printer.resolution())
pen = painter.pen()
pen.setColor(Qt.black)
painter.save()
painter.setRenderHint(QPainter.TextAntialiasing,True)
painter.setRenderHint(QPainter.HighQualityAntialiasing,False)
offset_header = 0
if header:
painter.setPen(pen)
painter.setFont(fontHeaders)
painter.drawText(headerPageRect,Qt.AlignRight, header)
if framed:
pen.setWidth(5)
painter.setPen(pen)
painter.drawLine(0,header_height-offset_line,printer.width(),header_height-offset_line)
offset_header = header_height
offset_header = - offset_document + offset_header
painter.translate(0, offset_header)
layout = document.documentLayout()
ctx = QAbstractTextDocumentLayout.PaintContext()
ctx.clip = viewRect
ctx.palette.setColor(QPalette.Text,Qt.black)
layout.draw(painter,ctx)
painter.restore()
if footer:
painter.save()
painter.setRenderHint(QPainter.TextAntialiasing,True)
painter.setRenderHint(QPainter.HighQualityAntialiasing,False)
pen = painter.pen()
pen.setWidth(5)
pen.setColor(Qt.black)
painter.setPen(pen)
if framed:
painter.drawLine(0,paper_height+header_height-offset_line,printer.width(),paper_height+header_height-offset_line)
painter.setFont(fontHeaders)
painter.drawText(footerPageRect,Qt.AlignRight, footer)
painter.restore()
@timed
def printExamModel(self,printer,numbered=True,framed=True):
headermap = {}
footermap = {}
footer = ''
header = ''
for modelname, infomodel in self.models.items():
counter = 1
for i in range(infomodel.get('ini'),infomodel.get('end')+1):
headermap.setdefault(i,modelname)
footermap.setdefault(i,counter)
counter += 1
p = QPainter(printer)
self.setPage(printer=printer)
body = QRectF(QPointF(0,0),self.pageSize())
for i in range(self.pageCount()):
if i != 0:
printer.newPage()
if headermap:
header = None
ini = None
end = None
cur = None
total = None
if isinstance(headermap,dict):
header = headermap.get(i+1)
info = self.models.get(header)
header = str(header)
if isinstance(info,dict):
end = info.get('end')
ini = info.get('ini')
if isinstance(end,int) and isinstance(ini,int):
total = end - ini +1
footer = None
if isinstance(footermap,dict):
footer = footermap.get(i+1)
if numbered:
if footer and total:
footer = "{}/{}".format(footer,total)
else:
footer = ''
self.printPageWithHeaders(i+1,p,self,body,printer,header=header,footer=footer,framed=framed)
# class MyPrinter(QPrinter):
# def __init__(self,*args,**kwargs):
# super().__init__(*args,**kwargs)
# def paintRequested(self,*args,**kwargs):
# super().__init__(*args,**kwargs)