def pre_header(document, settings): # TODO: need to fix templates lr and kr, now they have different styles for i in range(6): if settings['type'] != 'LR': custom_header_style = document.styles['h{}'.format(i + 1)] # this code will be need in future when u will generate new template for kr else: custom_header_style = document.styles.add_style( 'h{}'.format(i + 1), WD_STYLE_TYPE.PARAGRAPH) custom_header_style.font.rtl = True custom_header_style.font.name = settings[FORMAT][ TYPE_OF_HEADER.format(i + 1)][FONT] custom_header_style.font.size = Pt( settings[FORMAT][TYPE_OF_HEADER.format(i + 1)][SIZE]) custom_header_style.font.bold = True custom_header_style.font.italic = False custom_header_style.font.underline = False custom_header_style.font.color.rgb = RGBColor.from_string('000000') paragraph_format = custom_header_style.paragraph_format paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT paragraph_format.space_after = Pt(STANDART_PT_HEADER) paragraph_format.left_indent = Cm( 1.5) # Inches(STANDART_INCHES + 0.1) custom_header_style.font.name = settings[FORMAT][TYPE_OF_HEADER.format( i + 1)][FONT] custom_header_style.font.size = Pt( settings[FORMAT][TYPE_OF_HEADER.format(i + 1)][SIZE])
def add_title(self, title, font_size=12, text_align='center', color='000000', left_indent=0., space_before=0.12, space_after=0.12): h = self.document.add_heading(title, 1) h.paragraph_format.left_indent = Inches(left_indent) if text_align == 'center': h.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER elif text_align == 'left': h.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT if text_align == 'right': h.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT h.style.font.bold = True h.style.font.color.rgb = RGBColor.from_string(color) h.style.font.size = Pt(font_size) h.paragraph_format.space_before = Inches(space_before) h.paragraph_format.space_after = Inches(space_after)
def _process_raw_document_content(self): author = self._document.add_paragraph(self._raw_metadata["author"]) author.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER title = self._document.add_paragraph(self._raw_metadata["title"], self._document.styles["Title"]) title.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER for line in self._raw_document_content: if line.startswith("# "): line = line.replace("# ", "") self._document.add_heading(line, level=1) elif line.startswith("András:") or line.startswith("Erika:"): name, line = line.split(": ") name += " üzenete:" self._document.add_paragraph( name, style=self._document.styles["MSN Sender"]) current_p = self._document.add_paragraph( "", style=self._document.styles["MSN Message"]) bullet = current_p.add_run("\u2022\u2000") bullet.font.color.rgb = RGBColor.from_string(self._grey) current_p.add_run(line) else: self._document.add_paragraph( line, style=self._document.styles["Body"]) year_p = self._document.add_paragraph() year_p.paragraph_format.space_before = Pt(11) year_p.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT year = year_p.add_run(self._raw_metadata["rights"][-4:]) year.font.italic = True
def _generate_styles(self): normal = self._document.styles["Normal"] normal.font.name = "Times New Roman" body = self._document.styles.add_style("Body", WD_STYLE_TYPE.PARAGRAPH) body.base_style = self._document.styles["Normal"] body.font.size = Pt(12) body.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.JUSTIFY body.paragraph_format.first_line_indent = Mm(12.5) body.paragraph_format.space_after = Pt(0) body.paragraph_format.widow_control = True msn_message = self._document.styles.add_style("MSN Message", WD_STYLE_TYPE.PARAGRAPH) msn_message.font.name = "Arial" msn_message.font.size = Pt(10) msn_message.base_style = self._document.styles["Body"] msn_message.paragraph_format.first_line_indent = Mm(0) msn_sender = self._document.styles.add_style("MSN Sender", WD_STYLE_TYPE.PARAGRAPH) msn_sender.font.name = "Arial" msn_sender.font.size = Pt(10) msn_sender.base_style = self._document.styles["Body"] msn_sender.font.color.rgb = RGBColor.from_string(self._grey) msn_sender.paragraph_format.first_line_indent = Mm(0) msn_sender.paragraph_format.keep_with_next = True msn_sender.next_paragraph_style = msn_message
def add_style(doc, style_name, base_style, font_name, font_size, font_color): styles = doc.styles new_style = styles.add_style(style_name, WD_STYLE_TYPE.PARAGRAPH) new_style.base_style = styles[base_style] font = new_style.font font.name = font_name font.size = Pt(font_size) font.color.rgb = RGBColor.from_string(font_color)
def __init__(self, **options): Formatter.__init__(self, **options) # create a dict of (start, end) tuples that wrap the # value of a token so that we can use it in the format # method later self.styles = {} # we iterate over the `_styles` attribute of a style item # that contains the parsed style values. for token, style in self.style: start = end = '' # a style item is a tuple in the following form: # colors are readily specified in hex: 'RRGGBB' if style['color'] == None: color = RGBColor.from_string("000000") else: color = RGBColor.from_string(style['color']) self.styles[token] = (color, style['bold'], style['italic'], style['underline'])
def __changeFont(self, runsCollection, *, fontName, fontSize, fontType, fontColor): if self.__isHexColor(fontColor): for run in runsCollection: font = run.font font.name = fontName font.bold, font.italic, font.underline = fontType font.color.rgb = RGBColor.from_string(fontColor[1:]) font.size = Pt(fontSize) else: raise ValueError()
def helperHeading(doc, text, hexColor, font_name): styles = doc.styles new_heading_style = styles.add_style('New Heading{}'.format(text), WD_STYLE_TYPE.PARAGRAPH) new_heading_style.base_style = styles['Heading 1'] font = new_heading_style.font font.name = font_name hex_tuple = RGBColor.from_string(hexColor) font.color.rgb = RGBColor(hex_tuple[0], hex_tuple[1], hex_tuple[2]) #font.size = Pt(font_size) doc.add_paragraph(text, style='New Heading{}'.format(text))
def helperRun(paragraph, text, hexColor, underline, tab): run = paragraph.add_run(text) run.underline = bool(underline) font = run.font #font.size = Pt(font_size) hex_tuple = RGBColor.from_string(hexColor) font.color.rgb = RGBColor(hex_tuple[0], hex_tuple[1], hex_tuple[2]) font.name = 'Athelas' if bool(tab) is True: paragraph.add_run().add_tab() paragraph.add_run().add_tab()
def apply_style(cell, style): styles = { # Color, Text Color, Bold? 'Header': ('2F5496', 'FFFFFF', True), 'Subheader': ('4B84E8', 'FFFFFF', False), 'Subsubheader': ('BDBDBD', '000000', False) } assert style in styles c, tc, bold = styles[style] set_cell_color(cell, c) for p in cell.paragraphs: for r in p.runs: r.font.bold = bold r.font.color.rgb = RGBColor.from_string(tc)
def add_critical_practice_to_table_cells(results_doc): """Take the doc created via MailMerge, search for Critical Control mergefields and reposition/reformat them, then save the changes""" document = Document(results_doc) for table in document.tables: for column in table.columns: for cell in column.cells: for paragraph in cell.paragraphs: note_regex = re.compile("Critical_Control") if note_regex.search(paragraph.text): paragraph.text = note_regex.sub('', paragraph.text) para = cell.paragraphs[0].insert_paragraph_before() paragraph_format = para.paragraph_format paragraph_format.space_before = Pt(6) new_critical_practice_line = para.add_run( "Critical Control") new_critical_practice_line.font.size = Pt(9) new_critical_practice_line.bold = True new_critical_practice_line.font.color.rgb = RGBColor.from_string( 'FF0000') document.save(results_doc)
def then_font_color_rgb_is_value(context, value): font = context.font expected_value = None if value == 'None' else RGBColor.from_string(value) assert font.color.rgb == expected_value
def rgb_get_fixture(self, request): r_cxml, rgb = request.param color_format = ColorFormat(element(r_cxml)) expected_value = None if rgb is None else RGBColor.from_string(rgb) return color_format, expected_value
def add_font_color(self, hex_color_string=None): hex_color_string = hex_color_string or '#000000' if '#' in hex_color_string: hex_color_string = hex_color_string[1:] color = RGBColor.from_string(hex_color_string) self.ref.font.color.rgb = color
def it_can_construct_from_a_hex_string_rgb_value(self): rgb = RGBColor.from_string('123456') assert rgb == RGBColor(0x12, 0x34, 0x56)
def edit_style(doc, style_name, font_name, font_size, font_color): style = doc.styles[style_name] font = style.font font.name = font_name font.size = Pt(font_size) font.color.rgb = RGBColor.from_string(font_color)
def write_docx(file_name, arrs, percentage=None): document = Document() document.styles['Normal'].font.name = u'微软雅黑' document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'微软雅黑') p = document.add_paragraph('') if percentage: p.add_run('------------------统计---------------------\n') run = p.add_run('小学:' + str(percentage['primary'][0]) + '% -' + str(percentage['primary'][1]) + '个\n') run.font.color.rgb = RGBColor.from_string('2F4F4F') run = p.add_run('初一:' + str(percentage['middle_one'][0]) + '% -' + str(percentage['middle_one'][1]) + '个\n') run.font.color.rgb = RGBColor.from_string('000066') run = p.add_run('初二、初三:' + str(percentage['middle'][0]) + '% -' + str(percentage['middle'][1]) + '个\n') run.font.color.rgb = RGBColor.from_string('4169E1') run = p.add_run('高中必修:' + str(percentage['must'][0]) + '% -' + str(percentage['must'][1]) + '个\n') run.font.color.rgb = RGBColor.from_string('DAA520') run = p.add_run('高中选修:' + str(percentage['select'][0]) + '% -' + str(percentage['select'][1]) + '个\n') run.font.color.rgb = RGBColor.from_string('3CB371') run = p.add_run('超纲:' + str(percentage['out'][0]) + '% -' + str(percentage['out'][1]) + '个\n') run.font.color.rgb = RGBColor.from_string('FF4500') p.add_run('-----------------------------------------\n\n') for line in arrs: print(line) run = p.add_run(line[0]) if line[1][0] == '#': run.font.color.rgb = RGBColor.from_string(line[1][1:]) elif line[1] == 's': p.add_run(' ') elif line[1] == 'n': p.add_run('\n') p.add_run("\n======= 单词表 =======\n") p.add_run("\n小学单词 \n") for line in arrs: if line[1] == '#2F4F4F': if len(line[0]) > 2: run = p.add_run(line[0] + " \n") p.add_run("\n初中单词\n") for line in arrs: if line[1] == '#4169E1': if len(line[0]) > 2: run = p.add_run(line[0] + " \n") p.add_run("\n高中必修单词\n") for line in arrs: if line[1] == '#DAA520': if len(line[0]) > 2: run = p.add_run(line[0] + " \n") p.add_run("\n高中选修单词\n") for line in arrs: if line[1] == '#3CB371': if len(line[0]) > 2: run = p.add_run(line[0] + " \n") print(arrs) document.save(file_name) return file_name
def export_to_word(vuln_info, template, output_file='openvas_report.docx'): """ Export vulnerabilities info in a Word file. :param vuln_info: Vulnerability list info :type vuln_info: list(Vulnerability) :param output_file: Filename of the Excel file :type output_file: str :param template: Path to Docx template :type template: str :raises: TypeError """ import matplotlib.pyplot as plt import numpy as np import tempfile import os import math from docx import Document from docx.oxml.shared import qn, OxmlElement from docx.oxml.ns import nsdecls from docx.oxml import parse_xml from docx.shared import Cm, Pt, Twips from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING from docx.enum.section import WD_ORIENT from docx.enum.style import WD_STYLE_TYPE from docx.enum.table import WD_ALIGN_VERTICAL from docx.shared import RGBColor if not isinstance(vuln_info, list): raise TypeError("Expected list, got '{}' instead".format( type(vuln_info))) else: for x in vuln_info: if not isinstance(x, Vulnerability): raise TypeError( "Expected Vulnerability, got '{}' instead".format(type(x))) if not isinstance(output_file, str): raise TypeError("Expected str, got '{}' instead".format( type(output_file))) else: if not output_file: raise ValueError("output_file must have a valid name.") if template is not None: if not isinstance(template, str): raise TypeError("Expected str, got '{}' instead".format( type(template))) vuln_info, vuln_levels, vuln_host_by_level, vuln_by_family = _get_collections( vuln_info) # ==================== # DOCUMENT PROPERTIES # ==================== # Create new doc if template is None: document = Document() doc_section = document.sections[0] # Set A4 Format doc_section.page_width = Cm(21.0) doc_section.page_height = Cm(29.7) # Shrink margins almost to 0 doc_section.left_margin = Cm(1.5) doc_section.right_margin = doc_section.left_margin doc_section.top_margin = Cm(1.0) doc_section.bottom_margin = Cm(1.0) # Force portrait doc_section.orientation = WD_ORIENT.PORTRAIT # use template else: document = Document(template) doc_section = document.sections[0] # Defining styles (if not defined already) # All used style will be custom, and with 'OR-' prefix. # In this way, the template can still define styles. doc_styles = document.styles # Base paragraph if 'OR-base' not in doc_styles: style_pr_base = doc_styles.add_style('OR-base', WD_STYLE_TYPE.PARAGRAPH) style_pr_base.font.name = 'Ubuntu' style_pr_base.font.size = Pt(8) style_pr_base.font.color.rgb = RGBColor.from_string('080808') style_pr_base.paragraph_format.left_indent = Cm(0) style_pr_base.paragraph_format.right_indent = Cm(0) style_pr_base.paragraph_format.first_line_indent = Cm(0) style_pr_base.paragraph_format.space_before = Cm(0) style_pr_base.paragraph_format.space_after = Cm(0) style_pr_base.paragraph_format.line_spacing_rule = WD_LINE_SPACING.SINGLE style_pr_base.paragraph_format.widow_control = True style_pr_base.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY_LOW # Base Styles modification if 'OR-base_bold' not in doc_styles: style_pr_body = doc_styles.add_style('OR-base_bold', WD_STYLE_TYPE.PARAGRAPH) style_pr_body.base_style = doc_styles['OR-base'] style_pr_body.font.bold = True # Section headers if 'OR-Heading_base' not in doc_styles: style_pr_or_head_base = doc_styles.add_style('OR-Heading_base', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_head_base.base_style = doc_styles['OR-base_bold'] style_pr_or_head_base.font.color.rgb = RGBColor.from_string('183868') style_pr_or_head_base.paragraph_format.space_after = Pt(4) # - Titles if 'OR-Title' not in doc_styles: style_pr_or_title = doc_styles.add_style('OR-Title', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_title.base_style = doc_styles['OR-Heading_base'] style_pr_or_title.font.size = Pt(36) style_pr_or_title.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER style_pr_or_head_base.paragraph_format.space_after = Pt(8) # - Headers if 'OR-Heading_1' not in doc_styles: style_pr_or_header = doc_styles.add_style('OR-Heading_1', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_header.base_style = doc_styles['OR-Heading_base'] style_pr_or_header.font.size = Pt(20) if 'OR-Heading_2' not in doc_styles: style_pr_or_header = doc_styles.add_style('OR-Heading_2', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_header.base_style = doc_styles['OR-Heading_base'] style_pr_or_header.font.size = Pt(16) if 'OR-Heading_3' not in doc_styles: style_pr_or_header = doc_styles.add_style('OR-Vuln_title', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_header.base_style = doc_styles['OR-Heading_base'] style_pr_or_header.font.size = Pt(12) # - Vulnerabilities Titles for name, rgb in Config.colors().items(): if 'OR-Vuln_title_' + name not in doc_styles: style_pr_or_header = doc_styles.add_style('OR-Vuln_title_' + name, WD_STYLE_TYPE.PARAGRAPH) style_pr_or_header.base_style = doc_styles['OR-Vuln_title'] style_pr_or_header.font.color.rgb = RGBColor.from_string(rgb[1:]) # - Host with vulnerabilities title if 'OR-Vuln_hosts' not in doc_styles: style_pr_or_header = doc_styles.add_style('OR-Vuln_hosts', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_header.base_style = doc_styles['OR-Heading_base'] style_pr_or_header.font.size = Pt(10) # TOC specific if 'OR-TOC_base' not in doc_styles: style_pr_or_toc_base = doc_styles.add_style('OR-TOC_base', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_toc_base.base_style = doc_styles['OR-base'] style_pr_or_toc_base.font.color.rgb = RGBColor.from_string('183868') if 'OR-TOC_1' not in doc_styles: style_pr_or_toc = doc_styles.add_style('OR-TOC_1', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_toc.base_style = doc_styles['OR-TOC_base'] style_pr_or_toc.font.bold = True if 'OR-TOC_2' not in doc_styles: style_pr_or_toc = doc_styles.add_style('OR-TOC_2', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_toc.base_style = doc_styles['OR-TOC_base'] if 'OR-TOC_3' not in doc_styles: style_pr_or_toc = doc_styles.add_style('OR-Toc_3', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_toc.base_style = doc_styles['OR-TOC_base'] if 'OR-TOC_4' not in doc_styles: style_pr_or_toc = doc_styles.add_style('OR-TOC_4', WD_STYLE_TYPE.PARAGRAPH) style_pr_or_toc.base_style = doc_styles['OR-TOC_base'] style_pr_or_toc.font.italic = True # Tables style # - Specific paragraph style to allow space before and after if 'OR-cell' not in doc_styles: style_pr_cell = doc_styles.add_style('OR-cell', WD_STYLE_TYPE.PARAGRAPH) style_pr_cell.base_style = doc_styles['OR-base'] style_pr_cell.paragraph_format.space_before = Pt(1.5) style_pr_cell.paragraph_format.space_after = Pt(1.5) if 'OR-cell_bold' not in doc_styles: style_pr_cell = doc_styles.add_style('OR-cell_bold', WD_STYLE_TYPE.PARAGRAPH) style_pr_cell.base_style = doc_styles['OR-cell'] style_pr_cell.font.bold = True # Clear all contents document._body.clear_content() # Set doc title doc_prop = document.core_properties doc_title = 'OpenVAS/GreenBone Report' doc_prop.title = doc_title doc_prop.category = "Report" # Effective writeable width # If margins set are float, try to fix (issue in python-docx: expected an int) # In this case, they _should be_ in twentieths of a point, so # multiply Twips helper try: doc_section.left_margin except ValueError as e: fixed_margin = float(re.search(": '(.+?)'", str(e)).group(1)) doc_section.left_margin = Twips(fixed_margin) try: doc_section.right_margin except ValueError as e: fixed_margin = float(re.search(": '(.+?)'", str(e)).group(1)) doc_section.right_margin = Twips(fixed_margin) page_width = doc_section.page_width - (doc_section.left_margin + doc_section.right_margin) ## Start actual document writing ## document.add_paragraph(doc_title, style='OR-Title') # ==================== # TABLE OF CONTENTS # ==================== # WARNING -Not working with LibreOffice document.add_paragraph('Table of Contents', style='OR-Heading_1') # keep the title as cover of the report document.add_page_break() par = document.add_paragraph(style='OR-base') run = par.add_run() fld_char = OxmlElement('w:fldChar') # creates a new element fld_char.set(qn('w:fldCharType'), 'begin') # sets attribute on element instr_text = OxmlElement('w:instrText') instr_text.set(qn('xml:space'), 'preserve') # sets attribute on element instr_text.text = r'TOC \h \z \t "OR-TOC_1;1;OR-OR-TOC_1;2;OR-TOC_3;3;OR-TOC_4;3"' fld_char2 = OxmlElement('w:fldChar') fld_char2.set(qn('w:fldCharType'), 'separate') fld_char3 = OxmlElement('w:t') fld_char3.text = "# Right-click to update field. #" fld_char2.append(fld_char3) fld_char4 = OxmlElement('w:fldChar') fld_char4.set(qn('w:fldCharType'), 'end') r_element = run._r r_element.append(fld_char) r_element.append(instr_text) r_element.append(fld_char2) r_element.append(fld_char4) document.add_page_break() # ==================== # MANAGEMENT SUMMARY # ==================== document.add_paragraph('Management Summary', style='OR-Heading_1') document.add_paragraph('< TYPE YOUR MANAGEMENT SUMMARY HERE >', style='OR-base') document.add_page_break() # ==================== # TECHNICAL FINDINGS # ==================== document.add_paragraph('Technical Findings', style='OR-Heading_1') document.add_paragraph( 'The section below discusses the technical findings.', style='OR-base') # -------------------- # SUMMARY TABLE # -------------------- document.add_paragraph('Summary', style='OR-Heading_2') colors_sum = [] labels_sum = [] vuln_sum = [] aff_sum = [] table_summary = document.add_table(rows=1, cols=3) # TABLE HEADERS # -------------------- hdr_cells = table_summary.rows[0].cells hdr_cells[0].paragraphs[0].add_run('Risk level') hdr_cells[1].paragraphs[0].add_run('Vulns number') hdr_cells[2].paragraphs[0].add_run('Affected hosts') # FIELDS # -------------------- # Provide data to table and charts for level in Config.levels().values(): row_cells = table_summary.add_row().cells row_cells[0].text = level.capitalize() row_cells[1].text = str(vuln_levels[level]) row_cells[2].text = str(vuln_host_by_level[level]) colors_sum.append(Config.colors()[level]) labels_sum.append(level) vuln_sum.append(vuln_levels[level]) aff_sum.append(vuln_host_by_level[level]) # Apply styles # -------------------- for h in hdr_cells: h.vertical_alignment = WD_ALIGN_VERTICAL.CENTER for p in h.paragraphs: p.style = doc_styles['OR-cell_bold'] for r in table_summary.rows[1:]: for c in r.cells: c.vertical_alignment = WD_ALIGN_VERTICAL.CENTER for p in c.paragraphs: p.style = doc_styles['OR-cell'] # -------------------- # CHART # -------------------- fd, path = tempfile.mkstemp(suffix='.png') chart_dpi = 144 chart_height = Cm(8) par_chart = document.add_paragraph(style='OR-base') par_chart.alignment = WD_ALIGN_PARAGRAPH.CENTER run_chart = par_chart.add_run() bar_chart, bar_axis = plt.subplots(dpi=chart_dpi) bar_axis.set_title('Vulnerability summary by risk level', fontsize=10) pos = np.arange(len(labels_sum)) width = 0.35 bar_axis.set_xticks(pos) bar_axis.set_xticklabels(labels_sum) bar_chart.gca().spines['left'].set_visible(False) bar_chart.gca().spines['right'].set_visible(False) bar_chart.gca().spines['top'].set_visible(False) bar_chart.gca().spines['bottom'].set_position('zero') bar_axis.tick_params(top=False, bottom=True, left=False, right=False, labelleft=False, labelbottom=True) bars_vuln = plt.bar(pos - width / 2, vuln_sum, width, align='center', label='Vulnerabilities', color=colors_sum, edgecolor='black') bars_aff = plt.bar(pos + width / 2, aff_sum, width, align='center', label='Affected hosts', color=colors_sum, edgecolor='black', hatch='//') for barcontainer in (bars_vuln, bars_aff): for bar in barcontainer: height = bar.get_height() bar_chart.gca().text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.3, str(int(height)), ha='center', color='black', fontsize=8) bar_chart.legend() bar_chart.savefig(path) # plt.show() # DEBUG bar_height = chart_height run_chart.add_picture(path, height=bar_height) os.remove(path) pie_chart, pie_axis = plt.subplots(dpi=chart_dpi, subplot_kw=dict(aspect="equal")) pie_axis.set_title('Vulnerability by family', fontsize=10) values = list(vuln_by_family.values()) pie, tx, autotexts = pie_axis.pie(values, labels=vuln_by_family.keys(), autopct='', textprops=dict(fontsize=8)) for i, txt in enumerate(autotexts): txt.set_text('{}'.format(values[i])) pie_chart.savefig(path) # plt.show() # DEBUG pie_height = chart_height run_chart.add_picture(path, height=pie_height) os.remove(path) # ==================== # VULN PAGES # ==================== cur_level = "" for i, vuln in enumerate(vuln_info, 1): # -------------------- # GENERAL # -------------------- level = vuln.level.lower() if level != cur_level: document.add_paragraph( level.capitalize(), style='OR-Heading_2').paragraph_format.page_break_before = True cur_level = level else: document.add_page_break() title = "[{}] {}".format(level.upper(), vuln.name) par = document.add_paragraph(title, style='OR-Vuln_title_' + vuln.level) table_vuln = document.add_table(rows=8, cols=3) table_vuln.autofit = False table_vuln.columns[0].width = Cm(0.35) table_vuln.columns[1].width = Cm(2.85) table_vuln.columns[-1].width = page_width for c in range(len(table_vuln.columns) - 1): table_vuln.columns[-1].width -= table_vuln.columns[c].width # COLOR # -------------------- col_cells = table_vuln.columns[0].cells col_cells[0].merge(col_cells[7]) color_fill = parse_xml(r'<w:shd {} w:fill="{}"/>'.format( nsdecls('w'), Config.colors()[vuln.level][1:])) col_cells[0]._tc.get_or_add_tcPr().append(color_fill) # TABLE HEADERS # -------------------- hdr_cells = table_vuln.columns[1].cells hdr_cells[0].paragraphs[0].add_run('Description') hdr_cells[1].paragraphs[0].add_run('Impact') hdr_cells[2].paragraphs[0].add_run('Recommendation') hdr_cells[3].paragraphs[0].add_run('Details') hdr_cells[4].paragraphs[0].add_run('CVSS') hdr_cells[5].paragraphs[0].add_run('CVEs') hdr_cells[6].paragraphs[0].add_run('Family') hdr_cells[7].paragraphs[0].add_run('References') # FIELDS # -------------------- cves = ", ".join(vuln.cves) cves = cves.upper() if cves != "" else "No CVE" cvss = str(vuln.cvss) if vuln.cvss != -1.0 else "No CVSS" txt_cells = table_vuln.columns[2].cells txt_cells[0].text = vuln.description txt_cells[1].text = vuln.impact txt_cells[2].text = vuln.solution txt_cells[3].text = vuln.insight txt_cells[4].text = cvss txt_cells[5].text = cves txt_cells[6].text = vuln.family txt_cells[7].text = vuln.references for c in txt_cells: for p in c.paragraphs: p.style = doc_styles['OR-cell'] # Apply styles # -------------------- for h in hdr_cells: for p in h.paragraphs: p.style = doc_styles['OR-cell_bold'] for c in txt_cells: for p in c.paragraphs: p.style = doc_styles['OR-cell'] # VULN HOSTS # -------------------- document.add_paragraph('Vulnerable hosts', style='OR-Vuln_hosts') # add coloumn for result per port and resize columns table_hosts = document.add_table(cols=5, rows=(len(vuln.hosts) + 1)) table_hosts.columns[0].width = Cm(2.8) table_hosts.columns[1].width = Cm(3.0) table_hosts.columns[2].width = Cm(2.0) table_hosts.columns[3].width = Cm(2.0) table_hosts.columns[-1].width = page_width for c in range(len(table_hosts.columns) - 1): table_hosts.columns[-1].width -= table_hosts.columns[c].width # TABLE HEADERS # -------------------- hdr_cells = table_hosts.rows[0].cells hdr_cells[0].paragraphs[0].add_run('IP') hdr_cells[1].paragraphs[0].add_run('Host name') hdr_cells[2].paragraphs[0].add_run('Port number') hdr_cells[3].paragraphs[0].add_run('Port protocol') hdr_cells[4].paragraphs[0].add_run('Port result') # FIELDS # -------------------- for j, (host, port) in enumerate(vuln.hosts, 1): cells = table_hosts.rows[j].cells cells[0].text = host.ip cells[1].text = host.host_name if host.host_name else "-" if port and port is not None: cells[2].text = "-" if port.number == 0 else str(port.number) cells[3].text = port.protocol cells[4].text = port.result else: cells[2].text = "No port info" # Apply styles # -------------------- for h in hdr_cells: for p in h.paragraphs: p.style = doc_styles['OR-cell_bold'] for r in table_hosts.rows[1:]: for c in r.cells: for p in c.paragraphs: p.style = doc_styles['OR-cell'] document.save(output_file)
def processData(dirname, sheet, domainList): ''' 第一部分:读取数据 ''' #读取 for file in os.listdir(HTMLPATH + dirname): if file.endswith('html'): htmlFile = open(HTMLPATH + dirname + '/' + file, 'r') soup = BeautifulSoup(htmlFile, 'html.parser') htmlFile.close() break table_report_table = soup.find_all('table', class_='report_table') tr_even = table_report_table[0].find_all('tr', class_='even') #================================================= # 搜集内容:域名domain domain = tr_even[0].td.text.split('/')[2] # ================================================ # 判断是哪种风险,找出为未知风险的报告 risk = tr_even[1].td.text if risk == '未知风险': # 从excel中找到相应的域名 flag = False i = 0 for i in range(len(domainList)): if domainList[i].value == domain: flag = True break # 如果没找到,则报错并返回 if flag == False: print(domain + ':汇总表中未找到') return 0 #找到的,则在对应的备注栏填写'站点不可达' sheet.cell(i + 3, 11, '站点不可达') return 0 #================================================ #搜集内容:时间timeStatistics[] for tr in tr_even: if tr.find('th', text='时间统计') != None: timeStatistics = tr.td.text.split('\n')[1:4] timeStatistics[0] = timeStatistics[0].strip()[3:22] #timeStart timeStatistics[1] = timeStatistics[1].strip()[3:22] #timeStop timeStatistics[2] = timeStatistics[2].strip()[3:] #timeConsume break #================================================ #搜集内容:高中低漏洞数distribut[],漏洞总数sum_all sum_all = 0 distribut = tr_even[2].td.text.split('\n')[1:4] for i in range(len(distribut)): distribut[i] = int(distribut[i].strip()[4:-2]) sum_all += distribut[i] #================================================ #搜集内容:检测结果result[],result里面每个元素都是列表,包含4个元素 table_tmp = [] result = [] for table in table_report_table: if table.find('th', text='漏洞名称') != None: table_tmp.append(table) table = table_tmp[-1].find_all('tr')[1:] for tr in table: td = tr.find_all('td') tdClass = td[0].attrs['class'] #这里tdClass是list格式,虽然这里只有1个元素 if tdClass[0] == 'vul-vh': result.append([ td[0].text.split('\n')[2].strip(), td[1].text.strip(), '高危', 'EB3323' ]) elif tdClass[0] == 'vul-vm': result.append([ td[0].text.split('\n')[2].strip(), td[1].text.strip(), '中危', 'F6C143' ]) else: result.append([ td[0].text.split('\n')[2].strip(), td[1].text.strip(), '低危', '4EAC5B' ]) ''' 第二部分:写到word和excel中 ''' #从excel中找到相应的域名 flag = False i = 0 for i in range(len(domainList)): if domainList[i].value == domain: flag = True break #如果没找到,则报错并返回 if flag == False: print(domain + ':汇总表中未找到') return 0 # 获取当前年月日 timeStr = time.strftime('%Y%m%d', time.localtime()) #设置文件名 wordFileName = '006' + timeStr + str(i + 1).zfill(5) + 'a.docx' wordFileName_Pdf = '006' + timeStr + str(i + 1).zfill(5) + 'a.pdf' pdfFileName = '006' + timeStr + str(i + 1).zfill(5) + 'ad.pdf' wordFilePath = WORDPATH + wordFileName # 写入excel for j in range(len(distribut)): #distribut里放的是数字形式,如果为string,则excel里面也会算文字 sheet.cell(i + 3, j + 3, distribut[j]) sheet.cell(i + 3, j + 4, timeStatistics[0]) sheet.cell(i + 3, j + 5, wordFileName_Pdf) #从模板word复制到WORDPATH下 os.system('cp ' + templateFilePath + ' ' + wordFilePath) wordFile = Document(wordFilePath) tables = wordFile.tables #第一个表所有和第二个表的时间 ##域名 run = tables[0].cell(0, 1).paragraphs[0].add_run(domain) run.font.size = Pt(16) run.font.bold = 1 ##时间 for i in range(len(timeStatistics)): #1表 run = tables[0].cell(i + 1, 1).paragraphs[0].add_run(timeStatistics[i]) run.font.size = Pt(16) run.font.bold = 1 #2表 run = tables[1].cell(i + 3, 1).paragraphs[0].add_run(timeStatistics[i]) run.font.size = Pt(11) #第二个表剩余的内容 ##域名 run = tables[1].cell(0, 1).paragraphs[0].add_run(domain) run.font.size = Pt(11) ##漏洞数 run = tables[1].cell(1, 1).paragraphs[0].add_run(str(distribut[0])) run.font.size = Pt(11) run = tables[1].cell(1, 3).paragraphs[0].add_run(str(distribut[1])) run.font.size = Pt(11) run = tables[1].cell(2, 1).paragraphs[0].add_run(str(distribut[2])) run.font.size = Pt(11) run = tables[1].cell(2, 3).paragraphs[0].add_run(str(sum_all)) run.font.size = Pt(11) #第三个表 for i in range(len(result)): new_cells = tables[2].add_row().cells #序号 run = new_cells[0].paragraphs[0].add_run(str(i + 1)) run.font.size = Pt(11) run.font.bold = 1 new_cells[0].paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #检测问题 run = new_cells[1].paragraphs[0].add_run(result[i][0]) run.font.size = Pt(11) #漏洞数 run = new_cells[2].paragraphs[0].add_run(result[i][1]) run.font.size = Pt(11) new_cells[2].paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #风险程度 run = new_cells[3].paragraphs[0].add_run(result[i][2]) run.font.size = Pt(11) run.font.bold = 1 run.font.color.rgb = RGBColor.from_string(result[i][3]) new_cells[3].paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #保存文档 wordFile.save(wordFilePath) ''' 重命名pdf ''' os.system('cp ' + PDFPATH + dirname + '/http_' + domain + '.pdf ' + PDFOUTPUTPATH + pdfFileName) return 0
def when_I_assign_value_to_font_color_rgb(context, value): font = context.font new_value = None if value == 'None' else RGBColor.from_string(value) font.color.rgb = new_value
def writeTable(sctPars, filename, obstime): """ Write *.docx and *.tex tables to insert in the flight description. """ import re import os from docx import Document from docx.shared import Inches from docx.shared import Cm from docx.shared import Pt from docx.shared import RGBColor from astropy import units as u from astropy.coordinates import SkyCoord # Check mode if sctPars['INSTMODE'] in ['SYMMETRIC_CHOP', 'TOTAL_POWER']: pass else: print( 'Table cannot be yet saved in this mode. Wait for future implementations !' ) return lines = { 51.814: '[OIII]', 54.311: '[FeI]', 56.311: '[SI]', 57.317: '[NIII]', 60.640: '[PII]', 63.184: '[OI]', 67.200: '[FII]', 68.473: '[SiI]', 87.384: '[FeII]', 88.356: '[OIII]', 89.237: '[AlI]', 105.370: '[FeIII]', 111.183: '[FeI]', 121.898: '[NII]', 129.682: '[SiI]', 145.525: '[OI]', 157.741: '[CII]', 177.431: '[CIII]', 205.178: '[NII]' } blue = sctPars["BLUE_MICRON"] red = sctPars["RED_MICRON"] wb = float(blue) wr = float(red) blueline = '?' redline = '?' for key in lines.keys(): if np.abs(wb - key) < 0.01: blueline = ' ' + lines[key] if np.abs(wr - key) < 0.01: redline = ' ' + lines[key] # Look for readme file aor_label = re.findall(r"act(\d+)", filename)[0] aorfile = 'act' + aor_label + '.readme' if os.path.exists(aorfile): with open(aorfile) as f: content = f.read().splitlines() if len(content) == 1: time_planned = content[0] comments = 'Comments: None' elif len(content) == 2: time_planned = content[0] + ' m' comments = content[1] else: print( 'The readme file has too many lines.\n First line is time, second (optional) comments.' ) time_planned = ' ? m' comments = 'Comments: None' else: print('There is no readme file with time planned (and comments).') time_planned = ' ? m' comments = 'Comments: None' document = Document() # Change font, size, and color run = document.add_paragraph().add_run('Observations') font = run.font font.bold = True font.name = 'Calibri' font.size = Pt(12) font.color.rgb = RGBColor.from_string('002a77') table = document.add_table(rows=6, cols=6) # Fix width of cells for cell in table.columns[0].cells: cell.width = Cm(2) for cell in table.columns[1].cells: cell.width = Cm(4) for cell in table.columns[2].cells: cell.width = Cm(5) for cell in table.columns[3].cells: cell.width = Cm(4) for cell in table.columns[4].cells: cell.width = Cm(4) for cell in table.columns[5].cells: cell.width = Cm(4) row1 = table.rows[0] act = row1.cells[0] act.text = 'Act\n' + aor_label for paragraph in act.paragraphs: for run in paragraph.runs: run.font.bold = True target = row1.cells[1].merge(row1.cells[2]) target.text = 'Target\n' + sctPars["TARGET_NAME"] row1.cells[3].text = 'AOR\n' + sctPars["AORID"] coords = row1.cells[4].merge(row1.cells[5]) coords.text = 'R.A.: ' + sctPars["TARGET_LAMBDA"] + '\nDec:' + sctPars[ "TARGET_BETA"] row2 = table.rows[1] redshift = table.cell(1, 0).merge(table.cell(1, 2)) c = 299792.458 cz = '{0:.1f}'.format(float(sctPars["REDSHIFT"]) * c) redshift.text = 'Redshift: ' + cz + ' km/s' redshift.text += '\n Rest Wavelength:' redshift.text += '\n Grating steps:' redshift.text += '\n Grating positions per nod:' nodcycles = sctPars['NODCYCLES'] ngratpos = sctPars['BLUE_POSUP'] # hypothesis of posup only gratstep = sctPars['BLUE_SIZEUP'] ngratnod = str(int(ngratpos) // int(nodcycles)) row2.cells[3].text = 'Blue grating\n' + blue + '\u00b5m' + blueline row2.cells[ 3].text += '\n' + ngratpos + ' \u00d7 ' + gratstep + '\n' + ngratnod redgrating = table.cell(1, 4).merge(table.cell(1, 5)) redgrating.text = 'Red grating\n' + red + '\u00b5m' + redline redgrating.text += '\n' + ngratpos + ' \u00d7 ' + gratstep + '\n' + ngratnod dichroic = table.cell(2, 0).merge(table.cell(2, 1)) dichroic.text = 'Dichroic: ' + sctPars["DICHROIC"] table.cell(2, 2).text = 'Blue order: M' + sctPars["ORDER"] gratingmode = table.cell(2, 3).merge(table.cell(2, 5)) gratingmode.text = 'Grating mode: ' + sctPars['BLUE_LAMBDA'] # Case of symmetric chop if sctPars['INSTMODE'] == 'SYMMETRIC_CHOP': # Chopping part mode = table.cell(3, 0).merge(table.cell(3, 1)) mode.text = 'Mode:\n' + sctPars['OBSMODE'] + ' ' + sctPars['NODPATTERN'] table.cell(3, 2).text = 'Chop throw:\n' + sctPars['CHOP_AMP'] + '"' table.cell( 3, 3).text = 'Chop angle:\n' + sctPars['CHOP_POSANG'] + '\u00b0 J2000' table.cell(3, 4).text = 'Primary array: ' + sctPars['PRIMARYARRAY'] table.cell( 3, 5).text = 'FOV angle:\n' + sctPars['DETANGLE'] + '\u00b0 J2000' elif sctPars['INSTMODE'] == 'TOTAL_POWER': mode = table.cell(3, 0).merge(table.cell(3, 1)) mode.text = 'Mode:\n' + sctPars['INSTMODE'] + ' ' + sctPars[ 'NODPATTERN'] table.cell(3, 2).text = 'Off mode:\n' + sctPars['OFFPOS'] #ra = float(sctPars['OFFPOS_LAMBDA']) Wrong ! This is not the position #dec = float(sctPars['OFFPOS_BETA']) #c = SkyCoord(ra=ra*u.degree, dec=dec*u.degree, frame='icrs') #sra, sdec = c.to_string('hmsdms').split(' ') table.cell(3, 3).text = 'Offset:\n' + sctPars[ 'OFFPOS_LAMBDA'] + '\n' + sctPars['OFFPOS_BETA'] table.cell(3, 4).text = 'Primary array: ' + sctPars['PRIMARYARRAY'] table.cell( 3, 5).text = 'FOV angle:\n' + sctPars['DETANGLE'] + '\u00b0 J2000' dithering = table.cell(4, 0).merge(table.cell(4, 1)) dithering.text = 'Map: ' + sctPars['DITHMAP_NUMPOINTS'] + ' point dither' timeplanned = table.cell(4, 2).merge(table.cell(4, 3)) timeplanned.text = 'Time planned: ' + time_planned timerunning = table.cell(4, 4).merge(table.cell(4, 5)) obstime = '{0:.1f} m'.format(float(obstime) / 60) timerunning.text = 'Time running: ' + obstime comment = table.cell(5, 0).merge(table.cell(5, 5)) comment.text = comments table.style = 'Table Grid' table.allow_autofit = True # Change font, size, and color for row in table.rows: for cell in row.cells: paragraphs = cell.paragraphs for paragraph in paragraphs: for run in paragraph.runs: font = run.font font.name = 'Calibri' font.size = Pt(12) #font.color.rgb = RGBColor(0x3f, 0x2c, 0x36) font.color.rgb = RGBColor.from_string('002a77') # Save file #filename = filename[:-4]+'.docx' document.save(filename) # Latex format table for future flight descriptions in Latex filename = filename[:-5] + '.tex' with open(filename, 'w') as f: f.write(r'%\documentclass{article}' + '\n') f.write(r'%\begin{document}' + '\n') f.write(r'\begin{table}' + '\n' + r'\centering' + '\n') f.write(r'\begin{tabular}{llll}' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') f.write(r'\multicolumn{2}{l}{\bf Act ' + aor_label + '}&{\sl AOR ID:} ' + sctPars["AORID"].replace('_', '\_') + '&{\sl P.I.:} ' + sctPars["OBSERVER"] + r'\\' + '\n') f.write(r'\multicolumn{2}{l}{' + sctPars["TARGET_NAME"].replace('_', '\_') + '}& ' + sctPars["TARGET_LAMBDA"].replace(' ', ':') + ' ' + sctPars["TARGET_BETA"].replace(' ', ':') + '& ' + cz + r' km/s\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') f.write(r'\multicolumn{2}{l}{\sl Grating} & Blue & Red\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\multicolumn{2}{l}{\sl Reference Wavelength:} & ' + blue + r'$\mu$m' + blueline + ' &' + red + r'$\mu$m' + redline + r'\\' + '\n') f.write(r'\multicolumn{2}{l}{\sl Grating Steps:} & ' + ngratpos + r' $\times$ ' + gratstep + '& ' + ngratpos + r' $\times$ ' + gratstep + r'\\' + '\n') f.write(r'\multicolumn{2}{l}{\sl Grating Steps per Nod:} &' + ngratnod + '&' + ngratnod + r'\\' + '\n') f.write( r'\multicolumn{2}{l}{\sl Grating mode: }& \multicolumn{2}{l}{' + sctPars['BLUE_LAMBDA'] + r'}\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') f.write('{\sl Dichroic: } ' + sctPars["DICHROIC"] + '&{\sl Blue order: } M' + sctPars["ORDER"] + '&{\sl Primary array: } ' + sctPars['PRIMARYARRAY'] + '&{\sl FOV angle: } ' + sctPars['DETANGLE'] + r'$^o$ J2000' + r'\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') if sctPars['INSTMODE'] == 'SYMMETRIC_CHOP': f.write(r'{\sl Chop}&{\sl Mode}&{\sl Throw}&{\sl Angle}\\' + '\n') f.write(r'\hline' + '\n') f.write('&' + sctPars['OBSMODE'] + ' ' + sctPars['NODPATTERN'] + '&' + sctPars['CHOP_AMP'] + r'"' + '&' + sctPars['CHOP_POSANG'] + '$^o$ J2000' + r'\\' + '\n') else: f.write( r'{\sl Mode}&{\sl Nod pattern}&{\sl Offset}&{\sl Coords}\\' + '\n') f.write(r'\hline' + '\n') f.write(sctPars['INSTMODE'] + ' &' + sctPars['NODPATTERN'] + '&' + sctPars['OFFPOS'] + #'&'+ sra +' '+sdec+r'\\'+'\n') '&' + sctPars['OFFPOS_LAMBDA'] + ' ' + sctPars['OFFPOS_BETA'] + r'\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') f.write(r'\multicolumn{2}{l}{{\sl Map}: ' + sctPars['DITHMAP_NUMPOINTS'] + ' point dither}' + r'&{\sl Time planned: } ' + time_planned + r'&{\sl Time running: } ' + obstime + r'\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') f.write(r'\multicolumn{4}{l}{' + comments + r'}\\' + '\n') f.write(r'\hline' + '\n') f.write(r'\hline' + '\n') f.write(r'\end{tabular}' + '\n') f.write(r'\end{table}' + '\n') f.write(r'%\end{document}')
def get_para_object(count, label, body, word_list, par, name): run = par.add_run("{}, Label: {}".format(count, str(label))) run.font.size = Pt(7) run.font.bold = True run.font.underline = True run.font.all_caps = True # run.font.outline = True if label >= 4: # run.font.highlight_color = WD_COLOR_INDEX.RED run.font.color.rgb = RGBColor.from_string('FF0000') elif label == 3: # run.font.highlight_color = WD_COLOR_INDEX.YELLOW run.font.color.rgb = RGBColor.from_string('808000') elif label < 3: # run.font.highlight_color = WD_COLOR_INDEX.BRIGHT_GREEN run.font.color.rgb = RGBColor.from_string('6897BB') run.add_break(WD_BREAK.LINE) body = ' '.join(body.split()) if type(name) != type(' '): names = [] else: names = name.split(";") # print (names) for word in word_list: f = word.split()[0] s = word.split()[1] if len(f) <= 3 and len(s) <= 3: word_list.remove(word) h_body = get_highlighted_body(body, word_list) h_body = unify_phrases(h_body) # print (h_body) r = re.compile('HSTART_') fragments = r.split(h_body) # par = doc.add_paragraph() second = fragments[0] done = False for n in names: if n in second: name_ind = second.lower().index(n.lower()) run = par.add_run(second[:name_ind]) run.font.size = Pt(6) run = par.add_run(second[name_ind:name_ind + len(n)]) run.font.size = Pt(9) run.font.italic = True run.font.underline = True run = par.add_run(second[name_ind + len(n):]) run.font.size = Pt(6) done = True break if not done: run = par.add_run(fragments[0]) run.font.size = Pt(6) # run.font.size = Pt(6) for ind, f in enumerate(fragments[1:]): first = f.split('HEND')[0] second = f.split('HEND')[1] # count = int(first[0]) count = int(first[0]) first = first[1:] run = par.add_run(first) font_map_obj = FONT_MAP[count] run.font.size = Pt(8) run.font.highlight_color = font_map_obj['highlight'] run.font.color.rgb = font_map_obj['color'] done = False if second: for n in names: if n in second: name_ind = second.lower().index(n.lower()) run = par.add_run(second[:name_ind]) run.font.size = Pt(6) run = par.add_run(second[name_ind:name_ind + len(n)]) run.font.size = Pt(9) run.font.italic = True run.font.underline = True run = par.add_run(second[name_ind + len(n):]) run.font.size = Pt(6) done = True break if not done: run = par.add_run(second) run.font.size = Pt(6) return par
section = doc3.sections[0] new_width, new_height = section.page_height, section.page_width section.orientation = WD_ORIENT.LANDSCAPE section.page_width = new_width section.page_height = new_height # 2 - blue, 4 - bright green, 5 - dark_blue, 6 - dark red, 7 - dark yellow # 8 - gray, 10 - green, 11 -pink, 12 - red, 13- teal, 14- turquoise, 15- violet #141414 - black, FF0000 - red, F3FF69 - yellow, FFACB7 - pink, 808000- mehendi, 6897BB - blue FONT_MAP = { 0: { 'size': Pt(10), 'highlight': WD_COLOR_INDEX.BRIGHT_GREEN, 'color': RGBColor.from_string('141414') }, 1: { 'size': Pt(10), 'highlight': WD_COLOR_INDEX.PINK, 'color': RGBColor.from_string('141414') }, 2: { 'size': Pt(10), 'highlight': WD_COLOR_INDEX.RED, 'color': RGBColor.from_string('141414') }, 3: { 'size': Pt(10), 'highlight': WD_COLOR_INDEX.TURQUOISE, 'color': RGBColor.from_string('141414')
# font.size = Pt(36) # Each Document object provides access to its CoreProperties object via its core_properties attribute. # About CoreProperties objects: https://python-docx.readthedocs.org/en/latest/api/document.html#id1 document.core_properties.author = 'author' document.core_properties.comments = 'comments' # document.add_heading('Document Title', 0) p = document.add_paragraph('A plain paragraph having some ') p.add_run('bold').bold = True p.add_run(' and some ') p.add_run('italic.').italic = True # About RGBColor objects: https://python-docx.readthedocs.org/en/latest/api/shared.html#rgbcolor-objects p.add_run('\n天蓝色').font.color.rgb = RGBColor(0x87, 0xCE, 0xEB) # 天蓝色 p.add_run('WHITE').font.color.rgb = RGBColor.from_string('FFFFFF') # white p.add_run('RED').font.color.rgb = RGBColor.from_string('FF0000') # red p.add_run('BLUE').font.color.rgb = RGBColor.from_string('0000FF') # blue p.add_run('BLACK').font.color.rgb = RGBColor.from_string('000000') # black # 紫色:800080 天蓝色:87CEEB 黄色:FFFF00 # 白色: FFFFFF 红色: FF0000 蓝色: 0000FF 黑色: 000000 # More: http://www.360doc.com/content/12/0219/22/19147_187921920.shtml # add pic, original size picname = 'pic.png' if os.path.exists(picname): p.add_run('\nCode: ').font.color.rgb = RGBColor.from_string('0000FF') # blue # See: http://stackoverflow.com/questions/26617218/python-docx-add-picture-size-wont-print-as-shown-on-screen # document.add_picture('pic.png', width=Inches(4.9)) im = Image.open(picname) width, height = im.size
def generate_report_or_attachments(mission_id, zip_attachments=False): ''' Generates the report docx or attachments zip. :param mission_id: The id of the mission :param zip_attachments: True to return a zip of attachments, False to get the docx report as an IOStream :return: Returns StringIO if returning a report, a zip object otherwise ''' system_classification = DARTDynamicSettings.objects.get_as_object( ).system_classification system_classification_verbose = system_classification.verbose_legend system_classification_short = system_classification.short_legend mission = Mission.objects.get(id=mission_id) tests = TestSortingHelper.get_ordered_testdetails( mission_id=mission_id, reportable_tests_only=True) # Set some values we'll use throughout this section total_reportable_tests = len(tests) total_tests_with_findings = TestDetail.objects.filter( mission=mission, has_findings=True).count() total_tests_without_findings = TestDetail.objects.filter( mission=mission, has_findings=False).count() LIGHTEST_PERMISSIBLE_CLASSIFICATION_LABEL_COLOR = 0xbbbbbb DARKEN_OVERLY_LIGHT_CLASSIFICATION_LABEL_COLOR_BY = 0x444444 mission_data_dir = None report_has_attachments = False def replace_document_slugs(doc): """Cycle through the runs in each paragraph in the template & replace handlebar slugs""" logger.debug('> replace_document_slugs') handlebar_slugs = { r'{{AREA}}': str(mission.business_area), r'{{MISSION}}': str(mission.mission_name), r'{{GENERATION_DATE}}': now().strftime('%x'), r'{{TOTAL_TESTS}}': str(total_reportable_tests), r'{{TESTS_WITH_FINDINGS}}': str(total_tests_with_findings), r'{{TESTS_WITHOUT_FINDINGS}}': str(total_tests_without_findings), } for p in doc.paragraphs: for r in p.runs: for pattern in handlebar_slugs.keys(): if re.search(pattern, r.text): logger.debug('>> Replaced: {old} With: {new}'.format( old=r.text.encode('utf-8'), new=handlebar_slugs[pattern].encode('utf-8'))) r.text = re.sub(pattern, handlebar_slugs[pattern], r.text) def get_or_create_mission_data_dir(mission_data_dir): if mission_data_dir is None: mission_data_dir = os.path.join( settings.BASE_DIR, 'SUPPORTING_DATA_PACKAGE', str(mission.id) + "_" + str(now().strftime('%Y%m%d-%H%M%S'))) if not os.path.isdir(mission_data_dir): os.makedirs(mission_data_dir) return mission_data_dir def add_to_data_dir(mission_data_dir, test_case_number, supporting_data): # If test case directory path is not created, create it path = os.path.join(mission_data_dir, str(test_case_number)) # Shouldn't have race condition issues, but may need to add # try/catch if so later on if not os.path.isdir(path): os.makedirs(path) # Copy file to destination path shutil.copy( os.path.join(settings.MEDIA_ROOT, supporting_data.filename()), path) def prepend_classification(text): return '(' + system_classification_short + ') ' + text def portion_mark_and_insert(paragraphs, document): for paragraph in normalize_newlines(paragraphs).split('\n'): if len(paragraph) > 0: document.add_paragraph(prepend_classification(paragraph)) # Load the template file to get the styles document = Document(settings.REPORT_TEMPLATE_PATH) # Get the table templates table_no_findings = document.tables[0] table_with_findings = document.tables[1] data_table = document.tables[2] # Set the classification legend color from the background color of the banner classification_style = document.styles['Table Classification'] classification_font = classification_style.font # RGBColor doesn't handle shorthand hex codes, so let's just go ahead and expand it # if we come across a legacy or "misguided" entry if len(system_classification.background_color.hex_color_code) == 3: new_hex_code = '' for char in system_classification.background_color.hex_color_code: new_hex_code += char + char system_classification.background_color.hex_color_code = new_hex_code system_classification.background_color.save() if len(system_classification.text_color.hex_color_code) == 3: new_hex_code = '' for char in system_classification.text_color.hex_color_code: new_hex_code += char + char system_classification.text_color.hex_color_code = new_hex_code system_classification.text_color.save() classification_font.color.rgb = RGBColor.from_string( system_classification.get_report_label_color().hex_color_code) # Intro H1 and text document.add_heading('Introduction', level=1) portion_mark_and_insert(mission.introduction, document) # Scope H1 and text document.add_heading('Scope', level=1) portion_mark_and_insert(mission.scope, document) # Objectives H1 and text document.add_heading('Objectives', level=1) portion_mark_and_insert(mission.objectives, document) # Exec Summary H1 and text document.add_heading('Executive Summary', level=1) portion_mark_and_insert(mission.executive_summary, document) # Technical Assessment / Attack Architecture and text H1 document.add_heading('Technical Assessment / Attack Architecture', level=1) portion_mark_and_insert(mission.technical_assessment_overview, document) # Technical Assessment / Test Cases and Results and loop document.add_heading('Technical Assessment / Test Cases and Results', level=1) # For each test, Test # - Objective Attack Phase: H2 """ Note: If a test case is hidden from the report, the test_case number and the auto-generated paragraph number in Word won't align and it could cause confusion. The team may need to discuss how they want to handle this situation. """ """ WARNING Hidden test cases are NOT numbered in the report so the customer doesn't think something was accidentally left out. This will result in test case numbering differing between report and web when one or more test cases are hidden from the report. """ test_case_number = 0 tests_with_findings = 0 tests_without_findings = 0 # Let's just always craete a mission data dir when zipping attachments on report generation; this way # mission attachments will always return a zip file... it just may be empty - located here to account for times # when Get Attachments is actuated, but there are no test cases yet. if zip_attachments: mission_data_dir = get_or_create_mission_data_dir(mission_data_dir) for t in tests: if test_case_number > 0: document.add_page_break() test_case_number += 1 if t.has_findings: test_title = "*" tests_with_findings += 1 else: test_title = "" tests_without_findings += 1 if t.enclave: test_title += "(%s) %s" % (t.enclave, t.test_objective) else: test_title += "%s" % (t.test_objective, ) document.add_heading(test_title, level=2) # Duplicate one of the pre-made tables; if this is the last of a specific type of # test case (findings / no findings), use the cut operation to remove the blank # table. #TODO: convert to logging: print("T w/ F: {0}\nTotal: {1}\nT w/o F: {2}\nTotal: {3}".format(tests_with_findings, total_tests_with_findings, tests_without_findings, total_tests_without_findings)) is_last_test_case = True if test_case_number == total_reportable_tests else False if t.has_findings: if tests_with_findings == total_tests_with_findings or is_last_test_case: table = copy_table(document, table_with_findings, cut=True) else: table = copy_table(document, table_with_findings, cut=False) else: if tests_without_findings == total_tests_without_findings or is_last_test_case: table = copy_table(document, table_no_findings, cut=True) else: table = copy_table(document, table_no_findings, cut=False) # Classification Marking - Top cell = table.cell(0, 0) get_cleared_paragraph(cell).text = system_classification_verbose # Classification Marking - Bottom cell = table.cell(16, 1) get_cleared_paragraph(cell).text = system_classification_verbose # Test Case Number (Table Header Row) cell = table.cell(1, 0) get_cleared_paragraph(cell).text = 'Test #{0}'.format(test_case_number) row_number = 0 # # Test Case Title (Table Header Row) # row_number += 1 cell = table.cell(row_number, 1) get_cleared_paragraph(cell).text = t.test_objective # # Attack Phase / Type # row_number += 1 include_attack_phase = False include_attack_type = False if mission.attack_phase_include_flag \ and t.attack_phase_include_flag \ and len(t.get_attack_phase_display())> 0: include_attack_phase = True if mission.attack_type_include_flag \ and t.attack_type_include_flag \ and len(t.attack_type) > 0: include_attack_type = True left_cell = table.cell(row_number, 0) right_cell = table.cell(row_number, 1) if include_attack_phase or include_attack_type: if include_attack_phase and include_attack_type: # Table text in column 1 assumes both items are included already right_cell.text = ' - '.join( [t.get_attack_phase_display(), t.attack_type]) elif include_attack_phase: get_cleared_paragraph(left_cell).text = "Attack Phase:" right_cell.text = t.get_attack_phase_display() elif include_attack_type: get_cleared_paragraph(left_cell).text = "Attack Type:" right_cell.text = t.attack_type else: logger.debug('Removing Attack Phase/Type Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Assumptions # row_number += 1 if mission.assumptions_include_flag and t.assumptions_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field(t.assumptions) else: logger.debug('Removing Assumptions Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Description # row_number += 1 if mission.test_description_include_flag and t.test_description_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field(t.test_description) else: logger.debug('Removing Description Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Findings # row_number += 1 if mission.findings_include_flag and t.findings_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field(t.findings) else: logger.debug('Removing Findings Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Mitigations # row_number += 1 if mission.mitigation_include_flag and t.mitigation_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field(t.mitigation) else: logger.debug('Removing Mitigations Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Tools # row_number += 1 if mission.tools_used_include_flag and t.tools_used_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field(t.tools_used) else: logger.debug('Removing Tools Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Commands / Syntax # row_number += 1 if mission.command_syntax_include_flag and t.command_syntax_include_flag: cell = get_cleared_paragraph(table.cell(row_number, 1)) cell.style = 'Normal-LeftAlign' cell.text = standardize_report_output_field(t.command_syntax) else: logger.debug('Removing Commands/Syntax Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Targets # row_number += 1 if mission.targets_include_flag and t.targets_include_flag: cell = table.cell(row_number, 1) cell.text = '\n'.join([str(x) for x in t.target_hosts.all()]) else: logger.debug('Removing Targets Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Sources # row_number += 1 if mission.sources_include_flag and t.sources_include_flag: cell = table.cell(row_number, 1) cell.text = '\n'.join([str(x) for x in t.source_hosts.all()]) else: logger.debug('Removing Sources Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Date / Time # row_number += 1 if mission.attack_time_date_include_flag and t.attack_time_date_include_flag: cell = table.cell(row_number, 1) cell.text = localtime( t.attack_time_date).strftime('%b %d, %Y @ %I:%M %p') else: logger.debug('Removing Date/Time Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Side Effects # row_number += 1 if mission.attack_side_effects_include_flag and t.attack_side_effects_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field(t.attack_side_effects) else: logger.debug('Removing Side Effects Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Details # row_number += 1 if mission.test_result_observation_include_flag and t.test_result_observation_include_flag: cell = table.cell(row_number, 1) cell.text = standardize_report_output_field( t.test_result_observation) else: logger.debug('Removing Details Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Supporting Data # row_number += 1 supporting_data_cell = None supporting_data_row = None if mission.supporting_data_include_flag: supporting_data_cell = table.cell(row_number, 1) supporting_data_row = table.rows[row_number] else: logger.debug('Removing Supporting Data Row') remove_row(table, table.rows[row_number]) row_number -= 1 # # Notes - Used for post report generation notes / customer use # row_number += 1 if False: #TODO: add mission-level toggle logger.debug('Removing Customer Notes Row') remove_row(table, table.rows[row_number]) row_number -= 1 # Attachments section supporting_data_cell_items = [] if mission.supporting_data_include_flag: my_data = SupportingData.objects.filter( test_detail_id=t.id).filter(include_flag=True) if len(my_data) > 0: is_first_screenshot = True for d in my_data: allowed_image_types = [ 'gif', 'tiff', 'jpeg', 'bmp', 'png', ] try: file_path = os.path.join(settings.MEDIA_ROOT, d.filename()) logger.debug( '>> Beginning processing of {} at {}.'.format( d.filename(), file_path, )) if imghdr.what(file_path) not in allowed_image_types: raise UnrecognizedImageError( 'File type is not in the allowed image types. ' 'Handling as non-image.') if is_first_screenshot: document.add_heading('Screenshots / Diagrams', level=3) logger.debug( 'This is the first screenshot of this test case.' ) is_first_screenshot = False image_table = copy_table(document, data_table) logger.debug('Creating a new image table.') document.add_paragraph() # Classification Marking - Top cell = image_table.cell(0, 0) get_cleared_paragraph( cell).text = system_classification_verbose # Classification Marking - Bottom cell = image_table.cell(2, 0) get_cleared_paragraph( cell).text = system_classification_verbose content_cell = image_table.cell(1, 0) get_cleared_paragraph( content_cell).add_run().add_picture( d.test_file, width=Inches(5)) content_cell.paragraphs[0].add_run("\r" + d.caption) except UnrecognizedImageError as e: logger.debug( '>> Attachment {attachment_name} not recognized as an image; adding as file.' .format(attachment_name=d.filename())) supporting_data_cell_items.append( '- {filename}: {caption}'.format( filename=d.filename(), caption=d.caption, )) if zip_attachments: add_to_data_dir(mission_data_dir, test_case_number, d) except (InvalidImageStreamError, UnexpectedEndOfFileError) as e: logger.warning( '>> Attempting to add {file_name} to the report output resulted in an error: ' '\n{trace}'.format(file_name=d.filename, trace=traceback.format_exc(10))) except OSError as e: logger.warning( '>> Attempting to add {file_name} to the report output resulted in an error: ' '\n{trace}'.format(file_name=d.filename, trace=traceback.format_exc(10))) try: if not d.test_file.closed: d.test_file.close() except IOError as e: logger.warning( '>> Attempting to close {file_name} resulted in an error: ' '\n{trace}'.format( file_name=d.filename, trace=traceback.format_exc(10))) pass if len(supporting_data_cell_items) > 0: logger.debug( 'There are {} data cell items for TC {}.'.format( len(supporting_data_cell_items), t.id)) get_cleared_paragraph( supporting_data_cell).text = '\n'.join( supporting_data_cell_items) if len(supporting_data_cell_items) == 0: logger.debug( 'There are no supporting_data_cell_items; removing the supporting data row.' ) remove_row(table, supporting_data_row) # Conclusion H1 and text document.add_heading('Conclusion', level=1) portion_mark_and_insert(mission.conclusion, document) data_table.cell(0, 0).text = "" get_cleared_paragraph( data_table.cell(1, 0) ).text = "This table is used during report generation and can be deleted in the final report output." data_table.cell(2, 0).text = "" # Replace document slugs replace_document_slugs(document) if zip_attachments: zip_file = shutil.make_archive(mission_data_dir, 'zip', mission_data_dir) with open(mission_data_dir + '.zip', 'rb') as f: return io.BytesIO(f.read()) else: my_stream = StringIO() document.save(my_stream) return my_stream
fpname = sys.argv[1] outfpn = sys.argv[2] ############################################# # configurable parameters (#TODO: configuration file/CL options) # refmarker = '\[\d*\].*' # regexp to recognize a bibliography item: "[decimal number]" refmarker = '\[\d.*' # regexp to recognize a bibliography item: "[decimal" doistr = 'DOI:' #pre-fix to be recognised for link creation urlbase = 'https://dx.doi.org/' #pre-fix for assembling link linkrgb = '0563C1' #colour of links linkunderline = True #underline links? # parameter-derived variables linkRGBcolor = RGBColor.from_string(linkrgb) ############################################# def add_run_copy(paragraph, run, text=None): """Add a 'run' at the end of a paragraph using a 'run' template. A 'run' is a part of a paragraph with identically formatted text each time character formatting changes, a new 'run' is needed. source: https://github.com/python-openxml/python-docx/issues/519#issuecomment-441710870 I have the impression that not everything is neatly copied Don't know why