class Document(TemplateElement):

    def __init__(self, doc_xml, cedible=False):
        self.doc_xml = doc_xml
        self.doc     = None

        # Templating Globals
        self.tex_cedible = cedible

        # Document Globals
        self.doc_type    = None
        self.doc_id      = None

        # 52 - Guia Despacho
        self.doc_gd_type = None

        # TeX Sections
        self.preamble   = None
        self.emitter    = None
        self.sii_patch  = None
        self.receiver   = None
        self.items      = None
        self.payments   = None
        self.totals     = None
        self.references = None
        self.barcode    = None
        self.signature  = None
        self.disclaimer = None

        # Init Hooks
        self.init_docvars()
        self.init_checks()

    def init_docvars(self):
        """ Extract Information, to be used by the individual sections once registered """
        doc = xml.wrap_xml(self.doc_xml)

        self.doc_type = doc.Documento.Encabezado.IdDoc.TipoDTE._int
        self.doc_id   = doc.Documento.Encabezado.IdDoc.Folio._int

        # Guia Despacho Specifics
        if self.doc_type == 52:
            self.doc_gd_type = doc.Documento.Encabezado.IdDoc.IndTraslado._int

    def init_checks(self):
        """ Run Global Assertions """
        if self.tex_cedible:
            if self.doc_type in (56, 61):
                raise AssertionError("NC and ND are not subject to the CEDIBLE template")

            if self.doc_type == 52:
                non_cedibles = (2, 4, 5, 7, 8)

                if self.doc_gd_type in non_cedibles:
                    raise AssertionError("GD of types {0} are not subject to a CEDIBLE template".format(non_cedibles))

    @property
    def resources(self):
        resources = []

        for name, attr in self.__dict__.items():
            if isinstance(attr, TemplateElement):
                section    = getattr(self, name)
                resources += section.resources

        return resources

    @property
    def carta(self):
        self._check()

        tex = self.preamble.carta

        tex += '\n'
        tex += '\n'

        tex += '\\begin{document}\n'

        # HEAD
        tex += '\\begin{minipage}[t]{0.6\\textwidth}\n'
        tex += self.emitter.carta
        tex += '\\end{minipage}%\n'
        tex += '\\begin{minipage}[t]{0.4\\textwidth}\n'
        tex += '    \\vspace{-2em}\n'
        tex += self.sii_patch.carta
        tex += '    \\vfill\n'
        tex += '\\end{minipage}%\n'
        tex += '\\vspace{2mm}\n'
        tex += self.receiver.carta

        # ITEMS
        tex += '\\begin{mdframed}[style=items]\n'
        tex += self.items.carta
        tex += '\\end{mdframed}\n'

        tex += '\\null\n'
        tex += '\\vfill\n'

        # FEFERENCES
        tex += '\\begin{mdframed}[style=references]\n'
        tex += self.references.carta
        tex += '\\end{mdframed}\n'

        # PAYMENTS/TOTALS
        tex += '\\begin{mdframed}[style=summary]\n'
        tex += '    \\vspace{-1em} % Zip them together (suppress spacing)\n'
        tex += '    \\begin{minipage}[t]{0.7\\textwidth}\n'
        tex += self.payments.carta
        tex += '    \\end{minipage}%\n'
        tex += '    \\begin{minipage}[t]{0.3\\textwidth}\n'
        tex += self.totals.carta
        tex += '    \\end{minipage}\n'
        tex += '    \\vspace{-1em} % Zip them together (suppress spacing)\n'
        tex += '\\end{mdframed}\n'

        # BARCODE
        tex += '\\begin{minipage}[t]{0.5\\textwidth}\n'
        tex += '    \\vfill\n'
        tex += self.barcode.carta
        tex += '\\end{minipage}%\n'

        # SIGNATURE (§19.983)
        tex += '\\begin{minipage}[t]{0.5\\textwidth}\n'
        tex += '    \\vfill\n'
        tex += self.signature.carta
        tex += '\\end{minipage}%\n'

        # DISCLAIMER
        tex += self.disclaimer.carta

        tex += '\\end{document}\n'
        return tex

    @property
    def oficio(self):
        self._check()

        tex = self.preamble.oficio

        tex += '\n'
        tex += '\n'

        tex += '\\begin{document}\n'

        # HEAD
        tex += '\\begin{minipage}[t]{0.6\\textwidth}\n'
        tex += self.emitter.oficio
        tex += '\\end{minipage}%\n'
        tex += '\\begin{minipage}[t]{0.4\\textwidth}\n'
        tex += '    \\vspace{-2em}\n'
        tex += self.sii_patch.oficio
        tex += '    \\vfill\n'
        tex += '\\end{minipage}%\n'
        tex += '\\vspace{2mm}\n'
        tex += self.receiver.oficio

        # ITEMS
        tex += '\\begin{mdframed}[style=items]\n'
        tex += self.items.oficio
        tex += '\\end{mdframed}\n'

        tex += '\\null\n'
        tex += '\\vfill\n'

        # FEFERENCES
        tex += '\\begin{mdframed}[style=references]\n'
        tex += self.references.oficio
        tex += '\\end{mdframed}\n'

        # PAYMENTS/TOTALS
        tex += '\\begin{mdframed}[style=summary]\n'
        tex += '    \\vspace{-1em} % Zip them together (suppress spacing)\n'
        tex += '    \\begin{minipage}[t]{0.7\\textwidth}\n'
        tex += self.payments.oficio
        tex += '    \\end{minipage}%\n'
        tex += '    \\begin{minipage}[t]{0.3\\textwidth}\n'
        tex += self.totals.oficio
        tex += '    \\end{minipage}\n'
        tex += '    \\vspace{-1em} % Zip them together (suppress spacing)\n'
        tex += '\\end{mdframed}\n'

        # BARCODE
        tex += '\\begin{minipage}[t]{0.5\\textwidth}\n'
        tex += '    \\vfill\n'
        tex += self.barcode.oficio
        tex += '\\end{minipage}%\n'

        # SIGNATURE (§19.983)
        tex += '\\begin{minipage}[t]{0.5\\textwidth}\n'
        tex += '    \\vfill\n'
        tex += self.signature.oficio
        tex += '\\end{minipage}%\n'

        # DISCLAIMER
        tex += self.disclaimer.oficio

        tex += '\\end{document}\n'
        return tex

    @property
    def thermal80mm(self):
        self._check()

        tex = self.preamble.thermal80mm

        tex += '\n'
        tex += '\n'

        tex += '\\begin{document}\n'

        tex += self.sii_patch.thermal80mm
        tex += '\\vspace{1mm}\n'
        tex += '\hrule\hrule\hrule\n'
        tex += self.emitter.thermal80mm
        tex += '\hrule\hrule\hrule\n'
        tex += self.receiver.thermal80mm
        tex += '\hrule\hrule\hrule\n'
        tex += self.items.thermal80mm
        tex += '\hrule\hrule\hrule\n'
        tex += self.totals.thermal80mm
        tex += '\hrule\hrule\hrule\n'
        tex += self.payments.thermal80mm
        tex += '\hrule\hrule\hrule\n'
        tex += self.references.thermal80mm
        tex += '\hrule\hrule\hrule\n'

        # FOOTER
        tex += self.signature.thermal80mm
        tex += self.barcode.thermal80mm
        tex += self.disclaimer.thermal80mm

        tex += '\\end{document}\n'
        return tex

    def set_preamble(self, preamble_section):
        self.preamble                 = preamble_section
        preamble_section.__document__ = self

    def set_emitter(self, emitter_section):
        self.emitter                 = emitter_section
        emitter_section.__document__ = self

    def set_sii_patch(self, sii_patch_section):
        self.sii_patch                 = sii_patch_section
        sii_patch_section.__document__ = self

    def set_receiver(self, receiver_section):
        self.receiver                 = receiver_section
        receiver_section.__document__ = self

    def set_items(self, items_section):
        self.items                 = items_section
        items_section.__document__ = self

    def set_payments(self, payments_section):
        self.payments                 = payments_section
        payments_section.__document__ = self

    def set_totals(self, totals_section):
        self.totals                 = totals_section
        totals_section.__document__ = self

    def set_references(self, references_section):
        self.references                 = references_section
        references_section.__document__ = self

    def set_barcode(self, barcode_section):
        self.barcode                 = barcode_section
        barcode_section.__document__ = self

    def set_signature(self, signature_section):
        self.signature                 = signature_section
        signature_section.__document__ = self

    def set_disclaimer(self, disclaimer_section):
        self.disclaimer                 = disclaimer_section
        disclaimer_section.__document__ = self

    def _check(self):
        errmsg = ""

        if not self.preamble:
            errmsg = "Missing 'Preamble' Section"

        if not self.emitter:
            errmsg = "Missing 'Emitter' Section"

        if not self.sii_patch:
            errmsg = "Missing 'Sii Patch' Section"

        if not self.receiver:
            errmsg = "Missing 'Receiver' Section"

        if not self.items:
            errmsg = "Missing 'Items' Section"

        if not self.payments:
            errmsg = "Missing 'Payments' Section"

        if not self.totals:
            errmsg = "Missing 'Totals' Section"

        if not self.references:
            errmsg = "Missing 'References' Section"

        if not self.barcode:
            errmsg = "Missing 'Barcode' Section"

        if not self.signature:
            errmsg = "Missing 'Signature' Section"

        if not self.disclaimer:
            errmsg = "Missing 'Disclaimer' Section"

        if errmsg:
            raise ValueError("Cannot create Template: " + errmsg) class SectionSignature(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Signature
    %% -----------------------------------------------------------------
    \\begin{mdframed}[style=signature]
        \\begin{spacing}{1.5}
            \\%s{
                Nombre:{\\leaders\\hbox{\\rule{1mm}{0.8pt}}\\hfill}  \\\\
                RUT:{\\leaders\\hbox{\\rule{1mm}{0.8pt}}\\hfill}
                FECHA:{\\leaders\\hbox{\\rule{1mm}{0.8pt}}\\hfill}   \\\\
                Recinto:{\\leaders\\hbox{\\rule{1mm}{0.8pt}}\\hfill}
                FIRMA:{\\leaders\\hbox{\\rule{1mm}{0.8pt}}\\hfill}
            }
        \\end{spacing}%%%%
        %%%%
        \\tiny{
            El acuse de recibo que se declara en este acto, de acuerdo a lo
            dispuesto en la letra b) del Art. 4° y la letra c) del Art. 5° de la
            Ley 19.983, acredita que la entrega de mercadería(s) o servicio(s)
            prestado(s) ha(n) sido recibido(s).
        }
    \\end{mdframed}
    \\vspace{0.5em}
    %s
    """
    @property
    def carta(self):
        return self._template('small')

    @property
    def oficio(self):
        return self._template('small')

    @property
    def thermal80mm(self):
        return self._template('scriptsize')

    def _template(self, size):
        assert self.__document__, "Have not been yet registered onto a Document"

        if self.__document__.tex_cedible:
            if self.__document__.doc_type == 52:
                cedible = "CEDIBLE CON SU FACTURA"
            else:
                cedible = "CEDIBLE"

            return self.__doc__ % (size, "\\raggedleft{\\textbf{%s}}" % cedible)
        else:
            return "" class SectionPreamble(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Preamble
    %% -----------------------------------------------------------------
    \\documentclass[{options}]{{article}}

    \\usepackage[paperwidth={width}mm,%
                paperheight={height}mm,%
                top={top}mm,%
                bottom={bottom}mm,%
                left={left}mm,%
                right={right}mm]{{geometry}}
    \\usepackage[utf8]{{inputenc}}
    \\usepackage[spanish]{{babel}}
    \\usepackage[parfill]{{parskip}}
    \\usepackage{{setspace}}
    \\usepackage{{textcomp}}
    \\usepackage{{mdframed}}
    \\usepackage{{longtable}}
    \\usepackage{{tabu}}
    \\usepackage{{graphicx}}

    \\pagestyle{{empty}}

    """

    def __init__(self, size=10, draft=False):
        self.size  = size
        self.draft = draft

    @property
    def carta(self):
        tex = self.__doc__.format(
            options = self._doc_options(),
            width   = 216,
            height  = 279,
            top     = 8,
            bottom  = 5,
            left    = 10,
            right   = 10
        )

        tex += """
        \\mdfdefinestyle{siipatch}{nobreak=true          ,%
                                  userdefinedwidth=8cm  ,%
                                  align=center          ,%
                                  linewidth=0.8mm       ,%
                                  innerleftmargin=0.5cm ,%
                                  innerrightmargin=0.5cm}

        \\mdfdefinestyle{emitter}{nobreak=true          ,%
                                 leftline=false        ,%
                                 rightline=false       ,%
                                 linewidth=0.5mm       ,%
                                 skipabove=1mm         ,%
                                 innertopmargin=0mm    ,%
                                 innerbottommargin=0mm ,%
                                 innerleftmargin=0cm   ,%
                                 innerrightmargin=0cm}

        \\mdfdefinestyle{items}{nobreak=true          ,%
                               topline=false         ,%
                               leftline=false        ,%
                               rightline=false       ,%
                               bottomline=false      ,%
                               linewidth=0.5mm       ,%
                               innertopmargin=0mm    ,%
                               innerbottommargin=0mm ,%
                               innerleftmargin=0cm   ,%
                               innerrightmargin=0cm}

        \\mdfdefinestyle{summary}{nobreak=true          ,%
                                 leftline=false        ,%
                                 rightline=false       ,%
                                 bottomline=false      ,%
                                 linewidth=0.5mm       ,%
                                 innertopmargin=0mm    ,%
                                 innerbottommargin=0mm ,%
                                 innerleftmargin=0cm   ,%
                                 innerrightmargin=0cm}

        \\mdfdefinestyle{references}{nobreak=true          ,%
                                    topline=false         ,%
                                    leftline=false        ,%
                                    rightline=false       ,%
                                    bottomline=false      ,%
                                    linewidth=0.5mm       ,%
                                    innertopmargin=0mm    ,%
                                    innerbottommargin=0mm ,%
                                    innerleftmargin=0cm   ,%
                                    innerrightmargin=0cm}

        \\mdfdefinestyle{signature}{nobreak=true    ,%
                                   linewidth=0.5mm ,%
                                   innertopmargin=0.5cm}
        """
        return tex

    @property
    def oficio(self):
        tex  = self.__doc__.format(
            options = self._doc_options(),
            width   = 216,
            height  = 330,
            top     = 8,
            bottom  = 5,
            left    = 10,
            right   = 10
        )

        tex += """
        \\mdfdefinestyle{siipatch}{nobreak=true          ,%
                                  userdefinedwidth=8cm  ,%
                                  align=center          ,%
                                  linewidth=0.8mm       ,%
                                  innerleftmargin=0.5cm ,%
                                  innerrightmargin=0.5cm}

        \\mdfdefinestyle{emitter}{nobreak=true          ,%
                                 leftline=false        ,%
                                 rightline=false       ,%
                                 linewidth=0.5mm       ,%
                                 skipabove=1mm         ,%
                                 innertopmargin=0mm    ,%
                                 innerbottommargin=0mm ,%
                                 innerleftmargin=0cm   ,%
                                 innerrightmargin=0cm}

        \\mdfdefinestyle{items}{nobreak=true          ,%
                               topline=false         ,%
                               leftline=false        ,%
                               rightline=false       ,%
                               bottomline=false      ,%
                               linewidth=0.5mm       ,%
                               innertopmargin=0mm    ,%
                               innerbottommargin=0mm ,%
                               innerleftmargin=0cm   ,%
                               innerrightmargin=0cm}

        \\mdfdefinestyle{summary}{nobreak=true          ,%
                                 leftline=false        ,%
                                 rightline=false       ,%
                                 bottomline=false      ,%
                                 linewidth=0.5mm       ,%
                                 innertopmargin=0mm    ,%
                                 innerbottommargin=0mm ,%
                                 innerleftmargin=0cm   ,%
                                 innerrightmargin=0cm}

        \\mdfdefinestyle{references}{nobreak=true          ,%
                                    topline=false         ,%
                                    leftline=false        ,%
                                    rightline=false       ,%
                                    bottomline=false      ,%
                                    linewidth=0.5mm       ,%
                                    innertopmargin=0mm    ,%
                                    innerbottommargin=0mm ,%
                                    innerleftmargin=0cm   ,%
                                    innerrightmargin=0cm}

        \\mdfdefinestyle{signature}{nobreak=true    ,%
                                   linewidth=0.5mm ,%
                                   innertopmargin=0.5cm}
        """
        return tex

    @property
    def thermal80mm(self):
        # XXX we have a problem with properly controlling the rasterization of EPSON T-88V printers
        # thus we have to cheat like this. (TODO: do properly, this is to generic. Not all printers
        # and drivers under CUPS have the same problem)
        if sys.platform in ('linux2', 'darwin'):
            left, right = 0, 16
        else:
            left = right = 5

        tex  = self.__doc__.format(
            options = self._doc_options(),
            width   = 80,
            height  = 297,
            top     = 0,
            bottom  = 0,
            left    = left,
            right   = right
        )

        tex += """
        \\mdfdefinestyle{siipatch}{nobreak=true           ,%
                                   align=center          ,%
                                   linewidth=0.8mm       ,%
                                   innerleftmargin=0.5cm ,%
                                   innerrightmargin=0.5cm}

        \\mdfdefinestyle{signature}{nobreak=true          ,%
                                    linewidth=0.3mm      ,%
                                    skipabove=1mm        ,%
                                    innertopmargin=0.3cm ,%
                                    innerleftmargin=1mm  ,%
                                    innerrightmargin=1mm}
        """
        return tex

    def _doc_options(self):
        s  = '{size}pt'.format(size=self.size)
        s += ',draft' if self.draft is True else ''

        return s class SectionEmitter(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Emitter
    %% -----------------------------------------------------------------
    \\begin{minipage}[t]{\\textwidth}
        \\begin{center}
            \\Large{\\textbf{%s}}\\break
            \\normalsize{%s}
            \\vspace{2mm}
            {
                \\extrarowsep=_-1pt^-1pt
                \\begin{tabu}{X[-1r] X[-1l]}
                    \\rowfont{\\scriptsize}
                    \\everyrow{\\rowfont{\\scriptsize}}
                    \\textbf{CASA MATRIZ:}      & %s  \\\\
                    \\textbf{SUCURSAL EMISORA:} & %s  \\\\
                    \\textbf{FONO:}             & %s  \\\\
                \\end{tabu}
            }
            \\vspace{2mm}
            %s
        \\end{center}
        \\vfill
    \\end{minipage}%%%%
    """
    def __init__(self, emitter_name_long, emitter_name_short,
                       emitter_activity,
                       emitter_hq_addr, emitter_branch_addr, emitter_phone,
                       order_number='', emitter_salesman='', licence_plate='',
                       logo_path=''):
        self._emitter_name_long   = emitter_name_long
        self._emitter_name_short  = emitter_name_short
        self._emitter_activity    = emitter_activity
        self._emitter_hq_addr     = emitter_hq_addr
        self._emitter_branch_addr = emitter_branch_addr
        self._emitter_phone       = emitter_phone

        # carta/oficio specifics
        self._logo_path = logo_path
        if self._logo_path:
            with open(self._logo_path, 'rb') as fh:
                self._logo_data = fh.read()

        # thermal*mm specifics
        self._order_number     = order_number
        self._licence_plate    = licence_plate
        self._emitter_salesman = emitter_salesman

    @property
    def resources(self):
        ress = []

        if self._logo_path:
            _, ext = path.splitext(self._logo_path)
            ress.append(Resource('logo' + ext, self._logo_data))

        return ress

    @property
    def carta(self):
        return self.__doc__ % (
            self._emitter_name_long,
            self._emitter_activity,
            self._emitter_hq_addr,
            self._emitter_branch_addr,
            self._emitter_phone,
            self._logo_template()
        )

    @property
    def oficio(self):
        return self.__doc__ % (
            self._emitter_name_long,
            self._emitter_activity,
            self._emitter_hq_addr,
            self._emitter_branch_addr,
            self._emitter_phone,
            self._logo_template()
        )

    @property
    def thermal80mm(self):
        tex = """
           {
            \\scriptsize
            \\tabulinesep=_1.0mm^1.0mm

            \\textbf{RAZON SOCIAL EMISOR:} \\\\
            \\begin{tabu}{@{} X[-1l] X[l] @{}}
                \\textbf{Nombre:}               & %s  \\\\
                \\textbf{Giro:}                 & %s  \\\\
                \\textbf{Casa Matriz:}          & %s  \\\\
                \\textbf{Sucursal:}             & %s  \\\\
                \\textbf{Fono:}                 & %s  \\\\
                \\textbf{N\\textdegree Pedido:} & %s  \\\\
                \\textbf{Vendedor:}             & %s  \\\\
                \\textbf{Patente:}              & %s  \\\\
            \\end{tabu}
            }%%%%
        """
        return tex % (
            self._emitter_name_short,
            self._emitter_activity,
            self._emitter_hq_addr,
            self._emitter_branch_addr,
            self._emitter_phone,
            self._order_number,
            self._emitter_salesman,
            self._licence_plate
        )

    def _logo_template(self):
        if self._logo_path:
            _, ext = path.splitext(self._logo_path)

            height    = '15mm'
            width     = '0.7\\textwidth'
            keepratio = 'true'

            return '\\includegraphics[height={h}, width={w}, keepaspectratio={r}]{{logo{ext}}}'.format(
                h   = height,
                w   = width,
                r   = keepratio,
                ext = ext
            )
        else:
            return '' class SectionEmitterProvider(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Emitter
    %% -----------------------------------------------------------------
    \\begin{minipage}[t]{\\textwidth}
        \\begin{center}
            \\Large{\\textbf{%s}}\\break
            \\normalsize{%s}
            \\vspace{2mm}
            {
                \\extrarowsep=_-1pt^-1pt
                \\begin{tabu}{X[-1r] X[-1l]}
                    \\rowfont{\\scriptsize}
                    \\everyrow{\\rowfont{\\scriptsize}}
                    \\textbf{DIRECCION:} & %s \\\\
                    \\textbf{FONO:}      & %s \\\\
                \\end{tabu}
            }
            \\vspace{0.6cm}
            \\centerline{\\textbf{\\large --- DOCUMENTO PROVEEDOR ---}}
        \\end{center}
        \\vfill
    \\end{minipage}%%%%
    """
    def __init__(self, emitter_name, emitter_activity, emitter_address, emitter_phone):
        self._emitter_name     = emitter_name
        self._emitter_activity = emitter_activity
        self._emitter_address  = emitter_address
        self._emitter_phone    = emitter_phone

    @property
    def carta(self):
        return self.__doc__ % (
            self._emitter_name,
            self._emitter_activity,
            self._emitter_address,
            self._emitter_phone
        )

    @property
    def oficio(self):
        return self.__doc__ % (
            self._emitter_name,
            self._emitter_activity,
            self._emitter_address,
            self._emitter_phone
        )

    @property
    def thermal80mm(self):
        tex = """
           {
            \\scriptsize
            \\tabulinesep=_1.0mm^1.0mm

            \\textbf{RAZON SOCIAL EMISOR:} \\\\
            \\begin{tabu}{@{} X[-1l] X[l] @{}}
                \\textbf{Nombre:}               & %s  \\\\
                \\textbf{Giro:}                 & %s  \\\\
                \\textbf{Direccion:}            & %s  \\\\
                \\textbf{Fono:}                 & %s  \\\\
            \\end{tabu}
            \\centerline{\\textbf{\\large --- DOCUMENTO PROVEEDOR ---}}
            }%%%%
        """
        return tex % (
            self._emitter_name,
            self._emitter_activity,
            self._emitter_address,
            self._emitter_phone
        ) class SectionSiiPatch(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Sii Patch
    %% -----------------------------------------------------------------
    \\begin{center}
        \\begin{mdframed}[style=siipatch]
            \\begin{center}
                \\large{\\textbf{R.U.T.: %s}}\\break
                \\newline
                \\large{\\textbf{%s}}\\break
                \\newline
                \\large{\\textbf{N\\textdegree\\ %s}}
            \\end{center}
        \\end{mdframed}
        \\vspace{0.5em}
        \\large{\\textbf{S.I.I. - %s}}
        %s
    \\end{center}
    """
    def __init__(self, rut, dte_type, dte_serial, sii_branch, logo_path=''):
        self._rut          = rut
        self._dte_type     = dte_type
        self._dte_type_str = DOC_TYPE_STRINGS[dte_type]
        self._dte_serial   = dte_serial
        self._sii_branch   = sii_branch

        self._logo_path = logo_path
        if self._logo_path:
            with open(self._logo_path, 'r') as fh:
                self._logo_data = fh.read()

    @property
    def resources(self):
        ress = []

        if self._logo_path:
            ress.append(Resource('logo.eps', self._logo_data))

        return ress

    @property
    def carta(self):
        return self.__doc__ % (
            self._rut,
            self._dte_type_str,
            self._dte_serial,
            self._sii_branch,
            ''
        )

    @property
    def oficio(self):
        return self.__doc__ % (
            self._rut,
            self._dte_type_str,
            self._dte_serial,
            self._sii_branch,
            ''
        )

    @property
    def thermal80mm(self):
        tex_logo = ''
        if self._logo_path:
            tex_logo += '\\break'
            tex_logo += '\\vspace{0.5em}'
            tex_logo += '\\includegraphics[width=0.7\\textwidth]{logo.eps}'

        return self.__doc__ % (
            self._rut,
            self._dte_type_str,
            self._dte_serial,
            self._sii_branch,
            tex_logo
        ) class SectionReceiver(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Receiver
    %% -----------------------------------------------------------------
    \\begin{minipage}{0.5\\textwidth}
    \\begin{flushleft}
        \\small{\\textbf{Emisión:} %s}
    \\end{flushleft}
    \\end{minipage}%%%%
    \\begin{minipage}{0.5\\textwidth}
    \\begin{flushright}
        \\small{\\textbf{Vencimiento:} %s}
    \\end{flushright}
    \\end{minipage}

    \\begin{mdframed}[style=emitter]
        {
            \\tabulinesep=_1.0mm^1.0mm
            \\vspace{1mm}

            \\begin{tabu}{X[-1r] X[-1l] X[-1r] X[-1l]}
                \\rowfont{\\footnotesize}
                \\everyrow{\\rowfont{\\footnotesize}}
                \\textbf{SEÑOR(ES):} & %s &
                \\textbf{R.U.T.:}    & %s \\\\
                \\textbf{DIRECCION:} & %s &
                \\textbf{COMUNA:}    & %s \\\\
                \\textbf{GIRO:}      & %s &
                \\textbf{CIUDAD:}    & %s \\\\
                \\hline
                \\textbf{VENDEDOR:}             & %s &
                \\textbf{PATENTE:}              & %s \\\\
                \\textbf{N\\textdegree PEDIDO:} & %s &
                                                & \\\\
            \\end{tabu}
        }
    \\end{mdframed}
    """
    def __init__(self, emission_date, expiration_date,
                       receivername, receiverrut, receiveraddress,
                       receivercomune, receiveractivity, receivercity='',
                       emittersalesman='', ordernumber='', licenceplate=''):
        self._emission_date    = emission_date
        self._expiration_date  = expiration_date

        self._receivername     = escape_tex(receivername)
        self._receiverrut      = receiverrut
        self._receiveraddress  = escape_tex(receiveraddress)
        self._receivercomune   = escape_tex(receivercomune)
        self._receiveractivity = escape_tex(receiveractivity)
        self._receivercity     = escape_tex(receivercity)

        self._emittersalesman  = escape_tex(emittersalesman)
        self._ordernumber      = ordernumber
        self._licenceplate     = licenceplate

    @property
    def carta(self):
        return self.__doc__ % (
            self._emission_date,
            self._expiration_date,
            self._receivername,
            self._receiverrut,
            self._receiveraddress,
            self._receivercomune,
            self._receiveractivity,
            self._receivercity,
            self._emittersalesman,
            self._ordernumber,
            self._licenceplate
        )

    @property
    def oficio(self):
        return self.__doc__ % (
            self._emission_date,
            self._expiration_date,
            self._receivername,
            self._receiverrut,
            self._receiveraddress,
            self._receivercomune,
            self._receiveractivity,
            self._receivercity,
            self._emittersalesman,
            self._ordernumber,
            self._licenceplate
        )

    @property
    def thermal80mm(self):
        tex = """
        {
            \\scriptsize
            \\tabulinesep=_1.0mm^1.0mm

            \\textbf{RAZON SOCIAL RECEPTOR:} \\\\
            \\begin{tabu}{@{} X[-1l] X[l] @{}}
                \\textbf{Señor(es):} & %s \\\\
                \\textbf{R.U.T.:}    & %s \\\\
                \\textbf{Giro:}      & %s \\\\
                \\textbf{Dirección:} & %s \\\\
                \\textbf{Comuna:}    & %s \\\\
                \\textbf{Ciudad:}    & %s \\\\
            \\end{tabu}
        }
        """
        return tex % (
            self._receivername,
            self._receiverrut,
            self._receiveractivity,
            self._receiveraddress,
            self._receivercomune,
            self._receivercity
        ) class SectionItems(TemplateElement):
    """
    %%%% -----------------------------------------------------------------
    %%%% SECTION - Items
    %%%% -----------------------------------------------------------------
    {
        %%%% ITEM TABLE
        \\begin{longtabu}{%s}
            %%%% HEADER
            \\rowfont{\\%s}
            \\everyrow{\\rowfont{\\%s}}
            %s

            \\firsthline[1mm]

            %%%% CONTENT
            %s
        \\end{longtabu}

        %s
    }
    """
    def __init__(self, column_layout, table_margins=False, draft=False, provider=False):
        """ Before using this Object you need to determine how the Columns are named, aligned and
        if they are expanding or not.
        You do that by providing the `column_layout` argument in the following Format:

            (
                {'name': 'Colname1', 'align': 'left'|'right'|'center', expand: True|False},
                {'name': 'Colname2', 'align': 'left'|'right'|'center', expand: True|False},
                {'name': 'Colname3', 'align': 'left'|'right'|'center', expand: True|False},
                ...
            )

        For proper LaTeX results it is recomended to make all but one Column non-expanding. This
        way the expanding one will take up as much space as possible without making spacing look
        ugly, but also ensuring everything looks not so overy spread (specially the numeric
        columns)
        """
        self._colsettings   = column_layout
        self._items         = []
        self._table_margins = table_margins
        self._draft         = draft
        self._provider      = provider

        self.__doc__ = self.__doc__ % (
            self._build_tablecols(),
            '%s', '%s', '%s', '%s', '%s'
        )

    def append_row(self, row: tuple):
        """ Expecting the column tuple to be in the expected order the column layout was set up at
        init time.
        """
        if len(row) != len(self._colsettings):
            raise ValueError(
                "Rows have to provide one value per set up Column:\n"
                ">{0}<\n"
                ">{1}<".format(
                    ', '.join([col for col in row]),
                    ', '.join([col['name'] for col in self._colsettings])
                )
            )
        else:
            self._items.append(row)

    @property
    def carta(self):
        return self.__doc__ % (
            'small', 'footnotesize',
            self._build_headers(),
            self._build_rows(),
            self._build_disclaimer()

        )

    @property
    def oficio(self):
        return self.__doc__ % (
            'small', 'footnotesize',
            self._build_headers(),
            self._build_rows(),
            self._build_disclaimer()
        )

    @property
    def thermal80mm(self):
        return self.__doc__ % (
            'scriptsize', 'scriptsize',
            self._build_headers(),
            self._build_rows(),
            self._build_disclaimer()
        )

    def _build_tablecols(self):
        cols = []
        cols.append('' if self._table_margins else '@{}')

        for coldef in self._colsettings:
            setstr  = 'X['
            setstr += {True: '', False: '-1'}[coldef['expand']]
            setstr += {'left': 'l', 'center': 'c', 'right': 'r'}[coldef['align']]
            setstr += ']'
            cols.append(setstr)

        cols.append('' if self._table_margins else '@{}')
        return ' '.join(cols)

    def _build_headers(self):
        cols = ['\\textbf{%s}' % escape_tex(col['name']) for col in self._colsettings]
        tex  = (' & \n' + ' ' * 4 * 3).join(cols)
        tex += ' \\\\\n'

        return tex

    def _build_rows(self):
        rows = []

        for row in self._items:
            stringed = (str(col) for col in row)
            escaped  = (escape_tex(colstr) for colstr in stringed)

            rows.append(' & '.join(escaped) + ' \\\\')

        return '\n'.join(rows)

    def _build_disclaimer(self):
        assert self.__document__, "Have not been yet registered onto a Document"

        disclaimers = []
        doc_gd_type = self.__document__.doc_gd_type

        if doc_gd_type:
            disclaimers.append('\\centerline{\\textbf{\\large --- %s ---}}' % GUIA_DESPACHO_TYPES[doc_gd_type])

        if self._draft:
            disclaimers.append('\\centerline{\\textbf{\\large --- %s ---}}' % "BORRADOR")

        if self._provider:
            disclaimers.append('\\centerline{\\textbf{\\large --- %s ---}}' % "DOCUMENTO PROVEEDOR")

        return "\n".join(disclaimers) class SectionPayments(TemplateElement):
    """
    %%%% -----------------------------------------------------------------
    %%%% SECTION - Payments
    %%%% -----------------------------------------------------------------
    {
        \extrarowsep=^1pt_1pt

        \\begin{longtabu}{%s}
            %%%% HEADER
            \\rowfont{\\%s}
            \everyrow{\\rowfont{\\%s}}
            \\textbf{Tipo Pago} &
            \\textbf{Monto[\$]} &
            \\textbf{Detalle}   \\\\

            %%%% DATA
            \\rowfont{\\%s}
            \everyrow{\\rowfont{\\%s}}
            %s
        \end{longtabu}
    }
    """
    def __init__(self, table_margins=False):
        self._payments = []

        self._table_margins = table_margins

    def append_payment(self, mode, amount, detail):
        self._payments.append((mode, amount, detail))

    @property
    def carta(self):
        return self.__doc__ % (
            self._build_tablecols(),
            'small',
            'small',
            'footnotesize',
            'footnotesize',
            self._build_payments()
        )

    @property
    def oficio(self):
        return self.__doc__ % (
            self._build_tablecols(),
            'small',
            'small',
            'footnotesize',
            'footnotesize',
            self._build_payments()
        )

    @property
    def thermal80mm(self):
        return self.__doc__ % (
            self._build_tablecols(),
            'scriptsize',
            'scriptsize',
            'scriptsize',
            'scriptsize',
            self._build_payments()
        )

    def _build_tablecols(self):
        return 'X[-1l] X[-1r] X[l]' if self._table_margins else '@{} X[-1l] X[-1r] X[l] @{}'

    def _build_payments(self):
        pays = []
        for pay in self._payments:
            pays.append('{0} & {1} & {2} \\\\'.format(*pay))

        if pays:
            return ('\n' + ' ' * 4 * 3).join(pays)
        else:
            return '' class SectionTotals(TemplateElement):
    """
    %%%% -----------------------------------------------------------------
    %%%% SECTION - Totals
    %%%% -----------------------------------------------------------------
    {
        %s
        \\begin{longtabu}{%s}
            \\rowfont{\\%s}
            \\everyrow{\\rowfont{\\%s}}
            \\textbf{Desc. Global:}   & %s \\$ \\\\
            \\textbf{Monto Neto:}     & %s \\$ \\\\
            \\textbf{Monto Exento:}   & %s \\$ \\\\
            \\textbf{(19\\%%) IVA:}   & %s \\$ \\\\
            %s

            \\tabucline{%s}

            \\textbf{Monto Total:}  & %s \\$ \\\\
        \\end{longtabu}
        %s
    }
    """
    def __init__(self, discount, net_value, exempt_value, tax, total, special_tax=None):
        """ The only thing to remark here is the **kwargs/"other tax" which
        expects the following structure:

            {
                19: (12, 123456),  # as in... <code>: (<rate>, <value>)
                ...
            }
        """
        self._payments = []

        self._discount     = discount
        self._net_value    = net_value
        self._exempt_value = exempt_value
        self._tax          = tax
        self._other_tax    = special_tax or {}
        self._total        = total

    @property
    def carta(self):
        tex = self.__doc__ % (
            '',
            'X[-1r] X[-1r]',
            'small',
            'small',
            self._discount,
            self._net_value,
            self._exempt_value,
            self._tax,
            self._build_other_tax(),
            '2-',
            self._total,
            ''
        )
        return tex

    @property
    def oficio(self):
        tex = self.__doc__ % (
            '',
            'X[-1r] X[-1r]',
            'small',
            'small',
            self._discount,
            self._net_value,
            self._exempt_value,
            self._tax,
            self._build_other_tax(),
            '2-',
            self._total,
            ''
        )
        return tex

    @property
    def thermal80mm(self):
        tex_spacing  = '\\extrarowsep=^-1pt_-1pt'
        tex_spacing += '\\vspace{-1em}'

        tex = self.__doc__ % (
            tex_spacing,
            '@{} X[-1l] X[r] @{}',
            'scriptsize',
            'scriptsize',
            self._discount,
            self._net_value,
            self._exempt_value,
            self._tax,
            self._build_other_tax(),
            '-',
            self._total,
            '\\vspace{-1em}'
        )
        return tex

    def _build_other_tax(self):
        rows = []

        for code, detail in self._other_tax.items():
            name, rate, sign = SPECIAL_TAX[code]
            _, value         = detail

            value_templ = ""
            if sign == '-':
                value_templ += "-{0}"
            else:
                value_templ += "{0}"

            rows.append(
                '\\textbf{(%s\\%%) %s:} & %s \\$\\\\' % (
                    value_templ.format(rate),
                    name,
                    value_templ.format(value)
                )
            )

        if rows:
            return ('\n' + ' ' * 4 * 3).join(rows)
        else:
            return '' class SectionReferences(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - References
    %% -----------------------------------------------------------------
    {
        %%%% ITEM TABLE
        \\begin{longtabu}{@{} X[-1l] X[l] X[-1r] X[-1r] X[-1r] @{}}
            %%%% HEADER
            \\rowfont{\\%s}
            \\everyrow{\\rowfont{\\%s}}
            \\textbf{Nro.}  &
            \\textbf{Razón} &
            \\textbf{Tipo}  &
            \\textbf{Folio} &
            \\textbf{Fecha} \\\\

            %%\\tabucline{1-4}
            \\firsthline[1mm]

            %%%% CONTENT
            %s
        \\end{longtabu}
    }
    """
    def __init__(self):
        self._refs = []

    def append_reference(self, reason, index, dte_type, dte_serial, dte_date):
        self._refs.append((
            index,
            reason,
            DOC_TYPE_STRINGS[dte_type],
            dte_serial,
            dte_date
        ))

    @property
    def carta(self):
        return self.__doc__ % (
            'small',
            'footnotesize',
            self._build_references(),
        )

    @property
    def oficio(self):
        return self.__doc__ % (
            'small',
            'footnotesize',
            self._build_references()
        )

    @property
    def thermal80mm(self):
        return self.__doc__ % (
            'scriptsize',
            'scriptsize',
            self._build_references()
        )

    def _build_references(self):
        refs = []

        for ref in self._refs:
            refs.append('{0} & {1} & {2} & {3} & {4}\\\\'.format(*ref))

        if refs:
            return ('\n' + ' ' * 4 * 3).join(refs)
        else:
            return '– sin referencias –' class SectionBarcode(TemplateElement):
    """
    %% -----------------------------------------------------------------
    %% SECTION - Barcode
    %% -----------------------------------------------------------------
    \\begin{center}
        \\includegraphics[width=%s\\textwidth]{barcode.eps} \\\\
        \\scriptsize{
            Timbre Electrónico SII \\\\
            Res. %s del %s - Verifique documento: www.sii.cl
        }
    \\end{center}
    """
    def __init__(self, data, resolution_number, resolution_datestr):
        self._data        = data
        self._res_number  = resolution_number
        self._res_datestr = resolution_datestr

        self._barcode = None

    @property
    def resources(self):
        ress = []
        ress.append(Resource('barcode.eps', self._eps))

        return ress

    @property
    def carta(self):
        tex = self.__doc__ % (
            0.9,
            self._res_number,
            self._res_datestr
        )
        return tex

    @property
    def oficio(self):
        tex = self.__doc__ % (
            0.9,
            self._res_number,
            self._res_datestr
        )
        return tex

    @property
    def thermal80mm(self):
        tex = self.__doc__ % (
            1.0,
            self._res_number,
            self._res_datestr
        )
        return tex

    @property
    def _eps(self):
        if not self._barcode:
            pdf417        = PDF417(self._data)
            self._barcode = pdf417.eps
        return self._barcode class SectionDisclaimer(TemplateElement):
    """
    %%%% -----------------------------------------------------------------
    %%%% SECTION - Disclaimer
    %%%% -----------------------------------------------------------------
    \\tiny{
        %s\\ (%s) queda facultado(a) para informar y publicar en los registros
        o bancos de datos personales que operan en el país, u otras empresas con similares
        servicios, la mora o incumplimiento de las obligaciones expresadas en esta factura.
    }
    """
    def __init__(self, company_name, company_rut):
        self._company_name = company_name
        self._company_rut  = company_rut

    @property
    def carta(self):
        return self.__doc__ % (self._company_name, self._company_rut)

    @property
    def oficio(self):
        return self.__doc__ % (self._company_name, self._company_rut)

    @property
    def thermal80mm(self):
        return self.__doc__ % (self._company_name, self._company_rut) class SectionDisclaimerDummy(TemplateElement):
    """
    %%%% -----------------------------------------------------------------
    %%%% SECTION - Disclaimer
    %%%% -----------------------------------------------------------------
    \\tiny{
        Este documento ha sido emitido por una tercera parte, siendo esta impresion en papel
        generada por el receptor para fines internos de archivacion u otros.
    }
    """
    def __init__(self):
        pass

    @property
    def carta(self):
        return self.__doc__

    @property
    def oficio(self):
        return self.__doc__

    @property
    def thermal80mm(self):
        return self.__doc__
import re
import base64
import datetime
import tempfile
import os.path as path
from sii.lib     import types
from sii.lib.lib import format  as fmt
from sii.lib.lib import xml     as xml
from sii.lib.lib import syscall as sys
from . import Document
from . import SectionSignature
from . import SectionPreamble
from . import SectionEmitter, SectionEmitterProvider
from . import SectionSiiPatch
from . import SectionReceiver
from . import SectionItems
from . import SectionPayments
from . import SectionTotals
from . import SectionReferences
from . import SectionBarcode
from . import SectionDisclaimer, SectionDisclaimerDummy

        return emitter


def _assemble_siipatch(dte, company):
    dte_type  = int(dte.Documento.Encabezado.IdDoc.TipoDTE)
    dte_folio = int(dte.Documento.Encabezado.IdDoc.Folio)

    info_rut = None
    info_sii = None

    if company:
        info_rut = fmt.rut(*company.rut_full.split('-'))
        info_sii = company.sii_office_location
    else:
        info_rut = fmt.rut(*str(dte.Documento.Encabezado.Emisor.RUTEmisor).split('-'))
        info_sii = "---------------"

    patch = SectionSiiPatch(
        rut        = info_rut,
        dte_type   = dte_type,
        dte_serial = dte_folio,
        sii_branch = info_sii,
        logo_path  = ""
    )

    return patch


def _assemble_receiver(dte, company):
    receiver = SectionReceiver(
        emission_date    = str(dte.Documento.Encabezado.IdDoc.FchEmis),
        expiration_date  = str(dte.Documento.Encabezado.IdDoc.FchVenc),
        receivername     = str(dte.Documento.Encabezado.Receptor.RznSocRecep),
        receiverrut      = fmt.rut(*str(dte.Documento.Encabezado.Receptor.RUTRecep).split('-')),
        receiveraddress  = str(dte.Documento.Encabezado.Receptor.DirRecep),
        receivercomune   = _str_or_none(dte.Documento.Encabezado.Receptor, 'CmnaRecep'),
        receiveractivity = str(dte.Documento.Encabezado.Receptor.GiroRecep),
        receivercity     = _str_or_none(dte.Documento.Encabezado.Receptor, 'CiudadRecep'),
        emittersalesman  = '',
        ordernumber      = '',
        licenceplate     = ''
    )

    return receiver


def _assemble_items(dte, company, draft, provider):
    items = SectionItems(
        column_layout = (
            {  # NroLinDet
                'name': 'Nro.',
                'align': 'left',
                'expand': False
            },
            {  # QtyItem
                'name': 'Cant.',
                'align': 'left',
                'expand': False
            },
            {  # NmbItem
                'name': 'Detalle',
                'align': 'left',
                'expand': True
            },
            {
                'name': 'Unmd.',
                'align': 'center',
                'expand': False
            },
            {
                'name': 'P.Unit.[$]',
                'align': 'right',
                'expand': False
            },
            {
                'name': 'Dscto.[$]',  # TODO complete with percentage or amount depending on the provided unit
                'align': 'right',
                'expand': False
            },
            {  # MontoItem
                'name': 'Valor[$]',
                'align': 'right',
                'expand': False
            }
        ),
        table_margins = False,
        draft         = draft,
        provider      = provider
    )

    def extract_price(detail):
        if any([det.PrcItem._float % 1 != 0 for det in dte.Documento.Detalle if hasattr(det, 'PrcItem')]):
            return float(detail.PrcItem)
        else:
            return int(detail.PrcItem)

    for detalle in dte.Documento.Detalle:
        col_number   = str(detalle.NroLinDet)                               if hasattr(detalle, 'NroLinDet')      else "-"
        col_quantity = str(detalle.QtyItem._number)                         if hasattr(detalle, 'QtyItem')        else "-"
        col_detail   = str(detalle.NmbItem)                                 if hasattr(detalle, 'NmbItem')        else "-"
        col_unit     = str(detalle.UnmdItem)                                if hasattr(detalle, 'UnmdItem')       else "-"
        col_price    = fmt.thousands(extract_price(detalle),      zero="-") if hasattr(detalle, 'PrcItem')        else "-"
        col_discount = fmt.thousands(int(detalle.DescuentoMonto), zero="-") if hasattr(detalle, 'DescuentoMonto') else "-"
        col_total    = fmt.thousands(int(detalle.MontoItem),      zero="-") if hasattr(detalle, 'MontoItem')      else "-"

        items.append_row((
            col_number,
            col_quantity,
            col_detail,
            col_unit,
            col_price,
            col_discount,
            col_total
        ))

    return items


def _assemble_payments(dte, company):

    payments = SectionPayments(