コード例 #1
0
def test_link_with_zoom_and_shift(tmp_path):
    pdf = FPDF()
    pdf.set_font("helvetica", size=24)
    pdf.add_page()
    link = pdf.add_link()
    pdf.set_link(link, page=2, x=pdf.epw / 4, y=pdf.epw / 3, zoom=4)
    pdf.set_xy(30, 50)
    pdf.cell(
        w=140,
        h=10,
        txt="Link to 2nd page zoomed & shifted",
        border=1,
        align="C",
        link=link,
    )
    pdf.add_page()
    pdf.multi_cell(
        pdf.epw,
        txt="Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
        " sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
        " Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
        " Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
        " Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
    )
    # Drawing Adobe Reader viewport after clicking on the link,
    # with the right panel open. The initial zoom level does not matter.
    pdf.set_draw_color(r=255, g=0, b=0)
    pdf.rect(x=pdf.epw / 4, y=pdf.epw / 3, w=53.5, h=31)
    assert_pdf_equal(pdf, HERE / "link_with_zoom_and_shift.pdf", tmp_path)
コード例 #2
0
def write_to_pdf(data: dict, filename: str):
    pdf = FPDF()
    effective_page_width = pdf.w - 2 * pdf.l_margin
    pdf.compress = False
    pdf.add_page()
    pdf.add_font("TimesNewRoman", '', 'TimesNewRoman.ttf', uni=True)
    pdf.set_font("TimesNewRoman", size=30)
    pdf.cell(w=0, txt=data['title'])
    pdf.ln(30)
    pdf.set_line_width(1)
    pdf.set_draw_color(255, 0, 0)
    for index_news, news_dict in enumerate(data['items']):
        pdf.set_font("TimesNewRoman", size=20)
        pdf.line(20, pdf.get_y() - 10, effective_page_width, pdf.get_y() - 10)
        pdf.multi_cell(effective_page_width, 10, news_dict['title'])
        if news_dict['contain_image']:
            download_image_and_paste_in_pdf(pdf, news_dict, index_news)
        pdf.multi_cell(effective_page_width, 10, news_dict['published'])
        pdf.multi_cell(effective_page_width, 10, news_dict['summary'][news_dict['summary'].rfind(']') + 1:])
        pdf.set_font("TimesNewRoman", size=15)
        pdf.ln(5)
        pdf.multi_cell(effective_page_width, 10, 'Link on news:\n' + news_dict['link'])
        if news_dict['contain_image']:
            pdf.multi_cell(effective_page_width, 10, 'Link on image:\n' + news_dict['link_on_image'])
        pdf.ln(40)
    try:
        pdf.output(filename, 'F')
    except PermissionError:
        raise RssReaderException.FileException(f'close file:\n{filename}')
コード例 #3
0
class PDFGridGenerator():
    """A pdf grid generator.

    To use:
    >>> g = PDFGridGenerator()
    >>> g.generate('filename.pdf')
    """
    def __init__(self):
        self.pdf = FPDF()
        self.pdf.add_page()
        self.pdf.set_auto_page_break(False)
        self.pdf.set_margins(0, 0)

        self.pdf.set_draw_color(191, 187, 187)
        self.pdf.set_line_width(0.35)

    def save(self, filename):
        self.pdf.output(filename)

    def generate_grid(self):
        # Fonts must be set before writing text
        self.pdf.set_font('Arial', 'B', 8)
        self.pdf.set_text_color(0, 0, 0)

        self.generate_vertical_lines(5)
        self.generate_horizontal_lines(5)

        self.left_numbers(5)
        self.top_numbers(5)

    def generate_vertical_lines(self, spacing):
        lines = 42
        for i in range(1, lines):
            x = i * 5
            self.pdf.line(x, 5, x, 290)

    def generate_horizontal_lines(self, spacing):
        lines = 59
        for j in range(1, lines):
            y = j * 5
            self.pdf.line(5, y, 205, y)

    def left_numbers(self, spacing):
        lines = 58
        for i in range(0, lines):
            self.pdf.set_xy(0, i * 5 + 3)
            self.pdf.write(5, str(i))

    def top_numbers(self, spacing):
        lines = 41
        for i in range(0, lines):
            self.pdf.set_xy(i * 5 + 3, 1)
            self.pdf.write(5, str(i))

    def text(self, x, y, txt, font_size=13):
        self.pdf.set_font('Arial', '', font_size)
        self.pdf.set_text_color(0, 0, 0)
        self.pdf.set_xy(x * 5 + 4, y * 5 + 5)
        self.pdf.write(5, txt)
コード例 #4
0
def create_publication_cert(uuid, name, author, journal, publication_date,
                            created_date):
    pdf = FPDF()
    pdf.add_page()
    pdf.set_margins(25, 15, 15)

    pdf.image(settings.STATIC_ROOT + "/pdf/logo.png", 25, 15, 50)

    pdf.add_font('Arial',
                 '',
                 settings.STATIC_ROOT + '/pdf/arial.ttf',
                 uni=True)

    pdf.set_font('Arial', '', 8)
    pdf.set_draw_color(50, 50, 50)
    pdf.cell(90, 5, ln=1)
    pdf.cell(90)
    pdf.cell(0, 3, "АНС «СибАК» ИНН 5404255395 КПП 540201001", ln=1)
    pdf.cell(90)
    pdf.cell(0,
             3,
             "Адрес: г.Новосибирск, ул. Красный проспект, 165, офис 4",
             ln=1)
    pdf.cell(90)
    pdf.cell(0, 3, "Телефон: 8-913-915-38-00", ln=1)
    pdf.cell(90)
    pdf.cell(0, 3, "Р/с 40703810029100000978 в ОАО АКБ \"АВАНГАРД\"", ln=1)
    pdf.cell(90)
    pdf.cell(0, 3, "к/с 3010181000000000201", ln=1)
    pdf.cell(90)
    pdf.cell(0, 3, "БИК 044525201", ln=1)

    pdf.set_font_size(16)
    pdf.cell(0, 30, "СПРАВКА", ln=1, align="C")

    pdf.set_font_size(12)
    pdf.cell(0,
             6,
             created_date.strftime('%d.%m.%Y') + " No. 14362",
             ln=1,
             align="L")
    pdf.cell(0, 6, ln=1)

    pdf.cell(0, 6, author, ln=1, align="R")

    pdf.cell(0, 6, ln=1)
    spisok = [name, journal, publication_date.strftime('%d.%m.%Y')]
    pdf.multi_cell(
        0, 6, "Издательство подтверждает, что Ваша статья «{}» \
    принята для публикации в научном журнале «{}». \
    Журнал будет опубликован на сайте издательства {}.".format(*spisok))
    pdf.cell(0, 24, ln=1)

    pathlib.Path(settings.MEDIA_ROOT + '/pdf/publication_cert').mkdir(
        parents=True, exist_ok=True)
    pdf.output(
        settings.MEDIA_ROOT + "/pdf/publication_cert/publication_cert_" +
        str(uuid) + ".pdf", 'F')
    return "/pdf/publication_cert/publication_cert_" + str(uuid) + ".pdf"
コード例 #5
0
ファイル: drawing.py プロジェクト: subratcall/completePython
def draw_lines():
    pdf = FPDF()
    pdf.add_page()
    pdf.line(10, 10, 10, 100)
    pdf.set_line_width(1)
    pdf.set_draw_color(255, 0, 0)
    pdf.line(20, 20, 100, 20)
    pdf.output('draw_lines.pdf')
コード例 #6
0
ファイル: views.py プロジェクト: Arockiamary/PDF-Creation
def draw_lines(request):
    pdf = FPDF()
    pdf.add_page()
    pdf.line(10, 10, 10, 100)
    pdf.set_line_width(1)
    pdf.set_draw_color(255, 0, 0)
    pdf.line(20, 20, 100, 20)
    pdf.output('draw_lines.pdf')
    return render(request, "index.html")
コード例 #7
0
class Report:
    def __init__(self, data):
        """Initialize the Report object."""
        self.pdf = FPDF("P", "mm", "A4")
        self.id = data["id"]
        self.data = data

    def render(self):
        self.pdf.add_page()
        self.draw_shapes()
        self.draw_title()
        self.draw_heading()
        self.draw_inventory()
        return self.pdf.output("report_%s.pdf" % self.data["id"])

    def draw_shapes(self):
        """Draw the report edge."""
        self.pdf.set_line_width(4)
        self.pdf.rect(10, 10, 190, 277)
        self.pdf.set_line_width(2)
        self.pdf.rect(15, 15, 180, 267)
        self.pdf.set_draw_color(192, 192, 192)
        self.pdf.set_line_width(1)
        self.pdf.rect(16, 16, 178, 265)

    def draw_inventory(self):
        """Draw the inventory paragraph the report."""
        self.pdf.set_y(100)
        self.pdf.set_font("Arial", "", 18)
        self.pdf.cell(170, 7, "Inventory", 0, ln=2, align="C")

        self.pdf.set_font("Arial", "", 12)
        for inventory in self.data["inventory"]:
            text = "%s: %s" % (inventory["name"], inventory["price"])
            self.pdf.cell(150, 7, text, 0, ln=2, align="C")

    def draw_title(self):
        """Draw the report title."""
        title = self.data["organization"] + " report"
        self.pdf.set_font("Arial", "B", 20)
        w = self.pdf.get_string_width(title)
        x = 210 - w / 2
        self.pdf.cell(x, 50, title, 0, 1, "C")

    def draw_heading(self):
        """Draw the report heading."""
        self.pdf.set_font("Arial", "", 12)
        self.pdf.set_x(0)
        org_text = "Organization: %s " % self.data["organization"]
        self.pdf.cell(170, 7, org_text, 0, ln=2, align="R")
        reported_text = "Reported: %s " % self.data["reported_at"]
        self.pdf.cell(170, 7, reported_text, 0, ln=2, align="R")
        created_text = "Created: %s " % self.data["created_at"]
        self.pdf.cell(170, 7, created_text, 0, ln=2, align="R")
コード例 #8
0
def test_local_context_inherited_shared_props(tmp_path):
    "The only thing that should differ between the 2 squares is their opacity"
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("Helvetica", "", 12)
    pdf.set_draw_color(0, 128, 255)
    pdf.set_fill_color(255, 128, 0)
    pdf.set_line_width(2)
    pdf.set_dash_pattern(dash=0.5, gap=9.5, phase=3.25)
    with pdf.local_context(
            fill_opacity=0.5):  # => triggers creation of a local GraphicsStyle
        pdf.rect(x=60, y=60, w=60, h=60, style="DF")
    pdf.rect(x=60, y=150, w=60, h=60, style="DF")
    assert_pdf_equal(pdf, HERE / "local_context_inherited_shared_props.pdf",
                     tmp_path)
コード例 #9
0
def printReport():

    df = pd.read_csv("OUTPUT/HDIrank2019_02_mergedDataframes.csv")
    df = df.drop(columns="Unnamed: 0")

    reg = df.groupby("region").mean().drop(
        columns=["HDIrank", "LastYearRank"]).T.round(2)

    pdf = FPDF('P', 'mm', 'A4')
    pdf.add_page()
    font_type = ('Arial', 'B', 16)
    num_col = 6
    w, h = 190, 277
    pdf.set_font(*font_type)
    pdf.set_text_color(0)
    pdf.set_draw_color(0)
    pdf.cell(w, 10, 'Datos Acumulados', 1, 1, 'C')
    pdf.set_line_width(0.2)
    for col in reg.columns:
        pdf.cell(w / num_col, 10, col, 1, 0, 'C')
    pdf.ln()
    pdf.set_fill_color(243, 95, 95)
    font_type = ('Arial', '', 12)
    pdf.set_font(*font_type)
    '''
    NOT WORKING!
    def fit_word(string,cell_w,font_type):
        ver = FPDF()
        #font_type(font,style,size))
        ver.set_font(*font_type)
        # if string fits, return it unchanged
        if ver.get_string_width(string)<cell_w:
            return string
        # cut string until it fits
        while ver.get_string_width(string)>=cell_w:
            string = string[:-1]
        # replace last 3 characters with "..."
        string = string[:-3] + "..."
        return string
    '''

    for index, row in reg.iterrows():
        for value in reg.columns:
            pdf.cell(w / num_col, 10, row[value], 1, 0, 'C', 0)
        pdf.ln()

    pdf.output("OUTPUT/report.pdf", 'F')
    return "Repo done"
コード例 #10
0
def gen_PDF(events):
    pdf = FPDF('L', 'in', 'Letter')
    for event in events:
        pdf.add_page()
        pdf.set_draw_color(r=0, g=0, b=255)
        pdf.set_line_width(.05)
        pdf.rect(.15, .15, 10.7, 8.2)
        pdf.image('images/3IDcav3.png', x=.14, y=.22, h=1)
        pdf.image('images/3IDLogo.png', x=9.8, y=.22, h=1)
        pdf.set_draw_color(r=0, g=0, b=0)
        pdf.line(5.5, 1.5, 5.5, 8)
        pdf.line(.5, 4.25, 10.5, 4.25)

        pdf.set_font('Arial', 'B', 24)
        pdf.set_xy(0, .75)
        pdf.cell(w=0, txt=events[event].title, align='C')
        pdf.set_xy(1, 1.5)
        pdf.set_font('Arial', '', 14)
        pdf.multi_cell(
            4.5, .22,
            'Purpose: {}\n\nFrequency: {}\n\nTime: {}\n\nLocation: {}'.format(
                events[event].purpose, events[event].frequency,
                events[event].time, events[event].location), 0, 'L')
        pdf.set_xy(1, 4.5)
        pdf.multi_cell(
            4.5, .22, 'Inputs: \n{}\nOutputs: \n{}'.format(
                '  - ' + str(events[event].inputs)[1:-1].replace(
                    "'", "").replace(',', '\n  -'),
                '  - ' + str(events[event].outputs)[1:-1].replace(
                    "'", "").replace(',', '\n  -'), 0, 'L'))
        pdf.set_xy(6.5, 1.5)
        pdf.multi_cell(
            4.5, .22, 'Chair: {}\n\nMembers: \n{}'.format(
                events[event].chair,
                '  - ' + str(events[event].members)[1:-1].replace(
                    "'", "").replace(',', '\n  -'), 0, 'L'))
        pdf.set_xy(6.5, 4.5)
        pdf.multi_cell(
            4.5, .22, 'Agenda: \n{}'.format(
                '  - ' + str(events[event].agenda)[1:-1].replace(
                    "'", "").replace(',', '\n  -'), 0, 'L'))

    try:
        pdf.output('exports/test.pdf')
    except FileNotFoundError:
        os.mkdir('exports')
        pdf.output('exports/test.pdf')
コード例 #11
0
def test_text_modes(tmp_path):
    pdf = FPDF(format=(350, 150))
    pdf.add_page()
    pdf.set_font("Helvetica", size=80)
    with pdf.local_context(fill_color=(255, 128, 0)):
        pdf.cell(txt="FILL default")
    with pdf.local_context(text_color=(0, 128, 255)):
        pdf.cell(txt=" text mode")
    pdf.ln()
    with pdf.local_context(text_mode=TextMode.STROKE, line_width=2):
        pdf.cell(txt="STROKE text mode")
    pdf.ln()
    pdf.text_mode = TextMode.FILL_STROKE
    pdf.line_width = 4
    pdf.set_draw_color(255, 0, 255)
    pdf.cell(txt="FILL_STROKE text mode")
    pdf.ln()
    with pdf.local_context():
        pdf.text_mode = TextMode.INVISIBLE
        pdf.cell(txt="INVISIBLE text mode")
    assert_pdf_equal(pdf, HERE / "text_modes.pdf", tmp_path)
コード例 #12
0
def output_pdf(logger, all_news, about_website=None, file_name=None):
    """Function which create or overwrites PDF-file with selected fresh or cached news"""
    logger.info('Convert to PDF-format')
    pdf = FPDF()
    pdf.add_page()
    if about_website is not None:
        pdf.set_font("Arial", "B", size=14)
        pdf.set_fill_color(200, 220, 255)
        for value in about_website.values():
            line = 1
            pdf.cell(190, 8, txt=value, ln=line, align="C")
            line += 1
    pdf.set_font("Arial", size=10)
    pdf.set_line_width(1)
    pdf.set_draw_color(35, 41, 153)
    for news in all_news:
        link = news['Source of image']
        for key, value in news.items():
            if key != 'Summary':
                pdf.multi_cell(190, 6, txt=f'{key}: {value}', align="L")
            else:
                position_y = pdf.get_y()
                try:
                    filename, _ = urllib.request.urlretrieve(link)
                    pdf.image(filename, 80, position_y, h=30, type='jpeg', link=link)
                    pdf.ln(31)
                    os.remove(filename)
                except Exception as ex:
                    logger.warning("Error finding image: {}, {}.".format(type(ex), ex))
                pdf.multi_cell(190, 6, txt=f'{key}: {value}', align="L")
        position_y = pdf.get_y()
        pdf.set_line_width(1)
        pdf.set_draw_color(35, 41, 153)
        pdf.line(10, position_y, 200, position_y)
    logger.info('Creating of PDF-file')
    try:
        pdf.output(file_name)
        logger.info('Converted successfully!')
    except Exception as ex:
        logger.error("PDF file writing error : {}, {}.".format(type(ex), ex))
コード例 #13
0
ファイル: suppdf.py プロジェクト: VUIIS/dax
def make_overpdf(overfile, info, pagenum, pagecount):
    # Assessor name top left of header
    # processor name and version left of footer
    # Date and Page numbers in right footer

    # Initialize the pdf
    pdf = FPDF(orientation="P", unit='in', format='letter')
    pdf.set_margins(left=0, top=0, right=0)
    pdf.add_page()
    pdf.set_font('courier', size=9)

    # Draw lines in header and footer
    pdf.set_draw_color(r=155, g=155, b=155)
    pdf.set_line_width(0.01)
    pdf.line(x1=0.2, y1=0.2, x2=8.3, y2=0.2)
    pdf.line(x1=0.2, y1=10.7, x2=8.3, y2=10.7)

    # Write the assessor label in the header
    pdf.set_xy(0.3, 0.1)
    pdf.cell(0, 0.05, '{}'.format(info['assessor']))

    # Prevent footer from falling off page
    pdf.set_auto_page_break(False)

    # Write proc version in left of footer
    pdf.set_xy(0.3, -0.2)
    pdf.cell(0, 0, '{}'.format(info['proctype']))

    # Write date in right of footer
    pdf.set_xy(-2.1, -0.2)
    pdf.cell(0, 0, '{}'.format(info['procdate']))

    # Write page numbers right of footer
    pdf.set_xy(-0.6, -0.2)
    pdf.cell(0, 0, '{}/{}'.format(pagenum, pagecount))

    # Write the pdf to file
    pdf.output(overfile)
コード例 #14
0
def main():
	pdf = FPDF()
	pdf.add_page()
	pdf.set_xy(0, 0)
	pdf.set_font('arial', 'B', 13.0)
	pdf.image("image_1.jpg", 75, 30)

	setCandidateName()
	setColorsChart()
	readMetrics()
	setMetricsWidths()
	setDiscsWidths()

	setLine(pdf, 45, 75, 70, 0, metrics[0])	
	setLine(pdf, 40, 70, 90, 1, metrics[1])
	setLine(pdf, 45, 75, 110, 2, metrics[2])
	setLine(pdf, 118, 148, 85, 3, metrics[3])
	setLine(pdf, 135, 165, 65, 4, metrics[4])
	setLine(pdf, 122, 152, 104, 5, metrics[5])
	setLine(pdf, 115, 145, 120, 6, metrics[6])
	pdf.set_font('arial', 'B', 13.0)

	#addCandidateName
	pdf.set_draw_color(0,0,0)
	
	pdf.set_y(140)
	pdf.cell(70)
	pdf.cell(w = 60, h = 10, ln =1, txt = candidate, border = 'B', align = 'C')
	
	#addDiscTestResult
	pdf.image("DISC.jpg", 65, 180, 80,80)
	setLine(pdf,45,65,200,0,discs[0])
	setLine(pdf,45,65,245,5,discs[1])
	setLine(pdf,145,165,200,8,discs[2])
	setLine(pdf,145,165,240,2,discs[3])

	#generateReportFile
	pdf.output('candidateSurvey.pdf', 'F')
コード例 #15
0
    def some_initialisations(self, copyname):
        document = FPDF(orientation='P', unit='mm', format='A4')
        doctitle = "Tax invoice - " + copyname
        document.set_title(doctitle)
        document.set_margins(
            20, 20, 20
        )  # left, top, right...... Define before creating page, otherwise will ruin formatting

        #Libreoffice ref: 11pt=3.88mm, 48 lines @1.2, 50 lines @1.15, 38 lines @1.5 line spacing
        # big character to font height is 75% approx.
        #space to char ht: 89%, 99%, 75%, 153% respectively for 1.15, 1.2, 1 and 1.5 line spacing
        #net char height for 11pt is 3.88*.75=2.91 mm
        #net space height for 11pt is 2.59 @1.15, 2.88 @1.2, 2.18 @1, 4.45 @1.5
        # cell ht: 5.5 @1.15, 5.8 @1.2, 5.1 @1, 7.35 @1.5

        #There is no page for the moment, so we have to add one with add_page.
        document.add_page()
        document.set_font("Times", size=11)
        document.set_text_color(20, 20, 20)
        document.set_fill_color(255, 255, 255)
        # cell fill colour, visible if cell property fill set to true
        document.set_draw_color(175, 175, 175)
        # border color, used for all drawing operations (lines, rectangles and cell borders)
        return document
コード例 #16
0
ファイル: kut2fpdf.py プロジェクト: juanjosepablos/pineboo
class kut2fpdf(object):

    _document = None  # Aquí se irán guardando los datos del documento
    logger = None
    _xml = None
    _xml_data = None
    _page_orientation = None
    _page_size = None
    _bottom_margin = None
    _left_margin = None
    _right_margin = None
    _top_margin = None
    _page_top = {}
    _data_row = None  # Apunta a la fila actual en data
    _parser_tools = None
    _avalible_fonts = None
    _unavalible_fonts = None
    design_mode = None
    _actual_data_line = None
    _no_print_footer = False
    _actual_section_size = None
    increase_section_size = None
    last_detail = False
    actual_data_level = None
    last_data_processed = None
    prev_level = None
    draws_at_header = None
    detailn = None
    name_ = None
    _actual_append_page_no = None
    reset_page_count = None

    def __init__(self):

        self.logger = logging.getLogger("kut2fpdf")
        checkDependencies({"fpdf": "pyfpdf"})
        from pineboolib.plugins.kugar.parsertools import parsertools
        self._parser_tools = parsertools()
        self._avalible_fonts = []
        self._unavalible_fonts = []
        self.design_mode = FLSettings().readBoolEntry("ebcomportamiento/kugar_debug_mode")
        self._actual_data_line = None
        self._no_print_footer = False
        self.increase_section_size = 0
        self.actual_data_level = 0
        self.prev_level = -1
        self.draws_at_header = {}
        self.detailn = {}
        self.name_ = None
        self._actual_append_page_no = 0
        self.reset_page_count = False
        self.new_page = False
    """
    Convierte una cadena de texto que contiene el ".kut" en un pdf y retorna la ruta a este último.
    @param name. Nombre de ".kut".
    @param kut. Cadena de texto que contiene el ".kut".
    @param data. Cadena de texto que contiene los datos para ser rellenados en el informe.
    @return Ruta a fichero pdf.
    """

    def parse(self, name, kut, data, report = None, flags = []):

        try:
            self._xml = self._parser_tools.loadKut(kut)
        except Exception:
            self.logger.exception(
                "KUT2FPDF: Problema al procesar %s.kut", name)
            return False
        try:
            self._xml_data = load2xml(data)
        except Exception:
            self.logger.exception("KUT2FPDF: Problema al procesar xml_data")
            return False

        self.name_ = name
        self.setPageFormat(self._xml)
        # self._page_orientation =
        # self._page_size =
        if report is None:
            from fpdf import FPDF
            self._actual_append_page_no = -1
            self._document = FPDF(self._page_orientation, "pt", self._page_size)
            for f in self._document.core_fonts:
                self.logger.debug("KUT2FPDF :: Adding font %s", f)
                self._avalible_fonts.append(f)
        else:
            self._document = report
        # Seteamos rutas a carpetas con tipos de letra ...
        
        # Cargamos las fuentes disponibles
        next_page_break = (flags[2] == 1) if len(flags) == 3 else True
        page_append = (flags[1] == 1) if len(flags) > 1 else False
        page_display = (flags[0] == 1) if len(flags) > 0 else False
        

        
        if page_append:
            self.prev_level = -1
            self.last_detail = False
        
        page_break = False
        if self.new_page:
            page_break = True
            self.new_page = False
                   
        
        if self.reset_page_count:
            self.reset_page_no()
            self.reset_page_count = False
        
        if self.design_mode:
            print("Append", page_append)
            print("Display", page_display)
            print("Page break", next_page_break)
        
        if next_page_break:
            self.reset_page_count = True
        
        if page_display:
            self.new_page = True
            
            
        
            

        self.processDetails(not page_break)
        
        #FIXME:Alguno valores no se encuentran
        for p in self._document.pages.keys():
            page_content = self._document.pages[p]["content"]
            for h in self.draws_at_header.keys():
                page_content = page_content.replace(h, str(self.draws_at_header[h]))
            
            
            self._document.pages[p]["content"] = page_content
                    
        #print(self.draws_at_header.keys())   
        self._document.set_title(self.name_)
        self._document.set_author("Pineboo - kut2fpdf plugin")
        
        
        return self._document    
        

        
    
    def get_file_name(self):
        import os
        
        pdf_name = aqApp.tmp_dir()
        pdf_name += "/%s_%s.pdf" % (self.name_, datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
        if os.path.exists(pdf_name):
            os.remove(pdf_name)
        if self._document is not None:
            self._document.output(pdf_name, 'F')        
            return pdf_name
        else:
            return None
    

    """
    Indica el techo para calcular la posición de los objetos de esa sección.
    @return Número con el techo de la página actual.
    """

    def topSection(self):
        return self._page_top[str(self._document.page_no())]

    """
    Actualiza el valor del techo de la página actual. Se suele actualizar al procesar una sección.
    @param value. Numero que elspecifica el nuevo techo.
    """

    def setTopSection(self, value):
        self._actual_section_size = value - self.topSection()
        self._page_top[str(self._document.page_no())] = value

    """
    Añade una nueva página al documento.
    """

    def newPage(self, data_level, add_on_header = True):
        self._document.add_page(self._page_orientation)
        self._page_top[str(self._document.page_no())] = self._top_margin
        self._document.set_margins(self._left_margin, self._top_margin, self._right_margin)  # Lo dejo pero no se nota nada
        self._no_print_footer = False
        if self.design_mode:
            self.draw_margins()
            
        self._actual_section_size = 0
        self._actual_append_page_no += 1
        if self.design_mode:
            print("Nueva página", self.number_pages())
        
        #l_ini = data_level
        #l_end = self.prev_level
        
        #if l_ini == l_end:
        #    l_end = l_end + 1
        
        #if l_ini <= l_end:
        #    for l in range(l_ini , l_end):
        #        print(l)
        #        self.processSection("AddOnHeader", str(l))
        pg_headers = self._xml.findall("PageHeader")
        
        for ph in pg_headers:
            if self.number_pages() == 0 or ph.get("PrintFrequency") == "1":
                ph_level = ph.get("Level") if ph.get("Level") is not None else None
                self.processSection("PageHeader", ph_level)
                break
        
        
        if add_on_header and not self.number_pages() == 0:
            for l in range(data_level + 1):
                self.processSection("AddOnHeader", str(l))
            
        
        #Por ahora se omite detail header

        
    """
    Procesa las secciones details con sus correspondientes detailHeader y detailFooter.
    """

    def processDetails(self, keep_page = None):
        # Procesamos la cabecera si procede ..
        top_level = 0
        level = 0
        first_page_created = keep_page if keep_page is not None and self._document.page_no() > 0 else False
        
        rows_array = self._xml_data.findall("Row")
        for data in rows_array:
            self._actual_data_line = data
            level = int(data.get("level"))
            if level > top_level:
                top_level = level
                
            if not first_page_created:
                self.newPage(level)
                first_page_created = True
            
            if rows_array[len(rows_array) - 1] is data:
                self.last_detail = True
            
            if level < self.prev_level:
                for l in range(level + 1, self.prev_level + 1):
                    self.processData("DetailFooter", self.last_data_processed, l)    
            
            
            
            
               
            if not str(level) in self.detailn.keys():
                self.detailn[str(level)] = 0
            else:    
                self.detailn[str(level)] += 1
            
            if level > self.prev_level:         
                self.processData("DetailHeader",  data, level)    
                      
                        
            self.processData("Detail", data, level)
            
            self.last_data_processed = data
            
                
            self.prev_level = level
        if not self._no_print_footer:
           for l in reversed(range(top_level + 1)):
               self.processData("DetailFooter", data, l)

    """
    Paso intermedio que calcula si detailHeader + detail + detailFooter entran en el resto de la ṕagina. Si no es así crea nueva página.
    @param section_name. Nombre de la sección a procesar.
    @param data. Linea de datos a procesar.
    @param data_level. Nivel de seccion.
    """

    def processData(self, section_name, data, data_level):
        self.actual_data_level = data_level
        listDF = self._xml.findall(section_name)
        data_size = len(listDF)      
        
        for dF in listDF:
            draw_if = dF.get("DrawIf")
            show = True
            if draw_if:
                show = data.get(draw_if)
            
            
            if dF.get("Level") == str(data_level) and show not in ("None", "False"):
                    
                if section_name in ("DetailHeader","Detail"):
                    heightCalculated = self._parser_tools.getHeight(dF) + self.topSection()
                    
                    if section_name is "DetailHeader":
                        for detail in self._xml.findall("Detail"):
                            if detail.get("Level") == str(data_level):
                                heightCalculated += self._parser_tools.getHeight(detail)
                            
                    for dFooter in self._xml.findall("DetailFooter"):
                        if dFooter.get("Level") == str(data_level):
                            heightCalculated += self._parser_tools.getHeight(dFooter)
                    
                    #for addFooter in self._xml.findall("AddOnFooter"):
                    #    if addFooter.get("Level") == str(data_level):
                    #        heightCalculated += self._parser_tools.getHeight(addFooter)
                    
                    pageFooter = self._xml.get("PageFooter")
                    if pageFooter:
                        if self._document.page_no() == 1 or pageFooter.get("PrintFrecuency") == "1":
                            heightCalculated += self._parser_tools.getHeight(pageFooter)

                    heightCalculated += self._bottom_margin
                    if heightCalculated > self._document.h:  # Si nos pasamos
                        self._no_print_footer = True
                        #Vemos el tope por abajo 
                        limit_bottom = self._document.h - self._parser_tools.getHeight(self._xml.get("AddOnFooter"))
                        actual_size = self._parser_tools.getHeight(dF) + self.topSection()
                        
                        
                        

                        if (actual_size > limit_bottom + 2) or self.last_detail: # +2 se usa de margen extra
                            self.processSection("AddOnFooter", str(data_level))
                            self.newPage(data_level)

                    
                self.processXML(dF, data)
                                    
                if dF.get("NewPage") == "true" and not self.last_detail:
                    self.newPage(data_level, False)
                
                
                break #Se ejecuta una sola instancia

    """
    Procesa las secciones fuera de detail
    @param name. Nombre de la sección a procesar.
    """

    def processSection(self, name, level=0):
        sec_list = self._xml.findall(name)
        sec_ = None
        for s in sec_list:
            if s.get("Level") == str(level) or s.get("Level") is None:
                sec_ = s
        
        if sec_ is not None:
            if sec_.get("PrintFrequency") == "1" or self._document.page_no() == 1 or name in ("AddOnHeader","AddOnFooter"):   
                self.processXML(sec_)

    """
    Procesa un elemento de xml.
    @param xml: El elemento a procesar.
    @param. data: Linea de datos afectada.
    """

    def processXML(self, xml, data=None):
        
        fix_height = True
        if data is None:
            data = self._actual_data_line
        
        if self.design_mode:
            print("Procesando", xml.tag, data.get("level"))
        
        size_updated = False
        if xml.tag == "DetailFooter":
            if xml.get("PlaceAtBottom") == "true":
                self.setTopSection(self._document.h - self._parser_tools.getHeight(xml))
                size_updated = True
        
               
        if xml.tag == "PageFooter":
            fix_height = False

        self.fix_extra_size() #Sirve para actualizar la altura con lineas que se han partido porque son muy largas
        
        
            
        for label in xml.iter("Label"):
            self.processText(label, data, fix_height)
        
        for field in xml.iter("Field"):
            self.processText(field, data, fix_height)
        
        for special in xml.iter("Special"):
            self.processText(special, data, fix_height, xml.tag)
        
        for calculated in xml.iter("CalculatedField"):
            self.processText(calculated, data, fix_height, xml.tag)
        
        
        #Busco draw_at_header en DetailFooter y los meto también
        if xml.tag == "DetailHeader":
            detail_level = xml.get("Level")
            for df in self._xml.iter("DetailFooter"):
                if df.get("Level") == detail_level:
                    for cf in df.iter("CalculatedField"):
                        if cf.get("DrawAtHeader") == "true":
                            header_name = "%s_header_%s_%s" % (self.detailn[detail_level], detail_level, cf.get("Field"))
                            self.draws_at_header[header_name] = ""
                            self.processText(cf, data, fix_height, xml.tag)
            
        
        
        for line in xml.iter("Line"):
            self.processLine(line, fix_height)

        if not size_updated:
            self.setTopSection(self.topSection() + self._parser_tools.getHeight(xml))
        
        
                    

    
    def fix_extra_size(self):
        if self.increase_section_size > 0:
            self.setTopSection(self.topSection() + self.increase_section_size)
            self.increase_section_size = 0    
    """
    Procesa una linea.
    @param xml. Sección de xml a procesar.
    @param fix_height. Ajusta la altura a los .kut originales, excepto el pageFooter.
    """

    def processLine(self, xml, fix_height=True):

        color = xml.get("Color")
        r = 0 if not color else int(color.split(",")[0])
        g = 0 if not color else int(color.split(",")[1])
        b = 0 if not color else int(color.split(",")[2])

        style = int(xml.get("Style"))
        width = int(xml.get("Width"))
        X1 = self.calculateLeftStart(xml.get("X1"))
        X1 = self.calculateWidth(X1, 0, False)
        X2 = self.calculateLeftStart(xml.get("X2"))
        X2 = self.calculateWidth(X2, 0, False)
        # Ajustar altura a secciones ya creadas
        Y1 = int(xml.get("Y1")) + self.topSection()
        Y2 = int(xml.get("Y2")) + self.topSection()
        if fix_height:
            Y1 = self._parser_tools.ratio_correction(Y1)
            Y2 = self._parser_tools.ratio_correction(Y2)
        
          
            
        
        self._document.set_line_width(width)
        self._document.set_draw_color(r, g, b)
        dash_length = 1
        space_length = 1
        if style == 2:
            dash_length = 20
            space_length = 20
        elif style == 3:
            dash_length = 10
            space_length = 10
        
        self._document.dashed_line(X1, Y1, X2, Y2, dash_length, space_length)  
        #else:
        #    self._document.line(X1, Y1, X2, Y2)

    """
    Comprueba si excedemos el margen izquierdo de la página actual
    @param x. Posición a comprobar.
    @return Valor corregido, si procede.
    """

    def calculateLeftStart(self, x):
        return self._parser_tools.ratio_correction(int(x)) + self._left_margin

    """
    Comprueba si excedemos el margen derecho de la página actual
    @param x. Posición a comprobar.
    @return Valor corregido, si procede.
    """

    def calculateWidth(self, width, pos_x, fix_ratio = True):
        width = int(width)
        if fix_ratio:
            width = self._parser_tools.ratio_correction(int(width))
        ret_ = width
        limit = self._document.w - self._right_margin
        if pos_x + width > limit:
            ret_ = limit - pos_x
        return ret_

    """
    Procesa una etiqueta. Esta puede ser un campo calculado, una etiqueta, un campo especial o una imagen.
    @param xml. Sección de xml a procesar.
    @param fix_height. Ajusta la altura a los .kut originales, excepto el pageFooter.
    """

    def processText(self, xml, data_row=None, fix_height=True, section_name = None):
        is_image = False
        is_barcode = False
        text = xml.get("Text")
        borderColor = xml.get("BorderColor")
        field_name = xml.get("Field")
        
        # x,y,W,H se calcula y corrigen aquí para luego estar correctos en los diferentes destinos posibles
        W = int(xml.get("Width"))
        
        H = self._parser_tools.getHeight(xml)
        
        x = int(xml.get("X"))
        
        
        y = int(xml.get("Y")) + self.topSection() # Añade la altura que hay ocupada por otras secciones
        if fix_height:
            y = self._parser_tools.ratio_correction(y)  # Corrige la posición con respecto al kut original
        
        
        
        



        data_type = xml.get("DataType")
        
        if xml.tag == "Field" and data_row is not None:
            text = data_row.get(field_name)

        elif xml.tag == "Special":
            if text == "":
                if xml.get("Type") == "1":
                    text = "PageNo"
            text = self._parser_tools.getSpecial( text, self._actual_append_page_no)

        calculation_type = xml.get("CalculationType")
        
        if calculation_type is not None and xml.tag != "Field":
            if calculation_type == "6":
                function_name = xml.get("FunctionName")
                try:
                    nodo = self._parser_tools.convertToNode(data_row)
                    from pineboolib.pncontrolsfactory import aqApp
                    ret_ = aqApp.call(function_name, [nodo, field_name])
                    if ret_ is False:
                        return
                    else:
                        text = str(ret_)                       
                        
                except Exception:
                    self.logger.exception(
                        "KUT2FPDF:: Error llamando a function %s", function_name)
                    return
            elif calculation_type == "1":
                text = self._parser_tools.calculate_sum(field_name, self.last_data_processed, self._xml_data, self.actual_data_level)
            
            elif calculation_type in ("5"):
                if data_row is None:
                    data_row = self._xml_data[0]
                
                text = data_row.get(field_name)
            
        if data_type is not None:
            text = self._parser_tools.calculated(text, int(data_type), xml.get("Precision"), data_row)
            
        if data_type == "5":
            is_image = True
        
        elif data_type == "6":
            is_barcode = True
            
        
        if xml.get("BlankZero") == "1" and text is not None:
            res_ = re.findall(r'\d+', text)
            res_ = "".join(res_)
            if int(res_) == 0:
                return
            
            

        if text is not None and text.startswith(filedir("../tempdata")):
            is_image = True

        negValueColor = xml.get("NegValueColor")
        Currency = xml.get("Currency")

        commaSeparator = xml.get("CommaSeparator")
        dateFormat = xml.get("DateFormat")

        if is_image:
            self.draw_image(x, y, W, H, xml, text)
        elif is_barcode:
            self.draw_barcode(x, y, W, H, xml, text)
        else:
            level = data_row.get("level")
            if level and str(level) in self.detailn.keys():
                val = "%s_header_%s_%s" % ( self.detailn[str(level)], level,field_name)
            
            if xml.get("DrawAtHeader") == "true" and level:
                if section_name == "DetailHeader":
                    val = ""
                    self.drawText(x, y, W, H, xml, val)
            
                    print(level, section_name, val, text)
            
            if section_name == "DetailFooter" and xml.get("DrawAtHeader") == "true":
                self.draws_at_header[val] = text
                print("Añadiendo a", val, text, level)
            
            else:
                self.drawText(x, y, W, H, xml, text)


    """
    Dibuja un campo texto en la página.
    @param x. Pos x de la etiqueta.
    @param y. Pos y de la etiqueta.
    @param W. Anchura de la etiqueta.
    @param H. Altura de la etiqueta.
    @param xml. Sección del xml afectada.
    @param txt. Texto calculado de la etiqueta a crear.
    """

    def drawText(self, x, y, W, H, xml, txt):


        if txt in ("None", None):
            return
        
        
        txt = self._parser_tools.restore_text(txt)
        
        resizeable = False
        
        if xml.get("ChangeHeight") == "1":
            resizeable = True
        
        
        
        height_resized = False
        orig_x = x
        orig_y = y
        orig_W = W
        orig_H = H
        # Corregimos margenes:
        x = self.calculateLeftStart(x)
        W = self.calculateWidth(W, x)
        
        #bg_color = xml.get("BackgroundColor").split(",")
        fg_color = self.get_color(xml.get("ForegroundColor"))
        self._document.set_text_color(fg_color[0], fg_color[1], fg_color[2])
        
        
        
        #self._document.set_draw_color(255, 255, 255)

        #if xml.get("BorderStyle") == "1":
            # FIXME: Hay que ajustar los margenes
        
        #font_name, font_size, font_style
        font_style = ""
        font_size = int(xml.get("FontSize"))
        font_name_orig = xml.get("FontFamily").lower() if xml.get("FontFamily") is not None else "helvetica"
        font_name = font_name_orig
        
        font_w = xml.get("FontWeight")
        
        if font_w in (None, "50"): #Normal
            font_w = 100
        elif int(font_w) >= 65:
            font_style += "B"
            font_w = 100

        
        
        
        fontI = xml.get("FontItalic")
        fontU = xml.get("FontUnderlined")  # FIXME: hay que ver si es así

        background_color = self.get_color(xml.get("BackgroundColor"))
        #if background_color != [255,255,255]: #Los textos que llevan fondo no blanco van en negrita
        #    font_style += "B"

        if fontI == "1":
            font_style += "I"

        if fontU == "1":
            font_style += "U"
        
        
        

        font_full_name = "%s%s" % (font_name, font_style)

        if font_full_name not in self._avalible_fonts:
            font_found = self._parser_tools.find_font(font_full_name)            
            if font_found:
                self.logger.warning("KUT2FPDF::Añadiendo el tipo de letra %s (%s)", font_full_name, font_found)
                self._document.add_font(font_full_name, "", font_found, True)
                self._avalible_fonts.append(font_full_name)

            else:
                if font_full_name not in self._unavalible_fonts:
                    self.logger.warning("KUT2FPDF:: No se encuentra el tipo de letra %s. Sustituido por helvetica%s." %(font_full_name, font_style))
                    self._unavalible_fonts.append(font_full_name)
                font_name = "helvetica"
        
        if font_name is not font_name_orig and font_name_orig.lower().find("narrow") > -1:
            font_w = 85
                    
        
        self._document.set_font(font_name, font_style, font_size)
        self._document.set_stretching(font_w)
        # Corregir alineación
        VAlignment = xml.get("VAlignment")  # 0 izquierda, 1 centrado,2 derecha
        HAlignment = xml.get("HAlignment")
        
        layout_direction = xml.get("layoutDirection")
        
        start_section_size = self._actual_section_size
        result_section_size = 0
        #Miramos si el texto sobrepasa el ancho
        
        array_text = []
        array_n = []
        text_lines = []
        if txt.find("\n") > -1:
            for t in txt.split("\n"):
                array_n.append(t)
        if array_n: #Hay saltos de lineas ...
            for n in array_n:
                text_lines.append(n)
        else: #No hay saltos de lineas
            text_lines.append(txt)
            
        for tl in text_lines:
            str_width = self._document.get_string_width(tl)
            if str_width > W + 2 and xml.tag !="Label" and resizeable: #Una linea es mas larga que el ancho del campo(Dejando 2 de juego)
                height_resized = True
                array_text = self.split_text(tl, W)
            else:
            
                array_text.append(tl)
        
        #calculated_h = orig_H * len(array_text)
        self.drawRect(orig_x, orig_y, orig_W, orig_H, xml)
        
        processed_lines = 0
        extra_size = 0
        for actual_text in array_text:
            
            if actual_text is None:
                continue
            
            processed_lines += 1
            
            if processed_lines > 1:
                extra_size += font_size + 2
            
            if HAlignment == "1":  # sobre X
                # Centrado
                x = x + (W / 2) - (self._document.get_string_width(actual_text) / 2)
                #x = x + (W / 2) - (str_width if not height_resized else W / 2)
            elif HAlignment == "2":
                # Derecha
                x = x + W - self._document.get_string_width(actual_text) - 2 # -2 de margen
                #x = x + W - str_width if not height_resized else W
            else:
                # Izquierda
                x = x + 2

                
            if VAlignment == "1":  # sobre Y
                # Centrado
                #y = (y + ((H / 2) / processed_lines)) + (((self._document.font_size_pt / 2) / 2) * processed_lines) 
                y = ( orig_y  + ( orig_H / 2))+ ((self._document.font_size_pt / 2) /2)
            elif VAlignment == "2":
                # Abajo
                y = orig_y + orig_H - font_size
            else:
                # Arriba
                y = orig_y + font_size
        
            y = y + extra_size
            
            if self.design_mode:
                self.write_debug(self.calculateLeftStart(orig_x), y, "Hal:%s, Val:%s, T:%s st:%s" % (HAlignment, VAlignment, txt, font_w), 6, "green")
                if xml.tag == "CalculatedField":
                    self.write_debug(self.calculateLeftStart(orig_x), y, "CalculatedField:%s, Field:%s" % (xml.get("FunctionName"), xml.get("Field")), 3, "blue")
            
            
            
            
            self._document.text(x, y, actual_text)
            result_section_size += start_section_size
        
        result_section_size = result_section_size - start_section_size
        
        if self.increase_section_size < extra_size: #Si algun incremento extra hay superior se respeta
            self.increase_section_size = extra_size

    
    def split_text(self, texto, limit_w):
        list_ = []
        linea_ = None   
        
        for t in texto.split(" "):
            if linea_  is None and t == "":
                continue
            
            if linea_ is not None:
                if self._document.get_string_width(linea_ + t) > limit_w:
                    list_.append(linea_)
                    linea_ = ""
            else:
                linea_ = ""
            
            linea_ += "%s " % t
        
        list_.append(linea_)
        
        return list_
                
            
                   
        
    """
    Dibuja un cuadrado en la página actual.
    @param x. Pos x del cuadrado.
    @param y. Pos y del cuadrado.
    @param W. Anchura del cuadrado.
    @param H. Altura del cuadrado.
    """
    def get_color(self, value):
        value = value.split(",")
        r = None
        g = None
        b = None
        if len(value) == 3:
            r = int(value[0])
            g = int(value[1])
            b = int(value[2])
        else:
            r = int(value[0:2])
            g = int(value[3:5])
            b = int(value[6:8])
        
        return [r,g,b]

    def drawRect(self, x, y, W, H, xml = None):
        style_ = ""
        border_color = None
        bg_color = None
        line_width = self._document.line_width
        border_width = 0.2
        #Calculamos borde  y restamos del ancho
        orig_x = x
        orig_y = y
        orig_w = W
        
        x = self.calculateLeftStart(orig_x) 
        W = self.calculateWidth(W, x)
          
        
        if xml is not None and not self.design_mode:
            if xml.get("BorderStyle") == "1":
                
                border_color = self.get_color(xml.get("BorderColor"))
                self._document.set_draw_color(border_color[0], border_color[1], border_color[2])
                style_ += "D"
                    
            
            bg_color = self.get_color(xml.get("BackgroundColor"))
            self._document.set_fill_color(bg_color[0], bg_color[1], bg_color[2])
            style_ =  "F" + style_
            
            border_width = int(xml.get("BorderWidth") if xml.get("BorderWidth") else 0.2)
        else:
            self.write_cords_debug(x,y,W,H, orig_x, orig_w)
            style_ = "D"
            self._document.set_draw_color(0, 0, 0)
            
        if style_ is not "":
            self._document.set_line_width(border_width)

            self._document.rect(x, y, W, H, style_)
            self._document.set_line_width(line_width)
            
            self._document.set_xy(orig_x, orig_y)
        #self._document.set_draw_color(255, 255, 255)
        #self._document.set_fill_color(0, 0, 0)
    
    def write_cords_debug(self, x, y, w, h, ox, ow):
        self.write_debug(x,y,"X:%s Y:%s W:%s H:%s orig_x:%s, orig_W:%s" % (round(x, 2),round(y, 2),round(w, 2),round(h, 2), round(ox, 2), round(ow, 2)), 2, "red")
        
    
    def write_debug(self, x,y,text, h, color = None):
        orig_color = self._document.text_color
        r = None
        g = None
        b = None
        current_font_family = self._document.font_family
        current_font_size = self._document.font_size_pt
        current_font_style = self._document.font_style
        if color is "red":
            r = 255
            g = 0
            b = 0
        elif color is "green":
            r = 0
            g = 255
            b = 0
        elif color is "blue":
            r = 0
            g = 0
            b = 255
        
        self._document.set_text_color(r,g,b)
        self._document.set_font_size(4)
        self._document.text(x, y + h, text) 
        self._document.text_color = orig_color
        #self._document.set_xy(orig_x, orig_y)
        self._document.set_font(current_font_family, current_font_style, current_font_size)
        
    """
    Inserta una imagen en la página actual.
    @param x. Pos x de la imagen.
    @param y. Pos y de la imagen.
    @param W. Anchura de la imagen.
    @param H. Altura de la imagen.
    @param xml. Sección del xml afectada.
    @param file_name. Nombr del fichero de tempdata a usar
    """

    def draw_image(self, x, y, W, H, xml, file_name):
        import os
        if not file_name.lower().endswith(".png"):
            file_name = self._parser_tools.parseKey(file_name)
        
        
        if file_name is not None and os.path.exists(file_name):            
            x = self.calculateLeftStart(x)
            W = self.calculateWidth(W, x)
            
            self._document.image(file_name, x, y, W, H, "PNG")
    
    def draw_barcode(self, x, y, W, H, xml, text):
        if text == "None":
            return
        from pineboolib.fllegacy.flcodbar import FLCodBar
        file_name = aqApp.tmp_dir()
        file_name += "/%s.png" % (text)
        type = xml.get("CodBarType")
        
        if not os.path.exists(file_name):   
            
            bar_code = FLCodBar(text) #Code128
            if type is not None:
                type = bar_code.nameToType(type.lower())
                bar_code.setType(type)
                
            pix = bar_code.pixmap()
            if not pix.isNull():
                pix.save(file_name, "PNG")
            
        self.draw_image(x , y, W, H, xml, file_name)
            
            
            
 
        
    """
    Define los parámetros de la página
    @param xml: Elemento xml con los datos del fichero .kut a procesar
    """

    def setPageFormat(self, xml):
        custom_size = None

        self._bottom_margin = int(xml.get("BottomMargin"))
        self._left_margin = int(xml.get("LeftMargin"))
        self._right_margin = int(xml.get("RightMargin"))
        self._top_margin = int(xml.get("TopMargin"))
        
        page_size = int(xml.get("PageSize"))
        page_orientation = xml.get("PageOrientation")
        
        if page_size in [30, 31]:
            custom_size = [int(xml.get("CustomHeightMM")), int(xml.get("CustomWidthMM"))]

        self._page_orientation = "P" if page_orientation == "0" else "L"
        self._page_size = self._parser_tools.converPageSize(
            page_size, int(page_orientation), custom_size)  # devuelve un array
        
        
    
    def draw_margins(self):
        self.draw_debug_line(0 + self._left_margin , 0, 0+ self._left_margin  , self._page_size[1]) #Vertical derecha
        self.draw_debug_line(self._page_size[0] - self._right_margin , 0, self._page_size[0] - self._right_margin  , self._page_size[1])#Vertical izquierda
        self.draw_debug_line(0, 0 + self._top_margin, self._page_size[0], 0 + self._top_margin) #Horizontal superior
        self.draw_debug_line(0, self._page_size[1] - self._bottom_margin, self._page_size[0], self._page_size[1] - self._bottom_margin) #Horizontal inferior
    def draw_debug_line(self, X1, Y1, X2, Y2, title= None, color="GREY"):
        dash_length = 2
        space_length = 2
        
        r = 0
        g = 0
        b = 0
        if color == "GREY":
            r = 220
            g = 220
            b = 220
        self._document.set_line_width(1)
        self._document.set_draw_color(r, g, b)
        self._document.dashed_line(X1, Y1, X2, Y2, dash_length, space_length)
    
    def number_pages(self):
        return self._actual_append_page_no if self._actual_append_page_no > 0 else 0
    
    def reset_page_no(self):
        self._actual_append_page_no = -1
コード例 #17
0
ファイル: kut2fpdf.py プロジェクト: deavid/pineboo
class kut2fpdf(object):

    _document = None  # Aquí se irán guardando los datos del documento
    logger = None
    _xml = None
    _xml_data = None
    _page_orientation = None
    _page_size = None
    _bottom_margin = None
    _left_margin = None
    _right_margin = None
    _top_margin = None
    _page_top = {}
    _data_row = None  # Apunta a la fila actual en data
    _parser_tools = None
    _avalible_fonts = []

    def __init__(self):

        self.logger = logging.getLogger("kut2rml")
        checkDependencies({"fpdf": "fpdf"})
        from pineboolib.plugins.kugar.parsertools import parsertools
        self._parser_tools = parsertools()

    """
    Convierte una cadena de texto que contiene el ".kut" en un pdf y retorna la ruta a este último.
    @param name. Nombre de ".kut".
    @param kut. Cadena de texto que contiene el ".kut".
    @param data. Cadena de texto que contiene los datos para ser rellenados en el informe.
    @return Ruta a fichero pdf.
    """

    def parse(self, name, kut, data):

        try:
            self._xml = self._parser_tools.loadKut(kut)
        except Exception:
            self.logger.exception(
                "KUT2FPDF: Problema al procesar %s.kut", name)
            return False

        try:
            self._xml_data = load2xml(data)
        except Exception:
            self.logger.exception("KUT2FPDF: Problema al procesar xml_data")
            return False

        self.setPageFormat(self._xml)
        # self._page_orientation =
        # self._page_size =

        from fpdf import FPDF
        self._document = FPDF(self._page_orientation, "pt", self._page_size)
        # Seteamos rutas a carpetas con tipos de letra ...

        # Cargamos las fuentes disponibles
        for f in self._document.core_fonts:
            self.logger.debug("KUT2FPDF :: Adding font %s", f)
            self._avalible_fonts.append(f)

        self.newPage()
        self.processDetails()

        pdfname = pineboolib.project.getTempDir()
        pdfname += "/%s_%s.pdf" % (name, datetime.datetime.now().strftime("%Y%m%d%H%M%S"))

        # Datos del creador del documento
        self._document.set_title(name)
        self._document.set_author("Pineboo - kut2fpdf plugin")

        self._document.output(pdfname, 'F')

        return pdfname

    """
    Indica el techo para calcular la posición de los objetos de esa sección.
    @return Número con el techo de la página actual.
    """

    def topSection(self):
        return self._page_top[str(self._document.page_no())]

    """
    Actualiza el valor del techo de la página actual. Se suele actualizar al procesar una sección.
    @param value. Numero que elspecifica el nuevo techo.
    """

    def setTopSection(self, value):
        self._page_top[str(self._document.page_no())] = value

    """
    Añade una nueva página al documento.
    """

    def newPage(self):
        self._document.add_page(self._page_orientation)
        self._page_top[str(self._document.page_no())] = self._top_margin
        self._document.set_margins(self._left_margin, self._top_margin,
                                   self._right_margin)  # Lo dejo pero no se nota nada
        # Corta con el borde inferior ...
        # self._document.set_auto_page_break(
        #    True, self._document.h - self._bottom_margin)

        self.processSection("PageHeader")
    """
    Procesa las secciones details con sus correspondientes detailHeader y detailFooter.
    """

    def processDetails(self):
        # Procesamos la cabecera si procede ..
        prevLevel = 0
        level = None
        for data in self._xml_data.findall("Row"):
            level = int(data.get("level"))
            if prevLevel > level:
                self.processData("DetailFooter", data, prevLevel)
            elif prevLevel < level:
                self.processData("DetailHeader",  data, level)

            self.processData("Detail", data, level)

            prevLevel = level

        if level:
            for l in reversed(range(level + 1)):
                self.processData("DetailFooter", data, l)

        if self._xml.find("PageFooter"):
            self.processSection("PageFooter")
        elif self._xml.find("AddOnFooter"):
            self.processSection("AddOnFooter")

    """
    Paso intermedio que calcula si detailHeader + detail + detailFooter entran en el resto de la ṕagina. Si no es así crea nueva página.
    @param section_name. Nombre de la sección a procesar.
    @param data. Linea de datos a procesar.
    @param data_level. Nivel de seccion.
    """

    def processData(self, section_name, data, data_level):
        listDF = self._xml.findall(section_name)
        for dF in listDF:
            if dF.get("Level") == str(data_level):
                if section_name == "Detail" and (not dF.get("DrawIf") or data.get(dF.get("DrawIf"))):
                    heightCalculated = self._parser_tools.getHeight(dF) + self.topSection()
                    for dFooter in self._xml.findall("DetailFooter"):
                        if dFooter.get("Level") == str(data_level):
                            heightCalculated += self._parser_tools.getHeight(dFooter)
                    pageFooter = self._xml.get("PageFooter")
                    if pageFooter:
                        if self._document.page_no() == 1 or pageFooter.get("PrintFrecuency") == "1":
                            heightCalculated += self._parser_tools.getHeight(pageFooter)

                    heightCalculated += self._bottom_margin

                    if heightCalculated > self._document.h:  # Si nos pasamos
                        self.processSection("PageFooter")  # Pie de página
                        self.newPage()

                if not dF.get("DrawIf") or data.get(dF.get("DrawIf")):
                    self.processXML(dF, data)
                    #self.logger.debug("%s_BOTTON = %s" % (name.upper(), self.actualVSize[str(self.pagina)]))

    """
    Procesa las secciones fuera de detail, pageHeader, pageFooter, AddOnFooter.
    @param name. Nombre de la sección a procesar.
    """

    def processSection(self, name):
        sec_ = self._xml.find(name)
        if sec_:
            if sec_.get("PrintFrequency") == "1" or self._document.page_no() == 1:
                if sec_.tag == "PageFooter":
                    self.setTopSection(self._document.h - int(sec_.get("Height")))
                self.processXML(sec_)

    """
    Procesa un elemento de xml.
    @param xml: El elemento a procesar.
    @param. data: Linea de datos afectada.
    """

    def processXML(self, xml, data=None):
        fix_height = True
        if xml.tag == "DetailFooter":
            if xml.get("PlaceAtBottom") == "true":
                self.setTopSection(self.topSection() + self._parser_tools.getHeight(xml))

        if xml.tag == "PageFooter":
            fix_height = False

        for child in xml.iter():
            if child.tag in ("Label", "Field", "Special", "CalculatedField"):
                self.processText(child, data, fix_height)
            elif child.tag == "Line":
                self.processLine(child, fix_height)

        if xml.get("PlaceAtBottom") != "true":
            self.setTopSection(self.topSection() + self._parser_tools.getHeight(xml))

    """
    Procesa una linea.
    @param xml. Sección de xml a procesar.
    @param fix_height. Ajusta la altura a los .kut originales, excepto el pageFooter.
    """

    def processLine(self, xml, fix_height=True):

        color = xml.get("Color")
        r = 0 if not color else int(color.split(",")[0])
        g = 0 if not color else int(color.split(",")[1])
        b = 0 if not color else int(color.split(",")[2])

        #style = int(xml.get("Style"))
        width = int(xml.get("Width"))
        X1 = self.calculateLeftStart(xml.get("X1"))
        X2 = self.calculateRightEnd(xml.get("X2"))
        # Ajustar altura a secciones ya creadas
        Y1 = int(xml.get("Y1")) + self.topSection()
        Y2 = int(xml.get("Y2")) + self.topSection()
        if fix_height:
            Y1 = self._parser_tools.heightCorrection(Y1)
            Y2 = self._parser_tools.heightCorrection(Y2)
        self._document.set_line_width(width)
        self._document.set_draw_color(r, g, b)
        self._document.line(X1, Y1, X2, Y2)

    """
    Comprueba si excedemos el margen izquierdo de la página actual
    @param x. Posición a comprobar.
    @return Valor corregido, si procede.
    """

    def calculateLeftStart(self, x):
        x = int(x)
        ret_ = x
        if x < self._left_margin:
            ret_ = self._left_margin

        return ret_

    """
    Comprueba si excedemos el margen derecho de la página actual
    @param x. Posición a comprobar.
    @return Valor corregido, si procede.
    """

    def calculateRightEnd(self, x):
        x = int(x)
        ret_ = x
        if x > (self._document.w - self._right_margin):
            ret_ = self._document.w - self._right_margin

        return ret_

    """
    Procesa una etiqueta. Esta peude ser un campo calculado, una etiqueta, un campo especial o una imagen.
    @param xml. Sección de xml a procesar.
    @param fix_height. Ajusta la altura a los .kut originales, excepto el pageFooter.
    """

    def processText(self, xml, data_row=None, fix_height=True):
        isImage = False
        text = xml.get("Text")
        BorderWidth = int(xml.get("BorderWidth"))
        borderColor = xml.get("BorderColor")

        # x,y,W,H se calcula y corrigen aquí para luego estar correctos en los diferentes destinos posibles
        W = int(xml.get("Width"))
        H = self._parser_tools.getHeight(xml)

        x = int(xml.get("X"))
        y = int(xml.get("Y")) + self.topSection()  # Añade la altura que hay ocupada por otras secciones
        if fix_height:
            y = self._parser_tools.heightCorrection(y)  # Corrige la posición con respecto al kut original

        dataType = xml.get("Datatype")

        if xml.tag == "Field" and data_row is not None:
            text = data_row.get(xml.get("Field"))

        elif xml.tag == "Special":
            text = self._parser_tools.getSpecial(
                text[1:len(text) - 1], self._document.page_no())

        elif xml.tag == "CalculatedField":
            if xml.get("FunctionName"):
                function_name = xml.get("FunctionName")
                try:
                    nodo = self._parser_tools.convertToNode(data_row)
                    text = str(pineboolib.project.call(function_name, [nodo]))
                except Exception:
                    self.logger.exception(
                        "KUT2FPDF:: Error llamando a function %s", function_name)
                    return
            else:
                if data_row is None:
                    data_row = self._xml_data[0]
                if xml.get("Field"):
                    text = data_row.get(
                        xml.get("Field")) if not "None" else ""

            if text and dataType is not None:
                text = self._parser_tools.calculated(text, int(dataType), xml.get("Precision"), data_row)

            if dataType == "5":
                isImage = True

        if text and text.startswith(filedir("../tempdata")):
            isImage = True

        precision = xml.get("Precision")
        negValueColor = xml.get("NegValueColor")
        Currency = xml.get("Currency")

        commaSeparator = xml.get("CommaSeparator")
        dateFormat = xml.get("DateFormat")

        if not isImage:

            self.drawText(x, y, W, H, xml, text)
        else:
            self.drawImage(x, y, W, H, xml, text)

    """
    Dibuja un campo texto en la página.
    @param x. Pos x de la etiqueta.
    @param y. Pos y de la etiqueta.
    @param W. Anchura de la etiqueta.
    @param H. Altura de la etiqueta.
    @param xml. Sección del xml afectada.
    @param txt. Texto calculado de la etiqueta a crear.
    """

    def drawText(self, x, y, W, H, xml, txt):

        if txt in ("None", None):
            return

        # Corregimos margenes:
        x = self.calculateLeftStart(x)
        W = self.calculateRightEnd(x + W) - x

        bg_color = xml.get("BackgroundColor").split(",")
        fg_color = xml.get("ForegroundColor").split(",")

        self._document.set_text_color(int(fg_color[0]), int(fg_color[1]), int(fg_color[2]))
        self._document.set_fill_color(int(bg_color[0]), int(bg_color[1]), int(bg_color[2]))

        if xml.get("BorderStyle") == "1":
            # FIXME: Hay que ajustar los margenes
            self.drawRect(x, y, W, H)

        #font_name, font_size, font_style
        font_style = ""
        font_size = int(xml.get("FontSize"))
        font_name = xml.get("FontFamily").lower()

        fontW = int(xml.get("FontWeight"))
        fontI = xml.get("FontItalic")
        fontU = xml.get("FontUnderlined")  # FIXME: hay que ver si es así

        if fontW > 60 and font_size > 10:
            font_style += "B"

        if fontI == "1":
            font_style += "I"

        if fontU == "1":
            font_style += "U"

        while font_name not in self._avalible_fonts:
            font_found = self._parser_tools.find_font(font_name)
            if font_found:
                self.logger.info("KUT2FPDF::Añadiendo el tipo de letra %s (%s)", font_name, font_found)
                self._document.add_font(font_name, "", font_found, True)
                self._avalible_fonts.append(font_name)

            else:
                self.logger.warning("KUT2FPDF:: No se encuentra el tipo de letra %s. Sustituido por helvetica.", font_name)
                font_name = "helvetica"

        self._document.set_font(font_name, font_style, font_size)

        # Corregir alineación
        VAlignment = xml.get("VAlignment")  # 0 izquierda, 1 centrado,2 derecha
        HAlignment = xml.get("HAlignment")

        if HAlignment == "1":  # sobre X
            # Centrado
            x = x + (W / 2) - (self._document.get_string_width(txt) / 2)
        elif HAlignment == "2":
            # Derecha
            x = x + W - self._document.get_string_width(txt)
        else:
            # Izquierda
            x = x

        if VAlignment == "1":  # sobre Y
            # Centrado
            y = (y + H / 2) + (self._document.font_size_pt / 2)
        elif VAlignment == "2":
            # Abajo
            y = y + W - font_size
        else:
            # Arriba
            y = y

        self._document.text(x, y, txt)

    """
    Dibuja un cuadrado en la página actual.
    @param x. Pos x del cuadrado.
    @param y. Pos y del cuadrado.
    @param W. Anchura del cuadrado.
    @param H. Altura del cuadrado.
    """

    def drawRect(self, x, y, W, H):
        self._document.rect(x, y, W, H, "DF")

    """
    Inserta una imagen en la página actual.
    @param x. Pos x de la imagen.
    @param y. Pos y de la imagen.
    @param W. Anchura de la imagen.
    @param H. Altura de la imagen.
    @param xml. Sección del xml afectada.
    @param file_name. Nombr del fichero de tempdata a usar
    """

    def drawImage(self, x, y, W, H, xml, file_name):

        self._document.image(file_name, x, y, W, H, "PNG")
    """
    Define los parámetros de la página
    @param xml: Elemento xml con los datos del fichero .kut a procesar
    """

    def setPageFormat(self, xml):
        custom_size = None

        self._bottom_margin = int(xml.get("BottomMargin"))
        self._left_margin = int(xml.get("LeftMargin"))
        self._right_margin = int(xml.get("RightMargin"))
        self._top_margin = int(xml.get("TopMargin"))

        page_size = int(xml.get("PageSize"))
        page_orientation = xml.get("PageOrientation")

        if page_size in [30, 31]:
            custom_size = [int(xml.get("CustomHeightMM")), int(xml.get("CustomWidthMM"))]

        self._page_orientation = "P" if page_orientation == "0" else "L"
        self._page_size = self._parser_tools.converPageSize(
            page_size, int(page_orientation), custom_size)  # devuelve un array
コード例 #18
0
            def create_pdf2(self):
                # Set up a logo
                conn = sqlite3.connect("db_member.db")
                conn.row_factory = sqlite3.Row
                cur = conn.cursor()
                cur.execute("SELECT max(id) FROM member")
                rows = cur.fetchall()
                for row in rows:
                    print("%s" % (row["max(id)"]))

                cur2 = conn.cursor()
                cur2.execute("SELECT name_pk FROM print_dt")
                cur3 = conn.cursor()
                cur3.execute("SELECT address FROM print_dt")
                cur4 = conn.cursor()
                cur4.execute("SELECT dt_name FROM print_dt")
                cur5 = conn.cursor()
                cur5.execute("SELECT * FROM `member`")

                rows5 = cur5.fetchall()
                rows4 = cur4.fetchall()
                rows3 = cur3.fetchall()
                rows2 = cur2.fetchall()

                for row5 in rows5:
                    row5[6]
                for row2 in rows2:
                    row2["name_pk"]

                for row3 in rows3:
                    row3["address"]

                for row4 in rows4:
                    row4["dt_name"]



                t = row2["name_pk"]
                t1 = row3["address"]
                t2 = "BS: " + row4["dt_name"]

                pdf = FPDF()
                pdf.set_font("Arial", size=12)
                pdf.add_page()

                pdf.image('demo.png', 8, 10, 25)
                pdf.add_font('DejaVu', '', 'DejaVuSerif-Italic.ttf', uni=True)
                pdf.set_font('DejaVu', '', 16)
                pdf.set_text_color(0, 70, 255)
                pdf.cell(35)
                pdf.cell(0, 5, t, ln=1)
                pdf.set_font('DejaVu', '', 14)

                pdf.cell(70)
                pdf.cell(0, 10, t2, ln=1)
                pdf.set_font('DejaVu', '', 14)
                pdf.set_text_color(0, 70, 255)
                pdf.cell(30)

                pdf.cell(0, 0, "ĐC:", ln=1)
                pdf.set_font('DejaVu', '', 12)
                pdf.set_text_color(0, 0, 0)
                pdf.cell(40)
                pdf.cell(0, 0, t1, ln=1)
                pdf.set_draw_color(0, 0, 0)
                pdf.set_line_width(1)
                pdf.line(30, 30, 180, 30)

                pdf.set_font('DejaVu', '', 16)
                pdf.set_text_color(255, 0, 40)
                pdf.cell(35)
                pdf.cell(0, 5, ' ', ln=1)

                pdf.set_font('DejaVu', '', 16)
                pdf.set_text_color(255, 0, 40)
                pdf.cell(35)
                pdf.cell(0, 15, 'PHIẾU KHÁM NỘI SOI TAI-MŨI-HỌNG', ln=1)

                pdf.set_font('DejaVu', '', 12)
                pdf.set_text_color(0, 70, 255)
                pdf.cell(75)
                pdf.cell(0, 0, 'Số Phiếu : ' + str(row5[0]), ln=1)

                pdf.set_font('DejaVu', '', 16)
                pdf.set_text_color(0, 70, 255)
                pdf.cell(0, 8, ' ', ln=1)

                pdf.set_font('DejaVu', '', 10)
                pdf.cell(5)
                pdf.cell(0, 0, 'Tên bệnh nhân : ' + str(row5[1]), ln=1)
                pdf.cell(85)
                pdf.cell(0, 0, 'Tuổi : ' + str(row5[4]), ln=1)
                pdf.cell(135)
                pdf.cell(0, 0, 'Giới tính : ' + str(row5[6]), ln=1)

                pdf.set_font('DejaVu', '', 10)
                pdf.set_text_color(0, 70, 255)
                pdf.cell(0, 8, ' ', ln=1)
                pdf.cell(5)
                pdf.cell(0, 0, 'Địa chỉ : ' + str(row5[3]), ln=1)
                pdf.cell(85)
                pdf.cell(0, 0, 'Số bảo hiểm : ' + str(row5[7]), ln=1)
                pdf.cell(135)
                pdf.cell(0, 0, 'Nghề nghiệp : ' + str(row5[2]), ln=1)

                pdf.set_font('DejaVu', '', 10)
                pdf.set_text_color(0, 70, 255)
                pdf.cell(0, 8, ' ', ln=1)
                pdf.cell(5)
                pdf.cell(0, 0, 'Triệu chứng : ' + str(row5[5]), ln=1)
                pdf.cell(85)
                pdf.cell(0, 0, 'Số bảo hiểm : ' + str(row5[7]), ln=1)
                pdf.cell(135)
                pdf.cell(0, 0, 'Nghề nghiệp : ' + str(row5[2]), ln=1)

                pdf.set_font('DejaVu', '', 14)
                pdf.cell(0, 15, ' ', ln=1)
                pdf.cell(70)
                pdf.cell(0, 0, 'HÌNH ẢNH NỘI SOI ', ln=1)
                #
                file_name =  ('anh\%s.png' % ("a" + str(row["max(id)"]) + "a" + str(2)))
                file_name1 = ('anh\%s.png' % ("a" + str(row["max(id)"]) + "a" + str(3)))
                file_name2 = ('anh\%s.png' % ("a" + str(row["max(id)"]) + "a" + str(4)))
                file_name3 = ('anh\%s.png' % ("a" + str(row["max(id)"]) + "a" + str(5)))
                file_name4 = ('anh\%s.png' % ("a" + str(row["max(id)"]) + "a" + str(6)))
                file_name5 = ('anh\%s.png' % ("a" + str(row["max(id)"]) + "a" + str(7)))
                #
                pdf.image(file_name, 12, 90, 60)
                pdf.image(file_name1, 12, 150, 60)
                pdf.image(file_name2, 74, 90, 60)
                pdf.image(file_name3, 74, 150, 60)
                pdf.image(file_name4, 136, 90, 60)
                pdf.image(file_name5, 136, 150, 60)

                pdf.set_font('DejaVu', '', 16)
                pdf.cell(0, 132, ' ', ln=1)
                pdf.cell(60)
                pdf.cell(0, 0, 'MÔ TẢ KẾT QUẢ NỘI SOI ', ln=1)
                pdf.set_font('DejaVu', '', 12)
                pdf.cell(0, 8, ' ', ln=1)
                pdf.cell(12)
                pdf.cell(0, 7, 'Chẩn đoán : ', ln=1)
                pdf.cell(12)
                pdf.cell(0, 7, 'Điều trị : ', ln=1)
                pdf.cell(12)
                pdf.cell(0, 7, 'Chỉ định bác sĩ : ', ln=1)

                pdf.set_x(120)
                pdf.cell(0, 10, " Ngày " + str(today.day) + " Tháng " + str(today.month) + " Năm " + str(today.year),
                         ln=1)
                pdf.set_x(145)
                pdf.cell(0, 6, 'Bác sĩ : ', ln=1)
                pdf.cell(0, 15, ' ', ln=1)
                pdf.set_x(126)
                pdf.cell(0, 0, t2, ln=1)
                directory1 = "doccument/"
                if not os.path.exists(directory1):
                    os.makedirs(directory1)
                pdf.output('doccument\%s.pdf' %("a" + str(row["max(id)"])))
                webbrowser.open_new(r'doccument\%s.pdf' %("a" + str(row["max(id)"])))
                conn.commit()
                cur.close()
コード例 #19
0
def invoice_to_PDF(order, client, filename=None):
    """
    creates and pdf invoice of the order, and returns the pfd as a string buffer.
    @order: the order used to create a invoice
    @client: if specified this data is used for the client address, otherwise the address is taken from the order
    @filename: if specified the pdf is written to disk with the given filename,
               filename can contains a complete path
    """
    pdf = FPDF()
    pdf.add_font('DejaVu',
                 '',
                 fname='{0}/fonts/ttf-dejavu/DejaVuSerif.ttf'.format(
                     settings.STATIC_ROOT),
                 uni=True)
    pdf.add_font('DejaVu',
                 'B',
                 fname='{0}/fonts/ttf-dejavu/DejaVuSerif-Bold.ttf'.format(
                     settings.STATIC_ROOT),
                 uni=True)
    pdf.add_font('DejaVu',
                 'I',
                 fname='{0}/fonts/ttf-dejavu/DejaVuSerif-Italic.ttf'.format(
                     settings.STATIC_ROOT),
                 uni=True)
    pdf.add_font(
        'DejaVu',
        'BI',
        fname='{0}/fonts/ttf-dejavu/DejaVuSerif-BoldItalic.ttf'.format(
            settings.STATIC_ROOT),
        uni=True)

    def check_for_new_page():
        if pdf.get_y() > PAGE_BOTTOM_Y:
            pdf.add_page()

    def text_fonts():
        pdf.set_font('DejaVu', '', 11.0)

    def add_row(column_data, columns=COLUMNS, border=0):
        """
        add a row to the pdf
        """
        cell_margin_left = 2
        cell_margin_right = 2

        check_for_new_page()
        last_y = START_TABLE_Y if pdf.page_no() == 1 else TOP_PAGE_MARGIN
        row_y = pdf.get_y() if pdf.get_y() > last_y else last_y
        max_y = row_y  # max_y is used to check if multi cell is wrapping text and so uses more rows.

        next_x = LEFT_PAGE_MARGIN
        for i, column in enumerate(columns):
            width = column[0]
            align = column[1]
            pdf.set_xy(next_x + cell_margin_left, row_y)
            pdf.multi_cell(w=width - cell_margin_right,
                           h=TABLE_ROW_HIGH,
                           txt=column_data[i] if len(column_data) > i else '',
                           align=align,
                           border=border)
            max_y = max(max_y,
                        pdf.get_y())  # keep track if multi cell wrapped text
            next_x += width

        pdf.set_y(max_y)

    def add_row_line():
        last_y = START_TABLE_Y if pdf.page_no() == 1 else TOP_PAGE_MARGIN
        line_y = pdf.get_y() if pdf.get_y() > last_y else last_y - 2
        pdf.set_xy(LEFT_PAGE_MARGIN + 1, line_y)
        pdf.line(LEFT_PAGE_MARGIN, line_y + 2, RIGHT_PAGE_MARGIN, line_y + 2)
        pdf.set_y(line_y + 5)

    def draw_vertical_lines_around_columns(columns=COLUMNS,
                                           top_y=TOP_PAGE_MARGIN,
                                           bottom_y=PAGE_BOTTOM_Y):
        ####
        # draw table vertical lines
        ####
        line_y = pdf.get_y() - 3
        line_x = LEFT_PAGE_MARGIN
        first = True
        for column in columns:
            if first:
                pdf.line(line_x, START_TABLE_Y, line_x,
                         line_y)  # vertical line in front of table
                first = False

            line_x += column[0]
            pdf.line(line_x, START_TABLE_Y, line_x,
                     line_y)  # vertical line after column

    pdf.add_page()
    text_fonts()

    ####
    # Header: logo and company info
    ####
    pdf.image('{0}/gfx/logos/logo_jeslee.jpg'.format(settings.STATIC_ROOT),
              20.0,
              TOP_PAGE_MARGIN,
              link='',
              type='',
              w=79.5,
              h=33.0)
    pdf.set_xy(125.0, TOP_PAGE_MARGIN)
    # pdf.set_font('arial', 'B', 13.0)
    pdf.set_left_margin(125.0)
    pdf.write(
        6,
        '{company}\n{street}\n{zip} {city}\n{email}\n\nBank: {bank_account}\nKvk: {kvk}\nBTW: {btw}'
        .format(company=settings.COMPANY['business_name'],
                street=settings.COMPANY['street'],
                zip=settings.COMPANY['zip'],
                city=settings.COMPANY['city'],
                email=settings.COMPANY['email'],
                bank_account=settings.COMPANY['bank_account'],
                kvk=settings.COMPANY['kvk_nr'],
                btw=settings.COMPANY['btw_nr']))

    ####
    # Invoice data
    ####
    pdf.set_xy(LEFT_PAGE_MARGIN, 75)
    pdf.set_left_margin(LEFT_PAGE_MARGIN)
    pdf.set_font('DejaVu', 'B', 14.0)
    pdf.write(6, 'Factuur {number}'.format(number=order.number))
    text_fonts()
    pdf.set_xy(125, 75)
    pdf.write(
        6, 'Factuurdatum: {date}'.format(
            date=datetime.now().strftime("%d %B %Y")))
    pdf.set_xy(LEFT_PAGE_MARGIN, 85)
    pdf.write(
        5, 'Referentie: {reference}'.format(
            reference=order.invoice_line2 if order.
            invoice_line2 else 'Uw bestelling bij Jeslee'))
    pdf.set_xy(LEFT_PAGE_MARGIN, 100)
    name = order.invoice_company_name \
        if order.invoice_company_name \
        else "{0} {1}".format(order.invoice_firstname, order.invoice_lastname)
    address = order.invoice_line1
    pdf.write(
        6, '{name}\n{address}\n{zip} {city}'.format(name=name.strip(),
                                                    address=address.strip(),
                                                    zip=order.invoice_code,
                                                    city=order.invoice_city))

    ####
    # Article data
    ####
    pdf.set_font('DejaVu', '', 9.0)
    add_row_line()
    add_row(['Artikelcode', 'Omschrijving', '', 'Bedrag\nincl. btw', 'Totaal'])
    add_row_line()
    for item in order.items.all():
        code = ''
        description = ''
        if item.product:
            code = item.product.sku
            description = item.product.description if item.product.description else item.product.name
            str(item.product.tax if item.product.tax else '')
        add_row([
            code, description,
            '%.0f x' % item.product_amount,
            to_currency(item.product_price_gross),
            to_currency(item.price_gross)
        ])

    add_row_line()

    columns_below_items = [
        (COLUMN_1_WIDTH + COLUMN_2_WIDTH + COLUMN_3_WIDTH + COLUMN_4_WIDTH,
         'R'), (COLUMN_5_WIDTH, 'R')
    ]
    add_row(['Subtotaal', to_currency(order.price - order.tax)],
            columns=columns_below_items)
    taxes = tax_per_percentage(order)

    for tax_percentage, tax in taxes.iteritems():
        add_row(['Btw {0}%'.format(tax_percentage),
                 to_currency(tax)],
                columns=columns_below_items)
    add_row_line()
    pdf.set_font('DejaVu', 'B', 10.0)
    add_row(['Totaal', to_currency(order.price)], columns=columns_below_items)
    table_bottom = pdf.get_y() + 2
    pdf.line(LEFT_PAGE_MARGIN, table_bottom, RIGHT_PAGE_MARGIN,
             table_bottom)  # bottom horizontal line of table

    pdf.set_font('DejaVu', '', 10.0)
    pdf.set_y(pdf.get_y() + 10)
    check_for_new_page()
    pdf.set_x(LEFT_PAGE_MARGIN)
    pdf.multi_cell(w=COLUMN_1_WIDTH + COLUMN_2_WIDTH + COLUMN_3_WIDTH +
                   COLUMN_4_WIDTH + COLUMN_5_WIDTH,
                   h=TABLE_ROW_HIGH,
                   txt=order.message,
                   align='L',
                   border=0)

    pdf.set_font('DejaVu', '', 10.0)
    pdf.set_xy(LEFT_PAGE_MARGIN, PAGE_BOTTOM_Y - 14)
    pay_date = datetime.now() + timedelta(PAY_INVOICE_WITHIN_DAYS)
    pdf.write(
        4,
        'We verzoeken u vriendelijk het bovenstaande bedrag van {order_price} voor '
        '{pay_date} te voldoen op onze bankrekening onder vermelding van het '
        'factuurnummer {invoice_number}.\nVoor vragen kunt u contact opnemen per e­mail ({email}).'
        .format(order_price=to_currency(order.price),
                pay_date=pay_date.strftime("%d %B %Y"),
                invoice_number=order.number,
                email=settings.COMPANY['email']))

    pdf.set_draw_color(80)
    pdf.set_text_color(80)
    pdf.line(LEFT_PAGE_MARGIN, PAGE_BOTTOM_Y, RIGHT_PAGE_MARGIN,
             PAGE_BOTTOM_Y)  # bottom horizontal line
    pdf.set_xy(LEFT_PAGE_MARGIN + 5, PAGE_BOTTOM_Y + 2)
    if filename:
        pdf.output(filename, 'F')
    return pdf.output('invoice.pdf', 'S')
コード例 #20
0
ファイル: kut2fpdf.py プロジェクト: Miguel-J/pineboo
class Kut2FPDF(object):
    """
    Convert kuts to pyFPDF.
    """

    _document: "FPDF"

    _xml: Element
    _xml_data: Element
    _page_orientation: str
    _page_size: List[int]
    _bottom_margin: int
    _left_margin: int
    _right_margin: int
    _top_margin: int
    _page_top: Dict[int, int]
    _data_row: Element
    _parser_tools: "kparsertools.KParserTools"
    _avalible_fonts: List[str]
    _unavalible_fonts: List[str]
    design_mode: bool
    _actual_data_line: Optional[Element]
    _no_print_footer: bool
    _actual_section_size: int
    increase_section_size: int
    last_detail: bool
    actual_data_level: int
    last_data_processed: Element
    prev_level: int
    draws_at_header: Dict[str, str]
    detailn: Dict[str, int]
    name_: str
    _actual_append_page_no: int
    reset_page_count: bool

    def __init__(self) -> None:
        """Constructor."""

        check_dependencies({"fpdf": "fpdf2"})

        self._parser_tools = kparsertools.KParserTools()
        self._avalible_fonts = []
        self._page_top: Dict[int, int] = {}
        self._unavalible_fonts = []
        self.design_mode = config.value("ebcomportamiento/kugar_debug_mode",
                                        False)
        self._actual_data_line = None
        self._no_print_footer = False
        self.increase_section_size = 0
        self.actual_data_level = 0
        self.prev_level = -1
        self.draws_at_header = {}
        self.detailn = {}
        self.name_ = ""
        self._actual_append_page_no = -1
        self.reset_page_count = False
        self.new_page = False

    def parse(self,
              name: str,
              kut: str,
              data: str,
              report: "FPDF" = None,
              flags: List[int] = []) -> Optional[str]:
        """
        Parse string containing ".kut" file into a pdf and return its file path.

        @param name. Filename path for ".kut".
        @param kut. String with ".kut" file contents.
        @param data. String with data to be used in the report.
        @return Path to PDF file.
        """
        try:
            self._xml = self._parser_tools.loadKut(kut).getroot()
        except Exception:
            LOGGER.exception("KUT2FPDF: Problema al procesar %s.kut", name)
            return None
        try:
            self._xml_data = load2xml(data).getroot()
        except Exception:
            LOGGER.exception("KUT2FPDF: Problema al procesar xml_data")
            return None

        application.PROJECT.message_manager().send(
            "progress_dialog_manager", "create",
            ["Pineboo", len(self._xml_data), "kugar"])
        application.PROJECT.message_manager().send(
            "progress_dialog_manager", "setLabelText",
            ["Creando informe ...", "kugar"])

        self.name_ = name
        self.setPageFormat(self._xml)
        # self._page_orientation =
        # self._page_size =
        if report is None:
            from fpdf import FPDF  # type: ignore

            self._actual_append_page_no = 0
            self._document = FPDF(self._page_orientation, "pt",
                                  self._page_size)
            for font in self._document.core_fonts:
                LOGGER.debug("KUT2FPDF :: Adding font %s", font)
                self._avalible_fonts.append(font)
        else:
            self._document = report
        # Seteamos rutas a carpetas con tipos de letra ...

        if not hasattr(self._document, "set_stretching"):
            raise Exception("incorrect pyfpdf versión , you need <= 1.7.3")

        # Cargamos las fuentes disponibles
        next_page_break = (flags[2] == 1) if len(flags) == 3 else True
        page_append = (flags[1] == 1) if len(flags) > 1 else False
        page_display = (flags[0] == 1) if len(flags) > 0 else False

        if page_append:
            self.prev_level = -1
            self.last_detail = False

        page_break = False
        if self.new_page:
            page_break = True
            self.new_page = False

        if self.reset_page_count:
            self.reset_page_no()
            self.reset_page_count = False

        if self.design_mode:
            print("Append", page_append)
            print("Display", page_display)
            print("Page break", next_page_break)

        if next_page_break:
            self.reset_page_count = True

        if page_display:
            self.new_page = True

        self.processDetails(not page_break)

        # FIXME:Alguno valores no se encuentran
        for pages in self._document.pages.keys():
            page_content = self._document.pages[pages]["content"]
            for header in self.draws_at_header.keys():
                page_content = page_content.replace(
                    header, str(self.draws_at_header[header]))

            self._document.pages[pages]["content"] = page_content

        # print(self.draws_at_header.keys())
        self._document.set_title(self.name_)
        self._document.set_author("Pineboo - kut2fpdf plugin")

        return self._document

    def get_file_name(self) -> Optional[str]:
        """Retrieve file name where PDF should be saved."""
        import os

        pdf_name = application.PROJECT.tmpdir
        pdf_name += "/%s_%s.pdf" % (
            self.name_, datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
        if os.path.exists(pdf_name):
            os.remove(pdf_name)
        if self._document is not None:
            self._document.output(pdf_name, "F")
            return pdf_name
        else:
            return None

    def topSection(self) -> int:
        """
        Retrieve top section margin for current page to calculate object positions.

        @return Number with ceiling for current page.
        """
        return self._page_top[int(self._document.page_no())]

    def setTopSection(self, value: int) -> None:
        """
        Update top section for current page.

        Usually updated when processing a section.
        @param value. Number specifying new ceiling.
        """
        self._actual_section_size = value - self.topSection()
        self._page_top[int(self._document.page_no())] = value

    def newPage(self, data_level: int, add_on_header: bool = True) -> None:
        """
        Add a new page to the document.
        """
        self._document.add_page(self._page_orientation)
        self._page_top[int(self._document.page_no())] = self._top_margin
        self._document.set_margins(
            self._left_margin, self._top_margin,
            self._right_margin)  # Lo dejo pero no se nota nada
        self._no_print_footer = False
        if self.design_mode:
            self.draw_margins()

        self._actual_section_size = 0
        self._actual_append_page_no += 1
        if self.design_mode:
            print("Nueva página", self.number_pages())

        # l_ini = data_level
        # l_end = self.prev_level

        # if l_ini == l_end:
        #    l_end = l_end + 1

        # if l_ini <= l_end:
        #    for l in range(l_ini , l_end):
        #        print(l)
        #        self.processSection("AddOnHeader", str(l))
        pg_headers = self._xml.findall("PageHeader")

        for page_header in pg_headers:
            if self.number_pages() == 1 or page_header.get(
                    "PrintFrequency") == "1":
                ph_level = (page_header.get("Level")
                            if page_header.get("Level") is not None else None)
                self.processSection("PageHeader", int(ph_level or "0"))
                break

        if add_on_header and not self.number_pages() == 1:
            for level in range(data_level + 1):
                self.processSection("AddOnHeader", int(level))

        # Por ahora se omite detail header

    def processDetails(self, keep_page: Optional[bool] = None) -> None:
        """
        Process detail secions with their matching detailHeader and detailFooter.
        """
        # Procesamos la cabecera si procede ..

        top_level = 0
        level = 0
        first_page_created = (keep_page if keep_page is not None
                              and self._document.page_no() > 0 else False)

        rows_array = self._xml_data.findall("Row")
        i = 0

        for data in rows_array:
            self._actual_data_line = data
            level_str: Optional[str] = data.get("level")
            if level_str is None:
                level_str = "0"
            level = int(level_str)
            if level > top_level:
                top_level = level

            if not first_page_created:
                self.newPage(level)
                first_page_created = True

            if rows_array[len(rows_array) - 1] is data:
                self.last_detail = True

            if level < self.prev_level:
                for lev in range(level + 1, self.prev_level + 1):
                    self.processData("DetailFooter", self.last_data_processed,
                                     lev)

            if not str(level) in self.detailn.keys():
                self.detailn[str(level)] = 0
            else:
                self.detailn[str(level)] += 1

            if level > self.prev_level:
                self.processData("DetailHeader", data, level)

            self.processData("Detail", data, level)

            self.last_data_processed = data

            self.prev_level = level

            application.PROJECT.message_manager().send(
                "progress_dialog_manager", "setProgress", [i, "kugar"])
            i += 1

        if not self._no_print_footer and hasattr(self, "last_data_processed"):
            for lev in reversed(range(top_level + 1)):
                self.processData("DetailFooter", self.last_data_processed, lev)

        application.PROJECT.message_manager().send("progress_dialog_manager",
                                                   "destroy", ["kugar"])

    def processData(self, section_name: str, data: Element,
                    data_level: int) -> None:
        """
        Check if detailHeader + detail + detailFooter do fit in the remaining page and create a new page if not.

        @param section_name. Section name to check
        @param data. Data to check
        @param data_level. Section level
        """
        self.actual_data_level = data_level
        list_sections = self._xml.findall(section_name)
        # data_size = len(listDF)

        for section in list_sections:
            draw_if = section.get("DrawIf")
            show = True
            if draw_if:
                show = bool(data.get(draw_if))

            if section.get("Level") == str(data_level) and show not in (
                    "", "False", "None"):

                if section_name in ("DetailHeader", "Detail"):
                    height_calculated = (
                        self._parser_tools.getHeight(section) +
                        self.topSection() + self.increase_section_size)

                    if section_name == "DetailHeader":
                        for detail in self._xml.findall("Detail"):
                            if detail.get("Level") == str(data_level):
                                height_calculated += self._parser_tools.getHeight(
                                    detail)

                    for detail_footer in self._xml.findall("DetailFooter"):
                        if detail_footer.get("Level") == str(data_level):
                            height_calculated += self._parser_tools.getHeight(
                                detail_footer)

                    aof_size = 0
                    for add_footer in self._xml.findall("AddOnFooter"):
                        # if add_footer.get("Level") == str(data_level):
                        aof_size += self._parser_tools.getHeight(add_footer)
                        height_calculated += self._parser_tools.getHeight(
                            add_footer)

                    page_footer: Any = self._xml.get("PageFooter")
                    if isinstance(page_footer, Element):
                        if (self._document.page_no() == 1
                                or page_footer.get("PrintFrecuency") == "1"):
                            height_calculated += self._parser_tools.getHeight(
                                page_footer)

                    height_calculated += self._bottom_margin
                    if (height_calculated +
                            aof_size) > self._document.h:  # Si nos pasamos
                        self._no_print_footer = True
                        # Vemos el tope por abajo
                        limit_bottom = self._document.h - aof_size
                        actual_size = self._parser_tools.getHeight(
                            section) + self.topSection()

                        if (actual_size >= limit_bottom - 2
                            ) or self.last_detail:  # +2 se usa de margen extra
                            self.processSection("AddOnFooter", int(data_level))

                            self.newPage(data_level)

                self.processXML(section, data)

                if section.get("NewPage") == "true" and not self.last_detail:
                    self.newPage(data_level, False)

                break  # Se ejecuta una sola instancia

    def processSection(self, name: str, level: int = 0) -> None:
        """
        Process non-detail sections.

        @param name. Section name to process
        """
        sec_list = self._xml.findall(name)
        sec_ = None
        for section in sec_list:
            if section.get("Level") == str(
                    level) or section.get("Level") is None:
                sec_ = section
                break

        if sec_ is not None:
            if (sec_.get("PrintFrequency") == "1"
                    or self._document.page_no() == 1
                    or name in ("AddOnHeader", "AddOnFooter")):
                self.processXML(sec_)

    def processXML(self, xml: Element, data: Optional[Element] = None) -> None:
        """
        Process single XML element.

        @param xml: Element to process
        @param. data: Line affected
        """

        fix_height = True
        if data is None:
            data = self._actual_data_line

        if self.design_mode and data is not None:
            print("Procesando", xml.tag, data.get("level"))

        size_updated = False
        if xml.tag == "DetailFooter":
            if xml.get("PlaceAtBottom") == "true":
                height = self._parser_tools.getHeight(xml)
                self.setTopSection(self._document.h - height -
                                   self.increase_section_size)
                size_updated = True

        if xml.tag == "PageFooter":
            fix_height = False

        if not size_updated:
            self.fix_extra_size(
            )  # Sirve para actualizar la altura con lineas que se han partido porque son muy largas

        for label in xml.iter("Label"):
            self.processText(label, data, fix_height)

        for field in xml.iter("Field"):
            self.processText(field, data, fix_height)

        for special in xml.iter("Special"):
            self.processText(special, data, fix_height, xml.tag)

        for calculated in xml.iter("CalculatedField"):
            self.processText(calculated, data, fix_height, xml.tag)

        # Busco draw_at_header en DetailFooter y los meto también
        if xml.tag == "DetailHeader":
            detail_level = xml.get("Level")
            if detail_level is None:
                raise Exception("Level tag not found")
            for detail_footer in self._xml.iter("DetailFooter"):
                if detail_footer.get("Level") == detail_level:
                    for calculated_filed in detail_footer.iter(
                            "CalculatedField"):
                        if calculated_filed.get("DrawAtHeader") == "true":
                            header_name = "%s_header_%s_%s" % (
                                self.detailn[detail_level],
                                detail_level,
                                calculated_filed.get("Field"),
                            )
                            self.draws_at_header[header_name] = ""
                            self.processText(calculated_filed, data,
                                             fix_height, xml.tag)

        for line in xml.iter("Line"):
            self.processLine(line, fix_height)

        if not size_updated:
            self.setTopSection(self.topSection() +
                               self._parser_tools.getHeight(xml))

    def fix_extra_size(self) -> None:
        """Increase size of the section if needed."""
        if self.increase_section_size > 0:
            self.setTopSection(self.topSection() + self.increase_section_size)
            self.increase_section_size = 0

    def processLine(self, xml: Element, fix_height: bool = True) -> None:
        """
        Process single line.

        @param xml. Sección de xml a procesar.
        @param fix_height. Ajusta la altura a los .kut originales, excepto el pageFooter.
        """

        color = xml.get("Color")
        red = 0 if not color else int(color.split(",")[0])
        green = 0 if not color else int(color.split(",")[1])
        blue = 0 if not color else int(color.split(",")[2])

        style = int(xml.get("Style") or "0")
        width = int(xml.get("Width") or "0")
        pos_x1 = self.calculateLeftStart(xml.get("X1") or "0")
        pos_x1 = self.calculateWidth(pos_x1, 0, False)
        pos_x2 = self.calculateLeftStart(xml.get("X2") or "0")
        pos_x2 = self.calculateWidth(pos_x2, 0, False)

        # Ajustar altura a secciones ya creadas
        pos_y1 = int(xml.get("Y1") or "0") + self.topSection()
        pos_y2 = int(xml.get("Y2") or "0") + self.topSection()
        if fix_height:
            pos_y1 = self._parser_tools.ratio_correction_h(pos_y1)
            pos_y2 = self._parser_tools.ratio_correction_h(pos_y2)

        self._document.set_line_width(
            self._parser_tools.ratio_correction_h(width))
        self._document.set_draw_color(red, green, blue)
        dash_length = 1
        space_length = 1
        if style == 2:
            dash_length = 20
            space_length = 20
        elif style == 3:
            dash_length = 10
            space_length = 10

        self._document.dashed_line(pos_x1, pos_y1, pos_x2, pos_y2, dash_length,
                                   space_length)
        # else:
        #    self._document.line(X1, Y1, X2, Y2)

    def calculateLeftStart(self, position: Union[str, int, float]) -> int:
        """
        Check if left margin is exceeded for current page.

        @param position. Position to check.
        @return Revised position.
        """
        return self._parser_tools.ratio_correction_w(
            int(position)) + self._left_margin

    def calculateWidth(self,
                       width: int,
                       pos_x: int,
                       fix_ratio: bool = True) -> int:
        """
        Check if right margin is exceeded for current page.

        @param x. Position to check.
        @return Revised position.
        """
        limit = self._document.w - self._right_margin
        ret_: int

        if fix_ratio:
            width = self._parser_tools.ratio_correction_w(width)
            pos_x = self._parser_tools.ratio_correction_w(pos_x)
            ret_ = width
            if pos_x + width > limit:
                ret_ = limit - pos_x
        else:
            ret_ = width

        return ret_

    def processText(
        self,
        xml: Element,
        data_row: Optional[Element] = None,
        fix_height: bool = True,
        section_name: Optional[str] = None,
    ) -> None:
        """
        Check tag (calculated, label, special or image).

        @param xml. XML section to process.
        @param fix_height. Revise height from original .kut file except pageFooter.
        """
        is_image = False
        is_barcode = False
        text: str = xml.get("Text") or ""
        # borderColor = xml.get("BorderColor")
        field_name = xml.get("Field") or ""

        # x,y,W,H se calcula y corrigen aquí para luego estar correctos en los diferentes destinos posibles
        width = int(xml.get("Width") or "0")

        height = self._parser_tools.getHeight(xml)

        pos_x = int(xml.get("X") or "0")

        pos_y = (int(xml.get("Y") or "0") + self.topSection()
                 )  # Añade la altura que hay ocupada por otras secciones
        if fix_height:
            pos_y = self._parser_tools.ratio_correction_h(
                pos_y)  # Corrige la posición con respecto al kut original

        data_type = xml.get("DataType")

        if xml.tag == "Field" and data_row is not None:
            text = data_row.get(field_name) or ""

        elif xml.tag == "Special":
            if text == "":
                if xml.get("Type") == "1":
                    text = "PageNo"
            text = self._parser_tools.getSpecial(text,
                                                 self._actual_append_page_no)

        calculation_type = xml.get("CalculationType")

        if calculation_type is not None and xml.tag != "Field":
            if calculation_type == "6":
                function_name = xml.get("FunctionName")
                if function_name and data_row is not None:
                    try:
                        nodo = self._parser_tools.convertToNode(data_row)

                        ret_ = application.PROJECT.call(
                            function_name, [nodo, field_name], None, False)
                        if ret_ is False:
                            return
                        else:
                            text = str(ret_)

                    except Exception:
                        LOGGER.exception(
                            "KUT2FPDF:: Error llamando a function %s",
                            function_name)
                        return
                else:
                    return
            elif calculation_type == "1":
                text = self._parser_tools.calculate_sum(
                    field_name, self.last_data_processed, self._xml_data,
                    self.actual_data_level)

            elif calculation_type in ("5"):
                if data_row is None:
                    data_row = self._xml_data[0]

                text = data_row.get(field_name) or ""

        if data_type is not None:
            text = self._parser_tools.calculated(text, int(data_type),
                                                 xml.get("Precision"),
                                                 data_row)

        if data_type == "5":
            is_image = True

        elif data_type == "6":
            is_barcode = True

        if xml.get("BlankZero") == "1" and text is not None:
            res_ = re.findall(r"\d+", text)
            if res_ == ["0"]:
                return

        if text is not None:
            from pineboolib.core.settings import config

            temporal = config.value("ebcomportamiento/temp_dir")
            if text.startswith(temporal):
                is_image = True

        # negValueColor = xml.get("NegValueColor")
        # Currency = xml.get("Currency")
        #
        # commaSeparator = xml.get("CommaSeparator")
        # dateFormat = xml.get("DateFormat")

        if is_image:
            self.draw_image(pos_x, pos_y, width, height, xml, text)
        elif is_barcode:
            self.draw_barcode(pos_x, pos_y, width, height, xml, text)
        elif data_row is not None:
            level = data_row.get("level") or "0"
            if level and str(level) in self.detailn.keys():
                val = "%s_header_%s_%s" % (self.detailn[str(level)], level,
                                           field_name)

            if xml.get("DrawAtHeader") == "true" and level:
                if section_name == "DetailHeader":
                    val = ""
                    self.drawText(pos_x, pos_y, width, height, xml, val)

                    # print(level, section_name, val, text)

            if section_name == "DetailFooter" and xml.get(
                    "DrawAtHeader") == "true":
                self.draws_at_header[val] = text
                # print("Añadiendo a", val, text, level)

            else:
                self.drawText(pos_x, pos_y, width, height, xml, text)

    def drawText(self, pos_x: int, pos_y: int, width: int, height: int,
                 xml: Element, txt: str) -> None:
        """
        Draw a text field onto the page.

        @param pos_x. Label X Pos.
        @param pos_y. Label Y Pos.
        @param width. Label Width.
        @param height. Label Height.
        @param xml. Related XML Section
        @param txt. Computed text of the label to be created.
        """

        if txt in ("None", None):
            # return
            txt = ""

        if width == 0 and height == 0:
            return

        txt = self._parser_tools.restore_text(txt)

        resizeable = False

        if xml.get("ChangeHeight") == "1":
            resizeable = True

        # height_resized = False
        orig_x = pos_x
        orig_y = pos_y
        orig_w = width
        orig_h = height
        # Corregimos margenes:
        pos_x = self.calculateLeftStart(pos_x)
        width = self.calculateWidth(width, pos_x)

        # bg_color = xml.get("BackgroundColor").split(",")
        fg_color = self.get_color(xml.get("ForegroundColor") or "")
        self._document.set_text_color(fg_color[0], fg_color[1], fg_color[2])

        # self._document.set_draw_color(255, 255, 255)

        # if xml.get("BorderStyle") == "1":
        # FIXME: Hay que ajustar los margenes

        # font_name, font_size, font_style
        font_style = ""
        font_size = int(xml.get("FontSize") or "0")
        font_name_orig = ((xml.get("FontFamily") or "").lower() if
                          xml.get("FontFamily") is not None else "helvetica")
        font_name = font_name_orig

        font_w = int(xml.get("FontWeight") or "50")

        if font_w == 50:  # Normal
            font_w = 100
        elif font_w >= 65:
            font_style += "B"
            font_w = 100

        font_italic = xml.get("FontItalic")
        font_underlined = xml.get(
            "FontUnderlined")  # FIXME: hay que ver si es así

        # background_color = self.get_color(xml.get("BackgroundColor"))
        # if background_color != [255,255,255]: #Los textos que llevan fondo no blanco van en negrita
        #    font_style += "B"

        if font_italic == "1":
            font_style += "I"

        if font_underlined == "1":
            font_style += "U"

        font_name = font_name.replace(" narrow", "")

        font_full_name = "%s%s" % (font_name, font_style)

        if font_full_name not in self._avalible_fonts:
            font_found: Optional[str] = None
            if font_full_name not in self._unavalible_fonts:
                font_found = self._parser_tools.find_font(
                    font_full_name, font_style)
            if font_found:
                if self.design_mode:
                    LOGGER.warning(
                        "KUT2FPDF::Añadiendo el tipo de letra %s %s (%s)",
                        font_name,
                        font_style,
                        font_found,
                    )
                self._document.add_font(font_name, font_style, font_found,
                                        True)
                self._avalible_fonts.append(font_full_name)

            else:
                if font_full_name not in self._unavalible_fonts:
                    if self.design_mode:
                        LOGGER.warning(
                            "KUT2FPDF:: No se encuentra el tipo de letra %s. Sustituido por helvetica%s."
                            % (font_full_name, font_style))
                    self._unavalible_fonts.append(font_full_name)
                font_name = "helvetica"

        if font_name is not font_name_orig and font_name_orig.lower().find(
                "narrow") > -1:
            font_w = 85

        self._document.set_font(font_name, font_style, font_size)
        self._document.set_stretching(font_w)
        # Corregir alineación
        vertical_alignment = xml.get(
            "VAlignment")  # 0 izquierda, 1 centrado,2 derecha
        horizontal_alignment = xml.get("HAlignment")

        # layout_direction = xml.get("layoutDirection")

        start_section_size = self._actual_section_size
        result_section_size = 0
        # Miramos si el texto sobrepasa el ancho

        array_text: List[str] = []
        array_n = []
        text_lines = []
        if txt.find("\n") > -1:
            for line_text in txt.split("\n"):
                array_n.append(line_text)
        if array_n:  # Hay saltos de lineas ...
            for n in array_n:
                text_lines.append(n)
        else:  # No hay saltos de lineas
            text_lines.append(txt)

        for text_line in text_lines:
            if len(text_line) > 1:
                if text_line[0] == " " and text_line[1] != " ":
                    text_line = text_line[1:]
            str_width = self._document.get_string_width(text_line)
            if (
                    str_width > width - 10 and xml.tag != "Label"
                    and resizeable
            ):  # Una linea es mas larga que el ancho del campo(Le quito 10 al ancho maximo para que corte parecido a kugar original)
                # height_resized = True
                array_text = self.split_text(text_line, width - 10)
            else:

                array_text.append(text_line)

        # calculated_h = orig_h * len(array_text)
        self.drawRect(orig_x, orig_y, orig_w, orig_h, xml)

        processed_lines = 0
        extra_size = 0
        for actual_text in array_text:

            processed_lines += 1

            if processed_lines > 1:
                extra_size += font_size + 2

            if horizontal_alignment == "1":  # sobre X
                # Centrado
                pos_x = pos_x + (width / 2) - (
                    self._document.get_string_width(actual_text) / 2)
                # x = x + (width / 2) - (str_width if not height_resized else width / 2)
            elif horizontal_alignment == "2":

                # Derecha
                pos_x = (pos_x + width -
                         self._document.get_string_width(actual_text) - 2
                         )  # -2 de margen
                # x = x + width - str_width if not height_resized else width
            else:
                # Izquierda
                if processed_lines == 1:
                    pos_x = pos_x + 2

            if vertical_alignment == "1":  # sobre Y
                # Centrado
                # y = (y + ((H / 2) / processed_lines)) + (((self._document.font_size_pt / 2) / 2) * processed_lines)
                pos_y = int((orig_y + (orig_h / 2)) +
                            ((self._document.font_size_pt / 2) / 2))

                if len(array_text) > 1:
                    pos_y = pos_y - (font_size // 2)

            elif vertical_alignment == "2":
                # Abajo
                pos_y = orig_y + orig_h - font_size
            else:
                # Arriba
                pos_y = orig_y + font_size

            pos_y = pos_y + extra_size

            if self.design_mode:
                self.write_debug(
                    self.calculateLeftStart(orig_x),
                    pos_y,
                    "Hal:%s, Val:%s, T:%s st:%s" %
                    (horizontal_alignment, vertical_alignment, txt, font_w),
                    6,
                    "green",
                )
                if xml.tag == "CalculatedField":
                    self.write_debug(
                        self.calculateLeftStart(orig_x),
                        pos_y,
                        "CalculatedField:%s, Field:%s" %
                        (xml.get("FunctionName"), xml.get("Field")),
                        3,
                        "blue",
                    )

            self._document.text(pos_x, pos_y, actual_text)
            result_section_size += start_section_size

        result_section_size = result_section_size - start_section_size

        if (self.increase_section_size < extra_size
            ):  # Si algun incremento extra hay superior se respeta
            self.increase_section_size = extra_size

    def split_text(self, texto: str, limit_w: int) -> List[str]:
        """Split text into lines based on visual width."""
        list_ = []
        linea_: Optional[str] = None

        for parte in texto.split(" "):
            if linea_ is None and parte == "":
                continue

            if linea_ is not None:
                if self._document.get_string_width(linea_ + parte) > limit_w:
                    list_.append(linea_)
                    linea_ = ""
            else:
                linea_ = ""

            linea_ += "%s " % parte
        if linea_ is not None:
            list_.append(linea_)
        return list_

    def get_color(self, value: str) -> List[int]:
        """Convert color text into [r,g,b] array."""
        lvalue = value.split(",")
        red: int
        green: int
        blue: int
        if len(lvalue) == 3:
            red = int(lvalue[0])
            green = int(lvalue[1])
            blue = int(lvalue[2])
        else:
            red = int(value[0:2])
            green = int(value[3:5])
            blue = int(value[6:8])

        return [red, green, blue]

    def drawRect(self,
                 pos_x: int,
                 pos_y: int,
                 width: int,
                 height: int,
                 xml: Element = None) -> None:
        """
        Draw a rectangle in current page.

        @param pos_x. left side
        @param pos_y. top side
        @param width. width
        @param height. heigth
        """
        style_ = ""
        border_color = None
        bg_color = None
        line_width = self._document.line_width
        border_width = 0.2
        # Calculamos borde  y restamos del ancho
        orig_x = pos_x
        orig_y = pos_y
        orig_w = width

        pos_x = self.calculateLeftStart(orig_x)
        width = self.calculateWidth(width, pos_x)

        if xml is not None and not self.design_mode:
            if xml.get("BorderStyle") == "1":

                border_color = self.get_color(xml.get("BorderColor") or "")
                self._document.set_draw_color(border_color[0], border_color[1],
                                              border_color[2])
                style_ += "D"

            bg_color = self.get_color(xml.get("BackgroundColor") or "")
            self._document.set_fill_color(bg_color[0], bg_color[1],
                                          bg_color[2])
            style_ = "F" + style_

            border_width = int(
                xml.get("BorderWidth") or "0" if xml.get("BorderWidth"
                                                         ) else 0.2)
        else:
            self.write_cords_debug(pos_x, pos_y, width, height, orig_x, orig_w)
            style_ = "D"
            self._document.set_draw_color(0, 0, 0)

        if style_ != "":
            self._document.set_line_width(border_width)

            self._document.rect(pos_x, pos_y, width, height, style_)
            self._document.set_line_width(line_width)

            self._document.set_xy(orig_x, orig_y)
        # self._document.set_draw_color(255, 255, 255)
        # self._document.set_fill_color(0, 0, 0)

    def write_cords_debug(
        self,
        pos_x: Union[float, int],
        pos_y: Union[float, int],
        width: Union[float, int],
        height: Union[float, int],
        orig_x: Union[float, int],
        orig_w: Union[float, int],
    ) -> None:
        """Debug for Kut coordinated."""
        self.write_debug(
            int(pos_x),
            int(pos_y),
            "X:%s Y:%s W:%s H:%s orig_x:%s, orig_W:%s" % (
                round(pos_x, 2),
                round(pos_y, 2),
                round(width, 2),
                round(height, 2),
                round(orig_x, 2),
                round(orig_w, 2),
            ),
            2,
            "red",
        )

    def write_debug(self,
                    pos_x: int,
                    pos_y: int,
                    text: str,
                    height: int,
                    color: Optional[str] = None) -> None:
        """Write debug data into the report."""
        orig_color = self._document.text_color
        red = 0
        green = 0
        blue = 0
        current_font_family = self._document.font_family
        current_font_size = self._document.font_size_pt
        current_font_style = self._document.font_style
        if color == "red":
            red = 255
        elif color == "green":
            green = 255
        elif color == "blue":
            blue = 255

        self._document.set_text_color(red, green, blue)
        self._document.set_font_size(4)
        self._document.text(pos_x, pos_y + height, text)
        self._document.text_color = orig_color
        # self._document.set_xy(orig_x, orig_y)
        self._document.set_font(current_font_family, current_font_style,
                                current_font_size)

    def draw_image(self, pos_x: int, pos_y: int, width: int, height: int,
                   xml: Element, file_name: str) -> None:
        """
        Draw image onto current page.

        @param pos_x. left position
        @param pos_y. top position
        @param width. width
        @param height. heigth
        @param xml. Related XML section
        @param file_name. filename of temp data to use
        """
        import os

        if not file_name.lower().endswith(".png"):
            file_name_ = self._parser_tools.parseKey(file_name)
            if file_name_ is None:
                return
            file_name = file_name_

        if os.path.exists(file_name):
            pos_x = self.calculateLeftStart(pos_x)
            width = self.calculateWidth(width, pos_x)

            self._document.image(file_name, pos_x, pos_y, width, height, "PNG")

    def draw_barcode(self, pos_x: int, pos_y: int, width: int, height: int,
                     xml: Element, text: str) -> None:
        """
        Draw barcode onto currrent page.
        """
        if text == "None":
            return
        from pineboolib.fllegacy import flcodbar

        file_name = application.PROJECT.tmpdir
        file_name += "/%s.png" % (text)
        codbartype = xml.get("CodBarType")

        if not os.path.exists(file_name):

            bar_code = flcodbar.FLCodBar(text)  # Code128
            if codbartype is not None:
                type: int = bar_code.nameToType(codbartype.lower())
                bar_code.setType(type)

            bar_code.setText(text)

            pix = bar_code.pixmap()
            if not pix.isNull():
                pix.save(file_name, "PNG")

        self.draw_image(pos_x + 10, pos_y, width - 20, height, xml, file_name)

    def setPageFormat(self, xml: Element) -> None:
        """
        Define page parameters.

        @param xml: XML with KUT data
        """
        custom_size = None

        self._bottom_margin = int(xml.get("BottomMargin") or "0")
        self._left_margin = int(xml.get("LeftMargin") or "0")
        self._right_margin = int(xml.get("RightMargin") or "0")
        self._top_margin = int(xml.get("TopMargin") or "0")

        page_size = int(xml.get("PageSize") or "0")
        page_orientation = xml.get("PageOrientation") or "0"

        if page_size in [30, 31]:
            custom_size = [
                int(xml.get("CustomHeightMM") or "0"),
                int(xml.get("CustomWidthMM") or "0"),
            ]

        self._page_orientation = "P" if page_orientation == "0" else "L"
        self._page_size = self._parser_tools.convertPageSize(
            page_size, int(page_orientation), custom_size)  # devuelve un array

    def draw_margins(self) -> None:
        """Draw margins on the report."""
        self.draw_debug_line(0 + self._left_margin, 0, 0 + self._left_margin,
                             self._page_size[1])  # Vertical derecha
        self.draw_debug_line(
            self._page_size[0] - self._right_margin,
            0,
            self._page_size[0] - self._right_margin,
            self._page_size[1],
        )  # Vertical izquierda
        self.draw_debug_line(0, 0 + self._top_margin, self._page_size[0],
                             0 + self._top_margin)  # Horizontal superior
        self.draw_debug_line(
            0,
            self._page_size[1] - self._bottom_margin,
            self._page_size[0],
            self._page_size[1] - self._bottom_margin,
        )  # Horizontal inferior

    def draw_debug_line(
        self,
        pos_x1: int,
        pos_y1: int,
        pos_x2: int,
        pos_y2: int,
        title: Optional[str] = None,
        color: str = "GREY",
    ) -> None:
        """Draw a debug line on the report."""
        dash_length = 2
        space_length = 2

        red = 0
        green = 0
        blue = 0
        if color == "GREY":
            red = 220
            green = 220
            blue = 220
        self._document.set_line_width(1)
        self._document.set_draw_color(red, green, blue)
        self._document.dashed_line(pos_x1, pos_y1, pos_x2, pos_y1, dash_length,
                                   space_length)

    def number_pages(self) -> int:
        """Get number of pages on the report."""
        return self._actual_append_page_no if self._actual_append_page_no > 0 else 0

    def reset_page_no(self) -> None:
        """Reset page number."""
        self._actual_append_page_no = -1
コード例 #21
0
ファイル: pdf.py プロジェクト: ceasaro/jeslee
def invoice_to_PDF(order, client, filename=None):
    """
    creates and pdf invoice of the order, and returns the pfd as a string buffer.
    @order: the order used to create a invoice
    @client: if specified this data is used for the client address, otherwise the address is taken from the order
    @filename: if specified the pdf is written to disk with the given filename,
               filename can contains a complete path
    """
    pdf = FPDF()
    pdf.add_font('DejaVu', '', fname='{0}/fonts/ttf-dejavu/DejaVuSerif.ttf'.format(settings.STATIC_ROOT), uni=True)
    pdf.add_font('DejaVu', 'B', fname='{0}/fonts/ttf-dejavu/DejaVuSerif-Bold.ttf'.format(settings.STATIC_ROOT), uni=True)
    pdf.add_font('DejaVu', 'I', fname='{0}/fonts/ttf-dejavu/DejaVuSerif-Italic.ttf'.format(settings.STATIC_ROOT), uni=True)
    pdf.add_font('DejaVu', 'BI', fname='{0}/fonts/ttf-dejavu/DejaVuSerif-BoldItalic.ttf'.format(settings.STATIC_ROOT), uni=True)

    def check_for_new_page():
        if pdf.get_y() > PAGE_BOTTOM_Y:
            pdf.add_page()

    def text_fonts():
        pdf.set_font('DejaVu', '', 11.0)

    def add_row(column_data, columns=COLUMNS, border=0):
        """
        add a row to the pdf
        """
        cell_margin_left = 2
        cell_margin_right = 2

        check_for_new_page()
        last_y = START_TABLE_Y if pdf.page_no() == 1 else TOP_PAGE_MARGIN
        row_y = pdf.get_y() if pdf.get_y() > last_y else last_y
        max_y = row_y  # max_y is used to check if multi cell is wrapping text and so uses more rows.

        next_x = LEFT_PAGE_MARGIN
        for i, column in enumerate(columns):
            width = column[0]
            align = column[1]
            pdf.set_xy(next_x+cell_margin_left, row_y)
            pdf.multi_cell(w=width-cell_margin_right,
                           h=TABLE_ROW_HIGH,
                           txt=column_data[i] if len(column_data) > i else '', align=align, border=border)
            max_y = max(max_y, pdf.get_y())  # keep track if multi cell wrapped text
            next_x += width

        pdf.set_y(max_y)

    def add_row_line():
        last_y = START_TABLE_Y if pdf.page_no() == 1 else TOP_PAGE_MARGIN
        line_y = pdf.get_y() if pdf.get_y() > last_y else last_y - 2
        pdf.set_xy(LEFT_PAGE_MARGIN + 1, line_y)
        pdf.line(LEFT_PAGE_MARGIN, line_y+2, RIGHT_PAGE_MARGIN, line_y+2)
        pdf.set_y(line_y+5)

    def draw_vertical_lines_around_columns(columns = COLUMNS, top_y=TOP_PAGE_MARGIN, bottom_y=PAGE_BOTTOM_Y):
        ####
        # draw table vertical lines
        ####
        line_y = pdf.get_y() - 3
        line_x = LEFT_PAGE_MARGIN
        first = True
        for column in columns:
            if first:
                pdf.line(line_x, START_TABLE_Y, line_x, line_y)  # vertical line in front of table
                first = False

            line_x += column[0]
            pdf.line(line_x, START_TABLE_Y, line_x, line_y)  # vertical line after column

    pdf.add_page()
    text_fonts()

    ####
    # Header: logo and company info
    ####
    pdf.image('{0}/gfx/logos/logo_jeslee.jpg'.format(settings.STATIC_ROOT), 20.0, TOP_PAGE_MARGIN, link='', type='', w=79.5, h=33.0)
    pdf.set_xy(125.0, TOP_PAGE_MARGIN)
    # pdf.set_font('arial', 'B', 13.0)
    pdf.set_left_margin(125.0)
    pdf.write(6, '{company}\n{street}\n{zip} {city}\n{email}\n\nBank: {bank_account}\nKvk: {kvk}\nBTW: {btw}'.format(
        company=settings.COMPANY['business_name'],
        street=settings.COMPANY['street'], zip=settings.COMPANY['zip'], city=settings.COMPANY['city'],
        email=settings.COMPANY['email'], bank_account=settings.COMPANY['bank_account'], kvk=settings.COMPANY['kvk_nr'], btw=settings.COMPANY['btw_nr']
    ))

    ####
    # Invoice data
    ####
    pdf.set_xy(LEFT_PAGE_MARGIN, 75)
    pdf.set_left_margin(LEFT_PAGE_MARGIN)
    pdf.set_font('DejaVu', 'B', 14.0)
    pdf.write(6, 'Factuur {number}'.format(number=order.number))
    text_fonts()
    pdf.set_xy(125, 75)
    pdf.write(6, 'Factuurdatum: {date}'.format(date=datetime.now().strftime("%d %B %Y")))
    pdf.set_xy(LEFT_PAGE_MARGIN, 85)
    pdf.write(5, 'Referentie: {reference}'.format(reference=order.invoice_line2 if order.invoice_line2 else 'Uw bestelling bij Jeslee'))
    pdf.set_xy(LEFT_PAGE_MARGIN, 100)
    name = order.invoice_company_name \
        if order.invoice_company_name \
        else "{0} {1}".format(order.invoice_firstname, order.invoice_lastname)
    address = order.invoice_line1
    pdf.write(6, '{name}\n{address}\n{zip} {city}'.format(
        name=name.strip(), address=address.strip(),
        zip=order.invoice_code, city=order.invoice_city
    ))


    ####
    # Article data
    ####
    pdf.set_font('DejaVu', '', 9.0)
    add_row_line()
    add_row(['Artikelcode', 'Omschrijving', '', 'Bedrag\nincl. btw', 'Totaal'])
    add_row_line()
    for item in order.items.all():
        code = ''
        description = ''
        if item.product:
            code = item.product.sku
            description = item.product.description if item.product.description else item.product.name
            str(item.product.tax if item.product.tax else '')
        add_row([code,
                 description,
                 '%.0f x' % item.product_amount,
                 to_currency(item.product_price_gross),
                 to_currency(item.price_gross)])

    add_row_line()

    columns_below_items = [(COLUMN_1_WIDTH + COLUMN_2_WIDTH + COLUMN_3_WIDTH+COLUMN_4_WIDTH , 'R'),
                           (COLUMN_5_WIDTH, 'R')]
    add_row(['Subtotaal', to_currency(order.price-order.tax)], columns=columns_below_items)
    taxes = tax_per_percentage(order)

    for tax_percentage, tax in taxes.iteritems():
        add_row(['Btw {0}%'.format(tax_percentage), to_currency(tax)], columns=columns_below_items)
    add_row_line()
    pdf.set_font('DejaVu', 'B', 10.0)
    add_row(['Totaal', to_currency(order.price)], columns=columns_below_items)
    table_bottom = pdf.get_y() + 2
    pdf.line(LEFT_PAGE_MARGIN, table_bottom, RIGHT_PAGE_MARGIN, table_bottom)  # bottom horizontal line of table

    pdf.set_font('DejaVu', '', 10.0)
    pdf.set_y(pdf.get_y() + 10)
    check_for_new_page()
    pdf.set_x(LEFT_PAGE_MARGIN)
    pdf.multi_cell(w=COLUMN_1_WIDTH + COLUMN_2_WIDTH + COLUMN_3_WIDTH+COLUMN_4_WIDTH+COLUMN_5_WIDTH,
                   h=TABLE_ROW_HIGH,
                   txt=order.message, align='L', border=0)




    pdf.set_font('DejaVu', '', 10.0)
    pdf.set_xy(LEFT_PAGE_MARGIN, PAGE_BOTTOM_Y-14)
    pay_date = datetime.now() + timedelta(PAY_INVOICE_WITHIN_DAYS)
    pdf.write(4, 'We verzoeken u vriendelijk het bovenstaande bedrag van {order_price} voor '
                 '{pay_date} te voldoen op onze bankrekening onder vermelding van het '
                 'factuurnummer {invoice_number}.\nVoor vragen kunt u contact opnemen per e­mail ({email}).'.
              format(order_price=to_currency(order.price),
                     pay_date=pay_date.strftime("%d %B %Y"),
                     invoice_number=order.number,
                     email=settings.COMPANY['email']))

    pdf.set_draw_color(80)
    pdf.set_text_color(80)
    pdf.line(LEFT_PAGE_MARGIN, PAGE_BOTTOM_Y, RIGHT_PAGE_MARGIN, PAGE_BOTTOM_Y)  # bottom horizontal line
    pdf.set_xy(LEFT_PAGE_MARGIN+5, PAGE_BOTTOM_Y+2)
    if filename:
        pdf.output(filename, 'F')
    return pdf.output('invoice.pdf', 'S')
コード例 #22
0
from fpdf import FPDF

pdf = FPDF('P', 'mm', 'A4')

pdf.add_page()

carros = [['HB20 1.0 Comfort Plus', 'Hyundai', '2014', 'R$ 33590,00'],
          ['2008 Allure 1.6 16v', 'Peugeot', '2018', 'R$ 71990,00'],
          ['Argo Drive 1.0 Firefly', 'Fiat', '2018', 'R$ 44999,00'],
          ['Corolla Sedan SEG 1.8 16v', 'Toyota', '2010', 'R$ 44900,00'],
          ['KA 1.0 SE', 'Ford', '2017', 'R$ 34900,00'],
          ['Fox 1.0 VHT', 'Volkswagen', '2013', 'R$ 26990,00']]

pdf.set_font('Arial', 'B', 16)

pdf.set_draw_color(51, 51, 255)
pdf.set_fill_color(51, 51, 255)
pdf.set_text_color(204, 255, 255)
pdf.cell(0, 20, 'Loja - Carro de qualidade', 1, 1, 'C', 1)

pdf.set_draw_color(255, 204, 51)
pdf.set_fill_color(255, 204, 51)
pdf.set_text_color(0, 0, 0)
sub = '{:<26} {:<16} {:<5} {:<9}'.format('Carro', 'Marca', 'Ano', 'Valor')
pdf.set_font('courier', 'B', 15)
pdf.cell(0, 20, sub, 1, 1, 'R', 1)

pdf.set_font('courier', '', 12)

i = 0
for carro in carros:
コード例 #23
0
def generate(transcripts, classID):
    pdf = FPDF()

    inp = []
    if transcripts:
        inp = transcripts

    pdf.add_page()
    # image_count = 1
    now = datetime.now()
    class_datestamp = f'Class Notes\n{classID} {now.strftime("%d/%m/%Y")}'
    pdf.set_font("Helvetica", size=normal_size)
    pdf.set_text_color(255)
    pdf.set_fill_color(r=13, g=40, b=76)
    pdf.multi_cell(w=0,
                   txt=class_datestamp,
                   align="C",
                   h=normal_size / 2,
                   fill=True)
    pdf.set_text_color(0)
    pdf.ln(h=padding)

    pdf.set_draw_color(r=13, g=40, b=76)

    for line in inp:
        line = line.strip()
        if isImportant(line, next_topic):
            pdf.ln(h=padding)
            pdf.set_text_color(255)
            pdf.set_font("Helvetica", style="B", size=topic_size)
            pdf.set_fill_color(r=13, g=40, b=76)
            pdf.multi_cell(
                txt=getImportantWord(line),
                w=pdf.get_string_width(getImportantWord(line) + " "),
                align="L",
                fill=True,
                h=topic_size / 2,
            )
            pdf.set_text_color(0)
            pdf.ln(h=padding)
        elif isImportant(line, next_subtopic):
            pdf.ln(h=padding)
            pdf.set_text_color(r=13, g=40, b=76)
            pdf.set_font("Helvetica", style="B", size=subtopic_size)
            pdf.multi_cell(
                txt=getImportantWord(line),
                w=pdf.get_string_width(getImportantWord(line)) + 10,
                align="L",
                h=subtopic_size / 2,
            )
            pdf.set_text_color(0)
            pdf.ln(h=padding)
        elif isImportant(line, image_trigger):
            pdf.set_font('Helvetica', size=topic_size)
            pdf.set_fill_color(167, 197, 238)
            pdf.set_text_color(r=13, g=40, b=76)
            pdf.multi_cell(txt="\nScreenshot here.\n\n",
                           w=0,
                           h=topic_size / 2,
                           align="C",
                           fill=True,
                           border=1)
            pdf.cell(txt='', ln=1, w=0)
            pdf.set_text_color(0)
            pdf.ln(h=padding)
            # image_path = os.path.join(
            #     "data", classID.lower(), f"image_{image_count}.png"
            # )
            # if os.path.exists(image_path):
            #     pdf.ln(h=padding)
            #     pdf.image(image_path, w=190)
            #     pdf.ln(h=padding)
            #     image_count += 1
        else:
            pdf.set_font("Helvetica", size=normal_size)
            pdf.multi_cell(txt=line, w=0, h=normal_size / 2)
            pdf.cell(txt="", ln=1, w=0)

    return pdf.output(dest="S").encode("latin-1")
コード例 #24
0
    def printwork(self):
        pdf = FPDF(orientation='P', unit='mm', format='A4')
        pdf.set_auto_page_break(auto=True, margin=10)
        pdf.add_page()
        pdf.set_image_filter("DCTDecode")

        # heading -> Personal Information
        pdf.set_font("times", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190,
                 5,
                 txt="Personal Information",
                 border=1,
                 ln=1,
                 align="L",
                 fill=1)

        # content -> personal Information
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", 'B', size=11)
        pdf.cell(30, 6, txt="ID:", ln=0, align="L")
        pdf.set_font("Helvetica", '', size=11)
        pdf.cell(50, 6, txt=str(self.id), ln=0, align='L')
        pdf.set_font("Helvetica", 'B', size=11)
        pdf.cell(30, 6, txt="Name:", ln=0, align="L")
        pdf.set_font("Helvetica", '', size=11)
        pdf.cell(50, 6, txt=self.name, ln=1, align='L')
        pdf.set_font("Helvetica", 'B', size=11)
        pdf.cell(30, 5, txt="Gender:", ln=0, align="L")
        pdf.set_font("Helvetica", '', size=11)
        pdf.cell(50, 5, txt=self.sex, ln=0, align='L')
        pdf.set_font("Helvetica", 'B', size=11)
        pdf.cell(30, 5, txt="Age:", ln=0, align="L")
        pdf.set_font("Helvetica", '', size=11)
        pdf.cell(50, 5, txt=self.age, ln=1, align='L')
        pdf.set_font("Helvetica", 'B', size=11)
        pdf.cell(30, 5, txt="Address:", ln=0, align="L")
        pdf.set_font("Helvetica", '', size=11)
        pdf.cell(50, 5, txt=self.address, ln=1, align='L')
        pdf.cell(190, 2, "", ln=1)

        # Heading -> C/C
        pdf.set_font("Helvetica", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190, 5, txt="C/C", border=1, ln=1, align="L", fill=1)

        # content -> c/c
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", '', size=11)
        pdf.multi_cell(190, 5, txt=self.cc, ln=0, align="L")
        pdf.cell(190, 2, "", ln=1)

        # Heading -> OE
        pdf.set_font("Helvetica", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190, 5, txt="O.E.", border=1, ln=1, align="L", fill=1)

        # content -> OE
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", '', size=11)
        pdf.multi_cell(190, 5, txt=self.oe, ln=0, align="L")
        pdf.cell(190, 2, "", ln=1)

        # Heading -> rf
        pdf.set_font("Helvetica", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190, 5, txt="R.F.", border=1, ln=1, align="L", fill=1)

        # content -> rf
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", '', size=11)
        pdf.multi_cell(190, 5, txt=self.rf, ln=0, align="L")
        pdf.cell(190, 2, "", ln=1)

        # Heading -> path report
        pdf.set_font("Helvetica", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190,
                 5,
                 txt="Pathology Reoprt",
                 border=1,
                 ln=1,
                 align="L",
                 fill=1)

        # content -> path report
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", '', size=11)
        pdf.multi_cell(190, 5, txt=self.pathreport, ln=0, align="L")
        pdf.cell(190, 2, "", ln=1)

        # Heading -> Dxs
        pdf.set_font("Helvetica", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190, 5, txt="Dxs", border=1, ln=1, align="L", fill=1)

        # content -> Dxs
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", '', size=11)
        pdf.multi_cell(190, 5, txt=self.dxs, ln=0, align="L")
        pdf.cell(190, 2, "", ln=1)

        # Heading -> comment
        pdf.set_font("Helvetica", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190, 5, txt="Comment", border=1, ln=1, align="L", fill=1)

        # content -> comment
        pdf.set_text_color(0, 0, 0)
        pdf.set_font("Helvetica", '', size=11)
        pdf.multi_cell(190, 5, txt=self.comments, ln=0, align="L")
        pdf.cell(190, 2, "", ln=1)
        curr_pos = pdf.get_y()
        if curr_pos + 55 > 275:
            pdf.add_page()
        pdf.set_font("times", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190,
                 5,
                 txt="Images of Rediology",
                 border=1,
                 ln=1,
                 align="L",
                 fill=1)
        pdf.cell(190, 1, "", ln=1)
        i = 1
        j = 0
        while 2 * i <= len(self.rediology) + len(self.rediology) % 2:
            yy = pdf.get_y()
            if j + 1 <= len(self.rediology):
                pdf.image(self.path + self.rediology[j], x=10, h=50, w=90)
                j += 1
            if j + 1 <= len(self.rediology):
                pdf.image(self.path + self.rediology[j],
                          x=110,
                          y=yy,
                          h=50,
                          w=90)
                j += 1
            pdf.ln(2)
            i += 1
            curr_pos = pdf.get_y()
            if curr_pos + 55 > 275:
                pdf.add_page()

        pdf.set_font("times", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190,
                 5,
                 txt="Images of MRI",
                 border=1,
                 ln=1,
                 align="L",
                 fill=1)
        pdf.cell(190, 1, "", ln=1)
        i = 1
        j = 0
        while 2 * i <= len(self.mri) + len(self.mri) % 2:
            yy = pdf.get_y()
            if j + 1 <= len(self.mri):
                pdf.image(self.path + self.mri[j], x=10, h=50, w=90)
                j += 1
            if j + 1 <= len(self.mri):
                pdf.image(self.path + self.mri[j], x=110, y=yy, h=50, w=90)
                j += 1
            pdf.ln(2)
            i += 1
            curr_pos = pdf.get_y()
            if curr_pos + 55 > 275:
                pdf.add_page()

        pdf.set_font("times", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190,
                 5,
                 txt="Images of X-Ray",
                 border=1,
                 ln=1,
                 align="L",
                 fill=1)
        pdf.cell(190, 1, "", ln=1)
        i = 1
        j = 0
        while 2 * i <= len(self.xray) + len(self.xray) % 2:
            yy = pdf.get_y()
            if j + 1 <= len(self.xray):
                pdf.image(self.path + self.xray[j], x=10, h=50, w=90)
                j += 1
            if j + 1 <= len(self.xray):
                pdf.image(self.path + self.xray[j], x=110, y=yy, h=50, w=90)
                j += 1
            pdf.ln(2)
            i += 1
            curr_pos = pdf.get_y()
            if curr_pos + 55 > 275:
                pdf.add_page()

        pdf.set_font("times", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190,
                 5,
                 txt="Images of CT Scan",
                 border=1,
                 ln=1,
                 align="L",
                 fill=1)
        pdf.cell(190, 1, "", ln=1)
        i = 1
        j = 0
        while 2 * i <= len(self.ctscan) + len(self.ctscan) % 2:
            yy = pdf.get_y()
            if j + 1 <= len(self.ctscan):
                pdf.image(self.path + self.ctscan[j], x=10, h=50, w=90)
                j += 1
            if j + 1 <= len(self.ctscan):
                pdf.image(self.path + self.ctscan[j], x=110, y=yy, h=50, w=90)
                j += 1
            pdf.ln(2)
            i += 1
            curr_pos = pdf.get_y()
            if curr_pos + 55 > 275:
                pdf.add_page()

        pdf.set_font("times", 'B', size=12)
        pdf.set_draw_color(0, 0, 0)
        pdf.set_fill_color(0, 0, 0)
        pdf.set_text_color(255, 255, 255)
        pdf.set_line_width(1)
        pdf.cell(190, 5, txt="Other Images", border=1, ln=1, align="L", fill=1)
        pdf.cell(190, 1, "", ln=1)
        i = 1
        j = 0
        while 2 * i <= len(self.pics) + len(self.pics) % 2:
            yy = pdf.get_y()
            if j + 1 <= len(self.pics):
                pdf.image(self.path + self.pics[j], x=10, h=50, w=90)
                j += 1
            if j + 1 <= len(self.pics):
                pdf.image(self.path + self.pics[j], x=110, y=yy, h=50, w=90)
                j += 1
            pdf.ln(2)
            i += 1
            curr_pos = pdf.get_y()
            if curr_pos + 55 > 275:
                pdf.add_page()

        destination = QFileDialog.getExistingDirectory()
        pdf.output(destination + "/" + str(self.id) + ".pdf")
コード例 #25
0
ファイル: asciifier.py プロジェクト: xldrkp/asciifier
    def to_pdf(self, **kwargs):
        from fpdf import FPDF
        import random
        paper_format = kwargs.get('paper_format', 'a4')
        paper = self.PAPER_SIZES[string.lower(paper_format)]
        font_scale = kwargs.get('font_scale', 1)
        font_name = kwargs.get('font_name')
        colorize = kwargs.get('colorize', False)
        if font_name is not None and not colorize:
            self.generate_luminosity_mapping(font_name)
        orientation = kwargs.get('orientation')
        if self.im.width > self.im.height:
            orientation = 'l'
        else:
            orientation = 'p'
        if orientation == 'l':
            paper.width, paper.height = paper.height, paper.width

        inner = Size(ceil(paper.width - self.margins.left - self.margins.right),
                     ceil(paper.height - self.margins.top - self.margins.bottom))
        imgpixels = Size(self.im.width, self.im.height)
        scale = min(inner.width, inner.height) / max(imgpixels.width, imgpixels.height)
        offset = Point(self.margins.left + (inner.width - imgpixels.width * scale) / 2,
                       self.margins.bottom + (inner.height - imgpixels.height * scale) / 2)

        pdf = FPDF(unit='mm', format=paper_format.upper(), orientation=orientation.upper())
        pdf.set_compression(True)
        pdf.set_title('ASCII Art')
        pdf.set_author('Oliver Lau <*****@*****.**> - Heise Medien GmbH & Co. KG')
        pdf.set_creator('asciifier')
        pdf.set_keywords('retro computing art fun')
        pdf.add_page()

        if font_name is not None:
            pdf.add_font(font_name, fname=font_name, uni=True)
        else:
            font_name = 'Courier'
        pdf.set_font(font_name, '', mm2pt(scale * font_scale))

        for y in range(0, self.im.height):
            yy = offset.y + scale * y
            for x in range(0, self.im.width):
                c = self.result[x][y]
                if c != ' ':
                    if colorize is True:
                        r, g, b = self.im.getpixel((x, y))
                        pdf.set_text_color(r, g, b)
                        pdf.text(offset.x + x * scale, yy, random.choice(Asciifier.COLOR_CHARS))
                    else:
                        pdf.text(offset.x + x * scale, yy, c)

        crop_area = Margin(offset.y - scale,
                           offset.x + (self.im.width - 1 + font_scale) * scale,
                           offset.y + (self.im.height - 2 + font_scale) * scale,
                           offset.x)

        if kwargs.get('cropmarks', False):
            pdf.set_draw_color(0, 0, 0)
            pdf.set_line_width(pt2mm(0.1))
            for p in [Point(crop_area.left, crop_area.top),
                      Point(crop_area.right, crop_area.top),
                      Point(crop_area.right, crop_area.bottom),
                      Point(crop_area.left, crop_area.bottom)]:
                pdf.line(p.x - 6, p.y, p.x - 2, p.y)
                pdf.line(p.x + 2, p.y, p.x + 6, p.y)
                pdf.line(p.x, p.y - 6, p.x, p.y - 2)
                pdf.line(p.x, p.y + 2, p.x, p.y + 6)

        if kwargs.get('logo'):
            logo_width = 20
            pdf.image(kwargs.get('logo'),
                      x=(crop_area.right - crop_area.left - logo_width / 2) / 2,
                      y=crop_area.bottom + 10,
                      w=logo_width)

        return pdf.output(dest='S')
コード例 #26
0
# .cell(w,h,string,border,ln)
pdf.cell(190, 10, 'Hello, World!', 1, 1, 'C')

pdf.set_font("Courier", '', 14)
pdf.cell(190, 10, 'printf("Hello, World!");', 'LTR', 1, 'C')

# .set_text_color(r,g=-1,b=-1)
pdf.set_text_color(198, 21, 21)
pdf.cell(190, 10, 'print("Hello, World!")', "LRB", 1, 'C')

pdf.set_font('Arial', 'B', 16)
pdf.set_text_color(0)
pdf.cell(95, 10, "C", 1, 0, 'C')

# .set_draw_color(r,g,b)
pdf.set_draw_color(198, 21, 21)
# .set_line_width(size) in user defined unit
pdf.set_line_width(1)
pdf.cell(95, 10, "Python", 1, 0, 'C')

pdf.set_draw_color(4, 139, 63)
pdf.set_fill_color(4, 139, 63)
pdf.line(10, 50, 200, 10)

pdf.set_draw_color(4, 139, 63)
pdf.set_fill_color(4, 139, 63)
# .rect(x,y,w,h,style)
pdf.rect(10, 60, 190, 10, 'DF')

pdf.set_draw_color(198, 21, 21)
pdf.set_fill_color(198, 21, 21)
コード例 #27
0
class Report:
    def __init__(self, data):
        self.output_path = os.path.join(data.data_path, "stats")
        self.dataset_name = os.path.basename(data.data_path)

        self.mapi_light_light_green = [210, 245, 226]
        self.mapi_light_green = [5, 203, 99]
        self.mapi_light_grey = [218, 222, 228]
        self.mapi_dark_grey = [99, 115, 129]

        self.pdf = FPDF("P", "mm", "A4")
        self.pdf.add_page()

        self.title_size = 20
        self.h1 = 16
        self.h2 = 13
        self.h3 = 10
        self.text = 10
        self.small_text = 8
        self.margin = 10
        self.cell_height = 7
        self.total_size = 190

        self.stats = self._read_stats_file("stats.json")

    def save_report(self, filename):
        self.pdf.output(os.path.join(self.output_path, filename), "F")

    def _make_table(self, columns_names, rows, row_header=False):
        self.pdf.set_font("Helvetica", "", self.h3)
        self.pdf.set_line_width(0.3)

        columns_sizes = [int(self.total_size / len(rows[0]))] * len(rows[0])

        if columns_names:
            self.pdf.set_draw_color(*self.mapi_light_grey)
            self.pdf.set_fill_color(*self.mapi_light_grey)
            for col, size in zip(columns_names, columns_sizes):
                self.pdf.rect(
                    self.pdf.get_x(),
                    self.pdf.get_y(),
                    size,
                    self.cell_height,
                    style="FD",
                )
                self.pdf.set_text_color(*self.mapi_dark_grey)
                self.pdf.cell(size, self.cell_height, col, align="L")
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.cell_height)

        self.pdf.set_draw_color(*self.mapi_light_grey)
        self.pdf.set_fill_color(*self.mapi_light_light_green)

        for row in rows:
            for i, (col, size) in enumerate(zip(row, columns_sizes)):
                if i == 0 and row_header:
                    self.pdf.set_draw_color(*self.mapi_light_grey)
                    self.pdf.set_fill_color(*self.mapi_light_grey)
                self.pdf.rect(
                    self.pdf.get_x(),
                    self.pdf.get_y(),
                    size,
                    self.cell_height,
                    style="FD",
                )
                self.pdf.set_text_color(*self.mapi_dark_grey)
                if i == 0 and row_header:
                    self.pdf.set_draw_color(*self.mapi_light_grey)
                    self.pdf.set_fill_color(*self.mapi_light_light_green)
                self.pdf.cell(size, self.cell_height, col, align="L")
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.cell_height)

    def _read_stats_file(self, filename):
        file_path = os.path.join(self.output_path, filename)
        with io.open_rt(file_path) as fin:
            return io.json_load(fin)

    def _make_section(self, title):
        self.pdf.set_font("Helvetica", "B", self.h1)
        self.pdf.set_text_color(*self.mapi_dark_grey)
        self.pdf.cell(0, self.margin, title, align="L")
        self.pdf.set_xy(self.margin, self.pdf.get_y() + 1.5 * self.margin)

    def _make_subsection(self, title):
        self.pdf.set_xy(self.margin, self.pdf.get_y() - 0.5 * self.margin)
        self.pdf.set_font("Helvetica", "B", self.h2)
        self.pdf.set_text_color(*self.mapi_dark_grey)
        self.pdf.cell(0, self.margin, title, align="L")
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def _make_centered_image(self, image_path, desired_height):
        width, height = PIL.Image.open(image_path).size
        resized_width = width * desired_height / height
        if resized_width > self.total_size:
            resized_width = self.total_size
            desired_height = height * resized_width / width

        self.pdf.image(
            image_path,
            self.pdf.get_x() + self.total_size / 2 - resized_width / 2,
            self.pdf.get_y(),
            h=desired_height,
        )
        self.pdf.set_xy(self.margin,
                        self.pdf.get_y() + desired_height + self.margin)

    def make_title(self):
        # title
        self.pdf.set_font("Helvetica", "B", self.title_size)
        self.pdf.set_text_color(*self.mapi_light_green)
        self.pdf.cell(0, self.margin, "OpenSfM Quality Report", align="C")
        self.pdf.set_xy(self.margin, self.title_size)

        # version number
        try:
            out, _ = subprocess.Popen(
                ["git", "describe", "--tags"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            ).communicate()
            version = out.strip().decode()
        except BaseException as e:
            logger.warning(
                f"Exception thrwon while extracting 'git' version, {e}")
            version = ""

        # indicate we don't know the version
        version = "unknown" if version == "" else version

        self.pdf.set_font("Helvetica", "", self.small_text)
        self.pdf.set_text_color(*self.mapi_dark_grey)
        self.pdf.cell(0,
                      self.margin,
                      f"Processed with OpenSfM version {version}",
                      align="R")
        self.pdf.set_xy(self.margin, self.pdf.get_y() + 2 * self.margin)

    def make_dataset_summary(self):
        self._make_section("Dataset Summary")

        rows = [
            ["Dataset", self.dataset_name],
            ["Date", self.stats["processing_statistics"]["date"]],
            [
                "Area Covered",
                f"{self.stats['processing_statistics']['area']/1e6:.6f} km²",
            ],
            [
                "Processing Time",
                f"{self.stats['processing_statistics']['steps_times']['Total Time']:.2f} seconds",
            ],
        ]
        self._make_table(None, rows, True)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def _has_meaningful_gcp(self):
        return (self.stats["reconstruction_statistics"]["has_gcp"]
                and "average_error" in self.stats["gcp_errors"])

    def make_processing_summary(self):
        self._make_section("Processing Summary")

        rec_shots, init_shots = (
            self.stats["reconstruction_statistics"]
            ["reconstructed_shots_count"],
            self.stats["reconstruction_statistics"]["initial_shots_count"],
        )
        rec_points, init_points = (
            self.stats["reconstruction_statistics"]
            ["reconstructed_points_count"],
            self.stats["reconstruction_statistics"]["initial_points_count"],
        )

        geo_string = []
        if self.stats["reconstruction_statistics"]["has_gps"]:
            geo_string.append("GPS")
        if self._has_meaningful_gcp():
            geo_string.append("GCP")

        rows = [
            [
                "Reconstructed Images",
                f"{rec_shots} over {init_shots} shots ({rec_shots/init_shots*100:.1f}%)",
            ],
            [
                "Reconstructed Points",
                f"{rec_points} over {init_points} points ({rec_points/init_points*100:.1f}%)",
            ],
            [
                "Reconstructed Components",
                f"{self.stats['reconstruction_statistics']['components']} component",
            ],
            [
                "Detected Features",
                f"{self.stats['features_statistics']['detected_features']['median']} features",
            ],
            [
                "Reconstructed Features",
                f"{self.stats['features_statistics']['reconstructed_features']['median']} features",
            ],
            ["Geographic Reference", " and ".join(geo_string)],
        ]

        row_gps_gcp = [" / ".join(geo_string) + " errors"]
        geo_errors = []
        if self.stats["reconstruction_statistics"]["has_gps"]:
            geo_errors.append(
                f"{self.stats['gps_errors']['average_error']:.2f}")
        if self._has_meaningful_gcp():
            geo_errors.append(
                f"{self.stats['gcp_errors']['average_error']:.2f}")
        row_gps_gcp.append(" / ".join(geo_errors) + " meters")
        rows.append(row_gps_gcp)

        self._make_table(None, rows, True)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

        topview_height = 130
        topview_grids = [
            f for f in os.listdir(self.output_path) if f.startswith("topview")
        ]
        self._make_centered_image(
            os.path.join(self.output_path, topview_grids[0]), topview_height)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def make_processing_time_details(self):
        self._make_section("Processing Time Details")

        columns_names = list(
            self.stats["processing_statistics"]["steps_times"].keys())
        formatted_floats = []
        for v in self.stats["processing_statistics"]["steps_times"].values():
            formatted_floats.append(f"{v:.2f} sec.")
        rows = [formatted_floats]
        self._make_table(columns_names, rows)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + 2 * self.margin)

    def make_gps_details(self):
        self._make_section("GPS/GCP Errors Details")

        # GPS
        for error_type in ["gps", "gcp"]:
            rows = []
            columns_names = [error_type.upper(), "Mean", "Sigma", "RMS Error"]
            if "average_error" not in self.stats[error_type + "_errors"]:
                continue
            for comp in ["x", "y", "z"]:
                row = [comp.upper() + " Error (meters)"]
                row.append(
                    f"{self.stats[error_type + '_errors']['mean'][comp]:.3f}")
                row.append(
                    f"{self.stats[error_type +'_errors']['std'][comp]:.3f}")
                row.append(
                    f"{self.stats[error_type +'_errors']['error'][comp]:.3f}")
                rows.append(row)

            rows.append([
                "Total",
                "",
                "",
                f"{self.stats[error_type +'_errors']['average_error']:.3f}",
            ])
            self._make_table(columns_names, rows)
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

    def make_features_details(self):
        self._make_section("Features Details")

        heatmap_height = 60
        heatmaps = [
            f for f in os.listdir(self.output_path) if f.startswith("heatmap")
        ]
        self._make_centered_image(os.path.join(self.output_path, heatmaps[0]),
                                  heatmap_height)
        if len(heatmaps) > 1:
            logger.warning("Please implement multi-model display")

        columns_names = ["", "Min.", "Max.", "Mean", "Median"]
        rows = []
        for comp in ["detected_features", "reconstructed_features"]:
            row = [comp.replace("_", " ").replace("features", "").capitalize()]
            for t in columns_names[1:]:
                row.append(
                    f"{self.stats['features_statistics'][comp][t.replace('.', '').lower()]:.0f}"
                )
            rows.append(row)
        self._make_table(columns_names, rows)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def make_reconstruction_details(self):
        self._make_section("Reconstruction Details")

        rows = [
            [
                "Average reprojection Error",
                f"{self.stats['reconstruction_statistics']['reprojection_error']:.2f} pixels",
            ],
            [
                "Average Track Length",
                f"{self.stats['reconstruction_statistics']['average_track_length']:.2f} images",
            ],
            [
                "Average Track Length (> 2)",
                f"{self.stats['reconstruction_statistics']['average_track_length_over_two']:.2f} images",
            ],
        ]
        self._make_table(None, rows, True)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def make_camera_models_details(self):
        self._make_section("Camera Models Details")

        for camera, params in self.stats["camera_errors"].items():
            residual_grids = [
                f for f in os.listdir(self.output_path)
                if f.startswith("residuals_" + str(camera.replace("/", "_")))
            ]
            if not residual_grids:
                continue

            initial = params["initial_values"]
            optimized = params["optimized_values"]
            names = [""] + list(initial.keys())

            rows = []
            rows.append(["Initial"] + [f"{x:.4f}" for x in initial.values()])
            rows.append(["Optimized"] +
                        [f"{x:.4f}" for x in optimized.values()])

            self._make_subsection(camera)
            self._make_table(names, rows)
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

            residual_grid_height = 80
            self._make_centered_image(
                os.path.join(self.output_path, residual_grids[0]),
                residual_grid_height)

    def make_tracks_details(self):
        self._make_section("Tracks Details")
        matchgraph_height = 60
        matchgraph = [
            f for f in os.listdir(self.output_path)
            if f.startswith("matchgraph")
        ]
        self._make_centered_image(
            os.path.join(self.output_path, matchgraph[0]), matchgraph_height)

        histogram = self.stats["reconstruction_statistics"][
            "histogram_track_length"]
        start_length, end_length = 2, 10
        row_length = ["Length"]
        for length, _ in sorted(histogram.items(), key=lambda x: int(x[0])):
            if int(length) < start_length or int(length) > end_length:
                continue
            row_length.append(length)
        row_count = ["Count"]
        for length, count in sorted(histogram.items(),
                                    key=lambda x: int(x[0])):
            if int(length) < start_length or int(length) > end_length:
                continue
            row_count.append(f"{count}")

        self._make_table(None, [row_length, row_count], True)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def add_page_break(self):
        self.pdf.add_page("P")

    def generate_report(self):
        self.make_title()
        self.make_dataset_summary()
        self.make_processing_summary()
        self.add_page_break()

        self.make_features_details()
        self.make_reconstruction_details()
        self.make_tracks_details()
        self.add_page_break()

        self.make_camera_models_details()
        self.make_gps_details()
        self.make_processing_time_details()
コード例 #28
0
class Report:
    def __init__(self, data: DataSet, stats=None) -> None:
        self.output_path = os.path.join(data.data_path, "stats")
        self.dataset_name = os.path.basename(data.data_path)
        self.io_handler = data.io_handler

        self.mapi_light_light_green = [255, 255, 255]
        self.mapi_light_green = [0, 0, 0]
        self.mapi_light_grey = [218, 222, 228]
        self.mapi_dark_grey = [0, 0, 0]

        self.pdf = FPDF("P", "mm", "A4")
        self.pdf.add_page()

        self.title_size = 20
        self.h1 = 16
        self.h2 = 13
        self.h3 = 10
        self.text = 10
        self.small_text = 8
        self.margin = 10
        self.cell_height = 7
        self.total_size = 190
        self.odm_stat = 'odm_processing_statistics'

        if stats is not None:
            self.stats = stats
        else:
            self.stats = self._read_stats_file("stats.json")

    def save_report(self, filename: str) -> None:
        # pyre-fixme[28]: Unexpected keyword argument `dest`.
        bytestring = self.pdf.output(dest="S")
        if isinstance(bytestring, str):
            bytestring = bytestring.encode("utf8")

        with self.io_handler.open(os.path.join(self.output_path, filename),
                                  "wb") as fwb:
            fwb.write(bytestring)

    def _make_table(self, columns_names, rows, row_header=False) -> None:
        self.pdf.set_font("Helvetica", "", self.h3)
        self.pdf.set_line_width(0.3)

        columns_sizes = [int(self.total_size / len(rows[0]))] * len(rows[0])

        if columns_names:
            self.pdf.set_draw_color(*self.mapi_light_grey)
            self.pdf.set_fill_color(*self.mapi_light_grey)
            for col, size in zip(columns_names, columns_sizes):
                self.pdf.rect(
                    self.pdf.get_x(),
                    self.pdf.get_y(),
                    size,
                    self.cell_height,
                    style="FD",
                )
                self.pdf.set_text_color(*self.mapi_dark_grey)
                self.pdf.cell(size, self.cell_height, col, align="L")
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.cell_height)

        self.pdf.set_draw_color(*self.mapi_light_grey)
        self.pdf.set_fill_color(*self.mapi_light_light_green)

        for row in rows:
            for i, (col, size) in enumerate(zip(row, columns_sizes)):
                if i == 0 and row_header:
                    self.pdf.set_draw_color(*self.mapi_light_grey)
                    self.pdf.set_fill_color(*self.mapi_light_grey)
                self.pdf.rect(
                    self.pdf.get_x(),
                    self.pdf.get_y(),
                    size,
                    self.cell_height,
                    style="FD",
                )
                self.pdf.set_text_color(*self.mapi_dark_grey)
                if i == 0 and row_header:
                    self.pdf.set_draw_color(*self.mapi_light_grey)
                    self.pdf.set_fill_color(*self.mapi_light_light_green)
                self.pdf.cell(size, self.cell_height, col, align="L")
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.cell_height)

    def _read_stats_file(self, filename) -> Dict[str, Any]:
        file_path = os.path.join(self.output_path, filename)
        with self.io_handler.open_rt(file_path) as fin:
            return io.json_load(fin)

    def _read_gcp_stats_file(self, filename):
        file_path = os.path.join(self.output_path,
                                 "ground_control_points.json")

        with self.io_handler.open_rt(file_path) as fin:
            return io.json_load(fin)

    def _make_section(self, title: str) -> None:
        self.pdf.set_font("Helvetica", "B", self.h1)
        self.pdf.set_text_color(*self.mapi_dark_grey)
        self.pdf.cell(0, self.margin, title, align="L")
        self.pdf.set_xy(self.margin, self.pdf.get_y() + 1.5 * self.margin)

    def _make_subsection(self, title: str) -> None:
        self.pdf.set_xy(self.margin, self.pdf.get_y() - 0.5 * self.margin)
        self.pdf.set_font("Helvetica", "B", self.h2)
        self.pdf.set_text_color(*self.mapi_dark_grey)
        self.pdf.cell(0, self.margin, title, align="L")
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def _make_centered_image(self, image_path: str,
                             desired_height: float) -> None:

        with tempfile.TemporaryDirectory() as tmp_local_dir:
            local_image_path = os.path.join(tmp_local_dir,
                                            os.path.basename(image_path))
            with self.io_handler.open(local_image_path, "wb") as fwb:
                with self.io_handler.open(image_path, "rb") as f:
                    fwb.write(f.read())

            width, height = PIL.Image.open(local_image_path).size
            resized_width = width * desired_height / height
            if resized_width > self.total_size:
                resized_width = self.total_size
                desired_height = height * resized_width / width

            self.pdf.image(
                local_image_path,
                self.pdf.get_x() + self.total_size / 2 - resized_width / 2,
                self.pdf.get_y(),
                h=desired_height,
            )
            self.pdf.set_xy(self.margin,
                            self.pdf.get_y() + desired_height + self.margin)

    def make_title(self) -> None:
        # title
        self.pdf.set_font("Helvetica", "B", self.title_size)
        self.pdf.set_text_color(*self.mapi_light_green)
        self.pdf.cell(0, self.margin, "ODM Quality Report", align="C")
        self.pdf.set_xy(self.margin, self.title_size)

        # version number
        version_file = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "../../../../../VERSION"))
        try:
            with open(version_file, 'r') as f:
                version = f.read().strip()
        except Exception as e:  # indicate we don't know the version
            version = "unknown"
            logger.warning(f"Invalid ODM version {version_file}: " + str(e))

        self.pdf.set_font("Helvetica", "", self.small_text)
        self.pdf.set_text_color(*self.mapi_dark_grey)
        self.pdf.cell(0,
                      self.margin,
                      f"Processed with ODM version {version}",
                      align="R")
        self.pdf.set_xy(self.margin, self.pdf.get_y() + 2 * self.margin)

    def make_dataset_summary(self) -> None:
        self._make_section("Dataset Summary")

        rows = [
            #["Dataset", self.dataset_name],
            ["Date", self.stats["processing_statistics"]["date"]],
            [
                "Area Covered",
                f"{self.stats['processing_statistics']['area']/1e6:.6f} km²",
            ],
            [
                "Processing Time",
                self.stats[self.odm_stat]['total_time_human'] if self.odm_stat in self.stats else \
                f"{self.stats['processing_statistics']['steps_times']['Total Time']:.2f} seconds",
            ],
            ["Capture Start", self.stats["processing_statistics"]["start_date"]],
            ["Capture End", self.stats["processing_statistics"]["end_date"]],
        ]
        self._make_table(None, rows, True)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def _has_meaningful_gcp(self) -> bool:
        return (self.stats["reconstruction_statistics"]["has_gcp"]
                and "average_error" in self.stats["gcp_errors"])

    def make_processing_summary(self) -> None:
        self._make_section("Processing Summary")

        rec_shots, init_shots = (
            self.stats["reconstruction_statistics"]
            ["reconstructed_shots_count"],
            self.stats["reconstruction_statistics"]["initial_shots_count"],
        )
        rec_points, init_points = (
            self.stats["reconstruction_statistics"]
            ["reconstructed_points_count"],
            self.stats["reconstruction_statistics"]["initial_points_count"],
        )

        geo_string = []
        if self.stats["reconstruction_statistics"]["has_gps"]:
            geo_string.append("GPS")
        if self._has_meaningful_gcp():
            geo_string.append("GCP")

        ratio_shots = rec_shots / init_shots * 100 if init_shots > 0 else -1
        rows = [
            [
                "Reconstructed Images",
                f"{rec_shots} over {init_shots} shots ({ratio_shots:.1f}%)",
            ],
            [
                "Reconstructed Points (Sparse)",
                f"{rec_points} over {init_points} points ({rec_points/init_points*100:.1f}%)",
            ],
            # [
            #     "Reconstructed Components",
            #     f"{self.stats['reconstruction_statistics']['components']} component",
            # ],
            [
                "Detected Features",
                f"{self.stats['features_statistics']['detected_features']['median']:,} features",
            ],
            [
                "Reconstructed Features",
                f"{self.stats['features_statistics']['reconstructed_features']['median']:,} features",
            ],
            ["Geographic Reference", " and ".join(geo_string)],
        ]

        # Dense (if available)
        if self.stats.get('point_cloud_statistics'):
            if self.stats['point_cloud_statistics'].get('dense'):
                rows.insert(2, [
                    "Reconstructed Points (Dense)",
                    f"{self.stats['point_cloud_statistics']['stats']['statistic'][0]['count']:,} points"
                ])

        # GSD (if available)
        if self.odm_stat in self.stats and self.stats[self.odm_stat].get(
                'average_gsd'):
            rows.insert(3, [
                "Average Ground Sampling Distance (GSD)",
                f"{self.stats[self.odm_stat]['average_gsd']:.1f} cm"
            ])

        row_gps_gcp = [" / ".join(geo_string) + " errors"]
        geo_errors = []
        if self.stats["reconstruction_statistics"]["has_gps"]:
            geo_errors.append(
                f"{self.stats['gps_errors']['average_error']:.2f}")
        if self._has_meaningful_gcp():
            geo_errors.append(
                f"{self.stats['gcp_errors']['average_error']:.2f}")
        row_gps_gcp.append(" / ".join(geo_errors) + " meters")
        rows.append(row_gps_gcp)

        self._make_table(None, rows, True)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

        topview_height = 110
        topview_grids = [
            f for f in self.io_handler.ls(self.output_path)
            if f.startswith("topview")
        ]
        self._make_centered_image(
            os.path.join(self.output_path, topview_grids[0]), topview_height)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def make_processing_time_details(self) -> None:
        self._make_section("Processing Time Details")

        columns_names = list(
            self.stats["processing_statistics"]["steps_times"].keys())
        formatted_floats = []
        for v in self.stats["processing_statistics"]["steps_times"].values():
            formatted_floats.append(f"{v:.2f} sec.")
        rows = [formatted_floats]
        self._make_table(columns_names, rows)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + 2 * self.margin)

    def make_gcp_error_details(self):
        self._make_section("Ground Control Point Error")

        gcp_stats = self._read_gcp_stats_file("ground_control_points.json")

        rows = []
        column_names = ["ID", "Error X (m)", "Error Y (m)", "Error Z (m)"]

        for gcp in gcp_stats:
            row = [gcp["id"]]
            row.append(f"{gcp['error'][0]:.3f}")
            row.append(f"{gcp['error'][1]:.3f}")
            row.append(f"{gcp['error'][2]:.3f}")

            rows.append(row)

        self._make_table(column_names, rows)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

    def make_gps_details(self) -> None:
        self._make_section("GPS/GCP/3D Errors Details")

        # GPS
        table_count = 0
        for error_type in ["gps", "gcp", "3d"]:
            rows = []
            columns_names = [error_type.upper(), "Mean", "Sigma", "RMS Error"]
            if "average_error" not in self.stats[error_type + "_errors"]:
                continue
            for comp in ["x", "y", "z"]:
                row = [comp.upper() + " Error (meters)"]
                row.append(
                    f"{self.stats[error_type + '_errors']['mean'][comp]:.3f}")
                row.append(
                    f"{self.stats[error_type +'_errors']['std'][comp]:.3f}")
                row.append(
                    f"{self.stats[error_type +'_errors']['error'][comp]:.3f}")
                rows.append(row)

            rows.append([
                "Total",
                "",
                "",
                f"{self.stats[error_type +'_errors']['average_error']:.3f}",
            ])
            self._make_table(columns_names, rows)
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)
            table_count += 1

        if table_count > 0:
            abs_error_type = "gps" if table_count == 2 else "gcp"

            a_ce90 = self.stats[abs_error_type + "_errors"].get("ce90", 0)
            a_le90 = self.stats[abs_error_type + "_errors"].get("le90", 0)
            r_ce90 = self.stats["3d_errors"].get("ce90", 0)
            r_le90 = self.stats["3d_errors"].get("le90", 0)

            rows = []
            if a_ce90 > 0 and a_le90 > 0:
                rows += [[
                    "Horizontal Accuracy CE90 (meters)",
                    f"{a_ce90:.3f}",
                    f"{r_ce90:.3f}" if r_ce90 > 0 else "-",
                ],
                         [
                             "Vertical Accuracy LE90 (meters)",
                             f"{a_le90:.3f}",
                             f"{r_le90:.3f}" if r_le90 > 0 else "-",
                         ]]

            if rows:
                if table_count > 2:
                    self.add_page_break()
                self._make_table(["", "Absolute", "Relative"], rows, True)
                self.pdf.set_xy(self.margin,
                                self.pdf.get_y() + self.margin / 2)

        # rows = []
        # columns_names = [
        #     "GPS Bias",
        #     "Scale",
        #     "Translation",
        #     "Rotation",
        # ]
        # for camera, params in self.stats["camera_errors"].items():
        #     bias = params["bias"]
        #     s, t, R = bias["scale"], bias["translation"], bias["rotation"]
        #     rows.append(
        #         [
        #             camera,
        #             f"{s:.2f}",
        #             f"{t[0]:.2f}      {t[1]:.2f}      {t[2]:.2f}",
        #             f"{R[0]:.2f}      {R[1]:.2f}      {R[2]:.2f}",
        #         ]
        #     )
        # self._make_table(columns_names, rows)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

    def make_features_details(self) -> None:
        self._make_section("Features Details")

        heatmap_height = 60
        heatmaps = [
            f for f in self.io_handler.ls(self.output_path)
            if f.startswith("heatmap")
        ]
        self._make_centered_image(os.path.join(self.output_path, heatmaps[0]),
                                  heatmap_height)
        if len(heatmaps) > 1:
            logger.warning("Please implement multi-model display")

        columns_names = ["", "Min.", "Max.", "Mean", "Median"]
        rows = []
        for comp in ["detected_features", "reconstructed_features"]:
            row = [comp.replace("_", " ").replace("features", "").capitalize()]
            for t in columns_names[1:]:
                row.append(
                    f"{self.stats['features_statistics'][comp][t.replace('.', '').lower()]:.0f}"
                )
            rows.append(row)
        self._make_table(columns_names, rows)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def make_reconstruction_details(self) -> None:
        self._make_section("Reconstruction Details")

        rows = [
            [
                "Average Reprojection Error (normalized / pixels / angular)",
                (f"{self.stats['reconstruction_statistics']['reprojection_error_normalized']:.2f} / "
                 f"{self.stats['reconstruction_statistics']['reprojection_error_pixels']:.2f} / "
                 f"{self.stats['reconstruction_statistics']['reprojection_error_angular']:.5f}"
                 ),
            ],
            [
                "Average Track Length",
                f"{self.stats['reconstruction_statistics']['average_track_length']:.2f} images",
            ],
            [
                "Average Track Length (> 2)",
                f"{self.stats['reconstruction_statistics']['average_track_length_over_two']:.2f} images",
            ],
        ]
        self._make_table(None, rows, True)
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 1.5)

        residual_histogram_height = 60
        residual_histogram = [
            f for f in self.io_handler.ls(self.output_path)
            if f.startswith("residual_histogram")
        ]
        self._make_centered_image(
            os.path.join(self.output_path, residual_histogram[0]),
            residual_histogram_height,
        )
        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def make_camera_models_details(self) -> None:
        self._make_section("Camera Models Details")

        for camera, params in self.stats["camera_errors"].items():
            residual_grids = [
                f for f in self.io_handler.ls(self.output_path)
                if f.startswith("residuals_" + str(camera.replace("/", "_")))
            ]
            if not residual_grids:
                continue

            initial = params["initial_values"]
            optimized = params["optimized_values"]
            names = [""] + list(initial.keys())

            rows = []
            rows.append(["Initial"] + [f"{x:.4f}" for x in initial.values()])
            rows.append(["Optimized"] +
                        [f"{x:.4f}" for x in optimized.values()])

            self._make_subsection(camera)
            self._make_table(names, rows)
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

            residual_grid_height = 100
            self._make_centered_image(
                os.path.join(self.output_path, residual_grids[0]),
                residual_grid_height)

    def make_rig_cameras_details(self) -> None:
        if len(self.stats["rig_errors"]) == 0:
            return

        self._make_section("Rig Cameras Details")

        columns_names = [
            "Translation X",
            "Translation Y",
            "Translation Z",
            "Rotation X",
            "Rotation Y",
            "Rotation Z",
        ]
        for rig_camera_id, params in self.stats["rig_errors"].items():
            initial = params["initial_values"]
            optimized = params["optimized_values"]

            rows = []
            r_init, t_init = initial["rotation"], initial["translation"]
            r_opt, t_opt = optimized["rotation"], optimized["translation"]
            rows.append([
                f"{t_init[0]:.4f} m",
                f"{t_init[1]:.4f} m",
                f"{t_init[2]:.4f} m",
                f"{r_init[0]:.4f}",
                f"{r_init[1]:.4f}",
                f"{r_init[2]:.4f}",
            ])
            rows.append([
                f"{t_opt[0]:.4f} m",
                f"{t_opt[1]:.4f} m",
                f"{t_opt[2]:.4f} m",
                f"{r_opt[0]:.4f}",
                f"{r_opt[1]:.4f}",
                f"{r_opt[2]:.4f}",
            ])

            self._make_subsection(rig_camera_id)
            self._make_table(columns_names, rows)
            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

    def make_tracks_details(self) -> None:
        self._make_section("Tracks Details")
        matchgraph_height = 80
        matchgraph = [
            f for f in self.io_handler.ls(self.output_path)
            if f.startswith("matchgraph")
        ]
        self._make_centered_image(
            os.path.join(self.output_path, matchgraph[0]), matchgraph_height)

        histogram = self.stats["reconstruction_statistics"][
            "histogram_track_length"]
        start_length, end_length = 2, 10
        row_length = ["Length"]
        for length, _ in sorted(histogram.items(), key=lambda x: int(x[0])):
            if int(length) < start_length or int(length) > end_length:
                continue
            row_length.append(length)
        row_count = ["Count"]
        for length, count in sorted(histogram.items(),
                                    key=lambda x: int(x[0])):
            if int(length) < start_length or int(length) > end_length:
                continue
            row_count.append(f"{count}")

        self._make_table(None, [row_length, row_count], True)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

    def add_page_break(self) -> None:
        self.pdf.add_page("P")

    def make_survey_data(self):
        self._make_section("Survey Data")

        self._make_centered_image(
            os.path.join(self.output_path, "overlap.png"), 90)
        self._make_centered_image(
            os.path.join(self.output_path, "overlap_diagram_legend.png"), 3)

        self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2)

    def _add_image_label(self, text):
        self.pdf.set_font_size(self.small_text)
        self.pdf.text(
            self.pdf.get_x() + self.total_size / 2 -
            self.pdf.get_string_width(text) / 2,
            self.pdf.get_y() - 5, text)

    def make_preview(self):
        ortho = os.path.join(self.output_path, "ortho.png")
        dsm = os.path.join(self.output_path, "dsm.png")
        dtm = os.path.join(self.output_path, "dtm.png")
        count = 0

        if os.path.isfile(ortho) or os.path.isfile(dsm):
            self._make_section("Previews")

            if os.path.isfile(ortho):
                self._make_centered_image(
                    os.path.join(self.output_path, ortho), 110)
                self._add_image_label("Orthophoto")
                count += 1

            if os.path.isfile(dsm) and self.stats.get('dsm_statistics'):
                self._make_centered_image(os.path.join(self.output_path, dsm),
                                          110)
                self._add_image_label("Digital Surface Model")

                self._make_centered_image(
                    os.path.join(self.output_path, "dsm_gradient.png"), 4)
                self.pdf.set_font_size(self.small_text)
                min_text = "{:,.2f}m".format(
                    self.stats['dsm_statistics']['min'])
                max_text = "{:,.2f}m".format(
                    self.stats['dsm_statistics']['max'])
                self.pdf.text(self.pdf.get_x() + 40,
                              self.pdf.get_y() - 5, min_text)
                self.pdf.text(
                    self.pdf.get_x() + 40 + 110.5 -
                    self.pdf.get_string_width(max_text),
                    self.pdf.get_y() - 5, max_text)
                count += 1

            if os.path.isfile(dtm) and self.stats.get('dtm_statistics'):
                if count >= 2:
                    self.add_page_break()

                self._make_centered_image(os.path.join(self.output_path, dtm),
                                          110)
                self._add_image_label("Digital Terrain Model")

                self._make_centered_image(
                    os.path.join(self.output_path, "dsm_gradient.png"), 4)
                self.pdf.set_font_size(self.small_text)
                min_text = "{:,.2f}m".format(
                    self.stats['dtm_statistics']['min'])
                max_text = "{:,.2f}m".format(
                    self.stats['dtm_statistics']['max'])
                self.pdf.text(self.pdf.get_x() + 40,
                              self.pdf.get_y() - 5, min_text)
                self.pdf.text(
                    self.pdf.get_x() + 40 + 110.5 -
                    self.pdf.get_string_width(max_text),
                    self.pdf.get_y() - 5, max_text)

            self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin)

            return True

    def generate_report(self) -> None:
        self.make_title()
        self.make_dataset_summary()
        self.make_processing_summary()
        self.add_page_break()

        if self.make_preview():
            self.add_page_break()

        if os.path.isfile(os.path.join(self.output_path, "overlap.png")):
            self.make_survey_data()

        self.make_gps_details()

        if os.path.isfile(
                os.path.join(self.output_path, "ground_control_points.json")):
            self.make_gcp_error_details()

        self.add_page_break()

        self.make_features_details()
        self.make_reconstruction_details()
        self.add_page_break()

        self.make_tracks_details()
        self.make_camera_models_details()
コード例 #29
0
ファイル: app.py プロジェクト: rakshitdeshpande/Ride-a-Bike
def bill():
    try:
        if 'username' in session and session['username'] != manager_name:
            if request.method == 'POST':
                a = datetime.datetime.now(pytz.timezone('Asia/Calcutta'))
                end_time = a.strftime("%c")
                db.details.update({"name": session['username']}, {
                    "$set": {
                        "destination": request.form['destination'],
                        "end_time": end_time
                    }
                })
                db.scooter.update({"rider_name": session['username']}, {
                    "$set": {
                        "docking_station": request.form['destination'],
                        "ignition_status": "off",
                        "rider_name": "-"
                    }
                })
                details = db.docking_station.find(
                    {"station_name": request.form['destination']})
                num = details[0]["no_of_scooters"]
                num = num + 1
                db.docking_station.update(
                    {"station_name": request.form['destination']},
                    {"$set": {
                        "no_of_scooters": num
                    }})
                data = db.details.find({"name": session['username']})
                start_time = data[0]["start_time"]
                a = start_time.split(" ")
                x = db.fare.find({})
                base_price = int(x[0]["base_price"])
                per_min = int(x[0]["per_min"])
                min_price = int(x[0]["min_price"])
                global time
                global before_hour
                global before_min
                try:
                    time = a[3].split(":")
                    before_hour = int(time[0])
                    before_min = int(time[1])
                except:
                    time = a[4].split(":")
                    before_hour = int(time[0])
                    before_min = int(time[1])
                a = end_time.split(" ")
                global after_hour
                global after_min
                try:
                    time = a[3].split(":")
                    after_hour = int(time[0])
                    after_min = int(time[1])
                except:
                    time = a[4].split(":")
                    after_hour = int(time[0])
                    after_min = int(time[1])

                #calculaitng ride timming
                hour = after_hour - before_hour - 1
                minutes = after_min + (60 - before_min) + (hour * 60)
                #caluclation fare on half an hour basis
                # quo = int(minutes/30)
                # rem = minutes%30
                # if rem == 0:
                #     amount = quo*50
                # else:
                #     amount = (quo + 1 )*50
                amount = base_price + (minutes * per_min)
                balance = data[0]["balance"]
                balance = balance - amount
                db.details.update({"name": session['username']},
                                  {"$set": {
                                      "balance": balance
                                  }})
                details = {
                    "name": session['username'],
                    "registration_number": data[0]["registration_number"],
                    "from": data[0]["from"],
                    "start_time": data[0]["start_time"],
                    "destination": data[0]["destination"],
                    "end_time": data[0]["end_time"],
                    "amount": amount,
                    "duration": minutes
                }
                db.logs.insert(details)

                #writing into pdf file
                pdf = FPDF()
                pdf.add_page()
                pdf.set_font("times", 'I', size=32)
                pdf.cell(200, 10, txt="Ride-a-Bike", ln=1, align="C")
                pdf.set_font("Arial", size=20)
                pdf.set_line_width(1)
                pdf.set_draw_color(255, 0, 0)
                pdf.line(10, 25, 200, 25)
                pdf.image('static/images/logo.png', x=85, y=30, w=50)
                pdf.set_font("Arial", 'B', size=20)
                pdf.cell(200, 120, txt=session['username'], ln=1, align="L")
                pdf.set_font("Arial", size=18)
                pdf.set_line_width(1)
                pdf.set_draw_color(0, 0, 0)
                pdf.line(15, 90, 195, 90)
                start_station = data[0]["from"]
                pdf.cell(200,
                         -75,
                         txt="From :  " + start_station + "    " + start_time,
                         ln=1,
                         align="L")
                pdf.cell(200,
                         100,
                         txt="To :   " + request.form['destination'] + "    " +
                         end_time,
                         ln=1,
                         align="L")
                pdf.cell(200,
                         -75,
                         txt="Minimum Fare : " + str(base_price),
                         ln=1,
                         align="L")
                pdf.cell(200,
                         100,
                         txt="Time Fare (" + str(minutes) + " mins) : " + str(
                             (minutes * per_min)),
                         ln=1,
                         align="L")
                pdf.set_font("Arial", 'B', size=20)
                pdf.cell(200,
                         -75,
                         txt="Total : " + str(amount),
                         ln=1,
                         align="L")
                pdf.set_line_width(1)
                pdf.set_draw_color(0, 0, 0)
                pdf.line(15, 165, 195, 165)
                pdf.output("ride-a-bike_bill.pdf")

                db.details.update({"name": session['username']},
                                  {"$set": {
                                      "status": "-"
                                  }})
                return render_template("bill.html",
                                       time=end_time,
                                       data=data,
                                       minutes=minutes,
                                       amount=amount,
                                       username=session['username'],
                                       balance=balance,
                                       base_price=x[0]["base_price"],
                                       per_min=x[0]["per_min"],
                                       min_price=x[0]["min_price"])
            else:
                return render_template("bill.html")
        else:
            return render_template("login_error.html")
    except:
        return redirect('/end_ride')
コード例 #30
0
def createfunc(fname, lname, email, address, contact, portfolio, LinkedIn,
               profession, field, subfield, qualification, degree, skills, age,
               projects, AdditionalSkills, Extracurricular, Certificates,
               achievement, Hobbies, tagline):
    pdf = FPDF()
    pdf.add_page()
    pdf.set_draw_color(176, 224, 230)
    pdf.set_font("Arial", 'B', size=14)
    name = fname + " " + lname
    info = email + " | " + contact
    pdf.cell(200, 20, txt=name, ln=2, align='L')

    pdf.set_font("Arial", size=12)
    pdf.cell(200, 10, txt=info, ln=2, align='L')
    pdf.cell(200, 10, txt=address, ln=2, align='L')
    pdf.cell(200, 10, txt=portfolio, ln=2, align='L', link=portfolio)
    pdf.cell(200, 10, txt=LinkedIn, ln=2, align='L', link=LinkedIn)
    pdf.set_line_width(3)

    pdf.line(10, 72, 200, 72)
    pdf.set_line_width(0.3)
    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 25, txt="Objective", border='B', ln=2, align='L')

    pdf.set_font("Arial", size=12)
    pdf.multi_cell(190, 10, txt=tagline)

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 10, txt="Education", border='B', ln=2, align='L')

    pdf.set_font("Arial", size=12)
    pdf.cell(200, 10, txt=field + " - " + degree, ln=2)

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 10, txt="Skills", border='B', ln=2, align='L')

    pdf.set_font("Arial", size=12)
    sk = len(skills)
    for i in range(sk):
        pdf.multi_cell(200, 10, txt=chr(149) + " " + skills[i])

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 10, txt="Projects", ln=2, border='B', align='L')
    pdf.set_font("Arial", size=12)
    pdf.multi_cell(200, 10, txt=projects)

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 10, txt="Additional Skills", border='B', ln=2, align='L')
    pdf.set_font("Arial", size=12)
    pdf.multi_cell(200, 10, txt=Additionalskills)

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190,
             10,
             txt="Extracurricular and Academic Activities",
             border='B',
             ln=2,
             align='L')
    pdf.set_font("Arial", size=12)
    pdf.multi_cell(200, 10, txt=Extracurricular)

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 10, txt="Certifications", border='B', ln=2, align='L')
    pdf.set_font("Arial", size=12)
    pdf.multi_cell(200, 10, txt=Certificates)

    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(190, 10, txt="Hobbies", border='B', ln=2, align='L')
    pdf.set_font("Arial", size=12)
    pdf.multi_cell(200, 10, txt=Hobbies)

    pdf.set_font("Arial", size=12)
    pdf.cell(
        200,
        20,
        txt=
        "I hereby declare that all the details given are true to the best of my knowledge and belief.",
        ln=2,
        align='L')
    pdf.cell(200, 25, txt=name, ln=2, align='L')

    pdf.output("Resume.pdf", dest='F').encode('utf-8')
コード例 #31
0
from fpdf import FPDF
import sys 
import os
from Мобилки1 import in_call, out_call, sms, call_duration, k_in, k_out, k_sms
from Мобилки2 import mbyte, k, internet
#создание
pdf = FPDF()
pdf.add_page()

#первая табличка
pdf.set_draw_color(0, 0, 0)
pdf.set_line_width(0.1)
pdf.line(12, 15, 12, 44)
pdf.line(12, 28, 185, 28)
pdf.line(12, 32, 106, 32)
pdf.line(106, 15, 106, 44)
pdf.line(123, 15, 123, 44)
pdf.line(61, 28, 61, 32)
pdf.line(185, 15, 185, 44)
pdf.line(106, 20, 123, 20)
pdf.line(12, 15, 185, 15)
pdf.line(12, 44, 185, 44)
#текст первой таблички
pdf.add_font('DejaVu', '', 'C:/Users/prova/AppData/Local/Microsoft/Windows/Fonts/DejaVuSansCondensed.ttf', uni=True)
pdf.set_font("DejaVu", size = 9)
pdf.cell(66, 15, txt = "АО ''Хороший банк'' г. Санкт-Петербург ", ln = 1, align = "C")
pdf.cell(31, 2, txt = "Банк получателя", ln = 1, align = "C")
pdf.set_font("DejaVu", size = 11)
pdf.cell(200, -18, txt = "БИК", ln = 1, align = "C")
pdf.set_font("DejaVu", size = 10)
pdf.cell(245, 18, txt = "044547195", ln = 1, align = "C")
コード例 #32
0
def MakePDFMonthCal (year, month, calParams, outputFile):

	startMonth = 1
	endMonth   = 12
	
	# Create a calendar instance
	if (calParams ['FirstDay'] == SUNDAY):
		cal = calendar.Calendar (calendar.SUNDAY)
	else:
		cal = calendar.Calendar (calendar.MONDAY)


	pdfFile = FPDF (orientation=calParams['PageOrientation'], unit='in', format='letter')

	pdfFile.add_page()
	pdfFile.set_left_margin (calParams['PageXOrigin'])
	pdfFile.set_top_margin  (calParams['PageYOrigin'])

	calXOffset = 0
	calYOffset = 0
	calMonth   = month

	fontStyle = ''
	if (calParams['FontBold'] == True):
		fontStyle += 'b'
	if (calParams['FontItalic'] == True):
		fontStyle += 'i'

	calHeight = calParams['PageHeight']
	calWidth  = calParams['PageWidth']
	numCols   = 1
	numRows   = 1

	if (month == -1):
	
		pdfFile.set_draw_color (calParams['DebugColourR'], calParams['DebugColourG'], calParams['DebugColourB'])
		pdfFile.set_line_width (calParams['DebugLineThickness'])

		pdfFile.set_xy (calParams['PageXOrigin'], calParams['PageYOrigin'])
		pdfFile.set_font (calParams['Font'], style=fontStyle, size=INCH_TO_POINT*calParams['YearHeader']) 
		pdfFile.set_text_color (calParams['FontColourR'], calParams['FontColourG'], calParams['FontColourB'])
		pdfFile.cell (calWidth-calParams['YearGridSpacing'], calParams['YearHeader'], txt=str(year), border=calParams['Debug'], align='C')

		calParams['PageYOrigin'] += (calParams['YearHeader'] + calParams['YearHeaderSpace'])

		if (calParams['PageOrientation'] == PORTRAIT):
			calHeight = (calParams['PageHeight'] - calParams['YearHeader'] - calParams['YearHeaderSpace']) / YEAR_MONTH_ROWS_PORT
			calWidth  = calParams['PageWidth']  / YEAR_MONTH_COLS_PORT

			numCols = YEAR_MONTH_COLS_PORT
			numRows = YEAR_MONTH_ROWS_PORT
		else:
			calHeight = (calParams['PageHeight'] - calParams['YearHeader'] - calParams['YearHeaderSpace']) / YEAR_MONTH_ROWS_LAND
			calWidth  = calParams['PageWidth']  / YEAR_MONTH_COLS_LAND

			numCols = YEAR_MONTH_COLS_LAND
			numRows = YEAR_MONTH_ROWS_LAND

		calHeight -= calParams['YearGridSpacing']
		calWidth  -= calParams['YearGridSpacing']

	else:

		startMonth = month
		endMonth   = month


	for calMonth in range (startMonth, endMonth+1):

		if (calParams['Debug']):
			pdfFile.set_draw_color (calParams['DebugColourR'], calParams['DebugColourG'], calParams['DebugColourB'])
			pdfFile.set_line_width (calParams['DebugLineThickness'])
			pdfFile.rect (calParams['PageXOrigin'] + calXOffset, calParams['PageYOrigin'] + calYOffset, calWidth, calHeight)

		#
		# Make title...
		#

		pdfFile.set_text_color (calParams['FontColourR'], calParams['FontColourG'], calParams['FontColourB'])

		if (calParams['TitleStyle'] == 1):

			pdfFile.set_xy (calParams['PageXOrigin'] + calXOffset, calParams['PageYOrigin'] + calYOffset)
			pdfFile.set_font (calParams['Font'], style=fontStyle, size=INCH_TO_POINT*calParams['BlockMonthTitleHeight']*calHeight) 
			pdfFile.cell (calWidth, \
			              calParams['BlockMonthTitleHeight']*calHeight, \
	        		      txt=calendar.month_name[calMonth], \
		        	      border=calParams['Debug'], align='C')


		elif (calParams['TitleStyle'] == 2):

			pdfFile.set_font (calParams['Font'], style=fontStyle, size=INCH_TO_POINT*calParams['BlockMonthTitleHeight']*calHeight)
			monthFontWidth = pdfFile.get_string_width (calendar.month_name[calMonth])
			yearFontWidth = pdfFile.get_string_width (str(year))

			pdfFile.set_xy (calParams['PageXOrigin'] + calXOffset, calParams['PageYOrigin'] + calYOffset)
			pdfFile.cell (monthFontWidth, calParams['BlockMonthTitleHeight']*calHeight + calYOffset,\
			              txt=calendar.month_name[calMonth], border=calParams['Debug'], align='L')

			pdfFile.set_xy (calParams['PageXOrigin'] + calXOffset + calWidth - yearFontWidth, calParams['PageYOrigin'] + calYOffset)
			pdfFile.cell (yearFontWidth, calParams['BlockMonthTitleHeight']*calHeight + calYOffset,\
			              txt=str(year), border=calParams['Debug'], align='R')


		#
		# Weekday titles...
		#
		dayIndices = list ( range (0, NUMBER_WEEKDAYS) )
		if  (calParams ['FirstDay'] == SUNDAY):
			dayIndices.insert (0, NUMBER_WEEKDAYS-1)
			dayIndices.pop ()

		fontScaleFactor, fontMaxWidth, fontMaxSize = FindMaxFontHeight (calParams['Font'], fontStyle, \
		                                                                calParams['BlockDayTitleHeight'] * calHeight, \
      	        	                                                        calWidth/NUMBER_WEEKDAYS, \
	                	                                                MAX_WEEKDAY_STR)

		for day in range (0, NUMBER_WEEKDAYS):

			pdfFile.set_xy (calParams['PageXOrigin'] + calXOffset + calWidth * day / NUMBER_WEEKDAYS, \
					calParams['PageYOrigin'] + calYOffset + (calParams['BlockMonthTitleHeight'] + calParams['BlockTitleSpace']) * calHeight)
			pdfFile.set_font (calParams['Font'], style=fontStyle, size=fontScaleFactor*calParams['BlockDayTitleHeight']*calHeight)

			if (calParams['DayTitleStyle'] == 1):
				pdfFile.cell (calWidth / NUMBER_WEEKDAYS, calParams['BlockDayTitleHeight'] * calHeight, \
				              txt=calendar.day_name[dayIndices[day]], border=calParams['Debug'], align='C')
			elif (calParams['DayTitleStyle'] == 2):
				pdfFile.cell (calWidth / NUMBER_WEEKDAYS, calParams['BlockDayTitleHeight'] * calHeight, \
				              txt=calendar.day_name[dayIndices[day]], border=calParams['Debug'], align='L')


		# Horizontal Lines
		if (calParams['HorizontalLines'] == True):

			pdfFile.set_line_width (calParams['HorizontalLineThickness'])
			pdfFile.set_draw_color (calParams['HorizontalLineColourR'], calParams['HorizontalLineColourG'], calParams['HorizontalLineColourB'])

			HorizontalLineAmount = calParams['HorizontalLineSpacing'] * GRID_ROWS

			for row in range (0, HorizontalLineAmount + 1):

				lineXStart = calParams['PageXOrigin'] + calXOffset
				lineXEnd   = calParams['PageXOrigin'] + calXOffset + calWidth
				lineYStart = calParams['PageYOrigin'] + calYOffset + calHeight \
				                                      - (row / HorizontalLineAmount) * calParams['BlockDayRegionHeight'] * calHeight
				lineYEnd   = lineYStart

				pdfFile.line (lineXStart, lineYStart, lineXEnd, lineYEnd)


		# boxes...
		if (calParams['DayGridStyle'] == 4):

			pdfFile.set_line_width (calParams['GridLineThickness'])
			pdfFile.set_draw_color (calParams['DayRegionColourR'], calParams['DayRegionColourG'], calParams['DayRegionColourB'])
			gridOffset = calParams['DayGridSpacing']

			for col in range (0, NUMBER_WEEKDAYS):
				for row in range (0, GRID_ROWS):

					boxXStart = calParams['PageXOrigin'] + calXOffset + (col / NUMBER_WEEKDAYS) * calWidth + gridOffset
					boxXEnd   = calWidth / NUMBER_WEEKDAYS - 2*gridOffset
					boxYStart = calParams['PageYOrigin'] + calYOffset + (1 - calParams['BlockDayRegionHeight']) * calHeight \
					                                     + calParams['BlockDayRegionHeight'] * calHeight * row / GRID_ROWS + gridOffset
					boxYEnd   = calParams['BlockDayRegionHeight'] * calHeight / GRID_ROWS - 2*gridOffset

					drawStyle = 'D'
					if (calParams['DayGridInvert'] == True):
						pdfFile.set_fill_color (calParams['FontColourR'], calParams['FontColourG'], calParams['FontColourB'])
						drawStyle = 'F'	

					pdfFile.rect (boxXStart, boxYStart, boxXEnd, boxYEnd, style=drawStyle)

		# circles
		if (calParams['DayGridStyle'] == 5 or calParams['DayGridStyle'] == 6):

			pdfFile.set_line_width (calParams['GridLineThickness'])
			pdfFile.set_draw_color (calParams['DayRegionColourR'], calParams['DayRegionColourG'], calParams['DayRegionColourB'])
			gridOffset = calParams['DayGridSpacing']

			for col in range (0, NUMBER_WEEKDAYS):
				for row in range (0, GRID_ROWS):

					boxXStart = calParams['PageXOrigin'] + calXOffset + (col / NUMBER_WEEKDAYS) * calWidth + gridOffset
					boxXEnd   = calWidth / NUMBER_WEEKDAYS - 2*gridOffset
					boxYStart = calParams['PageYOrigin'] + calYOffset + (1 - calParams['BlockDayRegionHeight']) * calHeight \
					                                     + calParams['BlockDayRegionHeight'] * calHeight * row / GRID_ROWS + gridOffset
					boxYEnd   = calParams['BlockDayRegionHeight'] * calHeight / GRID_ROWS - 2*gridOffset

					dX = boxXEnd
					dY = boxYEnd

					minBoxXStart = maxBoxXStart = boxXStart
					minBoxXEnd   = maxBoxXEnd   = boxXEnd
					minBoxYStart = maxBoxYStart = boxYStart
					minBoxYEnd   = maxBoxYEnd   = boxYEnd

					if (dX < dY):
						offset = (dY - dX) / 2
						minBoxYStart += offset
						minBoxYEnd   -= (2*offset)
						maxBoxXStart -= offset
						maxBoxXEnd   += offset
					else:
						offset = (dX - dY) / 2
						minBoxXStart += offset
						minBoxXEnd   -= (2*offset)
						maxBoxYStart -= offset
						maxBoxYEnd   += (2*offset)

					drawStyle = 'D'
					if (calParams['DayGridInvert'] == True):
						pdfFile.set_fill_color (calParams['FontColourR'], calParams['FontColourG'], calParams['FontColourB'])
						drawStyle = 'F'	

					pdfFile.ellipse (minBoxXStart, minBoxYStart, minBoxXEnd, minBoxYEnd, style=drawStyle)

					if (calParams['DayGridStyle'] == 6):
						pdfFile.ellipse (boxXStart, boxYStart, boxXEnd, boxYEnd)


		##
		## numbers
		##
		if (calParams['Debug']):
			pdfFile.set_draw_color (calParams['DebugColourR'], calParams['DebugColourG'], calParams['DebugColourB'])
			pdfFile.set_line_width (calParams['DebugLineThickness'])

			
		fontScaleFactor, fontMaxWidth, fontMaxSize = FindMaxFontHeight (calParams['Font'], fontStyle, \
		                                                                calParams['BlockDayRegionHeight'] * calHeight / GRID_ROWS, \
      	        	                                                        calParams['DayFontScale'] * calWidth / NUMBER_WEEKDAYS,
	                	                                                MAX_NUMBER_STR)


		gridOffset = 0
		if (calParams['DayNumPlacement'] == 2):

			gridOffset += max (calParams['DayCornerOffset'] * calWidth / NUMBER_WEEKDAYS, \
			                   calParams['DayCornerOffset'] * calParams['BlockDayRegionHeight'] * calHeight / GRID_ROWS)

		if (calParams['DayGridStyle'] == 4):
			gridOffset += calParams['DayGridSpacing']

		if (calParams['DayGridInvert'] == True):
			pdfFile.set_text_color (255, 255, 255)
		else:
			pdfFile.set_text_color (calParams['FontColourR'], calParams['FontColourG'], calParams['FontColourB'])


		# iterate over all the days in the month
		col = 0
		row = 0
		for i in cal.itermonthdays (year, calMonth):

			# if it is a day within the month, not from the previous or next month then display it
			if (i != 0):
	
				# Central placement	
				if (calParams['DayNumPlacement'] == 1):	

					numberXLoc = calParams['PageXOrigin'] + calXOffset + col / NUMBER_WEEKDAYS * calWidth
					numberYLoc = calParams['PageYOrigin'] + calYOffset + (1 - calParams['BlockDayRegionHeight']) * calHeight \
					                                      + (row / GRID_ROWS) * calParams['BlockDayRegionHeight'] * calHeight \
					                                      + calParams['BlockDayRegionHeight'] * calHeight / (2 * GRID_ROWS) \
					                                      + gridOffset

					pdfFile.set_xy (numberXLoc, numberYLoc)
					pdfFile.set_font (calParams['Font'], style=fontStyle, size=fontScaleFactor*calParams['BlockDayRegionHeight'] * calHeight / GRID_ROWS)
					pdfFile.cell (calWidth/NUMBER_WEEKDAYS, 0, txt=str(i), align='C', border=calParams['Debug'])

				# Corner placement
				elif (calParams['DayNumPlacement'] == 2):
			
					numberXLoc = calParams['PageXOrigin'] + calXOffset + col / NUMBER_WEEKDAYS * calWidth 
					numberYLoc = calParams['PageYOrigin'] + calYOffset + (1 - calParams['BlockDayRegionHeight']) * calHeight \
					                                      + (row / GRID_ROWS) * calParams['BlockDayRegionHeight'] * calHeight 

					if (calParams['HorizontalLines'] == True):
						pdfFile.set_fill_color (255, 255, 255)
						pdfFile.rect (numberXLoc, numberYLoc, fontMaxWidth + 3*gridOffset, fontMaxSize / INCH_TO_POINT + 3*gridOffset, style='F')
	
					numberXLoc += gridOffset
					numberYLoc += gridOffset

					pdfFile.set_xy (numberXLoc, numberYLoc)
					pdfFile.set_font (calParams['Font'], style=fontStyle, size=fontScaleFactor*calParams['BlockDayRegionHeight'] * calHeight / GRID_ROWS)
					pdfFile.cell (fontMaxWidth, fontMaxSize / INCH_TO_POINT, txt=str(i), align='L', border=calParams['Debug'])

			col += 1
			if (col % 7 == 0):
				col = 0
				row += 1

		if (calParams['Debug']):
			pdfFile.set_draw_color (calParams['DayRegionColourR'], calParams['DayRegionColourG'], calParams['DayRegionColourB'])
			pdfFile.set_line_width (calParams['GridLineThickness'])

		##
		## grid
		##

		pdfFile.set_draw_color (calParams['DayRegionColourR'], calParams['DayRegionColourG'], calParams['DayRegionColourB'])
		pdfFile.set_line_width (calParams['GridLineThickness'])

		# horizontal grid lines
		if (calParams['DayGridStyle'] == 1 or calParams['DayGridStyle'] == 2):

			for row in range (0, GRID_ROWS+1):

				lineXStart = calParams['PageXOrigin'] + calXOffset
				lineXEnd   = calParams['PageXOrigin'] + calXOffset + calWidth
				lineYStart = calParams['PageYOrigin'] + calYOffset + calHeight - (row / GRID_ROWS) * calParams['BlockDayRegionHeight'] * calHeight
				lineYEnd   = lineYStart

				pdfFile.line (lineXStart, lineYStart, lineXEnd, lineYEnd)

		# vertical grid lines
		if (calParams['DayGridStyle'] == 1 or calParams['DayGridStyle'] == 3):

			for day in range (0, NUMBER_WEEKDAYS+1):

				lineXStart = calParams['PageXOrigin'] + calXOffset + calWidth - (day / NUMBER_WEEKDAYS) * calWidth
				lineXEnd   = lineXStart
				lineYStart = calParams['PageYOrigin'] + calYOffset + (1 - calParams['BlockDayRegionHeight']) * calHeight
				lineYEnd   = calParams['PageYOrigin'] + calYOffset + calHeight

				pdfFile.line (lineXStart, lineYStart, lineXEnd, lineYEnd)


		if (calMonth % numCols == 0):
			calXOffset = 0
			calYOffset += (calHeight + calParams['YearGridSpacing'])
		else:
			calXOffset += calWidth
			calXOffset += calParams['YearGridSpacing']


	pdfFile.output (outputFile)

	return 0
コード例 #33
0
class PdfHandler:
    def __init__(self, orientation='P', unit='pt', format='A4'):
        self.pdf_handler = FPDF(orientation=orientation, unit=unit, format=format)
    
    def _set_margins(self, left, top, right=-1):
        return self.pdf_handler.set_margins(left, top, right=right)

    def _set_title(self, title):
        return self.pdf_handler.set_title(title)

    def _set_subject(self, subject):
        return self.pdf_handler.set_subject(subject)

    def _set_author(self, author):
        return self.pdf_handler.set_author(author)

    def _set_keywords(self, keywords):
        return self.pdf_handler.set_keywords(keywords)

    def _set_creator(self, creator):
        return self.pdf_handler.set_creator(creator)

    def _add_page(self, orientation=''):
        return self.pdf_handler.add_page(orientation=orientation)

    def _set_draw_color(self, r, g=-1, b=-1):
        return self.pdf_handler.set_draw_color(r, g=g, b=b)

    def _set_fill_color(self, r, g=-1, b=-1):
        return self.pdf_handler.set_fill_color(g=g, b=b)

    def _set_text_color(self, r, g=-1, b=-1):
        return self.pdf_handler.set_text_color(r, g=-g, b=b)

    def _get_string_width(self, s):
        return self.pdf_handler.get_string_width(s)

    def _set_line_width(self, width):
        return self.pdf_handler.set_line_width(width)

    def _line(self, x1, y1, x2, y2):
        return self.pdf_handler.line(x1, y1, x2, y2)

    def _dashed_line(self, x1, y1, x2, y2, dash_length=1, space_length=1):
        return self.pdf_handler.dashed_line(x1, y1, x2, y2, dash_length=dash_length, space_length=space_length)

    def _rect(self, x, y, w, h, style=''):
        return self.pdf_handler.rect(x, y, w, h, style=style)

    def _ellipse(self, x, y, w, h, style=''):
        return self.pdf_handler.ellipse(x, y, w, h, style=style)

    def _set_font(self, family, style='', size=0):
        return self.pdf_handler.set_font(family, style=style, size=size)

    def _set_font_size(self, size):
        return self.pdf_handler.set_font_size(size)

    def _text(self, x, y, txt=''):
        return self.pdf_handler.text(x, y, txt=txt)

    def _multi_cell(self, w, h, txt='', border=0, align='J', fill=0, split_only=False):
        return self.pdf_handler.multi_cell(w, h, txt=txt, border=border, align=align, fill=fill, split_only=split_only)

    def _write(self, h, txt='', link=''):
        return self.pdf_handler.write(h, txt=txt, link=link)

    def _image(self, name, x=None, y=None, w=0, h=0, image_type='', link=''):
        return self.pdf_handler.image(name, x=x, y=y, w=w,h=h,type=image_type,link=link)

    def _normalize_text(self, txt):
        return self.pdf_handler.normalize_text(txt)

    def _output(self, name='', dest=''):
        return self.pdf_handler.output(name, dest)
コード例 #34
0
class PDFPrinter:
    PAGE_FORMAT = 'A4'
    UNIT = 'mm'
    MARGIN = 10
    CONTENT_WIDTH = 297 - 2 * MARGIN
    CONTENT_HEIGHT = 210 - 2 * MARGIN
    HEADER_HEIGHT = 30
    NOTES_HEIGHT = 17
    TABLE_HEIGHT = CONTENT_HEIGHT - HEADER_HEIGHT - NOTES_HEIGHT
    FONT_S = 7
    FONT_XS = 6.5

    def __init__(self):
        self.colors = {}
        self.timelinesCount = None
        self.fontSize = 12
        self.textColor = Color.WHITE

        self.pdf = FPDF(orientation='L',
                        unit=PDFPrinter.UNIT,
                        format=PDFPrinter.PAGE_FORMAT)
        self.pdf.add_font('regular',
                          '',
                          os.path.join('fonts', 'ubuntu', 'Ubuntu-B.ttf'),
                          uni=True)
        self.pdf.add_font('condensed',
                          '',
                          os.path.join('fonts', 'roboto',
                                       'RobotoCondensed-Regular.ttf'),
                          uni=True)
        self.pdf.add_font('italic',
                          '',
                          os.path.join('fonts', 'roboto',
                                       'RobotoCondensed-Bold.ttf'),
                          uni=True)
        self.pdf.set_font("regular", size=self.fontSize)
        self.pdf.add_page()
        self.pdf.set_margins(PDFPrinter.MARGIN, PDFPrinter.MARGIN,
                             PDFPrinter.MARGIN)

        self.uglyMeasure = FPDF(orientation='L',
                                unit=PDFPrinter.UNIT,
                                format=PDFPrinter.PAGE_FORMAT)
        self.uglyMeasure.add_font('regular',
                                  '',
                                  os.path.join('fonts', 'ubuntu',
                                               'Ubuntu-B.ttf'),
                                  uni=True)
        self.uglyMeasure.add_font('condensed',
                                  '',
                                  os.path.join('fonts', 'roboto',
                                               'RobotoCondensed-Regular.ttf'),
                                  uni=True)
        self.uglyMeasure.add_font('italic',
                                  '',
                                  os.path.join('fonts', 'roboto',
                                               'RobotoCondensed-Bold.ttf'),
                                  uni=True)
        self.uglyMeasure.set_font("regular", size=self.fontSize)
        self.uglyMeasure.add_page()

    def defineColor(self, key, color):
        hex = color.lstrip('#')
        self.colors[key.lower()] = tuple(
            int(hex[i:i + 2], 16) for i in (0, 2, 4))

    def printHeader(self, names):
        self.timelinesCount = len(names)
        boxWidth = PDFPrinter.CONTENT_WIDTH / self.timelinesCount

        boxPos = 0
        for name in names:
            color = self.colors[name.lower()]
            x = PDFPrinter.MARGIN + boxWidth * boxPos
            y = PDFPrinter.MARGIN
            w = boxWidth
            h = 7

            self._box(x,
                      y,
                      w,
                      h,
                      color=color,
                      lineColor=Color.BLACK,
                      lineWidth=0.1)
            self._text(x,
                       y,
                       w,
                       h,
                       text=name,
                       color=self.textColor,
                       font='regular',
                       size=self.fontSize)

            boxPos += 1

    def printTimetable(self, timetable):
        colCount = len(timetable.keys())
        colWidth = PDFPrinter.CONTENT_WIDTH / colCount

        tablePositionY = 30
        tableHeight = PDFPrinter.TABLE_HEIGHT + 1
        tableHeaderHeight = 7
        timelineRowHeight = tableHeight - tableHeaderHeight
        timelineRowPositionY = tablePositionY + tableHeaderHeight

        timeBlockWidth = (colWidth - 2) / self.timelinesCount
        timeWindow = self._findTimeWindow(timetable)
        yPerMin = (timelineRowHeight - 2) / (timeWindow["toTime"] -
                                             timeWindow["fromTime"])

        colNo = 0
        for key, schedules in timetable.items():
            x = PDFPrinter.MARGIN + colWidth * colNo

            self._box(x=x,
                      y=tablePositionY,
                      w=colWidth,
                      h=tableHeaderHeight,
                      color=Color.LIGHT_GREY,
                      lineColor=Color.BLACK,
                      lineWidth=0.2)
            self._text(x=x,
                       y=tablePositionY,
                       w=colWidth,
                       h=tableHeaderHeight,
                       text=key,
                       color=Color.BLACK,
                       font='regular',
                       size=self.fontSize)
            self._box(x=x,
                      y=timelineRowPositionY,
                      w=colWidth,
                      h=timelineRowHeight,
                      color=None,
                      lineColor=Color.BLACK,
                      lineWidth=0.2)

            self._drawTimeblocks(schedules,
                                 areaWidth=timeBlockWidth,
                                 areaPositionX=x,
                                 areaPositionY=timelineRowPositionY + 0.8,
                                 scaleY=yPerMin,
                                 timeWindowStart=timeWindow["fromTime"])

            colNo += 1

    def _drawTimeblocks(self, schedules, areaWidth, areaPositionX,
                        areaPositionY, scaleY, timeWindowStart):
        timeBlockNo = 0

        for person, timelines in schedules.items():
            blockColor = self.colors[person.lower()]
            blockPositionX = areaPositionX + areaWidth * timeBlockNo + 0.5 * timeBlockNo + 0.5

            for timeline in timelines:
                fromTimePosY = scaleY * (self._timeToInt(timeline.fromTime) -
                                         timeWindowStart) + 0.4
                blockPositionY = areaPositionY + fromTimePosY
                blockHeight = scaleY * (self._timeToInt(timeline.toTime) -
                                        timeWindowStart) - fromTimePosY

                self._box(x=blockPositionX,
                          y=blockPositionY,
                          w=areaWidth,
                          h=blockHeight,
                          color=blockColor,
                          lineColor=None)

                if (timeline.kind == "~"):
                    self._drawLeftLines(x=blockPositionX,
                                        y=blockPositionY,
                                        w=areaWidth,
                                        h=blockHeight)
                elif (timeline.kind == "d"):
                    self._drawTwoRightLines(x=blockPositionX,
                                            y=blockPositionY,
                                            w=areaWidth,
                                            h=blockHeight)
                elif (timeline.kind == "e"):
                    self._drawEmptyRightCorner(x=blockPositionX,
                                               y=blockPositionY,
                                               w=areaWidth,
                                               h=blockHeight)

                self._drawTimeblockLabels(areaX=blockPositionX,
                                          areaY=blockPositionY,
                                          areaW=areaWidth,
                                          areaH=blockHeight,
                                          timeline=timeline,
                                          backColor=blockColor)

            timeBlockNo += 1

    def _drawLeftLines(self, x, y, w, h):
        hw = h / w

        posX = x
        posXM = x + w
        posY = y
        posYM = y + h
        jump = 3
        while (posXM > x):
            posX += jump
            posXM -= jump
            posY += jump * hw
            posYM -= jump * hw

            if jump == 2:
                jump = 0.5
            else:
                jump = 2

            if (posX < x + w and posY < y + h):
                self._line(posX, y, x, posY, Color.WHITE, lineWidth=0.2)
            if (posXM > x and posYM > y):
                self._line(posXM,
                           y + h,
                           x + w,
                           posYM,
                           Color.WHITE,
                           lineWidth=0.2)

    def _drawTwoRightLines(self, x, y, w, h):
        hw = h / w

        posX = x
        posXM = x + w
        posY = y
        posYM = y + h
        jump = 2
        for _ in range(4):
            posX += jump
            posXM -= jump
            posY += jump * hw
            posYM -= jump * hw

            # if jump == 2:
            #     jump = 0.7
            # else:
            #     jump = 2

            if (posX < x + w and posY < y + h):
                self._line(posX, y + h, x, posYM, Color.WHITE, lineWidth=0.3)
            if (posXM > x and posYM > y):
                self._line(posXM, y, x + w, posY, Color.WHITE, lineWidth=0.3)

    def _drawEmptyRightCorner(self, x, y, w, h):
        hw = h / w

        posX = x
        posXM = x + w
        posY = y
        posYM = y + h
        jump = 1
        for p in range(9):
            posX += jump
            posXM -= jump
            posY += jump * hw
            posYM -= jump * hw

            if p > 5:
                jump = 1
            else:
                jump = 0.2

            if (posX < x + w and posY < y + h):
                self._line(posX, y + h, x, posYM, Color.WHITE, lineWidth=0.2)
            if (posXM > x and posYM > y):
                self._line(posXM, y, x + w, posY, Color.WHITE, lineWidth=0.2)

    def _drawTimeblockLabels(self, areaX, areaY, areaW, areaH, timeline,
                             backColor):
        label = []
        if (timeline.name):
            label.append(timeline.name.upper())
        for component in timeline.components:
            label.append(component.upper())

        if (label):
            self._textBlock(x=areaX,
                            y=areaY,
                            w=areaW,
                            h=areaH,
                            textLines=label,
                            color=Color.WHITE,
                            font='condensed',
                            size=PDFPrinter.FONT_XS,
                            align='C',
                            backColor=backColor)
        if (timeline.fromTime):
            textSize = self._measure(text=timeline.fromTime,
                                     font='italic',
                                     size=PDFPrinter.FONT_S)
            self._text(x=areaX,
                       y=areaY - 1,
                       w=areaW / 2,
                       h=textSize.y,
                       text=timeline.fromTime,
                       color=Color.WHITE,
                       font='italic',
                       size=PDFPrinter.FONT_S,
                       align='L',
                       backColor=backColor)
        if (timeline.toTime):
            textSize = self._measure(text=timeline.toTime,
                                     font='italic',
                                     size=PDFPrinter.FONT_S)
            self._text(x=areaX + areaW / 2,
                       y=areaY + areaH - textSize.y + 1,
                       w=areaW / 2,
                       h=textSize.y,
                       text=timeline.toTime,
                       color=Color.WHITE,
                       font='italic',
                       size=PDFPrinter.FONT_S,
                       align='R',
                       backColor=backColor)

    def printNotes(self, notes):
        x = PDFPrinter.MARGIN
        y = PDFPrinter.MARGIN + PDFPrinter.HEADER_HEIGHT + PDFPrinter.TABLE_HEIGHT
        w = PDFPrinter.CONTENT_WIDTH
        h = 5

        for note in notes:
            y += h
            self._text(x,
                       y,
                       w,
                       0,
                       text=note,
                       color=Color.BLACK,
                       font='regular',
                       size=self.fontSize,
                       align='L')

    def save(self, fileName):
        self.pdf.output(fileName)

    def _measure(self, text, font, size):
        self.uglyMeasure.set_font(font, size=size)
        self.uglyMeasure.set_xy(0, 0)
        self.uglyMeasure.write(size / 2.83, txt=text)
        sizeX = self.uglyMeasure.get_x()
        self.uglyMeasure.write(size / 2.83, txt="\n")
        sizeY = self.uglyMeasure.get_y()

        result = Size(sizeX, sizeY)
        return result

    def _text(self,
              x,
              y,
              w,
              h,
              text,
              color,
              font,
              size,
              align='C',
              backColor=None):
        self.pdf.set_text_color(color[0], color[1], color[2])
        self.pdf.set_font(font, size=size)
        self.pdf.set_xy(x, y)
        fillBackground = False
        if (backColor):
            self.pdf.set_fill_color(backColor[0], backColor[1], backColor[2])
            fillBackground = True
        self.pdf.cell(w, h, txt=text, ln=1, align=align, fill=fillBackground)

    def _textBlock(self,
                   x,
                   y,
                   w,
                   h,
                   textLines,
                   color,
                   font,
                   size,
                   align='C',
                   backColor=None):
        textH = 0
        texts = []
        for line in textLines:
            textSize = self._measure(text=line, font=font, size=size)
            textSize.y += 1
            textH += textSize.y

            texts.append({"txt": line, "height": textSize.y})

        self.pdf.set_text_color(color[0], color[1], color[2])
        self.pdf.set_font(font, size=size)

        posY = y + (h - textH) / 2

        fillBackground = False
        if (backColor):
            self.pdf.set_fill_color(backColor[0], backColor[1], backColor[2])
            fillBackground = True

        for line in texts:
            self.pdf.set_xy(x, posY)
            self.pdf.cell(w,
                          line["height"],
                          txt=line["txt"],
                          align=align,
                          fill=fillBackground)

            posY += line["height"]

    def _box(self, x, y, w, h, color, lineColor, lineWidth=0):
        style = ''

        if (lineColor):
            self.pdf.set_line_width(lineWidth)
            self.pdf.set_draw_color(lineColor[0], lineColor[1], lineColor[2])
            style += 'D'

        if (color):
            self.pdf.set_fill_color(color[0], color[1], color[2])
            style += 'F'

        self.pdf.rect(x, y, w, h, style)

    def _line(self, x1, y1, x2, y2, color, lineWidth):
        self.pdf.set_line_width(lineWidth)
        self.pdf.set_draw_color(color[0], color[1], color[2])

        self.pdf.line(x1, y1, x2, y2)

    def _findTimeWindow(self, timetable):
        minTime = self._timeToInt("24:00")
        maxTime = self._timeToInt("00:00")

        for daySchedule in timetable.values():
            for personalSchedule in daySchedule.values():
                for schedule in personalSchedule:
                    f = self._timeToInt(schedule.fromTime)
                    t = self._timeToInt(schedule.toTime)

                    minTime = min(minTime, f, t)
                    maxTime = max(maxTime, f, t)

        return {"fromTime": minTime, "toTime": maxTime}

    def _timeToInt(self, str):
        parts = str.split(':')
        return int(parts[0]) * 60 + int(parts[1])
コード例 #35
0
ファイル: flippy.py プロジェクト: ola-ct/flippy
    def process(
        self,
        output_file_name=None,
        dpi=150,
        offset=0,
        fps=10,
        height_mm=50,
        margins=Margin(10, 10, 10, 10),
        paper_format="a4",
    ):
        def draw_raster():
            for ix in range(0, nx + 1):
                xx = x0 + ix * total.width
                pdf.line(xx, y0, xx, y1)
                if offset > 0 and ix != nx:
                    pdf.line(xx + offset, y0, xx + offset, y1)
            for iy in range(0, ny + 1):
                yy = y0 + iy * total.height
                pdf.line(x0, yy, x1, yy)

        height_mm = float(height_mm)
        tmp_files = []
        if self.clip:
            if fps != self.clip.fps:
                if self.verbosity > 0:
                    print "Transcoding from {} fps to {} fps ...".format(self.clip.fps, fps)
                self.clip.write_videofile("tmp.mp4", fps=fps, audio=False)
                tmp_files.append("tmp.mp4")
                self.clip = VideoFileClip("tmp.mp4")
                self.fps = self.clip.fps
                self.frame_count = int(self.clip.duration * self.fps)
            clip_size = Size.from_tuple(self.clip.size)
        elif self.frames:
            clip_size = Size.from_tuple(self.im.size)

        paper = self.PAPER_SIZES[paper_format.lower()]
        printable_area = Size(paper.width - margins.left - margins.right, paper.height - margins.top - margins.bottom)
        frame_mm = Size(height_mm / clip_size.height * clip_size.width, height_mm)
        total = Size(offset + frame_mm.width, frame_mm.height)
        frame = Size(int(frame_mm.width / 25.4 * dpi), int(frame_mm.height / 25.4 * dpi))
        nx = int(printable_area.width / total.width)
        ny = int(printable_area.height / total.height)
        if self.verbosity > 0:
            print "Input:  {} fps, {}x{}, {} frames" "\n        from: {}".format(
                self.fps, clip_size.width, clip_size.height, self.frame_count, self.input_file_name
            )
            print "Output: {}dpi, {}x{}, {:.2f}mm x {:.2f}mm, {}x{} tiles" "\n        to: {}".format(
                dpi, frame.width, frame.height, frame_mm.width, frame_mm.height, nx, ny, output_file_name
            )
        pdf = FPDF(unit="mm", format=paper_format.upper(), orientation="L")
        pdf.set_compression(True)
        pdf.set_title("Funny video")
        pdf.set_author("Oliver Lau <*****@*****.**> - Heise Medien GmbH & Co. KG")
        pdf.set_creator("flippy")
        pdf.set_keywords("flip-book, video, animated GIF")
        pdf.set_draw_color(128, 128, 128)
        pdf.set_line_width(0.1)
        pdf.set_font("Helvetica", "", 12)
        pdf.add_page()
        i = 0
        page = 0
        tx, ty = -1, 0
        x0, y0 = margins.left, margins.top
        x1, y1 = x0 + nx * total.width, y0 + ny * total.height

        if self.clip:
            all_frames = self.clip.iter_frames()
        elif self.frames:
            all_frames = AnimatedGif(self.im)
        else:
            all_frames = []
        for f in all_frames:
            ready = float(i + 1) / self.frame_count
            if self.verbosity:
                sys.stdout.write("\rProcessing frames |{:30}| {}%".format("X" * int(30 * ready), int(100 * ready)))
                sys.stdout.flush()
            tx += 1
            if type(f) == GifImagePlugin.GifImageFile:
                f.putpalette(self.palette)
                self.last_im.paste(f)
                im = self.last_im.convert("RGBA")
            else:
                im = Image.fromarray(f)
                im.thumbnail(frame.to_tuple())
            if tx == nx:
                tx = 0
                ty += 1
                if ty == ny:
                    ty = 0
                    draw_raster()
                    pdf.add_page()
                    page += 1
            temp_file = "tmp-{}-{}-{}.jpg".format(page, tx, ty)
            im.save(temp_file)
            tmp_files.append(temp_file)
            x = x0 + tx * total.width
            y = y0 + ty * total.height
            pdf.image(temp_file, x=x + offset, y=y, w=frame_mm.width, h=frame_mm.height)
            text = Point(x, y + frame_mm.height - 2)
            if offset > 0:
                pdf.rotate(90, text.x, text.y)
                pdf.text(text.x, text.y + 5, "{}".format(i))
                pdf.rotate(0)
            i += 1

        if y != 0 and x != 0:
            draw_raster()

        if self.verbosity > 0:
            print "\nGenerating PDF ..."
        pdf.output(name=output_file_name)
        if self.verbosity > 0:
            print "Removing temporary files ..."
        for temp_file in tmp_files:
            os.remove(temp_file)