Merge pull request #362 from Vincent-CIRCL/master

fix: [exportpdf] Custom path for fonts and font package
pull/370/head
Raphaël Vinot 2019-03-12 13:05:52 +01:00 committed by GitHub
commit 637af49b21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 447 additions and 28 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

46
docs/PDF-export/README.MD Normal file
View File

@ -0,0 +1,46 @@
## Overview
A big overview of the exportpdf tool is available at :
![Classes Diagram](./ExportPDF.png)
## Parameters
### Dynamic links
You can specify your MISP URL to allow the PDF generator to create links from the PDF to your MISP instance events.
The parameter to specify is "base_url_for_dynamic_link". Leave blank if you don't want the PDF generator to generate URL.
### Name
You can specify your organisation name, to be added to the PDF metadata.
The parameter to specify is "name_for_metadata". Leave blank if you don't want the PDF generator to add this name to the metadata.
### Textual description
You can specify if you want a textual description of the event to be prepend to the generated PDF.
The parameter to specify is "textual_description". Leave blank if you don't want the PDF generator to prepend this text description.
### Galaxy description
You can specify if you want related galaxies to be added to the PDF.
The parameter to specify is "galaxy_description". Leave blank if you don't want the PDF generator to add galaxies description.
### Related events
You can specify if you want related events to be added to the PDF. Be aware this option might leads to information leaks if you have confidential events in your MISP instance.
The parameter to specify is "related_events". Leave blank if you don't want the PDF generator to add related events..
Example of parameters all activated.
![Parameters](./Parameters.png)
## International fonts
If text of exported events does not show up in the final PDF, Fonts may be the source of the problem.
For that, you can activate a international font, which handle CJK characters, named "Noto".
Due to their size, fonts are not bundled with PyMISP. You can download them, by following next steps :
Manual fonts install on a MISP instance, connected in SSH :
> cd /usr/local/lib/python3.6/dist-packages/pymisp/tools/
> git clone https://github.com/MISP/pdf_fonts
Then you can activate the option by filling the following parameter :
![Internanlization parameters](./Internationalization_parameters.png)
Leave blank if you don't want to use internationalization fonts.
If "Noto" is not fine for you (e.g. you want to use Arial Unicode[,](https://github.com/Vincent-CIRCL/Arial_Unicode) etc.) you can give as a parameter the font to use. Be sure the font is only contained in one TTF.
Fill this parameter, with the font's TTF's absolute path, as follow :
![Internanlization path](./Internationalization_path.png)
Note that if you give a custom fonts, bold/italic/special styles won't be used in the final PDF.
Leave blank if you don't want to use your custom font.

View File

@ -129,7 +129,8 @@ class Flowable_Tag(Flowable):
# Copy of pdfexport.py moduleconfig
moduleconfig = ["MISP_base_url_for_dynamic_link", "MISP_name_for_metadata", "Activate_textual_description",
"Activate_galaxy_description", "Activate_related_events", "Activate_internationalization_fonts"]
"Activate_galaxy_description", "Activate_related_events", "Activate_internationalization_fonts",
"Custom_fonts_path"]
# == Row colors of the table (alternating) ==
EVEN_COLOR = colors.whitesmoke
@ -217,6 +218,7 @@ SECOND_LEVEL_GALAXY_WIDTHS = ["20%", "80%"]
CLUSTER_COLORS = [0] # or 1
OFFSET = 1
########################################################################
# "UTILITIES" METHODS. Not meant to be used except for development purposes
@ -379,34 +381,48 @@ def general_style_generator():
return lines_list
def internationalize_font():
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/tools"
def internationalize_font(config=None):
global FIRST_COL_FONT
global SECOND_COL_FONT
''' # Available fonts :
NotoSansCJKtc - DemiLight.ttf
NotoSansCJKtc - Regular.ttf
NotoSansCJKtc - Black.ttf
NotoSansCJKtc - Light.ttf
NotoSansCJKtc - Thin.ttf
NotoSansCJKtc - Bold.ttf
NotoSansCJKtc - Medium.ttf
'''
if is_in_config(config, 6) and config[moduleconfig[6]] != "" :
# Handle custom fonts. Has to be TrueType = one TTF only
fonts_path_custom = config[moduleconfig[6]]
noto_bold = BASE_DIR + "/pdf_fonts/Noto_TTF/NotoSansCJKtc-Bold.ttf"
noto = BASE_DIR + "/pdf_fonts/Noto_TTF/NotoSansCJKtc-DemiLight.ttf"
if fonts_path_custom and os.path.isfile(fonts_path_custom):
registerFont(TTFont("custom_font", fonts_path_custom))
FIRST_COL_FONT = 'custom_font'
SECOND_COL_FONT = 'custom_font'
if os.path.isfile(noto_bold) and os.path.isfile(noto):
registerFont(TTFont("Noto", noto))
registerFont(TTFont("Noto-bold", noto_bold))
FIRST_COL_FONT = 'Noto-bold'
SECOND_COL_FONT = 'Noto'
else:
logger.error(
"Trying to load a custom font, unable to access the file. Path : " + str(fonts_path_custom))
else:
logger.error(
"Trying to load a custom (internationalization) font, unable to access the file : " + noto_bold)
''' Handle provided NOTO fonts (CJK only for now)
# Available fonts :
NotoSansCJKtc - DemiLight.ttf
NotoSansCJKtc - Regular.ttf
NotoSansCJKtc - Black.ttf
NotoSansCJKtc - Light.ttf
NotoSansCJKtc - Thin.ttf
NotoSansCJKtc - Bold.ttf
NotoSansCJKtc - Medium.ttf
'''
font_path = os.path.join(os.path.abspath(os.path.dirname(sys.modules['pymisp'].__file__)), 'tools', 'pdf_fonts',
'Noto_TTF')
noto_bold = font_path + "/NotoSansCJKtc-Bold.ttf"
noto = font_path + "/NotoSansCJKtc-DemiLight.ttf"
if os.path.isfile(noto_bold) and os.path.isfile(noto):
registerFont(TTFont("Noto", noto))
registerFont(TTFont("Noto-bold", noto_bold))
FIRST_COL_FONT = 'Noto-bold'
SECOND_COL_FONT = 'Noto'
else:
logger.error(
"Trying to load a custom (internationalization) font, unable to access the file : " + noto_bold)
def get_table_styles():
@ -1227,8 +1243,8 @@ class Attributes():
# If the current event is an external analysis and a comment
if is_safe_attribute(attribute, "value") and is_safe_attribute(attribute,
"category") and is_safe_attribute(
attribute, "type") and getattr(attribute, "category") == "External analysis" and getattr(
attribute, "type") == "comment":
attribute, "type") and getattr(attribute, "category") == "External analysis" and getattr(
attribute, "type") == "comment":
# We add it to the description
text += "<br/>" + EXTERNAL_ANALYSIS_PREFIX + safe_string(getattr(attribute, "value"))
@ -1332,7 +1348,7 @@ class Sightings():
list_sighting[1]) + "</font>"
sight_text += " / " + "<font color =" + MISC_SIGHT_COLOR + "> Misc. : " + str(list_sighting[2]) + "</font>"
answer_sighting = self.value_formatter.get_unoverflowable_paragraph(sight_text)
answer_sighting = self.value_formatter.get_unoverflowable_paragraph(sight_text, do_escape_string=False)
else:
# No tags for this object
answer_sighting = self.value_formatter.get_unoverflowable_paragraph("No sighting")
@ -1778,7 +1794,7 @@ def convert_event_in_pdf_buffer(misp_event, config=None):
if is_in_config(config, 5): # We want internationalization
logger.info("Internationalization of fonts during pdf export activated. CJK-fonts supported.")
internationalize_font()
internationalize_font(config)
# DEBUG / TO DELETE : curr_document = SimpleDocTemplate('myfile.pdf')
curr_document = SimpleDocTemplate(pdf_buffer,

File diff suppressed because one or more lines are too long

View File

@ -33,7 +33,7 @@ class TestMISPEvent(unittest.TestCase):
self.storage_folder = self.root + "reportlab_testoutputs/"
self.storage_image_folder = self.root + "reportlab_test_image_outputs/"
self.moduleconfig = ["MISP_base_url_for_dynamic_link", "MISP_name_for_metadata", "Activate_textual_description",
"Activate_galaxy_description", "Activate_related_events", "Activate_internationalization_fonts"]
"Activate_galaxy_description", "Activate_related_events", "Activate_internationalization_fonts", "Custom_fonts_path"]
def init_event(self):
@ -296,6 +296,45 @@ class TestMISPEvent(unittest.TestCase):
reportlab_generator.convert_event_in_pdf_buffer(self.mispevent, config),
self.storage_folder + "japanese_test.pdf")
def test_utf_heavy(self):
if self.check_python_2():
self.assertTrue(True)
else:
config = {}
config[self.moduleconfig[0]] = "http://localhost:8080"
config[self.moduleconfig[1]] = "My Wonderful CERT"
config[self.moduleconfig[2]] = True
config[self.moduleconfig[3]] = True
config[self.moduleconfig[4]] = True
config[self.moduleconfig[5]] = True
self.init_event()
self.mispevent.load_file(self.test_folder + 'japanese_test_heavy.json')
reportlab_generator.register_value_to_file(
reportlab_generator.convert_event_in_pdf_buffer(self.mispevent, config),
self.storage_folder + "japanese_test_heavy.pdf")
def test_utf_ArialUNI_custompath(self):
if self.check_python_2():
self.assertTrue(True)
elif not manual_testing:
self.assertTrue(True)
else:
config = {}
config[self.moduleconfig[0]] = "http://localhost:8080"
config[self.moduleconfig[1]] = "My Wonderful CERT"
config[self.moduleconfig[2]] = True
config[self.moduleconfig[3]] = True
config[self.moduleconfig[4]] = True
config[self.moduleconfig[5]] = True
config[self.moduleconfig[6]] = "/home/user/Desktop/PyMISP/pymisp/tools/pdf_fonts/arial-unicode-ms/ARIALUNI.TTF"
self.init_event()
self.mispevent.load_file(self.test_folder + 'japanese_test_heavy.json')
reportlab_generator.register_value_to_file(
reportlab_generator.convert_event_in_pdf_buffer(self.mispevent, config),
self.storage_folder + "custom_path.pdf")
def test_batch_image_events(self):
# Test case ONLY for manual testing. Needs to download a full list of image events !