MayaChemTools

    1 #!/bin/env python
    2 #
    3 # File: RDKitDrawMolecules.py
    4 # Author: Manish Sud <msud@san.rr.com>
    5 #
    6 # Copyright (C) 2019 Manish Sud. All rights reserved.
    7 #
    8 # The functionality available in this script is implemented using RDKit, an
    9 # open source toolkit for cheminformatics developed by Greg Landrum.
   10 #
   11 # This file is part of MayaChemTools.
   12 #
   13 # MayaChemTools is free software; you can redistribute it and/or modify it under
   14 # the terms of the GNU Lesser General Public License as published by the Free
   15 # Software Foundation; either version 3 of the License, or (at your option) any
   16 # later version.
   17 #
   18 # MayaChemTools is distributed in the hope that it will be useful, but without
   19 # any warranty; without even the implied warranty of merchantability of fitness
   20 # for a particular purpose.  See the GNU Lesser General Public License for more
   21 # details.
   22 #
   23 # You should have received a copy of the GNU Lesser General Public License
   24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
   25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
   26 # Boston, MA, 02111-1307, USA.
   27 #
   28 
   29 from __future__ import print_function
   30 
   31 # Add local python path to the global path and import standard library modules...
   32 import os
   33 import sys;  sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python"))
   34 import time
   35 import re
   36 
   37 # RDKit imports...
   38 try:
   39     from rdkit import rdBase
   40     from rdkit import Chem
   41     from rdkit.Chem import AllChem
   42     from rdkit.Chem import Draw
   43     from rdkit.Chem.Draw.MolDrawing import DrawingOptions
   44 except ImportError as ErrMsg:
   45     sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg)
   46     sys.stderr.write("Check/update your RDKit environment and try again.\n\n")
   47     sys.exit(1)
   48 
   49 # MayaChemTools imports...
   50 try:
   51     from docopt import docopt
   52     import MiscUtil
   53     import RDKitUtil
   54 except ImportError as ErrMsg:
   55     sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg)
   56     sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n")
   57     sys.exit(1)
   58 
   59 ScriptName = os.path.basename(sys.argv[0])
   60 Options = {}
   61 OptionsInfo = {}
   62 
   63 def main():
   64     """Start execution of the script"""
   65     
   66     MiscUtil.PrintInfo("\n%s (RDK v%s; %s): Starting...\n" % (ScriptName, rdBase.rdkitVersion, time.asctime()))
   67     
   68     (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime()
   69     
   70     # Retrieve command line arguments and options...
   71     RetrieveOptions()
   72     
   73     # Process and validate command line arguments and options...
   74     ProcessOptions()
   75     
   76     # Perform actions required by the script...
   77     DrawMolecules()
   78     
   79     MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName)
   80     MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime))
   81 
   82 def DrawMolecules():
   83     """Draw molecules"""
   84     
   85     Infile = OptionsInfo["Infile"]
   86     Outfile = OptionsInfo["Outfile"]
   87     
   88     # Read molecules...
   89     MiscUtil.PrintInfo("\nReading file %s..." % Infile)
   90 
   91     ValidMols, MolCount, ValidMolCount  = RDKitUtil.ReadAndValidateMolecules(Infile, **OptionsInfo["InfileParams"])
   92     
   93     MiscUtil.PrintInfo("Total number of molecules: %d" % MolCount)
   94     MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount)
   95     MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount))
   96 
   97     # Compute 2D coordinates...
   98     if OptionsInfo["Compute2DCoords"]:
   99         MiscUtil.PrintInfo("\nComputing 2D coordinates...")
  100         for Mol in ValidMols:
  101             AllChem.Compute2DCoords(Mol)
  102     
  103     MiscUtil.PrintInfo("Generating image grid...")
  104     
  105     # Setup atoms lists for highlighting atoms and bonds...
  106     AtomLists = SetupAtomListsToHighlight(ValidMols)
  107     BondLists = None
  108         
  109     # Set up legends...
  110     MolNames = None
  111     if OptionsInfo["ShowMolName"]:
  112         MolNames = []
  113         MolCount = 0
  114         for Mol in ValidMols:
  115             MolCount += 1
  116             MolName = RDKitUtil.GetMolName(Mol, MolCount)
  117             MolNames.append(MolName)
  118     
  119     # Perform alignment to a common template...
  120     PerformAlignment(ValidMols)
  121 
  122     # Generate appropriate output files...
  123     if MiscUtil.CheckFileExt(Outfile, "svg"):
  124         GenerateSVGImageFile(ValidMols, MolNames, AtomLists, BondLists)
  125     elif MiscUtil.CheckFileExt(Outfile, "html htm"):
  126         GenerateHTMLTableFile(ValidMols, MolNames, AtomLists, BondLists)
  127     else:
  128         GenerateImageFile(ValidMols, MolNames, AtomLists, BondLists)
  129     
  130 def GenerateSVGImageFile(ValidMols, MolNames, AtomLists, BondLists):
  131     """Generate a SVG image file."""
  132     
  133     MolsSVGText = RDKitUtil.GetSVGForMolecules(ValidMols, OptionsInfo["NumOfMolsPerRow"], OptionsInfo["MolImageWidth"], OptionsInfo["MolImageHeight"], Legends = MolNames, AtomListsToHighlight = AtomLists, BondListsToHighlight = BondLists, BoldText =  OptionsInfo["FontBold"])
  134     
  135     MiscUtil.PrintInfo("\nGenerating SVG image file %s..." % OptionsInfo["Outfile"])
  136     
  137     OutFH = open(OptionsInfo["Outfile"], "w")
  138     OutFH.write(MolsSVGText)
  139     OutFH.close()
  140     
  141 def GenerateImageFile(ValidMols, MolNames, AtomLists, BondLists):
  142     """Generate a non SVG image file."""
  143     
  144     Outfile = OptionsInfo["Outfile"]
  145     
  146     NumOfMolsPerRow = OptionsInfo["NumOfMolsPerRow"]
  147     Width = OptionsInfo["MolImageWidth"]
  148     Height = OptionsInfo["MolImageHeight"]
  149     
  150     # Setup drawing options...
  151     UpdatedDrawingOptions = DrawingOptions()
  152     UpdatedDrawingOptions.atomLabelFontSize = int(OptionsInfo["AtomLabelFontSize"])
  153     UpdatedDrawingOptions.bondLineWidth = float(OptionsInfo["BondLineWidth"])
  154     
  155     MolsImage = Draw.MolsToGridImage(ValidMols, molsPerRow = NumOfMolsPerRow,  subImgSize = (Width,Height), legends = MolNames, highlightAtomLists = AtomLists, highlightBondLists = BondLists, useSVG = False, kekulize = OptionsInfo["Kekulize"],  options = UpdatedDrawingOptions)
  156     
  157     MiscUtil.PrintInfo("\nGenerating image file %s..." % Outfile)
  158     
  159     if MiscUtil.CheckFileExt(Outfile, "pdf"):
  160         if MolsImage.mode == 'RGBA':
  161             MolsImage = MolsImage.convert('RGB')
  162     
  163     MolsImage.save(Outfile)
  164     
  165 def GenerateHTMLTableFile(ValidMols, MolNames, HighlightAtomLists, HighlightBondLists):
  166     """Generate a HTML table file."""
  167 
  168     Outfile = OptionsInfo["Outfile"]
  169     
  170     Writer = open(Outfile, "w")
  171     if Writer is None:
  172         MiscUtil.PrintError("Failed to setup a writer for output fie %s " % Outfile)
  173     
  174     MiscUtil.PrintInfo("\nGenerating HTML table file %s..." % Outfile)
  175 
  176     WriteHTMLPageHeader(Writer, len(ValidMols))
  177     WriteHTMLPageTitle(Writer)
  178     
  179     WriteHTMLTableHeader(Writer)
  180     WriteHTMLTableRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists)
  181     WriteHTMLTableEnd(Writer)
  182     
  183     WriteHTMLPageFooter(Writer)
  184     WriteHTMLPageEnd(Writer)
  185     
  186     if Writer is not None:
  187         Writer.close()
  188 
  189 def WriteHTMLTableRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists):
  190     """Write out HTML table rows."""
  191 
  192     WriteTableHeaderRow(Writer, ValidMols)
  193     WriteTableDataRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists)
  194     WriteTableFooterRow(Writer, ValidMols)
  195 
  196 def WriteTableDataRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists):
  197     """Write out table data row."""
  198 
  199     Writer.write("""        <tbody>\n""")
  200 
  201     MolCount = len(ValidMols)
  202     ColCount = GetColCount(MolCount)
  203 
  204     for Index in range(0, MolCount, ColCount):
  205         Writer.write("""          <tr>\n""")
  206     
  207         if OptionsInfo["CounterCol"]:
  208             Writer.write("""            <td></td>\n""")
  209 
  210         for MolIndex in range(Index, (Index + ColCount)):
  211             SetupStructureDataDrawing(Writer, MolIndex, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists)
  212         
  213         Writer.write("""          </tr>\n""")
  214         
  215     Writer.write("""        </tbody>\n""")
  216 
  217 def SetupStructureDataDrawing(Writer, MolIndex, Mols, MolNames, HighlightAtomLists, HighlightBondLists):
  218     """Setup structure data drawing for a tabel cell."""
  219     
  220     if MolIndex >= len(Mols):
  221         Writer.write("""            <td></td>\n""")
  222         return
  223 
  224     Mol = Mols[MolIndex]
  225     MolName = None if MolNames is None else MolNames[MolIndex]
  226     HighlightAtomList = None if HighlightAtomLists is None else HighlightAtomLists[MolIndex]
  227     HighlightBondList = None if HighlightBondLists is None else HighlightBondLists[MolIndex]
  228     
  229     SVGText = RDKitUtil.GetInlineSVGForMolecule(Mol, OptionsInfo["MolImageWidth"], OptionsInfo["MolImageHeight"], Legend = MolName, AtomListToHighlight = HighlightAtomList, BondListToHighlight = HighlightBondList, BoldText = OptionsInfo["FontBold"], Base64Encoded = OptionsInfo["MolImageEncoded"])
  230 
  231     PopoverTag = GetMolPopoverTag(Mol)
  232     ImageTag = "img" if PopoverTag is None else "img %s" % PopoverTag
  233     
  234     if OptionsInfo["MolImageEncoded"]:
  235         SVGInlineImageTag = "%s src=\"data:image/svg+xml;base64,\n%s\"" % (ImageTag, SVGText)
  236     else:
  237         SVGInlineImageTag = "%s src=\"data:image/svg+xml;charset=UTF-8,\n%s\"" % (ImageTag, SVGText)
  238     
  239     Writer.write("""            <td bgcolor="white"><%s></td>\n""" % SVGInlineImageTag)
  240 
  241 def WriteTableHeaderRow(Writer, ValidMols):
  242     """Write out table header row."""
  243 
  244     if not OptionsInfo["TableHeader"]:
  245         return
  246     
  247     TableHeaderStyle = OptionsInfo["TableHeaderStyle"]
  248     if TableHeaderStyle is None:
  249         Writer.write("""      <thead>\n""")
  250         Writer.write("""        <tr>\n""")
  251     elif re.match("^(thead|table)", TableHeaderStyle):
  252         Writer.write("""      <thead class="%s">\n""" % TableHeaderStyle)
  253         Writer.write("""        <tr>\n""")
  254     else:
  255         Writer.write("""      <thead>\n""")
  256         Writer.write("""        <tr bgcolor="%s"\n""" % TableHeaderStyle)
  257 
  258     if OptionsInfo["CounterCol"]:
  259         Writer.write("""          <th></th>\n""")
  260     
  261     # Write out rest of the column headers...
  262     MolCount = len(ValidMols)
  263     ColCount = GetColCount(MolCount)
  264     for ColIndex in range(0, ColCount):
  265         ColLabel = MiscUtil.GetExcelStyleColumnLabel(ColIndex + 1)
  266         Writer.write("""          <th>%s</th>\n""" % ColLabel)
  267         
  268     Writer.write("""        </tr>\n""")
  269     Writer.write("""      </thead>\n""")
  270 
  271 def WriteTableFooterRow(Writer, ValidMols):
  272     """Write out table footer row."""
  273 
  274     if not OptionsInfo["TableFooter"]:
  275         return
  276     
  277     Writer.write("""      <tfoot>\n""")
  278     Writer.write("""        <tr>\n""")
  279 
  280     if OptionsInfo["CounterCol"]:
  281         Writer.write("""          <td></td>\n""")
  282 
  283     # Write out rest of the column headers...
  284     MolCount = len(ValidMols)
  285     ColCount = GetColCount(MolCount)
  286     for ColIndex in range(0, ColCount):
  287         ColLabel = MiscUtil.GetExcelStyleColumnLabel(ColIndex + 1)
  288         Writer.write("""          <td>%s</td>\n""" % ColLabel)
  289         
  290     Writer.write("""        </tr>\n""")
  291     Writer.write("""      </tfoot>\n""")
  292 
  293 def WriteHTMLPageHeader(Writer, MolCount):
  294     """Write out HTML page header."""
  295 
  296     ColCount = GetColCount(MolCount)
  297     
  298     # Exclude counter and structure columns from sorting and searching...
  299     if OptionsInfo["CounterCol"]:
  300         ColIndicesList = ["0"]
  301         ColVisibilityExcludeColIndicesList = ["0"]
  302         ColIndexOffset = 1
  303         FreezeLeftColumns = "1"
  304     else:
  305         ColIndicesList = []
  306         ColVisibilityExcludeColIndicesList = []
  307         ColIndexOffset = 0
  308 
  309     MaxDataColVisColCount = 25
  310     for Index in range(0, ColCount):
  311         ColIndex = Index + ColIndexOffset
  312         ColIndicesList.append("%s" % ColIndex)
  313         
  314         if OptionsInfo["ColVisibility"]:
  315             if Index >= MaxDataColVisColCount:
  316                 ColVisibilityExcludeColIndicesList.append("%s" %ColIndex)
  317     
  318     ColIndices = MiscUtil.JoinWords(ColIndicesList, ", ") if  len(ColIndicesList) else ""
  319     ColVisibilityExcludeColIndices = MiscUtil.JoinWords(ColVisibilityExcludeColIndicesList, ", ") if len(ColVisibilityExcludeColIndicesList) else ""
  320         
  321     DataColVisibilityExclude = False
  322     if OptionsInfo["ColVisibility"]:
  323         if ColCount > MaxDataColVisColCount:
  324             DataColVisibilityExclude = True
  325             MiscUtil.PrintWarning("The number of data columns, %d, is quite large. Only first %d data columns will be available in column visibility pulldown." % (ColCount, MaxDataColVisColCount))
  326             
  327     DisplayButtons = False
  328     if OptionsInfo["ColVisibility"]:
  329         if ColCount > 0:
  330             DisplayButtons = True
  331     
  332     FreezeCols = False
  333     if (OptionsInfo["CounterCol"] and OptionsInfo["ScrollX"]):
  334         FreezeCols = True
  335     
  336     Paging = "true" if OptionsInfo["Paging"] else "false"
  337     PageLength = "%d" % OptionsInfo["PageLength"]
  338     PagingType = "\"%s\"" % OptionsInfo["PagingType"]
  339 
  340     ScrollX = "true" if OptionsInfo["ScrollX"] else "false"
  341     
  342     ScrollY = ""
  343     if OptionsInfo["ScrollY"]:
  344         if re.search("vh$", OptionsInfo["ScrollYSize"]):
  345             ScrollY = "\"%s\"" % OptionsInfo["ScrollYSize"]
  346         else:
  347             ScrollY = "%s" % OptionsInfo["ScrollYSize"]
  348     
  349     # Start HTML header...
  350     Title = "Molecules table" if OptionsInfo["Header"] is None else OptionsInfo["Header"]
  351     
  352     Writer.write("""\
  353 <!doctype html>
  354 <html lang="en">
  355 <head>
  356     <title>%s</title>
  357     <meta charset="utf-8">
  358     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  359     <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
  360     <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/dataTables.bootstrap4.min.css">
  361   
  362 """ % (Title))
  363 
  364     if (FreezeCols):
  365         Writer.write("""\
  366     <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/fixedcolumns/3.2.4/css/fixedColumns.bootstrap4.min.css">
  367 """)
  368     
  369     if (OptionsInfo["KeysNavigation"]):
  370         Writer.write("""\
  371     <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/keytable/2.3.2/css/keyTable.bootstrap4.min.css">
  372 """)
  373     
  374     Writer.write("""\
  375 
  376     <script type="text/javascript" language="javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
  377     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
  378     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/1.10.16/js/dataTables.bootstrap4.min.js"></script>
  379 
  380 """)
  381 
  382     if (OptionsInfo["Popover"]):
  383         Writer.write("""\
  384     <script type="text/javascript" language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
  385     <script type="text/javascript" language="javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
  386 
  387 """)
  388         
  389     if DisplayButtons:
  390         Writer.write("""\
  391     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/buttons/1.5.1/js/dataTables.buttons.min.js"></script>
  392     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.bootstrap4.min.js"></script>
  393     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.colVis.min.js"></script>
  394 
  395 """)
  396     
  397     if (FreezeCols):
  398         Writer.write("""\
  399     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/fixedcolumns/3.2.4/js/dataTables.fixedColumns.min.js"></script>
  400 """)
  401     
  402     if (OptionsInfo["KeysNavigation"]):
  403         Writer.write("""\
  404     <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/keytable/2.3.2/js/dataTables.keyTable.min.js"></script>
  405 """)
  406     
  407     # Intialize table using Bootstrap, DataTables and JQuery frameworks...
  408     Writer.write("""\
  409 
  410     <script type="text/javascript" class="init">
  411 
  412 $(document).ready(function() {
  413 """)
  414 
  415     if (OptionsInfo["Popover"]):
  416         Writer.write("""\
  417     $('.MolPopover').popover();
  418 
  419 """)
  420         
  421     Writer.write("""\
  422     var MolsTable = $('#MolsTable').DataTable( {
  423         "columnDefs": [
  424             {
  425                 "orderable": false,
  426                 "searchable": false,
  427                 "targets": [%s]
  428             },
  429 """ % (ColIndices))
  430 
  431     if OptionsInfo["ColVisibility"]:
  432         Writer.write("""\
  433             {
  434                 "className": "noColVisCtrl",
  435                 "targets": [%s]
  436             }
  437 """ % (ColVisibilityExcludeColIndices))
  438 
  439     Writer.write("""\
  440         ],
  441 """)
  442 
  443     # Set up dom for displaying button and other options...
  444     if OptionsInfo["ColVisibility"]:
  445         if OptionsInfo["Paging"]:
  446             Writer.write("""\
  447         "dom":  "<'row'<'col-sm-6'l><'col-sm-6'<'float-right'B>>>" +
  448             "<'row'<'col-sm-12'tr>>" +
  449             "<'row'<'col-sm-5'i><'col-sm-7'p>>",
  450 """)
  451         else:
  452             Writer.write("""\
  453         "dom":  "<'row'<'col'<'float-right'B>>>" +
  454             "<'row'<'col-sm-12'tr>>" +
  455             "<'row'<'col-sm-5'i><'col-sm-7'p>>",
  456 """)
  457     else:
  458             Writer.write("""\
  459         "dom":  "<'row'<'col'l>>" +
  460             "<'row'<'col-sm-12'tr>>" +
  461             "<'row'<'col-sm-5'i><'col-sm-7'p>>",
  462 """)
  463     
  464     #
  465     if OptionsInfo["ColVisibility"]:
  466         # Set up buttons...
  467         Writer.write("""\
  468         "buttons": [
  469             {
  470                 "extend": "colvis",
  471                 "text": "Column visibility",
  472                 "className": "btn btn-outline-light text-dark",
  473                 "columns": ":not(.noColVisCtrl)",
  474 """)
  475         if not DataColVisibilityExclude:
  476             Writer.write("""\
  477                 "prefixButtons": [ "colvisRestore" ],
  478 """)
  479         
  480         Writer.write("""\
  481                 "columnText": function ( dt, colIndex, colLabel ) {
  482                     return "Column " + (colIndex + 1);
  483                 },
  484             }
  485         ],
  486 """)
  487     
  488     # Write out rest of the variables for DataTables...
  489     if FreezeCols:
  490         Writer.write("""\
  491         "fixedColumns": {
  492             "leftColumns": %s
  493         },
  494 """ % (FreezeLeftColumns))
  495     
  496     if (OptionsInfo["KeysNavigation"]):
  497         Writer.write("""\
  498         "keys": true,
  499 """)
  500     
  501     Writer.write("""\
  502         "pageLength": %s,
  503         "lengthMenu": [ [5, 10, 15, 25, 50, 100, 500, 1000, -1], [5, 10, 15, 25, 50, 100, 500, 1000, "All"] ],
  504         "paging": %s,
  505         "pagingType": %s,
  506         "scrollX": %s,
  507         "scrollY": %s,
  508         "scrollCollapse": true,
  509         "order": [],
  510     } );
  511 """ % (PageLength, Paging, PagingType, ScrollX, ScrollY))
  512     
  513     if OptionsInfo["CounterCol"]:
  514         Writer.write("""\
  515     MolsTable.on( 'order.dt search.dt', function () {
  516         MolsTable.column(0, {search:'applied', order:'applied'}).nodes().each( function (cell, rowIndex) {
  517             cell.innerHTML = rowIndex + 1;
  518         } );
  519     } ).draw();
  520 """)
  521     
  522     # End of Javacscript code...
  523     Writer.write("""\
  524 } );
  525 
  526     </script>
  527 """)
  528 
  529     # Finish up HTML header...
  530     Writer.write("""\
  531   
  532 </head>
  533 <body>
  534   <div class="container-fluid">
  535     <br/>
  536 """)
  537         
  538 def WriteHTMLPageEnd(Writer):
  539     """Write out HTML page end."""
  540 
  541     Writer.write("""\
  542   </div>
  543 </body>
  544 </html>
  545 """)
  546 
  547 def WriteHTMLPageTitle(Writer):
  548     """Write out HTML page title."""
  549 
  550     if OptionsInfo["Header"] is None:
  551         return
  552 
  553     Writer.write("""    <%s class="text-center">%s</%s>\n""" % (OptionsInfo["HeaderStyle"], OptionsInfo["Header"], OptionsInfo["HeaderStyle"]))
  554 
  555 def WriteHTMLPageFooter(Writer):
  556     """Write out HTML page footer."""
  557 
  558     if OptionsInfo["Footer"] is None:
  559         return
  560 
  561     Writer.write("""    <br/>\n    <p class="%s">%s</p>\n""" % (OptionsInfo["FooterClass"], OptionsInfo["Footer"]))
  562 
  563 def WriteHTMLTableHeader(Writer):
  564     """Write out HTML table header."""
  565 
  566     if OptionsInfo["TableStyle"] is None:
  567         Writer.write("""\n    <table id="MolsTable" cellspacing="0" width="100%">\n""")
  568     else:
  569         Writer.write("""    <table id="MolsTable" class="%s" cellspacing="0" width="100%s">\n""" % (OptionsInfo["TableStyle"], "%"))
  570         
  571 def WriteHTMLTableEnd(Writer):
  572     """Write out HTML table end."""
  573 
  574     Writer.write("""    </table>\n\n""")
  575 
  576 def GetColCount(MolCount):
  577     """Get tabke column count."""
  578     
  579     ColCount = OptionsInfo["NumOfMolsPerRow"] if OptionsInfo["NumOfMolsPerRow"] <= MolCount else MolCount
  580     
  581     return ColCount
  582 
  583 def SetupAtomListsToHighlight(ValidMols):
  584     """Set up atom lists to highlight using specified SMARTS pattern."""
  585 
  586     AtomListsToHighlight = None
  587     if OptionsInfo["HighlightSMARTSPattern"] is None:
  588         return  AtomListsToHighlight
  589     
  590     PatternMol = Chem.MolFromSmarts(OptionsInfo["HighlightSMARTSPattern"])
  591     AtomListsToHighlight = []
  592     for ValidMol in ValidMols:
  593         # Get matched atom lists and flatten it...
  594         MatchedAtomsLists = ValidMol.GetSubstructMatches(PatternMol)
  595         MatchedAtoms = [ Atom for AtomsList in MatchedAtomsLists for Atom in AtomsList] 
  596         AtomListsToHighlight.append(MatchedAtoms)
  597     
  598     return  AtomListsToHighlight
  599 
  600 def PerformAlignment(ValidMols):
  601     """Perform alignment to a common template specified by a SMARTS pattern."""
  602     
  603     if OptionsInfo["AlignmentSMARTSPattern"] is None:
  604         return
  605     
  606     PatternMol = Chem.MolFromSmarts(OptionsInfo["AlignmentSMARTSPattern"])
  607     AllChem.Compute2DCoords(PatternMol)
  608         
  609     MatchedValidMols = [ValidMol for ValidMol in ValidMols if ValidMol.HasSubstructMatch(PatternMol)]
  610     for ValidMol in MatchedValidMols:
  611         AllChem.GenerateDepictionMatching2DStructure(ValidMol, PatternMol)
  612 
  613 def GetMolPopoverTag(Mol):
  614     """Set up a popover window containing any additional information about molecule."""
  615     
  616     if not OptionsInfo["Popover"]:
  617         return None
  618     
  619     # Set up data label and values...
  620     AvailableDataLabels = Mol.GetPropNames(includePrivate = False, includeComputed = False)
  621     
  622     DataContentLines = []
  623     MaxDataCharWidth = OptionsInfo["PopoverTextWidth"]
  624     MaxDataDisplayCount = OptionsInfo["PopoverDataCount"]
  625 
  626     DataDisplayCount = 0
  627     SkippedDataDisplay = False
  628     for DataLabel in AvailableDataLabels:
  629         DataDisplayCount += 1
  630         if DataDisplayCount > MaxDataDisplayCount:
  631             SkippedDataDisplay = True
  632             break
  633         
  634         DataValue = "%s" % Mol.GetProp(DataLabel)
  635         DataValue = DataValue.strip()
  636         if MiscUtil.IsEmpty(DataValue):
  637             continue
  638 
  639         # Change any new lines to ;
  640         if re.search("(\r\n|\r|\n)", DataValue):
  641             DataValue = re.sub("(\r\n|\r|\n)", "; ", DataValue)
  642         
  643         DataValue = MiscUtil.TruncateText(DataValue, MaxDataCharWidth, "...")
  644         DataValue = MiscUtil.ReplaceHTMLEntitiesInText(DataValue)
  645         
  646         DataContent = "<b>%s</b>: %s" % (DataLabel, DataValue)
  647         DataContentLines.append(DataContent)
  648 
  649     if not len(DataContentLines):
  650         return None
  651 
  652     if SkippedDataDisplay:
  653         DataContent = "<b>... ... ...</b>"
  654         DataContentLines.append(DataContent)
  655         
  656         DataContent = "Showing 1 to %s of %s" % (MaxDataDisplayCount, len(AvailableDataLabels))
  657         DataContentLines.append(DataContent)
  658     else:
  659         DataContent = "Showing 1 to %s of %s" % (DataDisplayCount, len(AvailableDataLabels))
  660         DataContentLines.append(DataContent)
  661         
  662     DataContent = MiscUtil.JoinWords(DataContentLines, "<br/>")
  663     PopoverTag = """class="MolPopover" data-toggle="popover" data-html="true" data-trigger="click" data-placement="right" title="<span class='small'><b>Additional Information</b></span>" data-content="<span class='small'>%s</span>" """ % DataContent
  664     
  665     return PopoverTag
  666 
  667 def ProcessOptions():
  668     """Process and validate command line arguments and options"""
  669     
  670     MiscUtil.PrintInfo("Processing options...")
  671     
  672     # Validate options...
  673     ValidateOptions()
  674     
  675     OptionsInfo["Infile"] = Options["--infile"]
  676     OptionsInfo["Outfile"] = Options["--outfile"]
  677     OptionsInfo["Overwrite"] = Options["--overwrite"]
  678     
  679     # No need for any RDKit specific --outfileParams....
  680     OptionsInfo["InfileParams"] = MiscUtil.ProcessOptionInfileParameters("--infileParams", Options["--infileParams"], OptionsInfo["Infile"])
  681 
  682     AlignmentSMARTSPattern = None
  683     if not re.match("^None$", Options["--alignmentSMARTS"], re.I):
  684         AlignmentSMARTSPattern = Options["--alignmentSMARTS"]
  685     OptionsInfo["AlignmentSMARTSPattern"]  = AlignmentSMARTSPattern
  686     
  687     OptionsInfo["AtomLabelFontSize"] = Options["--atomLabelFontSize"]
  688     OptionsInfo["BondLineWidth"] = Options["--bondLineWidth"]
  689     
  690     Compute2DCoords = True
  691     if re.match("^yes$", Options["--compute2DCoords"], re.I):
  692         Compute2DCoords = True
  693     elif re.match("^no$", Options["--compute2DCoords"], re.I):
  694         Compute2DCoords = False
  695     OptionsInfo["Compute2DCoords"]  = Compute2DCoords
  696 
  697     CounterCol = True
  698     if re.match("^no$", Options["--counterCol"], re.I):
  699         CounterCol = False
  700     OptionsInfo["CounterCol"]  = CounterCol
  701     
  702     ColVisibility = True
  703     if re.match("^no$", Options["--colVisibility"], re.I):
  704         ColVisibility = False
  705     OptionsInfo["ColVisibility"]  = ColVisibility
  706     
  707     OptionsInfo["FontBold"] = True
  708     if re.match("^no$", Options["--fontBold"], re.I):
  709         OptionsInfo["FontBold"] = False
  710         
  711     Footer = None
  712     if not re.match("^None$", Options["--footer"], re.I):
  713         Footer = Options["--footer"]
  714     OptionsInfo["Footer"]  = Footer
  715 
  716     FooterClass = Options["--footerClass"].strip()
  717     if MiscUtil.IsEmpty(FooterClass):
  718         MiscUtil.PrintError("The value specified using option \"--footerClass\" is empty.")
  719     OptionsInfo["FooterClass"]  = FooterClass
  720     
  721     Header = None
  722     if not re.match("^None$", Options["--header"], re.I):
  723         Header = Options["--header"]
  724     OptionsInfo["Header"]  = Header
  725     
  726     HeaderStyle = Options["--headerStyle"].strip()
  727     if MiscUtil.IsEmpty(HeaderStyle):
  728         MiscUtil.PrintError("The value specified using option \"--headerStyle\" is empty.")
  729     OptionsInfo["HeaderStyle"]  = HeaderStyle
  730     
  731     HighlightSMARTSPattern = None
  732     if not re.match("^None$", Options["--highlightSMARTS"], re.I):
  733         HighlightSMARTSPattern = Options["--highlightSMARTS"]
  734     OptionsInfo["HighlightSMARTSPattern"]  = HighlightSMARTSPattern
  735     
  736     OptionsInfo["Kekulize"] = True
  737     if re.match("^no$", Options["--kekulize"], re.I):
  738         OptionsInfo["Kekulize"] = False
  739         
  740     OptionsInfo["KeysNavigation"] = True
  741     if re.match("^no$", Options["--keysNavigation"], re.I):
  742         OptionsInfo["KeysNavigation"] = False
  743     
  744     SizeValues = Options["--molImageSize"].split(",")
  745     OptionsInfo["MolImageWidth"] = int(SizeValues[0])
  746     OptionsInfo["MolImageHeight"] = int(SizeValues[1])
  747 
  748     OptionsInfo["MolImageEncoded"] = True
  749     if re.match("^no$", Options["--molImageEncoded"], re.I):
  750         OptionsInfo["MolImageEncoded"] = False
  751     
  752     OptionsInfo["NumOfMolsPerRow"] = int(Options["--numOfMolsPerRow"])
  753 
  754     OptionsInfo["Paging"] = True
  755     if re.match("^no$", Options["--paging"], re.I):
  756         OptionsInfo["Paging"] = False
  757     
  758     PagingType = Options["--pagingType"]
  759     if not re.match("^(numbers|simple|simple_numbers|full|full_numbers|simple_number)$", Options["--pagingType"], re.I):
  760         MiscUtil.PrintWarning("The paging type name, %s, specified using option \"--pagingType\" appears to be a unknown type..." % (PagingType))
  761     OptionsInfo["PagingType"] = PagingType.lower()
  762     
  763     OptionsInfo["PageLength"] = int(Options["--pageLength"])
  764     
  765     OptionsInfo["Popover"] = True
  766     if re.match("^no$", Options["--popover"], re.I):
  767         OptionsInfo["Popover"] = False
  768     OptionsInfo["PopoverDataCount"] = int(Options["--popoverDataCount"])
  769     OptionsInfo["PopoverTextWidth"] = int(Options["--popoverTextWidth"])
  770     
  771     OptionsInfo["ShowMolName"] = True
  772     if re.match("^no$", Options["--showMolName"], re.I):
  773         OptionsInfo["ShowMolName"] = False
  774     
  775     OptionsInfo["ScrollX"] = True
  776     if re.match("^no$", Options["--scrollX"], re.I):
  777         OptionsInfo["ScrollX"] = False
  778         
  779     OptionsInfo["ScrollY"] = True
  780     if re.match("^no$", Options["--scrollY"], re.I):
  781         OptionsInfo["ScrollY"] = False
  782 
  783     OptionsInfo["ScrollYSize"] = Options["--scrollYSize"]
  784     if re.match("vh$", Options["--scrollYSize"], re.I):
  785         ScrollYSize = int(re.sub("vh$", "", Options["--scrollYSize"]))
  786         if ScrollYSize <= 0:
  787             MiscUtil.PrintError("The value specified, %s, for option \"--scrollYSize\" is not valid. Supported value: > 0 followed by \"vh\"" % Options["--scrollYSize"])
  788     
  789     TableStyle = None
  790     if not re.match("^None$", Options["--tableStyle"], re.I):
  791         if re.match("^All$", Options["--tableStyle"], re.I):
  792             TableStyle = "table table-striped table-bordered table-hover table-dark"
  793         else:
  794             TableStyle = re.sub(" ", "", Options["--tableStyle"])
  795             for Style in [Style for Style in TableStyle.split(",")]:
  796                 if not re.match("^(table|table-striped|table-bordered|table-hover|table-dark|table-sm)$", Style, re.I):
  797                     MiscUtil.PrintWarning("The table style name, %s, specified using option \"-t, --tableStyle\" appears to be a unknown style..." % (Style))
  798             TableStyle = re.sub(",", " ", TableStyle.lower())
  799     OptionsInfo["TableStyle"]  = TableStyle
  800 
  801     OptionsInfo["TableFooter"] = True
  802     if re.match("^no$", Options["--tableFooter"], re.I):
  803         OptionsInfo["TableFooter"] = False
  804     
  805     OptionsInfo["TableHeader"] = True
  806     if re.match("^no$", Options["--tableHeader"], re.I):
  807         OptionsInfo["TableHeader"] = False
  808     
  809     TableHeaderStyle = None
  810     if not re.match("^None$", Options["--tableHeaderStyle"], re.I):
  811         TableHeaderStyle = Options["--tableHeaderStyle"]
  812         TableHeaderStyle = TableHeaderStyle.lower()
  813         CheckOptionTableClassColorValues("--tableHeaderStyle", [TableHeaderStyle])
  814     OptionsInfo["TableHeaderStyle"]  = TableHeaderStyle
  815 
  816 def CheckOptionTableClassColorValues(OptionName, ColorsList):
  817     """Check names of table color classes and issue a warning for unknown names."""
  818 
  819     TableClassColors = ["thead-dark", "thead-light", "table-primary", "table-success", "table-danger", "table-info", "table-warning", "table-active", "table-secondary", "table-light", "table-dark", "bg-primary", "bg-success", "bg-danger",  "bg-info", "bg-warning", "bg-secondary", "bg-dark", "bg-light"]
  820 
  821     for Color in ColorsList:
  822         if not Color in TableClassColors:
  823             MiscUtil.PrintWarning("The color class name, %s, specified using option \"%s\" appears to be a unknown name..." % (Color, OptionName))
  824         
  825 def RetrieveOptions():
  826     """Retrieve command line arguments and options"""
  827     
  828     # Get options...
  829     global Options
  830     Options = docopt(_docoptUsage_)
  831     
  832     # Set current working directory to the specified directory...
  833     WorkingDir = Options["--workingdir"]
  834     if WorkingDir:
  835         os.chdir(WorkingDir)
  836     
  837     # Handle examples option...
  838     if "--examples" in Options and Options["--examples"]:
  839         MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_))
  840         sys.exit(0)
  841 
  842 def ValidateOptions():
  843     """Validate option values"""
  844     
  845     MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"])
  846     MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "sdf sd mol smi csv tsv txt")
  847 
  848     MiscUtil.ValidateOptionsOutputFileOverwrite("-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"])
  849     MiscUtil.ValidateOptionsDistinctFileNames("-i, --infile", Options["--infile"], "-o, --outfile", Options["--outfile"])
  850     
  851     if not re.match("^None$", Options["--alignmentSMARTS"], re.I):
  852         PatternMol = Chem.MolFromSmarts(Options["--alignmentSMARTS"])
  853         if PatternMol is None:
  854             MiscUtil.PrintError("The value specified, %s, using option \"--alignmentSMARTS\" is not a valid SMARTS: Failed to create pattern molecule" % Options["--alignmentSMARTS"])
  855     
  856     MiscUtil.ValidateOptionIntegerValue("--atomLabelFontSize", Options["--atomLabelFontSize"], {">": 0})
  857     MiscUtil.ValidateOptionFloatValue("-b, --bondLineWidth", Options["--bondLineWidth"], {">": 0.0})
  858     
  859     MiscUtil.ValidateOptionTextValue("--compute2DCoords", Options["--compute2DCoords"], "yes no auto")
  860     
  861     MiscUtil.ValidateOptionTextValue("--counterCol", Options["--counterCol"], "yes no")
  862     MiscUtil.ValidateOptionTextValue("--colVisibility", Options["--colVisibility"], "yes no")
  863     
  864     MiscUtil.ValidateOptionTextValue("--f, -fontBold", Options["--fontBold"], "yes no")
  865     
  866     if not re.match("^None$", Options["--highlightSMARTS"], re.I):
  867         PatternMol = Chem.MolFromSmarts(Options["--highlightSMARTS"])
  868         if PatternMol is None:
  869             MiscUtil.PrintError("The value specified, %s, using option \"--highlightSMARTS\" is not a valid SMARTS: Failed to create pattern molecule" % Options["--highlightSMARTS"])
  870     
  871     MiscUtil.ValidateOptionTextValue("--kekulize", Options["--kekulize"], "yes no")
  872     
  873     MiscUtil.ValidateOptionTextValue("-k, --keysNavigation", Options["--keysNavigation"], "yes no")
  874     
  875     MiscUtil.ValidateOptionNumberValues("-m, --molImageSize", Options["--molImageSize"], 2, ",", "integer", {">": 0})
  876     MiscUtil.ValidateOptionTextValue("--molImageEncoded", Options["--molImageEncoded"], "yes no")
  877     
  878     MiscUtil.ValidateOptionIntegerValue("--numOfMolsPerRow", Options["--numOfMolsPerRow"], {">": 0})
  879     
  880     MiscUtil.ValidateOptionTextValue("-p, --paging", Options["--paging"], "yes no")
  881     MiscUtil.ValidateOptionIntegerValue("--pageLength", Options["--pageLength"], {">": 0})
  882     
  883     MiscUtil.ValidateOptionTextValue("--popover", Options["--popover"], "yes no")
  884     MiscUtil.ValidateOptionIntegerValue("--popoverDataCount", Options["--popoverDataCount"], {">": 0})
  885     MiscUtil.ValidateOptionIntegerValue("--popoverTextWidth", Options["--popoverTextWidth"], {">": 0})
  886     
  887     MiscUtil.ValidateOptionTextValue("--showMolName", Options["--showMolName"], "yes no")
  888     
  889     MiscUtil.ValidateOptionTextValue("--scrollX", Options["--scrollX"], "yes no")
  890     MiscUtil.ValidateOptionTextValue("--scrollY", Options["--scrollY"], "yes no")
  891     if not re.search("vh$", Options["--scrollYSize"], re.I):
  892         MiscUtil.ValidateOptionIntegerValue("--scrollYSize", Options["--scrollYSize"], {">": 0})
  893 
  894     MiscUtil.ValidateOptionTextValue("--tableFooter", Options["--tableFooter"], "yes no")
  895     MiscUtil.ValidateOptionTextValue("--tableHeader", Options["--tableHeader"], "yes no")
  896     
  897 # Setup a usage string for docopt...
  898 _docoptUsage_ = """
  899 RDKitDrawMolecules.py - Draw molecules and generate an image or HTML file
  900 
  901 Usage:
  902     RDKitDrawMolecules.py [--alignmentSMARTS <SMARTS>] [--atomLabelFontSize <number>]
  903                              [--bondLineWidth <number>] [--compute2DCoords <yes | no>] [--counterCol <yes or no>]
  904                              [--colVisibility <yes or no>] [--fontBold <yes or no>] [--footer <text>] [--footerClass <text>] 
  905                              [--header <text>] [--headerStyle <text>] [--highlightSMARTS <SMARTS>]
  906                              [--infileParams <Name,Value,...>] [--kekulize <yes or no>] [--keysNavigation <yes or no>]
  907                              [--molImageSize <width,height>] [--molImageEncoded <yes or no> ]
  908                              [--numOfMolsPerRow <number>] [--overwrite] [--paging <yes or no>]
  909                              [--pagingType <numbers, simple, ...>] [--pageLength <number>]
  910                              [--popover <yes or no>] [--popoverDataCount <number>] [--popoverTextWidth <number>]
  911                              [--showMolName <yes or no>] [--scrollX <yes or no>] [--scrollY <yes or no>]
  912                              [--scrollYSize <number>] [--tableFooter <yes or no>] [--tableHeader <yes or no>]
  913                              [--tableHeaderStyle <thead-dark,thead-light,...>]
  914                              [--tableStyle <table,table-striped,...>] [-w <dir>] -i <infile> -o <outfile>
  915     RDKitDrawMolecules.py -h | --help | -e | --examples
  916 
  917 Description:
  918     Draw molecules in a grid and write them out as an image file or a HTML table file. The
  919     SVG image or HTML table file appears to be the best among all the available image file
  920     options, as rendered in a browser. The Python modules aggdraw/cairo are required to
  921     generate high quality PNG images.
  922     
  923     The drawing of the molecules are embedded in HTML table columns as in line SVG
  924     images. The HTML table is an interactive table and requires internet access for viewing
  925     in a browser. It employs he following frameworks: JQuery, Bootstrap, and DataTable.
  926     
  927     The options '--atomLabelFontSize' and '--bondLineWidth' don't appear
  928     to work during the generation of a SVG image.
  929 
  930     The supported input file formats are: Mol (.mol), SD (.sdf, .sd), SMILES (.smi,
  931     .txt, .csv, .tsv)
  932 
  933     The output image file can be saved in any format supported by the Python Image
  934     Library (PIL). The image format is automatically detected from the output file extension.
  935 
  936     Some of the most common output image file formats are: GIF (.gif), JPEG (.jpg),
  937     PNG (.png), SVG (.svg), TIFF (.tif). In addition, a HTML (.html) file format
  938     containing a table is supported.
  939 
  940 Options:
  941     -a, --alignmentSMARTS <SMARTS>  [default: none]
  942         SMARTS pattern for aligning molecules to a common template.
  943     --atomLabelFontSize <number>  [default: 12]
  944         Font size for drawing atom labels. This option is ignored during the generation of
  945         a SVG and HTML output file.
  946     -b, --bondLineWidth <number>  [default: 1.2]
  947         Line width for drawing bonds. This option is ignored during the generation of a SVG
  948         and HTML output file.
  949     -c, --compute2DCoords <yes or no>  [default: auto]
  950         Compute 2D coordinates of molecules before drawing. Default: yes for all file
  951         formats.
  952     --counterCol <yes or no>  [default: yes]
  953         Show a counter column as the first column in the table. It contains the position
  954         for each row in the HTML table. This option is only used during the generation of
  955         a HTML table file.
  956     --colVisibility <yes or no>  [default: yes]
  957         Show a dropdown button to toggle visibility of columns in the table. This option is
  958         only used during the generation of a HTML table file.
  959     -e, --examples
  960         Print examples.
  961     -f --fontBold <yes or no>  [default: yes]
  962         Make all text fonts bold during the generation of  a SVG and HTML output file. This
  963         option is ignored for all other output files.
  964     --footer <text>  [default: none]
  965         Footer text to insert at the bottom of the HTML page after the table. This option is
  966         only used during the generation of a HTML table file.
  967     --footerClass <text>  [default: small text-center text-muted]
  968         Footer class style to use with <p> tag. This option is only used during the
  969         generation of a HTML table file.
  970     -h, --help
  971         Print this help message.
  972     --header <text>  [default: none]
  973         Header text to insert at the top of the HTML page before the table. This option is
  974         only used during the generation of a HTML table file.
  975     --headerStyle <text>  [default: h5]
  976         Header style to use. Possible values: h1 to h6. This option is only used during the
  977         generation of a HTML table file.
  978     --highlightSMARTS <SMARTS>  [default: none]
  979         SMARTS pattern for highlighting atoms and bonds in molecules. All matched
  980         substructures are highlighted.
  981     -i, --infile <infile>
  982         Input file name.
  983     --infileParams <Name,Value,...>  [default: auto]
  984         A comma delimited list of parameter name and value pairs for reading
  985         molecules from files. The supported parameter names for different file
  986         formats, along with their default values, are shown below:
  987             
  988             SD, MOL: removeHydrogens,yes,sanitize,yes,strictParsing,yes
  989             SMILES: smilesColumn,1,smilesNameColumn,2,smilesDelimiter,space,
  990                 smilesTitleLine,auto,sanitize,yes
  991             
  992         Possible values for smilesDelimiter: space, comma or tab.
  993     -k, --kekulize <yes or no>  [default: yes]
  994         Perform kekulization on molecules. This option is ignored during the generation of
  995         a SVG and HTML output file.
  996     --keysNavigation <yes or no>  [default: yes]
  997         Provide Excel like keyboard cell navigation for the table. This option is only used
  998         during the generation of a HTML table file.
  999     -m, --molImageSize <width,height>  [default: 250,200]
 1000         Image size of a molecule in pixels.
 1001     --molImageEncoded <yes or no>  [default: yes]
 1002         Base64 encode SVG image of a molecule for inline embedding in a HTML page.
 1003         The inline SVG image may fail to display in browsers without encoding.
 1004     -n, --numOfMolsPerRow <number>  [default: 4]
 1005         Number of molecules to draw in a row.
 1006     -o, --outfile <outfile>
 1007         Output file name.
 1008     --overwrite
 1009         Overwrite existing files.
 1010     -p, --paging <yes or no>  [default: yes]
 1011         Provide page navigation for browsing data in the table. This option is only used
 1012         during the generation of a HTML table file.
 1013     --pagingType <numbers, simple, ...>  [default: full_numbers]
 1014         Type of page navigation. Possible values: numbers, simple, simple_numbers,
 1015         full, full_numbers, or first_last_numbers.
 1016             
 1017             numbers - Page number buttons only
 1018             simple - 'Previous' and 'Next' buttons only
 1019             simple_numbers - 'Previous' and 'Next' buttons, plus page numbers
 1020             full - 'First', 'Previous', 'Next' and 'Last' buttons
 1021             full_numbers - 'First', 'Previous', 'Next' and 'Last' buttons, plus
 1022                 page numbers
 1023             first_last_numbers - 'First' and 'Last' buttons, plus page numbers
 1024             
 1025         This option is only used during the generation of a HTML table file.
 1026     --pageLength <number>  [default: 5]
 1027         Number of rows to show per page. This option is only used during the
 1028         generation of a HTML table file.
 1029     --popover <yes or no>  [default: yes]
 1030         Display a popover window containing additional information about the
 1031         molecule. The popover is opened after a click on the drawing of a
 1032         molecule. A subsequent click on the same drawing closes the popover.
 1033         This option is only used during the generation of a HTML table file.
 1034     --popoverDataCount <number>  [default: 25]
 1035         Maximum number of data fields to show in a popover window. This option is
 1036         only used during the generation of a HTML table file.
 1037     --popoverTextWidth <number>  [default: 50]
 1038         Maximum width in characters for text display in a popover window before
 1039         truncating the text. This option is only used during the generation of a HTML
 1040         table file.
 1041     -s, --showMolName <yes or no>  [default: yes]
 1042         Show molecule names under the images.This option is only used during the
 1043         generation of a HTML table file.
 1044     --scrollX <yes or no>  [default: yes]
 1045         Provide horizontal scroll bar in the table as needed.This option is only used
 1046         during the generation of a HTML table file.
 1047     --scrollY <yes or no>  [default: yes]
 1048         Provide vertical scroll bar in the table as needed.This option is only used during
 1049         the generation of a HTML table file.
 1050     --scrollYSize <number>  [default: 75vh]
 1051         Maximum height of table viewport either in pixels or percentage of the browser
 1052         window height before providing a vertical scroll bar. Default: 75% of the height of
 1053         browser window.This option is only used during the generation of a HTML table file.
 1054     -t, --tableStyle <table,table-striped,...>  [default: table,table-hover,table-sm]
 1055         Style of table. Possible values: table, table-striped, table-bordered,
 1056         table-hover, table-dark, table-sm, none, or All. Default: 'table,table-hover'. A
 1057         comma delimited list of any valid Bootstrap table styles is also supported
 1058         
 1059         This option is only used during the generation of a HTML table file.
 1060     --tableFooter <yes or no>  [default: yes]
 1061         Show Excel style column headers at the end of  the table. This option is only
 1062         used during the generation of a HTML table file.
 1063     --tableHeader <yes or no>  [default: yes]
 1064         Show Excel style column headers in the table. This option is only used
 1065         during the generation of a HTML table file.
 1066     --tableHeaderStyle <thead-dark,thead-light,...>  [default: thead-dark]
 1067         Style of table header. Possible values: thead-dark, thead-light, or none.
 1068         The names of the following contextual color classes are also supported:
 1069         table-primary (Blue), table-success (Green), table-danger (Red), table-info
 1070         (Light blue), table-warning (Orange), table-active (Grey), table-light (Light
 1071         grey), and  table-dark (Dark grey).
 1072         
 1073         This option is only used during the generation of a HTML table file.
 1074     -w, --workingdir <dir>
 1075         Location of working directory which defaults to the current directory.
 1076 
 1077 Examples:
 1078     To automatically compute 2D coordinates for molecules in a SMILES file and
 1079     generate a SVG image file containing 4 molecules per row in a grid with cell
 1080     size of 250 x 200 pixels, type:
 1081 
 1082         % RDKitDrawMolecules.py -i Sample.smi -o SampleOut.svg
 1083 
 1084     To automatically compute 2D coordinates for molecules in a SMILES file and
 1085     generate a SVG image file containing 2 molecules per row in a grid with cell
 1086     size of 400 x 300 pixels and without any keulization along with highlighting
 1087     a specific set of atoms and bonds indicated by a SMARTS pattern, type:
 1088 
 1089         % RDKitDrawMolecules.py -n 2 -m "400,300" -k no --fontBold no
 1090           --highlightSMARTS  'c1ccccc1' -i Sample.smi -o SampleOut.svg
 1091 
 1092     To generate a PNG image file for molecules in a SD file using existing 2D
 1093     coordinates, type
 1094 
 1095         % RDKitDrawMolecules.py --compute2DCoords no -i Sample.sdf
 1096           -o SampleOut.png
 1097 
 1098     To automatically compute 2D coordinates for molecules in a SD file and
 1099     generate a HTML file containing 4 molecules per row in a table, along with
 1100     all the bells and whistles to interact with the table, type:
 1101 
 1102         % RDKitDrawMolecules.py -i Sample.sdf -o SampleOut.html
 1103 
 1104     To automatically compute 2D coordinates for molecules in a SD file and
 1105     generate a HTML file containing 4 molecules per row in a table without
 1106     any bells and whistles to interact with the table, type:
 1107 
 1108         % RDKitDrawMolecules.py --counterCol no --colVisibility no
 1109           --keysNavigation no --paging  no --popover no --scrollX no
 1110           --scroll no --tableFooter no --tableHeader  no -i Sample.sdf
 1111           -o SampleOut.html
 1112 
 1113     To automatically compute 2D coordinates for molecules in a CSV SMILES file
 1114     with column headers, SMILES strings in column 1, and name in column 2 and
 1115     generate a PDF image file, type:
 1116 
 1117         % RDKitDrawMolecules.py --infileParams "smilesDelimiter,comma,
 1118           smilesTitleLine,yes,smilesColumn,1,smilesNameColumn,2"
 1119           -i SampleSMILES.csv -o SampleOut.pdf
 1120 
 1121 Author:
 1122     Manish Sud(msud@san.rr.com)
 1123 
 1124 See also:
 1125     RDKitConvertFileFormat.py, RDKitDrawMoleculesAndDataTable.py, RDKitRemoveDuplicateMolecules.py,
 1126     RDKitSearchFunctionalGroups.py, RDKitSearchSMARTS.py
 1127 
 1128 Copyright:
 1129     Copyright (C) 2019 Manish Sud. All rights reserved.
 1130 
 1131     The functionality available in this script is implemented using RDKit, an
 1132     open source toolkit for cheminformatics developed by Greg Landrum.
 1133 
 1134     This file is part of MayaChemTools.
 1135 
 1136     MayaChemTools is free software; you can redistribute it and/or modify it under
 1137     the terms of the GNU Lesser General Public License as published by the Free
 1138     Software Foundation; either version 3 of the License, or (at your option) any
 1139     later version.
 1140 
 1141 """
 1142 
 1143 if __name__ == "__main__":
 1144     main()