Skip to content
Commits on Source (7)
bin
build
dist
reports
.settings
doc/api
doc/javadoc
jar/libJniInchi.so.1.4
jar/libinchi.so.1.01.00
cdk.log*
*.class
cdk-src+libs-*.tar.gz
cdk-src+libs-*.zip
sourcedist
*~
netbeans-delta-build/
nbproject/CDK-dynamic.properties
nbproject/private/
target
.project
.classpath
*.dot
/.idea/
*.iml
.DS_Store
language: java
install: true
script: mvn clean test -q
Sam Adams
Jonathan Alvarsson
Rich Apodaca
Saravanaraj N Ayyampalayam
Eric Bach
Ulrich Bauer
Stephan Beisken
Arvid Berg
Lyle D. Burgoon
Ed Cannon
Zach Charlop-Powers
Alison Choy
Alex Clark
Miguel Correa
Krishna Dole
Fabian Dortu
Kai Dührkop
Martin Eklund
Uli Fechner
Luis de Figueiredo
Matteo Floris
Thorsten Flügel
Sambit Gaan
Jakub Galgonek
Dan Gezelter
Nimish Gopal
Saulius Gražulis
Rajarshi Guha
Martin Gütlein
Yonquan Han
Thierry Hanser
Cyrus Harmon
Kai Hartmann
Tobias Helmus
Christian Hoppe
Oliver Horlacher
Miguel Howard
Andy Howlett
Yi Hsiao
Rafel Israels
Nina Jeliazkova
Geert Josten
Klas Jönsson
Dmitry Katsubo
Jules Kerssemakers
Joos Kiener
Anatoli Krassavine
Stefan Kuhn
Uli Köhler
Violeta Labarta
Jonty Lawson
Kevin Lawson
Daniel Leidert
Katrin Leinweber
Qinqing Liu
Edgar Luttmann
Todd Martin
John Mayfield
Nathanaël Mazuir
Stephan Michels
Martin Morissette
Hirotomo Moriwaki
Scooter Morris
Peter Murray-Rust
Carl Mäsak
Irilenia Nobeli
Noel O'Boyle
Peter Odéus
Magda Oprian
Niels Out
Jerome Pansanel
Diego Pedrosa
Julio Peironcely
Jeffrey Plante
Tomáš Pluskal
Chris Pudney
Syed Asad Rahman
Jonathan Rienstra-Kiracofe
Mark Rijnbeek
David Robinson
Miguel Rojas Cherto
Bhupinder Sandhu
Jean-Sebastien Senecal
Onkar Shinde
Sulev Sild
Bradley Smith
Ola Spjuth
Christoph Steinbeck
Ralf Stephan
John K Sterling
Oliver Stueker
Matt Swain
Daniel Szisz
Aleksey Tarkhov
Stephan Tomkinson
Gilleain Torrance
Andreas Truszkowski
Paul Turner
Kazuya Ujihara
Mark B Vine
Joerg Wegner
Yap Chun Wei
Michael Wenk
Stephane Werner
Mark Williamson
Egon Willighagen
Lars Willighagen
Yong Zhang
Daniel Zaharevitz
Please report issues to GitHub: https://github.com/cdk/cdk/issues
CDK - The Chemical Development Kit
Copyright 1997-2009 The CDK Development Team
License: LGPL v2, see doc/lgpl.license
1. Introduction
You are currently reading the README file for the Chemistry Development Project (CDK).
This project is hosted under http://cdk.sourceforge.net/
Please refer to these pages for updated information and the latest version of the CDK.
The CDK is an open-source library of algorithms for structural chemo- and bioinformatics, implemented in
the programming language Java(tm). The library is published under terms of the the
GNU Lesser General Public License v2. This has implications on what you can do with sources and
binaries of the CDK library. For details, please refer to the file LICENSE, which should have been
provided with this distribution.
PLEASE NOTE: This is a library of useful data structures and algorithms to manipulate them
from the area of structural chemo- and bioinformatics. As such, it is intended for the use by
programmers, who wish to save some effort by reusing code. It is not intended for the enduser.
If you consider yourself to be more like user, you might not find what you wanted.
Please refer to other projects like the JChemPaint project (http://jchempaint.sourceforge.net)
or the Jmol project (http://www.jmol.org/) for programs that actually take advantage of the
CDK library.
2. Compiling
Compiling and jar-ing the software is done with Jakarta's
Ant (http://jakarta.apache.org/ant/) 1.7.1 or better and Java 1.5.0 or better:
cdk/$ ls build.xml
build.xml
cdk/$ ant
"ant -p" gives a list of possible compilation targets. The default target is 'dist-all', which
creates a number of .jar files in the 'dist' directory corresponding to subsets of the CDK
functionality. For convenience, one large .jar file containing everything can be created using the
target 'dist-large' (using the command "ant dist-large"). This is also created in dist/jar and is
typically named something like 'cdk-cvs-20060303.jar'.
2.1 Creating the JavaDoc documentation for the API
The JavaDoc documentation for the API describes all of the CDK classes in detail. It functions as
the user manual for the CDK, although you should also look at the list of examples and tutorials
below. This documentation is created by 'ant' from the Java source code for the CDK as follows:
cdk/$ ls javadoc.xml
javadoc.xml
cdk/$ ant -buildfile javadoc.xml html
The documenation is created as a series of .html pages in doc/api. If you use firefox, you can read
the documentation using the following command:
cdk/$ firefox doc/api/index.html
3. Running tests
IMPORTANT: this requires the SVN or Git version of the sources.
After you compiled the code, you can do "ant test-all" to run the test suite of non-interactive, automated
tests. You might need to copy an appropriate junit.jar into your $ANT_HOME/lib
directory or somewhere else in your classpath.
Upon "ant dist-all test-dist-all test-all", you should see something like:
test:
Running org.openscience.cdk.test.CDKTests
Tests run: 1065, Failures: 7, Errors: 1, Time elapsed: 27,55 sec
As you can see, the vast majority of tests ran successfully, but that there
are failures and errors. The $CDK_HOME/reports/results.txt file contains
information about the outcome of the tests. Some tests might fail intentionally
to mark know issues in CDK.
There are also run interactive tests, like the Controller2DTest. In order to try them, you can edit the "run"
target in the build.xml file to look like this:
<target name="run" depends="dist">
<java classname="org.openscience.cdk.test.ControllerTest" fork="yes">
<arg value=""/>
<classpath>
<pathelement location="${dist}/jar/cdk.jar"/>
<pathelement path="${java.class.path}"/>
<pathelement location="."/>
<fileset dir="jar">
<include name="*.jar"/>
</fileset>
</classpath>
</java>
</target>
Then, a "ant run" should give you a window where you can add bonds to a given structure.
Currently, there are more than 2500 test, of which a large part tests the data, datadebug and
nonotify classes.
4. Using CDK
CDK is a class library intended to be used by other programs. It will not run
as a stand-alone program, although it contains some GUI- and command
line applications. In order to use the CDK in your program, you need to build
the distribution jars by running "ant dist-all". They will end up in
$CDK_HOME/dist/jar. Copy all cdk-*.jars as well as all jars from $CDK_HOME/jar
to the lib directory of the project for which you intend to have CDK support and
use them as you would use any other third party jar.
Alternatively, run "ant dist-large" to create a jar cdk-svn-YYYYMMDD.jar in
$CDK_HOME/dist/jar. This large jar contains all the CDK code and all third
party libraries that code depends on.
5. Examples and tutorials
To get started using the CDK, you may be interested in the following websites which contain
examples and tutorials:
* http://www.chemistry-development-kit.org
* http://blue.chem.psu.edu/~rajarshi/code/java
* http://www.redbrick.dcu.ie/~noel/CDKJython.html
Further examples can also be found in issues of the CDK News:
* http://cdknews.org/
[![Build Status](https://travis-ci.org/cdk/cdk.svg?branch=master)](https://travis-ci.org/cdk/cdk)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.openscience.cdk/cdk/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.openscience.cdk/cdk)
# The Chemistry Development Kit (CDK)
Copyright &copy; 1997-2019 The CDK Development Team
License: LGPL v2, see LICENSE.txt
[Home Page](https://cdk.github.io/) | [JavaDoc](http://cdk.github.io/cdk/latest/docs/api/index.html?overview-summary.html) | [Wiki](https://github.com/cdk/cdk/wiki) | [Issues](https://github.com/cdk/cdk/issues) | [Mailing List](https://sourceforge.net/projects/cdk/lists/cdk-user)
## Introduction
The CDK is an open-source Java library for cheminformatics and bioinformatics.
Key Features:
* Molecule and reaction valence bond representation.
* Read and write file formats: SMILES, SDF, InChI, Mol2, CML, and others.
* Efficient molecule processing algorithms: Ring Finding, Kekulisation, Aromaticity.
* Coordinate generation and rendering.
* Canonical identifiers for fast exact searching.
* Substructure and SMARTS pattern searching.
* ECFP, Daylight, MACCS, and other fingerprint methods for similarity searching.
* QSAR descriptor calculations
## Install
The CDK is a class library intended to be used by other programs, it will not run as a stand-alone program.
The library is built with Apache Maven and currently requires Java 1.7 or later. From the root of the project run to build the JAR files for each module. The ``bundle/target/`` directory contains the main JAR with all dependencies included:
```bash
$ mvn install
```
You can also download a pre-built library JAR from [releases](https://github.com/cdk/cdk/releases).
Include the main JAR on the Java classpath when compiling and running your code:
```bash
$ javac -cp cdk-2.2.jar MyClass.java
$ java -cp cdk-2.2.jar:. MyClass
```
If you are using Maven, you can use the **uber** ``cdk-bundle``, note it is much more efficient to use include
the modules you need:
```xml
<dependency>
<artifactId>cdk-bundle</artifactId>
<groupId>org.openscience.cdk</groupId>
<version>2.2</version>
</dependency>
```
If you are a Python user, the Cinfony project provides access via [Jython](http://www.redbrick.dcu.ie/~noel/CDKJython.html). Noel O'Boyle's [Cinfony](http://cinfony.github.io/) provides a wrapper around the CDK and over toolkits exposing core functionality as a consistent API.
Further details on building the project in integrated development environments (IDEs) are available on the wiki:
* [Building the CDK](https://github.com/cdk/cdk/wiki/Building-CDK)
* [Maven Reporting Plugins](https://github.com/cdk/cdk/wiki/Maven-Reporting-Plugins)
## Getting Help
The [Toolkit-Rosetta Wiki Page](https://github.com/cdk/cdk/wiki/Toolkit-Rosetta) provides some examples for common tasks. If you need help using the CDK and have questions please use the user mailing list, [``cdk-user@lists.sf.net``](mailto:cdk-user@lists.sf.net) (**you must [subscribe here]( https://sourceforge.net/projects/cdk/lists/cdk-user) first to post**).
## Acknowledgments
![YourKit Logo](https://www.yourkit.com/images/yklogo.png)
The CDK developers use YourKit to profile and optimise code.
YourKit supports open source projects with its full-featured Java Profiler.
YourKit, LLC is the creator of <a href="https://www.yourkit.com/java/profiler/index.jsp">YourKit Java Profiler</a>
and <a href="https://www.yourkit.com/.net/profiler/index.jsp">YourKit .NET Profiler</a>,
innovative and intelligent tools for profiling Java and .NET applications.
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2015. NextMove Software Ltd
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cdk-app</artifactId>
<groupId>org.openscience.cdk</groupId>
<version>2.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cdk-depict</artifactId>
<dependencies>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-test</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-renderextra</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-renderawt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-renderbasic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-smiles</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-smarts</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-sdg</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openscience.cdk</groupId>
<artifactId>cdk-silent</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.freehep</groupId>
<artifactId>freehep-graphicsio-svg</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.freehep</groupId>
<artifactId>freehep-graphicsio-pdf</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.freehep</groupId>
<artifactId>freehep-graphicsio-ps</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
</project>
/* Copyright (C) 2015 The Chemistry Development Kit (CDK) project
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.openscience.cdk.depict;
import com.google.common.base.Charsets;
import org.openscience.cdk.renderer.RendererModel;
import org.openscience.cdk.renderer.elements.Bounds;
import org.openscience.cdk.renderer.elements.IRenderingElement;
import org.openscience.cdk.renderer.font.AWTFontManager;
import org.openscience.cdk.renderer.generators.BasicSceneGenerator;
import org.openscience.cdk.renderer.visitor.IDrawVisitor;
import javax.imageio.ImageIO;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import static org.openscience.cdk.depict.DepictionGenerator.AUTOMATIC;
/**
* Base class of a pre-rendered depiction. The class allows introspection of
* depiction size (decided at generation time) and serialization to raster
* and vector graphic formats.
*
* @author John May
*/
@SuppressWarnings("PMD.ShortVariable")
public abstract class Depiction {
/**
* For converting MM coordinates to PS Point (1/72 inch)
*/
protected static final double MM_TO_POINT = 2.83464566751;
/**
* When no fixed padding value is specified we use margin
* multiplied by this value.
*/
protected static final double DEFAULT_PADDING_FACTOR = 2;
/**
* Structured Vector Graphics (SVG) format key.
*/
public static final String SVG_FMT = "svg";
/**
* PostScript (PS) format key.
*/
public static final String PS_FMT = "ps";
/**
* Encapsulated PostScript (EPS) format key.
*/
public static final String EPS_FMT = "eps";
/**
* Portable Document Format (PDF) format key.
*/
public static final String PDF_FMT = "pdf";
/**
* Joint Photographic Experts Group (JPG) format key.
*/
public static final String JPG_FMT = "jpg";
/**
* Portable Network Graphics (PNG) format key.
*/
public static final String PNG_FMT = "png";
/**
* Graphics Interchange Format (GIF) format key.
*/
public static final String GIF_FMT = "gif";
/**
* Units in MM (specific to SVG).
*/
public static final String UNITS_MM = "mm";
/**
* Units in PX (specific to SVG).
*/
public static final String UNITS_PX = "px";
static final double ACS_1996_BOND_LENGTH_MM = 5.08;
private static final char DOT = '.';
private final RendererModel model;
/**
* Internal method passes in the rendering model parameters.
*
* @param model parameters
*/
Depiction(RendererModel model) {
this.model = model;
}
/**
* Render the depiction to a Java AWT {@link BufferedImage}.
*
* @return AWT buffered image
*/
public abstract BufferedImage toImg();
/**
* Render the image to an SVG image.
*
* @return svg XML content
*/
public final String toSvgStr() {
return toSvgStr(UNITS_MM);
}
/**
* Render the image to an SVG image.
*
* @param units the units for SVG - 'px' or 'mm'
* @return svg XML content
*/
public final String toSvgStr(String units) {
if (!units.equals(UNITS_MM) && !units.equals(UNITS_PX))
throw new IllegalArgumentException("Units must be 'px' or 'mm'!");
return toVecStr(SVG_FMT, units);
}
/**
* Render the image to an PS (PostScript) format string.
*
* @return PS content
*/
public final String toPsStr() {
return toVecStr(PS_FMT, UNITS_MM);
}
/**
* Render the image to an EPS (Encapsulated PostScript) format
* string.
*
* @return EPS content
*/
public final String toEpsStr() {
return toVecStr(EPS_FMT, UNITS_MM);
}
/**
* Render the image to an PDF format string.
*
* @return pdf content
*/
public final String toPdfStr() {
return toVecStr(PDF_FMT, UNITS_MM);
}
/**
* Access the specified padding value or fallback to a provided
* default.
*
* @param defaultPadding default value if the parameter is 'automatic'
* @return padding
*/
double getPaddingValue(double defaultPadding) {
double padding = model.get(RendererModel.Padding.class);
if (padding == AUTOMATIC)
padding = defaultPadding;
return padding;
}
/**
* Access the specified margin value or fallback to a provided
* default.
*
* @param defaultMargin default value if the parameter is 'automatic'
* @return margin
*/
double getMarginValue(final double defaultMargin) {
double margin = model.get(BasicSceneGenerator.Margin.class);
if (margin == AUTOMATIC)
margin = defaultMargin;
return margin;
}
/**
* Internal - implementations should overload this method for vector graphics
* rendering.
*
* @param fmt the vector graphics format
* @param units the units to use (px or mm)
* @return the vector graphics format string
*/
abstract String toVecStr(String fmt, String units);
/**
* List the available formats that can be rendered.
*
* @return supported formats
*/
public final List<String> listFormats() {
final List<String> formats = new ArrayList<>();
formats.add(SVG_FMT);
formats.add(SVG_FMT.toUpperCase(Locale.ROOT));
formats.add(PS_FMT);
formats.add(PS_FMT.toUpperCase(Locale.ROOT));
formats.add(EPS_FMT);
formats.add(EPS_FMT.toUpperCase(Locale.ROOT));
formats.add(PDF_FMT);
formats.add(PDF_FMT.toUpperCase(Locale.ROOT));
formats.addAll(Arrays.asList(ImageIO.getWriterFormatNames()));
return formats;
}
/**
* Write the depiction to the provided output stream.
*
* @param fmt format
* @param out output stream
* @throws IOException depiction could not be written, low level IO problem
* @see #listFormats()
*/
public final void writeTo(String fmt, OutputStream out) throws IOException {
if (fmt.equalsIgnoreCase(SVG_FMT)) {
out.write(toSvgStr().getBytes(Charsets.UTF_8));
} else if (fmt.equalsIgnoreCase(PS_FMT)) {
out.write(toEpsStr().getBytes(Charsets.UTF_8));
} else if (fmt.equalsIgnoreCase(PDF_FMT)) {
out.write(toPdfStr().getBytes(Charsets.UTF_8));
} else {
ImageIO.write(toImg(), fmt, out);
}
}
/**
* Write the depiction to the provided output stream.
*
* @param fmt format
* @param file output destination
* @throws IOException depiction could not be written, low level IO problem
* @see #listFormats()
*/
public final void writeTo(String fmt, File file) throws IOException {
try (FileOutputStream out = new FileOutputStream(file)) {
writeTo(fmt, out);
}
}
/**
* Write the depiction to the provided file path.
*
* @param fmt format
* @param path output destination path
* @throws IOException depiction could not be written, low level IO problem
* @see #listFormats()
*/
public final void writeTo(String fmt, String path) throws IOException {
writeTo(fmt, new File(replaceTildeWithHomeDir(ensureSuffix(path, fmt))));
}
/**
* Write the depiction to the provided file path, the format is determined
* by the path suffix.
*
* @param path output destination path
* @throws IOException depiction could not be written, low level IO problem
* @see #listFormats()
*/
public final void writeTo(String path) throws IOException {
final int i = path.lastIndexOf(DOT);
if (i < 0 || i + 1 == path.length())
throw new IOException("Cannot find suffix in provided path: " + path);
final String fmt = path.substring(i + 1);
writeTo(fmt, path);
}
/**
* Utility for resolving paths on unix systems that contain tilda for
* the home directory.
*
* @param path the file system path
* @return normalised path
*/
private static String replaceTildeWithHomeDir(String path) {
if (path.startsWith("~/"))
return System.getProperty("user.home") + path.substring(1);
return path;
}
/**
* Ensures a suffix on a file output if the path doesn't
* currently end with it. For example calling
* {@code writeTo(SVG_FMT, "~/chemical")} would create a file
* {@code ~/chemical.svg}.
*
* @param path the file system path
* @param suffix the format suffix
* @return path with correct suffix
*/
private static String ensureSuffix(String path, String suffix) {
if (path.endsWith(DOT + suffix))
return path;
return path + DOT + suffix;
}
/**
* Low-level draw method used by other rendering methods.
*
* @param visitor the draw visitor
* @param bounds a bound rendering element
* @param zoom if the diagram is zoomed at all
* @param viewBounds the view bounds - the root will be centered in the bounds
*/
protected final void draw(IDrawVisitor visitor, double zoom, Bounds bounds, Rectangle2D viewBounds) {
double modelScale = zoom * model.get(BasicSceneGenerator.Scale.class);
double zoomToFit = Math.min(viewBounds.getWidth() / (bounds.width() * modelScale),
viewBounds.getHeight() / (bounds.height() * modelScale));
AffineTransform transform = new AffineTransform();
transform.translate(viewBounds.getCenterX(), viewBounds.getCenterY());
transform.scale(modelScale, -modelScale);
// default is shrink only unless specified
if (model.get(BasicSceneGenerator.FitToScreen.class) || zoomToFit < 1)
transform.scale(zoomToFit, zoomToFit);
transform.translate(-(bounds.minX + bounds.maxX) / 2,
-(bounds.minY + bounds.maxY) / 2);
// not always needed
AWTFontManager fontManager = new AWTFontManager();
fontManager.setFontForZoom(zoomToFit);
visitor.setRendererModel(model);
visitor.setFontManager(fontManager);
visitor.setTransform(transform);
// setup up transform
visitor.visit(bounds.root());
}
/**
* Utility method for recalling a depiction in pixels to one in millimeters.
*
* @param bondLength the desired bond length (mm)
* @return the scaling factor
*/
final double rescaleForBondLength(double bondLength) {
return bondLength / model.get(BasicSceneGenerator.BondLength.class);
}
protected void svgPrevisit(String fmt, double rescale, SvgDrawVisitor visitor, List<? extends IRenderingElement> elements) {
visitor.setTransform(AffineTransform.getScaleInstance(rescale, rescale));
visitor.previsit(elements);
visitor.setTransform(null);
}
}
/* Copyright (C) 2015 The Chemistry Development Kit (CDK) project
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.openscience.cdk.depict;
import org.openscience.cdk.renderer.elements.Bounds;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* Internal: Immutable value class to help with store diagram dimensions
* (a tuple width and height). Given some dimensions we can add/subtract,
* grow/shrink as needed. Utility methods are provided for laying out rendering
* elements in grids and rows.
*/
@SuppressWarnings("PMD.ShortVariable")
final class Dimensions {
/**
* Magic value for automated sizing.
*/
final static Dimensions AUTOMATIC = new Dimensions(DepictionGenerator.AUTOMATIC,
DepictionGenerator.AUTOMATIC);
/**
* The values.
*/
final double w, h;
public Dimensions(double w, double h) {
this.w = w;
this.h = h;
}
Dimensions add(double w, double h) {
return new Dimensions(this.w + w, this.h + h);
}
Dimensions scale(double coef) {
return new Dimensions(coef * w, coef * h);
}
static Dimensions ofRow(List<Bounds> elems) {
return ofGrid(elems, 1, elems.size());
}
static Dimensions ofCol(List<Bounds> elems) {
return ofGrid(elems, elems.size(), 1);
}
static Dimensions ofGrid(List<Bounds> bounds, int nRow, int nCol) {
return ofGrid(bounds, new double[nRow + 1], new double[nCol + 1]);
}
/**
* Determine how much space is needed to depiction the bound {@link IRenderingElements} if
* they were aligned in a grid without padding or margins. The method takes arrays
* for for the offset which are one item bigger than the size of the gird
* (e.g. 3x2 would need arrays of length 4 and 2). The arrays are filled with the
* cumulative width/heights for each grid point allowing easy alignment.
*
* @param bounds bound rendering elements
* @param yOffset array for col offsets
* @param xOffset array for row offset
* @return the dimensions required
*/
static Dimensions ofGrid(List<Bounds> bounds, double[] yOffset, double[] xOffset) {
int nRow = yOffset.length - 1;
int nCol = xOffset.length - 1;
int nBounds = bounds.size();
for (int i = 0; i < nBounds; i++) {
// +1 because first offset is always 0
int col = 1 + i % nCol;
int row = 1 + i / nCol;
final Bounds bound = bounds.get(i);
if (bound.isEmpty())
continue;
double width = bound.width();
double height = bound.height();
if (width > xOffset[col])
xOffset[col] = width;
if (height > yOffset[row])
yOffset[row] = height;
}
for (int i = 1; i < yOffset.length; i++)
yOffset[i] += yOffset[i - 1];
for (int i = 1; i < xOffset.length; i++)
xOffset[i] += xOffset[i - 1];
return new Dimensions(xOffset[nCol], yOffset[nRow]);
}
/**
* Determine grid size (nrow, ncol) that could be used
* for displaying a given number of elements.
*
* @param nElem number of elements
* @return grid dimensions (integers)
*/
static Dimension determineGrid(int nElem) {
switch (nElem) {
case 0:
return new Dimension(0, 0);
case 1:
return new Dimension(1, 1);
case 2:
return new Dimension(2, 1);
case 3:
return new Dimension(3, 1);
case 4:
return new Dimension(2, 2);
case 5:
return new Dimension(3, 2);
case 6:
return new Dimension(3, 2);
case 7:
return new Dimension(4, 2);
case 8:
return new Dimension(4, 2);
case 9:
return new Dimension(3, 3);
default:
// not great but okay
int nrow = (int) Math.floor(Math.sqrt(nElem));
int ncol = (int) Math.ceil(nElem / (double) nrow);
return new Dimension(ncol, nrow);
}
}
@Override
public String toString() {
return Math.ceil(w) + "x" + Math.ceil(h);
}
}
/*
* Copyright (c) 2015 John May <jwmay@users.sf.net>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version. All we ask is that proper credit is given
* for our work, which includes - but is not limited to - adding the above
* copyright notice to the beginning of your source code files, and to any
* copyright notice that you may distribute with programs based on this work.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
*/
package org.openscience.cdk.depict;
import org.freehep.graphicsio.pdf.PDFGraphics2D;
import org.freehep.graphicsio.svg.SVGGraphics2D;
import org.freehep.graphicsio.ps.PSGraphics2D;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
/**
* Internal - wrapper around the FreeHEP vector graphics output that makes things consistent
* in terms of writing the required headers and footers.
* @see <a href="http://java.freehep.org/">java.freehep.org</a>
*
* The toString() method cites the following documents:
*
* [PLDS92] {@cdk.cite Adobe1992}
* [EPSF92] {@cdk.cite Adobe1992a}
* [EGFF96] {@cdk.cite Murray1996}
*/
final class FreeHepWrapper {
private final ByteArrayOutputStream bout;
private final String fmt;
final Graphics2D g2;
private final Dimension dim;
public FreeHepWrapper(String fmt, double w, double h) {
this.dim = new Dimension((int) Math.ceil(w), (int) Math.ceil(h));
try {
this.g2 = createGraphics2d(this.fmt = fmt,
this.bout = new ByteArrayOutputStream(),
this.dim);
} catch (IOException e) {
throw new InstantiationError("Could not create Vector Graphics output: " + e.getMessage());
}
}
private static Graphics2D createGraphics2d(String fmt, OutputStream out, Dimension dim) throws IOException {
switch (fmt) {
case Depiction.SVG_FMT:
SVGGraphics2D svg = new SVGGraphics2D(out, dim);
svg.setCreator("Chemistry Development Kit (http://www.github.com/cdk/)");
svg.writeHeader();
return svg;
case Depiction.PDF_FMT:
PDFGraphics2D pdf = new PDFGraphics2D(out, dim);
pdf.setCreator("Chemistry Development Kit (http://www.github.com/cdk/)");
Properties props = new Properties();
props.setProperty(PDFGraphics2D.FIT_TO_PAGE, "false");
props.setProperty(PDFGraphics2D.PAGE_SIZE, PDFGraphics2D.CUSTOM_PAGE_SIZE);
props.setProperty(PDFGraphics2D.CUSTOM_PAGE_SIZE, dim.width + ", " + dim.height);
props.setProperty(PDFGraphics2D.PAGE_MARGINS, "0, 0, 0, 0");
pdf.setProperties(props);
pdf.writeHeader();
return pdf;
case Depiction.EPS_FMT:
PSGraphics2D eps = new PSGraphics2D(out, dim);
// For EPS (Encapsulated PostScript) page size has no
// meaning since this image is supposed to be included
// in another page.
Properties eps_props = new Properties();
eps_props.setProperty(PDFGraphics2D.FIT_TO_PAGE, "false");
eps.setProperties(eps_props);
eps.writeHeader();
return eps;
case Depiction.PS_FMT:
PSGraphics2D ps = new PSGraphics2D(out, dim);
Properties ps_props = new Properties();
ps_props.setProperty(PDFGraphics2D.FIT_TO_PAGE, "true");
ps.setProperties(ps_props);
ps.writeHeader();
return ps;
default:
throw new IOException("Unsupported vector format, " + fmt);
}
}
public void dispose() {
try {
switch (fmt) {
case Depiction.SVG_FMT:
((SVGGraphics2D) g2).writeTrailer();
((SVGGraphics2D) g2).closeStream();
break;
case Depiction.PDF_FMT:
((PDFGraphics2D) g2).writeTrailer();
((PDFGraphics2D) g2).closeStream();
break;
}
} catch (IOException e) {
// ignored we write to an internal array
}
g2.dispose();
}
@Override
public String toString() {
String result = new String(bout.toByteArray(), StandardCharsets.UTF_8);
// we want SVG in mm not pixels!
if (fmt.equals(Depiction.SVG_FMT)) {
result = result.replaceAll("\"([-+0-9.]+)px\"", "\"$1mm\"");
} else if (fmt.equals(Depiction.EPS_FMT)) {
String nl;
// We should determine new-line separator (nl) not from OS type, but from the line-endings
// in the actual EPS outpu; there is nothing that would prevent us from generating Unix-style
// file in Windows :)
if( result.contains("\r\n")) {
nl = "\r\n";
} else if( result.contains("\r")) {
nl = "\r";
} else {
nl = "\n";
}
String split[] = result.split(nl,2);
if( split.length > 1 && split[0].startsWith("%!PS-") ) {
String boundingBox;
if( this.dim != null ) {
boundingBox = "%%BoundingBox: 0 0 " +
dim.width + " " + dim.height + nl;
} else {
boundingBox = "";
}
if(!split[0].contains("EPS") && !boundingBox.equals("")) {
split[0] += " EPSF-3.0";
}
// EGFF96 (p. 379):
// "Both the %%PS-Adobe- [sic] and the %%BoundingBox: lines must appear in every EPS file.
// Ordinary PostScript files may formally be changed into EPS files by adding these two lines
// to the PostScript header."
// PLDS92 (p. 29):
// "The order of some comments in the document is significant, but in a
// section of the document they may appear in any order. For example, in the
// header section, %%DocumentResources:, %%Title:, and %%Creator: may
// appear in any order."
//
// Thus, I infer that the "%%BoundingBox:" comment may be added immediately after the
// "%!PS-..." header line. This is also given as a valid example in EPSF92, p. 4.
result = split[0] + nl +
boundingBox +
split[1].
replaceFirst("(\\d+ ){4}setmargins",
"0 0 0 0 setmargins").
replaceFirst("(\\d+ ){2}setpagesize",
dim.width + " " + dim.height +
" setpagesize");
}
}
return result;
}
}
/*
* Copyright (c) 2015 John May <jwmay@users.sf.net>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version. All we ask is that proper credit is given
* for our work, which includes - but is not limited to - adding the above
* copyright notice to the beginning of your source code files, and to any
* copyright notice that you may distribute with programs based on this work.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
*/
package org.openscience.cdk.depict;
import org.openscience.cdk.renderer.RendererModel;
import org.openscience.cdk.renderer.elements.Bounds;
import org.openscience.cdk.renderer.elements.RectangleElement;
import org.openscience.cdk.renderer.generators.BasicSceneGenerator;
import org.openscience.cdk.renderer.visitor.AWTDrawVisitor;
import org.openscience.cdk.renderer.visitor.IDrawVisitor;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
/**
* Internal - depicts a set of molecules aligned in a grid. This class
* also handles the degenerate case of a single molecule as a 1x1 grid.
*/
final class MolGridDepiction extends Depiction {
private final RendererModel model;
private final Dimensions dimensions;
private final int nCol, nRow;
private final List<Bounds> elements;
public MolGridDepiction(RendererModel model,
List<Bounds> molecules,
List<Bounds> titles,
Dimensions dimensions,
int nRow, int nCol) {
super(model);
this.model = model;
this.dimensions = dimensions;
this.elements = new ArrayList<>();
// degenerate case is when no title are provided
if (titles.isEmpty()) {
elements.addAll(molecules);
} else {
assert molecules.size() == titles.size();
// interweave molecules and titles
for (int r = 0; r < nRow; r++) {
final int fromIndex = r * nCol;
final int toIndex = Math.min(molecules.size(), (r + 1) * nCol);
if (fromIndex >= toIndex)
break;
final List<Bounds> molsublist = molecules.subList(fromIndex, toIndex);
// need to pad list
while (molsublist.size() < nCol)
molsublist.add(new Bounds());
elements.addAll(molsublist);
elements.addAll(titles.subList(fromIndex, toIndex));
}
nRow *= 2;
}
this.nCol = nCol;
this.nRow = nRow;
}
@Override
public BufferedImage toImg() {
// format margins and padding for raster images
final double margin = getMarginValue(DepictionGenerator.DEFAULT_PX_MARGIN);
final double padding = getPaddingValue(DEFAULT_PADDING_FACTOR * margin);
final double scale = model.get(BasicSceneGenerator.Scale.class);
final double zoom = model.get(BasicSceneGenerator.ZoomFactor.class);
// row and col offsets for alignment
double[] yOffset = new double[nRow + 1];
double[] xOffset = new double[nCol + 1];
Dimensions required = Dimensions.ofGrid(elements, yOffset, xOffset)
.scale(scale * zoom);
final Dimensions total = calcTotalDimensions(margin, padding, required, null);
final double fitting = calcFitting(margin, padding, required, null);
// create the image for rendering
final BufferedImage img = new BufferedImage((int) Math.ceil(total.w), (int) Math.ceil(total.h),
BufferedImage.TYPE_4BYTE_ABGR);
// we use the AWT for vector graphics if though we're raster because
// fractional strokes can be figured out by interpolation, without
// when we shrink diagrams bonds can look too bold/chubby
final Graphics2D g2 = img.createGraphics();
final IDrawVisitor visitor = AWTDrawVisitor.forVectorGraphics(g2);
visitor.setTransform(AffineTransform.getScaleInstance(1,-1));
visitor.visit(new RectangleElement(0, -(int) Math.ceil(total.h), (int) Math.ceil(total.w), (int) Math.ceil(total.h),
true, model.get(BasicSceneGenerator.BackgroundColor.class)));
// compound the zoom, fitting and scaling into a single value
final double rescale = zoom * fitting * scale;
// x,y base coordinates include the margin and centering (only if fitting to a size)
final double xBase = margin + (total.w - 2*margin - (nCol-1)*padding - (rescale * xOffset[nCol])) / 2;
final double yBase = margin + (total.h - 2*margin - (nRow-1)*padding - (rescale * yOffset[nRow])) / 2;
for (int i = 0; i < elements.size(); i++) {
final int row = i / nCol;
final int col = i % nCol;
// skip empty elements
final Bounds bounds = this.elements.get(i);
if (bounds.isEmpty())
continue;
// calc the 'view' bounds:
// amount of padding depends on which row or column we are in.
// the width/height of this col/row can be determined by the next offset
double x = xBase + col * padding + rescale * xOffset[col];
double y = yBase + row * padding + rescale * yOffset[row];
double w = rescale * (xOffset[col+1] - xOffset[col]);
double h = rescale * (yOffset[row+1] - yOffset[row]);
draw(visitor, zoom, bounds, rect(x, y, w, h));
}
// we created the Graphic2d instance so need to dispose of it
g2.dispose();
return img;
}
private double calcFitting(double margin, double padding, Dimensions required, String fmt) {
if (dimensions == Dimensions.AUTOMATIC)
return 1; // no fitting
Dimensions targetDim = dimensions;
// PDF and PS are in point to we need to account for that
if (PDF_FMT.equals(fmt) || PS_FMT.equals(fmt) || EPS_FMT.equals(fmt))
targetDim = targetDim.scale(MM_TO_POINT);
targetDim = targetDim.add(-2 * margin, -2 * margin)
.add(-((nCol - 1) * padding), -((nRow - 1) * padding));
double resize = Math.min(targetDim.w / required.w,
targetDim.h / required.h);
if (resize > 1 && !model.get(BasicSceneGenerator.FitToScreen.class))
resize = 1;
return resize;
}
private Dimensions calcTotalDimensions(double margin, double padding, Dimensions required, String fmt) {
if (dimensions == Dimensions.AUTOMATIC) {
return required.add(2 * margin, 2 * margin)
.add((nCol - 1) * padding, (nRow - 1) * padding);
} else {
// we want all vector graphics dims in MM
if (PDF_FMT.equals(fmt) || PS_FMT.equals(fmt) || EPS_FMT.equals(fmt))
return dimensions.scale(MM_TO_POINT);
else
return dimensions;
}
}
@Override
String toVecStr(String fmt, String units) {
// format margins and padding for raster images
double margin = getMarginValue(units.equals(Depiction.UNITS_MM) ? DepictionGenerator.DEFAULT_MM_MARGIN
: DepictionGenerator.DEFAULT_PX_MARGIN);
double padding = getPaddingValue(DEFAULT_PADDING_FACTOR * margin);
final double scale = model.get(BasicSceneGenerator.Scale.class);
double zoom = model.get(BasicSceneGenerator.ZoomFactor.class);
// All vector graphics will be written in mm not px to we need to
// adjust the size of the molecules accordingly. For now the rescaling
// is fixed to the bond length proposed by ACS 1996 guidelines (~5mm)
if (units.equals(Depiction.UNITS_MM))
zoom *= rescaleForBondLength(Depiction.ACS_1996_BOND_LENGTH_MM);
// PDF and PS units are in Points (1/72 inch) in FreeHEP so need to adjust for that
if (fmt.equals(PDF_FMT) || fmt.equals(PS_FMT) || fmt.equals(EPS_FMT)) {
zoom *= MM_TO_POINT;
margin *= MM_TO_POINT;
padding *= MM_TO_POINT;
}
// row and col offsets for alignment
double[] yOffset = new double[nRow+1];
double[] xOffset = new double[nCol+1];
Dimensions required = Dimensions.ofGrid(elements, yOffset, xOffset)
.scale(zoom * scale);
final Dimensions total = calcTotalDimensions(margin, padding, required, fmt);
final double fitting = calcFitting(margin, padding, required, fmt);
// create the image for rendering
FreeHepWrapper wrapper = null;
if (!fmt.equals(SVG_FMT))
wrapper = new FreeHepWrapper(fmt, total.w, total.h);
final IDrawVisitor visitor = fmt.equals(SVG_FMT) ? new SvgDrawVisitor(total.w, total.h, units)
: AWTDrawVisitor.forVectorGraphics(wrapper.g2);
if (fmt.equals(SVG_FMT)) {
svgPrevisit(fmt, scale * zoom * fitting, (SvgDrawVisitor) visitor, elements);
} else {
// pdf can handle fraction coords just fine
((AWTDrawVisitor) visitor).setRounding(false);
}
visitor.setTransform(AffineTransform.getScaleInstance(1,-1));
visitor.visit(new RectangleElement(0, -(int) Math.ceil(total.h), (int) Math.ceil(total.w), (int) Math.ceil(total.h),
true, model.get(BasicSceneGenerator.BackgroundColor.class)));
// compound the fitting and scaling into a single value
final double rescale = zoom * fitting * scale;
// x,y base coordinates include the margin and centering (only if fitting to a size)
final double xBase = margin + (total.w - 2*margin - (nCol-1)*padding - (rescale * xOffset[nCol])) / 2;
final double yBase = margin + (total.h - 2*margin - (nRow-1)*padding - (rescale * yOffset[nRow])) / 2;
for (int i = 0; i < elements.size(); i++) {
final int row = i / nCol;
final int col = i % nCol;
// calc the 'view' bounds:
// amount of padding depends on which row or column we are in.
// the width/height of this col/row can be determined by the next offset
double x = xBase + col * padding + rescale * xOffset[col];
double y = yBase + row * padding + rescale * yOffset[row];
double w = rescale * (xOffset[col+1] - xOffset[col]);
double h = rescale * (yOffset[row+1] - yOffset[row]);
draw(visitor, zoom, elements.get(i), rect(x, y, w, h));
}
if (wrapper != null) {
wrapper.dispose();
return wrapper.toString();
} else {
return visitor.toString();
}
}
private Rectangle2D.Double rect(double x, double y, double w, double h) {
return new Rectangle2D.Double(x, y, w, h);
}
}
# Open Babel superatom (abbreviations) list
# https://github.com/openbabel/superatoms/blob/master/superatom.txt
#
# The following license applies to this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
#
*C(=O)OCC COOEt
*OCC(C)C OiBu
*C(C)(C)C tBu
*CCCC nBu
*C(C)C iPr
*CCC nPr
*CC Et
*NC(F)(F)F NCF3
*C(F)(F)F CF3
*C(Cl)(Cl)Cl CCl3
*C#N CN
*[N+]#[C-] NC
*N(O)C N(OH)CH3
*N(=O)=O NO2
*[N+]([O-])=O NO2
*N=O NO
*S(=O)(=O)O SO3H
*C(=O)O COOH
*C(=O)[O-] COO-
*OCC OEt
*OC(=O)C OAc
*NC(=O)C NHAc
*C(=O)C Ac
*C=O CHO
*NC NMe
*SC SMe
*OC OMe
\ No newline at end of file
/*
* Copyright (c) 2018 Saulius Gražulis <grazulis@ibt.lt>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version. All we ask is that proper credit is given
* for our work, which includes - but is not limited to - adding the above
* copyright notice to the beginning of your source code files, and to any
* copyright notice that you may distribute with programs based on this work.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
*/
package org.openscience.cdk.depict;
import org.junit.Test;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.silent.SilentChemObjectBuilder;
import org.openscience.cdk.smiles.SmilesParser;
import static org.junit.Assert.*;
public class DepictionTest {
@Test
public void depictAsPs() throws CDKException {
DepictionGenerator dg = new DepictionGenerator();
SmilesParser sp = new SmilesParser(SilentChemObjectBuilder.getInstance());
IAtomContainer ac = sp.parseSmiles("[nH]1cccc1");
String eps = dg.depict(ac).toPsStr();
String nl = System.getProperty("line.separator");
String lines[] = eps.split(nl,3);
assertEquals("%!PS-Adobe-3.0", lines[0]);
assertEquals("%%Creator: FreeHEP Graphics2D Driver", lines[1]);
}
@Test
public void depictAsEps() throws CDKException {
DepictionGenerator dg = new DepictionGenerator();
SmilesParser sp = new SmilesParser(SilentChemObjectBuilder.getInstance());
IAtomContainer ac = sp.parseSmiles("[nH]1cccc1");
String eps = dg.depict(ac).toEpsStr();
String nl = System.getProperty("line.separator");
String lines[] = eps.split(nl,3);
assertEquals("%!PS-Adobe-3.0 EPSF-3.0", lines[0]);
assertEquals("%%BoundingBox: 0 0 28 35", lines[1]);
}
@Test
public void depictAsEps2() throws CDKException {
DepictionGenerator dg = new DepictionGenerator();
SmilesParser sp = new SmilesParser(SilentChemObjectBuilder.getInstance());
IAtomContainer ac = sp.parseSmiles("C1CCCCC1CCCCC");
String eps = dg.depict(ac).toEpsStr();
String nl = System.getProperty("line.separator");
String lines[] = eps.split(nl,3);
assertEquals("%!PS-Adobe-3.0 EPSF-3.0", lines[0]);
assertEquals("%%BoundingBox: 0 0 92 33", lines[1]);
}
}
/*
* Copyright (c) 2015 John May <jwmay@users.sf.net>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version. All we ask is that proper credit is given
* for our work, which includes - but is not limited to - adding the above
* copyright notice to the beginning of your source code files, and to any
* copyright notice that you may distribute with programs based on this work.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 U
*/
package org.openscience.cdk.depict;
import org.junit.Test;
import org.openscience.cdk.renderer.elements.GeneralPath;
import org.openscience.cdk.renderer.elements.LineElement;
import org.openscience.cdk.renderer.elements.MarkedElement;
import org.openscience.cdk.renderer.elements.RectangleElement;
import org.openscience.cdk.renderer.elements.TextElement;
import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
public class SvgDrawVisitorTest {
@Test
public void empty() {
String empty = new SvgDrawVisitor(50, 50, Depiction.UNITS_MM).toString();
assertThat(empty, is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='50.0mm' height='50.0mm' viewBox='0 0 50.0 50.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ "</svg>\n"));
}
@Test
public void markedElement() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(50, 50, Depiction.UNITS_MM);
visitor.visit(MarkedElement.markup(new LineElement(0, 0, 1, 1, 0.5, Color.RED),
"test-class"));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='50.0mm' height='50.0mm' viewBox='0 0 50.0 50.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <line x1='.0' y1='.0' x2='1.0' y2='1.0' stroke='#FF0000' stroke-width='.5'/>\n"
+ " </g>\n"
+ "</svg>\n"));
}
@Test
public void translatedLine() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(50, 50, Depiction.UNITS_MM);
visitor.visit(new LineElement(0, 0, 1, 1, 0.5, Color.RED));
visitor.setTransform(AffineTransform.getTranslateInstance(10, 10));
visitor.visit(new LineElement(0, 0, 1, 1, 0.5, Color.RED));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='50.0mm' height='50.0mm' viewBox='0 0 50.0 50.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <line x1='.0' y1='.0' x2='1.0' y2='1.0' stroke='#FF0000' stroke-width='.5'/>\n"
+ " <line x1='10.0' y1='10.0' x2='11.0' y2='11.0' stroke='#FF0000' stroke-width='.5'/>\n"
+ " </g>\n"
+ "</svg>\n"));
}
@Test
public void scaledStroke() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(50, 50, Depiction.UNITS_MM);
visitor.visit(new LineElement(0, 0, 1, 1, 0.5, Color.RED));
visitor.setTransform(AffineTransform.getScaleInstance(2, 2));
visitor.visit(new LineElement(0, 0, 1, 1, 0.5, Color.RED));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='50.0mm' height='50.0mm' viewBox='0 0 50.0 50.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <line x1='.0' y1='.0' x2='1.0' y2='1.0' stroke='#FF0000' stroke-width='.5'/>\n"
+ " <line x1='.0' y1='.0' x2='2.0' y2='2.0' stroke='#FF0000' stroke-width='1.0'/>\n"
+ " </g>\n"
+ "</svg>\n"));
}
@Test
public void filledPath() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(50, 50, Depiction.UNITS_MM);
visitor.visit(GeneralPath.shapeOf(new RoundRectangle2D.Double(0,0,10,10,2,2), Color.BLUE));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='50.0mm' height='50.0mm' viewBox='0 0 50.0 50.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <path d='M.0 1.0v8.0c.0 .55 .45 1.0 1.0 1.0h8.0c.55 .0 1.0 -.45 1.0 -1.0v-8.0c.0 -.55 -.45 -1.0 -1.0 -1.0h-8.0c-.55 .0 -1.0 .45 -1.0 1.0z' stroke='none' fill='#0000FF'/>\n"
+ " </g>\n"
+ "</svg>\n"));
}
@Test
public void transformedPath() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(50, 50, Depiction.UNITS_MM);
visitor.setTransform(AffineTransform.getTranslateInstance(15, 15));
visitor.visit(GeneralPath.shapeOf(new RoundRectangle2D.Double(0, 0, 10, 10, 2, 2), Color.BLUE));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='50.0mm' height='50.0mm' viewBox='0 0 50.0 50.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <path d='M15.0 16.0v8.0c.0 .55 .45 1.0 1.0 1.0h8.0c.55 .0 1.0 -.45 1.0 -1.0v-8.0c.0 -.55 -.45 -1.0 -1.0 -1.0h-8.0c-.55 .0 -1.0 .45 -1.0 1.0z' stroke='none' fill='#0000FF'/>\n"
+ " </g>\n"
+ "</svg>\n"));
}
@Test
public void textElements() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(100, 100, Depiction.UNITS_MM);
visitor.visit(new TextElement(50, 50, "PNG < EPS < SVG", Color.RED));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='100.0mm' height='100.0mm' viewBox='0 0 100.0 100.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <text x='50.0' y='50.0' fill='#FF0000' text-anchor='middle'>PNG &lt; EPS &lt; SVG</text>\n"
+ " </g>\n"
+ "</svg>\n"));
}
@Test
public void rectElements() {
final SvgDrawVisitor visitor = new SvgDrawVisitor(100, 100, Depiction.UNITS_MM);
visitor.visit(new RectangleElement(0,0,100,100, Color.WHITE));
assertThat(visitor.toString(), is("<?xml version='1.0' encoding='UTF-8'?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg version='1.2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='100.0mm' height='100.0mm' viewBox='0 0 100.0 100.0'>\n"
+ " <desc>Generated by the Chemistry Development Kit (http://github.com/cdk)</desc>\n"
+ " <g stroke-linecap='round' stroke-linejoin='round'>\n"
+ " <rect x='.0' y='-100.0' width='100.0' height='100.0' fill='none' stroke='#FFFFFF'/>\n"
+ " </g>\n"
+ "</svg>\n"));
}
}