diff --git a/CHANGELOG b/CHANGELOG index 8f2f06e50..003a17712 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ Participant: Files: - Fixed error when a file was removed from filesystem. 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 Tordado web server. - Fixed error in main script when using other database engine. diff --git a/README.rst b/README.rst index 6420c928c..c973340b8 100644 --- a/README.rst +++ b/README.rst @@ -84,8 +84,7 @@ portable version you should observe the following install steps.* `_. Note 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 - instruction will fail unless you installed the packages reportlab and - pillow manually. + instruction will fail unless you installed the packages reportlab manually. b. Add python directories to PATH (via Control Panel > System > 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 `_, License: Apache License v2.0 -* `Pillow `_, License: Standard - PIL License - * `ReportLab `_, License: BSD diff --git a/extras/win32-portable/create_portable.txt b/extras/win32-portable/create_portable.txt index 8dfd7e93b..f30d0cbe9 100644 --- a/extras/win32-portable/create_portable.txt +++ b/extras/win32-portable/create_portable.txt @@ -7,7 +7,7 @@ How to create a new portable Windows distribution of OpenSlides: 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: @@ -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 if Python is installed in 64-bit(!) version. + diff --git a/extras/win32-portable/prepare_portable.py b/extras/win32-portable/prepare_portable.py index 921a91de6..303eee5b8 100755 --- a/extras/win32-portable/prepare_portable.py +++ b/extras/win32-portable/prepare_portable.py @@ -72,9 +72,6 @@ SITE_PACKAGES = { "sgmlop.pyd", ], }, - "pillow": { - "copy": ["PIL"], - }, "sockjs-tornado": { "copy": ["sockjs"], }, diff --git a/openslides/assignment/views.py b/openslides/assignment/views.py index 3037394d1..301979679 100644 --- a/openslides/assignment/views.py +++ b/openslides/assignment/views.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -import os - -from django.conf import settings from django.contrib import messages from django.core.urlresolvers import reverse from django.shortcuts import redirect @@ -507,8 +504,7 @@ class AssignmentPollPDF(PDFView): pdf_document.build(story) def append_to_pdf(self, story): - imgpath = os.path.join(settings.SITE_ROOT, 'static/img/circle.png') - circle = "  " % imgpath + circle = "*" # = Unicode Character 'HEAVY LARGE CIRCLE' (U+2B55) cell = [] cell.append(Spacer(0, 0.8 * cm)) cell.append(Paragraph( @@ -548,26 +544,44 @@ class AssignmentPollPDF(PDFView): number = int(ballot_papers_number) number = max(1, number) - # Choose kind of ballot paper - if self.poll.yesnoabstain: + counter = 0 + cellcolumnA = [] + # Choose kind of ballot paper (YesNoAbstain or Yes) + if self.poll.yesnoabstain: # YesNoAbstain ballot: max 27 candidates for option in options: + counter += 1 candidate = option.candidate cell.append(Paragraph( - candidate.clean_name, stylesheet['Ballot_option_name'])) + candidate.clean_name, stylesheet['Ballot_option_name_YNA'])) if candidate.name_suffix: cell.append(Paragraph( "(%s)" % candidate.name_suffix, - stylesheet['Ballot_option_group'])) + stylesheet['Ballot_option_suffix_YNA'])) else: cell.append(Paragraph( - " ", stylesheet['Ballot_option_group'])) - cell.append(Paragraph( - circle + _("Yes") + "  " * 3 + circle - + _("No") + "  " * 3 + circle + _("Abstention"), - stylesheet['Ballot_option_YNA'])) + " ", stylesheet['Ballot_option_suffix_YNA'])) + cell.append(Paragraph("%(circle)s \ + %(yes)s     \ + %(circle)s \ + %(no)s     \ + %(circle)s \ + %(abstain)s" % + {'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 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 if rest: data.append([cell, '']) @@ -577,22 +591,31 @@ class AssignmentPollPDF(PDFView): t = Table(data, 10.5 * cm, 14.84 * cm) else: t = Table(data, 10.5 * cm, 29.7 * cm) - else: + else: # Yes ballot: max 46 candidates for option in options: + counter += 1 candidate = option.candidate - cell.append(Paragraph( - circle + candidate.clean_name, - stylesheet['Ballot_option_name'])) + cell.append(Paragraph("%s \ + %s" % + (circle, candidate.clean_name), stylesheet['Ballot_option_name'])) if candidate.name_suffix: cell.append(Paragraph( "(%s)" % candidate.name_suffix, - stylesheet['Ballot_option_group_right'])) + stylesheet['Ballot_option_suffix'])) else: cell.append(Paragraph( - " ", stylesheet['Ballot_option_group_right'])) + " ", stylesheet['Ballot_option_suffix'])) + if counter == 22: + cellcolumnA = cell + cell = [] + cell.append(Spacer(0, 0.75 * cm)) + # print ballot papers 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 if rest: data.append([cell, '']) diff --git a/openslides/motion/pdf.py b/openslides/motion/pdf.py index a9166b98c..2f89dc31a 100644 --- a/openslides/motion/pdf.py +++ b/openslides/motion/pdf.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- from operator import attrgetter -import os import random from bs4 import BeautifulSoup -from django.conf import settings from django.utils.translation import ugettext as _ from natsort import natsorted from reportlab.lib import colors @@ -261,17 +259,19 @@ def all_motion_cover(pdf, motions): def motion_poll_to_pdf(pdf, poll): - imgpath = os.path.join(settings.SITE_ROOT, 'static/img/circle.png') - circle = "  " % imgpath + circle = "*" # = Unicode Character 'HEAVY LARGE CIRCLE' (U+2B55) cell = [] cell.append(Spacer(0, 0.8 * cm)) cell.append(Paragraph(_("Motion No. %s") % poll.motion.identifier, stylesheet['Ballot_title'])) cell.append(Paragraph(poll.motion.title, stylesheet['Ballot_subtitle'])) cell.append(Paragraph(_("%d. Vote") % poll.poll_number, stylesheet['Ballot_description'])) cell.append(Spacer(0, 0.5 * cm)) - cell.append(Paragraph(circle + unicode(_("Yes")), stylesheet['Ballot_option'])) - cell.append(Paragraph(circle + unicode(_("No")), stylesheet['Ballot_option'])) - cell.append(Paragraph(circle + unicode(_("Abstention")), stylesheet['Ballot_option'])) + cell.append(Paragraph("%s %s" + % (circle, unicode(_("Yes"))), stylesheet['Ballot_option'])) + cell.append(Paragraph("%s %s" + % (circle, unicode(_("No"))), stylesheet['Ballot_option'])) + cell.append(Paragraph("%s %s" + % (circle, unicode(_("Abstention"))), stylesheet['Ballot_option'])) data = [] # get ballot papers config values ballot_papers_selection = config["motion_pdf_ballot_papers_selection"] diff --git a/openslides/static/fonts/README.txt b/openslides/static/fonts/README.txt new file mode 100644 index 000000000..70eb8a912 --- /dev/null +++ b/openslides/static/fonts/README.txt @@ -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 diff --git a/openslides/static/fonts/circle.ttf b/openslides/static/fonts/circle.ttf new file mode 100644 index 000000000..f4063ce5d Binary files /dev/null and b/openslides/static/fonts/circle.ttf differ diff --git a/openslides/static/img/circle.png b/openslides/static/img/circle.png deleted file mode 100644 index aff3a482a..000000000 Binary files a/openslides/static/img/circle.png and /dev/null differ diff --git a/openslides/utils/pdf.py b/openslides/utils/pdf.py index e3c2911c4..60fd91f65 100644 --- a/openslides/utils/pdf.py +++ b/openslides/utils/pdf.py @@ -22,6 +22,8 @@ pdfmetrics.registerFont(TTFont( 'Ubuntu-Bold', path_join(settings.SITE_ROOT, 'static/fonts/Ubuntu-B.ttf'))) pdfmetrics.registerFont(TTFont( '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 @@ -185,30 +187,29 @@ stylesheet.add(ParagraphStyle(name='Ballot_option', leading=24, leftIndent=30), ) +stylesheet.add(ParagraphStyle(name='Ballot_option_name_YNA', + parent=stylesheet['Ballot_option'], + leading=14), + ) stylesheet.add(ParagraphStyle(name='Ballot_option_name', - parent=stylesheet['Normal'], - fontSize=12, - leading=15, - leftIndent=30), + parent=stylesheet['Ballot_option_name_YNA'], + leading=17), ) -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, - leading=15, - leftIndent=30), + leading=11), ) -stylesheet.add(ParagraphStyle(name='Ballot_option_YNA', - parent=stylesheet['Normal'], - fontSize=12, - leading=15, - leftIndent=49, - spaceAfter=18), - ) -stylesheet.add(ParagraphStyle(name='Ballot_option_group_right', - parent=stylesheet['Normal'], - fontSize=8, +stylesheet.add(ParagraphStyle(name='Ballot_option_suffix', + parent=stylesheet['Ballot_option_suffix_YNA'], 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 stylesheet.add(ParagraphStyle(name='formfield', diff --git a/requirements_production.txt b/requirements_production.txt index ef1748a7c..55703c1c0 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -5,7 +5,6 @@ bleach>=1.2,<1.3 django-haystack>=2.1,<2.2 django-mptt>=0.6,<0.7 natsort>=3.0,<3.1 -pillow>=2.2,<2.3 reportlab>=2.7,<2.8 sockjs-tornado>=1.0,<1.1 tornado>=3.1,<3.2