diff --git a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerTestFunctions.java b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerTestFunctions.java
index aa357bbda2901b9a4604fae54b30fe7e599a5f4c..24e3bebbcaeec0d8b6de0588da9b9c522b123152 100644
--- a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerTestFunctions.java
+++ b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerTestFunctions.java
@@ -7,6 +7,7 @@ import lcsb.mapviewer.converter.ConverterParams;
 import lcsb.mapviewer.converter.InvalidInputDataExecption;
 import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerSpecies;
 import lcsb.mapviewer.model.Project;
+import lcsb.mapviewer.model.cache.UploadedFileEntry;
 import lcsb.mapviewer.model.graphics.HorizontalAlign;
 import lcsb.mapviewer.model.graphics.VerticalAlign;
 import lcsb.mapviewer.model.map.Drawable;
@@ -20,6 +21,7 @@ import lcsb.mapviewer.model.map.compartment.TopSquareCompartment;
 import lcsb.mapviewer.model.map.kinetics.SbmlFunction;
 import lcsb.mapviewer.model.map.kinetics.SbmlParameter;
 import lcsb.mapviewer.model.map.kinetics.SbmlUnit;
+import lcsb.mapviewer.model.map.layout.graphics.Glyph;
 import lcsb.mapviewer.model.map.layout.graphics.Layer;
 import lcsb.mapviewer.model.map.layout.graphics.LayerText;
 import lcsb.mapviewer.model.map.model.Model;
@@ -46,9 +48,13 @@ import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.geom.Point2D;
 import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 
 import static org.junit.Assert.assertEquals;
 
@@ -299,4 +305,22 @@ public abstract class CellDesignerTestFunctions extends TestUtils {
     return project;
   }
 
+  protected Glyph createGlyph() throws IOException {
+    Glyph glyph = new Glyph();
+    glyph.setFile(createFile("testFiles/glyphs/uni.png"));
+
+    return glyph;
+  }
+
+  private UploadedFileEntry createFile(final String filePath) throws IOException {
+    UploadedFileEntry uploadedFileEntry = new UploadedFileEntry();
+    byte[] byteArray = Files.readAllBytes(Paths.get(filePath));
+    uploadedFileEntry.setFileContent(byteArray);
+    uploadedFileEntry.setOriginalFileName("glyphs/" + new File(filePath).getName());
+    uploadedFileEntry.setLength(byteArray.length);
+    uploadedFileEntry.setLocalPath(null);
+    return uploadedFileEntry;
+
+  }
+
 }
diff --git a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ProjectExportTest.java b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ProjectExportTest.java
index 57001079c87ca067e121c547b66889480533695f..1f5034282ddf0a0f27cbec29170abcb28c820063 100644
--- a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ProjectExportTest.java
+++ b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ProjectExportTest.java
@@ -7,6 +7,7 @@ import lcsb.mapviewer.converter.zip.ZipEntryFile;
 import lcsb.mapviewer.converter.zip.ZipEntryFileFactory;
 import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.ProjectComparator;
+import lcsb.mapviewer.model.map.layout.graphics.Glyph;
 import lcsb.mapviewer.model.map.model.ElementSubmodelConnection;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelData;
@@ -99,6 +100,22 @@ public class ProjectExportTest extends CellDesignerTestFunctions {
     testSerializationOverZip(project);
   }
 
+  @Test
+  public void testGlyph() throws Exception {
+    Glyph glyph = createGlyph();
+    Project project = createProject();
+    Model model = createEmptyModel();
+    model.setWidth(260);
+    Protein protein = createProtein();
+    protein.setGlyph(glyph);
+    model.addElement(protein);
+    project.addModel(model);
+    project.addGlyph(glyph);
+
+    testSerializationOverZip(project);
+  }
+
+
   private void testSerializationOverZip(final Project project) throws Exception {
     File tempFile = File.createTempFile("CD-", ".zip");
     try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
diff --git a/converter-CellDesigner/testFiles/glyphs/uni.png b/converter-CellDesigner/testFiles/glyphs/uni.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd325269723c196ab52460e32a904db0e3bdc46f
Binary files /dev/null and b/converter-CellDesigner/testFiles/glyphs/uni.png differ
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java
index 5d6bc2741c2dc10b2f18223f019874358c771f2b..c00117499d594f9ca6dbf8395de7f63544cf679c 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java
@@ -10,9 +10,11 @@ import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
 import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
 import lcsb.mapviewer.converter.zip.ZipEntryFile;
 import lcsb.mapviewer.converter.zip.ZipEntryFileFactory;
+import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.cache.UploadedFileEntry;
 import lcsb.mapviewer.model.map.compartment.Compartment;
 import lcsb.mapviewer.model.map.layout.ReferenceGenomeType;
+import lcsb.mapviewer.model.map.layout.graphics.Glyph;
 import lcsb.mapviewer.model.map.model.ElementSubmodelConnection;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelSubmodelConnection;
@@ -40,6 +42,7 @@ import java.lang.reflect.Modifier;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -65,6 +68,7 @@ public class ComplexZipConverter {
    */
   private static final Logger logger = LogManager.getLogger();
   protected static final String IMMEDIATE_LINK_PREFIX = "IMMEDIATE_LINK:";
+  protected static final String GLYPH_PREFIX = "Glyph:";
 
   /**
    * Class used to create single submap from a file.
@@ -97,7 +101,7 @@ public class ComplexZipConverter {
    * @return complex {@link Model} created from input data
    * @throws InvalidInputDataExecption thrown when there is a problem with accessing input data
    */
-  public Model createModel(final ComplexZipConverterParams params) throws InvalidInputDataExecption, ConverterException {
+  public Model createModel(final ComplexZipConverterParams params, final Project project) throws InvalidInputDataExecption, ConverterException {
     try {
       ZipFile zipFile = params.getZipFile();
       Enumeration<? extends ZipEntry> entries;
@@ -154,16 +158,22 @@ public class ComplexZipConverter {
           processReaction(mapping, nameModelMap, result, reaction);
         }
         for (final Species species : mappingModel.getSpeciesList()) {
-          processSpecies(species, nameModelMap);
+          processSpecies(species, nameModelMap, project.getGlyphs());
         }
       }
+
+      project.addModel(result);
+      for (final Model model : result.getSubmodels()) {
+        project.addModel(model);
+      }
+
       return result;
     } catch (final IOException e) {
       throw new InvalidArgumentException(e);
     }
   }
 
-  private void processSpecies(final Species species, final Map<String, Model> nameModelMap) {
+  private void processSpecies(final Species species, final Map<String, Model> nameModelMap, final List<Glyph> glyphs) {
     String notes = species.getNotes();
     if (notes != null) {
       String[] lines = notes.split("\n");
@@ -172,28 +182,49 @@ public class ComplexZipConverter {
         if (line.startsWith(IMMEDIATE_LINK_PREFIX)) {
           String link = line.replace(IMMEDIATE_LINK_PREFIX, "").trim();
 
-          Compartment compartment = species.getCompartment();
-          if (compartment == null) {
-            logger.warn("[SUBMODEL MAPPING] Species {} in mapping file doesn't start inside compartment. Skipped. Link skipped",
-                species.getElementId());
-          } else {
-            String modelName = compartment.getName().toLowerCase();
-            Model model = nameModelMap.get(modelName);
-            if (model == null) {
-              throw new InvalidArgumentException("Mapping file references to " + modelName + " submodel. But such model doesn't exist");
-            }
-            Element elementToChange = model.getElementByElementId(species.getName());
-            if (elementToChange == null) {
-              throw new InvalidArgumentException("Mapping file references to element with alias: " + species.getName()
-                  + ". But such element doesn't exist");
-            }
+          Element elementToChange = getElementToChange(species, nameModelMap);
+          if (elementToChange != null) {
             elementToChange.setImmediateLink(link);
           }
+        } else if (line.startsWith(GLYPH_PREFIX)) {
+          String filename = line.replace(GLYPH_PREFIX, "").trim();
+
+          Element elementToChange = getElementToChange(species, nameModelMap);
+          if (elementToChange != null) {
+            for (Glyph glyph : glyphs) {
+              if (glyph.getFile().getOriginalFileName().equalsIgnoreCase(filename)) {
+                elementToChange.setGlyph(glyph);
+              }
+            }
+          }
         }
       }
     }
   }
 
+  private static Element getElementToChange(
+      final Species species,
+      final Map<String, Model> nameModelMap) {
+    Compartment compartment = species.getCompartment();
+    if (compartment == null) {
+      logger.warn("[SUBMODEL MAPPING] Species {} in mapping file doesn't start inside compartment. Skipped. Link skipped",
+          species.getElementId());
+    } else {
+      String modelName = compartment.getName().toLowerCase();
+      Model model = nameModelMap.get(modelName);
+      if (model == null) {
+        throw new InvalidArgumentException("Mapping file references to " + modelName + " submodel. But such model doesn't exist");
+      }
+      Element result = model.getElementByElementId(species.getName());
+      if (result == null) {
+        throw new InvalidArgumentException("Mapping file references to element with alias: " + species.getName()
+            + ". But such element doesn't exist");
+      }
+      return result;
+    }
+    return null;
+  }
+
   protected boolean isIgnoredFile(final String name) {
     if (name == null) {
       return true;
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java b/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java
index 6be2c0667115a0e7bf148fd87bd26656a9bb4d01..00f51f01df6db6f3144fdfd0f4b9191b7fc1bec4 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/OverviewParser.java
@@ -1,5 +1,23 @@
 package lcsb.mapviewer.converter;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import lcsb.mapviewer.common.exception.InvalidArgumentException;
+import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
+import lcsb.mapviewer.converter.zip.OverviewLinkDeserializer;
+import lcsb.mapviewer.model.map.OverviewImage;
+import lcsb.mapviewer.model.map.OverviewImageLink;
+import lcsb.mapviewer.model.map.OverviewLink;
+import lcsb.mapviewer.model.map.OverviewModelLink;
+import lcsb.mapviewer.model.map.OverviewSearchLink;
+import lcsb.mapviewer.model.map.model.ModelData;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import javax.imageio.ImageIO;
 import java.awt.Polygon;
 import java.awt.geom.Area;
 import java.awt.geom.Point2D;
@@ -20,33 +38,11 @@ import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
-import javax.imageio.ImageIO;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-import lcsb.mapviewer.common.exception.InvalidArgumentException;
-import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
-import lcsb.mapviewer.converter.zip.OverviewLinkDeserializer;
-import lcsb.mapviewer.model.map.OverviewImage;
-import lcsb.mapviewer.model.map.OverviewImageLink;
-import lcsb.mapviewer.model.map.OverviewLink;
-import lcsb.mapviewer.model.map.OverviewModelLink;
-import lcsb.mapviewer.model.map.OverviewSearchLink;
-import lcsb.mapviewer.model.map.model.Model;
-
 /**
  * Parser used to extract data about {@link OverviewImage overview images} from
  * zip file.
- * 
+ *
  * @author Piotr Gawron
- * 
  */
 public class OverviewParser {
   /**
@@ -58,37 +54,17 @@ public class OverviewParser {
 
   private static final String JSON_COORDINATES_FILENAME = "coords.json";
 
-  /**
-   * Name of the column in {@link #COORDINATES_FILENAME} where information about
-   * {@link OverviewModelLink#zoomLevel} is stored.
-   */
   private static final String ZOOM_LEVEL_COORDINATES_COLUMN = "MODEL_ZOOM_LEVEL";
-  /**
-   * Name of the column in {@link #COORDINATES_FILENAME} where information about
-   * {@link OverviewModelLink#xCoord},{@link OverviewModelLink#yCoord} is stored.
-   */
+
   private static final String REDIRECTION_COORDINATES_COORDINATE_COLUMN = "MODEL_COORDINATES";
-  /**
-   * Name of the column in {@link #COORDINATES_FILENAME} where information about
-   * {@link OverviewModelLink#linkedModel} or
-   * {@link OverviewImageLink#linkedOverviewImage} is stored.
-   */
+
   private static final String TARGET_FILENAME_COORDINATE_COLUMN = "LINK_TARGET";
-  /**
-   * Name of the column in {@link #COORDINATES_FILENAME} where information about
-   * {@link OverviewLink#polygon} is stored.
-   */
+
   private static final String POLYGON_COORDINATE_COLUMN = "POLYGON";
-  /**
-   * Name of the column in {@link #COORDINATES_FILENAME} where information about
-   * {@link OverviewLink#overviewImage source of the image} is stored.
-   */
+
   private static final String FILENAME_COORDINATE_COLUMN = "FILE";
 
-  /**
-   * Name of the column in {@link #COORDINATES_FILENAME} where information about
-   * type of the link (implementation of {@link OverviewLink} class) is stored.
-   */
+
   private static final String TARGET_TYPE_COORDINATE_COLUMN = "LINK_TYPE";
 
   /**
@@ -126,20 +102,16 @@ public class OverviewParser {
    * Method that parse zip file and creates list of {@link OverviewImage images}
    * from it.
    *
-   * @param models
-   *          map with models where the key is name of the file and value is model
-   *          that was parsed from the file
-   * @param files
-   *          list with files to parse
-   * @param outputDirectory
-   *          directory where images should be stored, directory path should be
-   *          absolute
+   * @param models          map with models where the key is name of the file and value is model
+   *                        that was parsed from the file
+   * @param files           list with files to parse
+   * @param outputDirectory directory where images should be stored, directory path should be
+   *                        absolute
    * @return list of {@link OverviewImage images}
-   * @throws InvalidOverviewFile
-   *           thrown when the zip file contains invalid data
+   * @throws InvalidOverviewFile thrown when the zip file contains invalid data
    */
-  public List<OverviewImage> parseOverviewLinks(final Set<Model> models, final List<ImageZipEntryFile> files,
-      final String outputDirectory, final ZipFile zipFile) throws InvalidOverviewFile {
+  public List<OverviewImage> parseOverviewLinks(final Set<ModelData> models, final List<ImageZipEntryFile> files,
+                                                final String outputDirectory, final ZipFile zipFile) throws InvalidOverviewFile {
     if (outputDirectory != null) {
       File f = new File(outputDirectory);
       if (!f.exists()) {
@@ -220,9 +192,9 @@ public class OverviewParser {
     }
   }
 
-  private Map<String, Model> createMapping(final Set<Model> models) {
-    Map<String, Model> result = new HashMap<>();
-    for (final Model model : models) {
+  private Map<String, ModelData> createMapping(final Set<ModelData> models) {
+    Map<String, ModelData> result = new HashMap<>();
+    for (final ModelData model : models) {
       result.put(model.getName().toLowerCase(), model);
     }
     return result;
@@ -232,20 +204,16 @@ public class OverviewParser {
    * This method process data from {@link #COORDINATES_FILENAME} in zip archive.
    * This method adds connections between images and between images and models.
    *
-   * @param models
-   *          map with models where the key is name of the file and value is model
-   *          that was parsed from the file
-   * @param images
-   *          list of {@link OverviewImage images} that should be connected
-   * @param coordinatesData
-   *          {@link String} with the data taken from
-   *          {@link #COORDINATES_FILENAME} file
-   * @throws InvalidOverviewFile
-   *           thrown when the data are invalid
+   * @param models          map with models where the key is name of the file and value is model
+   *                        that was parsed from the file
+   * @param images          list of {@link OverviewImage images} that should be connected
+   * @param coordinatesData {@link String} with the data taken from
+   *                        {@link #COORDINATES_FILENAME} file
+   * @throws InvalidOverviewFile thrown when the data are invalid
    */
-  protected List<OverviewImage> processCoordinates(final Set<Model> models, final List<OverviewImage> images, final String coordinatesData)
+  protected List<OverviewImage> processCoordinates(final Set<ModelData> models, final List<OverviewImage> images, final String coordinatesData)
       throws InvalidOverviewFile {
-    Map<String, Model> modelMapping = createMapping(models);
+    Map<String, ModelData> modelMapping = createMapping(models);
     String[] rows = coordinatesData.replaceAll("\r", "\n").split("\n");
     Integer filenameColumn = null;
     Integer polygonColumn = null;
@@ -399,9 +367,9 @@ public class OverviewParser {
     return images;
   }
 
-  protected List<OverviewImage> processJsonCoordinates(final Set<Model> models, final List<OverviewImage> images, final String json)
+  protected List<OverviewImage> processJsonCoordinates(final Set<ModelData> models, final List<OverviewImage> images, final String json)
       throws InvalidOverviewFile {
-    Map<String, Model> modelMapping = createMapping(models);
+    Map<String, ModelData> modelMapping = createMapping(models);
     ObjectMapper mapper = new ObjectMapper();
     final SimpleModule module = new SimpleModule();
     module.addDeserializer(OverviewLink.class, new OverviewLinkDeserializer());
@@ -429,7 +397,7 @@ public class OverviewParser {
                 ((OverviewImageLink) link).setLinkedOverviewImage(linkImage);
               }
               if (link instanceof OverviewModelLink) {
-                Model model = modelMapping.get(((OverviewModelLink) link).getLinkedModel().getName().toLowerCase());
+                ModelData model = modelMapping.get(((OverviewModelLink) link).getLinkedModel().getName().toLowerCase());
 
                 if (model == null) {
                   throw new InvalidOverviewFile(((OverviewModelLink) link).getLinkedModel().getName() + " is missing");
@@ -454,35 +422,32 @@ public class OverviewParser {
    * Creates a link from parameters and place it in appropriate
    * {@link OverviewImage}.
    *
-   * @param filename
-   *          {@link OverviewImage#filename name of the image}
-   * @param polygon
-   *          {@link OverviewImage#polygon polygon} describing link
-   * @param linkTarget
-   *          defines target that should be invoked when the link is activated.
-   *          This target is either a file name (in case of
-   *          {@link #MODEL_LINK_TYPE} or {@link #IMAGE_LINK_TYPE}) or a search
-   *          string (in case of {@link #SEARCH_LINK_TYPE}).
-   * @param coord
-   *          coordinates on the model where redirection should be placed in case
-   *          of {@link #MODEL_LINK_TYPE} connection
-   * @param zoomLevel
-   *          zoom level on the model where redirection should be placed in case
-   *          of {@link #MODEL_LINK_TYPE} connection
-   * @param linkType
-   *          type of the connection. This will define implementation of
-   *          {@link OverviewImage} that will be used. For now three values are
-   *          acceptable: {@link #MODEL_LINK_TYPE}, {@link #IMAGE_LINK_TYPE},
-   *          {@link #SEARCH_LINK_TYPE}.
-   * @param images
-   *          list of images that are available
-   * @param models
-   *          list of models that are available
-   * @throws InvalidCoordinatesFile
-   *           thrown when one of the input parameters is invalid
+   * @param filename   filename name of the image
+   * @param polygon    polygon polygon describing link
+   * @param linkTarget defines target that should be invoked when the link is activated.
+   *                   This target is either a file name (in case of
+   *                   {@link #MODEL_LINK_TYPE} or {@link #IMAGE_LINK_TYPE}) or a search
+   *                   string (in case of {@link #SEARCH_LINK_TYPE}).
+   * @param coord      coordinates on the model where redirection should be placed in case
+   *                   of {@link #MODEL_LINK_TYPE} connection
+   * @param zoomLevel  zoom level on the model where redirection should be placed in case
+   *                   of {@link #MODEL_LINK_TYPE} connection
+   * @param linkType   type of the connection. This will define implementation of
+   *                   {@link OverviewImage} that will be used. For now three values are
+   *                   acceptable: {@link #MODEL_LINK_TYPE}, {@link #IMAGE_LINK_TYPE},
+   *                   {@link #SEARCH_LINK_TYPE}.
+   * @param images     list of images that are available
+   * @param models     list of models that are available
+   * @throws InvalidCoordinatesFile thrown when one of the input parameters is invalid
    */
-  private void createOverviewLink(final String filename, final String polygon, final String linkTarget, final String coord, final String zoomLevel,
-      final String linkType, final List<OverviewImage> images, final Map<String, Model> models) throws InvalidCoordinatesFile {
+  private void createOverviewLink(final String filename,
+                                  final String polygon,
+                                  final String linkTarget,
+                                  final String coord,
+                                  final String zoomLevel,
+                                  final String linkType,
+                                  final List<OverviewImage> images,
+                                  final Map<String, ModelData> models) throws InvalidCoordinatesFile {
     OverviewImage image = null;
     for (final OverviewImage oi : images) {
       if (oi.getFilename().equalsIgnoreCase(filename)) {
@@ -494,7 +459,7 @@ public class OverviewParser {
     }
     OverviewLink ol = null;
     if (linkType.equals(MODEL_LINK_TYPE)) {
-      Model model = models.get(linkTarget.toLowerCase());
+      ModelData model = models.get(linkTarget.toLowerCase());
       if (model == null) {
         throw new InvalidCoordinatesFile("Unknown model in \"" + COORDINATES_FILENAME + "\" file: " + linkTarget);
       }
diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
index b5f450031f4f9d21313458eba8b0fdb377bc8c4e..51e60b8766d5525509f02858b723c69d49cb5145 100644
--- a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
+++ b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java
@@ -7,6 +7,7 @@ import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
 import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
 import lcsb.mapviewer.converter.zip.ZipEntryFile;
 import lcsb.mapviewer.model.Project;
+import lcsb.mapviewer.model.cache.UploadedFileEntry;
 import lcsb.mapviewer.model.map.InconsistentModelException;
 import lcsb.mapviewer.model.map.compartment.Compartment;
 import lcsb.mapviewer.model.map.compartment.SquareCompartment;
@@ -30,6 +31,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Enumeration;
@@ -72,17 +74,6 @@ public class ProjectFactory {
   public Project create(final ComplexZipConverterParams params, final Project project)
       throws InvalidInputDataExecption, ConverterException {
     try {
-      Model model = converter.createModel(params);
-
-      Set<Model> models = new HashSet<>();
-      models.add(model);
-      models.addAll(model.getSubmodels());
-
-      project.addModel(model);
-      for (final Model m : model.getSubmodels()) {
-        project.addModel(m);
-      }
-
       ZipFile zipFile = params.getZipFile();
       Enumeration<? extends ZipEntry> entries;
 
@@ -103,10 +94,12 @@ public class ProjectFactory {
           }
         }
       }
+      converter.createModel(params, project);
+
+
       if (!imageEntries.isEmpty()) {
         OverviewParser parser = new OverviewParser();
-        project
-            .addOverviewImages(parser.parseOverviewLinks(models, imageEntries, params.getVisualizationDir(), zipFile));
+        project.addOverviewImages(parser.parseOverviewLinks(project.getModels(), imageEntries, params.getVisualizationDir(), zipFile));
       }
       if (!project.getGlyphs().isEmpty()) {
         assignGlyphsToElements(project);
@@ -151,7 +144,7 @@ public class ProjectFactory {
     String[] lines = notes.split("[\n\r]+");
     StringBuilder result = new StringBuilder();
     for (final String line : lines) {
-      if (!line.startsWith("Glyph:")) {
+      if (!line.startsWith(ComplexZipConverter.GLYPH_PREFIX)) {
         result.append(line).append("\n");
       }
     }
@@ -161,8 +154,8 @@ public class ProjectFactory {
   Glyph extractGlyph(final Project project, final String notes) throws InvalidGlyphFile {
     String[] lines = notes.split("[\n\r]+");
     for (final String line : lines) {
-      if (line.startsWith("Glyph:")) {
-        String glyphString = line.replace("Glyph:", "").trim().toLowerCase();
+      if (line.startsWith(ComplexZipConverter.GLYPH_PREFIX)) {
+        String glyphString = line.replace(ComplexZipConverter.GLYPH_PREFIX, "").trim().toLowerCase();
         for (final Glyph glyph : project.getGlyphs()) {
           if (glyph.getFile().getOriginalFileName().toLowerCase().equalsIgnoreCase(glyphString)) {
             return glyph;
@@ -189,12 +182,28 @@ public class ProjectFactory {
 
       addModelFileToZip(mapping, "submaps/mapping." + converter.getFileExtensions().get(0), converter, zos);
 
+      for (ModelData model : project.getModels()) {
+        addGlyphsToZip(model.getModel(), zos);
+      }
+
     } catch (IOException ioe) {
       throw new ConverterException(ioe);
     }
     return byteArrayOutputStream.toByteArray();
   }
 
+  private void addGlyphsToZip(final Model model, final ZipOutputStream zos) throws IOException {
+    for (Element element : model.getElements()) {
+      if (element.getGlyph() != null) {
+        UploadedFileEntry uploadedFileEntry = element.getGlyph().getFile();
+        ZipEntry entry = new ZipEntry("glyphs/" + new File(uploadedFileEntry.getOriginalFileName()).getName());
+        zos.putNextEntry(entry);
+        zos.write(uploadedFileEntry.getFileContent());
+        zos.closeEntry();
+      }
+    }
+  }
+
   private int idCounter = 0;
 
   private Model createMappingModel(final Set<ModelData> models) {
@@ -228,7 +237,16 @@ public class ProjectFactory {
 
           Species sourceElement = getCompartmentChildForConnection(parentElement.getElementId(), mappingParentCompartment);
           String notes = sourceElement.getNotes();
-          notes += ComplexZipConverter.IMMEDIATE_LINK_PREFIX + parentElement.getImmediateLink() + "\n";
+          notes += "\n" + ComplexZipConverter.IMMEDIATE_LINK_PREFIX + parentElement.getImmediateLink();
+          sourceElement.setNotes(notes);
+        }
+        if (parentElement.getGlyph() != null) {
+          UploadedFileEntry uploadedFileEntry = parentElement.getGlyph().getFile();
+          Compartment mappingParentCompartment = getCompartmentForConnection(parentModel.getName(), mapping);
+
+          Species sourceElement = getCompartmentChildForConnection(parentElement.getElementId(), mappingParentCompartment);
+          String notes = sourceElement.getNotes();
+          notes += "\n" + ComplexZipConverter.GLYPH_PREFIX + "glyphs/" + new File(uploadedFileEntry.getOriginalFileName()).getName();
           sourceElement.setNotes(notes);
         }
       }
diff --git a/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterTest.java b/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterTest.java
index 5850f0dad1bc7462da0d3249e7b4de1a83f6f784..bea7c1a8885b1d67c9bc966c546ece254a9cb132 100644
--- a/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterTest.java
+++ b/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterTest.java
@@ -4,6 +4,7 @@ import lcsb.mapviewer.common.exception.InvalidArgumentException;
 import lcsb.mapviewer.common.exception.InvalidClassException;
 import lcsb.mapviewer.converter.zip.LayoutZipEntryFile;
 import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
+import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelSubmodelConnection;
 import lcsb.mapviewer.model.map.model.SubmodelType;
@@ -40,7 +41,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
   }
 
   @Test
-  public void testConstructor3() throws Exception {
+  public void testConstructor3() {
     new ComplexZipConverter(MockConverter.class);
   }
 
@@ -54,7 +55,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s2.xml", "s2", false, false, SubmodelType.PATHWAY));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    Model model = converter.createModel(params);
+    Model model = converter.createModel(params, new Project());
     assertNotNull(model);
     assertEquals("main", model.getName());
     assertEquals(3, model.getSubmodelConnections().size());
@@ -108,7 +109,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s2.xml", "s2", false, false, SubmodelType.PATHWAY));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    Model model = converter.createModel(params);
+    Model model = converter.createModel(params, new Project());
     assertNotNull(model);
     assertEquals("main", model.getName());
     assertEquals(3, model.getSubmodelConnections().size());
@@ -160,7 +161,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s1.xml", "s1", false, false, SubmodelType.DOWNSTREAM_TARGETS));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    converter.createModel(params);
+    converter.createModel(params, new Project());
   }
 
   @Test(expected = InvalidArgumentException.class)
@@ -174,7 +175,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s2.xml", "s2", false, false, SubmodelType.PATHWAY));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    converter.createModel(params);
+    converter.createModel(params, new Project());
   }
 
   @Test(expected = InvalidArgumentException.class)
@@ -188,7 +189,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s2.xml", "s2", false, false, SubmodelType.PATHWAY));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    converter.createModel(params);
+    converter.createModel(params, new Project());
   }
 
   @Test(expected = InvalidArgumentException.class)
@@ -202,7 +203,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s2.xml", "s2", false, false, SubmodelType.PATHWAY));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    converter.createModel(params);
+    converter.createModel(params, new Project());
   }
 
   @Test
@@ -216,7 +217,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s2.xml", "s2", false, false, SubmodelType.PATHWAY));
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", "", false, false, null));
-    Model model = converter.createModel(params);
+    Model model = converter.createModel(params, new Project());
 
     assertNotNull(model);
 
@@ -247,7 +248,7 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.entry(new ModelZipEntryFile("s3.xml", "s3", false, false, SubmodelType.UNKNOWN));
     params.entry(new ModelZipEntryFile("mapping.xml", "", false, false, null));
     params.entry(new ModelZipEntryFile("blabla.xml", "s3", false, false, SubmodelType.UNKNOWN));
-    converter.createModel(params);
+    converter.createModel(params, new Project());
   }
 
   @Test(expected = InvalidArgumentException.class)
@@ -258,11 +259,11 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
     params.zipFile(new ZipFile("testFiles/invalid_mapping.zip"));
     params.entry(new ModelZipEntryFile("main.xml", "main", true, false, null));
     params.entry(new ModelZipEntryFile("mapping.xml", null, false, true, null));
-    converter.createModel(params);
+    converter.createModel(params, new Project());
   }
 
   @Test
-  public void testIsIgnoredFileForMac() throws Exception {
+  public void testIsIgnoredFileForMac() {
     ComplexZipConverter converter = new ComplexZipConverter(MockConverter.class);
     assertTrue(converter.isIgnoredFile("__MACOSX/.desc"));
     assertTrue(converter.isIgnoredFile(".DS_Store"));
@@ -272,13 +273,13 @@ public class ComplexZipConverterTest extends ConverterTestFunctions {
   }
 
   @Test
-  public void testIsIgnoredFileForOldMacEntries() throws Exception {
+  public void testIsIgnoredFileForOldMacEntries() {
     ComplexZipConverter converter = new ComplexZipConverter(MockConverter.class);
     assertTrue(converter.isIgnoredFile(".DS_Store/.desc"));
   }
 
   @Test
-  public void testIsIgnoredFileForValidFiles() throws Exception {
+  public void testIsIgnoredFileForValidFiles() {
     ComplexZipConverter converter = new ComplexZipConverter(MockConverter.class);
     assertFalse(converter.isIgnoredFile("mapping.xml"));
   }
diff --git a/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java b/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java
index 02ddc58b20808b342eaa3c85d701483c48e696fe..1485ed962095df0b1426cb10c0d77fa66a1479c4 100644
--- a/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java
+++ b/converter/src/test/java/lcsb/mapviewer/converter/OverviewParserTest.java
@@ -1,8 +1,16 @@
 package lcsb.mapviewer.converter;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
+import lcsb.mapviewer.model.map.OverviewImage;
+import lcsb.mapviewer.model.map.OverviewLink;
+import lcsb.mapviewer.model.map.OverviewModelLink;
+import lcsb.mapviewer.model.map.model.Model;
+import lcsb.mapviewer.model.map.model.ModelData;
+import lcsb.mapviewer.model.map.model.ModelFullIndexed;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.awt.geom.Point2D;
 import java.io.File;
@@ -16,23 +24,15 @@ import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
-import org.apache.commons.io.FileUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import lcsb.mapviewer.converter.zip.ImageZipEntryFile;
-import lcsb.mapviewer.model.map.OverviewImage;
-import lcsb.mapviewer.model.map.OverviewLink;
-import lcsb.mapviewer.model.map.OverviewModelLink;
-import lcsb.mapviewer.model.map.model.Model;
-import lcsb.mapviewer.model.map.model.ModelFullIndexed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 public class OverviewParserTest extends ConverterTestFunctions {
 
   private static final String TEST_FILES_VALID_OVERVIEW_ZIP = "testFiles/valid_overview.zip";
   private static final String TEST_FILES_VALID_OVERVIEW_CASE_SENSITIVE_ZIP = "testFiles/valid_overview_case_sensitive.zip";
-  private OverviewParser parser = new OverviewParser();
+  private final OverviewParser parser = new OverviewParser();
 
   @Before
   public void setUp() throws Exception {
@@ -44,7 +44,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
 
   @Test
   public void testParsingValidFile() throws Exception {
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
     List<ImageZipEntryFile> imageEntries = createImageEntries(TEST_FILES_VALID_OVERVIEW_ZIP);
     List<OverviewImage> result = parser.parseOverviewLinks(models, imageEntries, null,
         new ZipFile(TEST_FILES_VALID_OVERVIEW_ZIP));
@@ -66,8 +66,8 @@ public class OverviewParserTest extends ConverterTestFunctions {
     assertTrue(link instanceof OverviewModelLink);
 
     OverviewModelLink modelLink = (OverviewModelLink) link;
-    Model mainModel = models.iterator().next();
-    assertEquals(mainModel.getModelData(), modelLink.getLinkedModel());
+    ModelData mainModel = models.iterator().next();
+    assertEquals(mainModel, modelLink.getLinkedModel());
     assertEquals((Integer) 10, modelLink.getxCoord());
     assertEquals((Integer) 10, modelLink.getyCoord());
     assertEquals((Integer) 3, modelLink.getZoomLevel());
@@ -75,7 +75,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
 
   @Test
   public void testParsingValidCaseSensitiveFile() throws Exception {
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
     List<ImageZipEntryFile> imageEntries = createImageEntries(TEST_FILES_VALID_OVERVIEW_CASE_SENSITIVE_ZIP);
     for (final ImageZipEntryFile imageZipEntryFile : imageEntries) {
       imageZipEntryFile.setFilename(imageZipEntryFile.getFilename().toLowerCase());
@@ -99,8 +99,8 @@ public class OverviewParserTest extends ConverterTestFunctions {
     assertTrue(link instanceof OverviewModelLink);
 
     OverviewModelLink modelLink = (OverviewModelLink) link;
-    Model mainModel = models.iterator().next();
-    assertEquals(mainModel.getModelData(), modelLink.getLinkedModel());
+    ModelData mainModel = models.iterator().next();
+    assertEquals(mainModel, modelLink.getLinkedModel());
     assertEquals((Integer) 10, modelLink.getxCoord());
     assertEquals((Integer) 10, modelLink.getyCoord());
     assertEquals((Integer) 3, modelLink.getZoomLevel());
@@ -109,8 +109,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
   private List<ImageZipEntryFile> createImageEntries(final String string) throws IOException {
     List<ImageZipEntryFile> result = new ArrayList<>();
 
-    ZipFile zipFile = new ZipFile(string);
-    try {
+    try (ZipFile zipFile = new ZipFile(string)) {
       Enumeration<? extends ZipEntry> entries = zipFile.entries();
       while (entries.hasMoreElements()) {
         ZipEntry entry = entries.nextElement();
@@ -119,14 +118,12 @@ public class OverviewParserTest extends ConverterTestFunctions {
         }
       }
       return result;
-    } finally {
-      zipFile.close();
     }
   }
 
   @Test
   public void testParsingValidFile2() throws Exception {
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
     String tmpDir = Files.createTempDirectory("tmp").toFile().getAbsolutePath();
 
@@ -145,7 +142,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
   @Test(expected = InvalidOverviewFile.class)
   public void testParsingInvalidFile1() throws Exception {
     List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_1.zip");
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
     parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/invalid_overview_1.zip"));
   }
@@ -153,7 +150,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
   @Test(expected = InvalidOverviewFile.class)
   public void testParsingInvalidFile2() throws Exception {
     List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_2.zip");
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
     parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/invalid_overview_2.zip"));
   }
@@ -161,16 +158,16 @@ public class OverviewParserTest extends ConverterTestFunctions {
   @Test(expected = InvalidOverviewFile.class)
   public void testParsingInvalidFile3() throws Exception {
     List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/invalid_overview_3.zip");
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
     parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/invalid_overview_3.zip"));
   }
 
-  private Set<Model> createValidTestMapModel() {
-    Set<Model> result = new HashSet<>();
+  private Set<ModelData> createValidTestMapModel() {
+    Set<ModelData> result = new HashSet<>();
     Model model = new ModelFullIndexed(null);
     model.setName("main");
-    result.add(model);
+    result.add(model.getModelData());
     return result;
   }
 
@@ -181,9 +178,9 @@ public class OverviewParserTest extends ConverterTestFunctions {
   public void testParseInvalidCoordinates() throws Exception {
     String invalidCoordinates = "test.png\t10,10 100,10 100,100 10,10\tmain.xml\t10,10\t3\n"
         + "test.png\t10,10 10,400 400,400 400,10\tmain.xml\t10,10\t4";
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
-    List<OverviewImage> images = new ArrayList<OverviewImage>();
+    List<OverviewImage> images = new ArrayList<>();
     OverviewImage oi = new OverviewImage();
     oi.setFilename("test.png");
     oi.setWidth(1000);
@@ -198,9 +195,9 @@ public class OverviewParserTest extends ConverterTestFunctions {
     String invalidCoordinates = "FILE\tPOLYGON\tLINK_TARGET\tMODEL_COORDINATES\tMODEL_ZOOM_LEVEL\tLINK_TYPE\n"
         + "test.png\t10,10 100,10 100,100 10,10\tmain.xml\t10,10\t3\tMODEL\n"
         + "test.png\t200,200 200,400 400,400 400,200\tmain.xml\t10,10\t4\tMODEL";
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
-    List<OverviewImage> images = new ArrayList<OverviewImage>();
+    List<OverviewImage> images = new ArrayList<>();
     OverviewImage oi = new OverviewImage();
     oi.setFilename("test.png");
     oi.setWidth(1000);
@@ -215,7 +212,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
   @Test
   public void testParseValidComplexCoordinates() throws Exception {
     String invalidCoordinates = FileUtils.readFileToString(new File("testFiles/coordinates.txt"), "UTF-8");
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
     List<OverviewImage> images = new ArrayList<>();
     OverviewImage oi = new OverviewImage();
@@ -239,7 +236,7 @@ public class OverviewParserTest extends ConverterTestFunctions {
   @Test
   public void testParseValidComplexCoordinatesWithExtraImages() throws Exception {
     String invalidCoordinates = FileUtils.readFileToString(new File("testFiles/coordinates.txt"), "UTF-8");
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
 
     List<OverviewImage> images = new ArrayList<>();
     OverviewImage oi = new OverviewImage();
@@ -267,10 +264,10 @@ public class OverviewParserTest extends ConverterTestFunctions {
 
   @Test
   public void testJsonCoordinates() throws Exception {
-    Set<Model> models = createValidTestMapModel();
+    Set<ModelData> models = createValidTestMapModel();
     Model child = new ModelFullIndexed(null);
     child.setName("child");
-    models.add(child);
+    models.add(child.getModelData());
 
     List<ImageZipEntryFile> imageEntries = createImageEntries("testFiles/valid_overview_with_json.zip");
     List<OverviewImage> result = parser.parseOverviewLinks(models, imageEntries, null, new ZipFile("testFiles/valid_overview_with_json.zip"));
diff --git a/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java b/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java
index 327f984bdc2ab4ec4d4574fdf6c1e679b6800550..ea88c764532c884c45bd397a0572489ff5e9fe9c 100644
--- a/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java
+++ b/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java
@@ -149,7 +149,7 @@ public class ProjectFactoryTest extends ConverterTestFunctions {
   public void testParseGlyphsAndPutThemAsElementGlyphs() throws Exception {
     Model model = new ModelFullIndexed(null);
     final GenericProtein protein = createProtein();
-    protein.setNotes("Glyph: glyphs/g1.png");
+    protein.setNotes(ComplexZipConverter.GLYPH_PREFIX + "glyphs/g1.png");
     model.addElement(protein);
 
     MockConverter.modelToBeReturned = model;
@@ -188,7 +188,7 @@ public class ProjectFactoryTest extends ConverterTestFunctions {
     final Layer layer = new Layer();
 
     final LayerText text = new LayerText();
-    text.setNotes("Glyph: glyphs/g1.png");
+    text.setNotes(ComplexZipConverter.GLYPH_PREFIX + " glyphs/g1.png");
     layer.addLayerText(text);
     model.addLayer(layer);
 
diff --git a/model-command/src/test/java/lcsb/mapviewer/commands/ColorModelCommandTest.java b/model-command/src/test/java/lcsb/mapviewer/commands/ColorModelCommandTest.java
index 5d2fdaadc98bb325e3952ee48b079112c7847e80..2a3b2cebfcfe2ca83126c712023003ee6afd0418 100644
--- a/model-command/src/test/java/lcsb/mapviewer/commands/ColorModelCommandTest.java
+++ b/model-command/src/test/java/lcsb/mapviewer/commands/ColorModelCommandTest.java
@@ -45,7 +45,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testAliasMatchWithInvalidElementId() throws Exception {
+  public void testAliasMatchWithInvalidElementId() {
     GenericDataOverlayEntry colorSchema = new GenericDataOverlayEntry();
     colorSchema.setName(null);
     colorSchema.setElementId("1");
@@ -65,7 +65,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testSpeciesMatchWithProteinType() throws Exception {
+  public void testSpeciesMatchWithProteinType() {
     GenericDataOverlayEntry colorSchema = new GenericDataOverlayEntry();
     colorSchema.setName("s1");
     colorSchema.addType(Protein.class);
@@ -82,7 +82,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testSpeciesMatchWithMiriamData() throws Exception {
+  public void testSpeciesMatchWithMiriamData() {
     GenericDataOverlayEntry colorSchema = new GenericDataOverlayEntry();
     colorSchema.setName("s1");
     colorSchema.addMiriamData(new MiriamData(MiriamType.HGNC_SYMBOL, "SNCA"));
@@ -105,7 +105,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testSpeciesMatchWithMiriamDataDifferentAnnotator() throws Exception {
+  public void testSpeciesMatchWithMiriamDataDifferentAnnotator() {
     GenericDataOverlayEntry colorSchema = new GenericDataOverlayEntry();
     colorSchema.setName("s1");
     colorSchema.addMiriamData(new MiriamData(MiriamType.HGNC_SYMBOL, "SNCA"));
@@ -123,7 +123,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionMatchWithProteinMiriamData() throws Exception {
+  public void testReactionMatchWithProteinMiriamData() {
     GenericDataOverlayEntry colorSchema = new GenericDataOverlayEntry();
     colorSchema.addMiriamData(new MiriamData(MiriamType.HGNC_SYMBOL, "SNCA"));
 
@@ -138,7 +138,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionMatchWithMiriamData() throws Exception {
+  public void testReactionMatchWithMiriamData() {
     GenericDataOverlayEntry colorSchema = new GenericDataOverlayEntry();
     colorSchema.addMiriamData(new MiriamData(MiriamType.PUBMED, "1234"));
 
@@ -155,7 +155,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
 
 
   @Test
-  public void testGetModifiedElements() throws Exception {
+  public void testGetModifiedElements() {
     Reaction reaction = new Reaction("re");
     reaction.addMiriamData(new MiriamData(MiriamType.PUBMED, "1234"));
 
@@ -174,7 +174,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testApplyColorToReaction() throws Exception {
+  public void testApplyColorToReaction() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -249,7 +249,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testColoring2() throws Exception {
+  public void testColoring2() {
     Model model = createSimpleModel();
     GenericProtein protein = createProteinWithLayout();
     protein.addMiriamData(new MiriamData(MiriamType.HGNC, "11138"));
@@ -280,7 +280,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testColorTheSameElementTwiceUsingDifferentSelector() throws Exception {
+  public void testColorTheSameElementTwiceUsingDifferentSelector() {
     Model model = createSimpleModel();
     GenericProtein protein = createProteinWithLayout();
     protein.setName("SNCA");
@@ -304,7 +304,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionColoring1() throws Exception {
+  public void testReactionColoring1() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -326,7 +326,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionColoring2() throws Exception {
+  public void testReactionColoring2() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -335,7 +335,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
     Reaction reaction = createReactionWithLayout(protein1, protein1);
     model.addReaction(reaction);
 
-    Collection<DataOverlayEntry> schemas = new ArrayList<DataOverlayEntry>();
+    Collection<DataOverlayEntry> schemas = new ArrayList<>();
     DataOverlayEntry schema = new GenericDataOverlayEntry();
     schema.setElementId(reaction.getIdReaction());
     schema.setColor(Color.RED);
@@ -351,7 +351,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionColoring3() throws Exception {
+  public void testReactionColoring3() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -360,7 +360,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
     Reaction reaction = createReactionWithLayout(protein1, protein1);
     model.addReaction(reaction);
 
-    Collection<DataOverlayEntry> schemas = new ArrayList<DataOverlayEntry>();
+    Collection<DataOverlayEntry> schemas = new ArrayList<>();
     DataOverlayEntry schema = new GenericDataOverlayEntry();
     schema.setElementId(reaction.getIdReaction());
     schema.setValue(-1.0);
@@ -376,7 +376,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionColoring4() throws Exception {
+  public void testReactionColoring4() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -386,7 +386,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
     reaction.addMiriamData(new MiriamData(MiriamType.PUBMED, "12345"));
     model.addReaction(reaction);
 
-    Collection<DataOverlayEntry> schemas = new ArrayList<DataOverlayEntry>();
+    Collection<DataOverlayEntry> schemas = new ArrayList<>();
     DataOverlayEntry schema = new GenericDataOverlayEntry();
     schema.addMiriamData(new MiriamData(MiriamType.PUBMED, "12345"));
     schema.setValue(-1.0);
@@ -403,7 +403,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testColoringComplexModel() throws Exception {
+  public void testColoringComplexModel() {
     Model model = createSimpleModel();
     Model model2 = createSimpleModel();
 
@@ -420,8 +420,10 @@ public class ColorModelCommandTest extends CommandTestFunctions {
     Model coloredModel2 = coloredModel.getSubmodelConnections().iterator().next().getSubmodel().getModel();
     Model coloredModel3 = coloredModel.getSubmodelByConnectionName("BLA");
 
-    assertNotEquals(coloredModel2.getElementByElementId(protein1.getElementId()).getFillColor(), model2.getElementByElementId(protein1.getElementId()).getFillColor());
-    assertNotEquals(coloredModel3.getElementByElementId(protein1.getElementId()).getFillColor(), model2.getElementByElementId(protein1.getElementId()).getFillColor());
+    assertNotEquals(coloredModel2.getElementByElementId(protein1.getElementId()).getFillColor(),
+        model2.getElementByElementId(protein1.getElementId()).getFillColor());
+    assertNotEquals(coloredModel3.getElementByElementId(protein1.getElementId()).getFillColor(),
+        model2.getElementByElementId(protein1.getElementId()).getFillColor());
   }
 
   @Test
@@ -453,7 +455,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testColoredReactions() throws Exception {
+  public void testColoredReactions() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -477,7 +479,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testColoredReactions2() throws Exception {
+  public void testColoredReactions2() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -506,7 +508,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionColoringWithModelNotMatching() throws Exception {
+  public void testReactionColoringWithModelNotMatching() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -529,7 +531,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testReactionColoringWithModelMatch() throws Exception {
+  public void testReactionColoringWithModelMatch() {
     Model model = createSimpleModel();
     Protein protein1 = createProteinWithLayout();
     Protein protein2 = createProteinWithLayout();
@@ -552,7 +554,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testAliasColoringWithModelNotMatching() throws Exception {
+  public void testAliasColoringWithModelNotMatching() {
     Model model = createSimpleModel();
     Protein p1 = createProtein();
     p1.setName("CNC");
@@ -571,7 +573,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testAliasColoringWithModelMatch() throws Exception {
+  public void testAliasColoringWithModelMatch() {
     Model model = createSimpleModel();
     Protein p1 = createProtein();
     p1.setName("CNC");
@@ -606,7 +608,7 @@ public class ColorModelCommandTest extends CommandTestFunctions {
   }
 
   @Test
-  public void testAliasColoringWithElementIdMatch() throws Exception {
+  public void testAliasColoringWithElementIdMatch() {
     Model model = createSimpleModel();
     model.addElement(createProtein());
 
diff --git a/model/src/main/java/lcsb/mapviewer/model/ProjectComparator.java b/model/src/main/java/lcsb/mapviewer/model/ProjectComparator.java
index b1862ab7964b2350d3a4aa87e535e0f713b22ec8..776c2556b7f86616cadf9afc6834cc3d0595d3fe 100644
--- a/model/src/main/java/lcsb/mapviewer/model/ProjectComparator.java
+++ b/model/src/main/java/lcsb/mapviewer/model/ProjectComparator.java
@@ -3,23 +3,29 @@ package lcsb.mapviewer.model;
 import lcsb.mapviewer.common.Comparator;
 import lcsb.mapviewer.common.Configuration;
 import lcsb.mapviewer.common.comparator.SetComparator;
+import lcsb.mapviewer.model.map.layout.graphics.Glyph;
+import lcsb.mapviewer.model.map.layout.graphics.GlyphComparator;
 import lcsb.mapviewer.model.map.model.ModelComparator;
 import lcsb.mapviewer.model.map.model.ModelData;
 import lcsb.mapviewer.model.map.model.ModelDataComparator;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import java.util.HashSet;
+
 public class ProjectComparator extends Comparator<Project> {
 
   private static final Logger logger = LogManager.getLogger();
 
   private final ModelComparator modelComparator;
   private final SetComparator<ModelData> modelDataSetComparator;
+  private final SetComparator<Glyph> glyphSetComparator;
 
   public ProjectComparator(final double epsilon) {
     super(Project.class);
     modelComparator = new ModelComparator(epsilon);
     modelDataSetComparator = new SetComparator<>(new ModelDataComparator(epsilon));
+    glyphSetComparator = new SetComparator<>(new GlyphComparator());
   }
 
   public ProjectComparator() {
@@ -28,7 +34,13 @@ public class ProjectComparator extends Comparator<Project> {
 
   @Override
   protected int internalCompare(final Project arg0, final Project arg1) {
-    int compareResult = modelComparator.compare(arg0.getTopModel(), arg1.getTopModel());
+    int compareResult = glyphSetComparator.compare(new HashSet<>(arg0.getGlyphs()), new HashSet<>(arg1.getGlyphs()));
+    if (compareResult != 0) {
+      logger.debug("Glyphs different");
+      return compareResult;
+    }
+
+    compareResult = modelComparator.compare(arg0.getTopModel(), arg1.getTopModel());
     if (compareResult != 0) {
       logger.debug("Top model different: {}, {}", arg0.getTopModel(), arg1.getTopModel());
       return compareResult;
@@ -38,6 +50,7 @@ public class ProjectComparator extends Comparator<Project> {
       logger.debug("Models different");
       return modelDataSetComparator.compare(arg0.getModels(), arg1.getModels());
     }
+
     return 0;
   }
 
diff --git a/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntryComparator.java b/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntryComparator.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dda81abcb2ae007c29ca5aaba6f5ad1e1f87e4c
--- /dev/null
+++ b/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntryComparator.java
@@ -0,0 +1,32 @@
+package lcsb.mapviewer.model.cache;
+
+import lcsb.mapviewer.common.Comparator;
+import lcsb.mapviewer.common.comparator.StringComparator;
+
+import java.util.Arrays;
+
+public class UploadedFileEntryComparator extends Comparator<UploadedFileEntry> {
+
+  public UploadedFileEntryComparator() {
+    super(UploadedFileEntry.class, true);
+  }
+
+
+  @Override
+  protected int internalCompare(final UploadedFileEntry arg0, final UploadedFileEntry arg1) {
+    StringComparator stringComparator = new StringComparator();
+
+    if (stringComparator.compare(arg0.getOriginalFileName(), arg1.getOriginalFileName()) != 0) {
+      logger.debug("Original file name is different: {} != {}", arg0.getOriginalFileName(), arg1.getOriginalFileName());
+      return stringComparator.compare(arg0.getOriginalFileName(), arg1.getOriginalFileName());
+    }
+
+    if (!Arrays.equals(arg0.getFileContent(), arg1.getFileContent())) {
+      logger.debug("Glyph content different for file {}.", arg0.getOriginalFileName());
+      return -1;
+    }
+
+    return 0;
+  }
+
+}
diff --git a/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/GlyphComparator.java b/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/GlyphComparator.java
new file mode 100644
index 0000000000000000000000000000000000000000..e217a4bc5d86d30bfb5e4fe12435bf3c95fea74e
--- /dev/null
+++ b/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/GlyphComparator.java
@@ -0,0 +1,24 @@
+package lcsb.mapviewer.model.map.layout.graphics;
+
+import lcsb.mapviewer.common.Comparator;
+import lcsb.mapviewer.model.cache.UploadedFileEntryComparator;
+
+public class GlyphComparator extends Comparator<Glyph> {
+
+
+  public GlyphComparator() {
+    super(Glyph.class, true);
+  }
+
+  @Override
+  protected int internalCompare(final Glyph arg0, final Glyph arg1) {
+    UploadedFileEntryComparator fileComparator = new UploadedFileEntryComparator();
+
+    if (fileComparator.compare(arg0.getFile(), arg1.getFile()) != 0) {
+      return fileComparator.compare(arg0.getFile(), arg1.getFile());
+    }
+
+    return 0;
+  }
+
+}
diff --git a/model/src/main/java/lcsb/mapviewer/model/map/species/ElementComparator.java b/model/src/main/java/lcsb/mapviewer/model/map/species/ElementComparator.java
index 72997425c9a9cc13cb463d4658542bdd5af140d1..e4cd56e75133735ca72d43f0461967a92b483846 100644
--- a/model/src/main/java/lcsb/mapviewer/model/map/species/ElementComparator.java
+++ b/model/src/main/java/lcsb/mapviewer/model/map/species/ElementComparator.java
@@ -14,6 +14,7 @@ import lcsb.mapviewer.model.graphics.VerticalAlign;
 import lcsb.mapviewer.model.map.MiriamData;
 import lcsb.mapviewer.model.map.MiriamDataComparator;
 import lcsb.mapviewer.model.map.compartment.CompartmentComparator;
+import lcsb.mapviewer.model.map.layout.graphics.GlyphComparator;
 import lcsb.mapviewer.model.map.model.ElementSubmodelConnectionComparator;
 
 /**
@@ -63,6 +64,7 @@ public class ElementComparator extends Comparator<Element> {
     StringComparator stringComparator = new StringComparator();
     ColorComparator colorComparator = new ColorComparator();
     DoubleComparator doubleComparator = new DoubleComparator(epsilon);
+    GlyphComparator glyphComparator = new GlyphComparator();
 
     if (stringComparator.compare(arg0.getElementId(), arg1.getElementId()) != 0) {
       logger.debug("ElementId different: {}, {}", arg0.getElementId(), arg1.getElementId());
@@ -216,6 +218,11 @@ public class ElementComparator extends Comparator<Element> {
       return stringComparator.compare(arg0.getImmediateLink(), arg1.getImmediateLink());
     }
 
+    if (glyphComparator.compare(arg0.getGlyph(), arg1.getGlyph()) != 0) {
+      logger.debug("Glyph different: {}, {}", arg0.getGlyph(), arg1.getGlyph());
+      return glyphComparator.compare(arg0.getGlyph(), arg1.getGlyph());
+    }
+    
     return 0;
   }