From 5c5e245f7475bd689c705ed4ff87354fd306d91d Mon Sep 17 00:00:00 2001 From: Stefan Katzensteiner Date: Tue, 20 Jan 2015 15:50:29 +0100 Subject: [PATCH] Added module web_printscreen_zb --- .../web_printscreen_zb/__init__.py | 27 + .../web_printscreen_zb/__openerp__.py | 41 + .../web_printscreen_zb/controllers.py | 185 +++ .../static/src/js/web_printscreen_export.js | 122 ++ .../static/src/xml/web_printscreen_export.xml | 16 + .../web_printscreen_zb/trml2pdf.py | 1044 +++++++++++++++++ .../views/web_printscreen_zb.xml | 12 + setup/lib/config_at.py | 3 +- 8 files changed, 1449 insertions(+), 1 deletion(-) create mode 100644 ext/custom-addons/web_printscreen_zb/__init__.py create mode 100644 ext/custom-addons/web_printscreen_zb/__openerp__.py create mode 100644 ext/custom-addons/web_printscreen_zb/controllers.py create mode 100644 ext/custom-addons/web_printscreen_zb/static/src/js/web_printscreen_export.js create mode 100644 ext/custom-addons/web_printscreen_zb/static/src/xml/web_printscreen_export.xml create mode 100644 ext/custom-addons/web_printscreen_zb/trml2pdf.py create mode 100644 ext/custom-addons/web_printscreen_zb/views/web_printscreen_zb.xml diff --git a/ext/custom-addons/web_printscreen_zb/__init__.py b/ext/custom-addons/web_printscreen_zb/__init__.py new file mode 100644 index 00000000..63149b13 --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/__init__.py @@ -0,0 +1,27 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2013 ZestyBeanz Technologies Pvt. Ltd. +# (http://wwww.zbeanztech.com) +# contact@zbeanztech.com +# prajul@zbeanztech.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + +import trml2pdf +import controllers + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/ext/custom-addons/web_printscreen_zb/__openerp__.py b/ext/custom-addons/web_printscreen_zb/__openerp__.py new file mode 100644 index 00000000..b074cd8e --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/__openerp__.py @@ -0,0 +1,41 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2013 ZestyBeanz Technologies Pvt. Ltd. +# (http://wwww.zbeanztech.com) +# contact@zbeanztech.com +# prajul@zbeanztech.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Web Printscreen ZB', + 'version': '1.0', + 'category': 'Web', + 'description': """ + Module to export current active tree view in to excel report + """, + 'author': 'Zesty Beanz Technologies', + 'website': 'http://www.zbeanztech.com', + 'depends': ['web'], + 'data': ['views/web_printscreen_zb.xml'], + 'qweb': ['static/src/xml/web_printscreen_export.xml'], + 'installable': True, + 'auto_install': False, + 'web_preload': False, +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/ext/custom-addons/web_printscreen_zb/controllers.py b/ext/custom-addons/web_printscreen_zb/controllers.py new file mode 100644 index 00000000..8c383af8 --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/controllers.py @@ -0,0 +1,185 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2013 ZestyBeanz Technologies Pvt. Ltd. +# (http://wwww.zbeanztech.com) +# contact@zbeanztech.com +# prajul@zbeanztech.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + +try: + import json +except ImportError: + import simplejson as json +import openerp.addons.web.http as openerpweb +from openerp.addons.web.controllers.main import ExcelExport +from openerp.addons.web.controllers.main import Export +import re +from cStringIO import StringIO +from lxml import etree +import trml2pdf +import time, os +import locale +import openerp.tools as tools +try: + import xlwt +except ImportError: + xlwt = None + +class ZbExcelExport(ExcelExport): + _cp_path = '/web/export/zb_excel_export' + + def from_data(self, fields, rows): + workbook = xlwt.Workbook() + worksheet = workbook.add_sheet('Sheet 1') + style = xlwt.easyxf('align: wrap yes') + font = xlwt.Font() + font.bold = True + style.font = font + ignore_index = [] + count = 0 + for i, fieldname in enumerate(fields): + if fieldname.get('header_data_id', False): + field_name = fieldname.get('header_name', '') + worksheet.write(0, i-count, field_name, style) + worksheet.col(i).width = 8000 + else: + count += 1 + ignore_index.append(i) + style = xlwt.easyxf('align: wrap yes') + bold_style = xlwt.easyxf('align: wrap yes') + font = xlwt.Font() + font.bold = True + bold_style.font = font + for row_index, row in enumerate(rows): + count = 0 + for cell_index, cell_value in enumerate(row): + if cell_index not in ignore_index: + cell_style = style + if cell_value.get('bold', False): + cell_style = bold_style + cellvalue = cell_value.get('data', '') + if isinstance(cellvalue, basestring): + cellvalue = re.sub("\r", " ", cellvalue) + if cell_value.get('number', False) and cellvalue: + cellvalue = float(cellvalue) + if cellvalue is False: cellvalue = None + worksheet.write(row_index + 1, cell_index - count, cellvalue, cell_style) + else: + count += 1 + fp = StringIO() + workbook.save(fp) + fp.seek(0) + data = fp.read() + fp.close() + return data + + @openerpweb.httprequest + def index(self, req, data, token): + data = json.loads(data) + return req.make_response( + self.from_data(data.get('headers', []), data.get('rows', [])), + headers=[ + ('Content-Disposition', 'attachment; filename="%s"' + % data.get('model', 'Export.xls')), + ('Content-Type', self.content_type) + ], + cookies={'fileToken': token} + ) + +class ExportPdf(Export): + _cp_path = '/web/export/zb_pdf' + fmt = { + 'tag': 'pdf', + 'label': 'PDF', + 'error': None + } + + @property + def content_type(self): + return 'application/pdf' + + def filename(self, base): + return base + '.pdf' + + def from_data(self, uid, fields, rows, company_name): + pageSize=[210.0,297.0] + new_doc = etree.Element("report") + config = etree.SubElement(new_doc, 'config') + def _append_node(name, text): + n = etree.SubElement(config, name) + n.text = text + _append_node('date', time.strftime(str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')))) + _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) + _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) + _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) + _append_node('PageFormat', 'a4') + _append_node('header-date', time.strftime(str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')))) + _append_node('company', company_name) + l = [] + t = 0 + temp = [] + tsum = [] + skip_index = [] + header = etree.SubElement(new_doc, 'header') + i = 0 + for f in fields: + if f.get('header_data_id', False): + value = f.get('header_name', "") + field = etree.SubElement(header, 'field') + field.text = tools.ustr(value) + else: + skip_index.append(i) + i += 1 + lines = etree.SubElement(new_doc, 'lines') + for row_lines in rows: + node_line = etree.SubElement(lines, 'row') + j = 0 + for row in row_lines: + if not j in skip_index: + para = "yes" + tree = "no" + value = row.get('data', '') + if row.get('bold', False): + para = "group" + if row.get('number', False): + tree = "float" + col = etree.SubElement(node_line, 'col', para=para, tree=tree) + col.text = tools.ustr(value) + j += 1 + transform = etree.XSLT( + etree.parse(os.path.join(tools.config['root_path'], + 'addons/base/report/custom_new.xsl'))) + rml = etree.tostring(transform(new_doc)) + self.obj = trml2pdf.parseNode(rml, title='Printscreen') + return self.obj + +class ZbPdfExport(ExportPdf): + _cp_path = '/web/export/zb_pdf_export' + + @openerpweb.httprequest + def index(self, req, data, token): + data = json.loads(data) + uid = data.get('uid', False) + return req.make_response(self.from_data(uid, data.get('headers', []), data.get('rows', []), + data.get('company_name','')), + headers=[('Content-Disposition', + 'attachment; filename=PDF Export'), + ('Content-Type', self.content_type)]) +# cookies={'fileToken': long(token)}) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/ext/custom-addons/web_printscreen_zb/static/src/js/web_printscreen_export.js b/ext/custom-addons/web_printscreen_zb/static/src/js/web_printscreen_export.js new file mode 100644 index 00000000..dfcbe0a2 --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/static/src/js/web_printscreen_export.js @@ -0,0 +1,122 @@ +openerp.web_printscreen_zb = function(instance, m) { + + var _t = instance.web._t; + var QWeb = instance.web.qweb; + + instance.web.ListView.include({ + load_list: function () { + var self = this; + this._super.apply(this, arguments); + self.$pager.find(".oe_list_button_import_excel").unbind('click').click(function(event){self.export_to_excel("excel")}) + self.$pager.find(".oe_list_button_import_pdf").unbind('click').click(function(event){self.export_to_excel("pdf")}) + }, + export_to_excel: function(export_type) { + var self = this + var export_type = export_type + view = this.getParent() + // Find Header Element + header_eles = self.$el.find('.oe_list_header_columns') + header_name_list = [] + $.each(header_eles,function(){ + $header_ele = $(this) + header_td_elements = $header_ele.find('th') + $.each(header_td_elements,function(){ + $header_td = $(this) + text = $header_td.text().trim() || "" + data_id = $header_td.attr('data-id') + if (text && !data_id){ + data_id = 'group_name' + } + header_name_list.push({'header_name': text.trim(), 'header_data_id': data_id}) + // } + }); + }); + + //Find Data Element + data_eles = self.$el.find('.oe_list_content > tbody > tr') + export_data = [] + $.each(data_eles,function(){ + data = [] + $data_ele = $(this) + is_analysis = false + if ($data_ele.text().trim()){ + //Find group name + group_th_eles = $data_ele.find('th') + $.each(group_th_eles,function(){ + $group_th_ele = $(this) + text = $group_th_ele.text().trim() || "" + is_analysis = true + data.push({'data': text, 'bold': true}) + }); + data_td_eles = $data_ele.find('td') + $.each(data_td_eles,function(){ + $data_td_ele = $(this) + text = $data_td_ele.text().trim() || "" + if ($data_td_ele && $data_td_ele[0].classList.contains('oe_number') && !$data_td_ele[0].classList.contains('oe_list_field_float_time')){ + text = text.replace('%', '') + text = instance.web.parse_value(text, { type:"float" }) + data.push({'data': text || "", 'number': true}) + } + else{ + data.push({'data': text}) + } + }); + export_data.push(data) + } + }); + + //Find Footer Element + + footer_eles = self.$el.find('.oe_list_content > tfoot> tr') + $.each(footer_eles,function(){ + data = [] + $footer_ele = $(this) + footer_td_eles = $footer_ele.find('td') + $.each(footer_td_eles,function(){ + $footer_td_ele = $(this) + text = $footer_td_ele.text().trim() || "" + if ($footer_td_ele && $footer_td_ele[0].classList.contains('oe_number')){ + text = instance.web.parse_value(text, { type:"float" }) + data.push({'data': text || "", 'bold': true, 'number': true}) + } + else{ + data.push({'data': text, 'bold': true}) + } + }); + export_data.push(data) + }); + + //Export to excel + $.blockUI(); + if (export_type === 'excel'){ + view.session.get_file({ + url: '/web/export/zb_excel_export', + data: {data: JSON.stringify({ + model : view.model, + headers : header_name_list, + rows : export_data, + })}, + complete: $.unblockUI + }); + } + else{ + console.log(view) + new instance.web.Model("res.users").get_func("read")(this.session.uid, ["company_id"]).then(function(res) { + new instance.web.Model("res.company").get_func("read")(res['company_id'][0], ["name"]).then(function(result) { + view.session.get_file({ + url: '/web/export/zb_pdf_export', + data: {data: JSON.stringify({ + uid: view.session.uid, + model : view.model, + headers : header_name_list, + rows : export_data, + company_name: result['name'] + })}, + complete: $.unblockUI + }); + }); + }); + } + }, + }); +}; diff --git a/ext/custom-addons/web_printscreen_zb/static/src/xml/web_printscreen_export.xml b/ext/custom-addons/web_printscreen_zb/static/src/xml/web_printscreen_export.xml new file mode 100644 index 00000000..7af23f9e --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/static/src/xml/web_printscreen_export.xml @@ -0,0 +1,16 @@ + + + + + PDF + + Excel + + + diff --git a/ext/custom-addons/web_printscreen_zb/trml2pdf.py b/ext/custom-addons/web_printscreen_zb/trml2pdf.py new file mode 100644 index 00000000..5a29c331 --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/trml2pdf.py @@ -0,0 +1,1044 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2013 ZestyBeanz Technologies Pvt. Ltd. +# (http://wwww.zbeanztech.com) +# contact@zbeanztech.com +# prajul@zbeanztech.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + + +import sys +import copy +import reportlab +import re +from reportlab.pdfgen import canvas +from reportlab import platypus +from openerp.report.render.rml2pdf import utils +from openerp.report.render.rml2pdf import color +import os +import logging +from lxml import etree +import base64 +from reportlab.platypus.doctemplate import ActionFlowable +from openerp.tools.safe_eval import safe_eval as eval +from reportlab.lib.units import inch,cm,mm +from openerp.tools.misc import file_open +from reportlab.pdfbase import pdfmetrics +from reportlab.lib.pagesizes import A4, letter + +try: + from cStringIO import StringIO + _hush_pyflakes = [ StringIO ] +except ImportError: + from StringIO import StringIO + +_logger = logging.getLogger(__name__) + +encoding = 'utf-8' + +def _open_image(filename, path=None): + """Attempt to open a binary file and return the descriptor + """ + if os.path.isfile(filename): + return open(filename, 'rb') + for p in (path or []): + if p and os.path.isabs(p): + fullpath = os.path.join(p, filename) + if os.path.isfile(fullpath): + return open(fullpath, 'rb') + try: + if p: + fullpath = os.path.join(p, filename) + else: + fullpath = filename + return file_open(fullpath) + except IOError: + pass + raise IOError("File %s cannot be found in image path" % filename) + +class NumberedCanvas(canvas.Canvas): + def __init__(self, *args, **kwargs): + canvas.Canvas.__init__(self, *args, **kwargs) + self._codes = [] + self._flag=False + self._pageCount=0 + self._currentPage =0 + self._pageCounter=0 + self.pages={} + + def showPage(self): + self._currentPage +=1 + if not self._flag: + self._pageCount += 1 + else: + self.pages.update({self._currentPage:self._pageCount}) + self._codes.append({'code': self._code, 'stack': self._codeStack}) + self._startPage() + self._flag=False + + def pageCount(self): + if self.pages.get(self._pageCounter,False): + self._pageNumber=0 + self._pageCounter +=1 + key=self._pageCounter + if not self.pages.get(key,False): + while not self.pages.get(key,False): + key += 1 + self.setFont("Helvetica", 8) + self.drawRightString((self._pagesize[0]-30), (self._pagesize[1]-40), + " %(this)i / %(total)i" % { + 'this': self._pageNumber+1, + 'total': self.pages.get(key,False), + } + ) + + def save(self): + """add page info to each page (page x of y)""" + # reset page counter + self._pageNumber = 0 + for code in self._codes: + self._code = code['code'] + self._codeStack = code['stack'] + self.pageCount() + canvas.Canvas.showPage(self) +# self.restoreState() + self._doc.SaveToFile(self._filename, self) + +class PageCount(platypus.Flowable): + def __init__(self, story_count=0): + platypus.Flowable.__init__(self) + self.story_count = story_count + + def draw(self): + self.canv.beginForm("pageCount%d" % self.story_count) + self.canv.setFont("Helvetica", utils.unit_get(str(8))) + self.canv.drawString(0, 0, str(self.canv.getPageNumber())) + self.canv.endForm() + +class PageReset(platypus.Flowable): + def draw(self): + self.canv._doPageReset = True +class PageReset(platypus.Flowable): + def draw(self): + self.canv._doPageReset = True + +class _rml_styles(object,): + def __init__(self, nodes, localcontext): + self.localcontext = localcontext + self.styles = {} + self.styles_obj = {} + self.names = {} + self.table_styles = {} + self.default_style = reportlab.lib.styles.getSampleStyleSheet() + + for node in nodes: + for style in node.findall('blockTableStyle'): + self.table_styles[style.get('id')] = self._table_style_get(style) + for style in node.findall('paraStyle'): + sname = style.get('name') + self.styles[sname] = self._para_style_update(style) + + self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname]) + + for variable in node.findall('initialize'): + for name in variable.findall('name'): + self.names[ name.get('id')] = name.get('value') + + def _para_style_update(self, node): + data = {} + for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']: + if node.get(attr): + data[attr] = color.get(node.get(attr)) + for attr in ['fontName', 'bulletFontName', 'bulletText']: + if node.get(attr): + data[attr] = node.get(attr) + for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', + 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading', + 'borderWidth','borderPadding','borderRadius']: + if node.get(attr): + data[attr] = utils.unit_get(node.get(attr)) + if node.get('alignment'): + align = { + 'right':reportlab.lib.enums.TA_RIGHT, + 'center':reportlab.lib.enums.TA_CENTER, + 'justify':reportlab.lib.enums.TA_JUSTIFY + } + data['alignment'] = align.get(node.get('alignment').lower(), reportlab.lib.enums.TA_LEFT) + return data + + def _table_style_get(self, style_node): + styles = [] + for node in style_node: + start = utils.tuple_int_get(node, 'start', (0,0) ) + stop = utils.tuple_int_get(node, 'stop', (-1,-1) ) + if node.tag=='blockValign': + styles.append(('VALIGN', start, stop, str(node.get('value')))) + elif node.tag=='blockFont': + styles.append(('FONT', start, stop, str(node.get('name')))) + elif node.tag=='blockTextColor': + styles.append(('TEXTCOLOR', start, stop, color.get(str(node.get('colorName'))))) + elif node.tag=='blockLeading': + styles.append(('LEADING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockAlignment': + styles.append(('ALIGNMENT', start, stop, str(node.get('value')))) + elif node.tag=='blockSpan': + styles.append(('SPAN', start, stop)) + elif node.tag=='blockLeftPadding': + styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockRightPadding': + styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockTopPadding': + styles.append(('TOPPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockBottomPadding': + styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockBackground': + styles.append(('BACKGROUND', start, stop, color.get(node.get('colorName')))) + if node.get('size'): + styles.append(('FONTSIZE', start, stop, utils.unit_get(node.get('size')))) + elif node.tag=='lineStyle': + kind = node.get('kind') + kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ] + assert kind in kind_list + thick = 1 + if node.get('thickness'): + thick = float(node.get('thickness')) + styles.append((kind, start, stop, thick, color.get(node.get('colorName')))) + return platypus.tables.TableStyle(styles) + + def para_style_get(self, node): + style = False + sname = node.get('style') + if sname: + if sname in self.styles_obj: + style = self.styles_obj[sname] + else: + _logger.warning('Warning: style not found, %s - setting default!\n' % (node.get('style'),) ) + if not style: + style = self.default_style['Normal'] + para_update = self._para_style_update(node) + if para_update: + # update style only is necessary + style = copy.deepcopy(style) + style.__dict__.update(para_update) + return style + +class _rml_doc(object): + def __init__(self, node, localcontext=None, images=None, path='.', title=None): + if images is None: + images = {} + if localcontext is None: + localcontext = {} + self.localcontext = localcontext + self.etree = node + self.filename = self.etree.get('filename') + self.images = images + self.path = path + self.title = title + + def docinit(self, els): + from reportlab.lib.fonts import addMapping + from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase.ttfonts import TTFont + + for node in els: + for font in node.findall('registerFont'): + name = font.get('fontName').encode('ascii') + fname = font.get('fontFile').encode('ascii') + if name not in pdfmetrics._fonts: + pdfmetrics.registerFont(TTFont(name, fname)) + addMapping(name, 0, 0, name) #normal + addMapping(name, 0, 1, name) #italic + addMapping(name, 1, 0, name) #bold + addMapping(name, 1, 1, name) #italic and bold + + def setTTFontMapping(self,face, fontname, filename, mode='all'): + from reportlab.lib.fonts import addMapping + from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase.ttfonts import TTFont + + if fontname not in pdfmetrics._fonts: + pdfmetrics.registerFont(TTFont(fontname, filename)) + if mode == 'all': + addMapping(face, 0, 0, fontname) #normal + addMapping(face, 0, 1, fontname) #italic + addMapping(face, 1, 0, fontname) #bold + addMapping(face, 1, 1, fontname) #italic and bold + elif (mode== 'normal') or (mode == 'regular'): + addMapping(face, 0, 0, fontname) #normal + elif mode == 'italic': + addMapping(face, 0, 1, fontname) #italic + elif mode == 'bold': + addMapping(face, 1, 0, fontname) #bold + elif mode == 'bolditalic': + addMapping(face, 1, 1, fontname) #italic and bold + + def _textual_image(self, node): + rc = '' + for n in node: + rc +=( etree.tostring(n) or '') + n.tail + return base64.decodestring(node.tostring()) + + def _images(self, el): + result = {} + for node in el.findall('.//image'): + rc =( node.text or '') + result[node.get('name')] = base64.decodestring(rc) + return result + + def render(self, out): + el = self.etree.findall('.//docinit') + if el: + self.docinit(el) + + el = self.etree.findall('.//stylesheet') + self.styles = _rml_styles(el,self.localcontext) + + el = self.etree.findall('.//images') + if el: + self.images.update( self._images(el[0]) ) + + el = self.etree.findall('.//template') + if len(el): + pt_obj = _rml_template(self.localcontext, out, el[0], self, images=self.images, path=self.path, title=self.title) + el = utils._child_get(self.etree, self, 'story') + pt_obj.render(el) + else: + self.canvas = canvas.Canvas(out) + pd = self.etree.find('pageDrawing')[0] + pd_obj = _rml_canvas(self.canvas, self.localcontext, None, self, self.images, path=self.path, title=self.title) + pd_obj.render(pd) + + self.canvas.showPage() + self.canvas.save() + +class _rml_canvas(object): + def __init__(self, canvas, localcontext, doc_tmpl=None, doc=None, images=None, path='.', title=None): + if images is None: + images = {} + self.localcontext = localcontext + self.canvas = canvas + self.styles = doc.styles + self.doc_tmpl = doc_tmpl + self.doc = doc + self.images = images + self.path = path + self.title = title + if self.title: + self.canvas.setTitle(self.title) + + def _textual(self, node, x=0, y=0): + text = node.text and node.text.encode('utf-8') or '' + rc = utils._process_text(self, text) + for n in node: + if n.tag == 'seq': + from reportlab.lib.sequencer import getSequencer + seq = getSequencer() + rc += str(seq.next(n.get('id'))) + if n.tag == 'pageCount': + if x or y: + self.canvas.translate(x,y) + self.canvas.doForm('pageCount%s' % (self.canvas._storyCount,)) + if x or y: + self.canvas.translate(-x,-y) + if n.tag == 'pageNumber': + rc += str(self.canvas.getPageNumber()) + rc += utils._process_text(self, n.tail) + return rc.replace('\n','') + + def _drawString(self, node): + v = utils.attr_get(node, ['x','y']) + text=self._textual(node, **v) + text = utils.xml2str(text) + self.canvas.drawString(text=text, **v) + + def _drawCenteredString(self, node): + v = utils.attr_get(node, ['x','y']) + text=self._textual(node, **v) + text = utils.xml2str(text) + self.canvas.drawCentredString(text=text, **v) + + def _drawRightString(self, node): + v = utils.attr_get(node, ['x','y']) + text=self._textual(node, **v) + text = utils.xml2str(text) + self.canvas.drawRightString(text=text, **v) + + def _rect(self, node): + if node.get('round'): + self.canvas.roundRect(radius=utils.unit_get(node.get('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) + else: + self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) + + def _ellipse(self, node): + x1 = utils.unit_get(node.get('x')) + x2 = utils.unit_get(node.get('width')) + y1 = utils.unit_get(node.get('y')) + y2 = utils.unit_get(node.get('height')) + + self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + + def _curves(self, node): + line_str = node.text.split() + lines = [] + while len(line_str)>7: + self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]]) + line_str = line_str[8:] + + def _lines(self, node): + line_str = node.text.split() + lines = [] + while len(line_str)>3: + lines.append([utils.unit_get(l) for l in line_str[0:4]]) + line_str = line_str[4:] + self.canvas.lines(lines) + + def _grid(self, node): + xlist = [utils.unit_get(s) for s in node.get('xs').split(',')] + ylist = [utils.unit_get(s) for s in node.get('ys').split(',')] + + self.canvas.grid(xlist, ylist) + + def _translate(self, node): + dx = utils.unit_get(node.get('dx')) or 0 + dy = utils.unit_get(node.get('dy')) or 0 + self.canvas.translate(dx,dy) + + def _circle(self, node): + self.canvas.circle(x_cen=utils.unit_get(node.get('x')), y_cen=utils.unit_get(node.get('y')), r=utils.unit_get(node.get('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + + def _place(self, node): + flows = _rml_flowable(self.doc, self.localcontext, images=self.images, path=self.path, title=self.title).render(node) + infos = utils.attr_get(node, ['x','y','width','height']) + + infos['y']+=infos['height'] + for flow in flows: + w,h = flow.wrap(infos['width'], infos['height']) + if w<=infos['width'] and h<=infos['height']: + infos['y']-=h + flow.drawOn(self.canvas,infos['x'],infos['y']) + infos['height']-=h + else: + raise ValueError("Not enough space") + + def _line_mode(self, node): + ljoin = {'round':1, 'mitered':0, 'bevelled':2} + lcap = {'default':0, 'round':1, 'square':2} + + if node.get('width'): + self.canvas.setLineWidth(utils.unit_get(node.get('width'))) + if node.get('join'): + self.canvas.setLineJoin(ljoin[node.get('join')]) + if node.get('cap'): + self.canvas.setLineCap(lcap[node.get('cap')]) + if node.get('miterLimit'): + self.canvas.setDash(utils.unit_get(node.get('miterLimit'))) + if node.get('dash'): + dashes = node.get('dash').split(',') + for x in range(len(dashes)): + dashes[x]=utils.unit_get(dashes[x]) + self.canvas.setDash(node.get('dash').split(',')) + + def _image(self, node): + import urllib + import urlparse + from reportlab.lib.utils import ImageReader + nfile = node.get('file') + if not nfile: + if node.get('name'): + image_data = self.images[node.get('name')] + _logger.debug("Image %s used", node.get('name')) + s = StringIO(image_data) + else: + newtext = node.text + if self.localcontext: + res = utils._regex.findall(newtext) + for key in res: + newtext = eval(key, {}, self.localcontext) or '' + image_data = None + if newtext: + image_data = base64.decodestring(newtext) + if image_data: + s = StringIO(image_data) + else: + _logger.debug("No image data!") + return False + else: + if nfile in self.images: + s = StringIO(self.images[nfile]) + else: + try: + up = urlparse.urlparse(str(nfile)) + except ValueError: + up = False + if up and up.scheme: + # RFC: do we really want to open external URLs? + # Are we safe from cross-site scripting or attacks? + _logger.debug("Retrieve image from %s", nfile) + u = urllib.urlopen(str(nfile)) + s = StringIO(u.read()) + else: + _logger.debug("Open image file %s ", nfile) + s = _open_image(nfile, path=self.path) + try: + img = ImageReader(s) + (sx,sy) = img.getSize() + _logger.debug("Image is %dx%d", sx, sy) + args = { 'x': 0.0, 'y': 0.0, 'mask': 'auto'} + for tag in ('width','height','x','y'): + if node.get(tag): + args[tag] = utils.unit_get(node.get(tag)) + if ('width' in args) and (not 'height' in args): + args['height'] = sy * args['width'] / sx + elif ('height' in args) and (not 'width' in args): + args['width'] = sx * args['height'] / sy + elif ('width' in args) and ('height' in args): + if (float(args['width'])/args['height'])>(float(sx)>sy): + args['width'] = sx * args['height'] / sy + else: + args['height'] = sy * args['width'] / sx + self.canvas.drawImage(img, **args) + finally: + s.close() +# self.canvas._doc.SaveToFile(self.canvas._filename, self.canvas) + + def _path(self, node): + self.path = self.canvas.beginPath() + self.path.moveTo(**utils.attr_get(node, ['x','y'])) + for n in utils._child_get(node, self): + if not n.text : + if n.tag=='moveto': + vals = utils.text_get(n).split() + self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1])) + elif n.tag=='curvesto': + vals = utils.text_get(n).split() + while len(vals)>5: + pos=[] + while len(pos)<6: + pos.append(utils.unit_get(vals.pop(0))) + self.path.curveTo(*pos) + elif n.text: + data = n.text.split() # Not sure if I must merge all TEXT_NODE ? + while len(data)>1: + x = utils.unit_get(data.pop(0)) + y = utils.unit_get(data.pop(0)) + self.path.lineTo(x,y) + if (not node.get('close')) or utils.bool_get(node.get('close')): + self.path.close() + self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + + def setFont(self, node): + fontname = node.get('name') + if fontname not in pdfmetrics.getRegisteredFontNames()\ + or fontname not in pdfmetrics.standardFonts: + # let reportlab attempt to find it + try: + pdfmetrics.getFont(fontname) + except Exception: + _logger.debug('Could not locate font %s, substituting default: %s', + fontname, + self.canvas._fontname) + fontname = self.canvas._fontname + return self.canvas.setFont(fontname, utils.unit_get(node.get('size'))) + + def render(self, node): + tags = { + 'drawCentredString': self._drawCenteredString, + 'drawRightString': self._drawRightString, + 'drawString': self._drawString, + 'rect': self._rect, + 'ellipse': self._ellipse, + 'lines': self._lines, + 'grid': self._grid, + 'curves': self._curves, + 'fill': lambda node: self.canvas.setFillColor(color.get(node.get('color'))), + 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.get('color'))), + 'setFont': self.setFont , + 'place': self._place, + 'circle': self._circle, + 'lineMode': self._line_mode, + 'path': self._path, + 'rotate': lambda node: self.canvas.rotate(float(node.get('degrees'))), + 'translate': self._translate, + 'image': self._image + } + for n in utils._child_get(node, self): + if n.tag in tags: + tags[n.tag](n) + +class _rml_draw(object): + def __init__(self, localcontext, node, styles, images=None, path='.', title=None): + if images is None: + images = {} + self.localcontext = localcontext + self.node = node + self.styles = styles + self.canvas = None + self.images = images + self.path = path + self.canvas_title = title + + def render(self, canvas, doc): + canvas.saveState() + cnv = _rml_canvas(canvas, self.localcontext, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title) + cnv.render(self.node) + canvas.restoreState() + +class _rml_Illustration(platypus.flowables.Flowable): + def __init__(self, node, localcontext, styles, self2): + self.localcontext = (localcontext or {}).copy() + self.node = node + self.styles = styles + self.width = utils.unit_get(node.get('width')) + self.height = utils.unit_get(node.get('height')) + self.self2 = self2 + def wrap(self, *args): + return self.width, self.height + def draw(self): + drw = _rml_draw(self.localcontext ,self.node,self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title) + drw.render(self.canv, None) + +class _rml_flowable(object): + def __init__(self, doc, localcontext, images=None, path='.', title=None): + if images is None: + images = {} + self.localcontext = localcontext + self.doc = doc + self.styles = doc.styles + self.images = images + self.path = path + self.title = title + + def _textual(self, node): + rc1 = utils._process_text(self, node.text or '') + for n in utils._child_get(node,self): + txt_n = copy.deepcopy(n) + for key in txt_n.attrib.keys(): + if key in ('rml_except', 'rml_loop', 'rml_tag'): + del txt_n.attrib[key] + if not n.tag == 'bullet': + txt_n.text = utils.xml2str(self._textual(n)) + txt_n.tail = n.tail and utils.xml2str(utils._process_text(self, n.tail.replace('\n',''))) or '' + rc1 += etree.tostring(txt_n) + return rc1 + + def _table(self, node): + children = utils._child_get(node,self,'tr') + if not children: + return None + length = 0 + colwidths = None + rowheights = None + data = [] + styles = [] + posy = 0 + for tr in children: + paraStyle = None + if tr.get('style'): + st = copy.deepcopy(self.styles.table_styles[tr.get('style')]) + for si in range(len(st._cmds)): + s = list(st._cmds[si]) + s[1] = (s[1][0],posy) + s[2] = (s[2][0],posy) + st._cmds[si] = tuple(s) + styles.append(st) + if tr.get('paraStyle'): + paraStyle = self.styles.styles[tr.get('paraStyle')] + data2 = [] + posx = 0 + for td in utils._child_get(tr, self,'td'): + if td.get('style'): + st = copy.deepcopy(self.styles.table_styles[td.get('style')]) + for s in st._cmds: + s[1][1] = posy + s[2][1] = posy + s[1][0] = posx + s[2][0] = posx + styles.append(st) + if td.get('paraStyle'): + # TODO: merge styles + paraStyle = self.styles.styles[td.get('paraStyle')] + posx += 1 + + flow = [] + for n in utils._child_get(td, self): + if n.tag == etree.Comment: + n.text = '' + continue + fl = self._flowable(n, extra_style=paraStyle) + if isinstance(fl,list): + flow += fl + else: + flow.append( fl ) + + if not len(flow): + flow = self._textual(td) + data2.append( flow ) + if len(data2)>length: + length=len(data2) + for ab in data: + while len(ab) tag: + # - we reset page number to 0 + # - we add an new PageCount flowable (relative to the current + # story number), but not for NumeredCanvas at is handle page + # count itself) + # NOTE: _rml_template render() method add a PageReset flowable at end + # of each story, so we're sure to pass here at least once per story. + if not isinstance(self.canv, NumberedCanvas): + self.handle_flowable([ PageCount(story_count=self.canv._storyCount) ]) + self.canv._pageCount = self.page + self.page = 0 + self.canv._flag = True + self.canv._pageNumber = 0 + self.canv._doPageReset = False + self.canv._storyCount += 1 + +class _rml_template(object): + def __init__(self, localcontext, out, node, doc, images=None, path='.', title=None): + if images is None: + images = {} + if not localcontext: + localcontext={'internal_header':True} + self.localcontext = localcontext + self.images= images + self.path = path + self.title = title + + pagesize_map = {'a4': A4, + 'us_letter': letter + } + pageSize = (841.8897637795275, 595.275590551181) + self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','rotation':'int','title':'str','author':'str'})) + self.page_templates = [] + self.styles = doc.styles + self.doc = doc + self.image=[] + pts = node.findall('pageTemplate') + for pt in pts: + frames = [] + for frame_el in pt.findall('frame'): + frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) ) + if utils.attr_get(frame_el, ['last']): + frame.lastFrame = True + frames.append( frame ) + try : + gr = pt.findall('pageGraphics')\ + or pt[1].findall('pageGraphics') + except Exception: # FIXME: be even more specific, perhaps? + gr='' + if len(gr): +# self.image=[ n for n in utils._child_get(gr[0], self) if n.tag=='image' or not self.localcontext] + drw = _rml_draw(self.localcontext,gr[0], self.doc, images=images, path=self.path, title=self.title) + self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) )) + else: + drw = _rml_draw(self.localcontext,node,self.doc,title=self.title) + self.page_templates.append( platypus.PageTemplate(frames=frames,onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) )) + self.doc_tmpl.addPageTemplates(self.page_templates) + + def render(self, node_stories): + if self.localcontext and not self.localcontext.get('internal_header',False): + del self.localcontext['internal_header'] + fis = [] + r = _rml_flowable(self.doc,self.localcontext, images=self.images, path=self.path, title=self.title) + story_cnt = 0 + for node_story in node_stories: + if story_cnt > 0: + fis.append(platypus.PageBreak()) + fis += r.render(node_story) + # Reset Page Number with new story tag + fis.append(PageReset()) + story_cnt += 1 + if self.localcontext and self.localcontext.get('internal_header',False): + self.doc_tmpl.afterFlowable(fis) + self.doc_tmpl.build(fis,canvasmaker=NumberedCanvas) + else: + self.doc_tmpl.build(fis) + +def parseNode(rml, localcontext=None, fout=None, images=None, path='.', title=None): + node = etree.XML(rml) + r = _rml_doc(node, localcontext, images, path, title=title) + #try to override some font mappings + try: + from customfonts import SetCustomFonts + SetCustomFonts(r) + except ImportError: + # means there is no custom fonts mapping in this system. + pass + except Exception: + _logger.warning('Cannot set font mapping', exc_info=True) + pass + fp = StringIO() + r.render(fp) + return fp.getvalue() + +def parseString(rml, localcontext=None, fout=None, images=None, path='.', title=None): + node = etree.XML(rml) + r = _rml_doc(node, localcontext, images, path, title=title) + + #try to override some font mappings + try: + from customfonts import SetCustomFonts + SetCustomFonts(r) + except Exception: + pass + + if fout: + fp = file(fout,'wb') + r.render(fp) + fp.close() + return fout + else: + fp = StringIO() + r.render(fp) + return fp.getvalue() + +def trml2pdf_help(): + print 'Usage: trml2pdf input.rml >output.pdf' + print 'Render the standard input (RML) and output a PDF file' + sys.exit(0) + +if __name__=="__main__": + if len(sys.argv)>1: + if sys.argv[1]=='--help': + trml2pdf_help() + print parseString(file(sys.argv[1], 'r').read()), + else: + print 'Usage: trml2pdf input.rml >output.pdf' + print 'Try \'trml2pdf --help\' for more information.' + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/ext/custom-addons/web_printscreen_zb/views/web_printscreen_zb.xml b/ext/custom-addons/web_printscreen_zb/views/web_printscreen_zb.xml new file mode 100644 index 00000000..0386b7d2 --- /dev/null +++ b/ext/custom-addons/web_printscreen_zb/views/web_printscreen_zb.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/setup/lib/config_at.py b/setup/lib/config_at.py index e46f6261..64e727b3 100644 --- a/setup/lib/config_at.py +++ b/setup/lib/config_at.py @@ -117,7 +117,8 @@ class Config(): 'cam_custom', 'cam_reports', 'account_cancel', - 'cam_invoice_skonto', + 'cam_invoice_skonto', + #'web_printscreen_zb', #'crm', #'sale', #'cam_hr_overtime',