#include <openbabel/babelconfig.h>
#include <openbabel/obmolecformat.h>
#include <openbabel/mol.h>
#include <openbabel/op.h>
#include <openbabel/depict/depict.h>
#include <openbabel/depict/cairopainter.h>
#include <openbabel/alias.h>
#include <cstdlib>
using namespace std;
namespace OpenBabel
{
class PNG2Format : public OBMoleculeFormat
{
public:
PNG2Format() : _ncols(0), _nrows(0), _nmax(0)
{
OBConversion::RegisterFormat("_png2",this);
}
virtual const char* Description() {
return
"PNG2 format\n"
"An internal format to write 2D depiction using Cairo\n"
"Called from PNGFormat\n";
};
virtual unsigned int Flags()
{
return NOTREADABLE | WRITEBINARY | DEPICTION2D;
};
bool WriteChemObject(OBConversion* pConv);
bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
private:
int _ncols, _nrows, _nmax;
vector<OBBase*> _objects;
CairoPainter _cairopainter; };
PNG2Format thePNG2Format;
bool PNG2Format::WriteChemObject(OBConversion* pConv) {
OBBase* pOb = pConv->GetChemObject();
if(pConv->GetOutputIndex()<=1)
{
_objects.clear();
_nmax=0;
pConv->AddOption("pngwritechemobject"); const char* pc = pConv->IsOption("c");
const char* pr = pConv->IsOption("r");
if(pr)
_nrows = atoi(pr);
if(pc)
_ncols = atoi(pc);
if(pr && pc) _nmax = _nrows * _ncols;
const char* pmax =pConv->IsOption("N");
if(pmax)
_nmax = atoi(pmax);
}
OBMoleculeFormat::DoOutputOptions(pOb, pConv);
_objects.push_back(pOb);
bool ret=true;
bool nomore = _nmax && (_objects.size()==_nmax);
if((pConv->IsLast() || nomore))
{
int nmols = _objects.size();
if(!(nmols==0 || (_nrows && _ncols) || ((!_nrows && !_ncols) && nmols==1)) )
{
if(!_nrows && !_ncols ) {
_ncols = (int)ceil(sqrt(((double)nmols)));
}
if(_nrows)
_ncols = (nmols-1) / _nrows + 1; else if(_ncols)
_nrows = (nmols-1) / _ncols + 1;
}
int n=0;
vector<OBBase*>::iterator iter;
for(iter=_objects.begin(); ret && iter!=_objects.end(); ++iter)
{
pConv->SetOutputIndex(++n);
pConv->SetLast(n==_objects.size());
ret=WriteMolecule(*iter, pConv);
}
for(iter=_objects.begin();iter!=_objects.end(); ++iter)
delete *iter;
_objects.clear();
_nmax = _ncols = _nrows = 0;
}
if(!ret || nomore)
pConv->SetOutputIndex(pConv->GetOutputIndex()+1);
return ret && !nomore;
}
bool PNG2Format::WriteMolecule(OBBase* pOb, OBConversion* pConv)
{
OBMol* pmol = dynamic_cast<OBMol*>(pOb);
if (pmol == nullptr)
return false;
ostream& ofs = *pConv->GetOutStream();
OBMol workingmol(*pmol);
if (!pConv->IsOption("pngwritechemobject") || (!_nrows && !_ncols))
{ _nmax = _nrows = _ncols = 1;
pConv->SetLast(true);
pConv->SetOutputIndex(1);
}
if(!workingmol.Has2D(true))
{
OBOp* pOp = OBOp::FindType("gen2D");
if(!pOp)
{
obErrorLog.ThrowError("PNG2Format", "gen2D not found", obError, onceOnly);
return false;
}
if(!pOp->Do(&workingmol))
{
obErrorLog.ThrowError("PNG2Format", string(workingmol.GetTitle()) + "- Coordinate generation unsuccessful", obError);
return false;
}
}
if(!workingmol.Has2D() && workingmol.NumAtoms()>1)
{
string mes("Molecule ");
mes += workingmol.GetTitle();
mes += " needs 2D coordinates to display in PNG2format";
obErrorLog.ThrowError("PNG2Format", mes, obError);
return false;
}
const char* pp = pConv->IsOption("p");
int size = pp ? atoi(pp) : 300;
pp = pConv->IsOption("w"); int width = pp ? atoi(pp) : size;
pp = pConv->IsOption("h");
int height = pp ? atoi(pp) : size;
bool transparent=false;
string background, bondcolor;
const char* bg = pConv->IsOption("b");
background = bg ? "black" : "white";
bondcolor = bg ? "white" : "black";
if(bg && (!strcmp(bg, "none") || bg[0]=='0'))
{
transparent = true;
bondcolor = "gray";
}
const char* bcol = pConv->IsOption("B");
if(bcol && *bcol)
bondcolor = bcol;
if(bg && *bg)
background = bg;
string text;
if(!pConv->IsOption("d"))
{
text = pmol->GetTitle();
_cairopainter.SetTitle(text);
}
if(pConv->GetOutputIndex()==1) {
_cairopainter.SetWidth(width);
_cairopainter.SetHeight(height);
_cairopainter.SetTableSize(_nrows, _ncols);
}
_cairopainter.SetIndex(pConv->GetOutputIndex());
if((pConv->GetOutputIndex()==1) && pConv->IsLast() && pConv->IsOption("m")) {
_cairopainter.SetCropping(true);
_cairopainter.SetTitle("");
}
OBDepict depictor(&_cairopainter);
if(!pConv->IsOption("C"))
depictor.SetOption(OBDepict::drawTermC);
if(pConv->IsOption("a"))
depictor.SetOption(OBDepict::drawAllC);
if(pConv->IsOption("A"))
{
AliasData::RevertToAliasForm(workingmol);
depictor.SetAliasMode();
}
_cairopainter.SetBondColor(bondcolor);
depictor.SetBondColor(bondcolor);
_cairopainter.SetBackground(background);
_cairopainter.SetTransparent(transparent);
if(pConv->IsOption("t"))
_cairopainter.SetPenWidth(4);
else
_cairopainter.SetPenWidth(1);
if(pConv->IsOption("u"))
depictor.SetOption(OBDepict::bwAtoms);
if(!pConv->IsOption("U"))
depictor.SetOption(OBDepict::internalColor);
if(pConv->IsOption("s"))
depictor.SetOption(OBDepict::asymmetricDoubleBond);
depictor.DrawMolecule(&workingmol);
if (pConv->IsLast())
{
if(!pConv->IsOption("O"))
_cairopainter.WriteImage(ofs);
else {
stringstream ss;
_cairopainter.WriteImage(ss);
OBConversion conv2(&ss,pConv->GetOutStream());
conv2.CopyOptions(pConv);
OBBase Ob; OBFormat* ppng = OBConversion::FindFormat("png");
if( !conv2.SetInAndOutFormats(ppng, ppng)
|| !ppng->ReadMolecule(&Ob , &conv2))
{
obErrorLog.ThrowError("PNG Format", "Failed to embed molecule(s)",obError);
_cairopainter.WriteImage(ofs); return true;
}
vector<OBBase*>::iterator iter;
bool ret=true;
for(iter=_objects.begin(); ret && iter!=_objects.end(); ++iter)
{
conv2.SetLast(iter==_objects.end()-1);
ret=ppng->WriteMolecule(*iter, &conv2);
}
if(ret)
ofs << ss.rdbuf(); }
}
return true; }
}