[ADD] report: support for dynamic fonts in report

search for TTF file on the system (in typical repositories)
add font field on the company and config wizard, the chosen value will replace the fontName value by the new one

bzr revid: mat@openerp.com-20131003141307-y6rsfooxepfgmofd
This commit is contained in:
Martin Trigaux 2013-10-03 16:13:07 +02:00
commit cd94736f1f
3 changed files with 134 additions and 94 deletions

View File

@ -20,14 +20,15 @@
##############################################################################
import os
import re
import openerp
from openerp import SUPERUSER_ID, tools
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools import image_resize_image
from openerp.report.render.rml2pdf import customfonts
class multi_company_default(osv.osv):
"""
Manage multi company default value
@ -74,7 +75,7 @@ class res_company(osv.osv):
_name = "res.company"
_description = 'Companies'
_order = 'name'
def _get_address_data(self, cr, uid, ids, field_names, arg, context=None):
""" Read the 'address' functional fields. """
result = {}
@ -108,9 +109,12 @@ class res_company(osv.osv):
size = (180, None)
result[record.id] = image_resize_image(record.partner_id.image, size)
return result
def _get_companies_from_partner(self, cr, uid, ids, context=None):
return self.pool['res.company'].search(cr, uid, [('partner_id', 'in', ids)], context=context)
def _get_font(self, cr, uid, context=None):
return sorted(customfonts.RegisterCustomFonts())
_columns = {
'name': fields.related('partner_id', 'name', string='Company Name', size=128, required=True, store=True, type='char'),
@ -124,6 +128,7 @@ class res_company(osv.osv):
'rml_footer': fields.text('Report Footer', help="Footer text displayed at the bottom of all reports."),
'rml_footer_readonly': fields.related('rml_footer', type='text', string='Report Footer', readonly=True),
'custom_footer': fields.boolean('Custom Footer', help="Check this to define the report footer manually. Otherwise it will be filled in automatically."),
'font': fields.selection(_get_font, "Font",help="Set the font into the report header, will be used for every RML report of the company"),
'logo': fields.related('partner_id', 'image', string="Logo", type="binary"),
'logo_web': fields.function(_get_logo_web, string="Logo Web", type="binary", store={
'res.company': (lambda s, c, u, i, x: i, ['partner_id'], 10),
@ -178,6 +183,21 @@ class res_company(osv.osv):
if state_id:
return {'value':{'country_id': self.pool.get('res.country.state').browse(cr, uid, state_id, context).country_id.id }}
return {}
def onchange_font_name(self, cr, uid, ids, font, rml_header, rml_header2, rml_header3, context=None):
""" To change default header style of all <para> and drawstring. """
def _change_header(header,font):
""" Replace default fontname use in header and setfont tag """
default_para = re.sub('fontName.?=.?".*"', 'fontName="%s"'% font,header)
return re.sub('(<setFont.?name.?=.?)(".*?")(.)', '\g<1>"%s"\g<3>'% font,default_para)
return {'value':{
'rml_header': _change_header(rml_header,font),
'rml_header2':_change_header(rml_header2,font),
'rml_header3':_change_header(rml_header3,font)
}}
def on_change_country(self, cr, uid, ids, country_id, context=None):
res = {'domain': {'state_id': []}}
currency_id = self._get_euro(cr, uid, context=context)
@ -374,7 +394,8 @@ class res_company(osv.osv):
'rml_header':_get_header,
'rml_header2': _header2,
'rml_header3': _header3,
'logo':_get_logo
'logo':_get_logo,
'font':'Helvetica',
}
_constraints = [

View File

@ -80,6 +80,7 @@
<field name="custom_footer" on_change="onchange_footer(custom_footer, phone, fax, email, website, vat, company_registry, bank_ids)"/>
<field name="rml_footer" attrs="{'invisible': [('custom_footer','=',False)]}"/>
<field name="rml_footer_readonly" attrs="{'invisible': [('custom_footer','=',True)]}"/>
<field name="font" on_change="onchange_font_name(font, rml_header, rml_header2, rml_header3)"/>
</group>
</page>
<page string="Header/Footer" groups="base.group_no_one">

View File

@ -20,14 +20,13 @@
#
##############################################################################
import glob
import logging
import os
import platform
from reportlab import rl_config
from reportlab.lib import fonts
from reportlab.pdfbase import pdfmetrics, ttfonts
import logging
import os,platform
from openerp.tools import config
# .apidoc title: TTF Font Table
"""This module allows the mapping of some system-available TTF fonts to
the reportlab engine.
@ -38,103 +37,127 @@ should have the same filenames, only need the code below).
Due to an awful configuration that ships with reportlab at many Linux
and Ubuntu distros, we have to override the search path, too.
"""
_fonts_cache = {'registered_fonts': [], 'total_system_fonts': 0}
_logger = logging.getLogger(__name__)
CustomTTFonts = [ ('Helvetica',"DejaVu Sans", "DejaVuSans.ttf", 'normal'),
('Helvetica',"DejaVu Sans Bold", "DejaVuSans-Bold.ttf", 'bold'),
('Helvetica',"DejaVu Sans Oblique", "DejaVuSans-Oblique.ttf", 'italic'),
('Helvetica',"DejaVu Sans BoldOblique", "DejaVuSans-BoldOblique.ttf", 'bolditalic'),
('Times',"Liberation Serif", "LiberationSerif-Regular.ttf", 'normal'),
('Times',"Liberation Serif Bold", "LiberationSerif-Bold.ttf", 'bold'),
('Times',"Liberation Serif Italic", "LiberationSerif-Italic.ttf", 'italic'),
('Times',"Liberation Serif BoldItalic", "LiberationSerif-BoldItalic.ttf", 'bolditalic'),
('Times-Roman',"Liberation Serif", "LiberationSerif-Regular.ttf", 'normal'),
('Times-Roman',"Liberation Serif Bold", "LiberationSerif-Bold.ttf", 'bold'),
('Times-Roman',"Liberation Serif Italic", "LiberationSerif-Italic.ttf", 'italic'),
('Times-Roman',"Liberation Serif BoldItalic", "LiberationSerif-BoldItalic.ttf", 'bolditalic'),
('Courier',"FreeMono", "FreeMono.ttf", 'normal'),
('Courier',"FreeMono Bold", "FreeMonoBold.ttf", 'bold'),
('Courier',"FreeMono Oblique", "FreeMonoOblique.ttf", 'italic'),
('Courier',"FreeMono BoldOblique", "FreeMonoBoldOblique.ttf", 'bolditalic'),
# Sun-ExtA can be downloaded from http://okuc.net/SunWb/
('Sun-ExtA',"Sun-ExtA", "Sun-ExtA.ttf", 'normal'),
# Basic fonts familly included in PDF standart, will always be in the font list
BasePDFFonts = [
('Helvetica', 'Helvetica'),
('Times', 'Times'),
('Courier', 'Courier'),
]
# List of fonts found on the disk
CustomTTFonts = []
TTFSearchPath_Linux = [
# Search path for TTF files, in addition of rl_config.TTFSearchPath
TTFSearchPath = [
'/usr/share/fonts/truetype', # SuSE
'/usr/share/fonts/dejavu', '/usr/share/fonts/liberation', # Fedora, RHEL
'/usr/share/fonts/truetype/*', # Ubuntu,
'/usr/share/fonts/truetype/*','/usr/local/share/fonts' # Ubuntu,
'/usr/share/fonts/TTF/*', # at Mandriva/Mageia
'/usr/share/fonts/TTF', # Arch Linux
]
'/usr/lib/openoffice/share/fonts/truetype/',
'~/.fonts',
'~/.local/share/fonts',
TTFSearchPath_Windows = [
'c:/winnt/fonts',
'c:/windows/fonts'
]
TTFSearchPath_Darwin = [
#mac os X - from
#http://developer.apple.com/technotes/tn/tn2024.html
# mac os X - from
# http://developer.apple.com/technotes/tn/tn2024.html
'~/Library/Fonts',
'/Library/Fonts',
'/Network/Library/Fonts',
'/System/Library/Fonts',
]
TTFSearchPathMap = {
'Darwin': TTFSearchPath_Darwin,
'Windows': TTFSearchPath_Windows,
'Linux': TTFSearchPath_Linux,
}
# windows
'c:/winnt/fonts',
'c:/windows/fonts'
]
# ----- The code below is less distro-specific, please avoid editing! -------
__foundFonts = None
def FindCustomFonts():
"""Fill the __foundFonts list with those filenames, whose fonts
can be found in the reportlab ttf font path.
This process needs only be done once per loading of this module,
it is cached. But, if the system admin adds some font in the
meanwhile, the server must be restarted eventually.
def all_sysfonts_list():
"""
dirpath = []
global __foundFonts
__foundFonts = {}
searchpath = []
if config.get('fonts_search_path'):
searchpath += map(str.strip, config.get('fonts_search_path').split(','))
local_platform = platform.system()
if local_platform in TTFSearchPathMap:
searchpath += TTFSearchPathMap[local_platform]
# Append the original search path of reportlab (at the end)
searchpath += rl_config.TTFSearchPath
This function returns list of font directories of system.
"""
filepath = []
# Perform the search for font files ourselves, as reportlab's
# TTFOpenFile is not very good at it.
for dirglob in searchpath:
dirglob = os.path.expanduser(dirglob)
for dirname in glob.iglob(dirglob):
abp = os.path.abspath(dirname)
if os.path.isdir(abp):
dirpath.append(abp)
searchpath = list(set(TTFSearchPath + rl_config.TTFSearchPath))
for dirname in searchpath:
dirname = os.path.expanduser(dirname)
if os.path.exists(dirname):
for filename in [x for x in os.listdir(dirname) if x.lower().endswith('.ttf')]:
filepath.append(os.path.join(dirname, filename))
return filepath
for k, (name, font, filename, mode) in enumerate(CustomTTFonts):
if filename in __foundFonts:
continue
for d in dirpath:
abs_filename = os.path.join(d, filename)
if os.path.exists(abs_filename):
_logger.debug("Found font %s at %s", filename, abs_filename)
__foundFonts[filename] = abs_filename
break
def init_new_font(familyName, name, font_dir):
return {
'regular':(familyName, name, font_dir, 'regular'),
'italic':(),
'bold':(familyName, name, font_dir, 'bold'),
'bolditalic':(),
}
def RegisterCustomFonts():
"""
This function prepares a list for all system fonts to be registered
in reportlab and returns the updated list with new fonts.
"""
global CustomTTFonts
all_system_fonts = all_sysfonts_list()
if len(all_system_fonts) != _fonts_cache['total_system_fonts']:
font_modes, last_family, registered_font_list, _fonts_cache['registered_fonts'] = {}, "", [], list(BasePDFFonts)
#Prepares a list of registered fonts. Remove such fonts those don't have cmap for Unicode.
for dirname in all_system_fonts:
try:
font_info = ttfonts.TTFontFile(dirname)
if font_info.styleName in ('Regular','Normal','Book','Medium', 'Roman'):
_fonts_cache['registered_fonts'].append((font_info.name, font_info.fullName))
registered_font_list.append((font_info.familyName, font_info.name, dirname, font_info.styleName.lower().replace(" ", "")))
_logger.debug("Found font %s at %s", font_info.name, dirname)
except:
_logger.warning("Could not register Font %s", dirname)
#Prepare font list for mapping.Each font family requires four type of modes(regular,bold,italic,bolditalic).
#If all modes are not found, dummy entries are made for remaining modes.
for familyName, name, font_dir, mode in sorted(registered_font_list):
if not last_family or not font_modes:
last_family = familyName
font_modes = init_new_font(familyName, name, font_dir)
if last_family != familyName:
# new font familly, adding previous to the list of fonts
if not font_modes['italic']:
font_modes['italic'] = font_modes['regular'][:3]+('italic',)
if not font_modes['bolditalic']:
font_modes['bolditalic'] = font_modes['bold'][:3]+('bolditalic',)
CustomTTFonts.extend(font_modes.values())
font_modes = init_new_font(familyName, name, font_dir)
if (mode== 'normal') or (mode == 'regular') or (mode == 'medium') or (mode == 'book') or (mode == 'roman'):
font_modes['regular'] = (familyName, name, font_dir, 'regular')
elif (mode == 'italic') or (mode == 'oblique'):
font_modes['italic'] = (familyName, name, font_dir, 'italic')
elif mode == 'bold':
font_modes['bold'] = (familyName, name, font_dir, 'bold')
elif (mode == 'bolditalic') or (mode == 'boldoblique'):
font_modes['bolditalic'] = (familyName, name, font_dir, 'bolditalic')
last_family = familyName
# add the last one
if font_modes:
if not font_modes['italic']:
font_modes['italic'] = font_modes['regular'][:3]+('italic',)
if not font_modes['bolditalic']:
font_modes['bolditalic'] = font_modes['bold'][:3]+('bolditalic',)
CustomTTFonts.extend(font_modes.values())
_fonts_cache['total_system_fonts'] = len(all_system_fonts)
# remove duplicates
CustomTTFonts = sorted(list(set(CustomTTFonts)))
_fonts_cache['registered_fonts'] = sorted(list(set(_fonts_cache['registered_fonts'])))
return _fonts_cache['registered_fonts']
def SetCustomFonts(rmldoc):
""" Map some font names to the corresponding TTF fonts
@ -144,16 +167,11 @@ def SetCustomFonts(rmldoc):
This function is called once per report, so it should
avoid system-wide processing (cache it, instead).
"""
global __foundFonts
if __foundFonts is None:
FindCustomFonts()
if not _fonts_cache['registered_fonts']:
RegisterCustomFonts()
for name, font, filename, mode in CustomTTFonts:
if os.path.isabs(filename) and os.path.exists(filename):
rmldoc.setTTFontMapping(name, font, filename, mode)
elif filename in __foundFonts:
rmldoc.setTTFontMapping(name, font, __foundFonts[filename], mode)
return True
#eof
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: