Fixed #551: Used unicode font for circle in ballot pdf. Removed pillow.

That fixed also #1140 (Server error when trying to view ballot paper pdf)
This commit is contained in:
Emanuel Schütze 2014-03-07 00:17:25 +01:00
parent d0a0a09d7b
commit b5fb83c026
11 changed files with 92 additions and 58 deletions

View File

@ -15,6 +15,7 @@ Participant:
Files: Files:
- Fixed error when a file was removed from filesystem. - Fixed error when a file was removed from filesystem.
Other: Other:
- Used unicode font for circle in ballot pdf. Removed pillow dependency package.
- Fixed http status code when requesting a non-existing static page using - Fixed http status code when requesting a non-existing static page using
Tordado web server. Tordado web server.
- Fixed error in main script when using other database engine. - Fixed error in main script when using other database engine.

View File

@ -84,8 +84,7 @@ portable version you should observe the following install steps.*
<http://www.python.org/ftp/python/2.7.5/python-2.7.5.msi>`_. Note <http://www.python.org/ftp/python/2.7.5/python-2.7.5.msi>`_. Note
that the 32-bit MSI installer is required even on a 64-bit Windows that the 32-bit MSI installer is required even on a 64-bit Windows
system. If you use the 64-bit MSI installer, step 3 of this system. If you use the 64-bit MSI installer, step 3 of this
instruction will fail unless you installed the packages reportlab and instruction will fail unless you installed the packages reportlab manually.
pillow manually.
b. Add python directories to PATH (via Control Panel > System > b. Add python directories to PATH (via Control Panel > System >
Advanced): ``";C:\\Python27;C:\\Python27\\Scripts"``. Note that the path Advanced): ``";C:\\Python27;C:\\Python27\\Scripts"``. Note that the path
@ -260,9 +259,6 @@ OpenSlides uses the following projects or parts of them:
* `pdf.js <http://mozilla.github.io/pdf.js/>`_, License: Apache License v2.0 * `pdf.js <http://mozilla.github.io/pdf.js/>`_, License: Apache License v2.0
* `Pillow <https://github.com/python-imaging/Pillow/>`_, License: Standard
PIL License
* `ReportLab <http://www.reportlab.com/software/opensource/rl-toolkit/>`_, * `ReportLab <http://www.reportlab.com/software/opensource/rl-toolkit/>`_,
License: BSD License: BSD

View File

@ -7,7 +7,7 @@ How to create a new portable Windows distribution of OpenSlides:
2. Install all required python packages (see requirements_production.txt): 2. Install all required python packages (see requirements_production.txt):
easy_install -Z django django-mptt beautifulsoup4 bleach natsort pillow reportlab sockjs_tornado tornado django-haystack whoosh easy_install -Z django django-mptt beautifulsoup4 bleach natsort reportlab sockjs_tornado tornado django-haystack whoosh
3. Install pywin32 from binary installer: 3. Install pywin32 from binary installer:
@ -34,3 +34,4 @@ How to create a new portable Windows distribution of OpenSlides:
NOTE: Creating the portable Windows distribution of OpenSlides is not possible NOTE: Creating the portable Windows distribution of OpenSlides is not possible
if Python is installed in 64-bit(!) version. if Python is installed in 64-bit(!) version.

View File

@ -72,9 +72,6 @@ SITE_PACKAGES = {
"sgmlop.pyd", "sgmlop.pyd",
], ],
}, },
"pillow": {
"copy": ["PIL"],
},
"sockjs-tornado": { "sockjs-tornado": {
"copy": ["sockjs"], "copy": ["sockjs"],
}, },

View File

@ -1,8 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os
from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import redirect from django.shortcuts import redirect
@ -507,8 +504,7 @@ class AssignmentPollPDF(PDFView):
pdf_document.build(story) pdf_document.build(story)
def append_to_pdf(self, story): def append_to_pdf(self, story):
imgpath = os.path.join(settings.SITE_ROOT, 'static/img/circle.png') circle = "*" # = Unicode Character 'HEAVY LARGE CIRCLE' (U+2B55)
circle = "<img src='%s' width='15' height='15'/>&nbsp;&nbsp;" % imgpath
cell = [] cell = []
cell.append(Spacer(0, 0.8 * cm)) cell.append(Spacer(0, 0.8 * cm))
cell.append(Paragraph( cell.append(Paragraph(
@ -548,26 +544,44 @@ class AssignmentPollPDF(PDFView):
number = int(ballot_papers_number) number = int(ballot_papers_number)
number = max(1, number) number = max(1, number)
# Choose kind of ballot paper counter = 0
if self.poll.yesnoabstain: cellcolumnA = []
# Choose kind of ballot paper (YesNoAbstain or Yes)
if self.poll.yesnoabstain: # YesNoAbstain ballot: max 27 candidates
for option in options: for option in options:
counter += 1
candidate = option.candidate candidate = option.candidate
cell.append(Paragraph( cell.append(Paragraph(
candidate.clean_name, stylesheet['Ballot_option_name'])) candidate.clean_name, stylesheet['Ballot_option_name_YNA']))
if candidate.name_suffix: if candidate.name_suffix:
cell.append(Paragraph( cell.append(Paragraph(
"(%s)" % candidate.name_suffix, "(%s)" % candidate.name_suffix,
stylesheet['Ballot_option_group'])) stylesheet['Ballot_option_suffix_YNA']))
else: else:
cell.append(Paragraph( cell.append(Paragraph(
"&nbsp;", stylesheet['Ballot_option_group'])) "&nbsp;", stylesheet['Ballot_option_suffix_YNA']))
cell.append(Paragraph( cell.append(Paragraph("<font name='circlefont' size='15'>%(circle)s</font> \
circle + _("Yes") + "&nbsp; " * 3 + circle <font name='Ubuntu'>%(yes)s &nbsp;&nbsp;&nbsp;</font> \
+ _("No") + "&nbsp; " * 3 + circle + _("Abstention"), <font name='circlefont' size='15'>%(circle)s</font> \
stylesheet['Ballot_option_YNA'])) <font name='Ubuntu'>%(no)s &nbsp;&nbsp;&nbsp;</font> \
<font name='circlefont' size='15'>%(circle)s</font> \
<font name='Ubuntu'>%(abstain)s</font>" %
{'circle': circle,
'yes': _("Yes"),
'no': _("No"),
'abstain': _("Abstention")},
stylesheet['Ballot_option_circle_YNA']))
if counter == 13:
cellcolumnA = cell
cell = []
cell.append(Spacer(0, 1.3 * cm))
# print ballot papers # print ballot papers
for user in xrange(number / 2): for user in xrange(number / 2):
data.append([cell, cell]) if len(options) > 13:
data.append([cellcolumnA, cell])
else:
data.append([cell, cell])
rest = number % 2 rest = number % 2
if rest: if rest:
data.append([cell, '']) data.append([cell, ''])
@ -577,22 +591,31 @@ class AssignmentPollPDF(PDFView):
t = Table(data, 10.5 * cm, 14.84 * cm) t = Table(data, 10.5 * cm, 14.84 * cm)
else: else:
t = Table(data, 10.5 * cm, 29.7 * cm) t = Table(data, 10.5 * cm, 29.7 * cm)
else: else: # Yes ballot: max 46 candidates
for option in options: for option in options:
counter += 1
candidate = option.candidate candidate = option.candidate
cell.append(Paragraph( cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> \
circle + candidate.clean_name, <font name='Ubuntu'>%s</font>" %
stylesheet['Ballot_option_name'])) (circle, candidate.clean_name), stylesheet['Ballot_option_name']))
if candidate.name_suffix: if candidate.name_suffix:
cell.append(Paragraph( cell.append(Paragraph(
"(%s)" % candidate.name_suffix, "(%s)" % candidate.name_suffix,
stylesheet['Ballot_option_group_right'])) stylesheet['Ballot_option_suffix']))
else: else:
cell.append(Paragraph( cell.append(Paragraph(
"&nbsp;", stylesheet['Ballot_option_group_right'])) "&nbsp;", stylesheet['Ballot_option_suffix']))
if counter == 22:
cellcolumnA = cell
cell = []
cell.append(Spacer(0, 0.75 * cm))
# print ballot papers # print ballot papers
for user in xrange(number / 2): for user in xrange(number / 2):
data.append([cell, cell]) if len(options) > 22:
data.append([cellcolumnA, cell])
else:
data.append([cell, cell])
rest = number % 2 rest = number % 2
if rest: if rest:
data.append([cell, '']) data.append([cell, ''])

View File

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from operator import attrgetter from operator import attrgetter
import os
import random import random
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from natsort import natsorted from natsort import natsorted
from reportlab.lib import colors from reportlab.lib import colors
@ -261,17 +259,19 @@ def all_motion_cover(pdf, motions):
def motion_poll_to_pdf(pdf, poll): def motion_poll_to_pdf(pdf, poll):
imgpath = os.path.join(settings.SITE_ROOT, 'static/img/circle.png') circle = "*" # = Unicode Character 'HEAVY LARGE CIRCLE' (U+2B55)
circle = "<img src='%s' width='15' height='15'/>&nbsp;&nbsp;" % imgpath
cell = [] cell = []
cell.append(Spacer(0, 0.8 * cm)) cell.append(Spacer(0, 0.8 * cm))
cell.append(Paragraph(_("Motion No. %s") % poll.motion.identifier, stylesheet['Ballot_title'])) cell.append(Paragraph(_("Motion No. %s") % poll.motion.identifier, stylesheet['Ballot_title']))
cell.append(Paragraph(poll.motion.title, stylesheet['Ballot_subtitle'])) cell.append(Paragraph(poll.motion.title, stylesheet['Ballot_subtitle']))
cell.append(Paragraph(_("%d. Vote") % poll.poll_number, stylesheet['Ballot_description'])) cell.append(Paragraph(_("%d. Vote") % poll.poll_number, stylesheet['Ballot_description']))
cell.append(Spacer(0, 0.5 * cm)) cell.append(Spacer(0, 0.5 * cm))
cell.append(Paragraph(circle + unicode(_("Yes")), stylesheet['Ballot_option'])) cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> <font name='Ubuntu'>%s</font>"
cell.append(Paragraph(circle + unicode(_("No")), stylesheet['Ballot_option'])) % (circle, unicode(_("Yes"))), stylesheet['Ballot_option']))
cell.append(Paragraph(circle + unicode(_("Abstention")), stylesheet['Ballot_option'])) cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> <font name='Ubuntu'>%s</font>"
% (circle, unicode(_("No"))), stylesheet['Ballot_option']))
cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> <font name='Ubuntu'>%s</font>"
% (circle, unicode(_("Abstention"))), stylesheet['Ballot_option']))
data = [] data = []
# get ballot papers config values # get ballot papers config values
ballot_papers_selection = config["motion_pdf_ballot_papers_selection"] ballot_papers_selection = config["motion_pdf_ballot_papers_selection"]

View File

@ -0,0 +1,16 @@
This directory contains truetype fonts which are used for PDF generation in OpenSlides.
They are registered in the file 'openslides/utils/pdf.py'.
The Ubuntu font is available in three styles: Regular, Bold and Italic.
If you want to use your own font just replace the Ubuntu font with your truetype font files.
Or you have to change the filename in the registerFont function in pdf.py. Please note
that the font name 'Ubuntu' is already used in some stylesheets in the same file.
The font file circle.ttf contains only the unicode character 'HEAVY LARGE CIRCLE' (U+2B55)
which is extracted from the free unicode font "Quivira 4.0" [1] with the freeware
OpenType font editor "Type light" [2]. The circle glyph is mapped to the '*' character
for using in OpenSlides PDF ballot papers of motions and elections.
[1] http://www.quivira-font.com/
[2] http://www.cr8software.net/typelight.html

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -22,6 +22,8 @@ pdfmetrics.registerFont(TTFont(
'Ubuntu-Bold', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-B.ttf'))) 'Ubuntu-Bold', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-B.ttf')))
pdfmetrics.registerFont(TTFont( pdfmetrics.registerFont(TTFont(
'Ubuntu-Italic', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-RI.ttf'))) 'Ubuntu-Italic', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-RI.ttf')))
pdfmetrics.registerFont(TTFont(
'circlefont', path_join(settings.SITE_ROOT, 'static/fonts/circle.ttf')))
# set style information # set style information
@ -185,30 +187,29 @@ stylesheet.add(ParagraphStyle(name='Ballot_option',
leading=24, leading=24,
leftIndent=30), leftIndent=30),
) )
stylesheet.add(ParagraphStyle(name='Ballot_option_name_YNA',
parent=stylesheet['Ballot_option'],
leading=14),
)
stylesheet.add(ParagraphStyle(name='Ballot_option_name', stylesheet.add(ParagraphStyle(name='Ballot_option_name',
parent=stylesheet['Normal'], parent=stylesheet['Ballot_option_name_YNA'],
fontSize=12, leading=17),
leading=15,
leftIndent=30),
) )
stylesheet.add(ParagraphStyle(name='Ballot_option_group',
parent=stylesheet['Normal'], stylesheet.add(ParagraphStyle(name='Ballot_option_suffix_YNA',
parent=stylesheet['Ballot_option_name_YNA'],
fontSize=8, fontSize=8,
leading=15, leading=11),
leftIndent=30),
) )
stylesheet.add(ParagraphStyle(name='Ballot_option_YNA', stylesheet.add(ParagraphStyle(name='Ballot_option_suffix',
parent=stylesheet['Normal'], parent=stylesheet['Ballot_option_suffix_YNA'],
fontSize=12,
leading=15,
leftIndent=49,
spaceAfter=18),
)
stylesheet.add(ParagraphStyle(name='Ballot_option_group_right',
parent=stylesheet['Normal'],
fontSize=8,
leading=16, leading=16,
leftIndent=49), leftIndent=48),
)
stylesheet.add(ParagraphStyle(name='Ballot_option_circle_YNA',
parent=stylesheet['Ballot_option_name_YNA'],
leftIndent=48,
spaceAfter=18),
) )
# Password paper stylesheets # Password paper stylesheets
stylesheet.add(ParagraphStyle(name='formfield', stylesheet.add(ParagraphStyle(name='formfield',

View File

@ -5,7 +5,6 @@ bleach>=1.2,<1.3
django-haystack>=2.1,<2.2 django-haystack>=2.1,<2.2
django-mptt>=0.6,<0.7 django-mptt>=0.6,<0.7
natsort>=3.0,<3.1 natsort>=3.0,<3.1
pillow>=2.2,<2.3
reportlab>=2.7,<2.8 reportlab>=2.7,<2.8
sockjs-tornado>=1.0,<1.1 sockjs-tornado>=1.0,<1.1
tornado>=3.1,<3.2 tornado>=3.1,<3.2