#! /usr/bin/python2
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006-2018  CZ.NIC, z. s. p. o.
#
# This file is part of FRED.
#
# FRED is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FRED is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with FRED.  If not, see <https://www.gnu.org/licenses/>.

"""Usage: doc2pdf [OPTION...] filename.rml

Render the standard input (RML) and output a PDF file.

 Options:
  -? --help               this help
  -f --config=FILENAME    use configuration file
  -o --output=FILENAME    output into file (default is stdout)
                          special value 'no' for profiling

Examples: 
  fred-doc2pdf input.rml > output.pdf
  fred-doc2pdf -o output.pdf input.rml
  xsltproc template.xsl data.xml | doc2pdf > output.pdf
  xsltproc template.xsl data.xml | doc2pdf -o output.pdf
"""

#    This module is running on the Tiny RML2PDF system (http://openreport.org/) 
#    and  The ReportLab Open Source PDF library (the ReportLab Toolkit) 
#    http://www.reportlab.org/.
#    
#    It was written due to these system don't work with UTF8 encoding.
#    
#    For this purpose module works with TrueType fonts. It set path
#    and load default font. This names are must be saved into configuration.py
#    module.
#    
#    Templates must declare used font names and this names must be accessible
#    on the defined path.
#    
#    Here is example how to declare font familly in RML template:
#    
#    <docinit>
#      <registerDefaultFont 
#        fontName="Times-Roman" fontFile="Times_New_Roman.ttf" 
#        fontNameBold="Times-Bold" fontFileBold="Times_New_Roman_Bold.ttf" 
#        fontNameItalic="Times-Italic" fontFileItalic="Times_New_Roman_Italic.ttf" 
#        fontNameBoldItalic="Times-BoldItalic" fontFileBoldItalic="Times_New_Roman_Bold_Italic.ttf" 
#        />
#      <registerFont 
#        fontName="Arial" fontFile="Arial.ttf" 
#        fontNameBold="Arial-Bold" fontFileBold="Arial_Bold.ttf" 
#        fontNameItalic="Arial-Italic" fontFileItalic="Arial_Italic.ttf" 
#        fontNameBoldItalic="Arial-BoldItalic" fontFileBoldItalic="Arial_Bold_Italic.ttf" 
#        />
#    </docinit>
#
#    python -m profile fred-doc2pdf -o no test_invoice.rml > profile.log

import ConfigParser
import os
import sys
import getopt
import warnings

import reportlab
from reportlab import platypus
from reportlab.graphics.barcode import createBarcodeDrawing, getCodes
from reportlab.graphics import renderPDF
from reportlab.pdfbase.ttfonts import TTFont
try:
    from trml2pdf import trml2pdf, utils
except ImportError:
    # XXX: Avoid bug in Fedora trml2pdf12 package, https://bugzilla.redhat.com/show_bug.cgi?id=1623822
    trml2pdf, utils = None, None

DEFAULT_CONFIG_FILENAME = '/etc/fred/fred-doc2pdf.conf'

# variables form configuration file
CONFIG = {}
HOOKS = {}

# []
# _calc_height( 639.968503937 481.228346457 None None )
cache_tables = {}
cache_cells = {}

def fnc(*argvs):
    'Function in case if required function mising.'
    sys.stderr.write("Error: Missing function.\n")
    sys.stderr.write("argvs: %s.\n"%str(argvs))


class ParserError(Exception):
    "Parser error is risen when some node or attribute is not correct."


def get_config_from_option(argv):
    params = {}
    args = []
    try:
        opt, args = getopt.getopt(argv[1:], 'f:hto:', ['config=', 'help', 'test=', 'output='])
        status = 1
    except getopt.GetoptError, msg:
        sys.stderr.write('Option Error:%s\n'%msg)
        status = 0
    else:
        for k, v in opt:
            params[k] = v
        
    return status, params, args


def get_config(config_filename=None):
    """Return config parser with configuration loaded.

    @param config_filename: Path to custom configuration file provided from command line.
    """
    config = ConfigParser.SafeConfigParser()
    config.read(DEFAULT_CONFIG_FILENAME)
    if config_filename is not None:
        config.read(config_filename)
    return config


def attr_get(node, attrs, dict={}):
    """Overwrite function attr_get() in module utils. We need encode unicodes.
    This HOOK fix problem with unicode.
    """
    res = {}
    for name in attrs:
        if node.hasAttribute(name):
            res[name] = utils.unit_get(node.getAttribute(name))
    for key in dict:
        if node.hasAttribute(key):
            if dict[key]=='str':
                    # res[key] = str(node.getAttribute(key))
                    # we need encode unicode to local encoding:
                    value = node.getAttribute(key)
                    if type(value) is unicode: value = value.encode(trml2pdf.encoding)
                    res[key] = str(value)
            elif dict[key]=='bool':
                res[key] = utils.bool_get(node.getAttribute(key))
            elif dict[key]=='int':
                res[key] = int(node.getAttribute(key))
    #
    # Dirty hack for setting pagesize attribute of pageTemplate.
    # rml2pdf doesn't pass pagesize attribute of pageTemplate into reportlab
    # template object to support pages with different size in one document.
    # This hack will do it instead of rml2pdf
    #
    if node.localName=='pageTemplate' and node.hasAttribute('pageSize'):
        ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
        pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
        res['pagesize']=pageSize
    return res


"""
*** HOOK OF THE REGISTER FONTS ***
If your documents works with unicode, you need use TrueType fonts.
Here is hook for register TTF indo document.
"""


def fred_docinit(self, els):
    """Evaluate registerTTFont and registerFontFamily elements."""
    for node in els:
        # Evaluate registerTTFont headers
        for font in node.getElementsByTagName('registerTTFont'):
            name = font.getAttribute('faceName').encode('ascii')
            filename = font.getAttribute('fileName').encode('ascii')
            reportlab.pdfbase.pdfmetrics.registerFont(TTFont(name, filename))

        # Evaluate registerFontFamily headers
        for font in node.getElementsByTagName('registerFontFamily'):
            normal = font.getAttribute('normal').encode('ascii')
            bold = font.getAttribute('bold').encode('ascii')
            italic = font.getAttribute('italic').encode('ascii')
            bold_italic = font.getAttribute('boldItalic').encode('ascii')

            # Flags in addMapping are `bold` and `italic`
            reportlab.lib.fonts.addMapping(normal, False, False, normal)
            reportlab.lib.fonts.addMapping(normal, True, False, bold)
            reportlab.lib.fonts.addMapping(normal, False, True, italic)
            reportlab.lib.fonts.addMapping(normal, True, True, bold_italic)


"""
*** Use cache for very long tables ***
"""    
class FredTable(platypus.Table):
    'Optimized for very long tables'

    def __init__(self, data, colWidths=None, rowHeights=None, style=None,
                 repeatRows=0, repeatCols=0, splitByRow=1, emptyTableAction=None, ident=None,
                 hAlign=None, vAlign=None, normalizedData=0, cellStyles=None, rowSplitRange=None,
                 spaceBefore=None, spaceAfter=None):
        "Compatible with Reportlab verion 2.1 and 2.3 and 3.3.0"

        global cache_cells

        seq_types = (tuple, list)

        self.ident = ident
        self._longTableOptimize = 1
        self.hAlign = hAlign or 'CENTER'
        self.vAlign = vAlign or 'MIDDLE'
        if not isinstance(data, seq_types):
            raise ValueError("%s invalid data type" % self.identity())
        self._nrows = nrows = len(data)
        self._cellvalues = []
        _seqCW = isinstance(colWidths, seq_types)
        _seqRH = isinstance(rowHeights, seq_types)
        if nrows: self._ncols = ncols = max(list(map(platypus.tables._rowLen,data)))
        elif colWidths and _seqCW: ncols = len(colWidths)
        else: ncols = 0
        if not emptyTableAction: emptyTableAction = platypus.tables.rl_config.emptyTableAction
        if not (nrows and ncols):
            if emptyTableAction=='error':
                raise ValueError("%s must have at least a row and column" % self.identity())
            elif emptyTableAction=='indicate':
                self.__class__ = platypus.tables.Preformatted
                global _emptyTableStyle
                if '_emptyTableStyle' not in list(globals().keys()):
                    _emptyTableStyle = platypus.tables.ParagraphStyle('_emptyTableStyle')
                    _emptyTableStyle.textColor = platypus.tables.colors.red
                    _emptyTableStyle.backColor = platypus.tables.colors.yellow
                platypus.tables.Preformatted.__init__(self,'%s(%d,%d)' % (self.__class__.__name__,nrows,ncols), _emptyTableStyle)
            elif emptyTableAction=='ignore':
                self.__class__ = platypus.tables.Spacer
                platypus.tables.Spacer.__init__(self,0,0)
            else:
                raise ValueError('%s bad emptyTableAction: "%s"' % (self.identity(),emptyTableAction))
            return

        # we need a cleanup pass to ensure data is strings - non-unicode and non-null
        if normalizedData:
            self._cellvalues = data
        else:
            self._cellvalues = data = self.normalizeData(data)
        if not _seqCW: colWidths = ncols*[colWidths]
        elif len(colWidths)!=ncols:
            if platypus.tables.rl_config.allowShortTableRows and isinstance(colWidths,list):
                n = len(colWidths)
                if n<ncols:
                    colWidths[n:] = (ncols-n)*[colWidths[-1]]
                else:
                    colWidths = colWidths[:ncols]
            else:
                raise ValueError("%s data error - %d columns in data but %d in column widths" % (self.identity(),ncols, len(colWidths)))
        if not _seqRH: rowHeights = nrows*[rowHeights]
        elif len(rowHeights) != nrows:
            raise ValueError("%s data error - %d rows in data but %d in row heights" % (self.identity(),nrows, len(rowHeights)))
        for i,d in enumerate(data):
            n = len(d)
            if n!=ncols:
                if platypus.tables.rl_config.allowShortTableRows and isinstance(d,list):
                    d[n:] = (ncols-n)*['']
                else:
                    raise ValueError("%s expected %d not %d columns in row %d!" % (self.identity(),ncols,n,i))
        self._rowHeights = self._argH = rowHeights
        self._colWidths = self._argW = colWidths

        if cellStyles is not None:
            self._cellStyles = cellStyles
        else:
            # speed optimalization:
            self._cellStyles = None
            if cache_cells.has_key(ncols):
                if nrows <= cache_cells[ncols][0]:
                    # use cached objects
                    self._cellStyles = cache_cells[ncols][1]

        if self._cellStyles is None:
            cellrows = []
            for i in xrange(nrows):
                cellcols = []
                for j in xrange(ncols):
                    cellcols.append(platypus.tables.CellStyle(`(i,j)`))
                cellrows.append(cellcols)
            self._cellStyles = cellrows
            # save into cache:
            cache_cells[ncols] = (nrows, cellrows)

        self._bkgrndcmds = []
        self._linecmds = []
        self._spanCmds = []
        self._nosplitCmds = []
        # NB repeatRows can be a list or tuple eg (1,) reapesat only the second row of a table
        # or an integer eg 2 to repeat both rows 0 & 1
        self.repeatRows = repeatRows
        self.repeatCols = repeatCols
        self.splitByRow = splitByRow

        if style:
            self.setStyle(style)

        self._rowSplitRange = rowSplitRange
        if spaceBefore is not None:
            self.spaceBefore = spaceBefore
        if spaceAfter is not None:
            self.spaceAfter = spaceAfter

    def _calc_height(self, availHeight, availWidth, H=None, W=None):
        'replace original at reportlab.platypus.tables._calc_height'

        global cache_tables
    
        # _calc_height( 639.968503937 481.228346457 None None )
        cache_key = '%f:%f:%d' % (availHeight, availWidth, len(self._argH))
        
        if cache_tables.has_key(cache_key):
            # get parsed values from cache
            self._rowHeights, self._height, self._rowpositions = cache_tables[cache_key]
            return
        
        HOOKS.get('original_table_calc_height', fnc)(self, availHeight, availWidth, H, W)

        # keep parsed values in cache
        cache_tables[cache_key] = (self._rowHeights, self._height, self._rowpositions)


def fred_table(self, node):
    length = 0
    colwidths = None
    rowheights = None
    data = []
    childs = trml2pdf._child_get(node,'tr')
    if not childs:
        return None
    for tr in childs:
        data2 = []
        for td in trml2pdf._child_get(tr, 'td'):
            flow = []
            for n in td.childNodes:
                if n.nodeType==node.ELEMENT_NODE:
                    flow.append( self._flowable(n) )
            if not len(flow):
                flow = self._textual(td)
            data2.append( flow )
        if len(data2)>length:
            length=len(data2)
            for ab in data:
                while len(ab)<length:
                    ab.append('')
        while len(data2)<length:
            data2.append('')
        data.append( data2 )
    if node.hasAttribute('colWidths'):
        assert length == len(node.getAttribute('colWidths').split(','))
        colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
    if node.hasAttribute('rowHeights'):
        rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
    # HOOK: Here is reason why this function is overwritten:
    # original code: platypus.Table -> new code: FredTable
    table = FredTable(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
    if node.hasAttribute('style'):
        table.setStyle(self.styles.table_styles[node.getAttribute('style')])
    return table


class BarCode(platypus.flowables.Flowable):
    """
    Origin (x, y) is the 'left bottom corner' of boundary border at <pageGraphics>.

    Code types:
        Standard39
        Extended39
        EAN13
        FIM
        EAN8
        Extended93
        USPS_4State
        Codabar
        MSI
        POSTNET
        Code11
        Standard93
        I2of5
        Code128 (default)

    Attributes:
        * fontName
        * strokeWidth
        * barFillColor
        * humanReadable
        * height
        * debug
        * lquiet
        * background
        * barWidth
        * strokeColor
        * barStrokeWidth
        * barStrokeColor
        * rquiet
        * quiet
        * barHeight
        * width
        * fontSize
        * fillColor
        * textColor
        * showBoundary
    """
    XPOS, YPOS, WIDTH, HEIGHT = range(4)

    def __init__(self, node, styles, value):
        platypus.flowables.Flowable.__init__(self)

        self.node = node
        self.styles = styles
        self.value = value
        self.xpos = utils.unit_get(node.getAttribute('x'))
        self.ypos = utils.unit_get(node.getAttribute('y'))
        self.width = utils.unit_get(node.getAttribute('width'))
        self.height = utils.unit_get(node.getAttribute('height'))
        self.code_name = node.getAttribute('code')
        self.codes = getCodes()

        try:
            self.codes[self.code_name]
        except KeyError, msg:
            raise ParserError("Unknown barcode name '%s'." % self.code_name)

    def get_default_bounds(self):
        "Get default size of barcode"
        bcc = self.codes[self.code_name]
        barcode = bcc(value=self.value)
        return barcode.getBounds() # x, y, width, height

    def wrap(self, *args):
        x, y, width, height = self.get_default_bounds()
        if self.width:
            width = self.width
        if self.height:
            height = self.height
        return (width, height)

    def draw(self):
        kwargs = {"barStrokeWidth": 0.00001}

        units = ("strokeWidth", "barWidth", "barStrokeWidth", "barHeight",
                 "fontSize", "isoScale")
        colors = ("barFillColor", "background", "strokeColor", "barStrokeColor",
                  "fillColor", "textColor")
        bools = ("humanReadable", "debug", "lquiet", "rquiet", "quiet",
                 "checksum")
        names = ("fontName", "routing")

        for attr in colors:
            if self.node.hasAttribute(attr):
                kwargs[attr] = trml2pdf.color.get(self.node.getAttribute(attr))

        for attr in names:
            if self.node.hasAttribute(attr):
                kwargs[attr] = self.node.getAttribute(attr).encode(trml2pdf.encoding)

        for attr in units:
            if self.node.hasAttribute(attr):
                kwargs[attr] = utils.unit_get(self.node.getAttribute(attr))

        for attr in bools:
            if self.node.hasAttribute(attr):
                kwargs[attr] = utils.bool_get(self.node.getAttribute(attr))

        bcd = createBarcodeDrawing(self.code_name, value=self.value,
                                   width=self.width, height=self.height,
                                   **kwargs)
        renderPDF.draw(bcd, self.canv, self.xpos, self.ypos,
                       showBoundary=self.node.getAttribute("showBoundary"))


def _flowable(self, node):
    "Replace orifinal _flowable from trml2pdf.py: _rml_flowable._flowable"
    if node.localName == 'barCode':
        return BarCode(node, self.styles, self._textual(node))

    return HOOKS.get('original_flowable', fnc)(self, node)

        
"""
*** HOOK OF THE TOTAL PAGE NUMBER ***

This code HOOKS modules reportlab and trml2pdf.
These modules in actual versions have not implemented count value of the total page number.
So we need make some hooks until original modules will not updated.

reportlab$ svn info
Path: .
URL: http://www.reportlab.co.uk/svn/public/reportlab/trunk/reportlab
Repository Root: http://www.reportlab.co.uk/svn/public
Repository UUID: 636e621b-80dc-0310-9e7d-ef5f34d1b73f
Revision: 3004
Node Kind: directory
Schedule: normal
Last Changed Author: rgbecker
Last Changed Rev: 3004
Last Changed Date: 2006-12-19 15:50:51 +0100 (\u00dat, 19 pro 2006)

"""

# Global variable keep number of the total page:
num_total_page = 0
# Definition of the MARK what represent place where will be put total page number.
# Decimal   Octal   Hex    Value
# 028          034      01C    FS    (File Separator)
FS = 28
mark_count_page_number = chr(FS)
mark_count_page_number_octal = r'\0%o'%FS


def fred_textual(self, node):
    """Add tag of the Total page number.
    This hook implements new tag pageNumberTotal.
    """
    rc = ''
    for n in node.childNodes:
        if n.nodeType == n.ELEMENT_NODE:
            if n.localName=='pageNumber':
                rc += str(self.canvas.getPageNumber())
            # HOOK: this two lines add new tag for input total page number
            # Warning: If the text in printed as center ro right alignment, it has no effect
            # and number will be align on the same position as an internal mark.
            # TODO: Find better way of the implementation (or wait for next release?)
            elif n.localName=='pageNumberTotal':
                rc += mark_count_page_number
        elif (n.nodeType == node.CDATA_SECTION_NODE):
            rc += n.data
        elif (n.nodeType == node.TEXT_NODE):
            rc += n.data
    return rc.encode(trml2pdf.encoding)


def fred_barcode(self, node):
    "Barcode in the <pageGraphics>"
    bc = BarCode(node, self.styles, self._textual(node))
    bc.canv = self.canvas
    bc.draw()


def fred_canvas_render(self, node):
    "Hook reportlab _canvas_render()"
    tags = {
        'drawCentredString': self._drawCenteredString,
        'drawRightString': self._drawRightString,
        'drawString': self._drawString,
        'rect': self._rect,
        'ellipse': self._ellipse,
        'lines': self._lines,
        'grid': self._grid,
        'curves': self._curves,
        'fill': lambda node: self.canvas.setFillColor(trml2pdf.color.get(node.getAttribute('color'))),
        'stroke': lambda node: self.canvas.setStrokeColor(trml2pdf.color.get(node.getAttribute('color'))),
        'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
        'place': self._place,
        'circle': self._circle,
        'lineMode': self._line_mode,
        'path': self._path,
        'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
        'translate': self._translate,
        'image': self._image,
        'barCode': self._barcode,
        }
    for nd in node.childNodes:
        if nd.nodeType==nd.ELEMENT_NODE:
            for tag in tags:
                if nd.localName==tag:
                    tags[tag](nd)
                    break


def save_num_total_pages(self):
    """Save number of the total pages.
    This hook saves the number of pages for use later in the creating document process.
    """
    global num_total_page
    num_total_page = self.page
    HOOKS.get('original_BaseDocTemplate_endBuild', fnc)(self)


def load_num_total_pages(self, document):
    """Load number of the total pages.
    Befor output data repalce all occurences of the TotalPage mark by real number.
    """
    self.stream = self.stream.replace(mark_count_page_number_octal, str(num_total_page))
    HOOKS.get('original_PDFPage_check_format', fnc)(self, document)


def parse_options(argv):
    """Parse and return commandline options.

    Returns filename of the RML file and configuration filename.
    Returns None if any problem occurs.
    """
    global CONFIG

    config_filename = None
    
    if len(argv) < 2:
        return None
        
    status, opt, args = get_config_from_option(argv)
    if not status:
        return None
    
    if opt.has_key('-h') or opt.has_key('--help'):
        print __doc__
        return None

    if opt.has_key('-f') or opt.has_key('--config'):
        config_filename = opt.has_key('--config') and opt['--config'] or opt['-f']

    filename = None

    if opt.has_key('-o') or opt.has_key('--output'):
        CONFIG['output'] = opt.has_key('-o') and opt['-o'] or opt['--output']

    if sys.stdin.isatty():
        # required filename only in atty mode
        if len(args) == 1:
            filename = args[0]
        else:
            sys.stderr.write('RML file must be set. See help.\n')
    
    return filename, config_filename


# =============================================
#
#   HOOKS - overwrite previous code
#
# =============================================
def init_hooks():
    'Initialisate all hooks into reportalb module.'
    global HOOKS

    # register originals
    # ================================================
    #
    # First KEEP original code:
    # It must precedent functions, because they use it
    #
    # ================================================
    
    # table cache
    HOOKS['original_table_calc_height'] = reportlab.platypus.tables.Table._calc_height
    
    # Fonts
    HOOKS['original_rml_doc_render'] = trml2pdf._rml_doc.render
    
    # Page numbers
    HOOKS['original_BaseDocTemplate_endBuild'] = reportlab.platypus.BaseDocTemplate._endBuild
    HOOKS['original_PDFPage_check_format'] = reportlab.pdfbase.pdfdoc.PDFPage.check_format
    
    HOOKS['original_flowable'] = trml2pdf._rml_flowable._flowable

    # ================================================
    # Second hooks originals:
        
    # ---------------------------------------------
    # fix unicode incompatibilities
    # ---------------------------------------------
    
    # Default encoding (used for TrueType). Don't touch.
    trml2pdf.encoding = 'UTF-8'
    
    # fix problem with unicode
    utils.attr_get = attr_get

    # Hook table cache for speed optimalisation
    trml2pdf._rml_flowable._table = fred_table

    # Hook flowable for barcode.
    trml2pdf._rml_flowable._flowable = _flowable

    # ---------------------------------------------
    # Set of functions for register TrueType fonts.
    # ---------------------------------------------
    trml2pdf._rml_doc.docinit = fred_docinit
    
    # ---------------------------------------------
    # Manage Page numbers
    # ---------------------------------------------
    
    #Add tag of the Total page number.
    #This hook implements new tag pageNumberTotal.
    trml2pdf._rml_canvas._textual = fred_textual
    trml2pdf._rml_canvas.render = fred_canvas_render
    trml2pdf._rml_canvas._barcode = fred_barcode

    #Save number of the total pages.
    #This hook saves the number of pages for use later in the creating document process.
    reportlab.platypus.BaseDocTemplate._endBuild = save_num_total_pages
    
    #Load number of the total pages.
    #Befor output data repalce all occurences of the TotalPage mark by real number.
    reportlab.pdfbase.pdfdoc.PDFPage.check_format = load_num_total_pages
    
    # =============================================
    # Optimalisation for count long tables
    reportlab.rl_config.longTableOptimize = 1
    

def run(body):
    'Run PDF process'
    init_hooks()
    if CONFIG.has_key('output'):
        if CONFIG['output'] == 'no':
            trml2pdf.parseString(body) # no any pdf output
            return
        try:
            fp = open(CONFIG['output'], 'w')
        except IOError, msg:
            sys.stderr.write('IOError: %s.\n'%msg)
        else:
            fp.write(trml2pdf.parseString(body))
            fp.close()
    else:
        print trml2pdf.parseString(body)


def _import_trml2pdf(config):
    """Import trml2pdf module

    Hack to add extra pythonpath to sys.path and import trml2pdf to avoid bug in Feroda trml2pdf12 package
    # https://bugzilla.redhat.com/show_bug.cgi?id=1623822
    """
    global trml2pdf, utils
    if config.has_section('main') and config.has_option('main', 'module_path'):
        pythonpath = config.get('main', 'module_path')
        if pythonpath:
            sys.path.insert(0, pythonpath)
    if trml2pdf is None:
        try:
            from trml2pdf import trml2pdf, utils
        except ImportError:
            sys.stderr.write('trml2pdf module not found.\n')
            exit(1)


def main(argv):
    'Main module function'
    #
    # Python 2.5 throws this warning. It doesn't brake generation process
    # so it's filtered
    #
    warnings.filterwarnings(
        "ignore",
        "struct integer overflow masking is deprecated",
        DeprecationWarning
    )
    options = parse_options(argv)

    if options is None:
        filename, config_filename = None, None
    else:
        filename, config_filename = options

    if config_filename is not None and not os.path.isfile(config_filename):
        sys.stderr.write("Error: Missing configuration file: '{}'.\n".format(config_filename))
        exit(1)

    config = get_config(config_filename)
    _import_trml2pdf(config)

    # Add TTF search path if defined and not empty
    if config.has_section('main') and config.has_option('main', 'true_type_path'):
        true_type_path = config.get('main', 'true_type_path')
        if true_type_path:
            reportlab.rl_config.TTFSearchPath.append(true_type_path)

    if sys.stdin.isatty():
        # run as single command
        if filename is None:
            return
        try:
            body = file(filename).read()
        except IOError, msg:
            sys.stderr.write('IOError: %s.\n'%msg)
        else:
            run(body)
    else:
        # run in pipe
        run(sys.stdin.read())
    



    
if __name__=="__main__":
    main(sys.argv)

