Merge branch 'alpha/JAL-3362_Jalview_212_alpha' into alpha/merge_212_JalviewJS_2112
[jalview.git] / src / jalview / renderer / AnnotationRenderer.java
index c0ddf71..70991c6 100644 (file)
@@ -1,6 +1,6 @@
 /*
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * 
  * This file is part of Jalview.
  * 
 package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
 package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
+import jalview.analysis.CodingUtils;
+import jalview.analysis.Rna;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.HiddenMarkovModel;
+import jalview.datamodel.ProfilesI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeI;
+import jalview.schemes.NucleotideColourScheme;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.ZappoColourScheme;
+import jalview.util.Platform;
+import jalview.workers.InformationThread;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -35,64 +45,160 @@ import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Image;
-import java.awt.font.LineMetrics;
 import java.awt.geom.AffineTransform;
 import java.awt.image.ImageObserver;
 import java.util.BitSet;
 import java.util.Hashtable;
 
 import java.awt.geom.AffineTransform;
 import java.awt.image.ImageObserver;
 import java.util.BitSet;
 import java.util.Hashtable;
 
-import com.stevesoft.pat.Regex;
-
 public class AnnotationRenderer
 {
 public class AnnotationRenderer
 {
+  private static final int UPPER_TO_LOWER = 'a' - 'A'; // 32
+
+  private static final int CHAR_A = 'A'; // 65
+
+  private static final int CHAR_Z = 'Z'; // 90
+
   /**
    * flag indicating if timing and redraw parameter info should be output
    */
   private final boolean debugRedraw;
 
   /**
    * flag indicating if timing and redraw parameter info should be output
    */
   private final boolean debugRedraw;
 
+  private int charWidth, endRes, charHeight;
+
+  private boolean validCharWidth, hasHiddenColumns;
+
+  private FontMetrics fm;
+
+  private final boolean USE_FILL_ROUND_RECT = Platform.isAMacAndNotJS();
+
+  // todo remove these flags, read from group/viewport where needed
+  boolean av_renderHistogram = true;
+
+  boolean av_renderProfile = true;
+
+  boolean av_normaliseProfile = false;
+
+  boolean av_infoHeight = false;
+
+  ResidueShaderI profcolour = null;
+
+  private ColumnSelection columnSelection;
+
+  private HiddenColumns hiddenColumns;
+
+  private ProfilesI hconsensus;
+
+  private Hashtable<String, Object>[] complementConsensus;
+
+  private Hashtable<String, Object>[] hStrucConsensus;
+
+  private boolean av_ignoreGapsConsensus;
+
+  private boolean av_ignoreBelowBackground;
+
+  /**
+   * attributes set from AwtRenderPanelI
+   */
+  /**
+   * old image used when data is currently being calculated and cannot be
+   * rendered
+   */
+  private Image fadedImage;
+
+  /**
+   * panel being rendered into
+   */
+  private ImageObserver annotationPanel;
+
+  /**
+   * width of image to render in panel
+   */
+  private int imgWidth;
+
+  /**
+   * offset to beginning of visible area
+   */
+  private int sOffset;
+
+  /**
+   * offset to end of visible area
+   */
+  private int visHeight;
+
+  /**
+   * indicate if the renderer should only render the visible portion of the
+   * annotation given the current view settings
+   */
+  private boolean useClip = true;
+
+  /**
+   * master flag indicating if renderer should ever try to clip. not enabled for
+   * jalview 2.8.1
+   */
+  private boolean canClip = false;
+
   public AnnotationRenderer()
   {
     this(false);
   }
   public AnnotationRenderer()
   {
     this(false);
   }
+
   /**
    * Create a new annotation Renderer
   /**
    * Create a new annotation Renderer
-   * @param debugRedraw flag indicating if timing and redraw parameter info should be output
+   * 
+   * @param debugRedraw
+   *          flag indicating if timing and redraw parameter info should be
+   *          output
    */
   public AnnotationRenderer(boolean debugRedraw)
   {
    */
   public AnnotationRenderer(boolean debugRedraw)
   {
-    this.debugRedraw=debugRedraw;
+    this.debugRedraw = debugRedraw;
   }
 
   }
 
-  public void drawStemAnnot(Graphics g, Annotation[] row_annotations,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
+  /**
+   * Remove any references and resources when this object is no longer required
+   */
+  public void dispose()
+  {
+    hconsensus = null;
+    complementConsensus = null;
+    hStrucConsensus = null;
+    fadedImage = null;
+    annotationPanel = null;
+  }
+
+  void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX,
+          int x, int y, int iconOffset, int startRes, int column,
+          boolean validRes, boolean validEnd)
   {
     g.setColor(STEM_COLOUR);
   {
     g.setColor(STEM_COLOUR);
-    int sCol = (lastSSX / charWidth) + startRes;
+    int sCol = (lastSSX / charWidth)
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("(\\))");
 
 
-    String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
-            : row_annotations[column - 1].displayCharacter;
+    char dc = (column == 0 || row_annotations[column - 1] == null) ? ' '
+            : row_annotations[column - 1].secondaryStructure;
 
     boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
 
     boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
-            || !dc.equals(row_annotations[sCol - 1].displayCharacter);
+            || dc != row_annotations[sCol - 1].secondaryStructure;
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
-            || !dc.equals(row_annotations[column].displayCharacter);
-    // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
-    // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && closeparen.search(dc))
+            || dc != row_annotations[column].secondaryStructure;
+
+    if (column > 0 && Rna.isClosingParenthesis(dc))
     {
     {
-       
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
-        g.fillPolygon(new int[]
-        { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
+        /*
+         * if new annotation with a closing base pair half of the stem, 
+         * display a backward arrow
+         */
+        g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
         x1 += 5;
       }
       if (diffdownstream)
         x1 += 5;
       }
       if (diffdownstream)
@@ -102,13 +208,17 @@ public class AnnotationRenderer
     }
     else
     {
     }
     else
     {
-       
       // display a forward arrow
       if (diffdownstream)
       {
       // display a forward arrow
       if (diffdownstream)
       {
-        g.fillPolygon(new int[]
-        { x2 - 5, x2 - 5, x2 }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
+        /*
+         * if annotation ending with an opeing base pair half of the stem, 
+         * display a forward arrow
+         */
+        g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
         x2 -= 5;
       }
       if (diffupstream)
         x2 -= 5;
       }
       if (diffupstream)
@@ -120,75 +230,19 @@ public class AnnotationRenderer
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
 
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
 
-  private int charWidth, endRes, charHeight;
-
-  private boolean validCharWidth, hasHiddenColumns;
-
-  private FontMetrics fm;
-
-  private final boolean MAC = new jalview.util.Platform().isAMac();
-
-  boolean av_renderHistogram = true, av_renderProfile = true,
-          av_normaliseProfile = false;
-
-  ColourSchemeI profcolour = null;
-
-  private ColumnSelection columnSelection;
-
-  private Hashtable[] hconsensus;
-
-  private Hashtable[] hStrucConsensus;
-
-  private boolean av_ignoreGapsConsensus;
-
-  /**
-   * attributes set from AwtRenderPanelI
-   */
-  /**
-   * old image used when data is currently being calculated and cannot be
-   * rendered
-   */
-  private Image fadedImage;
-
-  /**
-   * panel being rendered into
-   */
-  private ImageObserver annotationPanel;
-
-  /**
-   * width of image to render in panel
-   */
-  private int imgWidth;
-  /**
-   * offset to beginning of visible area
-   */
-  private int sOffset;
-  /**
-   * offset to end of visible area
-   */
-  private int visHeight;
-  /**
-   * indicate if the renderer should only render the visible portion of the annotation given the current view settings
-   */
-  private boolean useClip=true;
-  /**
-   * master flag indicating if renderer should ever try to clip. not enabled for jalview 2.8.1 
-   */
-  private boolean canClip=false;
-
-  
-  public void drawNotCanonicalAnnot(Graphics g, Color nonCanColor, Annotation[] row_annotations,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
+  void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
+          Annotation[] row_annotations, int lastSSX, int x, int y,
+          int iconOffset, int startRes, int column, boolean validRes,
+          boolean validEnd)
   {
   {
-       //System.out.println(nonCanColor);
-       
+    // System.out.println(nonCanColor);
+
     g.setColor(nonCanColor);
     g.setColor(nonCanColor);
-    int sCol = (lastSSX / charWidth) + startRes;
+    int sCol = (lastSSX / charWidth)
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("}|]|<|[a-z]");
-    
+
     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
             : row_annotations[column - 1].displayCharacter;
 
     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
             : row_annotations[column - 1].displayCharacter;
 
@@ -197,18 +251,20 @@ public class AnnotationRenderer
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
             || !dc.equals(row_annotations[column].displayCharacter);
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
             || !dc.equals(row_annotations[column].displayCharacter);
-    // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
+    // System.out.println("Column "+column+" diff up: "+diffupstream+"
+    // down:"+diffdownstream);
     // If a closing base pair half of the stem, display a backward arrow
     // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && closeparen.search(dc))//  closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc)) )
+    if (column > 0 && Rna.isClosingParenthesis(dc))
     {
     {
-       
+
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
-        g.fillPolygon(new int[]
-        { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
+        g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
         x1 += 5;
       }
       if (diffdownstream)
         x1 += 5;
       }
       if (diffdownstream)
@@ -218,13 +274,14 @@ public class AnnotationRenderer
     }
     else
     {
     }
     else
     {
-       
+
       // display a forward arrow
       if (diffdownstream)
       {
       // display a forward arrow
       if (diffdownstream)
       {
-        g.fillPolygon(new int[]
-        { x2 - 5, x2 - 5, x2 }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
+        g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
         x2 -= 5;
       }
       if (diffupstream)
         x2 -= 5;
       }
       if (diffupstream)
@@ -235,6 +292,7 @@ public class AnnotationRenderer
     // draw arrow body
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
     // draw arrow body
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
+
   // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
   // av)
   public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
   // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
   // av)
   public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
@@ -245,19 +303,23 @@ public class AnnotationRenderer
     fadedImage = annotPanel.getFadedImage();
     imgWidth = annotPanel.getFadedImageWidth();
     // visible area for rendering
     fadedImage = annotPanel.getFadedImage();
     imgWidth = annotPanel.getFadedImageWidth();
     // visible area for rendering
-    int[] bounds=annotPanel.getVisibleVRange();
-    if (bounds!=null)
+    int[] bounds = annotPanel.getVisibleVRange();
+    if (bounds != null)
     {
       sOffset = bounds[0];
       visHeight = bounds[1];
     {
       sOffset = bounds[0];
       visHeight = bounds[1];
-      if (visHeight==0)
+      if (visHeight == 0)
       {
       {
-        useClip=false;
-      } else {
-        useClip=canClip;
+        useClip = false;
       }
       }
-    } else {
-      useClip=false;
+      else
+      {
+        useClip = canClip;
+      }
+    }
+    else
+    {
+      useClip = false;
     }
 
     updateFromAlignViewport(av);
     }
 
     updateFromAlignViewport(av);
@@ -266,48 +328,87 @@ public class AnnotationRenderer
   public void updateFromAlignViewport(AlignViewportI av)
   {
     charWidth = av.getCharWidth();
   public void updateFromAlignViewport(AlignViewportI av)
   {
     charWidth = av.getCharWidth();
-    endRes = av.getEndRes();
+    endRes = av.getRanges().getEndRes();
     charHeight = av.getCharHeight();
     hasHiddenColumns = av.hasHiddenColumns();
     validCharWidth = av.isValidCharWidth();
     av_renderHistogram = av.isShowConsensusHistogram();
     av_renderProfile = av.isShowSequenceLogo();
     av_normaliseProfile = av.isNormaliseSequenceLogo();
     charHeight = av.getCharHeight();
     hasHiddenColumns = av.hasHiddenColumns();
     validCharWidth = av.isValidCharWidth();
     av_renderHistogram = av.isShowConsensusHistogram();
     av_renderProfile = av.isShowSequenceLogo();
     av_normaliseProfile = av.isNormaliseSequenceLogo();
-    profcolour = av.getGlobalColourScheme();
-    if (profcolour == null)
+    profcolour = av.getResidueShading();
+    if (profcolour == null || profcolour.getColourScheme() == null)
     {
     {
-      // Set the default colour for sequence logo if the alignnent has no
-      // colourscheme set
-      profcolour = av.getAlignment().isNucleotide() ? new jalview.schemes.NucleotideColourScheme()
-              : new jalview.schemes.ZappoColourScheme();
+      /*
+       * Use default colour for sequence logo if 
+       * the alignment has no colourscheme set
+       * (would like to use user preference but n/a for applet)
+       */
+      ColourSchemeI col = av.getAlignment().isNucleotide()
+              ? new NucleotideColourScheme()
+              : new ZappoColourScheme();
+      profcolour = new ResidueShader(col);
     }
     columnSelection = av.getColumnSelection();
     }
     columnSelection = av.getColumnSelection();
-    hconsensus = av.getSequenceConsensusHash();// hconsensus;
-    hStrucConsensus = av.getRnaStructureConsensusHash(); // hStrucConsensus;
-    av_ignoreGapsConsensus = av.getIgnoreGapsConsensus();
+    hiddenColumns = av.getAlignment().getHiddenColumns();
+    hconsensus = av.getSequenceConsensusHash();
+    complementConsensus = av.getComplementConsensusHash();
+    hStrucConsensus = av.getRnaStructureConsensusHash();
+    av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
+    av_ignoreBelowBackground = av.isIgnoreBelowBackground();
+    av_infoHeight = av.isInfoLetterHeight();
   }
 
   }
 
-  public int[] getProfileFor(AlignmentAnnotation aa, int column)
+
+
+  /**
+   * Returns profile data; the first element is the profile type, the second is
+   * the number of distinct values, the third the total count, and the remainder
+   * depend on the profile type.
+   * 
+   * @param aa
+   * @param column
+   * @return
+   */
+  int[] getProfileFor(AlignmentAnnotation aa, int column)
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
     // all vis settings for the alignment as a whole rather than a subset
     //
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
     // all vis settings for the alignment as a whole rather than a subset
     //
-    if (aa.autoCalculated && aa.label.startsWith("Consensus"))
+    if (InformationThread.HMM_CALC_ID.equals(aa.getCalcId()))
+    {
+      HiddenMarkovModel hmm = aa.sequenceRef.getHMM();
+      return AAFrequency.extractHMMProfile(hmm, column,
+              av_ignoreBelowBackground, av_infoHeight); // TODO check if this follows standard
+                                         // pipeline
+    }
+    if (aa.autoCalculated
+            && (aa.label.startsWith("Consensus") || aa.label
+                    .startsWith("cDNA Consensus")))
     {
     {
-      if (aa.groupRef != null && aa.groupRef.consensusData != null
+      boolean forComplement = aa.label.startsWith("cDNA Consensus");
+      if (aa.groupRef != null && aa.groupRef.getConsensusData() != null
               && aa.groupRef.isShowSequenceLogo())
       {
               && aa.groupRef.isShowSequenceLogo())
       {
+        // TODO? group consensus for cDNA complement
         return AAFrequency.extractProfile(
         return AAFrequency.extractProfile(
-                aa.groupRef.consensusData[column],
-                aa.groupRef.getIgnoreGapsConsensus());
+                aa.groupRef.getConsensusData().get(column),
+                                               aa.groupRef.getIgnoreGapsConsensus());
       }
       // TODO extend annotation row to enable dynamic and static profile data to
       // be stored
       if (aa.groupRef == null && aa.sequenceRef == null)
       {
       }
       // TODO extend annotation row to enable dynamic and static profile data to
       // be stored
       if (aa.groupRef == null && aa.sequenceRef == null)
       {
-        return AAFrequency.extractProfile(hconsensus[column],
-                av_ignoreGapsConsensus);
+        if (forComplement)
+        {
+          return AAFrequency.extractCdnaProfile(complementConsensus[column],
+                  av_ignoreGapsConsensus);
+        }
+        else
+        {
+          return AAFrequency.extractProfile(hconsensus.get(column),
+                  av_ignoreGapsConsensus);
+        }
       }
     }
     else
       }
     }
     else
@@ -359,7 +460,7 @@ public class AnnotationRenderer
           AlignViewportI av, Graphics g, int activeRow, int startRes,
           int endRes)
   {
           AlignViewportI av, Graphics g, int activeRow, int startRes,
           int endRes)
   {
-    long stime=System.currentTimeMillis();
+    long stime = System.currentTimeMillis();
     boolean usedFaded = false;
     // NOTES:
     // AnnotationPanel needs to implement: ImageObserver, access to
     boolean usedFaded = false;
     // NOTES:
     // AnnotationPanel needs to implement: ImageObserver, access to
@@ -367,8 +468,8 @@ public class AnnotationRenderer
     updateFromAwtRenderPanel(annotPanel, av);
     fm = g.getFontMetrics();
     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
     updateFromAwtRenderPanel(annotPanel, av);
     fm = g.getFontMetrics();
     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
-    int temp = 0;
-    if (aa==null)
+    // int temp = 0;
+    if (aa == null)
     {
       return false;
     }
     {
       return false;
     }
@@ -380,576 +481,640 @@ public class AnnotationRenderer
     boolean validRes = false;
     boolean validEnd = false;
     boolean labelAllCols = false;
     boolean validRes = false;
     boolean validEnd = false;
     boolean labelAllCols = false;
-    boolean centreColLabels, centreColLabelsDef = av
-            .getCentreColumnLabels();
+//    boolean centreColLabels;
+//    boolean centreColLabelsDef = av.isCentreColumnLabels();
     boolean scaleColLabel = false;
     boolean scaleColLabel = false;
-    AlignmentAnnotation consensusAnnot=av.getAlignmentConsensusAnnotation(),structConsensusAnnot=av.getAlignmentStrucConsensusAnnotation();
-    boolean renderHistogram = true, renderProfile = true, normaliseProfile = false;
+    final AlignmentAnnotation consensusAnnot = av
+            .getAlignmentConsensusAnnotation();
+    final AlignmentAnnotation structConsensusAnnot = av
+            .getAlignmentStrucConsensusAnnotation();
+    final AlignmentAnnotation complementConsensusAnnot = av
+            .getComplementConsensusAnnotation();
 
     BitSet graphGroupDrawn = new BitSet();
     int charOffset = 0; // offset for a label
 
     BitSet graphGroupDrawn = new BitSet();
     int charOffset = 0; // offset for a label
-    float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a
-    // column.
-    Font ofont = g.getFont();
     // \u03B2 \u03B1
     // debug ints
     // \u03B2 \u03B1
     // debug ints
-    int yfrom=0,f_i=0,yto=0,f_to=0;
-    boolean clipst=false,clipend=false;
+    int yfrom = 0, f_i = 0, yto = 0, f_to = 0;
+    boolean clipst = false, clipend = false;
     for (int i = 0; i < aa.length; i++)
     {
       AlignmentAnnotation row = aa[i];
     for (int i = 0; i < aa.length; i++)
     {
       AlignmentAnnotation row = aa[i];
+      boolean renderHistogram = true;
+      boolean renderProfile = false;
+      boolean normaliseProfile = false;
+      boolean isRNA = row.isRNA();
+
+      // check if this is a consensus annotation row and set the display
+      // settings appropriately
+      // TODO: generalise this to have render styles for consensus/profile
+      // data
+      if (row.groupRef != null && row == row.groupRef.getConsensus())
+      {
+        renderHistogram = row.groupRef.isShowConsensusHistogram();
+        renderProfile = row.groupRef.isShowSequenceLogo();
+        normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
+      }
+      else if (row == consensusAnnot || row == structConsensusAnnot
+              || row == complementConsensusAnnot)
+      {
+        renderHistogram = av_renderHistogram;
+        renderProfile = av_renderProfile;
+        normaliseProfile = av_normaliseProfile;
+      }
+      else if (InformationThread.HMM_CALC_ID.equals(row.getCalcId()))
       {
       {
-        // check if this is a consensus annotation row and set the display settings appropriately
-        // TODO: generalise this to have render styles for consensus/profile
-        // data
-        if (row.groupRef != null && row == row.groupRef.getConsensus())
+        if (row.groupRef != null)
         {
         {
-          renderHistogram = row.groupRef.isShowConsensusHistogram();
-          renderProfile = row.groupRef.isShowSequenceLogo();
-          normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
+          renderHistogram = row.groupRef.isShowInformationHistogram();
+          renderProfile = row.groupRef.isShowHMMSequenceLogo();
+          normaliseProfile = row.groupRef.isNormaliseHMMSequenceLogo();
         }
         }
-        else if (row == consensusAnnot || row == structConsensusAnnot)
+        else
         {
         {
-          renderHistogram = av_renderHistogram;
-          renderProfile = av_renderProfile;
-          normaliseProfile = av_normaliseProfile;
-        } else {
-          renderHistogram = true;
-          // don't need to set render/normaliseProfile since they are not currently used in any other annotation track renderer
+          renderHistogram = av.isShowInformationHistogram();
+          renderProfile = av.isShowHMMSequenceLogo();
+          normaliseProfile = av.isNormaliseHMMSequenceLogo();
         }
       }
         }
       }
+      else if (row == consensusAnnot || row == structConsensusAnnot
+              || row == complementConsensusAnnot)
+      {
+        renderHistogram = av_renderHistogram;
+        renderProfile = av_renderProfile;
+        normaliseProfile = av_normaliseProfile;
+      }
+
       Annotation[] row_annotations = row.annotations;
       if (!row.visible)
       {
         continue;
       }
       Annotation[] row_annotations = row.annotations;
       if (!row.visible)
       {
         continue;
       }
-      centreColLabels = row.centreColLabels || centreColLabelsDef;
+//      centreColLabels = row.centreColLabels || centreColLabelsDef;
       labelAllCols = row.showAllColLabels;
       scaleColLabel = row.scaleColLabel;
       lastSS = ' ';
       lastSSX = 0;
       labelAllCols = row.showAllColLabels;
       scaleColLabel = row.scaleColLabel;
       lastSS = ' ';
       lastSSX = 0;
-      
-      if (!useClip || ((y-charHeight)<visHeight && (y+row.height+charHeight*2)>=sOffset)) 
+
+      if (!useClip || ((y - charHeight) < visHeight
+              && (y + row.height + charHeight * 2) >= sOffset))
       {// if_in_visible_region
         if (!clipst)
         {
       {// if_in_visible_region
         if (!clipst)
         {
-          clipst=true;
-          yfrom=y;
-          f_i=i;
+          clipst = true;
+          yfrom = y;
+          f_i = i;
         }
         yto = y;
         }
         yto = y;
-        f_to=i;
-      if (row.graph > 0)
-      {
-        if (row.graphGroup > -1 && graphGroupDrawn.get(row.graphGroup)) {
-          continue;
-        }
-
-        // this is so that we draw the characters below the graph
-        y += row.height;
-
-        if (row.hasText)
+        f_to = i;
+        if (row.graph > 0)
         {
         {
-          iconOffset = charHeight - fm.getDescent();
-          y -= charHeight;
-        }
-      }
-      else if (row.hasText)
-      {
-        iconOffset = charHeight - fm.getDescent();
-
-      }
-      else
-      {
-        iconOffset = 0;
-      }
-
-      if (row.autoCalculated && av.isCalculationInProgress(row))
-      {
-        y += charHeight;
-        usedFaded = true;
-        g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
-                - row.height, imgWidth, y, annotationPanel);
-        g.setColor(Color.black);
-        // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
+          if (row.graphGroup > -1 && graphGroupDrawn.get(row.graphGroup))
+          {
+            continue;
+          }
 
 
-        continue;
-      }
+          // this is so that we draw the characters below the graph
+          y += row.height;
 
 
-      /*
-       * else if (annotationPanel.av.updatingConservation &&
-       * aa[i].label.equals("Conservation")) {
-       * 
-       * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
-       * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
-       * annotationPanel.imgWidth, y, annotationPanel);
-       * 
-       * g.setColor(Color.black); //
-       * g.drawString("Calculating Conservation.....",20, y-row.height/2);
-       * 
-       * continue; } else if (annotationPanel.av.updatingConservation &&
-       * aa[i].label.equals("Quality")) {
-       * 
-       * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
-       * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
-       * annotationPanel.imgWidth, y, annotationPanel); g.setColor(Color.black);
-       * // / g.drawString("Calculating Quality....",20, y-row.height/2);
-       * 
-       * continue; }
-       */
-      // first pass sets up state for drawing continuation from left-hand column
-      // of startRes
-      x = (startRes == 0) ? 0 : -1;
-      while (x < endRes - startRes)
-      {
-        if (hasHiddenColumns)
-        {
-          column = columnSelection.adjustForHiddenColumns(startRes + x);
-          if (column > row_annotations.length - 1)
+          if (row.hasText)
           {
           {
-            break;
+            iconOffset = charHeight - fm.getDescent();
+            y -= charHeight;
           }
         }
           }
         }
-        else
+        else if (row.hasText)
         {
         {
-          column = startRes + x;
-        }
+          iconOffset = charHeight - fm.getDescent();
 
 
-        if ((row_annotations == null) || (row_annotations.length <= column)
-                || (row_annotations[column] == null))
-        {
-          validRes = false;
         }
         else
         {
         }
         else
         {
-          validRes = true;
+          iconOffset = 0;
         }
         }
-        if (x > -1)
+
+        if (row.autoCalculated && av.isCalculationInProgress(row))
         {
         {
-          if (activeRow == i)
-          {
-            g.setColor(Color.red);
+          y += charHeight;
+          usedFaded = true;
+          g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0,
+                  y - row.height, imgWidth, y, annotationPanel);
+          g.setColor(Color.black);
+          // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
 
 
-            if (columnSelection != null)
-            {
-              for (int n = 0; n < columnSelection.size(); n++)
-              {
-                int v = columnSelection.columnAt(n);
+          continue;
+        }
 
 
-                if (v == column)
-                {
-                  g.fillRect(x * charWidth, y, charWidth, charHeight);
-                }
-              }
+        /*
+         * else if (annotationPanel.av.updatingConservation &&
+         * aa[i].label.equals("Conservation")) {
+         * 
+         * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
+         * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
+         * annotationPanel.imgWidth, y, annotationPanel);
+         * 
+         * g.setColor(Color.black); //
+         * g.drawString("Calculating Conservation.....",20, y-row.height/2);
+         * 
+         * continue; } else if (annotationPanel.av.updatingConservation &&
+         * aa[i].label.equals("Quality")) {
+         * 
+         * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
+         * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
+         * annotationPanel.imgWidth, y, annotationPanel);
+         * g.setColor(Color.black); // /
+         * g.drawString("Calculating Quality....",20, y-row.height/2);
+         * 
+         * continue; }
+         */
+        // first pass sets up state for drawing continuation from left-hand
+        // column
+        // of startRes
+        x = (startRes == 0) ? 0 : -1;
+        while (x < endRes - startRes)
+        {
+          if (hasHiddenColumns)
+          {
+            column = hiddenColumns.visibleToAbsoluteColumn(startRes + x);
+            if (column > row_annotations.length - 1)
+            {
+              break;
             }
           }
             }
           }
-          if (!row.isValidStruc())
+          else
           {
           {
-            g.setColor(Color.orange);
-            g.fillRect((int) row.getInvalidStrucPos() * charWidth, y,
-                    charWidth, charHeight);
+            column = startRes + x;
           }
           }
-          if (validCharWidth
-                  && validRes
-                  && row_annotations[column].displayCharacter != null
-                  && (row_annotations[column].displayCharacter.length() > 0))
-          {
 
 
-            if (centreColLabels || scaleColLabel)
+          if ((row_annotations == null)
+                  || (row_annotations.length <= column)
+                  || (row_annotations[column] == null))
+          {
+            validRes = false;
+          }
+          else
+          {
+            validRes = true;
+          }
+          final String displayChar = validRes
+                  ? row_annotations[column].displayCharacter
+                  : null;
+          if (x > -1)
+          {
+            if (activeRow == i)
             {
             {
-              fmWidth = fm.charsWidth(
-                      row_annotations[column].displayCharacter
-                              .toCharArray(), 0,
-                      row_annotations[column].displayCharacter.length());
+              g.setColor(Color.red);
 
 
-              if (scaleColLabel)
+              if (columnSelection != null)
               {
               {
-                // justify the label and scale to fit in column
-                if (fmWidth > charWidth)
+                if (columnSelection.contains(column))
                 {
                 {
-                  // scale only if the current font isn't already small enough
-                  fmScaling = charWidth;
-                  fmScaling /= fmWidth;
-                  g.setFont(ofont.deriveFont(AffineTransform
-                          .getScaleInstance(fmScaling, 1.0)));
-                  // and update the label's width to reflect the scaling.
-                  fmWidth = charWidth;
+                  g.fillRect(x * charWidth, y, charWidth, charHeight);
                 }
               }
             }
                 }
               }
             }
-            else
+            if (row.getInvalidStrucPos() > x)
             {
             {
-              fmWidth = fm
-                      .charWidth(row_annotations[column].displayCharacter
-                              .charAt(0));
+              g.setColor(Color.orange);
+              g.fillRect(x * charWidth, y, charWidth, charHeight);
             }
             }
-            charOffset = (int) ((charWidth - fmWidth) / 2f);
-
-            if (row_annotations[column].colour == null)
-              g.setColor(Color.black);
-            else
-              g.setColor(row_annotations[column].colour);
-
-            if (column == 0 || row.graph > 0)
+            else if (row.getInvalidStrucPos() == x)
             {
             {
-              g.drawString(row_annotations[column].displayCharacter,
-                      (x * charWidth) + charOffset, y + iconOffset);
+              g.setColor(Color.orange.darker());
+              g.fillRect(x * charWidth, y, charWidth, charHeight);
             }
             }
-            else if (row_annotations[column - 1] == null
-                    || (labelAllCols
-                            || !row_annotations[column].displayCharacter
-                                    .equals(row_annotations[column - 1].displayCharacter) || (row_annotations[column].displayCharacter
-                            .length() < 2 && row_annotations[column].secondaryStructure == ' ')))
+            if (validCharWidth && validRes && displayChar != null
+                    && (displayChar.length() > 0))
             {
             {
-               g.drawString(row_annotations[column].displayCharacter
-                         , x
-                      * charWidth + charOffset, y + iconOffset);
+              Graphics2D gg = ((Graphics2D) g);
+              float fmWidth = fm.charsWidth(displayChar.toCharArray(), 0,
+                      displayChar.length());
+
+              /*
+               * shrink label width to fit in column, if that is
+               * both configured and necessary
+               */
+              boolean scaledToFit = false;
+              float fmScaling = 1f;
+              if (scaleColLabel && fmWidth > charWidth)
+              {
+                scaledToFit = true;
+                fmScaling = charWidth;
+                fmScaling /= fmWidth;
+                // and update the label's width to reflect the scaling.
+                fmWidth = charWidth;
+              }
+
+              charOffset = (int) ((charWidth - fmWidth) / 2f);
+
+              if (row_annotations[column].colour == null)
+              {
+                gg.setColor(Color.black);
+              }
+              else
+              {
+                gg.setColor(row_annotations[column].colour);
+              }
+
+              /*
+               * draw the label, unless it is the same secondary structure
+               * symbol (excluding RNA Helix) as the previous column
+               */
+              final int xPos = (x * charWidth) + charOffset;
+              final int yPos = y + iconOffset;
+
+              /*
+               * translate to drawing position _before_ applying any scaling
+               */
+              gg.translate(xPos, yPos);
+              if (scaledToFit)
+              {
+                /*
+                 * use a scaling transform to make the label narrower
+                 * (JalviewJS doesn't have Font.deriveFont(AffineTransform))
+                 */
+                gg.transform(
+                        AffineTransform.getScaleInstance(fmScaling, 1.0));
+              }
+              if (column == 0 || row.graph > 0)
+              {
+                gg.drawString(displayChar, 0, 0);
+              }
+              else if (row_annotations[column - 1] == null || (labelAllCols
+                      || !displayChar.equals(
+                              row_annotations[column - 1].displayCharacter)
+                      || (displayChar.length() < 2
+                              && row_annotations[column].secondaryStructure == ' ')))
+              {
+                gg.drawString(displayChar, 0, 0);
+              }
+              if (scaledToFit)
+              {
+                /*
+                 * undo scaling before translating back 
+                 * (restoring saved transform does NOT work in JS PDFGraphics!)
+                 */
+                gg.transform(AffineTransform
+                        .getScaleInstance(1D / fmScaling, 1.0));
+              }
+              gg.translate(-xPos, -yPos);
             }
             }
-            g.setFont(ofont);
           }
           }
-        }
-        if (row.hasIcons)
-        {
-          char ss = validRes ? row_annotations[column].secondaryStructure
-                  : '-';
-          
-          if (ss == '(')
+          if (row.hasIcons)
           {
           {
-            // distinguish between forward/backward base-pairing
-            if (row_annotations[column].displayCharacter.indexOf(')') > -1)
+            char ss = validRes ? row_annotations[column].secondaryStructure
+                    : '-';
+
+            if (ss == '(')
             {
             {
-            
-              ss = ')';
-              
+              // distinguish between forward/backward base-pairing
+              if (displayChar.indexOf(')') > -1)
+              {
+
+                ss = ')';
+
+              }
             }
             }
-          }
-           if (ss == '[')
-          {
-            if ((row_annotations[column].displayCharacter.indexOf(']') > -1))
+            if (ss == '[')
             {
             {
+              if ((displayChar.indexOf(']') > -1))
+              {
                 ss = ']';
                 ss = ']';
-                
-                
+
+              }
             }
             }
-          }
-           if (ss == '{')
-           {
-             // distinguish between forward/backward base-pairing
-             if (row_annotations[column].displayCharacter.indexOf('}') > -1)
-             {
-               ss = '}';
-               
-               
-             }
-           }
-           if (ss == '<')
-           {
-             // distinguish between forward/backward base-pairing
-             if (row_annotations[column].displayCharacter.indexOf('<') > -1)
-             {
-               ss = '>';
-               
-               
-             }
-           }
-           if (ss >=65)
-           {
-             // distinguish between forward/backward base-pairing
-             if (row_annotations[column].displayCharacter.indexOf(ss+32) > -1)
-             {
-              
-               ss = (char) (ss+32);
-               
-               
-             }
-           }
-           
-               
-          if (!validRes || (ss != lastSS))
-             {
-               
-               
-            if (x > -1)
-             {
-               
-               
-              int nb_annot=x-temp;
-              //System.out.println("\t type :"+lastSS+"\t x :"+x+"\t nbre annot :"+nb_annot);
-              switch (lastSS)
-             {
-               
-              case '$':
-                drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                        column, validRes, validEnd);
-                break;
-
-              case '�':
-                drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                        column, validRes, validEnd);
-                break;
-
-              case '(': // Stem case for RNA secondary structure
-              case ')': // and opposite direction
-                drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                        column, validRes, validEnd);
-                temp=x;
-                break;
-              case '{':
-              case '}':
-              case '[':
-              case ']':
-              case '>':
-              case '<':
-              case 'A':
-              case 'a':
-              case 'B':
-              case 'b':
-              case 'C':
-              case 'c':
-              case 'D':
-              case 'd':
-              case 'E':
-              case 'e':
-              case 'F':
-              case 'f':
-              case 'G':
-              case 'g':
-              case 'H':
-              case 'h':
-              case 'I':
-              case 'i':
-              case 'J':
-              case 'j':
-              case 'K':
-              case 'k':
-              case 'L':
-              case 'l':
-              case 'M':
-              case 'm':
-              case 'N':
-              case 'n':
-              case 'O':
-              case 'o':
-              case 'P':
-              case 'p':
-              case 'Q':
-              case 'q':
-              case 'R':
-              case 'r':
-              case 'S':
-              case 's':
-              case 'T':
-              case 't':
-              case 'U':
-              case 'u':
-              case 'V':
-              case 'v':
-              case 'W':
-              case 'w':
-              case 'X':
-              case 'x':
-              case 'Y':
-              case 'y':
-              case 'Z':
-              case 'z':
-                 
-                 Color nonCanColor= getNotCanonicalColor(lastSS);
-                 drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                          column, validRes, validEnd);
-                 temp=x;
-                 break;
-              default:
-                g.setColor(Color.gray);
-                g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth)
-                        - lastSSX, 2);
-                temp=x;
-                break;
+            if (ss == '{')
+            {
+              // distinguish between forward/backward base-pairing
+              if (displayChar.indexOf('}') > -1)
+              {
+                ss = '}';
+
               }
             }
               }
             }
-            if (validRes)
+            if (ss == '<')
             {
             {
-              lastSS = ss;
+              // distinguish between forward/backward base-pairing
+              if (displayChar.indexOf('<') > -1)
+              {
+                ss = '>';
+
+              }
             }
             }
-            else
+            if (isRNA && (ss >= CHAR_A) && (ss <= CHAR_Z))
             {
             {
-              lastSS = ' ';
+              // distinguish between forward/backward base-pairing
+              int ssLowerCase = ss + UPPER_TO_LOWER;
+              // TODO would .equals() be safer here? or charAt(0)?
+              if (displayChar.indexOf(ssLowerCase) > -1)
+              {
+                ss = (char) ssLowerCase;
+              }
             }
             }
-            if (x > -1)
+
+            if (!validRes || (ss != lastSS))
             {
             {
-              lastSSX = (x * charWidth);
+
+              if (x > -1)
+              {
+
+                // int nb_annot = x - temp;
+                // System.out.println("\t type :"+lastSS+"\t x :"+x+"\t nbre
+                // annot :"+nb_annot);
+                switch (lastSS)
+                {
+                case '(': // Stem case for RNA secondary structure
+                case ')': // and opposite direction
+                  drawStemAnnot(g, row_annotations, lastSSX, x, y,
+                          iconOffset, startRes, column, validRes, validEnd);
+                  // temp = x;
+                  break;
+
+                case 'H':
+                  if (!isRNA)
+                  {
+                    drawHelixAnnot(g, row_annotations, lastSSX, x, y,
+                            iconOffset, startRes, column, validRes,
+                            validEnd);
+                    break;
+                  }
+                  // no break if isRNA - falls through to drawNotCanonicalAnnot!
+                case 'E':
+                  if (!isRNA)
+                  {
+                    drawSheetAnnot(g, row_annotations, lastSSX, x, y,
+                            iconOffset, startRes, column, validRes,
+                            validEnd);
+                    break;
+                  }
+                  // no break if isRNA - fall through to drawNotCanonicalAnnot!
+
+                case '{':
+                case '}':
+                case '[':
+                case ']':
+                case '>':
+                case '<':
+                case 'A':
+                case 'a':
+                case 'B':
+                case 'b':
+                case 'C':
+                case 'c':
+                case 'D':
+                case 'd':
+                case 'e':
+                case 'F':
+                case 'f':
+                case 'G':
+                case 'g':
+                case 'h':
+                case 'I':
+                case 'i':
+                case 'J':
+                case 'j':
+                case 'K':
+                case 'k':
+                case 'L':
+                case 'l':
+                case 'M':
+                case 'm':
+                case 'N':
+                case 'n':
+                case 'O':
+                case 'o':
+                case 'P':
+                case 'p':
+                case 'Q':
+                case 'q':
+                case 'R':
+                case 'r':
+                case 'S':
+                case 's':
+                case 'T':
+                case 't':
+                case 'U':
+                case 'u':
+                case 'V':
+                case 'v':
+                case 'W':
+                case 'w':
+                case 'X':
+                case 'x':
+                case 'Y':
+                case 'y':
+                case 'Z':
+                case 'z':
+
+                  Color nonCanColor = getNotCanonicalColor(lastSS);
+                  drawNotCanonicalAnnot(g, nonCanColor, row_annotations,
+                          lastSSX, x, y, iconOffset, startRes, column,
+                          validRes, validEnd);
+                  // temp = x;
+                  break;
+                default:
+                  g.setColor(Color.gray);
+                  g.fillRect(lastSSX, y + 6 + iconOffset,
+                          (x * charWidth) - lastSSX, 2);
+                  // temp = x;
+                  break;
+                }
+              }
+              if (validRes)
+              {
+                lastSS = ss;
+              }
+              else
+              {
+                lastSS = ' ';
+              }
+              if (x > -1)
+              {
+                lastSSX = (x * charWidth);
+              }
             }
           }
             }
           }
+          column++;
+          x++;
         }
         }
-        column++;
-        x++;
-      }
-      if (column >= row_annotations.length)
-      {
-        column = row_annotations.length - 1;
-        validEnd = false;
-      }
-      else
-      {
-        validEnd = true;
-      }
-      if ((row_annotations == null) || (row_annotations.length <= column)
-              || (row_annotations[column] == null))
-      {
-        validRes = false;
-      }
-      else
-      {
-        validRes = true;
-      }
-
-      // x ++;
+        if (column >= row_annotations.length)
+        {
+          column = row_annotations.length - 1;
+          validEnd = false;
+        }
+        else
+        {
+          validEnd = true;
+        }
+        if ((row_annotations == null) || (row_annotations.length <= column)
+                || (row_annotations[column] == null))
+        {
+          validRes = false;
+        }
+        else
+        {
+          validRes = true;
+        }
+        // x ++;
 
 
-      if (row.hasIcons)
-      {
-        switch (lastSS)
+        if (row.hasIcons)
         {
         {
-        case '$':
-          drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          break;
-
-        case '�':
-          drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          break;
-        case 's':
-        case 'S': // Stem case for RNA secondary structure
-               
-          drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          
-          break;
-        case '{':
-        case '}':
-        case '[':
-        case ']':
-        case '>':
-        case '<':
-        case 'A':
-        case 'a':
-        case 'B':
-        case 'b':
-        case 'C':
-        case 'c':
-        case 'D':
-        case 'd':
-        case 'E':
-        case 'e':
-        case 'F':
-        case 'f':
-        case 'G':
-        case 'g':
-        case 'H':
-        case 'h':
-        case 'I':
-        case 'i':
-        case 'J':
-        case 'j':
-        case 'K':
-        case 'k':
-        case 'L':
-        case 'l':
-        case 'M':
-        case 'm':
-        case 'N':
-        case 'n':
-        case 'O':
-        case 'o':
-        case 'P':
-        case 'p':
-        case 'Q':
-        case 'q':
-        case 'R':
-        case 'r':
-        case 'T':
-        case 't':
-        case 'U':
-        case 'u':
-        case 'V':
-        case 'v':
-        case 'W':
-        case 'w':
-        case 'X':
-        case 'x':
-        case 'Y':
-        case 'y':
-        case 'Z':
-        case 'z':
-               //System.out.println(lastSS);
-          Color nonCanColor = getNotCanonicalColor(lastSS);
-         drawNotCanonicalAnnot(g,nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                    column, validRes, validEnd);
-         break;
-        default:
-          drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          break;
+          switch (lastSS)
+          {
+
+          case 'H':
+            if (!isRNA)
+            {
+              drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
+                      startRes, column, validRes, validEnd);
+              break;
+            }
+            // no break if isRNA - fall through to drawNotCanonicalAnnot!
+
+          case 'E':
+            if (!isRNA)
+            {
+              drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
+                      startRes, column, validRes, validEnd);
+              break;
+            }
+            // no break if isRNA - fall through to drawNotCanonicalAnnot!
+
+          case '(':
+          case ')': // Stem case for RNA secondary structure
+
+            drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
+                    startRes, column, validRes, validEnd);
+
+            break;
+          case '{':
+          case '}':
+          case '[':
+          case ']':
+          case '>':
+          case '<':
+          case 'A':
+          case 'a':
+          case 'B':
+          case 'b':
+          case 'C':
+          case 'c':
+          case 'D':
+          case 'd':
+          case 'e':
+          case 'F':
+          case 'f':
+          case 'G':
+          case 'g':
+          case 'h':
+          case 'I':
+          case 'i':
+          case 'J':
+          case 'j':
+          case 'K':
+          case 'k':
+          case 'L':
+          case 'l':
+          case 'M':
+          case 'm':
+          case 'N':
+          case 'n':
+          case 'O':
+          case 'o':
+          case 'P':
+          case 'p':
+          case 'Q':
+          case 'q':
+          case 'R':
+          case 'r':
+          case 'T':
+          case 't':
+          case 'U':
+          case 'u':
+          case 'V':
+          case 'v':
+          case 'W':
+          case 'w':
+          case 'X':
+          case 'x':
+          case 'Y':
+          case 'y':
+          case 'Z':
+          case 'z':
+            // System.out.println(lastSS);
+            Color nonCanColor = getNotCanonicalColor(lastSS);
+            drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX,
+                    x, y, iconOffset, startRes, column, validRes, validEnd);
+            break;
+          default:
+            drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset,
+                    startRes, column, validRes, validEnd);
+            break;
+          }
         }
         }
-      }
 
 
-      if (row.graph > 0 && row.graphHeight > 0)
-      {
-        if (row.graph == AlignmentAnnotation.LINE_GRAPH)
+        if (row.graph > 0 && row.graphHeight > 0)
         {
         {
-          if (row.graphGroup > -1 && !graphGroupDrawn.get(row.graphGroup))
+          if (row.graph == AlignmentAnnotation.LINE_GRAPH)
           {
           {
-            // TODO: JAL-1291 revise rendering model so the graphGroup map is computed efficiently for all visible labels
-            float groupmax = -999999, groupmin = 9999999;
-            for (int gg = 0; gg < aa.length; gg++)
+            if (row.graphGroup > -1 && !graphGroupDrawn.get(row.graphGroup))
             {
             {
-              if (aa[gg].graphGroup != row.graphGroup)
+              // TODO: JAL-1291 revise rendering model so the graphGroup map is
+              // computed efficiently for all visible labels
+              float groupmax = -999999, groupmin = 9999999;
+              for (int gg = 0; gg < aa.length; gg++)
               {
               {
-                continue;
-              }
+                if (aa[gg].graphGroup != row.graphGroup)
+                {
+                  continue;
+                }
 
 
-              if (aa[gg] != row)
-              {
-                aa[gg].visible = false;
-              }
-              if (aa[gg].graphMax > groupmax)
-              {
-                groupmax = aa[gg].graphMax;
+                if (aa[gg] != row)
+                {
+                  aa[gg].visible = false;
+                }
+                if (aa[gg].graphMax > groupmax)
+                {
+                  groupmax = aa[gg].graphMax;
+                }
+                if (aa[gg].graphMin < groupmin)
+                {
+                  groupmin = aa[gg].graphMin;
+                }
               }
               }
-              if (aa[gg].graphMin < groupmin)
+
+              for (int gg = 0; gg < aa.length; gg++)
               {
               {
-                groupmin = aa[gg].graphMin;
+                if (aa[gg].graphGroup == row.graphGroup)
+                {
+                  drawLineGraph(g, aa[gg], aa[gg].annotations, startRes,
+                          endRes, y, groupmin, groupmax, row.graphHeight);
+                }
               }
               }
-            }
 
 
-            for (int gg = 0; gg < aa.length; gg++)
+              graphGroupDrawn.set(row.graphGroup);
+            }
+            else
             {
             {
-              if (aa[gg].graphGroup == row.graphGroup)
-              {
-                drawLineGraph(g, aa[gg], aa[gg].annotations, startRes, endRes, y, groupmin,
-                        groupmax, row.graphHeight);
-              }
+              drawLineGraph(g, row, row_annotations, startRes, endRes, y,
+                      row.graphMin, row.graphMax, row.graphHeight);
             }
             }
-
-            graphGroupDrawn.set(row.graphGroup);
           }
           }
-          else
+          else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
           {
           {
-            drawLineGraph(g, row, row_annotations, startRes, endRes, y, row.graphMin,
-                    row.graphMax, row.graphHeight);
+            drawBarGraph(g, row, row_annotations, startRes, endRes,
+                    row.graphMin, row.graphMax, y, renderHistogram,
+                    renderProfile, normaliseProfile);
           }
         }
           }
         }
-        else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
-        {
-          drawBarGraph(g, row, row_annotations, startRes, endRes,
-                  row.graphMin, row.graphMax, y, renderHistogram,renderProfile,normaliseProfile);
-        }
       }
       }
-    } else {
-      if (clipst && !clipend)
+      else
       {
       {
-        clipend = true;
-      }
-    }// end if_in_visible_region
+        if (clipst && !clipend)
+        {
+          clipend = true;
+        }
+      } // end if_in_visible_region
       if (row.graph > 0 && row.hasText)
       {
         y += charHeight;
       if (row.graph > 0 && row.hasText)
       {
         y += charHeight;
@@ -966,13 +1131,13 @@ public class AnnotationRenderer
       {
         if (clipst)
         {
       {
         if (clipst)
         {
-          System.err.println("Start clip at : " + yfrom + " (index " + f_i
-                  + ")");
+          System.err.println(
+                  "Start clip at : " + yfrom + " (index " + f_i + ")");
         }
         if (clipend)
         {
         }
         if (clipend)
         {
-          System.err.println("End clip at : " + yto + " (index " + f_to
-                  + ")");
+          System.err.println(
+                  "End clip at : " + yto + " (index " + f_to + ")");
         }
       }
       ;
         }
       }
       ;
@@ -984,25 +1149,25 @@ public class AnnotationRenderer
     return !usedFaded;
   }
 
     return !usedFaded;
   }
 
-  private final Color GLYPHLINE_COLOR = Color.gray;
+  public static final Color GLYPHLINE_COLOR = Color.gray;
 
 
-  private final Color SHEET_COLOUR = Color.green;
+  public static final Color SHEET_COLOUR = Color.green;
 
 
-  private final Color HELIX_COLOUR = Color.red;
+  public static final Color HELIX_COLOUR = Color.red;
 
 
-  private final Color STEM_COLOUR = Color.blue;
-  
-  private  Color sdNOTCANONICAL_COLOUR;
+  public static final Color STEM_COLOUR = Color.blue;
 
 
-  public void drawGlyphLine(Graphics g, Annotation[] row,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
+  // private Color sdNOTCANONICAL_COLOUR;
+
+  void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, int x,
+          int y, int iconOffset, int startRes, int column, boolean validRes,
+          boolean validEnd)
   {
     g.setColor(GLYPHLINE_COLOR);
     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
   }
 
   {
     g.setColor(GLYPHLINE_COLOR);
     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
   }
 
-  public void drawSheetAnnot(Graphics g, Annotation[] row,
+  void drawSheetAnnot(Graphics g, Annotation[] row,
 
           int lastSSX, int x, int y, int iconOffset, int startRes,
           int column, boolean validRes, boolean validEnd)
 
           int lastSSX, int x, int y, int iconOffset, int startRes,
           int column, boolean validRes, boolean validEnd)
@@ -1012,33 +1177,35 @@ public class AnnotationRenderer
     if (!validEnd || !validRes || row == null || row[column] == null
             || row[column].secondaryStructure != 'E')
     {
     if (!validEnd || !validRes || row == null || row[column] == null
             || row[column].secondaryStructure != 'E')
     {
-      g.fillRect(lastSSX, y + 4 + iconOffset,
-              (x * charWidth) - lastSSX - 4, 7);
-      g.fillPolygon(new int[]
-      { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
+      g.fillRect(lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX - 4,
+              7);
+      g.fillPolygon(
+              new int[]
+              { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
               new int[]
               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
               3);
     }
     else
     {
               new int[]
               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
               3);
     }
     else
     {
-      g.fillRect(lastSSX, y + 4 + iconOffset,
-              (x + 1) * charWidth - lastSSX, 7);
+      g.fillRect(lastSSX, y + 4 + iconOffset, (x + 1) * charWidth - lastSSX,
+              7);
     }
 
   }
 
     }
 
   }
 
-  public void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX,
-          int x, int y, int iconOffset, int startRes, int column,
-          boolean validRes, boolean validEnd)
+  void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, int x,
+          int y, int iconOffset, int startRes, int column, boolean validRes,
+          boolean validEnd)
   {
     g.setColor(HELIX_COLOUR);
 
   {
     g.setColor(HELIX_COLOUR);
 
-    int sCol = (lastSSX / charWidth) + startRes;
+    int sCol = (lastSSX / charWidth)
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
-    if (MAC)
+    if (USE_FILL_ROUND_RECT)
     {
       int ofs = charWidth / 2;
       // Off by 1 offset when drawing rects and ovals
     {
       int ofs = charWidth / 2;
       // Off by 1 offset when drawing rects and ovals
@@ -1062,8 +1229,8 @@ public class AnnotationRenderer
       else
       {
         // g.setColor(Color.magenta);
       else
       {
         // g.setColor(Color.magenta);
-        g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
-                + 1, 8, 0, 0);
+        g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset,
+                x2 - x1 - ofs + 1, 8, 0, 0);
 
       }
 
 
       }
 
@@ -1088,9 +1255,9 @@ public class AnnotationRenderer
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
   }
 
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
   }
 
-  public void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
-          Annotation[] aa_annotations, int sRes, int eRes, int y,
-          float min, float max, int graphHeight)
+  void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
+          Annotation[] aa_annotations, int sRes, int eRes, int y, float min,
+          float max, int graphHeight)
   {
     if (sRes > aa_annotations.length)
     {
   {
     if (sRes > aa_annotations.length)
     {
@@ -1134,7 +1301,7 @@ public class AnnotationRenderer
       column = sRes + x;
       if (hasHiddenColumns)
       {
       column = sRes + x;
       if (hasHiddenColumns)
       {
-        column = columnSelection.adjustForHiddenColumns(column);
+        column = hiddenColumns.visibleToAbsoluteColumn(column);
       }
 
       if (column > aaMax)
       }
 
       if (column > aaMax)
@@ -1150,17 +1317,21 @@ public class AnnotationRenderer
       }
 
       if (aa_annotations[column].colour == null)
       }
 
       if (aa_annotations[column].colour == null)
+      {
         g.setColor(Color.black);
         g.setColor(Color.black);
+      }
       else
       else
+      {
         g.setColor(aa_annotations[column].colour);
         g.setColor(aa_annotations[column].colour);
+      }
 
 
-      y1 = y
-              - (int) (((aa_annotations[column - 1].value - min) / range) * graphHeight);
-      y2 = y
-              - (int) (((aa_annotations[column].value - min) / range) * graphHeight);
+      y1 = y - (int) (((aa_annotations[column - 1].value - min) / range)
+              * graphHeight);
+      y2 = y - (int) (((aa_annotations[column].value - min) / range)
+              * graphHeight);
 
 
-      g.drawLine(x * charWidth - charWidth / 2, y1, x * charWidth
-              + charWidth / 2, y2);
+      g.drawLine(x * charWidth - charWidth / 2, y1,
+              x * charWidth + charWidth / 2, y2);
       x++;
     }
 
       x++;
     }
 
@@ -1178,9 +1349,11 @@ public class AnnotationRenderer
     }
   }
 
     }
   }
 
-  public void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
+  @SuppressWarnings("unused")
+  void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
           Annotation[] aa_annotations, int sRes, int eRes, float min,
           Annotation[] aa_annotations, int sRes, int eRes, float min,
-          float max, int y, boolean renderHistogram,boolean renderProfile,boolean normaliseProfile)
+          float max, int y, boolean renderHistogram, boolean renderProfile,
+          boolean normaliseProfile)
   {
     if (sRes > aa_annotations.length)
     {
   {
     if (sRes > aa_annotations.length)
     {
@@ -1209,7 +1382,7 @@ public class AnnotationRenderer
       column = sRes + x;
       if (hasHiddenColumns)
       {
       column = sRes + x;
       if (hasHiddenColumns)
       {
-        column = columnSelection.adjustForHiddenColumns(column);
+        column = hiddenColumns.visibleToAbsoluteColumn(column);
       }
 
       if (column > aaMax)
       }
 
       if (column > aaMax)
@@ -1223,12 +1396,16 @@ public class AnnotationRenderer
         continue;
       }
       if (aa_annotations[column].colour == null)
         continue;
       }
       if (aa_annotations[column].colour == null)
+      {
         g.setColor(Color.black);
         g.setColor(Color.black);
+      }
       else
       else
+      {
         g.setColor(aa_annotations[column].colour);
         g.setColor(aa_annotations[column].colour);
+      }
 
 
-      y1 = y
-              - (int) (((aa_annotations[column].value - min) / (range)) * _aa.graphHeight);
+      y1 = y - (int) (((aa_annotations[column].value - min) / (range))
+              * _aa.graphHeight);
 
       if (renderHistogram)
       {
 
       if (renderHistogram)
       {
@@ -1245,69 +1422,146 @@ public class AnnotationRenderer
       if (renderProfile)
       {
 
       if (renderProfile)
       {
 
+        /*
+         * {profile type, #values, total count, char1, pct1, char2, pct2...}
+         */
         int profl[] = getProfileFor(_aa, column);
         int profl[] = getProfileFor(_aa, column);
+
         // just try to draw the logo if profl is not null
         // just try to draw the logo if profl is not null
-        if (profl != null && profl[1] != 0)
+        if (profl != null && profl[2] != 0)
         {
         {
+          boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE;
+          boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE;
           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
-          double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
-          double hght;
-          float wdth;
-          double ht2 = 0;
-          char[] dc;
+          final double normaliseFactor = normaliseProfile ? _aa.graphHeight
+                  : (y2 - y1);
 
           /**
 
           /**
-           * profl.length == 74 indicates that the profile of a secondary
-           * structure conservation row was accesed. Therefore dc gets length 2,
-           * to have space for a basepair instead of just a single nucleotide
+           * Render a single base for a sequence profile, a base pair for
+           * structure profile, and a triplet for a cdna profile
            */
            */
-          if (profl.length == 74)
-          {
-            dc = new char[2];
-          }
-          else
-          {
-            dc = new char[1];
-          }
-          LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
-          double scale = 1f / (normaliseProfile ? profl[1] : 100f);
-          float ofontHeight = 1f / lm.getAscent();// magnify to fill box
-          double scl = 0.0;
-          for (int c = 2; c < profl[0];)
+          char[] dc = new char[isStructureProfile ? 2
+                  : (isCdnaProfile ? 3 : 1)];
+
+          // lm is not necessary - we can just use fm - could be off by no more
+          // than 0.5 px
+          // LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
+          // System.out.println(asc + " " + dec + " " + (asc - lm.getAscent())
+          // + " " + (dec - lm.getDescent()));
+
+          double asc = fm.getAscent();
+          double dec = fm.getDescent();
+          double fht = fm.getHeight();
+
+          double scale = 1f / (normaliseProfile ? profl[2] : 100f);
+          // float ofontHeight = 1f / fm.getAscent();// magnify to fill box
+
+          /*
+           * Traverse the character(s)/percentage data in the array
+           */
+
+          float ht2 = ht;
+
+          // profl[1] is the number of values in the profile
+          for (int i = 0, c = 3, last = profl[1]; i < last; i++)
           {
           {
-            dc[0] = (char) profl[c++];
 
 
-            if (_aa.label.startsWith("StrucConsensus"))
+            String s;
+            if (isStructureProfile)
             {
             {
+              // todo can we encode a structure pair as an int, like codons?
+              dc[0] = (char) profl[c++];
               dc[1] = (char) profl[c++];
               dc[1] = (char) profl[c++];
+              s = new String(dc);
             }
             }
+            else if (isCdnaProfile)
+            {
+              CodingUtils.decodeCodon2(profl[c++], dc);
+              s = new String(dc);
+            }
+            else
+            {
+              dc[0] = (char) profl[c++];
+              s = new String(dc);
+            }
+            // next profl[] position is profile % for the character(s)
 
 
-            wdth = charWidth;
-            wdth /= fm.charsWidth(dc, 0, dc.length);
-
-            ht += scl;
+            int percent = profl[c++];
+            if (percent == 0)
+            {
+              // failsafe in case a count rounds down to 0%
+              continue;
+            }
+            double newHeight = normaliseFactor * scale * percent;
+
+            /*
+             * Set character colour as per alignment colour scheme; use the
+             * codon translation if a cDNA profile
+             */
+            Color colour = null;
+            if (isCdnaProfile)
+            {
+              final String codonTranslation = ResidueProperties
+                      .codonTranslate(s);
+              colour = profcolour.findColour(codonTranslation.charAt(0),
+                      column, null);
+            }
+            else
+            {
+              colour = profcolour.findColour(dc[0], column, null);
+            }
+            g.setColor(colour == Color.white ? Color.lightGray : colour);
+
+            // Debug - render boxes around characters
+            // g.setColor(Color.red);
+            // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
+            // (int)(scl));
+            // g.setColor(profcolour.findColour(dc[0]).darker());
+
+            double sx = 1f * charWidth / fm.charsWidth(dc, 0, dc.length);
+            double sy = newHeight / asc;
+            double newAsc = asc * sy;
+            double newDec = dec * sy;
+            // it is not necessary to recalculate lm for the new font.
+            // note: lm.getBaselineOffsets()[lm.getBaselineIndex()]) must be 0
+            // by definition. Was:
+            // int hght = (int) (ht + (newAsc - newDec);
+            // - lm.getBaselineOffsets()[lm.getBaselineIndex()]));
+
+            if (Platform.isJS())
             {
             {
-              scl = htn * scale * profl[c++];
-              lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
-                      .getFontRenderContext());
-              g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
-                      wdth, scl / lm.getAscent())));
-              lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
-
-              // Debug - render boxes around characters
-              // g.setColor(Color.red);
-              // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
-              // (int)(scl));
-              // g.setColor(profcolour.findColour(dc[0]).darker());
-              g.setColor(profcolour.findColour(dc[0], column, null));
-
-              hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
-                      .getBaselineIndex()]));
-
-              g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
+              /*
+               * SwingJS does not implement font.deriveFont()
+               * so use a scaling transform to draw instead,
+               * this is off by a very small amount
+               */
+              final int hght = (int) (ht2 + (newAsc - newDec));
+              Graphics2D gg = (Graphics2D) g;
+              int xShift = (int) Math.round(x * charWidth / sx);
+              int yShift = (int) Math.round(hght / sy);
+              gg.transform(AffineTransform.getScaleInstance(sx, sy));
+              gg.drawString(s, xShift, yShift);
+              gg.transform(
+                      AffineTransform.getScaleInstance(1D / sx, 1D / sy));
+              ht2 += newHeight;
+            }
+            else
+            /**
+             * Java only
+             * 
+             * @j2sIgnore
+             */
+            {
+              // Java ('normal') method is to scale the font to fit
+
+              final int hght = (int) (ht + (newAsc - newDec));
+              Font font = ofont
+                      .deriveFont(AffineTransform.getScaleInstance(sx, sy));
+              g.setFont(font);
+              g.drawChars(dc, 0, dc.length, x * charWidth, hght);
+              ht += newHeight;
             }
           }
             }
           }
-          g.setFont(ofont);
         }
       }
       x++;
         }
       }
       x++;
@@ -1320,8 +1574,8 @@ public class AnnotationRenderer
               BasicStroke.JOIN_ROUND, 3f, new float[]
               { 5f, 3f }, 0f));
 
               BasicStroke.JOIN_ROUND, 3f, new float[]
               { 5f, 3f }, 0f));
 
-      y2 = (int) (y - ((_aa.threshold.value - min) / range)
-              * _aa.graphHeight);
+      y2 = (int) (y
+              - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
       g2.setStroke(new BasicStroke());
     }
       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
       g2.setStroke(new BasicStroke());
     }
@@ -1343,9 +1597,13 @@ public class AnnotationRenderer
       if (aa_annotations[j] != null)
       {
         if (aa_annotations[j].colour == null)
       if (aa_annotations[j] != null)
       {
         if (aa_annotations[j].colour == null)
+        {
           g.setColor(Color.black);
           g.setColor(Color.black);
+        }
         else
         else
+        {
           g.setColor(aa_annotations[j].colour);
           g.setColor(aa_annotations[j].colour);
+        }
 
         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
         if (height > y)
 
         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
         if (height > y)
@@ -1359,134 +1617,130 @@ public class AnnotationRenderer
     }
   }
 
     }
   }
 
-  
   Color getNotCanonicalColor(char lastss)
   Color getNotCanonicalColor(char lastss)
-       {
-         switch (lastss)
-      {
-         case '{':  
-             case '}':
-                 return new Color(255,125,5);
-                
-             case '[':
-             case ']':
-                 return new Color(245,115,10);
-                
-             case '>':
-             case '<':
-                 return new Color(235,135,15);
-                 
-             case 'A':
-             case 'a':
-                 return new Color(225,105,20);
-               
-             case 'B':
-             case 'b':
-                 return new Color(215,145,30);
-                 
-             case 'C':
-             case 'c':
-                 return new Color(205,95,35);
-                
-             case 'D':
-             case 'd':
-                 return new Color(195,155,45);
-                 
-             case 'E':
-             case 'e':
-                 return new Color(185,85,55);
-                
-             case 'F':
-             case 'f':
-                 return new Color(175,165,65);
-                
-             case 'G':
-             case 'g':
-                 return new Color(170,75,75);
-               
-             case 'H':
-             case 'h':
-                 return new Color(160,175,85);
-                 
-             case 'I':
-             case 'i':
-                 return new Color(150,65,95);
-                
-             case 'J':
-             case 'j':
-                 return new Color(140,185,105);
-                 
-             case 'K':
-             case 'k':
-                 return new Color(130,55,110);
-               
-             case 'L':
-             case 'l':
-                 return new Color(120,195,120);
-       
-             case 'M':
-             case 'm':
-                 return new Color(110,45,130);
-               
-             case 'N':
-             case 'n':
-                 return new Color(100,205,140);
-                 
-             case 'O':
-             case 'o':
-                 return new Color(90,35,150);
-               
-             case 'P':
-             case 'p':
-                 return new Color(85,215,160);
-               
-             case 'Q':
-             case 'q':
-                 return new Color(75,25,170);
-       
-             case 'R':
-             case 'r':
-                 return new Color(65,225,180);
-       
-             case 'S':
-             case 's':
-                 return new Color(55,15,185);
-               
-             case 'T':
-             case 't':
-                 return new Color(45,235,195);
-                
-             case 'U':
-             case 'u':
-                 return new Color(35,5,205);
-                 
-             case 'V':
-             case 'v':
-                 return new Color(25,245,215);
-                
-             case 'W':
-             case 'w':
-                 return new Color(15,0,225);
-                 
-             case 'X':
-             case 'x':
-                 return new Color(10,255,235);
-                
-             case 'Y':
-             case 'y':
-                 return new Color(5,150,245);
-                 
-             case 'Z':
-             case 'z':
-                 return new Color(0,80,255);
-                
-       default :
-               System.out.println("This is not a interaction : "+lastss);
-               return null;
-               
-      }
-       }
-}
+  {
+    switch (lastss)
+    {
+    case '{':
+    case '}':
+      return new Color(255, 125, 5);
+
+    case '[':
+    case ']':
+      return new Color(245, 115, 10);
+
+    case '>':
+    case '<':
+      return new Color(235, 135, 15);
+
+    case 'A':
+    case 'a':
+      return new Color(225, 105, 20);
+
+    case 'B':
+    case 'b':
+      return new Color(215, 145, 30);
+
+    case 'C':
+    case 'c':
+      return new Color(205, 95, 35);
 
 
-       
-       
+    case 'D':
+    case 'd':
+      return new Color(195, 155, 45);
+
+    case 'E':
+    case 'e':
+      return new Color(185, 85, 55);
+
+    case 'F':
+    case 'f':
+      return new Color(175, 165, 65);
+
+    case 'G':
+    case 'g':
+      return new Color(170, 75, 75);
+
+    case 'H':
+    case 'h':
+      return new Color(160, 175, 85);
+
+    case 'I':
+    case 'i':
+      return new Color(150, 65, 95);
+
+    case 'J':
+    case 'j':
+      return new Color(140, 185, 105);
+
+    case 'K':
+    case 'k':
+      return new Color(130, 55, 110);
+
+    case 'L':
+    case 'l':
+      return new Color(120, 195, 120);
+
+    case 'M':
+    case 'm':
+      return new Color(110, 45, 130);
+
+    case 'N':
+    case 'n':
+      return new Color(100, 205, 140);
+
+    case 'O':
+    case 'o':
+      return new Color(90, 35, 150);
+
+    case 'P':
+    case 'p':
+      return new Color(85, 215, 160);
+
+    case 'Q':
+    case 'q':
+      return new Color(75, 25, 170);
+
+    case 'R':
+    case 'r':
+      return new Color(65, 225, 180);
+
+    case 'S':
+    case 's':
+      return new Color(55, 15, 185);
+
+    case 'T':
+    case 't':
+      return new Color(45, 235, 195);
+
+    case 'U':
+    case 'u':
+      return new Color(35, 5, 205);
+
+    case 'V':
+    case 'v':
+      return new Color(25, 245, 215);
+
+    case 'W':
+    case 'w':
+      return new Color(15, 0, 225);
+
+    case 'X':
+    case 'x':
+      return new Color(10, 255, 235);
+
+    case 'Y':
+    case 'y':
+      return new Color(5, 150, 245);
+
+    case 'Z':
+    case 'z':
+      return new Color(0, 80, 255);
+
+    default:
+      System.out.println("This is not a interaction : " + lastss);
+      return null;
+
+    }
+  }
+}