pdfmake incl. fixes
This commit is contained in:
parent
546c4e65f6
commit
92a541215f
@ -22,6 +22,7 @@ Core:
|
|||||||
Motions:
|
Motions:
|
||||||
- Added origin field.
|
- Added origin field.
|
||||||
- Added button to sort and number all motions in a category.
|
- Added button to sort and number all motions in a category.
|
||||||
|
- Introduced pdfMake for clientside generation of PDFs.
|
||||||
|
|
||||||
Users:
|
Users:
|
||||||
- Added field is_committee and new default group Committees.
|
- Added field is_committee and new default group Committees.
|
||||||
|
@ -37,8 +37,15 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
color: '#555',
|
color: '#555',
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
margin: [80, 50, 80, 0], //margin: [left, top, right, bottom]
|
margin: [80, 50, 80, 0], //margin: [left, top, right, bottom]
|
||||||
columns: ['OpenSlides | Presentation and assembly system', {
|
columns: [
|
||||||
|
{
|
||||||
|
text: 'OpenSlides | Presentation and assembly system',
|
||||||
|
fontSize:10,
|
||||||
|
width: '70%'
|
||||||
|
},
|
||||||
|
{
|
||||||
fontSize: 6,
|
fontSize: 6,
|
||||||
|
width: '30%',
|
||||||
text: 'Stand: ' + date.toLocaleDateString() + " " + date.toLocaleTimeString(),
|
text: 'Stand: ' + date.toLocaleDateString() + " " + date.toLocaleTimeString(),
|
||||||
alignment: 'right'
|
alignment: 'right'
|
||||||
}]
|
}]
|
||||||
|
@ -19,6 +19,10 @@ urlpatterns = [
|
|||||||
views.SearchView.as_view(),
|
views.SearchView.as_view(),
|
||||||
name='core_search'),
|
name='core_search'),
|
||||||
|
|
||||||
|
url(r'^core/encode_media/$',
|
||||||
|
views.MediaEncoder.as_view(),
|
||||||
|
name="core_mediaencoding"),
|
||||||
|
|
||||||
url(r'^angular_js/(?P<openslides_app>site|projector)/$',
|
url(r'^angular_js/(?P<openslides_app>site|projector)/$',
|
||||||
views.AppsJsView.as_view(),
|
views.AppsJsView.as_view(),
|
||||||
name='core_apps_js'),
|
name='core_apps_js'),
|
||||||
@ -26,8 +30,8 @@ urlpatterns = [
|
|||||||
# View for the projectors are handelt by angular.
|
# View for the projectors are handelt by angular.
|
||||||
url(r'^projector.*$', views.ProjectorView.as_view()),
|
url(r'^projector.*$', views.ProjectorView.as_view()),
|
||||||
|
|
||||||
|
|
||||||
# Main entry point for all angular pages.
|
# Main entry point for all angular pages.
|
||||||
# Has to be the last entry in the urls.py
|
# Has to be the last entry in the urls.py
|
||||||
url(r'^.*$', views.IndexView.as_view()),
|
url(r'^.*$', views.IndexView.as_view()),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@ -606,3 +609,143 @@ class SearchView(utils_views.APIView):
|
|||||||
return super().get_context_data(
|
return super().get_context_data(
|
||||||
elements=search(unquote(query)),
|
elements=search(unquote(query)),
|
||||||
**context)
|
**context)
|
||||||
|
|
||||||
|
|
||||||
|
class MediaEncoder(utils_views.APIView):
|
||||||
|
"""
|
||||||
|
MediaEncoder is a class based view to prepare encoded media for pdfMake
|
||||||
|
"""
|
||||||
|
http_method_names = ['post']
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Encode_image is used in the context of PDF-Generation
|
||||||
|
Takes an array of IMG.src - Paths
|
||||||
|
Retrieves the according images
|
||||||
|
Encodes the images to BASE64
|
||||||
|
Add configured fonts
|
||||||
|
Puts it into a key-value structure
|
||||||
|
|
||||||
|
{
|
||||||
|
"images": {
|
||||||
|
"media/file/ubuntu.png":"$ENCODED_IMAGE"
|
||||||
|
},
|
||||||
|
"fonts": [{
|
||||||
|
$FontName : {
|
||||||
|
normal: $Filename
|
||||||
|
bold: $Filename
|
||||||
|
italics: $Filename
|
||||||
|
bolditalics: $Filename
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"default_font": "$DEFAULTFONT"
|
||||||
|
}
|
||||||
|
|
||||||
|
:param request:
|
||||||
|
:return: Response of the resulting dictionary
|
||||||
|
|
||||||
|
Calling e.g.
|
||||||
|
$.ajax({ type: "POST", url: "/motions/encode_images/",
|
||||||
|
data: JSON.stringify(["$FILEPATH"]),
|
||||||
|
success: function(data){ console.log(data); },
|
||||||
|
dataType: 'application/json' });
|
||||||
|
"""
|
||||||
|
body_unicode = request.body.decode('utf-8')
|
||||||
|
file_paths = json.loads(body_unicode)
|
||||||
|
images = {file_path: self.encode_image_from(file_path) for file_path in file_paths}
|
||||||
|
fonts = self.encoded_fonts()
|
||||||
|
default_font = self.get_default_font()
|
||||||
|
return Response({
|
||||||
|
"images": images,
|
||||||
|
"fonts": fonts,
|
||||||
|
"defaultFont": default_font
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_default_font(self):
|
||||||
|
"""
|
||||||
|
Returns the default font for pdfMake.
|
||||||
|
|
||||||
|
Note: For development purposes this is hard coded.
|
||||||
|
|
||||||
|
:return: the name of the default Font
|
||||||
|
"""
|
||||||
|
return 'OpenSans'
|
||||||
|
|
||||||
|
def encoded_fonts(self):
|
||||||
|
"""
|
||||||
|
Generate font encoding for pdfMake
|
||||||
|
:return: list of Font Encodings
|
||||||
|
"""
|
||||||
|
fonts = self.get_configured_fonts()
|
||||||
|
enc_fonts = [self.encode_font(name, files) for name, files in fonts.items()]
|
||||||
|
return enc_fonts
|
||||||
|
|
||||||
|
def get_configured_fonts(self):
|
||||||
|
"""
|
||||||
|
Returns the configured fonts
|
||||||
|
|
||||||
|
Note: For development purposes, the current font definition is hard coded
|
||||||
|
|
||||||
|
The form is {
|
||||||
|
$FontName : {
|
||||||
|
normal: $Filename
|
||||||
|
bold: $Filename
|
||||||
|
italics: $Filename
|
||||||
|
bolditalics: $Filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
This structure is required according to PDFMake specs.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
fonts = {
|
||||||
|
'OpenSans': {
|
||||||
|
'normal': 'OpenSans-Regular.ttf',
|
||||||
|
'bold': 'OpenSans-Bold.ttf',
|
||||||
|
'italics': 'OpenSans-Italic.ttf',
|
||||||
|
'bolditalics': 'OpenSans-BoldItalic.ttf'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fonts
|
||||||
|
|
||||||
|
def encode_font(self, font_name, font_files):
|
||||||
|
"""
|
||||||
|
Responsible to encode a single font
|
||||||
|
:param fontName: name of the font
|
||||||
|
:param font_files: files for different weighs
|
||||||
|
:return: dictionary with encoded font
|
||||||
|
"""
|
||||||
|
encoded_files = {type: self.encode_font_from(file_path) for type, file_path in font_files.items()}
|
||||||
|
return {font_name: encoded_files}
|
||||||
|
|
||||||
|
def encode_font_from(self, file_path):
|
||||||
|
"""
|
||||||
|
Returns the BASE64 encoded version of an image-file for a given path
|
||||||
|
:param file_path:
|
||||||
|
:return: dictionary with the string representation (content) and the name of the file
|
||||||
|
for the pdfMake.vfs structure
|
||||||
|
"""
|
||||||
|
path = os.path.join(settings.SITE_ROOT, 'static/fonts', os.path.basename(file_path))
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as file:
|
||||||
|
string_representation = "{}".format(base64.b64encode(file.read()).decode())
|
||||||
|
except:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return {"content": string_representation, "name": file_path}
|
||||||
|
|
||||||
|
def encode_image_from(self, file_path):
|
||||||
|
"""
|
||||||
|
Returns the BASE64 encoded version of an image-file for a given path
|
||||||
|
:param file_path:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
path = os.path.join(settings.MEDIA_ROOT, 'file', os.path.basename(file_path))
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as file:
|
||||||
|
string_representation = "data:image/{};base64,{}".format(os.path.splitext(file_path)[1][1:],
|
||||||
|
base64.b64encode(file.read()).decode())
|
||||||
|
except Exception:
|
||||||
|
# If any error occurs ignore it and return an empty string
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return string_representation
|
||||||
|
@ -49,9 +49,11 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
*/
|
*/
|
||||||
signment = function(motion, $scope, User) {
|
signment = function(motion, $scope, User) {
|
||||||
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
|
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
|
||||||
|
var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n'+gettextCatalog.getString(motion.state.name));
|
||||||
|
state.width = "70%";
|
||||||
label.width = "30%";
|
label.width = "30%";
|
||||||
label.bold = true;
|
label.bold = true;
|
||||||
var signment = converter.createElement("stack", [label]);
|
var signment = converter.createElement("columns", [label, state]);
|
||||||
signment.margin = [10, 20, 0, 10];
|
signment.margin = [10, 20, 0, 10];
|
||||||
signment.lineHeight = 2.5;
|
signment.lineHeight = 2.5;
|
||||||
return signment;
|
return signment;
|
||||||
@ -778,10 +780,11 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
'MotionContentProvider',
|
'MotionContentProvider',
|
||||||
'PdfMakeConverter',
|
'PdfMakeConverter',
|
||||||
'PdfMakeDocumentProvider',
|
'PdfMakeDocumentProvider',
|
||||||
|
'gettextCatalog',
|
||||||
function($scope, $http, ngDialog, MotionForm,
|
function($scope, $http, ngDialog, MotionForm,
|
||||||
Motion, Category, Mediafile, Tag,
|
Motion, Category, Mediafile, Tag,
|
||||||
User, Workflow, motion,
|
User, Workflow, motion,
|
||||||
SingleMotionContentProvider, MotionContentProvider, PdfMakeConverter, PdfMakeDocumentProvider) {
|
SingleMotionContentProvider, MotionContentProvider, PdfMakeConverter, PdfMakeDocumentProvider, gettextCatalog) {
|
||||||
Motion.bindOne(motion.id, $scope, 'motion');
|
Motion.bindOne(motion.id, $scope, 'motion');
|
||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||||
@ -794,13 +797,14 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
|
|
||||||
$scope.makePDF = function(){
|
$scope.makePDF = function(){
|
||||||
var content = motion.getText($scope.version) + motion.getReason($scope.version),
|
var content = motion.getText($scope.version) + motion.getReason($scope.version),
|
||||||
|
id = motion.identifier,
|
||||||
slice = Function.prototype.call.bind([].slice),
|
slice = Function.prototype.call.bind([].slice),
|
||||||
map = Function.prototype.call.bind([].map),
|
map = Function.prototype.call.bind([].map),
|
||||||
image_sources = map($(content).find("img"), function(element) {
|
image_sources = map($(content).find("img"), function(element) {
|
||||||
return element.getAttribute("src");
|
return element.getAttribute("src");
|
||||||
});
|
});
|
||||||
|
|
||||||
$http.post('/motions/encode_media/', JSON.stringify(image_sources)).success(function(data) {
|
$http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) {
|
||||||
/**
|
/**
|
||||||
* Converter for use with pdfMake
|
* Converter for use with pdfMake
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -812,8 +816,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake),
|
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake),
|
||||||
motionContentProvider = MotionContentProvider.createInstance(converter),
|
motionContentProvider = MotionContentProvider.createInstance(converter),
|
||||||
contentProvider = SingleMotionContentProvider.createInstance(motionContentProvider, motion, $scope, User),
|
contentProvider = SingleMotionContentProvider.createInstance(motionContentProvider, motion, $scope, User),
|
||||||
documentProvider = PdfMakeDocumentProvider.createInstance(contentProvider, data.defaultFont);
|
documentProvider = PdfMakeDocumentProvider.createInstance(contentProvider, data.defaultFont),
|
||||||
pdfMake.createPdf(documentProvider.getDocument()).open();
|
filename = gettextCatalog.getString("Motion") + " " + id + ".pdf";
|
||||||
|
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,13 +5,9 @@
|
|||||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
<i class="fa fa-angle-double-left fa-lg"></i>
|
||||||
<translate>All motions</translate>
|
<translate>All motions</translate>
|
||||||
</a>
|
</a>
|
||||||
<a ui-sref="motions_single_pdf({pk: motion.id})" target="_blank" class="btn btn-default btn-sm">
|
|
||||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
|
||||||
<translate>PDF</translate>
|
|
||||||
</a>
|
|
||||||
<a ng-click="makePDF()" class="btn btn-primary btn-sm">
|
<a ng-click="makePDF()" class="btn btn-primary btn-sm">
|
||||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
<i class="fa fa-file-pdf-o fa-lg"></i>
|
||||||
<translate>PDFmake</translate>
|
<translate>PDF</translate>
|
||||||
</a>
|
</a>
|
||||||
<!-- List of speakers -->
|
<!-- List of speakers -->
|
||||||
<a ui-sref="agenda.item.detail({id: motion.agenda_item_id})" class="btn btn-sm btn-default">
|
<a ui-sref="agenda.item.detail({id: motion.agenda_item_id})" class="btn btn-sm btn-default">
|
||||||
|
@ -14,6 +14,4 @@ urlpatterns = [
|
|||||||
url(r'^poll/(?P<poll_pk>\d+)/print/$',
|
url(r'^poll/(?P<poll_pk>\d+)/print/$',
|
||||||
views.MotionPollPDF.as_view(),
|
views.MotionPollPDF.as_view(),
|
||||||
name='motionpoll_pdf'),
|
name='motionpoll_pdf'),
|
||||||
|
|
||||||
url(r'^encode_media/', views.encode_media, name="media_encoding")
|
|
||||||
]
|
]
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
import base64
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import Http404, JsonResponse
|
from django.http import Http404
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_noop
|
from django.utils.translation import ugettext_noop
|
||||||
from reportlab.platypus import SimpleDocTemplate
|
from reportlab.platypus import SimpleDocTemplate
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.decorators import api_view
|
|
||||||
|
|
||||||
from openslides.core.config import config
|
from openslides.core.config import config
|
||||||
from openslides.utils.rest_api import (
|
from openslides.utils.rest_api import (
|
||||||
@ -472,142 +466,3 @@ class MotionPDFView(SingleObjectMixin, PDFView):
|
|||||||
motions_to_pdf(pdf, motions)
|
motions_to_pdf(pdf, motions)
|
||||||
else:
|
else:
|
||||||
motion_to_pdf(pdf, self.get_object())
|
motion_to_pdf(pdf, self.get_object())
|
||||||
|
|
||||||
|
|
||||||
@api_view(["POST"])
|
|
||||||
def encode_media(request):
|
|
||||||
"""
|
|
||||||
Encode_image is used in the context of PDF-Generation
|
|
||||||
Takes an array of IMG.src - Paths
|
|
||||||
Retrieves the according images
|
|
||||||
Encodes the images
|
|
||||||
Add configured fonts
|
|
||||||
Puts it into a key-value structure
|
|
||||||
|
|
||||||
{
|
|
||||||
"images": {
|
|
||||||
"media/file/ubuntu.png":"$ENCODED_IMAGE"
|
|
||||||
},
|
|
||||||
"fonts": [{
|
|
||||||
$FontName : {
|
|
||||||
normal: $Filename
|
|
||||||
bold: $Filename
|
|
||||||
italics: $Filename
|
|
||||||
bolditalics: $Filename
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
"default_font": "$DEFAULTFONT"
|
|
||||||
}
|
|
||||||
|
|
||||||
:param request:
|
|
||||||
:return: JsonResponse of the resulting dictionary
|
|
||||||
|
|
||||||
Calling e.g.
|
|
||||||
$.ajax({ type: "POST", url: "/motions/encode_images/",
|
|
||||||
data: JSON.stringify(["$FILEPATH"]),
|
|
||||||
success: function(data){ console.log(data); },
|
|
||||||
dataType: 'application/json' });
|
|
||||||
"""
|
|
||||||
body_unicode = request.body.decode('utf-8')
|
|
||||||
file_paths = json.loads(body_unicode)
|
|
||||||
images = {file_path: encode_image_from(file_path) for file_path in file_paths}
|
|
||||||
fonts = encoded_fonts()
|
|
||||||
default_font = get_default_font()
|
|
||||||
return JsonResponse({
|
|
||||||
"images": images,
|
|
||||||
"fonts": fonts,
|
|
||||||
"defaultFont": default_font
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def get_default_font():
|
|
||||||
"""
|
|
||||||
For development purposes this is hard coded
|
|
||||||
:return: the name of the default Font
|
|
||||||
"""
|
|
||||||
return "OpenSans"
|
|
||||||
|
|
||||||
|
|
||||||
def encoded_fonts():
|
|
||||||
"""
|
|
||||||
Generate font encoding for pdfMake
|
|
||||||
:return: list of Font Encodings
|
|
||||||
"""
|
|
||||||
|
|
||||||
fonts = get_configured_fonts()
|
|
||||||
|
|
||||||
enc_fonts = [encode_font(name, files) for name, files in fonts.items()]
|
|
||||||
|
|
||||||
return enc_fonts
|
|
||||||
|
|
||||||
|
|
||||||
def get_configured_fonts():
|
|
||||||
"""
|
|
||||||
For development purposes, the current font definition is hard coded
|
|
||||||
The form is {
|
|
||||||
$FontName : {
|
|
||||||
normal: $Filename
|
|
||||||
bold: $Filename
|
|
||||||
italics: $Filename
|
|
||||||
bolditalics: $Filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
This structure is required according to PDFMake specs.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
fonts = {
|
|
||||||
"OpenSans": {
|
|
||||||
"normal": 'OpenSans-Regular.ttf',
|
|
||||||
"bold": 'OpenSans-Bold.ttf',
|
|
||||||
"italics": 'OpenSans-Italic.ttf',
|
|
||||||
"bolditalics": 'OpenSans-BoldItalic.ttf'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fonts
|
|
||||||
|
|
||||||
|
|
||||||
def encode_font(fontName, font_files):
|
|
||||||
"""
|
|
||||||
Responsible to encode a single font
|
|
||||||
:param fontName: name of the font
|
|
||||||
:param font_files: files for different weighs
|
|
||||||
:return: dictionary with encoded font
|
|
||||||
"""
|
|
||||||
|
|
||||||
encoded_files = {type: encode_font_from(file_path) for type, file_path in font_files.items()}
|
|
||||||
return {fontName: encoded_files}
|
|
||||||
|
|
||||||
|
|
||||||
def encode_font_from(file_path):
|
|
||||||
"""
|
|
||||||
Returns the BASE64 encoded version of an image-file for a given path
|
|
||||||
:param file_path:
|
|
||||||
:return: dictionary with the string representation (content) and the name of the file
|
|
||||||
for the pdfMake.vfs structure
|
|
||||||
"""
|
|
||||||
path = os.path.join(settings.SITE_ROOT, 'static/fonts', os.path.basename(file_path))
|
|
||||||
try:
|
|
||||||
with open(path, "rb") as file:
|
|
||||||
string_representation = "{}".format(base64.b64encode(file.read()).decode())
|
|
||||||
except:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return {"content": string_representation, "name": file_path}
|
|
||||||
|
|
||||||
|
|
||||||
def encode_image_from(file_path):
|
|
||||||
"""
|
|
||||||
Returns the BASE64 encoded version of an image-file for a given path
|
|
||||||
:param file_path:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
path = os.path.join(settings.MEDIA_ROOT, 'file', os.path.basename(file_path))
|
|
||||||
try:
|
|
||||||
with open(path, "rb") as file:
|
|
||||||
string_representation = "data:image/{};base64,{}".format(os.path.splitext(file_path)[1][1:],
|
|
||||||
base64.b64encode(file.read()).decode())
|
|
||||||
except:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return string_representation
|
|
||||||
|
Loading…
Reference in New Issue
Block a user