svgjupyter-notebookipythonpngrdkit

How to save "IPython.core.display.SVG" as PNG file?


I am trying to save a variable with data-type of "IPython.core.display.SVG" as a PNG file in Jupyter Notebook environment.

First I tried:

with open('./file.png','wb+') as outfile:
    outfile.write(my_svg.data)

And I got the error:

TypeError: a bytes-like object is required, not 'str'

Next, I tried:

with open('./file.png','wb+') as outfile:
    outfile.write(my_svg.data.encode('utf-8'))

But, I cannot open "file.png". The operating system gives error:

The file “file.png” could not be opened. It may be damaged or use a file format that Preview doesn’t recognize.

I can save "my_svg" with "svg" extension as below:

with open('./file.svg','wb+') as outfile:
    outfile.write(my_svg.data.encode('utf-8'))

But, when I want to convert "file.svg" into "file.png" by:

import cairosvg
cairosvg.svg2png(url="./file.svg", write_to="./file.png")

I get the error:

ValueError: unknown locale: UTF-8

This is how I get "IPython.core.display.SVG" data-type in Jupyter Notebook:

from rdkit import Chem
from rdkit.Chem.Draw import rdMolDraw2D
from IPython.display import SVG


smile_1 = 'C(C(N)=O)c(c)c'
smile_2 = 'o(cn)c(c)c'

m1 = Chem.MolFromSmiles(smile_1,sanitize=False)
Chem.SanitizeMol(m1, sanitizeOps=(Chem.SanitizeFlags.SANITIZE_ALL^Chem.SanitizeFlags.SANITIZE_KEKULIZE^Chem.SanitizeFlags.SANITIZE_SETAROMATICITY))
m2 = Chem.MolFromSmiles(smile_2,sanitize=False)
Chem.SanitizeMol(m2, sanitizeOps=(Chem.SanitizeFlags.SANITIZE_ALL^Chem.SanitizeFlags.SANITIZE_KEKULIZE^Chem.SanitizeFlags.SANITIZE_SETAROMATICITY))

mols = [m1, m2]
legends = ["smile_1", "smile_2"]

molsPerRow=2
subImgSize=(200, 200)
nRows = len(mols) // molsPerRow
if len(mols) % molsPerRow:
  nRows += 1
  
fullSize = (molsPerRow * subImgSize[0], nRows * subImgSize[1])
d2d = rdMolDraw2D.MolDraw2DSVG(fullSize[0], fullSize[1], subImgSize[0], subImgSize[1])
d2d.drawOptions().prepareMolsBeforeDrawing=False
d2d.DrawMolecules(list(mols), legends=legends)
d2d.FinishDrawing()
SVG(d2d.GetDrawingText())

Environment:

Any help is greatly appreciated.


Solution

  • Instead of creating an SVG with rdkit and trying to convert it to a PNG, why not just create a PNG directly?

    from rdkit.Chem import Draw
    from rdkit import Chem
    
    # create rdkit mol
    smile = 'CCCC'
    mol = Chem.MolFromSmiles(smile)
    
    # create png
    d2d = Draw.MolDraw2DCairo(200, 200)
    d2d.DrawMolecule(mol)
    d2d.FinishDrawing()
    png_data = d2d.GetDrawingText()
    
    # save png to file
    with open('mol_image.png', 'wb') as png_file:
        png_file.write(png_data)
    

    I am not sure why MolDraw2DCairo is not working for you but using the package you mention (cairosvg) you could extend your code sample quite easily:

    # extra imports
    import cairosvg
    import tempfile
    
    # replace molecule drawing part
    d2d = rdMolDraw2D.MolDraw2DSVG(fullSize[0], fullSize[1], subImgSize[0], subImgSize[1])
    d2d.drawOptions().prepareMolsBeforeDrawing=False
    d2d.DrawMolecules(list(mols), legends=legends)
    d2d.FinishDrawing()
    svg_text = d2d.GetDrawingText()
    
    # save to png file
    with tempfile.NamedTemporaryFile(delete=True) as tmp:
        tmp.write(svg_text.encode())
        tmp.flush()
        cairosvg.svg2png(url=tmp.name, write_to="./mol_img.png")