Support using FreeType for text rendering (#3237)

* First working version with DrawText classes, original functionality only.  No font scaling.

* Added font scaling.

* Added atom colours.

* First stab at freetype text drawing.  A stash prior to major surgery.

* Freetype seems to be working.  On to whack-a-mole.

* Added class flag to atom labels and annotations.

* Another intermim commit whilst re-factoring all string drawing code from MolDraw2D.

* Fixed scaling and implemented max font size.

* Fixed bugs in non-FT Cairo and SVG drawing.

* More re-factoring of drawStrings - now creates StringRwct for each char in all strings.

* More re-factoring of string drawing - all mentions removed from MolDraw2D, I think..

* Working native Cairo, simple tests.

* Working native Cairo, simple tests.

* Padding roumd rectangles.

* Working FT Cairo, FT SVG, native SVG, simple tests.

* Two line labels mostly sorted. Native SVG wrong.

* Two line SVG labels sorted.

* Two line SVG labels sorted.

* Tidied out debug writes.

* Tweaked merge.

* Annotations working, radicals now failing.

* Fixed radicals crash.

* All tests passed for freetype drawings.  Grid drawings not right.

* Fixed bug in grid drawings.

* Better font size.

* Fixed legends in grids.

* Fixed rect intersection bug.

* Tidied up font sizes.

* moldraw2DTest1 all passing.

* All catch tests pass.

* Few rixes, and reactions look ok.

* Added minimum font size.

* Fixed radical drawing when max/min font size hit.

* Interim cmmmit, most test1.cpp working.

* Fixed uninitialised min_font_size_ in DrawText.  Took out use of MolDraw2D::setFontSize() which probably needs to go back in at some point.

* More test1.cpp passing.

* test1.cpp all pass, freetype and non-freetype

* Fixed superscripts hitting min font size in test860.  Made superscripts and subscripts same size.

* testc.pp all pass.

* Fixed bug in freetype text. All testt1.cpp pass.

* All tests passed.d

* Added option for different font.

* Added option for explicit terminal methyls.

* Added option to explicitly not use Freetype in drawers.  Used same in catch_tests.cpp.

* Got sense of NO_FREETYPE wrong in catch_tests.cpp.  D'oh!

* Fixed Python draw tests.

* Added new options to JSON interpreter.

* Fixed scale of text in contoured plots.

* Added optional molecule to grid drawer to help set scale.

* Fixed Python wrappers  for drawing 2D grids .

* Added Greg's CMakeLists.txt

* Moved fonts out of code tree.
Improved handling of font files not found, including logging to rdWarningLog.

* Interim commit.

* Tidied up some namespace std issues.

* Reverted to previous version.
Took out 'using namespace std;'

* update expected java results

* Added multi-line legends.  Also carves out a reserved bit for the legend, and sets the font size so the legend will fit.

* enable annotations on windows with freetype

* Removed stray font file.

* Removed stray font file.

* Re-instanted fontSize() and setFontSize(), though with change of units.

* Added RDK_BUILD_FREETYPE_SUPPORT to cmake.

* re-expose the fontsize controls to python.
document API change w.r.t. font size

* Update ReleaseNotes.md

Co-authored-by: David Cosgrove <david@cozchemix.co.uk>
Co-authored-by: greg landrum <greg.landrum@gmail.com>
This commit is contained in:
David Cosgrove
2020-06-23 16:31:50 +01:00
committed by GitHub
parent 83a1e75597
commit bc9e4d6478
33 changed files with 3641 additions and 1235 deletions

View File

@@ -2,27 +2,28 @@
option(RDK_BUILD_QT_SUPPORT "build support for QT drawing" OFF )
option(RDK_BUILD_QT_DEMO "build the QT drawing demo" OFF )
option(RDK_BUILD_CAIRO_SUPPORT "build support for Cairo drawing" OFF )
option(RDK_BUILD_FREETYPE_SUPPORT "build support for FreeType font handling" ON)
rdkit_headers(MolDraw2D.h
MolDraw2DSVG.h
MolDraw2Dwx.h
MolDraw2DUtils.h
DrawText.h
DrawTextSVG.h
DEST GraphMol/MolDraw2D
)
remove_definitions(-DRDKIT_GRAPHMOL_BUILD)
add_definitions(-DRDKIT_MOLDRAW2D_BUILD)
rdkit_library(MolDraw2D MolDraw2D.cpp MolDraw2DSVG.cpp
MolDraw2DDetails.cpp MolDraw2DUtils.cpp
MolDraw2DDetails.cpp MolDraw2DUtils.cpp DrawText.cpp
DrawTextSVG.cpp
LINK_LIBRARIES
ChemReactions FileParsers SmilesParse Depictor MolTransforms
SubstructMatch Subgraphs GraphMol EigenSolvers )
if(RDK_BUILD_QT_SUPPORT)
find_package(Qt5 COMPONENTS Widgets OpenGL REQUIRED)
target_sources(MolDraw2D PRIVATE MolDraw2DQt.cpp)
target_compile_options(MolDraw2D PRIVATE "-std=c++11")
target_link_libraries(MolDraw2D PUBLIC Qt5::Widgets Qt5::OpenGL)
@@ -33,10 +34,24 @@ if(RDK_BUILD_CAIRO_SUPPORT)
find_package(Cairo REQUIRED)
target_compile_definitions(MolDraw2D PUBLIC "-DRDK_BUILD_CAIRO_SUPPORT")
target_link_libraries(MolDraw2D PUBLIC Cairo::Cairo)
target_sources(MolDraw2D PRIVATE MolDraw2DCairo.cpp)
rdkit_headers(MolDraw2DCairo.h DEST GraphMol/MolDraw2D)
target_sources(MolDraw2D PRIVATE MolDraw2DCairo.cpp DrawTextCairo.cpp)
rdkit_headers(MolDraw2DCairo.h DrawTextCairo.h DEST GraphMol/MolDraw2D)
endif(RDK_BUILD_CAIRO_SUPPORT)
if(RDK_BUILD_FREETYPE_SUPPORT)
find_package(Freetype REQUIRED)
target_compile_definitions(MolDraw2D PUBLIC "-DRDK_BUILD_FREETYPE_SUPPORT")
target_link_libraries(MolDraw2D PUBLIC Freetype::Freetype)
target_sources(MolDraw2D PRIVATE DrawText.cpp
DrawTextFT.cpp DrawTextFTSVG.cpp)
rdkit_headers(DrawText.h DrawTextFT.h
DrawTextFTSVG.h DEST GraphMol/MolDraw2D)
if(RDK_BUILD_CAIRO_SUPPORT)
target_sources(MolDraw2D PRIVATE DrawTextFTCairo.cpp)
rdkit_headers(DrawTextFTCairo.h DEST GraphMol/MolDraw2D)
endif(RDK_BUILD_CAIRO_SUPPORT)
endif(RDK_BUILD_FREETYPE_SUPPORT)
rdkit_test(moldraw2DTest1 test1.cpp LINK_LIBRARIES
MolDraw2D )

View File

@@ -0,0 +1,743 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 29/04/2020.
//
#include <GraphMol/MolDraw2D/DrawText.h>
namespace RDKit {
// ****************************************************************************
DrawText::DrawText(double max_fnt_sz, double min_fnt_sz)
: colour_(DrawColour(0.0, 0.0, 0.0)), font_scale_(1.0),
max_font_size_(max_fnt_sz), min_font_size_(min_fnt_sz) {
}
// ****************************************************************************
DrawColour const &DrawText::colour() const {
return colour_;
}
// ****************************************************************************
void DrawText::setColour(const DrawColour &col) {
colour_ = col;
}
// ****************************************************************************
double DrawText::fontSize() const {
return fontScale() * FONT_SIZE;
}
// ****************************************************************************
void DrawText::setFontSize(double new_size) {
if(new_size < minFontSize()) {
BOOST_LOG(rdWarningLog) << "The new font size " << new_size
<< " is below the current minimum ("
<< minFontSize() << ")." << std::endl;
} else if(new_size > maxFontSize()) {
BOOST_LOG(rdWarningLog) << "The new font size " << new_size
<< " is above the current maximum ("
<< maxFontSize() << ")." << std::endl;
}
double new_scale = new_size / FONT_SIZE;
setFontScale(new_scale);
}
// ****************************************************************************
double DrawText::maxFontSize() const {
return max_font_size_;
}
// ****************************************************************************
void DrawText::setMaxFontSize(double new_max) {
max_font_size_ = new_max;
}
// ****************************************************************************
double DrawText::minFontSize() const {
return min_font_size_;
}
// ****************************************************************************
void DrawText::setMinFontSize(double new_min) {
min_font_size_ = new_min;
}
// ****************************************************************************
double DrawText::fontScale() const {
return font_scale_;
}
// ****************************************************************************
void DrawText::setFontScale(double new_scale) {
font_scale_ = new_scale;
double nfs = fontSize();
if(max_font_size_ != -1 && nfs > max_font_size_) {
font_scale_ = max_font_size_ / FONT_SIZE;
}
if(min_font_size_ != -1 && nfs < min_font_size_) {
font_scale_ = min_font_size_ / FONT_SIZE;
}
}
// ****************************************************************************
void DrawText::drawString(const std::string &str, const Point2D &cds,
TextAlignType talign) {
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> draw_chars;
getStringRects(str, rects, draw_modes, draw_chars);
alignString(talign, draw_modes, rects);
drawChars(cds, rects, draw_modes, draw_chars);
}
// ****************************************************************************
void DrawText::drawString(const std::string &label, const Point2D &cds,
OrientType orient) {
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> draw_chars;
getStringRects(label, orient, rects, draw_modes, draw_chars);
drawChars(cds, rects, draw_modes, draw_chars);
}
// ****************************************************************************
void DrawText::adjustLineForString(const std::string &label, OrientType orient,
const Point2D &end1, Point2D &end2) const {
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> draw_chars;
Point2D lab_pos = end2;
getStringRects(label, orient, rects, draw_modes, draw_chars);
double bond_len = (end1 - end2).length();
for(size_t i = 0; i < rects.size(); ++i) {
const auto &r = rects[i];
r->trans_ += lab_pos;
Point2D tl, tr, bl, br;
r->calcCorners(tl, tr, br, bl, 0.025 * bond_len);
std::unique_ptr<Point2D> ip(new Point2D);
// if it's a wide label, such as C:7, the bond can intersect
// more than 1 side of the rectangle, so check them all.
if (doLinesIntersect(end2, end1, tl, tr, ip.get())) {
end2 = *ip;
}
if (doLinesIntersect(end2, end1, tr, br, ip.get())) {
end2 = *ip;
}
if (doLinesIntersect(end2, end1, br, bl, ip.get())) {
end2 = *ip;
}
if (doLinesIntersect(end2, end1, bl, tl, ip.get())) {
end2 = *ip;
}
}
}
// ****************************************************************************
void DrawText::drawStringRects(const std::string &label, OrientType orient,
const Point2D &cds, MolDraw2D &mol_draw) const {
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> draw_chars;
size_t i = 0;
getStringRects(label, orient, rects, draw_modes, draw_chars);
for(auto r: rects) {
r->trans_.x += cds.x;
r->trans_.y += cds.y;
Point2D tl, tr, br, bl;
r->calcCorners(tl, tr, br, bl, 0.0);
tl = mol_draw.getAtomCoords(std::make_pair(tl.x, tl.y));
tr = mol_draw.getAtomCoords(std::make_pair(tr.x, tr.y));
br = mol_draw.getAtomCoords(std::make_pair(br.x, br.y));
bl = mol_draw.getAtomCoords(std::make_pair(bl.x, bl.y));
mol_draw.setColour(DrawColour(1.0, 0.0, 0.0));
mol_draw.drawLine(tl, tr);
mol_draw.setColour(DrawColour(0.0, 1.0, 0.0));
mol_draw.drawLine(tr, br);
mol_draw.setColour(DrawColour(0.0, 0.0, 1.0));
mol_draw.drawLine(br, bl);
mol_draw.setColour(DrawColour(0.0, 0.95, 0.95));
mol_draw.drawLine(bl, tl);
++i;
}
}
// ****************************************************************************
bool DrawText::doesRectIntersect(const std::string &label, OrientType orient,
const Point2D &cds, const StringRect &rect) const {
if(label.empty()) {
return false;
}
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> draw_chars;
getStringRects(label, orient, rects, draw_modes, draw_chars);
return doesRectIntersect(rects, cds, rect);
}
// ****************************************************************************
bool DrawText::doesRectIntersect(const std::vector<std::shared_ptr<StringRect>> &rects,
const Point2D &cds,
const StringRect &rect) const {
for(auto r: rects) {
StringRect nr(*r);
nr.trans_ += cds;
if(nr.doesItIntersect(rect)) {
return true;
}
}
return false;
}
// ****************************************************************************
bool DrawText::doesLineIntersect(const std::string &label, OrientType orient,
const Point2D &cds, const Point2D &end1,
const Point2D &end2, double padding) const {
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> draw_chars;
getStringRects(label, orient, rects, draw_modes, draw_chars);
return doesLineIntersect(rects, cds, end1, end2, padding);
}
// ****************************************************************************
bool DrawText::doesLineIntersect(const std::vector<std::shared_ptr<StringRect>> &rects,
const Point2D &cds, const Point2D &end1,
const Point2D &end2, double padding) const {
for(auto r: rects) {
StringRect nr(*r);
nr.trans_ += cds;
Point2D tl, tr, bl, br;
nr.calcCorners(tl, tr, br, bl, padding);
if (doLinesIntersect(end2, end1, tl, tr, nullptr)) {
return true;
}
if (doLinesIntersect(end2, end1, tr, br, nullptr)) {
return true;
}
if (doLinesIntersect(end2, end1, br, bl, nullptr)) {
return true;
}
if (doLinesIntersect(end2, end1, bl, tl, nullptr)) {
return true;
}
}
return false;
}
// ****************************************************************************
bool DrawText::doesStringIntersect(const std::string &label1, OrientType orient1,
const Point2D &cds1, const std::string &label2,
OrientType orient2, const Point2D &cds2) const {
if(label1.empty() || label2.empty()) {
return false;
}
std::vector<std::shared_ptr<StringRect>> rects1;
std::vector<TextDrawType> draw_modes1;
std::vector<char> draw_chars1;
getStringRects(label1, orient1, rects1, draw_modes1, draw_chars1);
return doesStringIntersect(rects1, cds1, label2, orient2, cds2);
}
// ****************************************************************************
bool DrawText::doesStringIntersect(const std::vector<std::shared_ptr<StringRect>> &rects,
const Point2D &cds1, const std::string &label2,
OrientType orient2, const Point2D &cds2) const {
if(label2.empty()) {
return false;
}
std::vector<std::shared_ptr<StringRect>> rects2;
std::vector<TextDrawType> draw_modes2;
std::vector<char> draw_chars2;
getStringRects(label2, orient2, rects2, draw_modes2, draw_chars2);
for(auto r1: rects) {
StringRect nr1(*r1);
nr1.trans_ += cds1;
for(auto r2: rects2) {
StringRect nr2(*r2);
nr2.trans_ += cds2;
if(nr1.doesItIntersect(nr2)) {
return true;
}
}
}
return false;
}
// ****************************************************************************
void DrawText::getStringExtremes(const std::string &label, OrientType orient,
double &x_min, double &y_min,
double &x_max, double &y_max,
bool dontSplit) const {
if (label.empty()) {
x_min = x_max = 0.0;
y_min = y_max = 0.0;
return;
}
x_min = y_min = std::numeric_limits<double>::max();
x_max = y_max = -std::numeric_limits<double>::max();
std::vector<std::shared_ptr<StringRect>> rects;
std::vector<TextDrawType> draw_modes;
std::vector<char> to_draw;
getStringRects(label, orient, rects, draw_modes, to_draw, dontSplit);
int i = 0;
for(auto r: rects) {
Point2D tl, tr, br, bl;
r->calcCorners(tl, tr, br, bl, 0.0);
// sometimes the rect is in a coordinate frame where +ve y is down,
// sometimes it's up. For these purposes, we don't care so long as
// the y_max is larger than the y_min. We probably don't need to do
// all the tests for x_min and x_max;
x_min = std::min(bl.x, x_min);
x_min = std::min(tr.x, x_min);
y_min = std::min(bl.y, y_min);
y_min = std::min(tr.y, y_min);
x_max = std::max(bl.x, x_max);
x_max = std::max(tr.x, x_max);
y_max = std::max(bl.y, y_max);
y_max = std::max(tr.y, y_max);
++i;
}
}
// ****************************************************************************
void DrawText::alignString(TextAlignType talign,
const std::vector<TextDrawType> &draw_modes,
std::vector<std::shared_ptr<StringRect> > &rects) const {
// std::string comes in with rects aligned with first char with its
// left hand and bottom edges at 0 on y and x respectively.
// Adjust relative to that so that the relative alignment point is at
// (0,0).
if(talign == TextAlignType::MIDDLE) {
size_t num_norm = count(draw_modes.begin(), draw_modes.end(),
TextDrawType::TextDrawNormal);
if (num_norm == 1) {
talign = TextAlignType::START;
}
}
Point2D align_trans, align_offset;
if(talign == TextAlignType::START || talign == TextAlignType::END) {
size_t align_char = 0;
for (size_t i = 0; i < rects.size(); ++i) {
if (draw_modes[i] == TextDrawType::TextDrawNormal) {
align_char = i;
if (talign == TextAlignType::START) {
break;
}
}
}
align_trans = rects[align_char]->trans_;
align_offset = rects[align_char]->offset_;
} else {
// centre on the middle of the Normal text. The super- or subscripts
// should be at the ends.
double x_min = std::numeric_limits<double>::max();
double x_max = -x_min;
double y_min = std::numeric_limits<double>::max();
double y_max = -y_min;
align_offset.x = align_offset.y = 0.0;
int num_norm = 0;
for (size_t i = 0; i < rects.size(); ++i) {
if (draw_modes[i] == TextDrawType::TextDrawNormal) {
Point2D tl, tr, br, bl;
rects[i]->calcCorners(tl, tr, br, bl, 0.0);
// sometimes the rect is in a coordinate frame where +ve y is down,
// sometimes it's up. For these purposes, we don't care so long as
// the y_max is larger than the y_min. We probably don't need to do
// all the tests for x_min and x_max;
x_min = std::min(bl.x, x_min);
x_min = std::min(tr.x, x_min);
y_min = std::min(bl.y, y_min);
y_min = std::min(tr.y, y_min);
x_max = std::max(bl.x, x_max);
x_max = std::max(tr.x, x_max);
y_max = std::max(bl.y, y_max);
y_max = std::max(tr.y, y_max);
align_offset += rects[i]->offset_;
++num_norm;
}
}
align_trans.x = (x_max - x_min) / 2.0;
align_trans.y = 0.0;
align_offset /= num_norm;
}
for(auto r: rects) {
r->trans_ -= align_trans;
r->offset_ = align_offset;
}
}
// ****************************************************************************
void DrawText::adjustStringRectsForSuperSubScript(const std::vector<TextDrawType> &draw_modes,
std::vector<std::shared_ptr<StringRect> > &rects) const {
double last_char = -1;
for(size_t i = 0; i < draw_modes.size(); ++i) {
switch(draw_modes[i]) {
case TextDrawType::TextDrawSuperscript:
// superscripts may come before or after a letter. If before,
// spin through to first non-superscript for last_height
if(last_char < 0) {
for (size_t j = i + 1; j < draw_modes.size(); ++j) {
if (draw_modes[j] == TextDrawType::TextDrawNormal) {
last_char = j;
break;
}
}
}
// adjust up by last height / 2.
rects[i]->rect_corr_ = rects[last_char]->height_;
rects[i]->trans_.y -= rects[i]->rect_corr_ / 2.0;
// if the last char was a subscript, remove the advance
if (i && draw_modes[i - 1] == TextDrawType::TextDrawSubscript) {
double move_by = rects[i]->trans_.x - rects[i-1]->trans_.x;
if(move_by > 0.0) {
for (size_t j = 0; j < i; ++j) {
rects[j]->trans_.x += move_by;
}
} else {
for (size_t j = i; j < draw_modes.size(); ++j) {
rects[j]->trans_.x += move_by;
}
}
rects[i]->trans_.x = rects[i-1]->trans_.x;
}
break;
case TextDrawType::TextDrawSubscript:
// adjust y down by last height / 2
rects[i]->rect_corr_ = -rects[last_char]->height_;
rects[i]->trans_.y -= rects[i]->rect_corr_ / 2.0;
// if the last char was a superscript, remove the advance
if (i && draw_modes[i - 1] == TextDrawType::TextDrawSuperscript) {
double move_by = rects[i]->trans_.x - rects[i-1]->trans_.x;
if(move_by > 0.0) {
for (size_t j = 0; j < i; ++j) {
rects[j]->trans_.x += move_by;
}
} else {
for (size_t j = i; j < draw_modes.size(); ++j) {
rects[j]->trans_.x += move_by;
}
}
rects[i]->trans_.x = rects[i-1]->trans_.x;
}
break;
case TextDrawType::TextDrawNormal:
last_char = i;
break;
}
}
}
// ****************************************************************************
double DrawText::selectScaleFactor(char c, TextDrawType draw_type) const {
switch(draw_type) {
case TextDrawType::TextDrawNormal:
return 1.0;
case TextDrawType::TextDrawSubscript:
return SUBS_SCALE;
case TextDrawType::TextDrawSuperscript:
if(c == '-' || c == '+') {
return SUBS_SCALE;
} else {
return SUPER_SCALE;
}
}
return 1.0;
}
// ****************************************************************************
void DrawText::getStringSize(const std::string &label, double &label_width,
double &label_height) const {
double x_min, y_min, x_max, y_max;
getStringExtremes(label, OrientType::E, x_min, y_min, x_max, y_max);
label_width = x_max - x_min;
label_height = y_max - y_min;
}
// ****************************************************************************
bool setStringDrawMode(const std::string &instring,
TextDrawType &draw_mode,
size_t &i) {
std::string bit1 = instring.substr(i, 5);
std::string bit2 = instring.substr(i, 6);
// could be markup for super- or sub-script
if (std::string("<sub>") == bit1) {
draw_mode = TextDrawType::TextDrawSubscript;
i += 4;
return true;
} else if (std::string("<sup>") == bit1) {
draw_mode = TextDrawType::TextDrawSuperscript;
i += 4;
return true;
} else if (std::string("</sub>") == bit2) {
draw_mode = TextDrawType::TextDrawNormal;
i += 5;
return true;
} else if (std::string("</sup>") == bit2) {
draw_mode = TextDrawType::TextDrawNormal;
i += 5;
return true;
}
return false;
}
// ****************************************************************************
void DrawText::getStringRects(const std::string &text, OrientType orient,
std::vector<std::shared_ptr<StringRect> > &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars,
bool dontSplit) const {
std::vector<std::string> text_bits;
if(!dontSplit) {
text_bits = atomLabelToPieces(text, orient);
} else {
text_bits.push_back(text);
}
if(orient == OrientType::W) {
// stick the pieces together again backwards and draw as one so there
// aren't ugly splits in the string.
std::string new_lab;
for (auto i = text_bits.rbegin(); i != text_bits.rend(); ++i) {
new_lab += *i;
}
getStringRects(new_lab, rects, draw_modes, draw_chars);
alignString(TextAlignType::END, draw_modes, rects);
} else if(orient == OrientType::E) {
// likewise, but forwards
std::string new_lab;
for (auto lab: text_bits) {
new_lab += lab;
}
getStringRects(new_lab, rects, draw_modes, draw_chars);
alignString(TextAlignType::START, draw_modes, rects);
} else {
double running_y = 0;
for(size_t i = 0; i < text_bits.size(); ++i) {
std::vector<std::shared_ptr<StringRect>> t_rects;
std::vector<TextDrawType> t_draw_modes;
std::vector<char> t_draw_chars;
getStringRects(text_bits[i], t_rects, t_draw_modes, t_draw_chars);
alignString(TextAlignType::MIDDLE, t_draw_modes, t_rects);
double max_height = -std::numeric_limits<double>::max();
for(auto r: t_rects) {
max_height = std::max(r->height_, max_height);
r->y_shift_ = running_y;
}
rects.insert(rects.end(), t_rects.begin(), t_rects.end());
draw_modes.insert(draw_modes.end(), t_draw_modes.begin(),
t_draw_modes.end());
draw_chars.insert(draw_chars.end(), t_draw_chars.begin(),
t_draw_chars.end());
if(orient == OrientType::N) {
running_y -= 1.1 * max_height;
} else if(orient == OrientType::S) {
running_y += 1.1 * max_height;
}
}
}
}
// ****************************************************************************
void DrawText::drawChars(const Point2D &a_cds,
const std::vector<std::shared_ptr<StringRect> > &rects,
const std::vector<TextDrawType> &draw_modes,
const std::vector<char> &draw_chars) {
double full_scale = fontScale();
for(size_t i = 0; i < rects.size(); ++i) {
Point2D draw_cds;
draw_cds.x = a_cds.x + rects[i]->trans_.x - rects[i]->offset_.x;
draw_cds.y = a_cds.y - rects[i]->trans_.y + rects[i]->offset_.y; // opposite sign convention
draw_cds.y -= rects[i]->rect_corr_ + rects[i]->y_shift_;
double mfs = minFontSize();
setMinFontSize(-1);
setFontScale(full_scale * selectScaleFactor(draw_chars[i], draw_modes[i]));
drawChar(draw_chars[i], draw_cds);
setMinFontSize(mfs);
setFontScale(full_scale);
}
}
// ****************************************************************************
std::vector<std::string> atomLabelToPieces(const std::string &label, OrientType orient) {
// cout << "splitting " << label << " : " << orient << endl;
std::vector<std::string> label_pieces;
if (label.empty()) {
return label_pieces;
}
// if we have the mark-up <lit>XX</lit> the symbol is to be used
// without modification
if (label.substr(0, 5) == "<lit>") {
std::string lit_sym = label.substr(5);
size_t idx = lit_sym.find("</lit>");
if (idx != std::string::npos) {
lit_sym = lit_sym.substr(0, idx);
}
label_pieces.emplace_back(lit_sym);
return label_pieces;
}
std::string next_piece;
size_t i = 0;
while (true) {
if (i == label.length()) {
if (!next_piece.empty()) {
label_pieces.emplace_back(next_piece);
break;
}
}
if (label.substr(i, 2) == "<s" || label[i] == ':' || isupper(label[i])) {
// save the old piece, start a new one
if (!next_piece.empty()) {
label_pieces.emplace_back(next_piece);
next_piece.clear();
}
}
next_piece += label[i++];
}
if (label_pieces.size() < 2) {
return label_pieces;
}
// if the orientation is S or E, any charge flag needs to be at the end.
if(orient == OrientType::E || orient == OrientType::S) {
for (size_t i = 0; i < label_pieces.size(); ++i) {
if(label_pieces[i] == "<sup>+</sup>" || label_pieces[i] == "<sup>-</sup>") {
label_pieces.push_back(label_pieces[i]);
label_pieces[i].clear();
break;
}
}
}
// Now group some together. This relies on the order that
// getAtomLabel built them in the first place. Each atom symbol
// needs to be flanked by any <sub> and <super> pieces.
std::vector<std::string> final_pieces;
std::string curr_piece;
bool had_symbol = false;
for(const auto p: label_pieces) {
if(p.empty()) {
continue;
}
if(!isupper(p[0])) {
curr_piece += p;
} else {
if(had_symbol) {
final_pieces.push_back(curr_piece);
curr_piece = p;
had_symbol = true;
} else {
curr_piece += p;
had_symbol = true;
}
}
}
if(!curr_piece.empty()) {
final_pieces.push_back(curr_piece);
}
// cout << "Final pieces : " << endl;
// for(auto l: final_pieces) {
// cout << l << endl;
// }
// cout << endl;
return final_pieces;
}
std::ostream& operator<<(std::ostream &oss, const TextAlignType &tat) {
switch(tat) {
case TextAlignType::START: oss << "START"; break;
case TextAlignType::MIDDLE: oss << "MIDDLE"; break;
case TextAlignType::END: oss << "END"; break;
}
return oss;
}
std::ostream& operator<<(std::ostream &oss, const TextDrawType &tdt) {
switch(tdt) {
case TextDrawType::TextDrawNormal: oss << "TextDrawNormal"; break;
case TextDrawType::TextDrawSuperscript: oss << "TextDrawSuperscript"; break;
case TextDrawType::TextDrawSubscript: oss << "TextDrawSubscript"; break;
}
return oss;
}
std::ostream& operator<<(std::ostream &oss, const OrientType &o) {
switch(o) {
case OrientType::C: oss << "C"; break;
case OrientType::N: oss << "N"; break;
case OrientType::S: oss << "S"; break;
case OrientType::E: oss << "E"; break;
case OrientType::W: oss << "W"; break;
}
return oss;
}
} // namespace RDKit

View File

@@ -0,0 +1,184 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
//
// Original author: David Cosgrove (CozChemIx) on 29/04/2020.
//
// This is an abstract base class for drawing text into a MolDraw2D
// object.
#ifndef RDKIT_DRAWTEXT_H
#define RDKIT_DRAWTEXT_H
#include <string>
#include <vector>
#include <Geometry/point.h>
#include <GraphMol/MolDraw2D/MolDraw2D.h>
using RDGeom::Point2D;
namespace RDKit {
// for aligning the drawing of text to the passed in coords.
enum class OrientType:unsigned char { C = 0, N, E, S, W };
enum class TextAlignType:unsigned char { START, MIDDLE, END };
enum class TextDrawType:unsigned char {
TextDrawNormal = 0,
TextDrawSuperscript,
TextDrawSubscript
};
std::ostream& operator<<(std::ostream &oss, const TextAlignType &tat);
std::ostream& operator<<(std::ostream &oss, const TextDrawType &tdt);
std::ostream& operator<<(std::ostream &oss, const OrientType &o);
// ****************************************************************************
class DrawText {
public:
static constexpr double FONT_SIZE = 0.6; // seems to be a good number
DrawText(double max_fnt_sz, double min_fnt_sz);
virtual ~DrawText() {}
DrawColour const &colour() const;
void setColour(const DrawColour &col);
// size in "pixels" i.e scale() * FONT_SIZE.
double fontSize() const;
void setFontSize(double new_size);
double maxFontSize() const;
void setMaxFontSize(double new_max);
double minFontSize() const;
void setMinFontSize(double new_max);
double fontScale() const;
void setFontScale(double new_scale);
// these are only relevant for the FreeType DrawText classes.
virtual std::string getFontFile() const { return "";}
virtual void setFontFile(const std::string &font_file) {
RDUNUSED_PARAM(font_file);
}
//! using the current scale, work out the size of the label
/*!
Bear in mind when implementing this, that, for example, NH2 will appear as
NH<sub>2</sub> to convey that the 2 is a subscript, and this needs to
be accounted for in the width and height.
*/
virtual void getStringSize(const std::string &label, double &label_width,
double &label_height) const;
// returns the extremes of the label, in draw (pixel) coords. dontSplit
// true suppresses the call to atomLabelToPieces.
void getStringExtremes(const std::string &label, OrientType orient,
double &x_min, double &y_min,
double &x_max, double &y_max,
bool dontSplit=false) const;
void getStringRects(const std::string &text, OrientType orient,
std::vector<std::shared_ptr<StringRect>> &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars,
bool dontSplit=false) const;
//! drawString centres the string on cds.
virtual void drawString(const std::string &str, const Point2D &cds,
TextAlignType align);
// Aligns them according to OrientType. This version assumes it's an
// atomic symbol and calls atomLabelToPieces. This will behave
// badly with general text. Surround the string with <lit></lit>
// if that's an issue, or use the version above.
void drawString(const std::string &label, const Point2D &cds,
OrientType orient);
// put the label on end2, and then move end2 so that it is at
// the intersection of a string rectangle and the line from end1 to
// end2, if there is an intersection. Mostly for trimming bonds
// back from atom labels. end1 and end2 in draw coords.
void adjustLineForString(const std::string &label, OrientType orient,
const Point2D &end1, Point2D &end2) const;
// draw the char, with the bottom left hand corner at cds
virtual void drawChar(char c, const Point2D &cds) = 0;
// puts a colourful rectangle around each character in the string.
// For debugging, mostly.
void drawStringRects(const std::string &label, OrientType orient,
const Point2D &cds, MolDraw2D &mol_draw) const;
// cds in draw coords
// does the label at cds intersect the given StringRect.
bool doesRectIntersect(const std::string &label, OrientType orient,
const Point2D &cds, const StringRect &rect) const;
// does the vector of StringRects, each translated by cds, intersect the
// given StringRect.
bool doesRectIntersect(const std::vector<std::shared_ptr<StringRect>> &rects,
const Point2D &cds, const StringRect &rect) const;
bool doesLineIntersect(const std::string &label, OrientType orient,
const Point2D &cds, const Point2D &end1,
const Point2D &end2, double padding) const;
bool doesLineIntersect(const std::vector<std::shared_ptr<StringRect>> &rects,
const Point2D &cds, const Point2D &end1,
const Point2D &end2, double padding) const;
bool doesStringIntersect(const std::vector<std::shared_ptr<StringRect>> &rects,
const Point2D &cds1, const std::string &label2,
OrientType orient2, const Point2D &cds2) const;
bool doesStringIntersect(const std::string &label1, OrientType orient1,
const Point2D &cds1, const std::string &label2,
OrientType orient2, const Point2D &cds2) const;
protected:
// amount to scale subscripts and superscripts by
constexpr static double SUBS_SCALE = 0.66;
constexpr static double SUPER_SCALE = 0.66;
virtual void alignString(TextAlignType align,
const std::vector<TextDrawType> &draw_modes,
std::vector<std::shared_ptr<StringRect> > &rects) const;
// adjust the string rectangles up and down for super- and subscripts
void adjustStringRectsForSuperSubScript(const std::vector<TextDrawType> &draw_modes,
std::vector<std::shared_ptr<StringRect>> &rects) const;
// return a scale factor appropriate for the character and draw type
// (normal or super- or subscript)
double selectScaleFactor(char c, TextDrawType draw_type) const;
private:
DrawColour colour_;
double font_scale_;
double max_font_size_;
double min_font_size_;
// return a vector of StringRects, one for each char in text, with
// super- and subscripts taken into account. Sizes in pixel coords,
// i.e. scaled by fontScale().
virtual void getStringRects(const std::string &text,
std::vector<std::shared_ptr<StringRect>> &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars) const = 0;
void drawChars(const Point2D &a_cds,
const std::vector<std::shared_ptr<StringRect>> &rects,
const std::vector<TextDrawType> &draw_modes,
const std::vector<char> &draw_chars);
};
//! establishes whether to put string draw mode into super- or sub-script
//! mode based on contents of instring from i onwards. Increments i
//! appropriately
//! \returns true or false depending on whether it did something or not
bool setStringDrawMode(const std::string &instring,
TextDrawType &draw_mode, size_t &i);
// take the label for the given atom and return the individual pieces
// that need to be drawn for it. So NH<sub>2</sub> will return
// "N", "H<sub>2</sub>".
std::vector<std::string> atomLabelToPieces(const std::string &label,
OrientType orient);
} // namespace RDKit
#endif // RDKIT_DRAWTEXT_H

View File

@@ -0,0 +1,88 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 29/04/2020.
//
#include <GraphMol/MolDraw2D/DrawTextCairo.h>
#include <GraphMol/MolDraw2D/MolDraw2D.h>
using namespace std;
namespace RDKit {
// ****************************************************************************
DrawTextCairo::DrawTextCairo(double max_fnt_sz, double min_fnt_sz, cairo_t *dp_cr)
: DrawText(max_fnt_sz, min_fnt_sz), dp_cr_(dp_cr) {
cairo_select_font_face(dp_cr, "sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(dp_cr, fontSize());
}
// ****************************************************************************
// draw the char, with the bottom left hand corner at cds
void DrawTextCairo::drawChar(char c, const Point2D &cds) {
PRECONDITION(dp_cr_, "no draw context");
cairo_set_font_size(dp_cr_, fontSize());
DrawColour col = colour();
cairo_set_source_rgb(dp_cr_, col.r, col.g, col.b);
char txt[2];
txt[0] = c;
txt[1] = 0;
cairo_move_to(dp_cr_, cds.x, cds.y);
cairo_show_text(dp_cr_, txt);
cairo_stroke(dp_cr_);
}
// ****************************************************************************
void DrawTextCairo::getStringRects(const string &text,
vector<shared_ptr<StringRect>> &rects,
vector<TextDrawType> &draw_modes,
vector<char> &draw_chars) const {
TextDrawType draw_mode = TextDrawType::TextDrawNormal;
double running_x = 0.0;
char char_str[2];
char_str[1] = 0;
double max_y = 0.0;
double full_fs = fontSize();
for(size_t i = 0; i < text.length(); ++i) {
// setStringDrawMode moves i along to the end of any <sub> or <sup>
// markup
if ('<' == text[i] && setStringDrawMode(text, draw_mode, i)) {
continue;
}
draw_chars.push_back(text[i]);
char_str[0] = text[i];
cairo_text_extents_t extents;
cairo_set_font_size(dp_cr_, selectScaleFactor(text[i], draw_mode) * full_fs);
cairo_text_extents(dp_cr_, char_str, &extents);
cairo_set_font_size(dp_cr_, full_fs);
double twidth = extents.width;
double theight = extents.height;
Point2D offset(extents.x_bearing + twidth / 2.0,
-extents.y_bearing / 2.0);
Point2D g_centre(offset.x, -extents.y_bearing - theight / 2.0);
rects.push_back(shared_ptr<StringRect>(new StringRect(offset, g_centre, twidth, theight)));
rects.back()->trans_.x = running_x;
draw_modes.push_back(draw_mode);
running_x += extents.x_advance;
max_y = max(max_y, -extents.y_bearing);
}
for(auto r: rects) {
r->g_centre_.y = max_y - r->g_centre_.y;
r->offset_.y = max_y / 2.0;
}
adjustStringRectsForSuperSubScript(draw_modes, rects);
}
} // namespace RDKit

View File

@@ -0,0 +1,49 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 29/04/2020.
//
// A concrete class derived from DrawText that uses the Cairo
// toy API to draw text onto a surface.
#ifndef RDKIT_DRAWTEXTCAIRO_H
#define RDKIT_DRAWTEXTCAIRO_H
#include <cairo.h>
#include <GraphMol/MolDraw2D/DrawText.h>
namespace RDKit {
// ****************************************************************************
class DrawTextCairo : public DrawText {
public:
DrawTextCairo(double max_fnt_sz, double min_fnt_sz, cairo_t *dp_cr);
#if 0
void getStringSize(const std::string &label, double &label_width,
double &label_height) const override;
#endif
void drawChar(char c, const Point2D &cds) override;
private:
cairo_t *dp_cr_;
// return a vector of StringRects, one for each char in text, with
// super- and subscripts taken into account. Sizes in pixel coords,
// i.e. scaled by fontScale().
void getStringRects(const std::string &text,
std::vector<std::shared_ptr<StringRect>> &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars) const override;
};
} // namespace RDKit
#endif // RDKIT_DRAWTEXTCAIRO_H

View File

@@ -0,0 +1,236 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
//
// Original author: David Cosgrove (CozChemIx) on 06/05/2020.
//
#include <cstdio>
#include <iostream>
#include <GraphMol/MolDraw2D/MolDraw2D.h>
#include <GraphMol/MolDraw2D/DrawTextFT.h>
namespace RDKit {
// ****************************************************************************
DrawTextFT::DrawTextFT(double max_fnt_sz, double min_fnt_sz,
const std::string &font_file) :
DrawText(max_fnt_sz, min_fnt_sz), library_(nullptr), face_(nullptr),
x_trans_(0), y_trans_(0), string_y_max_(0) {
int err_code = FT_Init_FreeType(&library_);
if(err_code != FT_Err_Ok) {
throw std::runtime_error(std::string("Couldn't initialise Freetype."));
}
setFontFile(font_file);
}
// ****************************************************************************
DrawTextFT::~DrawTextFT() {
FT_Done_Face(face_);
FT_Done_FreeType(library_);
}
// ****************************************************************************
void DrawTextFT::drawChar(char c, const Point2D &cds) {
FT_Load_Char(face_, c, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
x_trans_ = cds.x;
y_trans_ = cds.y;
extractOutline();
}
// ****************************************************************************
double DrawTextFT::fontCoordToDrawCoord(FT_Pos fc) const {
double pc = fontSize() * fc * em_scale_;
return pc;
}
// ****************************************************************************
void DrawTextFT::fontPosToDrawPos(FT_Pos fx, FT_Pos fy,
double &dx, double &dy) const {
dx = x_trans_ + fontCoordToDrawCoord(fx);
// freetype has the origin at bottom left, we want it in the top left.
FT_Pos rev_y = string_y_max_ - fy;
dy = y_trans_ + fontCoordToDrawCoord(rev_y);
}
// ****************************************************************************
double DrawTextFT::extractOutline() {
FT_Outline_Funcs callbacks;
callbacks.move_to = moveToFunction;
callbacks.line_to = lineToFunction;
callbacks.conic_to = conicToFunction;
callbacks.cubic_to = cubicToFunction;
callbacks.shift = 0;
callbacks.delta = 0;
FT_GlyphSlot slot = face_->glyph;
FT_Outline &outline = slot->outline;
FT_Error error = FT_Outline_Decompose(&outline, &callbacks, this);
if(error != FT_Err_Ok) {
/* not sure what to do in this case */ ;
}
return fontCoordToDrawCoord(slot->advance.x);
}
// ****************************************************************************
std::string DrawTextFT::getFontFile() const {
if(!font_file_.empty()) {
return font_file_;
}
std::string ff_name = getenv("RDBASE") ? getenv("RDBASE") : "";
if(ff_name.empty()) {
throw std::runtime_error("Freetype not initialised because RDBASE not defined.");
}
ff_name += "/Data/Fonts/Telex-Regular.ttf";
return ff_name;
}
// ****************************************************************************
void DrawTextFT::setFontFile(const std::string &font_file) {
if(face_ && font_file == font_file_) {
return;
}
font_file_ = font_file;
std::string font_file_ = getFontFile();
FT_Done_Face(face_);
face_ = nullptr;
// take the first face
int err_code = FT_New_Face(library_, font_file_.c_str(), 0, &face_);
if(err_code != FT_Err_Ok) {
throw std::runtime_error(std::string("Font file ") + font_file_
+ std::string(" not found."));
}
em_scale_ = 1.0 / face_->units_per_EM;
}
// ****************************************************************************
void DrawTextFT::getStringRects(const std::string &text,
std::vector<std::shared_ptr<StringRect>> &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars) const {
TextDrawType draw_mode = TextDrawType::TextDrawNormal;
double running_x = 0.0, max_y = 0.0;
for(size_t i = 0; i < text.length(); ++i) {
// setStringDrawMode moves i along to the end of any <sub> or <sup>
// markup
if ('<' == text[i] && setStringDrawMode(text, draw_mode, i)) {
continue;
}
draw_chars.push_back(text[i]);
FT_Pos this_x_min, this_y_min, this_x_max, this_y_max, advance;
calcGlyphBBox(text[i], this_x_min, this_y_min, this_x_max, this_y_max,
advance);
double oscale = selectScaleFactor(text[i], draw_mode);
double p_x_min = oscale * fontCoordToDrawCoord(this_x_min);
double p_y_min = oscale * fontCoordToDrawCoord(this_y_min);
double p_x_max = oscale * fontCoordToDrawCoord(this_x_max);
double p_y_max = oscale * fontCoordToDrawCoord(this_y_max);
double p_advance = oscale * fontCoordToDrawCoord(advance);
double width = p_x_max - p_x_min;
if(!this_x_max) {
// it was a space, probably.
width = p_advance;
}
double height = p_y_max - p_y_min;
Point2D offset(p_x_min + width / 2.0, p_y_max / 2.0);
Point2D g_centre(offset.x, p_y_max - height / 2.0);
rects.push_back(std::shared_ptr<StringRect>(new StringRect(offset, g_centre,
width, height)));
rects.back()->trans_.x = running_x;
draw_modes.push_back(draw_mode);
running_x += this_x_max ? p_x_max : p_advance;
max_y = std::max(max_y, p_y_max);
}
for(auto r: rects) {
r->g_centre_.y = max_y - r->g_centre_.y;
r->offset_.y = max_y / 2.0;
}
adjustStringRectsForSuperSubScript(draw_modes, rects);
}
// ****************************************************************************
void DrawTextFT::calcGlyphBBox(char c, FT_Pos &x_min, FT_Pos &y_min,
FT_Pos &x_max , FT_Pos &y_max,
FT_Pos &advance) const {
FT_Load_Char(face_, c, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
FT_GlyphSlot slot = face_->glyph;
FT_Outline &outline = slot->outline;
FT_BBox bbox;
FT_Outline_Get_BBox(&outline, &bbox);
x_min = bbox.xMin;
y_min = bbox.yMin;
x_max = bbox.xMax;
y_max = bbox.yMax;
advance = slot->advance.x;
}
// ****************************************************************************
int moveToFunction(const FT_Vector *to, void *user) {
auto *rdft = static_cast<DrawTextFT *>(user);
return rdft->MoveToFunctionImpl(to);
}
// ****************************************************************************
int lineToFunction(const FT_Vector *to, void *user) {
auto *rdft = static_cast<DrawTextFT *>(user);
return rdft->LineToFunctionImpl(to);
}
// ****************************************************************************
int conicToFunction(const FT_Vector *control, const FT_Vector *to,
void *user) {
auto *rdft = static_cast<DrawTextFT *>(user);
return rdft->ConicToFunctionImpl(control, to);
}
// ****************************************************************************
int cubicToFunction(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to, void *user) {
auto *rdft = static_cast<DrawTextFT *>(user);
return rdft->CubicToFunctionImpl(controlOne, controlTwo, to);
}
} // namespace RDKit

View File

@@ -0,0 +1,96 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
//
// Original author: David Cosgrove (CozChemIx) on 06/05/2020.
//
// This is an abstract base class derived from DrawText that does drawing
// using FreeType.
#ifndef RDKIT_DRAWTEXTFT_H
#define RDKIT_DRAWTEXTFT_H
#include <string>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BBOX_H
#include FT_OUTLINE_H
#include <GraphMol/MolDraw2D/DrawText.h>
namespace RDKit {
struct StringRect;
// ****************************************************************************
class DrawTextFT : public DrawText {
public:
DrawTextFT(double max_fnt_sz, double min_fnt_sz,
const std::string &font_file);
~DrawTextFT();
void drawChar(char c, const Point2D &cds) override;
virtual int MoveToFunctionImpl(const FT_Vector *to) = 0;
virtual int LineToFunctionImpl(const FT_Vector *to) = 0;
virtual int ConicToFunctionImpl(const FT_Vector *control,
const FT_Vector *to) = 0;
virtual int CubicToFunctionImpl(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to) = 0;
// unless over-ridden by the c'tor, this will return a hard-coded
// file from $RDBASE.
std::string getFontFile() const override;
void setFontFile(const std::string &font_file) override;
protected:
double fontCoordToDrawCoord(FT_Pos fc) const;
void fontPosToDrawPos(FT_Pos fx, FT_Pos fy, double &dx, double &dy) const;
// adds x_trans_ and y_trans_ to coords returns x advance distance
virtual double extractOutline();
private:
FT_Library library_;
FT_Face face_;
std::string font_file_; // over-rides default if not empty.
double x_trans_, y_trans_;
mutable FT_Pos string_y_max_; // maximum y value of string drawn, for inverting y
double em_scale_;
// return a vector of StringRects, one for each char in text, with
// super- and subscripts taken into account. Sizes in pixel coords,
// i.e. scaled by fontScale().
void getStringRects(const std::string &text,
std::vector<std::shared_ptr<StringRect>> &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars) const override;
// calculate the bounding box of the glyph for c in
// font units (0 -> face_->units_per_EM (2048 for roboto font).
void calcGlyphBBox(char c, FT_Pos &x_min, FT_Pos &y_min,
FT_Pos &x_max, FT_Pos &y_max, FT_Pos &advance) const;
};
// Callbacks for FT_Outline_Decompose. user should be a pointer to
// an instance of RDFreeType.
int moveToFunction(const FT_Vector *to, void *user);
int lineToFunction(const FT_Vector *to, void *user);
int conicToFunction(const FT_Vector *control, const FT_Vector *to,
void *user);
int cubicToFunction(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to, void *user);
} // namespace RDKit
#endif // RDKIT_DRAWTEXTFT_H

View File

@@ -0,0 +1,102 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 08/05/2020.
//
#include <GraphMol/MolDraw2D/DrawTextFTCairo.h>
namespace RDKit {
// ****************************************************************************
DrawTextFTCairo::DrawTextFTCairo(double max_fnt_sz, double min_fnt_sz,
const std::string &font_file,
cairo_t *dp_cr)
: DrawTextFT(max_fnt_sz, min_fnt_sz, font_file) , dp_cr_(dp_cr){
}
// ****************************************************************************
double DrawTextFTCairo::extractOutline() {
cairo_set_source_rgb(dp_cr_, colour().r, colour().g, colour().b);
double adv = DrawTextFT::extractOutline();
cairo_fill(dp_cr_);
return adv;
}
// ****************************************************************************
int DrawTextFTCairo::MoveToFunctionImpl(const FT_Vector *to) {
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
cairo_move_to(dp_cr_, dx, dy);
return 0;
}
// ****************************************************************************
int DrawTextFTCairo::LineToFunctionImpl(const FT_Vector *to) {
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
cairo_line_to(dp_cr_, dx, dy);
return 0;
}
// ****************************************************************************
int DrawTextFTCairo::ConicToFunctionImpl(const FT_Vector *control,
const FT_Vector *to) {
// Cairo doesn't have a quadratic bezier function, we need to promote
// it to a cubic using formula from
// https://lists.cairographics.org/archives/cairo/2010-April/019691.html
// and correcting the typo on the last line.
double x0, y0;
cairo_get_current_point (dp_cr_, &x0, &y0);
double x1, y1;
fontPosToDrawPos(control->x, control->y, x1, y1);
double x2, y2;
fontPosToDrawPos(to->x, to->y, x2, y2);
cairo_curve_to (dp_cr_,
(2.0 / 3.0) * x1 + (1.0 / 3.0) * x0,
(2.0 / 3.0) * y1 + (1.0 / 3.0) * y0,
(2.0 / 3.0) * x1 + (1.0 / 3.0) * x2,
(2.0 / 3.0) * y1 + (1.0 / 3.0) * y2,
x2, y2);
return 0;
}
// ****************************************************************************
int DrawTextFTCairo::CubicToFunctionImpl(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to) {
double controlOneX, controlOneY;
fontPosToDrawPos(controlOne->x, controlOne->y, controlOneX, controlOneY);
double controlTwoX, controlTwoY;
fontPosToDrawPos(controlTwo->x, controlTwo->y, controlTwoX, controlTwoY);
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
cairo_curve_to(dp_cr_, controlOneX, controlOneY, controlTwoX,
controlTwoY, dx, dy);
return 0;
}
} // namespace RDKit

View File

@@ -0,0 +1,47 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 08/05/2020.
//
#ifndef RDKIT_DRAWTEXTFTCAIRO_H
#define RDKIT_DRAWTEXTFTCAIRO_H
#include <cairo.h>
#include <GraphMol/MolDraw2D/DrawTextFT.h>
namespace RDKit {
// ****************************************************************************
class DrawTextFTCairo : public DrawTextFT {
public:
DrawTextFTCairo(double max_fnt_sz, double min_fnt_sz,
const std::string &font_file, cairo_t *dp_cr);
int MoveToFunctionImpl(const FT_Vector *to) override;
int LineToFunctionImpl(const FT_Vector *to) override;
int ConicToFunctionImpl(const FT_Vector *control,
const FT_Vector *to) override;
int CubicToFunctionImpl(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to) override;
protected:
// adds x_trans_ and y_trans_ to coords returns x advance distance
virtual double extractOutline() override;
private:
cairo_t *dp_cr_;
};
} // namespace RDKit
#endif // RDKIT_DRAWTEXTFTCAIRO_H

View File

@@ -0,0 +1,100 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 08/05/2020.
//
#include <GraphMol/MolDraw2D/DrawTextFTSVG.h>
namespace RDKit {
std::string DrawColourToSVG(const RDKit::DrawColour &col);
// ****************************************************************************
DrawTextFTSVG::DrawTextFTSVG(double max_fnt_sz, double min_fnt_sz,
const std::string &font_file,
std::ostream &oss, std::string &d_act_class)
: DrawTextFT(max_fnt_sz, min_fnt_sz, font_file),
oss_(oss), d_active_class_(d_act_class) {
}
// ****************************************************************************
double DrawTextFTSVG::extractOutline() {
std::string col = DrawColourToSVG(colour());
oss_ << "<path ";
if (!d_active_class_.empty()) {
oss_ << " class='" << d_active_class_ << "'" << " d='";
} else {
oss_ << " d='";
}
double adv = DrawTextFT::extractOutline();
oss_ << "' fill='" << col << "'/>" << std::endl;
return adv;
}
// ****************************************************************************
int DrawTextFTSVG::MoveToFunctionImpl(const FT_Vector *to) {
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
oss_ << "M " << dx << ' ' << dy << std::endl;
return 0;
}
// ****************************************************************************
int DrawTextFTSVG::LineToFunctionImpl(const FT_Vector *to) {
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
oss_ << "L " << dx << ' ' << dy << std::endl;
return 0;
}
// ****************************************************************************
int DrawTextFTSVG::ConicToFunctionImpl(const FT_Vector *control,
const FT_Vector *to) {
double controlX, controlY;
fontPosToDrawPos(control->x, control->y, controlX, controlY);
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
oss_ << "Q " << controlX << ' ' << controlY << ", "
<< dx << ' ' << dy << std::endl;
return 0;
}
// ****************************************************************************
int DrawTextFTSVG::CubicToFunctionImpl(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to) {
double controlOneX, controlOneY;
fontPosToDrawPos(controlOne->x, controlOne->y, controlOneX, controlOneY);
double controlTwoX, controlTwoY;
fontPosToDrawPos(controlTwo->x, controlTwo->y, controlTwoX, controlTwoY);
double dx, dy;
fontPosToDrawPos(to->x, to->y, dx, dy);
oss_ << "C " << controlOneX << ' ' << controlOneY << ", "
<< controlTwoX << ' ' << controlTwoY << ", "
<< dx << ' ' << dy << std::endl;
return 0;
}
} // namespace RDKit

View File

@@ -0,0 +1,48 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 08/05/2020.
//
#ifndef RDKIT_DRAWTEXTFTSVG_H
#define RDKIT_DRAWTEXTFTSVG_H
#include <iosfwd>
#include <GraphMol/MolDraw2D/DrawTextFT.h>
namespace RDKit {
// ****************************************************************************
class DrawTextFTSVG : public DrawTextFT {
public:
DrawTextFTSVG(double max_fnt_sz, double min_fnt_sz,
const std::string &font_file,
std::ostream &oss, std::string &d_act_class);
int MoveToFunctionImpl(const FT_Vector *to) override;
int LineToFunctionImpl(const FT_Vector *to) override;
int ConicToFunctionImpl(const FT_Vector *control,
const FT_Vector *to) override;
int CubicToFunctionImpl(const FT_Vector *controlOne,
const FT_Vector *controlTwo,
const FT_Vector *to) override;
protected:
// adds x_trans_ and y_trans_ to coords returns x advance distance
virtual double extractOutline() override;
private:
std::ostream &oss_;
std::string &d_active_class_;
};
} // namespace RDKit
#endif // RDKIT_DRAWTEXTFTSVG_H

View File

@@ -0,0 +1,129 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 29/04/2020.
//
// A concrete class derived from DrawText that uses SVG
// to draw text onto a picture.
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <GraphMol/MolDraw2D/DrawTextSVG.h>
#include <GraphMol/MolDraw2D/MolDraw2DSVG.h>
#include <GraphMol/MolDraw2D/MolDraw2DDetails.h>
namespace RDKit {
std::string DrawColourToSVG(const RDKit::DrawColour &col);
// ****************************************************************************
DrawTextSVG::DrawTextSVG(double max_fnt_sz, double min_fnt_sz,
std::ostream &oss, std::string &d_act_class) :
DrawText(max_fnt_sz, min_fnt_sz), oss_(oss), d_active_class_(d_act_class) {
}
namespace {
void escape_xhtml(std::string &data) {
boost::algorithm::replace_all(data, "&", "&amp;");
boost::algorithm::replace_all(data, "\"", "&quot;");
boost::algorithm::replace_all(data, "\'", "&apos;");
boost::algorithm::replace_all(data, "<", "&lt;");
boost::algorithm::replace_all(data, ">", "&gt;");
}
} // namespace
// ****************************************************************************
// draw the char, with the bottom left hand corner at cds
void DrawTextSVG::drawChar(char c, const Point2D &cds) {
unsigned int fontSz = fontSize();
std::string col = DrawColourToSVG(colour());
oss_ << "<text";
oss_ << " x='" << cds.x;
oss_ << "' y='" << cds.y << "'";
if (!d_active_class_.empty()) {
oss_ << " class='" << d_active_class_ << "'";
}
std::string cs;
cs += c;
escape_xhtml(cs);
oss_ << " style='font-size:" << fontSz
<< "px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;"
"font-family:sans-serif;text-anchor:start;"
<< "fill:" << col << "'";
oss_ << " >";
oss_ << cs;
oss_ << "</text>" << std::endl;
}
// ****************************************************************************
void DrawTextSVG::getStringRects(const std::string &text,
std::vector<std::shared_ptr<StringRect> > &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars) const {
double running_x = 0.0;
double act_font_size = fontSize();
double char_height;
double max_width = 0.0;
TextDrawType draw_mode = TextDrawType::TextDrawNormal;
for (size_t i = 0; i < text.length(); ++i) {
// setStringDrawMode moves i along to the end of any <sub> or <sup>
// markup
if ('<' == text[i] && setStringDrawMode(text, draw_mode, i)) {
continue;
}
draw_modes.push_back(draw_mode);
draw_chars.push_back(text[i]);
max_width = std::max(max_width,
static_cast<double>(MolDraw2D_detail::char_widths[(int)text[i]]));
}
for (size_t i = 0; i < draw_chars.size(); ++i) {
double char_width =
0.6 * act_font_size *
static_cast<double>(MolDraw2D_detail::char_widths[(int)draw_chars[i]]) / max_width;
// Absent a proper set of font metrics (we don't know what font we'll be
// using, for starters) this is something of an empirical bodge.
if(draw_chars[i] == '+') {
char_height = 0.6 * act_font_size;
} else if(draw_chars[i] == '-') {
char_height = 0.4 * act_font_size;
} else {
char_height = 0.8 * act_font_size;
}
double cscale = selectScaleFactor(draw_chars[i], draw_modes[i]);
char_height *= cscale;
char_width *= cscale;
Point2D offset(char_width / 2, char_height / 2);
if(draw_chars[i] == '+' || draw_chars[i] == '-') {
offset.y /= 2.0;
}
Point2D g_centre(char_width / 2, char_height / 2);
rects.push_back(std::shared_ptr<StringRect>(new StringRect(offset, g_centre, char_width, char_height)));
rects.back()->trans_.x += running_x;
// empirical spacing.
if(draw_modes[i] != TextDrawType::TextDrawNormal) {
running_x += char_width * 1.05;
} else {
running_x += char_width * 1.15;
}
}
for(auto r: rects) {
r->g_centre_.y = act_font_size - r->g_centre_.y;
r->offset_.y = act_font_size / 2.0;
}
adjustStringRectsForSuperSubScript(draw_modes, rects);
}
} // namespace RDKit

View File

@@ -0,0 +1,48 @@
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx) on 29/04/2020.
//
// A concrete class derived from DrawText that uses SVG
// to draw text onto a picture.
#ifndef RDKIT_DRAWTEXTSVG_H
#define RDKIT_DRAWTEXTSVG_H
#include <iosfwd>
#include <GraphMol/MolDraw2D/DrawText.h>
namespace RDKit {
// ****************************************************************************
class DrawTextSVG : public DrawText {
public:
DrawTextSVG(double max_fnt_sz, double min_fnt_sz, std::ostream &oss,
std::string &d_act_class);
void drawChar(char c, const Point2D &cds) override;
private:
std::ostream &oss_;
std::string &d_active_class_;
// return a vector of StringRects, one for each char in text, with
// super- and subscripts taken into account. Sizes in pixel coords,
// i.e. scaled by fontScale().
void getStringRects(const std::string &text,
std::vector<std::shared_ptr<StringRect>> &rects,
std::vector<TextDrawType> &draw_modes,
std::vector<char> &draw_chars) const override;
};
} // namespace RDKit
#endif // RDKIT_DRAWTEXTSVG_H

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,10 @@ using RDGeom::Point2D;
namespace RDKit {
class DrawText;
enum class TextAlignType:unsigned char;
enum class OrientType:unsigned char;
struct DrawColour {
double r = 0.0, g = 0.0, b = 0.0, a = 1.0;
DrawColour() = default;
@@ -63,26 +67,67 @@ struct DrawColour {
// for holding dimensions of the rectangle round a string.
struct StringRect {
Point2D centre_;
double width_{0.0};
double height_{0.0};
int clash_score_{0}; // rough measure of how badly it clashed with other
// things lower is better, 0 is no clash.
StringRect() : centre_(0.0, 0.0) {}
StringRect(const Point2D &in_cds)
: centre_(in_cds), width_(0.0), height_(0.0), clash_score_(0) {}
// tl is top, left; br is bottom, right
void calcCorners(Point2D &tl, Point2D &tr, Point2D &br, Point2D &bl) const {
double wb2 = width_ / 2.0;
double hb2 = height_ / 2.0;
tl = Point2D(centre_.x - wb2, centre_.y + hb2);
tr = Point2D(centre_.x + wb2, centre_.y + hb2);
br = Point2D(centre_.x + wb2, centre_.y - hb2);
bl = Point2D(centre_.x - wb2, centre_.y - hb2);
Point2D trans_; // Where to draw char relative to other chars in string
Point2D offset_; // offset for draw coords so char is centred correctly
Point2D g_centre_; // glyph centre relative to the origin of the char.
double y_shift_; // shift the whole thing in y by this. For multi-line text.
double width_, height_; // of the glyph itself, not the character cell
double rect_corr_; // because if we move a char one way, we need to move the rectangle the other.
int clash_score_; // rough measure of how badly it clashed with other things
// lower is better, 0 is no clash.
StringRect()
: trans_(0.0, 0.0), offset_(0.0, 0.0), g_centre_(offset_),
y_shift_(0.0), width_(0.0),
height_(0.0), rect_corr_(0.0), clash_score_(0) {}
StringRect(const Point2D &offset, const Point2D &g_centre, double w, double h)
: trans_(0.0, 0.0), offset_(offset), g_centre_(g_centre),
y_shift_(0.0), width_(w),
height_(h), rect_corr_(0.0), clash_score_(0) {}
// tl is top, left; br is bottom, right of the glyph, relative to the
// centre. Padding in draw coords.
void calcCorners(Point2D &tl, Point2D &tr, Point2D &br, Point2D &bl,
double padding) const {
double wb2 = padding + width_ / 2.0;
double hb2 = padding + height_ / 2.0;
Point2D c = trans_ + g_centre_ - offset_;
c.y -= y_shift_;
tl = Point2D(c.x - wb2, c.y - hb2);
tr = Point2D(c.x + wb2, c.y - hb2);
br = Point2D(c.x + wb2, c.y + hb2);
bl = Point2D(c.x - wb2, c.y + hb2);
}
bool doesItIntersect(const StringRect &other) const {
if (fabs(centre_.x - other.centre_.x) < (width_ + other.width_) / 2.0 &&
fabs(centre_.y - other.centre_.y) < (height_ + other.height_) / 2.0) {
Point2D ttl, ttr, tbr, tbl;
calcCorners(ttl, ttr, tbr, tbl, 0.0);
// is +ve y up or down?
if(ttl.y < tbl.y) {
std::swap(ttl, tbl);
std::swap(ttr, tbr);
}
Point2D otl, otr, obr, obl;
other.calcCorners(otl, otr, obr, obl, 0.0);
if(otl.y < obl.y) {
std::swap(otl, obl);
std::swap(otr, obr);
}
if((otl.x >= ttl.x && otl.x <= ttr.x
&& otl.y >= tbl.y && otl.y <= ttl.y)
|| (otr.x >= ttl.x && otr.x <= ttr.x
&& otr.y >= tbl.y && otr.y <= ttl.y)
|| (obr.x >= ttl.x && obr.x <= ttr.x
&& obr.y >= tbl.y && obr.y <= ttl.y)
|| (obl.x >= ttl.x && obl.x <= ttr.x
&& obl.y >= tbl.y && obl.y <= ttl.y)) {
return true;
}
if((ttl.x >= otl.x && ttl.x <= otr.x
&& ttl.y >= obl.y && ttl.y <= otl.y)
|| (ttr.x >= otl.x && ttr.x <= otr.x
&& ttr.y >= obl.y && ttr.y <= otl.y)
|| (tbr.x >= otl.x && tbr.x <= otr.x
&& tbr.y >= obl.y && tbr.y <= otl.y)
|| (tbl.x >= otl.x && tbl.x <= otr.x
&& tbl.y >= obl.y && tbl.y <= otl.y)) {
return true;
}
return false;
@@ -133,12 +178,15 @@ struct RDKIT_MOLDRAW2D_EXPORT MolDrawOptions {
// drawing a molecule
DrawColour backgroundColour{
1, 1, 1}; // color to be used while clearing the background
int legendFontSize = 12; // font size (in pixels) to be used for the legend
int legendFontSize = 16; // font size (in pixels) to be used for the legend
// (if present)
int maxFontSize = 40; // maximum size in pixels for font in drawn molecule.
// -1 means no max.
double annotationFontScale = 0.75; // scales font relative to atom labels for
int minFontSize = 12; // likewise for -1.
double annotationFontScale = 0.5; // scales font relative to atom labels for
// atom and bond annotation.
std::string fontFile = ""; // name of font for freetype rendering. If given,
// over-rides default
DrawColour legendColour{0, 0,
0}; // color to be used for the legend (if present)
double multipleBondOffset = 0.15; // offset (in Angstrom) for the extra lines
@@ -188,6 +236,7 @@ struct RDKIT_MOLDRAW2D_EXPORT MolDrawOptions {
// ellipses round longer labels.
bool centreMoleculesBeforeDrawing = false; // moves the centre of the drawn
// molecule to (0,0)
bool explicitMethyl = false; // draw terminal methyl and related as CH3
MolDrawOptions() {
highlightColourPalette.emplace_back(
@@ -213,14 +262,6 @@ struct RDKIT_MOLDRAW2D_EXPORT MolDrawOptions {
//! MolDraw2D is the base class for doing 2D renderings of molecules
class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
public:
enum class OrientType : unsigned char { C = 0, N, E, S, W };
// for aligning the drawing of text to the passed in coords.
typedef enum { START, MIDDLE, END } AlignType;
typedef enum {
TextDrawNormal = 0,
TextDrawSuperscript,
TextDrawSubscript
} TextDrawType;
//! constructor for a particular size
/*!
@@ -233,23 +274,8 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
sizes of the panels individual molecules are drawn in when
\c drawMolecules() is called.
*/
MolDraw2D(int width, int height, int panelWidth, int panelHeight)
: needs_scale_(true),
width_(width),
height_(height),
panel_width_(panelWidth > 0 ? panelWidth : width),
panel_height_(panelHeight > 0 ? panelHeight : height),
scale_(1.0),
x_trans_(0.0),
y_trans_(0.0),
x_offset_(0),
y_offset_(0),
font_size_(0.5),
curr_width_(2),
fill_polys_(true),
activeMolIdx_(-1) {}
virtual ~MolDraw2D() {}
MolDraw2D(int width, int height, int panelWidth, int panelHeight);
virtual ~MolDraw2D();
//! draw a single molecule
/*!
@@ -407,13 +433,14 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
virtual int panelWidth() const { return panel_width_; }
//! return the height of the drawing panels.
virtual int panelHeight() const { return panel_height_; }
virtual int drawHeight() const { return panel_height_ - legend_height_;}
//! returns the drawing scale (conversion from molecular coords -> drawing
// coords)
double scale() const { return scale_; }
//! calculates the drawing scale (conversion from molecular coords -> drawing
// coords)
void calculateScale(int width, int height,
void calculateScale(int width, int height, const ROMol &mol,
const std::vector<int> *highlight_atoms = nullptr,
const std::map<int, double> *highlight_radii = nullptr);
//! overload
@@ -429,7 +456,7 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
//! explicitly sets the scaling factors for the drawing
void setScale(int width, int height, const Point2D &minv,
const Point2D &maxv);
const Point2D &maxv, const ROMol *mol=nullptr);
//! sets the drawing offset (in drawing coords)
void setOffset(int x, int y) {
x_offset_ = x;
@@ -443,12 +470,8 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
//! returns the width and height of the grid (in molecular coords)
Point2D range() const { return Point2D(x_range_, y_range_); }
//! returns the font size (in molecule units)
virtual double fontSize() const { return font_size_; }
double drawFontSize() const;
//! set font size in molecule coordinate units. That's probably Angstrom for
//! RDKit.
//! font size in drawing coordinate units. That's probably pixels.
virtual double fontSize() const;
virtual void setFontSize(double new_size);
//! sets the current draw color
@@ -465,15 +488,10 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
//! returns the current line width
virtual int lineWidth() const { return curr_width_; }
//! establishes whether to put string draw mode into super- or sub-script
//! mode based on contents of instring from i onwards. Increments i
//! appropriately
//! \returns true or false depending on whether it did something or not
bool setStringDrawMode(const std::string &instring, TextDrawType &draw_mode,
int &i) const;
//! clears the contents of the drawing
virtual void clearDrawing() = 0;
//! draws a line from \c cds1 to \c cds2 using the current drawing style
// in atom coords.
virtual void drawLine(const Point2D &cds1, const Point2D &cds2) = 0;
//! using the current scale, work out the size of the label in molecule
@@ -484,30 +502,22 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
accounted for in the width and height.
*/
virtual void getStringSize(const std::string &label, double &label_width,
double &label_height) const = 0;
double &label_height) const;
// get the overall size of the label, allowing for it being split
// into pieces according to orientation.
void getLabelSize(const std::string &label, OrientType orient,
double &label_width, double &label_height) const;
// return extremes for string in molecule coords.
void getStringExtremes(const std::string &label, OrientType orient,
const Point2D &cds, double &x_min, double &y_min,
double &x_max, double &y_max) const;
//! drawString centres the string on cds.
virtual void drawString(const std::string &str, const Point2D &cds);
// unless the specific drawer over-rides this overload, it will just call
// the first one. SVG for one needs the alignment flag.
virtual void drawString(const std::string &str, const Point2D &cds,
AlignType align);
// draw the vector of strings from cds putting the nth+1 at the end of
// the nth. Aligns them according to OrientType.
virtual void drawStrings(const std::vector<std::string> &labels,
const Point2D &cds, OrientType orient);
// calculate where to put the centre of the str so that the first/last
// character, which might have <sub> or <sup> labels, is at in_cds.
// Normally, the whole string would be centred on in_cds.
// If align is 0, it's left aligned, 1 it's right aligned, anything
// else and it's not done at all.
virtual void alignString(const std::string &str,
const std::string &align_char, int align,
const Point2D &in_cds, Point2D &out_cds) const;
TextAlignType align);
//! draw a polygon. Note that if fillPolys() returns false, it
//! doesn't close the path. If you want it to in that case, you
//! do it explicitly yourself.
@@ -572,16 +582,17 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
virtual bool supportsAnnotations() { return true; }
protected:
std::unique_ptr<DrawText> text_drawer_;
private:
bool needs_scale_;
int width_, height_, panel_width_, panel_height_;
int width_, height_, panel_width_, panel_height_, legend_height_;
double scale_;
double x_min_, y_min_, x_range_, y_range_;
double x_trans_, y_trans_;
int x_offset_, y_offset_; // translation in screen coordinates
// font_size_ in molecule coordinate units. Default 0.5 (a bit bigger
// than the default width of a double bond)
double font_size_;
int curr_width_;
bool fill_polys_;
int activeMolIdx_;
@@ -593,13 +604,19 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
std::vector<std::vector<Point2D>> at_cds_; // from mol
std::vector<std::vector<int>> atomic_nums_;
std::vector<std::vector<std::pair<std::string, OrientType>>> atom_syms_;
// by the time atom_notes_ and bonds_notes_ are drawn, we're only ever
// using the trans_ member of the StringRect, but it is convenient to
// keep the whole thing rather than just a StringPos for the position
// for calculating the scale of the drawing. Went a long way down
// the rabbit hole before realising this, hence this note.
std::vector<std::vector<std::shared_ptr<StringRect>>> atom_notes_;
std::vector<std::vector<std::shared_ptr<StringRect>>> bond_notes_;
std::vector<std::vector<std::pair<std::shared_ptr<StringRect>, OrientType>>> radicals_;
Point2D bbox_[2];
// draw the char, with the bottom left hand corner at cds
virtual void drawChar(char c, const Point2D &cds) = 0;
virtual void initDrawing() = 0;
virtual void initTextDrawer(bool noFreetype) = 0;
// return a DrawColour based on the contents of highlight_atoms or
// highlight_map, falling back to atomic number by default
@@ -680,7 +697,9 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
void extractAtomSymbols(const ROMol &mol);
void extractAtomNotes(const ROMol &mol);
void extractBondNotes(const ROMol &mol);
void extractRadicals(const ROMol &mol);
// coords in atom coords
virtual void drawLine(const Point2D &cds1, const Point2D &cds2,
const DrawColour &col1, const DrawColour &col2);
void drawWedgedBond(const Point2D &cds1, const Point2D &cds2,
@@ -692,9 +711,8 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
void drawAtomLabel(int atom_num,
const std::vector<int> *highlight_atoms = nullptr,
const std::map<int, DrawColour> *highlight_map = nullptr);
void drawAtomLabel(int atom_num, const DrawColour &draw_colour);
virtual void drawAnnotation(const std::string &note,
const std::shared_ptr<StringRect> &note_rect);
OrientType calcRadicalRect(const ROMol &mol, const Atom *atom,
StringRect &rad_rect);
void drawRadicals(const ROMol &mol);
// find a good starting point for scanning round the annotation
// atom. If we choose well, the first angle should be the one.
@@ -703,25 +721,27 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
// see if the note will clash with anything else drawn on the molecule.
// note_vec should have unit length. note_rad is the radius along
// note_vec that the note will be drawn.
bool doesAtomNoteClash(StringRect &note_rect, const StringRect &atsym_rect,
const ROMol &mol, unsigned int atom_idx);
bool doesBondNoteClash(StringRect &note_rect, const ROMol &mol,
bool doesAtomNoteClash(StringRect &note_rect,
const std::vector<std::shared_ptr<StringRect>> &rects,
const ROMol &mol,
unsigned int atom_idx);
bool doesBondNoteClash(StringRect &note_rect,
const std::vector<std::shared_ptr<StringRect>> &rects,
const ROMol &mol,
const Bond *bond);
// does the note_vec form an unacceptably acute angle with one of the
// bonds from atom to its neighbours.
bool doesNoteClashNbourBonds(const StringRect &note_rect, const ROMol &mol,
const Atom *atom) const;
bool doesNoteClashNbourBonds(const StringRect &note_rect,
const std::vector<std::shared_ptr<StringRect>> &rects,
const ROMol &mol, const Atom *atom) const;
// does the note intersect with atsym, and if not, any other atom symbol.
bool doesNoteClashAtomLabels(const StringRect &note_rect,
const StringRect &atsym_rect, const ROMol &mol,
const std::vector<std::shared_ptr<StringRect>> &rects,
const ROMol &mol,
unsigned int atom_idx) const;
bool doesNoteClashOtherNotes(const StringRect &note_rect) const;
// take the label for the given atom and return the individual pieces
// that need to be drawn for it. So NH<sub>2</sub> will return
// "N", "H<sub>2</sub>".
std::vector<std::string> atomLabelToPieces(int atom_num) const;
std::vector<std::string> atomLabelToPieces(const std::string &label,
OrientType orient) const;
bool doesNoteClashOtherNotes(const StringRect &note_rect,
const std::vector<std::shared_ptr<StringRect>> &rects) const;
// cds1 and cds2 are 2 atoms in a ring. Returns the perpendicular pointing
// into the ring.
Point2D bondInsideRing(const ROMol &mol, const Bond *bond,
@@ -743,13 +763,14 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
// adds LaTeX-like annotation for super- and sub-script.
std::pair<std::string, OrientType> getAtomSymbolAndOrientation(
const Atom &atom, const ROMol &mol) const;
const Atom &atom) const;
std::string getAtomSymbol(const Atom &atom) const;
OrientType getAtomOrientation(const Atom &atom, const Point2D &nbr_sum) const;
OrientType getAtomOrientation(const Atom &atom) const;
// things used by calculateScale.
void adjustScaleForAtomLabels(const std::vector<int> *highlight_atoms,
const std::map<int, double> *highlight_radii);
void adjustScaleForRadicals(const ROMol &mol);
void adjustScaleForAnnotation(
const std::vector<std::shared_ptr<StringRect>> &notes);
@@ -773,6 +794,9 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
const std::map<int, DrawColour> *highlight_bond_map = nullptr,
const std::vector<std::pair<DrawColour, DrawColour>> *bond_colours =
nullptr);
virtual void drawAtomLabel(int atom_num, const DrawColour &draw_colour);
virtual void drawAnnotation(const std::string &note,
const std::shared_ptr<StringRect> &note_rect);
// calculate normalised perpendicular to vector between two coords
Point2D calcPerpendicular(const Point2D &cds1, const Point2D &cds2) const;
@@ -794,7 +818,7 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2D {
Point2D &l2f) const;
// calculate the width to draw a line in draw coords.
virtual unsigned int getDrawLineWidth();
virtual unsigned int getDrawLineWidth() const;
// sort out coords and scale for drawing reactions.
void get2DCoordsForReaction(ChemicalReaction &rxn, Point2D &arrowBegin,
@@ -817,9 +841,8 @@ RDKIT_MOLDRAW2D_EXPORT bool doLinesIntersect(const Point2D &l1s,
// rectangle of the string.
RDKIT_MOLDRAW2D_EXPORT bool doesLineIntersectLabel(const Point2D &ls,
const Point2D &lf,
const StringRect &lab_rect);
std::ostream &operator<<(std::ostream &oss, const MolDraw2D::OrientType &o);
const StringRect &lab_rect,
double padding=0.0);
} // namespace RDKit

View File

@@ -10,16 +10,44 @@
// derived from Dave Cosgrove's MolDraw2D
//
#include "MolDraw2DCairo.h"
#include <cairo.h>
#include <GraphMol/MolDraw2D/MolDraw2DCairo.h>
#ifdef RDK_BUILD_FREETYPE_SUPPORT
#include <GraphMol/MolDraw2D/DrawTextFTCairo.h>
#else
#include <GraphMol/MolDraw2D/DrawTextCairo.h>
#endif
namespace RDKit {
void MolDraw2DCairo::initDrawing() {
PRECONDITION(dp_cr, "no draw context");
cairo_select_font_face(dp_cr, "sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(dp_cr, fontSize());
cairo_set_line_cap(dp_cr, CAIRO_LINE_CAP_BUTT);
// drawOptions().backgroundColour = DrawColour(0.9, 0.9, 0.0);
}
void MolDraw2DCairo::initTextDrawer(bool noFreetype) {
double max_fnt_sz = drawOptions().maxFontSize;
double min_fnt_sz = drawOptions().minFontSize;
if(noFreetype) {
text_drawer_.reset(new DrawTextCairo(max_fnt_sz, min_fnt_sz, dp_cr));
} else {
#ifdef RDK_BUILD_FREETYPE_SUPPORT
try {
text_drawer_.reset(new DrawTextFTCairo(max_fnt_sz, min_fnt_sz,
drawOptions().fontFile, dp_cr));
} catch (std::runtime_error &e) {
BOOST_LOG(rdWarningLog) << e.what() << std::endl
<< "Falling back to native Cairo text handling."
<< std::endl;
text_drawer_.reset(new DrawTextCairo(max_fnt_sz, min_fnt_sz, dp_cr));
}
#else
text_drawer_.reset(new DrawTextCairo(max_fnt_sz, min_fnt_sz, dp_cr));
#endif
}
}
// ****************************************************************************
@@ -39,7 +67,6 @@ void MolDraw2DCairo::drawLine(const Point2D &cds1, const Point2D &cds2) {
Point2D c2 = getDrawCoords(cds2);
unsigned int width = getDrawLineWidth();
std::string dashString = "";
cairo_set_line_width(dp_cr, width);
@@ -96,23 +123,6 @@ void MolDraw2DCairo::drawWavyLine(const Point2D &cds1, const Point2D &cds2,
cairo_stroke(dp_cr);
}
// ****************************************************************************
// draw the char, with the bottom left hand corner at cds
void MolDraw2DCairo::drawChar(char c, const Point2D &cds) {
PRECONDITION(dp_cr, "no draw context");
char txt[2];
txt[0] = c;
txt[1] = 0;
cairo_set_font_size(dp_cr, drawFontSize());
Point2D c1 = cds;
cairo_move_to(dp_cr, c1.x, c1.y);
cairo_show_text(dp_cr, txt);
cairo_stroke(dp_cr);
// set the font size back to molecule units so getStringSize
// still works properly.
cairo_set_font_size(dp_cr, fontSize());
}
// ****************************************************************************
void MolDraw2DCairo::drawPolygon(const std::vector<Point2D> &cds) {
PRECONDITION(dp_cr, "no draw context");
@@ -154,60 +164,6 @@ void MolDraw2DCairo::clearDrawing() {
cairo_fill(dp_cr);
}
// ****************************************************************************
// using the current scale, work out the size of the label in molecule
// coordinates
void MolDraw2DCairo::getStringSize(const std::string &label,
double &label_width,
double &label_height) const {
PRECONDITION(dp_cr, "no draw context");
label_width = 0.0;
label_height = 0.0;
TextDrawType draw_mode = TextDrawNormal;
// we have seen different behaviour on different OSes if the font is sized to
// drawFontSize() / scale() which is what we really want. Adjust at the end.
cairo_set_font_size(dp_cr, drawFontSize());
bool had_a_super = false;
char txt[2];
txt[1] = 0;
for (int i = 0, is = label.length(); i < is; ++i) {
// setStringDrawMode moves i along to the end of any <sub> or <sup>
// markup
if ('<' == label[i] && setStringDrawMode(label, draw_mode, i)) {
continue;
}
txt[0] = label[i];
cairo_text_extents_t extents;
cairo_text_extents(dp_cr, txt, &extents);
double twidth = extents.x_advance, theight = extents.height;
label_height = std::max(label_height, theight);
double char_width = twidth;
if (TextDrawSubscript == draw_mode) {
char_width *= 0.75;
} else if (TextDrawSuperscript == draw_mode) {
char_width *= 0.75;
had_a_super = true;
}
label_width += char_width;
}
// convert back to molecule coords.
label_width /= scale();
label_height /= scale();
// subscript keeps its bottom in line with the bottom of the bit chars,
// superscript goes above the original char top by a quarter
if (had_a_super) {
label_height *= 1.25;
}
label_height *= 1.2; // empirical
}
// ****************************************************************************
namespace {
cairo_status_t grab_str(void *closure, const unsigned char *data,

View File

@@ -16,9 +16,11 @@
#ifndef MOLDRAW2DCAIRO_H
#define MOLDRAW2DCAIRO_H
#include <GraphMol/MolDraw2D/MolDraw2D.h>
#include <cairo.h>
#include <GraphMol/MolDraw2D/DrawTextCairo.h>
#include <GraphMol/MolDraw2D/MolDraw2D.h>
// ****************************************************************************
namespace RDKit {
@@ -27,19 +29,22 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DCairo : public MolDraw2D {
public:
// does not take ownership of the drawing context
MolDraw2DCairo(int width, int height, cairo_t *cr, int panelWidth = -1,
int panelHeight = -1)
int panelHeight = -1, bool noFreetype=false)
: MolDraw2D(width, height, panelWidth, panelHeight), dp_cr(cr) {
cairo_reference(dp_cr);
initDrawing();
initTextDrawer(noFreetype);
};
MolDraw2DCairo(int width, int height, int panelWidth = -1,
int panelHeight = -1)
MolDraw2DCairo(int width, int height,
int panelWidth = -1, int panelHeight = -1,
bool noFreetype=false)
: MolDraw2D(width, height, panelWidth, panelHeight) {
cairo_surface_t *surf =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
dp_cr = cairo_create(surf);
cairo_surface_destroy(surf); // dp_cr has a reference to this now;
initDrawing();
initTextDrawer(noFreetype);
};
~MolDraw2DCairo() {
if (dp_cr) {
@@ -58,7 +63,6 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DCairo : public MolDraw2D {
void finishDrawing();
void drawLine(const Point2D &cds1, const Point2D &cds2) override;
void drawChar(char c, const Point2D &cds) override;
// void drawString( const std::string &str, const Point2D &cds );
void drawPolygon(const std::vector<Point2D> &cds) override;
void clearDrawing() override;
@@ -67,17 +71,12 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DCairo : public MolDraw2D {
const DrawColour &col1, const DrawColour &col2,
unsigned int nSegments = 16, double vertOffset = 0.05) override;
// using the current scale, work out the size of the label in molecule
// coordinates
void getStringSize(const std::string &label, double &label_width,
double &label_height) const override;
// returns the PNG data in a string
std::string getDrawingText() const;
// writes the PNG data to a file
void writeDrawingText(const std::string &fName) const;
#ifdef WIN32
#if defined(WIN32) && !defined(RDK_BUILD_FREETYPE_SUPPORT)
bool supportsAnnotations() override {
return false;
}
@@ -87,7 +86,9 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DCairo : public MolDraw2D {
private:
cairo_t *dp_cr;
void initDrawing();
void initDrawing() override;
void initTextDrawer(bool noFreetype) override;
};
} // namespace RDKit
#endif // MOLDRAW2DCAIRO_H

View File

@@ -10,17 +10,20 @@
// derived from Dave Cosgrove's MolDraw2D
//
#include "MolDraw2DSVG.h"
#include <GraphMol/MolDraw2D/MolDraw2DDetails.h>
#include <GraphMol/MolDraw2D/MolDraw2DSVG.h>
#include <GraphMol/SmilesParse/SmilesWrite.h>
#include <Geometry/point.h>
#ifdef RDK_BUILD_FREETYPE_SUPPORT
#include <GraphMol/MolDraw2D/DrawTextFTSVG.h>
#else
#include <GraphMol/MolDraw2D/DrawTextSVG.h>
#endif
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <sstream>
namespace RDKit {
namespace {
std::string DrawColourToSVG(const DrawColour &col) {
const char *convert = "0123456789ABCDEF";
std::string res(7, ' ');
@@ -50,8 +53,8 @@ std::string DrawColourToSVG(const DrawColour &col) {
res[i++] = convert[v % 16];
return res;
}
} // namespace
// ****************************************************************************
void MolDraw2DSVG::initDrawing() {
d_os << "<?xml version='1.0' encoding='iso-8859-1'?>\n";
d_os << "<svg version='1.1' baseProfile='full'\n \
@@ -67,6 +70,35 @@ void MolDraw2DSVG::initDrawing() {
// scale(.85,.85)'>";
}
// ****************************************************************************
void MolDraw2DSVG::initTextDrawer(bool noFreetype) {
double max_fnt_sz = drawOptions().maxFontSize;
double min_fnt_sz = drawOptions().minFontSize;
if(noFreetype) {
text_drawer_.reset(new DrawTextSVG(max_fnt_sz, min_fnt_sz,
d_os, d_activeClass));
} else {
#ifdef RDK_BUILD_FREETYPE_SUPPORT
try {
text_drawer_.reset(new DrawTextFTSVG(
max_fnt_sz, min_fnt_sz, drawOptions().fontFile, d_os, d_activeClass));
} catch (std::runtime_error &e) {
BOOST_LOG(rdWarningLog) << e.what() << std::endl
<< "Falling back to native SVG text handling."
<< std::endl;
text_drawer_.reset(
new DrawTextSVG(max_fnt_sz, min_fnt_sz, d_os, d_activeClass));
}
#else
text_drawer_.reset(
new DrawTextSVG(max_fnt_sz, min_fnt_sz, d_os, d_activeClass));
#endif
}
}
// ****************************************************************************
void MolDraw2DSVG::finishDrawing() {
// d_os << "</g>";
@@ -121,6 +153,7 @@ void MolDraw2DSVG::drawWavyLine(const Point2D &cds1, const Point2D &cds2,
d_os << " />\n";
}
// ****************************************************************************
void MolDraw2DSVG::drawBond(
const ROMol &mol, const Bond *bond, int at1_idx, int at2_idx,
const std::vector<int> *highlight_atoms,
@@ -140,6 +173,33 @@ void MolDraw2DSVG::drawBond(
d_activeClass = o_class;
};
// ****************************************************************************
void MolDraw2DSVG::drawAtomLabel(int atom_num, const DrawColour &draw_colour) {
std::string o_class = d_activeClass;
if (!d_activeClass.empty()) {
d_activeClass += " ";
}
d_activeClass += boost::str(boost::format("atom-%d") % atom_num);
MolDraw2D::drawAtomLabel(atom_num, draw_colour);
d_activeClass = o_class;
}
// ****************************************************************************
void MolDraw2DSVG::drawAnnotation(const std::string &note,
const std::shared_ptr<StringRect> &note_rect) {
std::string o_class = d_activeClass;
if (!d_activeClass.empty()) {
d_activeClass += " ";
}
d_activeClass += "note";
MolDraw2D::drawAnnotation(note, note_rect);
d_activeClass = o_class;
}
// ****************************************************************************
void MolDraw2DSVG::drawLine(const Point2D &cds1, const Point2D &cds2) {
Point2D c1 = getDrawCoords(cds1);
@@ -169,24 +229,6 @@ void MolDraw2DSVG::drawLine(const Point2D &cds1, const Point2D &cds2) {
d_os << " />\n";
}
// ****************************************************************************
// draw the char, with the bottom left hand corner at cds
void MolDraw2DSVG::drawChar(char c, const Point2D &cds) {
unsigned int fontSz = drawFontSize();
std::string col = DrawColourToSVG(colour());
d_os << "<text";
d_os << " x='" << cds.x;
d_os << "' y='" << cds.y << "'";
d_os << " style='font-size:" << fontSz
<< "px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;"
"font-family:sans-serif;text-anchor:start;"
<< "fill:" << col << "'";
d_os << " >";
d_os << c;
d_os << "</text>";
}
// ****************************************************************************
void MolDraw2DSVG::drawPolygon(const std::vector<Point2D> &cds) {
PRECONDITION(cds.size() >= 3, "must have at least three points");
@@ -266,205 +308,6 @@ void MolDraw2DSVG::clearDrawing() {
d_os << "> </rect>\n";
}
// ****************************************************************************
// using the current scale, work out the size of the label in molecule
// coordinates
void MolDraw2DSVG::getStringSize(const std::string &label, double &label_width,
double &label_height) const {
label_width = 0.0;
label_height = 0.0;
TextDrawType draw_mode = TextDrawNormal;
bool had_a_super = false;
bool had_a_sub = false;
double act_font_size = drawFontSize() / scale();
for (int i = 0, is = label.length(); i < is; ++i) {
// setStringDrawMode moves i along to the end of any <sub> or <sup>
// markup
if ('<' == label[i] && setStringDrawMode(label, draw_mode, i)) {
continue;
}
label_height = act_font_size;
double char_width =
act_font_size *
static_cast<double>(MolDraw2D_detail::char_widths[(int)label[i]]) /
MolDraw2D_detail::char_widths[(int)'M'];
if (TextDrawSubscript == draw_mode) {
char_width *= 0.5;
had_a_sub = true;
} else if (TextDrawSuperscript == draw_mode) {
char_width *= 0.5;
had_a_super = true;
}
label_width += char_width;
}
// subscript keeps its bottom in line with the bottom of the bit chars,
// superscript goes above the original char top by a bit (empirical)
if (had_a_super) {
label_height *= 1.1;
}
if (had_a_sub) {
label_height *= 1.1;
}
}
namespace {
void escape_xhtml(std::string &data) {
boost::algorithm::replace_all(data, "&", "&amp;");
boost::algorithm::replace_all(data, "\"", "&quot;");
boost::algorithm::replace_all(data, "\'", "&apos;");
boost::algorithm::replace_all(data, "<", "&lt;");
boost::algorithm::replace_all(data, ">", "&gt;");
}
} // namespace
// ****************************************************************************
void MolDraw2DSVG::drawString(const std::string &str, const Point2D &cds) {
drawString(str, cds, MIDDLE);
}
// ****************************************************************************
// draws the string aligned as requested.
void MolDraw2DSVG::drawString(const std::string &str, const Point2D &cds,
AlignType align) {
unsigned int fontSz = drawFontSize();
double string_width, string_height;
getStringSize(str, string_width, string_height);
double draw_x = cds.x;
double draw_y = cds.y;
// for debugging text output
#if 0
draw_x = cds.x - string_width / 2.0;
draw_y = cds.y - string_height / 2.0;
DrawColour tcolour =colour();
setColour(DrawColour(.8,.8,.8));
std::vector<Point2D> poly;
poly.push_back(Point2D(draw_x,draw_y));
poly.push_back(Point2D(draw_x+string_width,draw_y));
poly.push_back(Point2D(draw_x+string_width,draw_y+string_height));
poly.push_back(Point2D(draw_x,draw_y+string_height));
drawPolygon(poly);
setColour(tcolour);
draw_x = cds.x;
draw_y = cds.y;
#endif
std::string col = DrawColourToSVG(colour());
std::string text_anchor = "middle";
double tmult = 0.0;
if(align == END) {
text_anchor = "end";
tmult = -1.0;
} else if(align == START) {
text_anchor = "start";
tmult = 1.0;
}
Point2D draw_coords = getDrawCoords(Point2D(draw_x, draw_y));
// fonts are laid out with room for wider letters like W and hanging bits like g.
// Very few atomic symbols need to care about this, and common ones look a bit
// out of line. For example O sits to the left of a double bond. This is an
// empirical tweak to push it back a bit. Use the string_height in x and y
// as it's just a correction factor based on the scaled font size.
draw_coords.x += string_height * tmult * 0.1 * scale();
draw_coords.y += string_height * 0.15 *scale();
d_os << "<text dominant-baseline=\"central\" text-anchor=\""
<< text_anchor << "\"";
d_os << " x='" << draw_coords.x;
d_os << "' y='" << draw_coords.y << "'";
if (!d_activeClass.empty()) {
d_os << " class='" << d_activeClass << "'";
}
d_os << " style='font-size:" << fontSz
<< "px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;"
"font-family:sans-serif;"
<< "fill:" << col << "'";
d_os << " >";
TextDrawType draw_mode =
TextDrawNormal; // 0 for normal, 1 for superscript, 2 for subscript
std::string span;
bool first_span = true;
auto write_span = [&]() {
if (!first_span) {
escape_xhtml(span);
d_os << span << "</tspan>";
span = "";
}
first_span = false;
};
for (int i = 0, is = str.length(); i < is; ++i) {
// setStringDrawMode moves i along to the end of any <sub> or <sup>
// markup
if ('<' == str[i] && setStringDrawMode(str, draw_mode, i)) {
write_span();
d_os << "<tspan";
switch (draw_mode) {
// To save people time later - on macOS Catalina, at least, Firefox
// renders the superscript as a subscript. It's fine on Safari.
case TextDrawSuperscript:
d_os << " style='baseline-shift:super;font-size:" << fontSz * 0.75
<< "px;"
<< "'";
break;
case TextDrawSubscript:
d_os << " style='baseline-shift:sub;font-size:" << fontSz * 0.75
<< "px;"
<< "'";
break;
default:
break;
}
d_os << ">";
continue;
}
if (first_span) {
first_span = false;
d_os << "<tspan>";
span = "";
}
span += str[i];
}
escape_xhtml(span);
d_os << span << "</tspan>";
d_os << "</text>\n";
}
// ****************************************************************************
void MolDraw2DSVG::alignString(const std::string &str, const std::string &align_char,
int align, const Point2D &in_cds,
Point2D &out_cds) const {
RDUNUSED_PARAM(str);
if(align != 0 && align != 1) {
out_cds = in_cds;
return;
}
double ac_width, ac_height;
getStringSize(align_char, ac_width, ac_height);
// align == 0 is left align - first char to go at in_cds.
double dir = align == 0 ? 1.0 : -1.0;
// this works with SVG, so long as we use the correct text anchor -
// W => end, E => start, N, S => middle
out_cds.x = in_cds.x - dir * 0.5 * ac_width;
// and this assumes dominant-baseline="central"
out_cds.y = in_cds.y;
}
// ****************************************************************************
static const char *RDKIT_SVG_VERSION = "0.9";
void MolDraw2DSVG::addMoleculeMetadata(const ROMol &mol, int confId) const {

View File

@@ -18,7 +18,8 @@
#include <iostream>
#include <sstream>
#include "MolDraw2D.h"
#include <GraphMol/MolDraw2D/MolDraw2D.h>
#include <GraphMol/MolDraw2D/DrawTextSVG.h>
// ****************************************************************************
@@ -28,14 +29,18 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DSVG : public MolDraw2D {
public:
// initialize to use a particular ostream
MolDraw2DSVG(int width, int height, std::ostream &os, int panelWidth = -1,
int panelHeight = -1)
int panelHeight = -1, bool noFreetype = false)
: MolDraw2D(width, height, panelWidth, panelHeight), d_os(os) {
initDrawing();
initTextDrawer(noFreetype);
};
// initialize to use the internal stringstream
MolDraw2DSVG(int width, int height, int panelWidth = -1, int panelHeight = -1)
MolDraw2DSVG(int width, int height, int panelWidth = -1,
int panelHeight = -1, bool noFreetype = false)
: MolDraw2D(width, height, panelWidth, panelHeight), d_os(d_ss) {
initDrawing();
initTextDrawer(noFreetype);
};
void setColour(const DrawColour &col) override;
@@ -46,13 +51,6 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DSVG : public MolDraw2D {
void finishDrawing();
void drawLine(const Point2D &cds1, const Point2D &cds2) override;
void drawString(const std::string &str, const Point2D &cds) override;
void drawString(const std::string &str, const Point2D &cds,
AlignType align) override;
void alignString(const std::string &str,
const std::string &align_char, int align,
const Point2D &in_cds, Point2D &out_cds) const override;
void drawPolygon(const std::vector<Point2D> &cds) override;
void drawEllipse(const Point2D &cds1, const Point2D &cds2) override;
void clearDrawing() override;
@@ -62,11 +60,6 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DSVG : public MolDraw2D {
unsigned int nSegments = 16,
double vertOffset = 0.05) override;
// using the current scale, work out the size of the label in molecule
// coordinates
void getStringSize(const std::string &label, double &label_width,
double &label_height) const override;
// this only makes sense if the object was initialized without a stream
std::string getDrawingText() const { return d_ss.str(); };
@@ -88,8 +81,8 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DSVG : public MolDraw2D {
std::stringstream d_ss;
std::string d_activeClass;
void drawChar(char c, const Point2D &cds) override;
void initDrawing();
void initDrawing() override;
void initTextDrawer(bool noFreetype) override;
protected:
void drawBond(
@@ -99,6 +92,11 @@ class RDKIT_MOLDRAW2D_EXPORT MolDraw2DSVG : public MolDraw2D {
const std::vector<int> *highlight_bonds = nullptr,
const std::map<int, DrawColour> *highlight_bond_map = nullptr,
const std::vector<std::pair<DrawColour, DrawColour> > *bond_colours = nullptr) override;
void drawAtomLabel(int atom_num, const DrawColour &draw_colour) override;
void drawAnnotation(const std::string &note,
const std::shared_ptr<StringRect> &note_rect) override;
};
} // namespace RDKit
#endif // MOLDRAW2DSVG_H

View File

@@ -139,7 +139,9 @@ void updateDrawerParamsFromJSON(MolDraw2D &drawer, const std::string &json) {
PT_OPT_GET(clearBackground);
PT_OPT_GET(legendFontSize);
PT_OPT_GET(maxFontSize);
PT_OPT_GET(minFontSize);
PT_OPT_GET(annotationFontScale);
PT_OPT_GET(fontFile);
PT_OPT_GET(multipleBondOffset);
PT_OPT_GET(padding);
PT_OPT_GET(additionalAtomLabelPadding);
@@ -168,7 +170,8 @@ void contourAndDrawGrid(MolDraw2D &drawer, const double *grid,
const std::vector<double> &xcoords,
const std::vector<double> &ycoords, size_t nContours,
std::vector<double> &levels,
const ContourParams &params) {
const ContourParams &params,
const ROMol *mol) {
PRECONDITION(grid, "no data");
PRECONDITION(params.colourMap.size() > 1,
"colourMap must have at least two entries");
@@ -176,7 +179,7 @@ void contourAndDrawGrid(MolDraw2D &drawer, const double *grid,
if (params.setScale) {
Point2D minP = {xcoords[0], ycoords[0]};
Point2D maxP = {xcoords.back(), ycoords.back()};
drawer.setScale(drawer.width(), drawer.height(), minP, maxP);
drawer.setScale(drawer.width(), drawer.height(), minP, maxP, mol);
}
size_t nX = xcoords.size();
@@ -282,7 +285,8 @@ void contourAndDrawGaussians(MolDraw2D &drawer,
const std::vector<double> &weights,
const std::vector<double> &widths,
size_t nContours, std::vector<double> &levels,
const ContourParams &params) {
const ContourParams &params,
const ROMol *mol) {
PRECONDITION(locs.size() == weights.size(), "size mismatch");
PRECONDITION(locs.size() == widths.size(), "size mismatch");
@@ -313,7 +317,7 @@ void contourAndDrawGaussians(MolDraw2D &drawer,
maxP.y += pad;
}
drawer.setScale(drawer.width(), drawer.height(), minP, maxP);
drawer.setScale(drawer.width(), drawer.height(), minP, maxP, mol);
}
size_t nx = (size_t)ceil(drawer.range().x / params.gridResolution) + 1;

View File

@@ -100,7 +100,7 @@ struct ContourParams {
\param nContours: the number of contours to draw
\param levels: the contours to use
\param ps: additional parameters controlling the contouring.
\param mol: molecule to be used to adjust the scale of the drawing.
If the \c levels argument is empty, the contour levels will be determined
automatically from the max and min values on the grid and \c levels will
be updated to include the contour levels.
@@ -108,18 +108,25 @@ struct ContourParams {
If \c ps.fillGrid is set, the data on the grid will also be drawn using
the color scheme in \c ps.colourMap
if the \c mol argument is given, it will be used to adjust the scale of
drawing. This is because a common use is to draw the molecule onto
the contour, and it makes sense if it fits.
*/
RDKIT_MOLDRAW2D_EXPORT void contourAndDrawGrid(
MolDraw2D &drawer, const double *grid, const std::vector<double> &xcoords,
const std::vector<double> &ycoords, size_t nContours,
std::vector<double> &levels, const ContourParams &ps = ContourParams());
std::vector<double> &levels, const ContourParams &ps = ContourParams(),
const ROMol *mol = nullptr);
//! \overload
RDKIT_MOLDRAW2D_EXPORT inline void contourAndDrawGrid(
MolDraw2D &drawer, const double *grid, const std::vector<double> &xcoords,
const std::vector<double> &ycoords, size_t nContours = 10,
const ContourParams &ps = ContourParams()) {
const ContourParams &ps = ContourParams(),
const ROMol *mol = nullptr) {
std::vector<double> levels;
contourAndDrawGrid(drawer, grid, xcoords, ycoords, nContours, levels, ps);
contourAndDrawGrid(drawer, grid, xcoords, ycoords, nContours, levels, ps,
mol);
};
//! Generates and draws contours for a set of gaussians
@@ -131,6 +138,7 @@ RDKIT_MOLDRAW2D_EXPORT inline void contourAndDrawGrid(
\param nContours: the number of contours to draw
\param levels: the contours to use
\param ps: additional parameters controlling the contouring.
\param mol: molecule to be used to adjust the scale of the drawing.
The values are calculated on a grid with spacing \c ps.gridResolution.
If \c ps.setScale is set, the grid size will be calculated based on the
@@ -144,19 +152,25 @@ RDKIT_MOLDRAW2D_EXPORT inline void contourAndDrawGrid(
If \c ps.fillGrid is set, the data on the grid will also be drawn using
the color scheme in \c ps.colourMap
if the \c mol argument is given, it will be used to adjust the scale of
drawing. This is because a common use is to draw the molecule onto
the contour, and it makes sense if it fits.
*/
RDKIT_MOLDRAW2D_EXPORT void contourAndDrawGaussians(
MolDraw2D &drawer, const std::vector<Point2D> &locs,
const std::vector<double> &heights, const std::vector<double> &widths,
size_t nContours, std::vector<double> &levels,
const ContourParams &ps = ContourParams());
const ContourParams &ps = ContourParams(), const ROMol *mol = nullptr);
//! \overload
RDKIT_MOLDRAW2D_EXPORT inline void contourAndDrawGaussians(
MolDraw2D &drawer, const std::vector<Point2D> &locs,
const std::vector<double> &heights, const std::vector<double> &widths,
size_t nContours = 10, const ContourParams &ps = ContourParams()) {
size_t nContours = 10, const ContourParams &ps = ContourParams(),
const ROMol *mol = nullptr) {
std::vector<double> levels;
contourAndDrawGaussians(drawer, locs, heights, widths, nContours, levels, ps);
contourAndDrawGaussians(drawer, locs, heights, widths, nContours, levels, ps,
mol);
};
} // namespace MolDraw2DUtils

View File

@@ -434,7 +434,7 @@ void addMoleculeMetadata(const RDKit::MolDraw2DSVG &self, const RDKit::ROMol &m,
void contourAndDrawGaussiansHelper(
RDKit::MolDraw2D &drawer, python::object pylocs, python::object pyheights,
python::object pywidths, unsigned int nContours, python::object pylevels,
const MolDraw2DUtils::ContourParams &params) {
const MolDraw2DUtils::ContourParams &params, python::object mol) {
std::unique_ptr<std::vector<RDGeom::Point2D>> locs =
pythonObjectToVect<RDGeom::Point2D>(pylocs);
std::unique_ptr<std::vector<double>> heights =
@@ -447,15 +447,20 @@ void contourAndDrawGaussiansHelper(
} else {
levels = std::unique_ptr<std::vector<double>>(new std::vector<double>);
}
ROMol *mol_p = nullptr;
if (mol) {
mol_p = python::extract<ROMol *>(mol);
}
MolDraw2DUtils::contourAndDrawGaussians(drawer, *locs, *heights, *widths,
nContours, *levels, params);
nContours, *levels, params, mol_p);
}
void contourAndDrawGridHelper(RDKit::MolDraw2D &drawer, python::object &data,
python::object &pyxcoords,
python::object &pyycoords, unsigned int nContours,
python::object &pylevels,
const MolDraw2DUtils::ContourParams &params) {
const MolDraw2DUtils::ContourParams &params,
python::object mol) {
if (!PyArray_Check(data.ptr())) {
throw_value_error("data argument must be a numpy array");
}
@@ -486,9 +491,13 @@ void contourAndDrawGridHelper(RDKit::MolDraw2D &drawer, python::object &data,
throw_value_error("data array and ycoords sizes do not match");
}
ROMol *mol_p = nullptr;
if (mol) {
mol_p = python::extract<RDKit::ROMol *>(mol);
}
MolDraw2DUtils::contourAndDrawGrid(drawer, (double *)PyArray_DATA(dataArr),
*xcoords, *ycoords, nContours, *levels,
params);
params, mol_p);
Py_DECREF(dataArr);
}
@@ -598,10 +607,15 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
.def_readwrite("maxFontSize", &RDKit::MolDrawOptions::maxFontSize,
"maximum font size in pixels. default=40, -1 means no"
" maximum.")
.def_readwrite("minFontSize", &RDKit::MolDrawOptions::minFontSize,
"minimum font size in pixels. default=12, -1 means no"
" minimum.")
.def_readwrite(
"annotationFontScale", &RDKit::MolDrawOptions::annotationFontScale,
"Scale of font for atom and bond annotation relative to atom"
"label font. Default=0.75.")
.def_readwrite("fontFile", &RDKit::MolDrawOptions::fontFile,
"Font file for use with FreeType text drawer.")
.def_readwrite(
"multipleBondOffset", &RDKit::MolDrawOptions::multipleBondOffset,
"offset (in Angstroms) for the extra lines in a multiple bond")
@@ -650,13 +664,16 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
.def_readwrite("additionalAtomLabelPadding",
&RDKit::MolDrawOptions::additionalAtomLabelPadding,
"additional padding to leave around atom labels. "
"Expressed as a fraction of the font size.");
"Expressed as a fraction of the font size.")
.def_readwrite("explicitMethyl", &RDKit::MolDrawOptions::explicitMethyl,
"Draw terminal methyls explictly. Default is false.");
docString = "Drawer abstract base class";
python::class_<RDKit::MolDraw2D, boost::noncopyable>(
"MolDraw2D", docString.c_str(), python::no_init)
.def("SetFontSize", &RDKit::MolDraw2D::setFontSize,
"change the default font size")
.def("FontSize", &RDKit::MolDraw2D::fontSize, "get the default font size")
"change the default font size. The units are, roughly, pixels.")
.def("FontSize", &RDKit::MolDraw2D::fontSize,
"get the default font size. The units are, roughly, pixels.")
.def(
"DrawMolecule", RDKit::drawMoleculeHelper1,
(python::arg("self"), python::arg("mol"),
@@ -769,7 +786,7 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
.def("DrawString",
(void (RDKit::MolDraw2D::*)(const std::string &,
const RDGeom::Point2D &,
RDKit::MolDraw2D::AlignType)) &
RDKit::TextAlignType)) &
RDKit::MolDraw2D::drawString,
(python::arg("self"), python::arg("string"), python::arg("pos"),
python::arg("align")),
@@ -803,6 +820,7 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
boost::noncopyable>("MolDraw2DSVG", docString.c_str(),
python::init<int, int>())
.def(python::init<int, int, int, int>())
.def(python::init<int, int, int, int, bool>())
.def("FinishDrawing", &RDKit::MolDraw2DSVG::finishDrawing,
"add the last bits of SVG to finish the drawing")
.def("AddMoleculeMetadata", RDKit::addMoleculeMetadata,
@@ -889,6 +907,7 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
- nContours: the number of contours to draw
- levels: the contours to use
- ps: additional parameters controlling the contouring.
- mol: molecule used to help set scale.
The values are calculated on a grid with spacing params.gridResolution.
If params.setScale is set, the grid size will be calculated based on the
@@ -902,13 +921,16 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
If params.fillGrid is set, the data on the grid will also be drawn using
the color scheme in params.colourMap
If mol is not 0, uses the molecule to help set the scale, assuming that
it will be drawn over the plot, so needs to fit on it.
*/)DOC";
python::def(
"ContourAndDrawGaussians", &RDKit::contourAndDrawGaussiansHelper,
(python::arg("drawer"), python::arg("locs"), python::arg("heights"),
python::arg("widths"), python::arg("nContours") = 10,
python::arg("levels") = python::object(),
python::arg("params") = RDKit::MolDraw2DUtils::ContourParams()),
python::arg("params") = RDKit::MolDraw2DUtils::ContourParams(),
python::arg("mol") = python::object()),
docString.c_str());
docString = R"DOC(Generates and draws contours for data on a grid
@@ -918,7 +940,8 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
- ycoords: the y coordinates of the grid
- nContours: the number of contours to draw
- levels: the contours to use
- ps: additional parameters controlling the contouring.
- ps: additional parameters controlling the contouring
- mol: molecule used to help set scale.
The values are calculated on a grid with spacing params.gridResolution.
If params.setScale is set, the grid size will be calculated based on the
@@ -932,12 +955,15 @@ BOOST_PYTHON_MODULE(rdMolDraw2D) {
If params.fillGrid is set, the data on the grid will also be drawn using
the color scheme in params.colourMap
If mol is not 0, uses the molecule to help set the scale, assuming that
it will be drawn over the plot, so needs to fit on it.
*/)DOC";
python::def(
"ContourAndDrawGrid", &RDKit::contourAndDrawGridHelper,
(python::arg("drawer"), python::arg("data"), python::arg("xcoords"),
python::arg("ycoords"), python::arg("nContours") = 10,
python::arg("levels") = python::object(),
python::arg("params") = RDKit::MolDraw2DUtils::ContourParams()),
python::arg("params") = RDKit::MolDraw2DUtils::ContourParams(),
python::arg("mol") = python::object()),
docString.c_str());
}

View File

@@ -1,6 +1,7 @@
from rdkit import RDConfig
import unittest
import random
from os import environ
from rdkit import Chem
from rdkit.Chem import Draw, AllChem, rdDepictor
from rdkit.Chem.Draw import rdMolDraw2D
@@ -26,13 +27,16 @@ class TestCase(unittest.TestCase):
def test2(self):
m = Chem.MolFromSmiles('c1ccc(C)c(C)c1C')
AllChem.Compute2DCoords(m)
d = Draw.MolDraw2DSVG(300, 300)
d = Draw.MolDraw2DSVG(300, 300, -1, -1, True)
do = d.drawOptions()
do.atomLabels[3] = 'foolabel'
d.DrawMolecule(m)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertTrue(txt.find("foolabel") != -1)
self.assertTrue(txt.find(">f</text>") != -1)
self.assertTrue(txt.find(">o</text>") != -1)
self.assertTrue(txt.find(">l</text>") != -1)
self.assertTrue(txt.find(">a</text>") != -1)
def testGithubIssue571(self):
if not hasattr(Draw, 'MolDraw2DCairo'):
@@ -315,24 +319,24 @@ M END""")
d.DrawMolecule(dm)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertTrue(txt.find("stroke-width:7px") >= 0)
self.assertTrue(txt.find("stroke-width:21px") == -1)
self.assertTrue(txt.find("stroke-width:5px") >= 0)
self.assertTrue(txt.find("stroke-width:10px") == -1)
d = Draw.MolDraw2DSVG(300, 300)
d.SetLineWidth(4)
d.DrawMolecule(dm)
d.FinishDrawing()
txt = d.GetDrawingText()
# the line width is scaled, so 4 is drawn as 21 pixels wide.
self.assertTrue(txt.find("stroke-width:7px") == -1)
self.assertTrue(txt.find("stroke-width:14px") >= 0)
# the line width is scaled, so 4 is drawn as 10 pixels wide.
self.assertTrue(txt.find("stroke-width:5px") == -1)
self.assertTrue(txt.find("stroke-width:10px") >= 0)
def testPrepareAndDrawMolecule(self):
m = Chem.MolFromSmiles("C1N[C@@H]2OCC12")
d = Draw.MolDraw2DSVG(300, 300)
d = Draw.MolDraw2DSVG(300, 300, -1, -1, True)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertTrue(txt.find("<tspan>H</tspan>") > 0)
self.assertTrue(txt.find(">H</text>") > 0)
def testAtomTagging(self):
m = Chem.MolFromSmiles("C1N[C@@H]2OCC12")
@@ -365,7 +369,7 @@ M END""")
d = Draw.MolDraw2DSVG(300, 300)
d.ClearDrawing()
Draw.ContourAndDrawGaussians(d, gs, hs, ws)
Draw.ContourAndDrawGaussians(d, gs, hs, ws, mol=dm)
d.drawOptions().clearBackground = False
d.DrawMolecule(dm)
d.FinishDrawing()
@@ -377,7 +381,7 @@ M END""")
d.ClearDrawing()
ps = Draw.ContourParams()
ps.fillGrid = True
Draw.ContourAndDrawGaussians(d, gs, hs, ws, params=ps)
Draw.ContourAndDrawGaussians(d, gs, hs, ws, params=ps, mol=dm)
d.drawOptions().clearBackground = False
d.DrawMolecule(dm)
d.FinishDrawing()
@@ -386,10 +390,6 @@ M END""")
print(txt, file=outf)
def testGridContours(self):
m = Chem.MolFromSmiles("C1N[C@@H]2OCC12")
dm = Draw.PrepareMolForDrawing(m)
conf = dm.GetConformer()
grid = np.zeros((50, 100), np.double)
ycoords = list(np.arange(0, 5, 0.1))
xcoords = list(np.arange(0, 10, 0.1))
@@ -412,6 +412,7 @@ M END""")
d = Draw.MolDraw2DSVG(300, 300)
d.ClearDrawing()
Draw.ContourAndDrawGrid(d, np.transpose(grid), xcoords, ycoords)
d.drawOptions().clearBackground = False
d.FinishDrawing()
txt = d.GetDrawingText()
with open("contour_from_py_3.svg", 'w+') as outf:
@@ -459,23 +460,64 @@ M END
def testSetDrawOptions(self):
m = Chem.MolFromSmiles('CCNC(=O)O')
d = rdMolDraw2D.MolDraw2DSVG(250, 200)
d = rdMolDraw2D.MolDraw2DSVG(250, 200, -1, -1, True)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertNotEqual(txt.find("fill:#0000FF' ><tspan>N"), -1)
self.assertEqual(txt.find("fill:#000000' ><tspan>N"), -1)
self.assertNotEqual(txt.find("fill:#0000FF' >N</text>"), -1)
self.assertEqual(txt.find("fill:#000000' >N</text>"), -1)
d = rdMolDraw2D.MolDraw2DSVG(250, 200)
d = rdMolDraw2D.MolDraw2DSVG(250, 200, -1, -1, True)
do = rdMolDraw2D.MolDrawOptions()
do.useBWAtomPalette()
d.SetDrawOptions(do)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertEqual(txt.find("fill:#0000FF' ><tspan>N"), -1)
self.assertNotEqual(txt.find("fill:#000000' ><tspan>N"), -1)
self.assertEqual(txt.find("fill:#0000FF' >N</text>"), -1)
self.assertNotEqual(txt.find("fill:#000000' >N</text>"), -1)
def testAlternativeFreetypeFont(self):
# this one, you have to look at the pictures
m = Chem.MolFromSmiles('S(=O)(=O)(O)c1c(Cl)c(Br)c(I)c(F)c(N)1')
d = rdMolDraw2D.MolDraw2DSVG(250, 200)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
with open('test_ff.svg', 'w') as f:
f.write(txt)
d = rdMolDraw2D.MolDraw2DSVG(250, 200)
do = rdMolDraw2D.MolDrawOptions()
rdbase = environ['RDBASE']
if rdbase:
do.fontFile = '{}/Code/GraphMol/MolDraw2D/Amadeus.ttf'.format(rdbase)
d.SetDrawOptions(do)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
with open('test_aff.svg', 'w') as f:
f.write(txt)
else:
pass
def testExplictMethyl(self):
m = Chem.MolFromSmiles('CC')
d = rdMolDraw2D.MolDraw2DSVG(250, 200)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertEqual(txt.find("class='atom-"), -1)
d = rdMolDraw2D.MolDraw2DSVG(250, 200)
do = rdMolDraw2D.MolDrawOptions()
do.explicitMethyl = True
d.SetDrawOptions(do)
rdMolDraw2D.PrepareAndDrawMolecule(d, m)
d.FinishDrawing()
txt = d.GetDrawingText()
self.assertNotEqual(txt.find("class='atom-"), -1)
def testDrawMoleculeWithHighlights(self):
COLS = [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (1.0, 0.55, 0.0)]

View File

@@ -14,7 +14,6 @@
#include <GraphMol/RDKitBase.h>
#include <GraphMol/SmilesParse/SmilesParse.h>
#include <GraphMol/FileParsers/FileParsers.h>
#include <GraphMol/MolDraw2D/MolDraw2D.h>
#include <GraphMol/MolDraw2D/MolDraw2DSVG.h>
#include <GraphMol/MolDraw2D/MolDraw2DUtils.h>
@@ -25,6 +24,10 @@
#include "MolDraw2DCairo.h"
#endif
// a lot of the tests check <text> flags in the SVG. That doesn't
// happen with the Freetype versions
static const bool NO_FREETYPE = true;
using namespace RDKit;
TEST_CASE("prepareAndDrawMolecule", "[drawing]") {
@@ -34,11 +37,11 @@ TEST_CASE("prepareAndDrawMolecule", "[drawing]") {
// we will be able to recognize that the prep worked because there
// will be an H in the output:
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareAndDrawMolecule(drawer, *m1);
drawer.finishDrawing();
std::string text = drawer.getDrawingText();
CHECK(text.find("<tspan>H</tspan>") != std::string::npos);
CHECK(text.find(">H</text>") != std::string::npos);
}
}
@@ -47,7 +50,7 @@ TEST_CASE("tag atoms in SVG", "[drawing][SVG]") {
auto m1 = "C1N[C@@H]2OCC12"_smiles;
REQUIRE(m1);
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawMolecule(*m1);
std::map<std::string, std::string> actions;
@@ -70,7 +73,7 @@ TEST_CASE("contour data", "[drawing][conrec]") {
auto m1 = "C1N[C@@H]2OCC12"_smiles;
REQUIRE(m1);
SECTION("grid basics") {
MolDraw2DSVG drawer(250, 250);
MolDraw2DSVG drawer(250, 250, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
const size_t gridSz = 100;
@@ -114,7 +117,9 @@ TEST_CASE("contour data", "[drawing][conrec]") {
std::vector<double> levels;
drawer.clearDrawing();
MolDraw2DUtils::contourAndDrawGrid(drawer, grid, xps, yps, 10, levels);
MolDraw2DUtils::contourAndDrawGrid(drawer, grid, xps, yps, 10, levels,
MolDraw2DUtils::ContourParams(),
m1.get());
drawer.drawOptions().clearBackground = false;
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -125,7 +130,7 @@ TEST_CASE("contour data", "[drawing][conrec]") {
delete[] grid;
}
SECTION("gaussian basics") {
MolDraw2DSVG drawer(250, 250);
MolDraw2DSVG drawer(250, 250, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawOptions().padding = 0.1;
@@ -143,7 +148,9 @@ TEST_CASE("contour data", "[drawing][conrec]") {
std::vector<double> levels;
drawer.clearDrawing();
MolDraw2DUtils::contourAndDrawGaussians(drawer, cents, weights, widths, 10,
levels);
levels,
MolDraw2DUtils::ContourParams(),
m1.get());
drawer.drawOptions().clearBackground = false;
drawer.drawMolecule(*m1);
@@ -154,7 +161,7 @@ TEST_CASE("contour data", "[drawing][conrec]") {
outs.flush();
}
SECTION("gaussian fill") {
MolDraw2DSVG drawer(250, 250);
MolDraw2DSVG drawer(250, 250, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawOptions().padding = 0.1;
@@ -174,7 +181,7 @@ TEST_CASE("contour data", "[drawing][conrec]") {
cps.fillGrid = true;
drawer.clearDrawing();
MolDraw2DUtils::contourAndDrawGaussians(drawer, cents, weights, widths, 10,
levels, cps);
levels, cps, m1.get());
drawer.drawOptions().clearBackground = false;
drawer.drawMolecule(*m1);
@@ -189,7 +196,7 @@ TEST_CASE("contour data", "[drawing][conrec]") {
auto m2 = "C1N[C@@H]2OCC12C=CC"_smiles;
REQUIRE(m2);
MolDraw2DSVG drawer(450, 250);
MolDraw2DSVG drawer(450, 250, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m2);
drawer.drawOptions().padding = 0.1;
@@ -210,7 +217,7 @@ TEST_CASE("contour data", "[drawing][conrec]") {
cps.gridResolution = 0.5;
drawer.clearDrawing();
MolDraw2DUtils::contourAndDrawGaussians(drawer, cents, weights, widths, 10,
levels, cps);
levels, cps, m2.get());
drawer.drawOptions().clearBackground = false;
drawer.drawMolecule(*m2);
@@ -226,7 +233,7 @@ TEST_CASE("dative bonds", "[drawing][organometallics]") {
SECTION("basics") {
auto m1 = "N->[Pt]"_smiles;
REQUIRE(m1);
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -235,13 +242,15 @@ TEST_CASE("dative bonds", "[drawing][organometallics]") {
outs << text;
outs.flush();
CHECK(text.find("<path class='bond-0' d='M 55.1495,101.204"
" L 52.2436,100 L 55.1495,98.7964") != std::string::npos);
CHECK(text.find("<path class='bond-0' d='M 126.052,100 L 85.9675,100'"
" style='fill:none;fill-rule:evenodd;"
"stroke:#0000FF;")
!= std::string::npos);
}
SECTION("more complex") {
auto m1 = "N->1[C@@H]2CCCC[C@H]2N->[Pt]11OC(=O)C(=O)O1"_smiles;
REQUIRE(m1);
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -250,16 +259,17 @@ TEST_CASE("dative bonds", "[drawing][organometallics]") {
outs << text;
outs.flush();
CHECK(text.find("<path class='bond-7' d='M 92.9861,93.568"
" L 92.2758,94.0033 L 92.4703,93.1932") !=
std::string::npos);
CHECK(text.find("<path class='bond-7' d='M 101.307,79.424 "
"L 95.669,87.1848' style='fill:none;"
"fill-rule:evenodd;stroke:#0000FF;")
!= std::string::npos);
}
SECTION("test colours") {
// the dative bonds point the wrong way, but the point is to test
// if the tip of the arrow is blue.
auto m1 = "[Cu++]->1->2.N1CCN2"_smiles;
REQUIRE(m1);
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -268,9 +278,10 @@ TEST_CASE("dative bonds", "[drawing][organometallics]") {
outs << text;
outs.flush();
CHECK(text.find("<path class='bond-2' d='M 61.7343,143.825"
" L 89.7427,152.925' style='fill:none;"
"fill-rule:evenodd;stroke:#0000FF") != std::string::npos);
CHECK(text.find("<path class='bond-2' d='M 53.289,140.668"
" L 81.0244,149.68' style='fill:none;"
"fill-rule:evenodd;stroke:#0000FF;")
!= std::string::npos);
}
}
@@ -279,7 +290,7 @@ TEST_CASE("zero-order bonds", "[drawing][organometallics]") {
auto m1 = "N-[Pt]"_smiles;
REQUIRE(m1);
m1->getBondWithIdx(0)->setBondType(Bond::ZERO);
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareMolForDrawing(*m1);
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -297,33 +308,42 @@ TEST_CASE("copying drawing options", "[drawing]") {
REQUIRE(m1);
SECTION("foundations") {
{
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDraw2DUtils::prepareAndDrawMolecule(drawer, *m1);
drawer.finishDrawing();
std::string text = drawer.getDrawingText();
CHECK(text.find("fill:#0000FF' ><tspan>HN") != std::string::npos);
std::ofstream outs("testFoundations_1.svg");
outs << text;
outs.flush();
CHECK(text.find("fill:#0000FF' >N</text>") != std::string::npos);
}
{
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
assignBWPalette(drawer.drawOptions().atomColourPalette);
MolDraw2DUtils::prepareAndDrawMolecule(drawer, *m1);
drawer.finishDrawing();
std::string text = drawer.getDrawingText();
CHECK(text.find("fill:#0000FF' ><tspan>HN") == std::string::npos);
CHECK(text.find("fill:#000000' ><tspan>HN") != std::string::npos);
std::ofstream outs("testFoundations_2.svg");
outs << text;
outs.flush();
CHECK(text.find("fill:#0000FF' >N</text>") == std::string::npos);
CHECK(text.find("fill:#000000' >N</text>") != std::string::npos);
}
}
SECTION("test") {
{
MolDraw2DSVG drawer(200, 200);
MolDraw2DSVG drawer(200, 200, -1, -1, NO_FREETYPE);
MolDrawOptions options = drawer.drawOptions();
assignBWPalette(options.atomColourPalette);
drawer.drawOptions() = options;
MolDraw2DUtils::prepareAndDrawMolecule(drawer, *m1);
drawer.finishDrawing();
std::string text = drawer.getDrawingText();
CHECK(text.find("fill:#0000FF' ><tspan>HN") == std::string::npos);
CHECK(text.find("fill:#000000' ><tspan>HN") != std::string::npos);
std::ofstream outs("testTest_1.svg");
outs << text;
outs.flush();
CHECK(text.find("fill:#0000FF' >N</text>") == std::string::npos);
CHECK(text.find("fill:#000000' >N</text>") != std::string::npos);
}
}
}
@@ -333,7 +353,7 @@ TEST_CASE("bad DrawMolecules() when molecules are not kekulized",
auto m1 = "CCN(CC)CCn1nc2c3ccccc3sc3c(CNS(C)(=O)=O)ccc1c32"_smiles;
REQUIRE(m1);
SECTION("foundations") {
MolDraw2DSVG drawer(500, 200, 250, 200);
MolDraw2DSVG drawer(500, 200, 250, 200, NO_FREETYPE);
drawer.drawOptions().prepareMolsBeforeDrawing = false;
RWMol dm1(*m1);
RWMol dm2(*m1);
@@ -363,18 +383,20 @@ TEST_CASE("draw atom/bond indices", "[drawing]") {
REQUIRE(m1);
SECTION("foundations") {
{
MolDraw2DSVG drawer(250, 200);
MolDraw2DSVG drawer(250, 200, -1, -1, NO_FREETYPE);
drawer.drawMolecule(*m1);
drawer.finishDrawing();
std::string text = drawer.getDrawingText();
std::ofstream outs("testAtomBondIndices_1.svg");
outs << text;
outs.flush();
CHECK(text.find("<tspan>1</tspan>") == std::string::npos);
CHECK(text.find("<tspan>1,(S)</tspan>") == std::string::npos);
CHECK(text.find(">1</text>") == std::string::npos);
CHECK(text.find(">(</text>") == std::string::npos);
CHECK(text.find(">S</text>") == std::string::npos);
CHECK(text.find(">)</text>") == std::string::npos);
}
{
MolDraw2DSVG drawer(250, 200);
MolDraw2DSVG drawer(250, 200, -1, -1, NO_FREETYPE);
drawer.drawOptions().addAtomIndices = true;
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -382,14 +404,14 @@ TEST_CASE("draw atom/bond indices", "[drawing]") {
std::ofstream outs("testAtomBondIndices_2.svg");
outs << text;
outs.flush();
CHECK(text.find("<tspan>1</tspan>") != std::string::npos);
CHECK(text.find(">1</text>") != std::string::npos);
// it only appears once though:
CHECK(text.find("<tspan>1</tspan>", text.find("<tspan>1</tspan>") + 1) ==
CHECK(text.find(">1</text>", text.find(">1</text>") + 1) ==
std::string::npos);
CHECK(text.find("1,(S)") == std::string::npos);
}
{
MolDraw2DSVG drawer(250, 200);
MolDraw2DSVG drawer(250, 200, -1, -1, NO_FREETYPE);
drawer.drawOptions().addBondIndices = true;
drawer.drawMolecule(*m1);
drawer.finishDrawing();
@@ -397,13 +419,13 @@ TEST_CASE("draw atom/bond indices", "[drawing]") {
std::ofstream outs("testAtomBondIndices_3.svg");
outs << text;
outs.flush();
CHECK(text.find("<tspan>1</tspan>") != std::string::npos);
CHECK(text.find(">1</text>") != std::string::npos);
// it only appears once though:
CHECK(text.find("<tspan>1</tspan>", text.find("<tspan>1</tspan>") + 1) ==
CHECK(text.find(">1</text>", text.find(">1</text>") + 1) ==
std::string::npos);
}
{
MolDraw2DSVG drawer(250, 200);
MolDraw2DSVG drawer(250, 200, -1, -1, NO_FREETYPE);
drawer.drawOptions().addAtomIndices = true;
drawer.drawOptions().addBondIndices = true;
drawer.drawMolecule(*m1);
@@ -412,13 +434,13 @@ TEST_CASE("draw atom/bond indices", "[drawing]") {
std::ofstream outs("testAtomBondIndices_4.svg");
outs << text;
outs.flush();
CHECK(text.find("<tspan>1</tspan>") != std::string::npos);
CHECK(text.find(">1</text>") != std::string::npos);
// it appears twice:
CHECK(text.find("<tspan>1</tspan>", text.find("<tspan>1</tspan>") + 1) !=
CHECK(text.find(">1</text>", text.find(">1</text>") + 1) !=
std::string::npos);
}
{
MolDraw2DSVG drawer(250, 200);
MolDraw2DSVG drawer(250, 200, -1, -1, NO_FREETYPE);
m1->getAtomWithIdx(2)->setProp(common_properties::atomNote, "foo");
drawer.drawOptions().addAtomIndices = true;
drawer.drawOptions().addStereoAnnotation = true;
@@ -429,9 +451,14 @@ TEST_CASE("draw atom/bond indices", "[drawing]") {
std::ofstream outs("testAtomBondIndices_5.svg");
outs << text;
outs.flush();
CHECK(text.find("<tspan>1,(S)</tspan>") != std::string::npos);
CHECK(text.find("<tspan>2,foo</tspan>") != std::string::npos);
CHECK(text.find("<tspan>1</tspan>") == std::string::npos);
CHECK(text.find(">1</text>") != std::string::npos);
CHECK(text.find(">,</text>") != std::string::npos);
CHECK(text.find(">(</text>") != std::string::npos);
CHECK(text.find(">S</text>") != std::string::npos);
CHECK(text.find(")</text>") != std::string::npos);
CHECK(text.find(">2</text>") != std::string::npos);
CHECK(text.find(">f</text>") != std::string::npos);
CHECK(text.find(">o</text>") != std::string::npos);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -37,14 +37,13 @@ using boost_adaptbx::python::streambuf;
namespace RDKit {
std::string molToSVG(const ROMol &mol, unsigned int width, unsigned int height,
python::object pyHighlightAtoms, bool kekulize,
unsigned int lineWidthMult, unsigned int fontSize,
unsigned int lineWidthMult,
bool includeAtomCircles, int confId) {
RDUNUSED_PARAM(kekulize);
std::unique_ptr<std::vector<int>> highlightAtoms =
pythonObjectToVect(pyHighlightAtoms, static_cast<int>(mol.getNumAtoms()));
std::stringstream outs;
MolDraw2DSVG drawer(width, height, outs);
drawer.setFontSize(fontSize / 24.);
drawer.setLineWidth(drawer.lineWidth() * lineWidthMult);
drawer.drawOptions().circleAtoms = includeAtomCircles;
drawer.drawMolecule(mol, highlightAtoms.get(), nullptr, nullptr, confId);

View File

@@ -230,7 +230,6 @@ public class Chemv2Tests extends GraphMolTest {
// System.out.print(svg);
assertTrue(svg.indexOf("<svg") > -1);
assertTrue(svg.indexOf("</svg>") > -1);
assertTrue(svg.indexOf("THE_LEGEND") > -1);
assertTrue(svg.indexOf("fill:#FFFF00;") > -1);
assertTrue(svg.indexOf("fill:#FF00FF;") > -1);
assertTrue(svg.indexOf("fill:#00FFFF;") > -1);

View File

@@ -42,4 +42,6 @@
#cmakedefine RDK_BUILD_CAIRO_SUPPORT
#cmakedefine RDK_BUILD_FREETYPE_SUPPORT
#cmakedefine RDK_USE_URF

BIN
Data/Fonts/Amadeus.ttf Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,5 @@
This font was found on the internet and did not come with a license. While we try to make sure that all the fonts on fontsquirrel.com are properly licensed for commercial use, there are many fonts that have either been abandoned by their authors or the authors distribute their fonts without an explicit license.
It is our opinion that if the unlicensed font is freely available for download from either the original source or from multiple free-font sites then we assume it to be safe to use the font commercially. This is no guarantee of such freedom, but there are so many unlicensed free fonts distributed by primary sources that the intentions must be read that the font is free to use how you like.
We are not lawyers and don't pretend to be them on TV. Please report any errors/violations you know of. http://www.fontsquirrel.com/contact

View File

@@ -0,0 +1,43 @@
Copyright 2011 The Telex Project Authors (info@andrestorresi.com.ar), with Reserved Font Name Telex.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -5,6 +5,8 @@
- FindMCS() may return single atom MCSs, whereas previously it returned an empty
MCS unless there was at least one commond bond across the input structures.
So the MCS between molecules `CC` and `CO` is now `[#6]` rather than being null.
- The fontSize()/setFontSize() (FontSize()/SetFontSize()) methods in MolDraw2D
now work in units of pixels (more or less) instead of the molecule units.
## Code removed in this release:
- To improve API consistency of the exceptions in RDKit with the default ones in