Beispiel #1
0
def FindMaxFontHeight (Font, FontStyle, BaseHeight, MaxWidth, MaxTestString):

	fontScale = MAX_FONT_SCALE
	fontWidth = 0
	fontSize  = 0

	pdfFile = FPDF (orientation=PORTRAIT, unit='in', format='letter')

	for i in range (1, MAX_WIDTH_ITERS):

		fontScale1 = fontScale - MAX_FONT_SCALE / (2 ** i)
		fontScale2 = fontScale + MAX_FONT_SCALE / (2 ** i)

		pdfFile.set_font (Font, style=FontStyle, size=fontScale1 * BaseHeight)
		testFontWidth1 = pdfFile.get_string_width (MaxTestString)

		pdfFile.set_font (Font, style=FontStyle, size=fontScale2 * BaseHeight)
		testFontWidth2 = pdfFile.get_string_width (MaxTestString)

		sizeDifference1 = abs (MaxWidth - testFontWidth1)
		sizeDifference2 = abs (MaxWidth - testFontWidth2)

		#print (i, fontScale1, testFontWidth1, sizeDifference1)
		#print (i, fontScale2, testFontWidth2, sizeDifference2)

		if (sizeDifference1 < sizeDifference2):
			fontScale = fontScale1
			fontWidth = testFontWidth1
			fontSize  = fontScale1 * BaseHeight
		else:
			fontScale = fontScale2
			fontWidth = testFontWidth2
			fontSize  = fontScale2 * BaseHeight

	return (fontScale, fontWidth, fontSize)
Beispiel #2
0
def dotest(outputname, nostamp):

    pdf = FPDF()
    pdf.set_font('Arial','',14)
    s = 'Texto largo que no cabe en esta celda pero que será ajustado'
    w = pdf.get_string_width(s)
    if not nostamp:
        print (s, w)
    assert round(w, 2) == 135.90
    pdf.add_font('DejaVu', '', './font/DejaVuSans.ttf', uni=True)
    pdf.set_font('DejaVu', '', 14)
    s = u('Texto largo que no cabe en esta celda pero que será ajustado') 
    w = pdf.get_string_width(s)
    if not nostamp:
        print (s, w)
    assert round(w, 2) == 153.64
Beispiel #3
0
def test_named_actions(tmp_path):
    pdf = FPDF()
    pdf.set_font("Helvetica", size=24)
    pdf.add_page()
    pdf.text(x=80, y=140, txt="First page")
    pdf.add_page()
    pdf.underline = True
    for x, y, named_action in (
        (40, 80, "NextPage"),
        (120, 80, "PrevPage"),
        (40, 200, "FirstPage"),
        (120, 200, "LastPage"),
    ):
        pdf.text(x=x, y=y, txt=named_action)
        pdf.add_action(
            NamedAction(named_action),
            x=x,
            y=y - pdf.font_size,
            w=pdf.get_string_width(named_action),
            h=pdf.font_size,
        )
    pdf.underline = False
    pdf.add_page()
    pdf.text(x=80, y=140, txt="Last page")
    assert_pdf_equal(pdf, HERE / "named_actions.pdf", tmp_path)
Beispiel #4
0
def dotest(outputname, nostamp):

    pdf = FPDF()
    pdf.set_font('Arial', '', 14)
    s = 'Texto largo que no cabe en esta celda pero que será ajustado'
    w = pdf.get_string_width(s)
    if not nostamp:
        print(s, w)
    assert round(w, 2) == 135.90
    pdf.add_font('DejaVu', '', './font/DejaVuSans.ttf', uni=True)
    pdf.set_font('DejaVu', '', 14)
    s = u('Texto largo que no cabe en esta celda pero que será ajustado')
    w = pdf.get_string_width(s)
    if not nostamp:
        print(s, w)
    assert round(w, 2) == 153.64
Beispiel #5
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")
def create_report_pdf(report, report_file_path):
    pdf = FPDF()
    pdf.add_page()
    page_width = pdf.w - 2 * pdf.l_margin

    pdf.set_font('Times', 'B', 1)
    pdf.cell(page_width, 0.0, 'Virus Scan Report', align='C')
    pdf.ln(10)

    pdf.set_font('Courier', '', 8)
    col_width = page_width / 4
    pdf.ln(1)
    th = pdf.font_size

    col_width1 = pdf.get_string_width(str(
        report[0]['hash_value'])) + 2 * pdf.c_margin
    col_width2 = pdf.get_string_width(
        report[0]['detection_name']) + 6 * pdf.c_margin
    col_width3 = pdf.get_string_width('Number of Engines') + 2 * pdf.c_margin
    col_width4 = pdf.get_string_width(str(
        report[0]['scan_date'])) + 2 * pdf.c_margin

    pdf.cell(col_width1, th, 'Resource', border=1)
    pdf.cell(col_width2, th, 'Detection Name', border=1)
    pdf.cell(col_width3, th, 'Number of Engines', border=1)
    pdf.cell(col_width4, th, 'Scan Date', border=1)
    pdf.ln(th)

    for row in report:
        if row and len(row.values()) > 4:
            pdf.cell(col_width1, th, str(row['hash_value']), border=1)
            pdf.cell(col_width2, th, str(row['detection_name']), border=1)
            pdf.cell(col_width3, th, str(row['number_of_engines']), border=1)
            pdf.cell(col_width4, th, str(row['scan_date']), border=1)
            pdf.ln(th)
    pdf.ln(10)

    pdf.set_font('Times', '', 10.0)
    pdf.cell(page_width, 0.0, '- end of report -', align='C')

    return pdf.output(report_file_path).encode('latin-1')
Beispiel #7
0
def test_launch_action(tmp_path):
    pdf = FPDF()
    pdf.set_font("Helvetica", size=24)
    pdf.add_page()
    x, y, text = 80, 140, "Launch action"
    pdf.text(x=x, y=y, txt=text)
    pdf.add_action(
        LaunchAction(file="goto_action.pdf"),
        x=x,
        y=y - pdf.font_size,
        w=pdf.get_string_width(text),
        h=pdf.font_size,
    )
    assert_pdf_equal(pdf, HERE / "launch_action.pdf", tmp_path)
def test_simple_text_annotation(tmp_path):
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("Helvetica", size=24)
    text = "A few words forming a sentence."
    pdf.text(x=50, y=150, txt=text)
    width = pdf.get_string_width(text)
    pdf.text_annotation(
        x=50,
        y=150 - pdf.font_size,
        w=width,
        h=pdf.font_size,
        text="The quick brown fox ate the lazy mouse . ",
    )
    assert_pdf_equal(pdf, HERE / "simple_text_annotation.pdf", tmp_path)
Beispiel #9
0
 def test_pdf_cell(self):
     pdf=FPDF()
     pdf.c_margin = 0.0
     pdf.add_font('symbol','','font/DejaVuSans.ttf',uni=True)
     pdf.add_page()
     f = 0.81
     font_name = 'times'
     
     text = 'small text'
     pdf.set_font(font_name,'',12)
     x,y = pdf.get_x(), pdf.get_y()
     w = pdf.get_string_width(text)
     h = pdf.font_size_pt / pdf.k
     pdf.cell(w, h, text)
     pdf.rect(x, y, w, h, '')
     pdf.line(x, y + f * h, x + w, y + f * h)
     
     text = 'Large text'
     pdf.set_font(font_name,'',24)
     x,y = pdf.get_x(), pdf.get_y()
     w = pdf.get_string_width(text)
     h = pdf.font_size_pt / pdf.k
     pdf.cell(w,h, text)
     pdf.rect(x, y, w, h, '')
     pdf.line(x, y + f * h, x + w, y + f * h)
     
     text = 'Larger text'
     pdf.set_font(font_name,'',48)
     x,y = pdf.get_x(), pdf.get_y()
     w = pdf.get_string_width(text)
     h = pdf.font_size_pt / pdf.k
     pdf.cell(w,h, text)
     pdf.rect(x, y, w, h, '')
     pdf.line(x, y + f * h, x + w, y + f * h)
     
     pdf.output('out/fpdf/test_pdf_cell.pdf', 'F')
Beispiel #10
0
def test_goto_remote_action(tmp_path):
    pdf = FPDF()
    pdf.set_font("Helvetica", size=24)
    pdf.add_page()
    x, y, text = 80, 140, "GoTo-Remote action"
    pdf.text(x=x, y=y, txt=text)
    dest = DestinationXYZ(page=1, page_as_obj_id=False).as_str(pdf)
    pdf.add_action(
        GoToRemoteAction("goto_action.pdf", dest=dest),
        x=x,
        y=y - pdf.font_size,
        w=pdf.get_string_width(text),
        h=pdf.font_size,
    )
    assert_pdf_equal(pdf, HERE / "goto_remote_action.pdf", tmp_path)
Beispiel #11
0
def test_goto_action(tmp_path):
    pdf = FPDF()
    pdf.set_font("Helvetica", size=24)
    pdf.add_page()
    x, y, text = 80, 140, "GoTo action"
    pdf.text(x=x, y=y, txt=text)
    pdf.add_action(
        GoToAction(dest=DestinationXYZ(page=2).as_str(pdf)),
        x=x,
        y=y - pdf.font_size,
        w=pdf.get_string_width(text),
        h=pdf.font_size,
    )
    pdf.add_page()
    pdf.text(x=80, y=140, txt="Page 2")
    assert_pdf_equal(pdf, HERE / "goto_action.pdf", tmp_path)
Beispiel #12
0
def generatePDF(userid, maxportfolioid):
    sql_statement = "SELECT symbol, purchase_price, lot_size from portfolio where userid = %s and portfolioid = %s"
    data = (userid, maxportfolioid)
    connection = DBconnection(sql_statement, data)
    Symbol = [i[0] for i in connection]
    PurchasePrice = [i[1] for i in connection]
    Lot_Size = [i[2] for i in connection]

    pdf = FPDF()

    #header of the pdf file
    header = 'Specifically curated for ' + str(userid)
    pdf.add_page()
    pdf.set_font('Arial', 'B', 16)
    w = pdf.get_string_width(header) + 6
    pdf.set_x((210 - w) / 2)
    pdf.cell(w, 9, header, 0, 0, 'C')
    pdf.line(20, 18, 210 - 20, 18)

    pdf.ln(10)
    pdf.set_font('Times', '', 12)
    pdf.multi_cell(
        0, 5,
        'Here is a list of suggested financial instruments for your peruse.')

    for i in range(len(Symbol)):
        pdf.ln()
        pdf.set_font('Arial', '', 12)
        pdf.set_fill_color(200, 220, 255)
        pdf.cell(
            0, 6, 'Financial Instrument ' + str(i + 1) + ": " +
            str(Symbol[i]) + " Unit Price " + str(PurchasePrice[i]) +
            " Lot Size " + str(Lot_Size[i]), 0, 1, 'L', 1)

        pdf.ln()
        pdf.set_font('Courier', 'B', 12)
        pdf.multi_cell(
            0, 5, 'A detailed analysis on ' + Symbol[i] + '---------------')

    #pdf.set_y(0) #on top of the page
    pdf.set_y(-30)  #30 CM from the bottom of the page
    pdf.set_font('Arial', '', 8)
    pdf.set_text_color(0)
    pdf.cell(0, 5, 'Page ' + str(pdf.page_no()), 0, 0, 'C')

    pdf.output(str(userid) + '.pdf', 'F')
    return pdf
Beispiel #13
0
def test_link_alt_text(tmp_path):
    """
    It can be tested that the reference file for this test
    has the link description read out loud by the NVDA screen reader
    when opened with Adobe Acrobat Reader.
    """
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("helvetica", size=24)
    text = "PyFPDF/fpdf2"
    pdf.text(x=80, y=150, txt=text)
    width = pdf.get_string_width(text)
    line_height = 10
    pdf.link(
        x=80,
        y=150 - line_height,
        w=width,
        h=line_height,
        link="https://github.com/PyFPDF/fpdf2",
        alt_text="GitHub repository of the fpdf2 library",
    )
    assert_pdf_equal(pdf, HERE / "link_alt_text.pdf", tmp_path)
class FNC41Encoder():
    def __init__(self):
        self.encoded_msg = None

        self.pdf = FPDF()
        self.pdf.add_page()

        if os.path.exists(PIGPEN_FONT_FILE):
            self.pdf.add_font(PIGPEN_FONT, '', PIGPEN_FONT_FILE, uni=True)
        else:
            print('[!] pigpen font not found, pigpen not supported')

        if os.path.exists(BRAILLE_FONT_FILE):
            self.pdf.add_font(BRAILLE_FONT, '', BRAILLE_FONT_FILE, uni=True)
        else:
            print('[!] braille font not found, braille not supported')

        self.pdf.set_font('Times', '', 16)

    def save(self, outfile):
        self.pdf.output(outfile, 'F')
        print("message written to '{}'".format(outfile))

    def write_msg(self, msg, encode_type='decimal', wrap=True):
        self.encode_type = encode_type

        msg = msg.upper()

        if encode_type == 'decimal':
            self._encode_decimal(msg)

        if encode_type == 'morse':
            self._encode_morse(msg)

        if encode_type == 'braille':
            self.encoded_msg = msg

        if encode_type == 'binary':
            self._encode_binary(msg)

        if encode_type == 'pigpen':
            self.encoded_msg = msg

        if encode_type == 'hex':
            self._encode_hex(msg)

        if self.encoded_msg:
            self.pdf.set_font('Times', '', 16)
            if self.encode_type == 'pigpen' and PIGPEN_SUPPORTED:
                self.pdf.set_font(PIGPEN_FONT, '', 16)
            if self.encode_type == 'braille' and BRAILLE_SUPPORTED:
                self.pdf.set_font(BRAILLE_FONT, '', 16)
            # fpdf.cell(w, h = 0, txt = '', border = 0, ln = 0, align = '', fill = False, link = '')
            len = self.pdf.get_string_width(self.encoded_msg) + 5
            if wrap:
                self.pdf.multi_cell(0, 10, self.encoded_msg)
            else:
                self.pdf.cell(len, 10, self.encoded_msg, 0)
        else:
            raise Exception("call the encode() method first")

    def write_msg_random(self, msg):
        counter = 0
        for s in msg.split(' '):
            t = random.choice(SUPPORTED_TYPES)
            self.write_msg(s, t, False)
            counter += 1
            if counter % 3 == 0:
                self.pdf.ln(10)

    def _encode_decimal(self, msg):
        l = []
        for c in msg:
            if c in ALPHA_TO_NUM.keys():
                l.append(str(ALPHA_TO_NUM[c]))
            else:
                l.append(c)
        self.encoded_msg = " ".join(l)

    def _encode_hex(self, msg):
        l = []
        for c in msg:
            if c in ALPHA_TO_NUM.keys():
                l.append(hex(ALPHA_TO_NUM[c]))
            else:
                l.append(c)
        self.encoded_msg = " ".join(l)

    def _encode_morse(self, msg):
        l = []
        for c in msg:
            if c in MORSE_CODE_DICT.keys():
                l.append(str(MORSE_CODE_DICT[c]))
            else:
                l.append(c)
        self.encoded_msg = " ".join(l)

    def _encode_braille(self, msg):
        l = ''
        for c in msg:
            if c in BRAILLE.keys():
                l += BRAILLE[c]
            else:
                l += c
        self.encoded_msg = l

    def _encode_binary(self, msg):
        l = []
        for c in msg:
            if c in ALPHA_TO_NUM.keys():
                l.append(str(bin(ALPHA_TO_NUM[c]).lstrip('0b')))
            else:
                l.append(c)
        self.encoded_msg = " ".join(l)
Beispiel #15
0
    def exportierenPDF(self, path, datum):
        font = "Helvetica"

        # basic pdf setup
        #----------------------------------------
        pdf = FPDF()
        pdf.set_font(font, size=12)
        pdf.add_page()
        row_height = pdf.font_size * 1.5
        #----------------------------------------

        # set title
        #----------------------------------------
        datumStr = str(Tools.convertWeekdayGerman(
            datum.dayOfWeek())) + ', ' + str(datum.day()) + '.' + str(
                datum.month()) + '.' + str(datum.year())
        title = "Vertretungsplan von " + datumStr
        pdf.set_font(font, 'B', 14)
        w = pdf.get_string_width(title) + 6
        pdf.set_x((210 - w) / 2)
        pdf.cell(w, 9, txt=title, border=0)
        pdf.ln(10)
        #----------------------------------------

        if datum.Informationen() != "":
            # set info title
            #----------------------------------------
            title = "allgemeine Informationen"
            pdf.set_font(font, '', 12)
            w = pdf.get_string_width(title) + 6
            pdf.set_x(10)
            pdf.cell(w, 9, txt=title, border=0)
            pdf.ln(10)
            #----------------------------------------

            # set info
            #----------------------------------------
            pdf.set_font(font, 'I', 11)
            w = pdf.get_string_width(datum.Informationen()) + 6
            pdf.set_x(10)
            pdf.cell(w, 9, txt=datum.Informationen(), border=1)
            pdf.ln(10)
            pdf.ln(10)
            #----------------------------------------

        # set table
        #----------------------------------------
        # content = [((("str",width),("str",width),("str",width),("str",width),("str",width)),("Art","typ",size)),
        #            ((("str",width),("str",width),("str",width),("str",width),("str",width)),("Art","typ",size))]
        content = []
        t1 = ("Klasse", pdf.get_string_width("Klasse") + 6)
        t2 = ("Std", pdf.get_string_width("Std") + 6)
        t3 = ("Fach", pdf.get_string_width("Fach") + 6)
        t4 = ("Vertretung", pdf.get_string_width("Vertretung") + 6)
        t5 = ("Raum", pdf.get_string_width("Raum") + 6)
        t6 = ("Bemerkung", pdf.w - t1[1] - t2[1] - t3[1] - t4[1] - t5[1] - 20)
        content.append(((t1, t2, t3, t4, t5, t6), (font, 'B', 13)))
        for stunde in Vertretungsstunde.VertretungsstundenListe():
            if stunde.datum == datum:
                klasse = (str(stunde.Klasse()), t1[1])
                stu = (str(stunde.Stunde()), t2[1])
                fach = (str(stunde.Fach()), t3[1])
                lehrer = (str(stunde.Lehrer()), t4[1])
                raum = (str(stunde.Raum()), t5[1])
                bemerkung = (stunde.Bemerkung(), t6[1])
                item = (klasse, stu, fach, lehrer, raum, bemerkung)
                content.append((item, (font, '', 12)))

        for row in content:
            font = row[1][0]
            style = row[1][1]
            size = row[1][2]
            pdf.set_font(font, style, size)
            row_height = pdf.font_size * 1.5
            for item in row[0]:
                pdf.cell(item[1], row_height, txt=item[0], border=1)
            pdf.ln(row_height)
        #----------------------------------------

        pdf.output(path)
Beispiel #16
0
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
Beispiel #17
0
def create_form1(personal_info, skills, languages, sum_text, work_experience,
                 education, awards, font):
    pdf = FPDF()
    pdf.add_page()
    '''HEADER'''
    pdf.set_font("Times", size=25, style="B")
    pdf.set_fill_color(16, 78, 139)
    pdf.set_text_color(255, 255, 255)
    pdf.cell(0, 10, " " + personal_info[0], fill=True, ln=1)
    pdf.set_font("courier", size=15, style="B")

    pdf.set_text_color(255, 255, 255)
    pdf.cell(0, 10, " " + personal_info[1], fill=True, ln=2)
    '''PERSONAL INFO'''
    pdf.set_xy(140, 30)
    pdf.set_font("arial", size=18, style="B")
    pdf.set_text_color(28, 28, 28)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 15, "Personal Info", fill=True, ln=1)

    pdf.set_draw_color(128, 128, 128)
    pdf.line(140, 42, 190, 42)

    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=14, style="B")
    pdf.set_text_color(0, 0, 0)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 10, "Address", fill=True, ln=1)
    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=12)
    pdf.set_text_color(99, 99, 99)
    pdf.cell(0,
             6,
             personal_info[2][0] + "_" + personal_info[2][1],
             fill=True,
             ln=1)
    pdf.set_xy(140, pdf.get_y())
    pdf.cell(0, 6, personal_info[2][2], fill=True, ln=1)
    pdf.set_xy(140, pdf.get_y())
    pdf.cell(0, 6, personal_info[2][3], fill=True, ln=1)

    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=14, style="B")
    pdf.set_text_color(0, 0, 0)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 10, "Phone", fill=True, ln=1)
    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=12)
    pdf.set_text_color(99, 99, 99)
    pdf.cell(0, 6, personal_info[3], fill=True, ln=1)

    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=14, style="B")
    pdf.set_text_color(0, 0, 0)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 10, "E_mail", fill=True, ln=1)
    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=12)
    pdf.set_text_color(99, 99, 99)
    pdf.cell(0, 6, personal_info[4], fill=True, ln=1)

    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=14, style="B")
    pdf.set_text_color(0, 0, 0)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 10, "Github", fill=True, ln=1)
    for i in range(len(personal_info[5]) - 1):
        link = "http://github/" + personal_info[5][0] + "/" + personal_info[5][
            i + 1]
        pdf.set_xy(140, pdf.get_y())
        pdf.set_font("arial", size=12)
        pdf.set_text_color(99, 99, 99)
        pdf.cell(0, 6, personal_info[5][i + 1], fill=True, ln=1, link=link)
    '''SKILLS'''
    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=18, style="B")
    pdf.set_text_color(28, 28, 28)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 15, "Skills", fill=True, ln=1)

    pdf.set_draw_color(128, 128, 128)
    pdf.line(140, pdf.get_y() - 4, 190, pdf.get_y() - 4)

    for i in range(len(skills[0])):
        pdf.set_fill_color(141, 238, 238)
        pdf.set_xy(140, pdf.get_y())
        pdf.set_font("arial", size=14)
        pdf.set_text_color(99, 99, 99)
        pdf.cell(0, 15, skills[0][i], fill=True, ln=1)
        x = 170
        for j in range(5):
            if j < skills[1][i]:
                pdf.set_fill_color(0, 0, 139)
                pdf.ellipse(x, pdf.get_y() - 5, 4, 4, style="F")
            else:
                pdf.set_fill_color(255, 255, 255)
                pdf.ellipse(x, pdf.get_y() - 5, 4, 4, style="F")
            x = x + 5
    '''LANGUAGES'''
    pdf.set_xy(140, pdf.get_y())
    pdf.set_font("arial", size=18, style="B")
    pdf.set_text_color(28, 28, 28)
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 15, "Languages", fill=True, ln=1)

    pdf.set_draw_color(128, 128, 128)
    pdf.line(140, pdf.get_y() - 3, 190, pdf.get_y() - 3)

    for i in range(len(languages[0])):
        pdf.set_fill_color(141, 238, 238)
        pdf.set_xy(140, pdf.get_y())
        pdf.set_font("arial", size=14)
        pdf.set_text_color(99, 99, 99)
        pdf.cell(0, 15, languages[0][i], fill=True, ln=1)
        x = 170
        for j in range(5):
            if j < languages[1][i]:
                pdf.set_fill_color(0, 0, 139)
                pdf.ellipse(x, pdf.get_y() - 5, 4, 4, style="F")
            else:
                pdf.set_fill_color(255, 255, 255)
                pdf.ellipse(x, pdf.get_y() - 5, 4, 4, style="F")
            x = x + 5

    pdf.set_xy(140, pdf.get_y())
    pdf.set_fill_color(141, 238, 238)
    pdf.cell(0, 15, "", fill=True, ln=1)
    '''SUMMARY TEXT'''
    x = 10
    y = 34
    pdf.set_font(font[0], size=font[1])
    pdf.set_text_color(128, 128, 128)
    for i in range(len(sum_text)):
        if i == 0:
            pass
        else:
            if x + pdf.get_string_width(sum_text[i]) > 115:
                x = 10
                y = y + font[1] / 2
            else:
                x = x + pdf.get_string_width(sum_text[i - 1]) + 2

        pdf.set_xy(x, y)
        pdf.cell(
            pdf.get_string_width(sum_text[i]) + 1, 5, sum_text[i], 0, 1, "C")
    '''WORK EXPERIENCE'''
    pdf.set_xy(10, pdf.get_y())
    pdf.set_font("arial", size=16)
    pdf.set_text_color(0, 0, 0)
    pdf.cell(20, 20, "Work Experience", fill=False, ln=1)

    pdf.set_draw_color(128, 128, 128)
    pdf.line(10, pdf.get_y() - 5, 130, pdf.get_y() - 5)

    for i in range(len(work_experience[1])):
        pdf.set_xy(10, pdf.get_y())
        pdf.set_font(font[0], size=font[1], style="B")
        pdf.set_text_color(0, 0, 0)
        pdf.cell(20, 10, work_experience[0][i], fill=False, ln=1)
        pdf.set_xy(40, pdf.get_y() - 10)
        pdf.cell(20, 10, work_experience[1][i], fill=False, ln=1)
        x = 40
        y = pdf.get_y()
        pdf.set_font(font[0], size=font[1])
        pdf.set_text_color(128, 128, 128)
        for j in range(len(work_experience[2][i])):
            if j == 0:
                pass
            else:
                if x + pdf.get_string_width(work_experience[2][i][j]) > 115:
                    x = 40
                    y = y + font[1] / 2
                else:
                    x = x + pdf.get_string_width(
                        work_experience[2][i][j - 1]) + 2

            pdf.set_xy(x, y)
            pdf.cell(
                pdf.get_string_width(work_experience[2][i][j]) + 1, 5,
                work_experience[2][i][j], 0, 1, "C")
    '''EDUCATION'''
    pdf.set_xy(10, pdf.get_y())
    pdf.set_font("arial", size=16)
    pdf.set_text_color(0, 0, 0)
    pdf.cell(20, 20, "Education", fill=False, ln=1)

    pdf.set_draw_color(128, 128, 128)
    pdf.line(10, pdf.get_y() - 5, 130, pdf.get_y() - 5)

    for i in range(len(education[1])):
        pdf.set_xy(10, pdf.get_y())
        pdf.set_font(font[0], size=font[1], style="B")
        pdf.set_text_color(0, 0, 0)
        pdf.cell(20, 10, education[0][i], fill=False, ln=1)
        pdf.set_xy(40, pdf.get_y() - 10)
        pdf.cell(20, 10, education[1][i], fill=False, ln=1)
        pdf.set_xy(40, pdf.get_y() - 3)
        pdf.cell(20, 10, "GPA  " + education[2][i], fill=False, ln=1)
        x = 40
        y = pdf.get_y()
        pdf.set_font(font[0], size=font[1])
        pdf.set_text_color(128, 128, 128)
        for j in range(len(education[3][i])):
            if j == 0:
                pass
            else:
                if x + pdf.get_string_width(education[3][i][j]) > 115:
                    x = 40
                    y = y + font[1] / 2
                else:
                    x = x + pdf.get_string_width(education[3][i][j - 1]) + 2

            pdf.set_xy(x, y)
            pdf.cell(
                pdf.get_string_width(education[3][i][j]) + 1, 5,
                education[3][i][j], 0, 1, "C")
    '''AWARDS AND HONORS'''
    pdf.set_xy(10, pdf.get_y())
    pdf.set_font("arial", size=16)
    pdf.set_text_color(0, 0, 0)
    pdf.cell(20, 20, "Awards and Honors", fill=False, ln=1)

    pdf.set_draw_color(128, 128, 128)
    pdf.line(10, pdf.get_y() - 5, 130, pdf.get_y() - 5)

    for i in range(len(awards[1])):
        pdf.set_xy(10, pdf.get_y())
        pdf.set_font(font[0], size=font[1], style="B")
        pdf.set_text_color(0, 0, 0)
        pdf.cell(20, 5, awards[0][i], fill=False, ln=1)
        pdf.set_font(font[0], size=font[1])
        pdf.set_text_color(128, 128, 128)
        pdf.set_xy(40, pdf.get_y() - 5)
        pdf.cell(20, 5, awards[1][i], fill=False, ln=1)

    pdf.set_display_mode("fullpage")
    pdf.output(f"{name}_form1.pdf")
Beispiel #18
0
fonts = [
    ("Helvetica", "", "helvetica"),
    ("Helvetica", "B", "helvetica_bold"),
    ("Helvetica", "BI", "helvetica_bold_oblique"),
    ("Helvetica", "I", "helvetica_oblique"),
    ("Symbol", "", "symbol"),
    ("Times", "", "times"),
    ("Times", "B", "times_bold"),
    ("Times", "BI", "times_bold_italic"),
    ("Times", "I", "times_italic"),
    ("ZapfDingbats", "", "zapfdingbats"),
    ("Courier", "", "courier"),
    # All the courier fonts are the same width
    #("Courier", "B", "courier_bold"),
    #("Courier", "BI", "courier_bold_oblique"),
    #("Courier", "I", "courier_oblique"),
]

for f in fonts:
    pdf.set_font(f[0], f[1], 14)

    sys.stdout.write("static const uint16_t %s_widths[256] = {\n" % (f[2]))
    for i in range(0, 256):
        if i % 14 == 0:
            sys.stdout.write("    ")
        width = int(pdf.get_string_width(chr(i)) * 2.83465 * 72)
        sys.stdout.write("%d, " % width)
        if i % 14 == 13:
            sys.stdout.write("\n")
    sys.stdout.write("\n};\n\n")
def pdf_generator(name1, dbacno, dbifsc, dbpan, dbphno, dbemail, dbloc,
                  data_table):
    print(data_table)
    pdf = FPDF()
    pdf.add_page()
    header = 'Fuzzy Logic Training Institute'
    pdf.image('./static/images/logo.png', 3, 8, 20)
    pdf.set_font('Times', 'B', 26)
    w = pdf.get_string_width(header) + 6
    pdf.set_x((210 - w) / 2)
    pdf.set_fill_color(251, 235, 101)
    pdf.cell(w, 9, header, 2, 1, 'L', 1)
    # pdf.line(50, 18, 210 - 50, 18)
    # pdf.set_font('Arial', 'B', 20)
    # pdf.cell(200, 10, 'Genesis Invoice Generation', ln=1, align='C')
    pdf.set_font('Times', 'B', 12)
    pdf.ln()
    pdf.ln()
    pdf.cell(40, 10, 'Name (As per bank account): ' + str(name1), ln=4)
    pdf.cell(40, 10, 'Bank account number: ' + str(dbacno), ln=5)
    pdf.cell(40, 10, 'IFSC Code: ' + str(dbifsc), ln=6)
    pdf.cell(40, 10, 'Pan Number: ' + str(dbpan), ln=7)
    pdf.cell(40, 10, 'Phone Number: ' + str(dbphno), ln=8)
    pdf.cell(40, 10, 'Email Id: ' + str(dbemail), ln=9)
    pdf.cell(40, 10, 'Base Location : ' + str(dbloc), ln=10)
    pdf.cell(40, 10, '', ln=11)

    spacing = 1
    data_table.insert(
        0,
        ['Date', 'College', 'Fees/day', 'Travel Allowance', 'Food Allowance'])

    col_width = pdf.w / 5.7
    row_height = pdf.font_size * 1.8
    for row in data_table:
        for item in row:
            pdf.cell(col_width,
                     row_height * spacing,
                     txt=str(item),
                     border=1,
                     align="C")
        pdf.ln(row_height * spacing)

    ren = 0
    travel = 0
    food = 0
    for i in data_table[1:]:
        ren += i[2]
        travel += i[3]
        food += i[4]
    grand = ren + travel + food

    pdf.cell(col_width * 2,
             row_height * spacing,
             txt="Total",
             border=1,
             align="C")
    pdf.cell(col_width,
             row_height * spacing,
             txt=str(ren),
             border=1,
             align="C")
    pdf.cell(col_width,
             row_height * spacing,
             txt=str(travel),
             border=1,
             align="C")
    pdf.cell(col_width,
             row_height * spacing,
             txt=str(food),
             border=1,
             align="C")
    pdf.ln(row_height * spacing)
    pdf.cell(col_width * 4,
             row_height * spacing,
             txt="Grand Total",
             border=1,
             align="C")
    pdf.cell(col_width,
             row_height * spacing,
             txt=str(int(grand)),
             border=1,
             align="C")
    pdf.ln(row_height * spacing)
    pdf.cell(col_width * 5,
             row_height * spacing,
             txt="Rupees: " + str(int(grand)),
             border=1,
             align="C")

    pdf.output("invoice/Invoice.pdf")
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)
Beispiel #21
0
from fpdf import FPDF

#font_file_name = 'font/mathjax_amsregular.ttf'
font_file_name = 'font/MathJax/ttf/mathjax_size4regular.ttf'  # large brackets and large square root

size = 10
pdf = FPDF()
pdf.add_page()
pdf.add_font('jax', '', font_file_name, uni=True)

lb = 0
for n in range(0xeffc):
    c = unichr(n)
    pdf.set_font('jax', '', size)
    w = pdf.get_string_width(c)
    if w > 0:
        pdf.set_font('times', '', size)
        pdf.cell(13, 10, hex(n) + ': ')
        pdf.set_font('jax', '', size)
        pdf.cell(10, 10, c)
        if lb == 6:
            pdf.ln(10)
            lb = 0
        else:
            lb += 1
    #print unichr(i)
pdf.output('out/font_view.pdf', 'F')
print 'done'
Beispiel #22
0
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
Beispiel #23
0
def createPDFacordo(db: Session, vaga: PessoaProjeto):

    contratado = get_pessoa_by_id(db, vaga.pessoa_id)
    projeto = get_projeto(db, vaga.projeto_id)
    contratante = get_pessoa_by_id(db, projeto.pessoa_id)
    acordo = get_tipo_acordo_by_id(db, vaga.tipo_acordo_id)
    # Observar após refatoração
    if (vaga.papel_id == 1):
        papel = "aliado"
    elif (vaga.papel_id == 2):
        papel = "colaborador"
    elif (vaga.papel_id == 3):
        papel = "idealizador"

    pdf = FPDF()
    pdf.add_page(orientation='P')

    espacamento = 8

    pdf.set_margins(20, 20, 20)
    pdf.set_font("Arial", 'B', size=16)
    # Espaço de formatação
    pdf.cell(20, 20, txt='', ln=1)
    pdf.cell(175, 12, txt='ACORDO DE PRESTAÇÃO DE SERVIÇOS', ln=1, align="C")
    pdf.set_font("Arial", 'B', size=14)
    pdf.cell(175,
             12,
             txt='IDENTIFICAÇÃO DAS PARTES CONTRATANTES',
             ln=1,
             align="L")

    # Corpo
    pdf.set_font("Arial", 'B', size=12)
    pdf.cell(pdf.get_string_width('CONTRATANTE: '),
             espacamento,
             txt='CONTRATANTE: ',
             align="L")
    pdf.set_font("Arial", size=12)
    w = pdf.get_x()
    pdf.cell(w, espacamento, txt=contratante.nome, ln=1, align="L")
    pdf.set_font("Arial", 'B', size=12)
    pdf.cell(pdf.get_string_width('CONTRATADO: '),
             espacamento,
             txt='CONTRATADO: ',
             align="L")
    pdf.set_font("Arial", size=12)
    w = pdf.get_x()
    pdf.cell(w, espacamento, txt=contratado.nome, ln=1, align="L")
    pdf.cell(20, 5, txt='', ln=1)
    pdf.multi_cell(
        0,
        espacamento,
        txt=
        'As partes acima identificadas têm, entre si, justo e acertado o presente Acordo de Prestação de Serviços, que se regerá pelo objeto do acordo pelas condições de remuneração, forma e termo de pagamento descritas no presente.',
        align="J")

    pdf.set_font("Arial", 'B', size=14)

    pdf.cell(175, 15, txt='DO OBJETO DE ACORDO', ln=1, align="L")

    pdf.set_font("Arial", size=12)
    pdf.multi_cell(
        0,
        espacamento,
        txt='É objeto do presente acordo a prestação do serviço no projeto ' +
        projeto.nome + ' como ' + papel + ' na vaga de ' + vaga.titulo +
        ' por meio de um contrato como ' + acordo.descricao + '.',
        align="J")

    if (vaga.remunerado):
        pdf.multi_cell(
            0,
            espacamento,
            txt=
            'Esta prestação de serviços será remunerada com valores e pagamentos a serem negociados entre ambas as partes.',
            align="J")
    else:
        pdf.multi_cell(
            0,
            espacamento,
            txt=
            'Esta prestação de serviços não será remunerada conforme anunciado na plataforma Conectar.',
            align="J")

    pdf.cell(20, 10, txt='', ln=1)

    pdf.multi_cell(
        0,
        espacamento,
        txt=
        'A execução da prestação de serviço aqui acordada será de responsabilidade das partes envolvidas, eximindo da plataforma Conectar de qualquer obrigação com o contratante ou contratado.',
        align="J")

    hoje = datetime.now()
    data_str = 'Dois Vizinhos, ' + \
        str(hoje.day) + " de " + MESES[hoje.month] + " de " + str(hoje.year)

    pdf.cell(20, 7, txt='', ln=1)
    pdf.cell(200, espacamento, txt=data_str, ln=1, align="L")

    pdf.cell(20, 20, txt='', ln=1)

    pdf.cell(175,
             espacamento,
             txt='___________________            ___________________',
             ln=1,
             align="C")
    pdf.cell(175,
             espacamento,
             txt='Contratante                            Contratado',
             ln=1,
             align="C")

    saida = str(uuid.uuid4().hex) + ".pdf"

    pdf.output(PDF_PATH + saida)

    if not os.getenv("DEV_ENV"):
        upload_object(PDF_PATH + saida, 'conectar')
    os.remove(PDF_PATH + saida)

    return saida
Beispiel #24
0
def createPDFcurriculo(db: Session, pessoa: Pessoa):

    pdf = FPDF()
    pdf.add_page(orientation='P')

    espacamento = 7

    pdf.set_margins(20, 20, 20)
    pdf.set_font("Arial", 'B', size=16)
    # Espaço de formatação
    pdf.cell(20, 20, txt='', ln=1)
    pdf.cell(175, 10, txt=pessoa.nome, ln=1, align="C")
    pdf.set_font("Arial", size=12)
    pdf.cell(175,
             10,
             txt='https://boraconectar.com/perfil/{}'.format(pessoa.usuario),
             ln=1,
             align='C')

    pdf.set_font("Arial", "B", size=12)
    pdf.cell(40, espacamento, txt='» Dados Pessoais', ln=1, align="L")

    pdf.set_font("Arial", size=12)
    nascimento = datetime.strftime(pessoa.data_nascimento, "%d/%m/%Y")
    pdf.cell(0,
             espacamento,
             txt='Data de Nascimento: ' + nascimento,
             ln=1,
             align="L")
    pdf.cell(0, espacamento, txt='Email: ' + pessoa.email, ln=1, align="L")
    if pessoa.telefone:
        pdf.cell(0,
                 espacamento,
                 txt='Telefone: ' + pessoa.telefone,
                 ln=1,
                 align="L")

    pdf.set_font("Arial", "B", size=12)
    pdf.cell(0, espacamento, txt='» Áreas', ln=1, align="L", border='T')

    pdf.set_fill_color(r=246, g=210, b=174)

    pdf.set_font("Arial", size=12)
    for area in pessoa.areas:
        pdf.cell(pdf.get_string_width(area.descricao) + 5,
                 7,
                 txt=area.descricao,
                 ln=0,
                 align="C",
                 border=1,
                 fill=True)
        pdf.cell(3, 8, txt='', ln=0)

    pdf.cell(20, 8, txt='', ln=1)

    pdf.set_font("Arial", 'B', size=12)
    pdf.cell(0, espacamento, txt='» Habilidades', ln=1, align="L")

    pdf.set_font("Arial", size=12)
    for habilidade in pessoa.habilidades:
        pdf.cell(pdf.get_string_width(habilidade.nome) + 5,
                 7,
                 txt=habilidade.nome,
                 ln=0,
                 align="C",
                 border=1,
                 fill=True)
        pdf.cell(3, 8, txt='', ln=0)

    pdf.cell(20, 8, txt='', ln=1)

    pdf.set_font("Arial", "B", size=12)
    pdf.cell(0,
             espacamento,
             txt='» Experiências Acadêmicas',
             ln=1,
             align="L",
             border='T')

    experienciasAcademicas = (db.query(models.ExperienciaAcad).filter(
        models.ExperienciaAcad.pessoa_id == pessoa.id).order_by(
            models.ExperienciaAcad.data_inicio.desc()).all())

    pdf.set_font("Arial", size=12)
    for exp in experienciasAcademicas:
        if exp.situacao == "Em andamento":
            situacao = "Em andamento"
        else:
            inicio = datetime.strftime(exp.data_inicio, "%d/%m/%Y")
            fim = nascimento = datetime.strftime(exp.data_fim, "%d/%m/%Y")
            situacao = "De " + inicio + " até " + fim
        pdf.multi_cell(0,
                       7,
                       txt=exp.instituicao + '\n' + exp.curso + '\n' +
                       situacao,
                       align='L',
                       border='B')

    pdf.set_font("Arial", "B", size=12)
    pdf.cell(0,
             espacamento,
             txt='» Experiências Profissionais',
             ln=1,
             align="L")

    experienciasProfissionais = (db.query(models.ExperienciaProf).filter(
        models.ExperienciaProf.pessoa_id == pessoa.id).order_by(
            models.ExperienciaProf.data_inicio.desc()).all())

    pdf.set_font("Arial", size=12)
    for exp in experienciasProfissionais:
        inicio = datetime.strftime(exp.data_inicio, "%d/%m/%Y")
        if not exp.data_fim:
            situacao = "De " + inicio + " até o momento atual"
        else:
            fim = nascimento = datetime.strftime(exp.data_fim, "%d/%m/%Y")
            situacao = "De " + inicio + " até " + fim
        pdf.multi_cell(0,
                       7,
                       txt=exp.organizacao + '\n' + exp.cargo + ' | ' +
                       exp.vinculo + '\n' + situacao,
                       align='L',
                       border='B')

    pdf.set_font("Arial", "B", size=12)
    pdf.cell(0, espacamento, txt='» Projetos e Pesquisas', ln=1, align="L")

    experienciasProjeto = (db.query(models.ExperienciaProj).filter(
        models.ExperienciaProj.pessoa_id == pessoa.id).order_by(
            models.ExperienciaProj.data_inicio.desc()).all())

    pdf.set_font("Arial", size=12)
    for exp in experienciasProjeto:
        if exp.situacao == "Em andamento":
            situacao = "Em andamento"
        else:
            inicio = datetime.strftime(exp.data_inicio, "%d/%m/%Y")
            fim = nascimento = datetime.strftime(exp.data_fim, "%d/%m/%Y")
            situacao = "De " + inicio + " até " + fim
        pdf.multi_cell(0,
                       7,
                       txt=exp.nome + '\n' + exp.cargo + '\n' + situacao,
                       align='L',
                       border='B')

    saida = str(uuid.uuid4().hex) + ".pdf"

    pdf.output(PDF_PATH + saida)

    return PDF_PATH + saida
                         border=0,
                         ln=0,
                         align='C',
                         fill=True)

        image_ptn_pt = Point(
            image_pt.x + landmark_list[index].x * horizontal_gap,
            image_pt.y + landmark_list[index].y * vertical_gap)
        pdf.image(sign_list[index]['miniature_uri'],
                  x=image_ptn_pt.x,
                  y=image_ptn_pt.y,
                  w=24,
                  h=24,
                  type='jpg')

        meaning_ptn_pt = Point(
            meaning_pt.x + landmark_list[index].x * horizontal_gap,
            meaning_pt.y + landmark_list[index].y * vertical_gap)
        pdf.set_xy(meaning_ptn_pt.x, meaning_ptn_pt.y)
        pdf.set_font('Arial', '', 8)
        alignment = 'C' if (pdf.get_string_width(
            sign_list[index]['meaning'].encode('cp1252')) < 70) else 'J'
        pdf.multi_cell(70,
                       3,
                       txt=sign_list[index]['meaning'].encode('cp1252'),
                       border=0,
                       align=alignment,
                       fill=False)

pdf.output('./permis-study-guide/study-guide.pdf', 'F')
Beispiel #26
0
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
Beispiel #27
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()
Beispiel #28
0
     pdf.add_font(text_font_data['name'],
                  '',
                  os.path.abspath('.') + '/app/pdf/' +
                  text_font_data['path'],
                  uni=True)
 text_font = text_font_data['name']
 # header section
 # left
 pdf.set_font(title_font, '', font_size['header_title'])
 pdf.cell(1, line_height['section'] + 1, data['header']['name'], ln=2)
 pdf.set_font_size(font_size['header_sub_title'])
 pdf.set_text_color(grey)
 pdf.cell(1, line_height['text'] + 1, data['header']['title'], ln=2)
 pdf.set_font_size(font_size['text'] + 2)
 online_text = data['pdf']['online']['text']
 pdf.cell(pdf.get_string_width(online_text),
          line_height['text'] + 1,
          online_text,
          ln=0)
 pdf.set_text_color(*blue)
 pdf.cell(1,
          line_height['text'] + 1,
          data['pdf']['online']['url'],
          ln=2,
          link=data['pdf']['online']['url'])
 header_y = pdf.get_y()
 # right
 pdf.set_y(page_size['top'] - 1)
 pdf.set_x(page_size['end_x'])
 pdf.set_font(text_font, '', font_size['text'])
 pdf.set_text_color(black)
Beispiel #29
0
from fpdf import FPDF

#font_file_name = 'font/mathjax_amsregular.ttf'
font_file_name = 'font/MathJax/ttf/mathjax_size4regular.ttf'

size = 10
pdf = FPDF()
pdf.add_page()
pdf.add_font('jax','',font_file_name,uni=True)

lb = 0
for n in range(0xeffc):
    c = unichr(n)
    pdf.set_font('jax','',size)
    w = pdf.get_string_width(c)
    if w > 0:
        pdf.set_font('times','',size)
        pdf.cell(13, 10, hex(n) + ': ' )
        pdf.set_font('jax','',size)
        pdf.cell(10, 10, c)
        if lb == 6: 
            pdf.ln(10)
            lb = 0
        else:
            lb += 1
    #print unichr(i)
pdf.output('out/latex/font_view.pdf', 'F')
print 'done'
Beispiel #30
0
def print_tickets_view(request, preorder_id, secret):
	if EVENT_DOWNLOAD_DATE and datetime.datetime.now() < datetime.datetime.strptime(EVENT_DOWNLOAD_DATE,'%Y-%m-%d %H:%M:%S'):
		messages.error(request, _("Tickets cannot be downloaded yet, please try again shortly before the event."))
		return redirect("my-tickets")

	preorder = get_object_or_404(CustomPreorder, Q(pk=preorder_id), Q(user_id=request.user.pk), Q(unique_secret=secret))

	# what to do if this preorder is not yet marked as paid?
	if not preorder.paid:
		messages.error(request, _("You cannot download your ticket until you paid for it."))
		return redirect("my-tickets")

	# check if this ticket is eligible for an invoice address and has not yet one saved
	single_ticket_over_limit = False
	billing_address = False
	if not preorder.get_billing_address():
		for tposition in preorder.get_tickets():
			amount = float(tposition['t'].price) * int(tposition['amount'])

			if amount >= EVENT_BILLING_ADDRESS_LIMIT:
				single_ticket_over_limit = True
				break

		if single_ticket_over_limit:
			if request.POST:
				p = request.POST
			else:
				p = None
			form = BillingAddressForm(p)

			if not request.POST.get('without_billingaddress') == 'yes':
				if form.is_valid():
					billing_address = PreorderBillingAddress()
					billing_address.company = form.cleaned_data['company']
					billing_address.firstname = form.cleaned_data['firstname']
					billing_address.lastname = form.cleaned_data['lastname']
					billing_address.address1 = form.cleaned_data['address1']
					billing_address.address2 = form.cleaned_data['address2']
					billing_address.city = form.cleaned_data['city']
					billing_address.zip = form.cleaned_data['zip']
					billing_address.country = form.cleaned_data['country']
					billing_address.preorder = preorder
					billing_address.save()
				else:
					limit = EVENT_BILLING_ADDRESS_LIMIT
					return render_to_response('billingaddress.html', locals(), context_instance=RequestContext(request))

	from pyqrcode import MakeQRImage
	from fpdf import FPDF
	import time
	from django.template.defaultfilters import floatformat
	from os import remove

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

	#initialisation
	pdf.add_font(family='dejavu', fname="%sdejavu/DejaVuSans.ttf" % settings.STATIC_ROOT, uni=True)
	pdf.add_font(family='dejavu', style="B", fname="%sdejavu/DejaVuSans-Bold.ttf" % settings.STATIC_ROOT, uni=True)
	pdf.add_font(family='dejavu', style="I", fname="%sdejavu/DejaVuSans-ExtraLight.ttf" % settings.STATIC_ROOT, uni=True)
	font = 'dejavu'

	#############################################

	delete_files = []

	for position in preorder.get_positions():
		#Print a ticket page for each preorder position

		#Fix for old tickets, probably no longer needed
		if not position.uuid:
			from uuid import uuid4
			position.uuid = str(uuid4())
			position.save()

		#create the QR code for the current ticket position
		qrcode = MakeQRImage(position.uuid)
		qrcode.save('%stmp/%s.jpg' % (settings.STATIC_ROOT, position.uuid), format="JPEG")

		#add new page
		pdf.add_page()
		pdf.set_right_margin(0)

		ticket = position.ticket

		#PDF "header"
		pdf.image('%s%s' % (settings.STATIC_ROOT, settings.EVENT_LOGO), 15, 15, 200, 96)
		#pdf.set_font(font,'B',27)
		#pdf.text(20,50,"%s" % 'SIGINT 2013')
		pdf.set_font(font,'I',6)
		pdf.text(110,100,"%s" % 'July 5th - July 7th')
		pdf.text(110,107,"%s" % 'Mediapark, Cologne, Germany')
		pdf.text(110,114,"%s" % 'https://sigint.ccc.de/')

		pdf.set_font(font,'I',40)

		# if price > 150, this is an invoice
		if ticket.price < 150 and ticket.price > 0:
			pass
			#pdf.text(220,100,"RECEIPT")
		elif ticket.price >= 150:
			pdf.text(220,90,"RECEIPT")
		pdf.set_font(font,'B',40)
		pdf.text(220,50,"ONLINE TICKET")

		# print billing address - if eligible
		if ticket.price >= EVENT_BILLING_ADDRESS_LIMIT:
			if preorder.get_billing_address() or billing_address:

				from django.utils.encoding import smart_str

				pdf.set_font('Arial','B',13)
				pdf.text(20,150,"Billing address")
				pdf.set_font('Arial','',10)

				if not billing_address:
					billing_address = preorder.get_billing_address()

				ytmp = 0

				if billing_address.company:
					pdf.text(20,170,"%s" % billing_address.company)
					ytmp+=12
				pdf.text(20,170+ytmp,"%s %s" % (billing_address.firstname, billing_address.lastname))
				pdf.text(20,182+ytmp,"%s" % billing_address.address1)
				if billing_address.address2:
					pdf.text(20,194+ytmp,"%s" % billing_address.address2)
					ytmp+=12
				pdf.text(20,194+ytmp,"%s %s" % (billing_address.zip, billing_address.city))
				pdf.text(20,206+ytmp,"%s" % billing_address.country)


		# print ticket table
		pdf.set_font(font,'I',15)
		pdf.text(20,260,"Type")
		if ticket.price > 0:
			pdf.text(350,260,"Price")

		i = 0

		pdf.set_font(font,'B',20)
		pdf.set_y(270+i)
		pdf.set_x(20)
		pdf.set_right_margin(250)
		pdf.set_left_margin(17)
		pdf.write(17, "\n%s"%ticket.name)
		pdf.set_left_margin(20)
		pdf.set_font(font,'B',20)

		pdf.set_left_margin(20)

		if ticket.price > 0:
			pdf.text(350, 302, "%s %s" % (str(floatformat(ticket.price, 2)), "€" if ticket.currency == "EUR" else ticket.currency))

			pdf.set_font(font,'',11)
			price_vat = str(floatformat(float(ticket.price)-float(ticket.price)/(float(ticket.tax_rate)/100+1), 2))
			price_net = str(floatformat(float(ticket.price)/(float(ticket.tax_rate)/100+1), 2))
			#pdf.text(350, 320, "incl. %s%% VAT: %s %s" % (ticket.tax_rate, price_vat, ticket.currency))
			#if ticket.price >= 150:
			pdf.set_font(font,'',7)
			pdf.text(350, 314, "%(price_net)s %(currency)s net + %(tax_rate)s%% VAT (%(price_vat)s %(currency)s) = %(price)s %(currency)s total" % ({
				'tax_rate': ticket.tax_rate,
				'price': ticket.price,
				'price_net': price_net,
				'price_vat': price_vat,
				'currency': "€" if ticket.currency == "EUR" else ticket.currency,
				}))

		## special tickets
		special_tickets = {
			'Speaker Ticket': 'SPEAKER',
			'Booth Operator': 'BOOTH',
			'Member of the Press': 'PRESS'
		}
		if ticket.name in special_tickets.keys():
			pdf.set_font(font,'B',72)
			pdf.text(pdf.w/2-(pdf.get_string_width(special_tickets[ticket.name])/2), 490, '%s' % special_tickets[ticket.name])

		## special tickets

		i = i + 50

		# print qr code
		pdf.image('%stmp/%s.jpg' % (settings.STATIC_ROOT, position.uuid), 300, 540, 300, 300)
		# save file url to "delete array"
		delete_files.append('%stmp/%s.jpg' % (settings.STATIC_ROOT, position.uuid))

		# print human readable ticket code
		pdf.set_font(font,'I',8)
		pdf.text(23, 790, 'Payment reference: %s-%s' % (settings.EVENT_PAYMENT_PREFIX, preorder.unique_secret[:10]))
		pdf.text(23, 800, '%s' % position.uuid)
		pdf.text(23, 810, '%s' % preorder.unique_secret)



		#PDF "Footer"

		# print invoice information
		pdf.set_font(font, '', 15)
		pdf.set_y(550)
		pdf.write(20, '%s' % settings.EVENT_INVOICE_ADDRESS)
		pdf.set_font(font, '', 10)
		pdf.set_y(640)
		if ticket.price > 0:
			pdf.write(15, '%s' % settings.EVENT_INVOICE_LEGAL)
		pdf.set_font(font, '', 10)
		pdf.set_y(680)
		pdf.write(15, 'Issued: %s' % time.strftime('%Y-%m-%d %H:%M', time.gmtime()))
		pdf.set_font(font, '', 8)
		pdf.set_y(720)
		pdf.set_right_margin(300)

		if ticket.price > 0 and ticket.price < 150:
			pdf.write(10, "Bis zu einem Ticketpreis von 150,00 EUR gilt das Ticket gleichzeitig als Kleinbetragsrechnung im Sinne von § 33 UStDV. Umtausch und Rückgabe ausgeschlossen.")
		elif ticket.price >= 150:
			pdf.write(10, "Umtausch und Rückgabe ausgeschlossen.")


	response = HttpResponse(mimetype="application/pdf")
	response['Content-Disposition'] = 'inline; filename=%s-%s.pdf' % (settings.EVENT_PAYMENT_PREFIX, preorder.unique_secret[:10])
	#response['Content-Length'] = in_memory.tell()
	response.write(pdf.output('', 'S'))

	# delete qrcode
	for f in delete_files:
		remove(f)

	return response
Beispiel #31
0
     pdf.add_font(title_font_data['name'], '', os.path.abspath('.') + '/app/pdf/' + title_font_data['path'], uni=True)
 title_font = title_font_data['name']
 # font for plain text
 if text_font_data['type'] == 'embeded':
     pdf.add_font(text_font_data['name'], '', os.path.abspath('.') + '/app/pdf/' + text_font_data['path'], uni=True)
 text_font = text_font_data['name']
 # header section
 # left
 pdf.set_font(title_font, '', font_size['header_title'])
 pdf.cell(1, line_height['section'] + 1, data['header']['name'], ln=2)
 pdf.set_font_size(font_size['header_sub_title'])
 pdf.set_text_color(grey)
 pdf.cell(1, line_height['text'] + 1, data['header']['title'], ln=2)
 pdf.set_font_size(font_size['text'] + 2)
 online_text = data['pdf']['online']['text']
 pdf.cell(pdf.get_string_width(online_text), line_height['text'] + 1, online_text, ln=0)
 pdf.set_text_color(*blue)
 pdf.cell(1, line_height['text'] + 1, data['pdf']['online']['url'], ln=2, link=data['pdf']['online']['url'])
 header_y = pdf.get_y()
 # right
 pdf.set_y(page_size['top'] - 1)
 pdf.set_x(page_size['end_x'])
 pdf.set_font(text_font, '', font_size['text'])
 pdf.set_text_color(black)
 pdf.cell(1, line_height['text'] - 0.5, data['about']['location'], ln=2, align='R')
 pdf.cell(1, line_height['text'] - 0.5, data['about']['phone'], ln=2, align='R')
 pdf.cell(1, line_height['text'] - 0.5, data['about']['email'], ln=2, align='R')
 pdf.set_text_color(*blue)
 pdf.cell(1, line_height['text'] - 0.5, data['about']['socials']['blog']['url'], ln=2, align='R', link=data['about']['socials']['blog']['url'])
 pdf.cell(1, line_height['text'] - 0.5, data['about']['socials']['github']['url'], ln=2, align='R', link=data['about']['socials']['github']['url'])
 pdf.set_text_color(black)
Beispiel #32
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")
Beispiel #33
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
def make_pdf_report(fn, dfs, cfg, cfg_ssv, campus, debug, do_solo):
    """Master function for creating the pdf reports
    do_solo will be true if we want a single file per student"""

    # First create Class and process config settings
    local_cfg = {}
    for label, cfg_name in [
        ("sort", "sort_students"),
        ("labels", "category_labels"),
    ]:
        if campus in cfg[cfg_name]:
            local_cfg[label] = cfg[cfg_name][campus]
        else:
            local_cfg[label] = cfg[cfg_name]["Standard"]

    labels = local_cfg["labels"]
    tgr_label = labels["TargetGR"]
    igr_label = labels["IdealGR"]

    for label, ssv_name in [
        ("orient", "pdf_orientation"),
        ("c_header", "counselor_header"),
        ("p_header", "print_header"),
        ("p_footer", "print_footer"),
    ]:
        if campus in cfg_ssv[ssv_name]:
            local_cfg[label] = cfg_ssv[ssv_name][campus]
        else:
            local_cfg[label] = cfg_ssv[ssv_name]["Standard"]

    if campus in cfg_ssv["school_goals"]:
        school_goals = cfg_ssv["school_goals"][campus]
        goal_descriptions = cfg_ssv["goal_descriptions"]
    else:
        school_goals = None

    top_margin = cfg_ssv["pdf_margins"]["top"]
    left_margin = cfg_ssv["pdf_margins"]["left"]
    right_margin = cfg_ssv["pdf_margins"]["right"]
    thick_line = cfg_ssv["pdf_lines"]["thick"]
    line = cfg_ssv["pdf_lines"]["line"]
    goals_start = cfg_ssv["pdf_goals_start"]
    footer_start = cfg_ssv["pdf_footer_start"]
    college_max = cfg_ssv["pdf_college_max"]

    if not do_solo:
        pdf = FPDF(orientation=local_cfg["orient"], unit="in", format="Letter")

        for font_name, filename in cfg_ssv["pdf_fonts"].items():
            pdf.add_font(font_name, "", filename, uni=True)
        pdf.set_line_width(line)
        pdf.set_margins(left=left_margin, top=top_margin, right=right_margin)
    else:
        create_folder_if_necessary(["Reports", campus])
        filenames = []

    # Get the student data and sort as appropriate
    df = dfs["roster"].copy()
    app_df = dfs["apps"].copy()

    # The sort string is pseudocode linking table columns surrounded by % with
    # ampersands and preceded by an equals to map to an Excel formula. The
    # next line reduces that to an ordered list of table names
    sort_order = [
        x for x in local_cfg["sort"].split(sep="%") if x not in ["=", "&", ""]
    ]
    if debug:
        print("Sort order for PDF: {}".format(str(sort_order)))
    df.sort_values(by=sort_order, inplace=True)

    ####################################
    # start repeating here for each page
    for i, stu_data in df.iterrows():
        if do_solo:
            student_fn = (campus + "_" +
                          stu_data["LastFirst"].replace(" ", "_") + "_" +
                          str(i) + date.today().strftime("_on_%m_%d_%Y") +
                          ".pdf")
            pdf = FPDF(orientation=local_cfg["orient"],
                       unit="in",
                       format="Letter")

            for font_name, filename in cfg_ssv["pdf_fonts"].items():
                pdf.add_font(font_name, "", filename, uni=True)
            pdf.set_line_width(line)
            pdf.set_margins(left=left_margin,
                            top=top_margin,
                            right=right_margin)

        pdf.add_page()

        pdf.set_y(top_margin)

        w = cfg_ssv["pdf_widths"]  # list of cell widths in inches
        h = cfg_ssv["pdf_heights"]  # list of cell heights in inches

        # The width of next two columns is variable based on header sizes
        # *First row
        name_text = ("College application odds report for " +
                     stu_data["First"] + " " + stu_data["Last"])
        c_text = _clean_excel(local_cfg["c_header"], stu_data, labels)
        pdf.set_font("font_b", "", 11)
        c_width = pdf.get_string_width(c_text) + 0.05

        if local_cfg["p_header"]:
            # We're squeezing in one more entry, so stealing off the name
            # and the counselor header
            pdf.set_font("font_i", "", 11)
            p_text = _clean_excel(local_cfg["p_header"], stu_data, labels)
            p_width = pdf.get_string_width(p_text) + 0.05
            n_width = sum(w) - p_width - c_width - 0.05
        else:
            n_width = sum(w) - c_width - 0.05

        pdf.set_font("font_b", "", 14)
        _shrink_cell(
            pdf=pdf,
            w=n_width,
            txt=name_text,
            h=h[0],
            border=0,
            ln=0,
            align="L",
            fill=False,
        )

        if local_cfg["p_header"]:
            pdf.set_font("font_i", "", 11)
            _shrink_cell(
                pdf=pdf,
                w=p_width,
                txt=p_text,
                h=h[0],
                border=0,
                ln=0,
                align="L",
                fill=False,
            )

        pdf.set_font("font_b", "", 11)
        _shrink_cell(
            pdf=pdf,
            w=c_width,
            txt=c_text,
            h=h[0],
            border=0,
            ln=1,
            align="L",
            fill=False,
        )

        # *Second row
        _set_color_name(pdf, "light_blue")
        pdf.cell(w=w[0],
                 txt="Student's name:",
                 h=h[1],
                 border=1,
                 ln=0,
                 align="L",
                 fill=True)

        pdf.set_font("font_r", "", 11)
        pdf.cell(w=w[1],
                 txt="ACT/SAT",
                 h=h[1],
                 border="B",
                 ln=0,
                 align="C",
                 fill=True)

        pdf.cell(w=w[2],
                 txt="GPA",
                 h=h[1],
                 border="B",
                 ln=0,
                 align="C",
                 fill=True)

        pdf.cell(w=w[3],
                 txt="Race/Eth",
                 h=h[1],
                 border=1,
                 ln=0,
                 align="C",
                 fill=True)

        txt = igr_label[0] + "GR"
        pdf.cell(w=w[4],
                 txt=txt,
                 h=h[1],
                 border="B",
                 ln=0,
                 align="C",
                 fill=True)

        txt = _notnan(stu_data["local_ideal_gr"], "TBD", "{:2.0%}")
        pdf.cell(w=w[5],
                 txt=txt,
                 h=h[1],
                 border="B",
                 ln=1,
                 align="C",
                 fill=False)

        # *Third row
        _set_color_name(pdf, "salmon")
        pdf.cell(
            w=w[0],
            txt=stu_data["LastFirst"],
            h=h[2],
            border=1,
            ln=0,
            align="L",
            fill=True,
        )

        txt = (_notnan(stu_data["ACT"], "TBD", "{:d}") + "/" +
               _notnan(stu_data["SAT"], "TBD", "{:d}"))
        pdf.cell(w=w[1],
                 txt=txt,
                 h=h[2],
                 border=0,
                 ln=0,
                 align="C",
                 fill=False)

        pdf.cell(
            w=w[2],
            txt=_notnan(stu_data["GPA"], "TBD", "{:4.2f}"),
            h=h[2],
            border=0,
            ln=0,
            align="C",
            fill=False,
        )

        pdf.cell(
            w=w[3],
            txt=_notnan(stu_data["Race/ Eth"], "TBD", "{}"),
            h=h[2],
            border=1,
            ln=0,
            align="C",
            fill=False,
        )

        _set_color_name(pdf, "light_blue")
        txt = tgr_label[0] + "GR"
        pdf.cell(w=w[4],
                 txt=txt,
                 h=h[2],
                 border="T",
                 ln=0,
                 align="C",
                 fill=True)

        txt = _notnan(stu_data["local_target_gr"], "TBD", "{:2.0%}")
        pdf.cell(w=w[5],
                 txt=txt,
                 h=h[2],
                 border=0,
                 ln=1,
                 align="C",
                 fill=False)

        # *Fourth row
        pdf.set_font("font_b", "", 11)
        pdf.cell(
            w=w[0],
            txt="Odds of 1 or more acceptances to:",
            h=h[3],
            border=0,
            ln=0,
            align="L",
            fill=True,
        )

        txt = _notnan(stu_data["local_sat_max"], "TBD", "{:1.0f}")
        pdf.cell(w=w[1], txt=txt, h=h[3], border=0, ln=0, align="C", fill=True)

        pdf.cell(w=w[2], txt="", h=h[3], border=0, ln=0, align="C", fill=True)

        pdf.cell(
            w=sum(w[3:]),
            txt="Goals for #s to the left:",
            h=h[3],
            border=0,
            ln=1,
            align="L",
            fill=True,
        )

        # *Fifth row
        pdf.set_font("font_r", "", 11)
        pdf.cell(
            w=sum(w[:2]),
            txt='"Money" ' + tgr_label + " grade rate (" + tgr_label[0] +
            "GR) or better schools",
            h=h[4],
            border=0,
            ln=0,
            align="L",
            fill=False,
        )

        txt = _notnan(stu_data["local_oneplus_mtgr"], "TBD", "{:3.0%}")
        pdf.cell(w=w[2],
                 txt=txt,
                 h=h[4],
                 border=0,
                 ln=0,
                 align="C",
                 fill=False)

        txt = "<--Shoot for at least 90% for Money " + tgr_label[0] + "GR"
        pdf.cell(w=sum(w[3:]),
                 txt=txt,
                 h=h[4],
                 border=0,
                 ln=1,
                 align="L",
                 fill=False)

        # *Sixth row
        pdf.cell(
            w=sum(w[:2]),
            txt='"Money" ' + igr_label + " grade rate (" + igr_label[0] +
            "GR) or better schools",
            h=h[5],
            border=0,
            ln=0,
            align="L",
            fill=False,
        )

        txt = _notnan(stu_data["local_oneplus_migr"], "TBD", "{:3.0%}")
        pdf.cell(w=w[2],
                 txt=txt,
                 h=h[5],
                 border=0,
                 ln=0,
                 align="C",
                 fill=False)

        txt = "<--Shoot for at least 50% for Money " + igr_label[0] + "GR"
        pdf.cell(w=sum(w[3:]),
                 txt=txt,
                 h=h[5],
                 border=0,
                 ln=1,
                 align="L",
                 fill=False)

        # *Seventh row is skinny/skip row
        pdf.cell(w=w[0], txt="", h=h[6], border=0, ln=1, align="C", fill=False)

        # *Eighth row is header for college lists and is actually the first of
        # *two rows used for that purpose
        pdf.set_font("font_b", "", 11)
        pdf.cell(
            w=w[0],
            txt='Schools currently applying to ("*" indicates',
            h=h[7],
            border=0,
            ln=0,
            align="L",
            fill=True,
        )

        if (stu_data["Race/ Eth"] == "W") or (stu_data["Race/ Eth"] == "A"):
            txt_race = "6 yr (all)"
        else:
            txt_race = "6 yr AA/H"

        for w_this, txt_this, ln_this in [
            (w[1], txt_race, 0), (w[2], "Odds of", 0), (w[3], "For you,", 0),
            (w[4], "", 0), (w[5], "", 1)
                # (0.8,'Award',0), # Replace the above with these two for new "award" column
                # (0.9,'Award letter',1)
        ]:
            pdf.cell(
                w=w_this,
                h=h[7],
                txt=txt_this,
                ln=ln_this,
                border=0,
                align="C",
                fill=True,
            )

        # *Ninth row is continuation of college header
        pdf.cell(w=w[0],
                 txt="prospective):",
                 h=h[8],
                 border=0,
                 ln=0,
                 align="L",
                 fill=True)

        for w_this, txt_this, ln_this in [
            (w[1], "Grad Rate", 0), (w[2], "Admit", 0),
            (w[3], "school is a", 0), (w[4], "App Status", 0),
            (w[5], "Award code", 1)
                # (0.80, 'code', 0), # Replace the above with these two for new "award" column
                # (0.9, 'received', 1)
        ]:
            pdf.cell(
                w=w_this,
                h=h[8],
                txt=txt_this,
                ln=ln_this,
                border=0,
                align="C",
                fill=True,
            )

        # From here on, we'll have a variable # of rows
        stu_apps = app_df[app_df["hs_student_id"] == i]
        num_apps = len(stu_apps)
        pdf.set_font("font_r", "", 11)
        tgr = stu_data["local_target_gr"]
        igr = stu_data["local_ideal_gr"]
        _set_color_name(pdf, "light_blue")
        apps_displayed = 0
        if num_apps:
            for j, app_data in stu_apps.iterrows():
                college_text = app_data["collegename"]
                _shrink_cell(
                    pdf=pdf,
                    w=w[0],
                    h=h[9],
                    ln=0,
                    txt=college_text,
                    align="L",
                    fill=False,
                    border=0,
                )

                # This block is complicated because of grad rate highlighting
                gr_this = app_data["local_6yr_all_aah"]
                gr_text = _notnan(gr_this, "N/A", "{:2.0%}")
                if gr_this >= igr:
                    _set_color_name(pdf, "navy_blue", type="text")
                    pdf.set_font("font_b", "", 11)
                if gr_this < tgr:
                    _set_color_name(pdf, "red", type="text")
                    _set_color_name(pdf, "grey")
                gr_fill = gr_this < tgr
                pdf.cell(w=w[1],
                         h=h[9],
                         ln=0,
                         txt=gr_text,
                         align="C",
                         fill=gr_fill,
                         border=0)

                # Back to normal for last entries
                _set_color_name(pdf, "black", type="text")
                pdf.set_font("font_r", "", 11)

                for w_, txt_, ln_ in [
                    (
                        w[2],
                        _notnan(app_data["local_odds"] / 100.0, "N/A",
                                "{:2.0%}"),
                        0,
                    ),
                    (w[3], app_data["local_class"], 0),
                    (w[4], app_data["local_result"], 0),
                    (
                        w[5],
                        _notnan(
                            app_data[
                                "local_money_code"],  # (0.8, _notnan(app_data['local_money_code'],
                            "N/A",
                            "{}",
                        ),
                        1,
                    ),
                ]:
                    pdf.cell(w=w_,
                             h=h[9],
                             txt=txt_,
                             align="C",
                             fill=False,
                             ln=ln_,
                             border=0)
                apps_displayed += 1
                if apps_displayed == college_max:
                    break

        # Optional school based goals at the bottom
        if school_goals is not None:
            pads, goal_eval = _get_student_goal_performance(
                pdf, w[0], school_goals, goal_descriptions, stu_data, stu_apps,
                labels)

            pdf.set_y(goals_start)
            # Two header lines:
            pdf.set_font("font_bi", "", 11)
            pdf.cell(
                w=w[0],
                h=h[10],
                border=0,
                ln=1,
                align="L",
                fill=False,
                txt="Your list compared to campus goals:",
            )

            pdf.set_font("font_b", "", 11)
            _set_color_name(pdf, "light_blue")

            if pads > 0:
                padder = max([1, pads - 3]) * " "
            else:
                padder = ""
            pdf.cell(
                w=w[0],
                h=h[10],
                border=0,
                ln=0,
                align="L",
                fill=True,
                txt=(padder + "Campus Goal"),
            )
            pdf.cell(w=w[1],
                     h=h[10],
                     border=0,
                     ln=0,
                     align="C",
                     fill=True,
                     txt="You")
            pdf.cell(w=w[1],
                     h=h[10],
                     border=0,
                     ln=1,
                     align="C",
                     fill=True,
                     txt="Met?")

            # Now do the variable number of goals
            pdf.set_font("font_r", "", 11)
            for goal_text, score, judgement in goal_eval:
                pdf.cell(
                    w=w[0],
                    h=h[10],
                    border=0,
                    ln=0,
                    align="L",
                    fill=False,
                    txt=(pads * " " + goal_text),
                )
                pdf.cell(w=w[1],
                         h=h[10],
                         border=0,
                         ln=0,
                         align="C",
                         fill=False,
                         txt=score)
                pdf.cell(
                    w=w[1],
                    h=h[10],
                    border=0,
                    ln=1,
                    align="C",
                    fill=False,
                    txt=judgement,
                )

        # Final (optional) print line for a page
        if local_cfg["p_footer"]:
            pdf.set_y(footer_start)
            pdf.set_font("font_bi", "", 11)
            p_text = _clean_excel(local_cfg["p_footer"], stu_data, labels)
            pdf.cell(w=sum(w),
                     h=h[11],
                     border="T",
                     ln=0,
                     align="L",
                     fill=False,
                     txt=p_text)

        # Final formatting of lines: bold rects then lines
        pdf.set_line_width(thick_line)
        pdf.rect(left_margin, top_margin + h[0], w[0],
                 sum(h[1:3]))  # around name

        pdf.rect(
            left_margin,
            top_margin + h[0],  # around name and scores/gpa
            sum(w[:3]),
            sum(h[1:3]),
        )

        pdf.rect(
            left_margin,
            top_margin + h[0],  # around whole left side
            sum(w[:3]),
            sum(h[1:6]),
        )

        pdf.rect(
            left_margin + sum(w[:4]),
            top_margin + h[0],  # upper right
            sum(w[4:]),
            sum(h[1:3]),
        )

        pdf.rect(
            left_margin + sum(w[:3]),
            top_margin + sum(h[:3]),  # lower right
            sum(w[3:]),
            sum(h[3:6]),
        )

        if school_goals is not None:
            pdf.rect(
                left_margin,
                goals_start + h[10],
                sum([w[0], w[1], w[1]]),
                h[10] * (1 + len(school_goals)),
            )

        # Skinny rects then lines
        pdf.set_line_width(line)
        line_top = top_margin + sum(h[:7])
        line_bottom = (top_margin + sum(h[:9]) + h[9] *
                       (num_apps if num_apps <= college_max else college_max))
        for x in [1, 2, 4, 5]:
            pdf.line(
                left_margin + sum(w[:x]),
                line_top,
                left_margin + sum(w[:x]),
                line_bottom,
            )
        # pdf.line(left_margin+sum(w[:x])+0.8,line_top,
        #         left_margin+sum(w[:x])+0.8,line_bottom)

        if do_solo:
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                this_file = os.path.join("Reports", campus, student_fn)
                pdf.output(this_file, "F")
                filenames.append(this_file)

    # The font we use is missing an unusued glyph and so throws two warnings
    # at save. The next three lines supress this, but probably good to
    # occasionally uncomment them
    if not do_solo:
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            pdf.output(fn, "F")
    else:
        # Create a zip file of all of the single files
        campus_fn = (campus + "_single_file_SSVs" +
                     date.today().strftime("_%m_%d_%Y") + ".zip")
        with zipfile.ZipFile(campus_fn, "w", zipfile.ZIP_DEFLATED) as myzip:
            for file in filenames:
                myzip.write(file)
Beispiel #35
0
#!/usr/bin/env python
# -*- coding: latin1 -*-

"Basic test to reproduce issue 63: warning in unicode get_char_width"

from fpdf import FPDF
pdf = FPDF()
pdf.set_font('Arial','',14)  
s = 'Texto largo que no cabe en esta celda pero que será ajustado'
w = pdf.get_string_width(s)
print (s, w)
assert round(w, 2) == 135.90
pdf.add_font('DejaVu', '', './font/DejaVuSans.ttf', uni=True)
pdf.set_font('DejaVu', '', 14)
s = u'Texto largo que no cabe en esta celda pero que será ajustado'
w = pdf.get_string_width(s)
print (s, w)
assert round(w, 2) == 153.64

Beispiel #36
0
            text_cipher[i] = text[i]
    with open(output_file, "w") as out_file:
        text_cipher = ''.join(text_cipher)
        out_file.write(text_cipher)

A4_width = 210
A4_height = 297
margin = 20
line_height = 10
offset = 1
pdf = FPDF('P', 'mm', (A4_width, A4_height))
pdf.add_page()
pdf.set_margins(margin, margin)
pdf.set_xy(margin, margin)
pdf.set_font('Courier', 'B', 20)
charWidth = pdf.get_string_width("W")
lines = text_cipher.split("\n")
for line in lines:
    words = line.split(" ")
    total_width = 0
    pdf.set_x(margin)
    for word in words:
        width = pdf.get_string_width(word + " ")
        total_width += width
        #print(word + ": " + str(width) + ", " + str(total_width))
        if (total_width >= (A4_width - 2 * margin)):
            #print('ln')
            pdf.ln()
            pdf.ln()
            total_width = width
        pdf.cell(width, line_height, word + " ", 0)
# coding: utf-8

from fpdf import FPDF
import qrcode

pdf = FPDF('L', 'mm', (36, 89))
pdf.set_auto_page_break(False)
pdf.add_page()
pdf.set_font('Arial', '', 12)
pdf.set_xy(5, 6)
pdf.cell(0,0,'Some awesome Device')

pdf.set_font('Arial', 'U', 10)
pdf.set_xy(5, 27)
len_inv = pdf.get_string_width('Inventar-Nr: ')
pdf.cell(len_inv,0,'Inventar-Nr:')
pdf.set_font('Arial', '', 10)
pdf.cell(0,0,'g0001')

img = qrcode.make('http://hshb.de/g0001')
img.save('g0001.png')

pdf.image('g0001.png', x = 61, y = 2, w = 25)

pdf.set_font('Arial', '', 8)
pdf.set_xy(63, 27)
pdf.cell(0,0,'hshb.de/g0001')

pdf.output('test.pdf')
Beispiel #38
0
from fpdf import FPDF

pdf = FPDF()

#header of the pdf file
header = 'Header of PDF Report'
pdf.add_page()
pdf.set_font('Arial', 'B', 16)
w = pdf.get_string_width(header) + 6
pdf.set_x((210 - w) / 2)
pdf.cell(w, 9, header, 0, 0, 'C')
pdf.line(20, 18, 210 - 20, 18)

#line break
pdf.ln(10)
pdf.set_font('Times', '', 12)
pdf.multi_cell(
    0, 5,
    'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.'
)

pdf.ln()
pdf.set_font('Arial', '', 12)
pdf.set_fill_color(200, 220, 255)
pdf.cell(0, 6, 'Chapter %d : %s' % (1, 'Sample Label'), 0, 1, 'L', 1)

pdf.ln()
pdf.set_font('Courier', 'B', 12)
pdf.multi_cell(
    0, 5,
    'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.'
Beispiel #39
0
# Figure out our font-size
# We need to know the format of our logo
logo_ratio = None
with Image.open(logo_filename) as img:
    width, height = img.size
    logo_ratio = float(height) / width
# Calculate the logo size
logo_width = cell_width - max_dimension - image_border
logo_height = logo_width * logo_ratio
# Calculate how much space will be left under the logo
height_left = cell_height - logo_height - image_border
width = None
# Start with pt=0, and go to our max height, trying to maximize width
pdf.set_font('Arial', '', 0)
for size in range(int(height_left * mm2pt)):
    width = pdf.get_string_width("x" * max_digits)
    # Stop if we get too wide
    if width > logo_width:
        break
    pdf.set_font('Arial', '', size)
pdf.set_text_color(r=0, g=0, b=0)

image_folder = 'img/'
if not os.path.exists(image_folder):
    os.makedirs(image_folder)

for page in range(pages):
    pdf.add_page()
    pdf.set_y(margin_top)
    for row in range(rows):
        pdf.set_x(margin_left)