Changeset 14328 in josm for trunk/src/com


Ignore:
Timestamp:
2018-10-14T15:15:50+02:00 (8 years ago)
Author:
Don-vip
Message:

see #14319, see #16838 - update to svgSalamander 1.1.2

Location:
trunk/src/com/kitfox/svg
Files:
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/com/kitfox/svg/ClipPath.java

    r11525 r14328  
    132132     * all attributes with track information.
    133133     *
    134      * @param curTime
     134     * @param curTime Time at which to evaluate node
    135135     * @return - true if this node has changed state as a result of the time
    136136     * update
  • trunk/src/com/kitfox/svg/FillElement.java

    r8084 r14328  
    5959     * @param xform - The current transformation that the shape is being
    6060     * rendered under.
     61     * @return paint object
    6162     */
    6263    abstract public Paint getPaint(Rectangle2D bounds, AffineTransform xform);
  • trunk/src/com/kitfox/svg/Gradient.java

    r11525 r14328  
    160160    }
    161161
     162    private void buildStops()
     163    {
     164        ArrayList<Stop> stopList = new ArrayList<>(stops);
     165        stopList.sort((o1, o2) -> Float.compare(o1.offset, o2.offset));
     166
     167        //Remove doubles
     168        for (int i = stopList.size() - 2; i > 0; --i)
     169        {
     170            if (stopList.get(i + 1).offset == stopList.get(i).offset)
     171            {
     172                stopList.remove(i + 1);
     173            }
     174        }
     175       
     176        stopFractions = new float[stopList.size()];
     177        stopColors = new Color[stopList.size()];
     178        int idx = 0;
     179        for (Stop stop : stopList)
     180        {
     181            int stopColorVal = stop.color.getRGB();
     182            Color stopColor = new Color((stopColorVal >> 16) & 0xff, (stopColorVal >> 8) & 0xff, stopColorVal & 0xff, clamp((int) (stop.opacity * 255), 0, 255));
     183
     184            stopColors[idx] = stopColor;
     185            stopFractions[idx] = stop.offset;
     186            idx++;
     187        }
     188    }
     189
    162190    public float[] getStopFractions()
    163191    {
    … …  
    173201        }
    174202
    175         stopFractions = new float[stops.size()];
    176         int idx = 0;
    177         for (Stop stop : stops) {
    178             float val = stop.offset;
    179             if (idx != 0 && val < stopFractions[idx - 1])
    180             {
    181                 val = stopFractions[idx - 1];
    182             }
    183             stopFractions[idx++] = val;
    184         }
     203        buildStops();
    185204
    186205        return stopFractions;
    … …  
    200219        }
    201220
    202         stopColors = new Color[stops.size()];
    203         int idx = 0;
    204         for (Stop stop : stops) {
    205             int stopColorVal = stop.color.getRGB();
    206             Color stopColor = new Color((stopColorVal >> 16) & 0xff, (stopColorVal >> 8) & 0xff, stopColorVal & 0xff, clamp((int) (stop.opacity * 255), 0, 255));
    207             stopColors[idx++] = stopColor;
    208         }
     221        buildStops();
    209222
    210223        return stopColors;
    211     }
    212 
    213     public void setStops(Color[] colors, float[] fractions)
    214     {
    215         if (colors.length != fractions.length)
    216         {
    217             throw new IllegalArgumentException();
    218         }
    219 
    220         this.stopColors = colors;
    221         this.stopFractions = fractions;
    222         stopRef = null;
    223224    }
    224225
  • trunk/src/com/kitfox/svg/Group.java

    r11525 r14328  
    259259     * Recalculates the bounding box by taking the union of the bounding boxes
    260260     * of all children. Caches the result.
     261     * @throws com.kitfox.svg.SVGException
    261262     */
    262263    public void calcBoundingBox() throws SVGException
  • trunk/src/com/kitfox/svg/ImageSVG.java

    r11526 r14328  
    115115            {
    116116                URI src = sty.getURIValue(getXMLBase());
    117                 // CVE-2017-5617: Allow only data scheme
    118117                if ("data".equals(src.getScheme()))
    119118                {
    120119                    imageSrc = new URL(null, src.toASCIIString(), new Handler());
    121120                }
     121                else
     122                {
     123                    if (!diagram.getUniverse().isImageDataInlineOnly())
     124                    {
     125                        try
     126                        {
     127                            imageSrc = src.toURL();
     128                        } catch (Exception e)
     129                        {
     130                            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
     131                                "Could not parse xlink:href " + src, e);
     132                            imageSrc = null;
     133                        }
     134                    }
     135                }
    122136            }
    123137        } catch (Exception e)
    … …  
    126140        }
    127141
    128         if (imageSrc != null)
    129         {
    130             diagram.getUniverse().registerImage(imageSrc);
    131 
    132             //Set widths if not set
    133             BufferedImage img = diagram.getUniverse().getImage(imageSrc);
    134             if (img == null)
    135             {
    136                 xform = new AffineTransform();
    137                 bounds = new Rectangle2D.Float();
    138                 return;
    139             }
    140 
    141             if (width == 0)
    142             {
    143                 width = img.getWidth();
    144             }
    145             if (height == 0)
    146             {
    147                 height = img.getHeight();
    148             }
    149 
    150             //Determine image xform
     142        diagram.getUniverse().registerImage(imageSrc);
     143
     144        //Set widths if not set
     145        BufferedImage img = diagram.getUniverse().getImage(imageSrc);
     146        if (img == null)
     147        {
    151148            xform = new AffineTransform();
    152             xform.translate(this.x, this.y);
    153             xform.scale(this.width / img.getWidth(), this.height / img.getHeight());
    154         }
     149            bounds = new Rectangle2D.Float();
     150            return;
     151        }
     152
     153        if (width == 0)
     154        {
     155            width = img.getWidth();
     156        }
     157        if (height == 0)
     158        {
     159            height = img.getHeight();
     160        }
     161
     162        //Determine image xform
     163        xform = new AffineTransform();
     164        xform.translate(this.x, this.y);
     165        xform.scale(this.width / img.getWidth(), this.height / img.getHeight());
    155166
    156167        bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height);
    … …  
    326337                URI src = sty.getURIValue(getXMLBase());
    327338
    328                 URL newVal = null;
    329                 // CVE-2017-5617: Allow only data scheme
     339                URL newVal;
    330340                if ("data".equals(src.getScheme()))
    331341                {
    332342                    newVal = new URL(null, src.toASCIIString(), new Handler());
     343                } else
     344                {
     345                    newVal = src.toURL();
    333346                }
    334347
    335                 if (newVal != null && !newVal.equals(imageSrc))
     348                if (!newVal.equals(imageSrc))
    336349                {
    337350                    imageSrc = newVal;
  • trunk/src/com/kitfox/svg/MissingGlyph.java

    r11525 r14328  
    6161    private Shape path = null;
    6262    //Alternately, we may have child graphical elements
    63     private int horizAdvX = -1;  //Inherits font's value if not set
    64     private int vertOriginX = -1;  //Inherits font's value if not set
    65     private int vertOriginY = -1;  //Inherits font's value if not set
    66     private int vertAdvY = -1;  //Inherits font's value if not set
     63    private float horizAdvX = -1;  //Inherits font's value if not set
     64    private float vertOriginX = -1;  //Inherits font's value if not set
     65    private float vertOriginY = -1;  //Inherits font's value if not set
     66    private float vertAdvY = -1;  //Inherits font's value if not set
    6767
    6868    /**
    … …  
    132132        if (getPres(sty.setName("horiz-adv-x")))
    133133        {
    134             horizAdvX = sty.getIntValue();
     134            horizAdvX = sty.getFloatValue();
    135135        }
    136136
    137137        if (getPres(sty.setName("vert-origin-x")))
    138138        {
    139             vertOriginX = sty.getIntValue();
     139            vertOriginX = sty.getFloatValue();
    140140        }
    141141
    142142        if (getPres(sty.setName("vert-origin-y")))
    143143        {
    144             vertOriginY = sty.getIntValue();
     144            vertOriginY = sty.getFloatValue();
    145145        }
    146146
    147147        if (getPres(sty.setName("vert-adv-y")))
    148148        {
    149             vertAdvY = sty.getIntValue();
     149            vertAdvY = sty.getFloatValue();
    150150        }
    151151    }
    … …  
    179179    }
    180180
    181     public int getHorizAdvX()
     181    public float getHorizAdvX()
    182182    {
    183183        if (horizAdvX == -1)
    … …  
    188188    }
    189189
    190     public int getVertOriginX()
     190    public float getVertOriginX()
    191191    {
    192192        if (vertOriginX == -1)
    … …  
    197197    }
    198198
    199     public int getVertOriginY()
     199    public float getVertOriginY()
    200200    {
    201201        if (vertOriginY == -1)
    … …  
    206206    }
    207207
    208     public int getVertAdvY()
     208    public float getVertAdvY()
    209209    {
    210210        if (vertAdvY == -1)
    … …  
    261261     * @param horizAdvX the horizAdvX to set
    262262     */
    263     public void setHorizAdvX(int horizAdvX)
     263    public void setHorizAdvX(float horizAdvX)
    264264    {
    265265        this.horizAdvX = horizAdvX;
    … …  
    269269     * @param vertOriginX the vertOriginX to set
    270270     */
    271     public void setVertOriginX(int vertOriginX)
     271    public void setVertOriginX(float vertOriginX)
    272272    {
    273273        this.vertOriginX = vertOriginX;
    … …  
    277277     * @param vertOriginY the vertOriginY to set
    278278     */
    279     public void setVertOriginY(int vertOriginY)
     279    public void setVertOriginY(float vertOriginY)
    280280    {
    281281        this.vertOriginY = vertOriginY;
    … …  
    285285     * @param vertAdvY the vertAdvY to set
    286286     */
    287     public void setVertAdvY(int vertAdvY)
     287    public void setVertAdvY(float vertAdvY)
    288288    {
    289289        this.vertAdvY = vertAdvY;
  • trunk/src/com/kitfox/svg/RadialGradient.java

    r11525 r14328  
    3737
    3838import com.kitfox.svg.xml.StyleAttribute;
     39import java.awt.Color;
    3940import java.awt.MultipleGradientPaint;
    4041import java.awt.Paint;
    … …  
    129130        Point2D.Float pt1 = new Point2D.Float(cx, cy);
    130131        Point2D.Float pt2 = hasFocus ? new Point2D.Float(fx, fy) : pt1;
     132        float[] stopFractions = getStopFractions();
     133        Color[] stopColors = getStopColors();
    131134        if (gradientUnits == GU_USER_SPACE_ON_USE)
    132135        {
    … …  
    135138                r,
    136139                pt2,
    137                 getStopFractions(),
    138                 getStopColors(),
     140                stopFractions,
     141                stopColors,
    139142                method,
    140143                MultipleGradientPaint.ColorSpaceType.SRGB,
    … …  
    152155                r,
    153156                pt2,
    154                 getStopFractions(),
    155                 getStopColors(),
     157                stopFractions,
     158                stopColors,
    156159                method,
    157160                MultipleGradientPaint.ColorSpaceType.SRGB,
  • trunk/src/com/kitfox/svg/RenderableElement.java

    r11525 r14328  
    112112     * Pushes transform stack, transforms to local coordinates and sets up
    113113     * clipping mask.
     114     *
     115     * @param g Graphics context
     116     * @throws com.kitfox.svg.SVGException
    114117     */
    115118    protected void beginLayer(Graphics2D g) throws SVGException
    … …  
    166169     * Restores transform and clipping values to the way they were before this
    167170     * layer was drawn.
     171     * @param g
    168172     */
    169173    protected void finishLayer(Graphics2D g)
  • trunk/src/com/kitfox/svg/SVGDiagram.java

    r11525 r14328  
    9090    final URI xmlBase;
    9191
    92     /** Creates a new instance of SVGDiagram */
     92    /**
     93     * Creates a new instance of SVGDiagram
     94     * @param xmlBase
     95     * @param universe
     96     */
    9397    public SVGDiagram(URI xmlBase, SVGUniverse universe)
    9498    {
    … …  
    100104    /**
    101105     * Draws this diagram to the passed graphics context
     106     * @param g
     107     * @throws com.kitfox.svg.SVGException
    102108     */
    103109    public void render(Graphics2D g) throws SVGException
    … …  
    114120     * SVGElement.getPath() is added for each entry.
    115121     *
     122     * @param point
     123     * @param retVec
    116124     * @return the passed in list
     125     * @throws com.kitfox.svg.SVGException
    117126     */
    118127    public List<List<SVGElement>> pick(Point2D point, List<List<SVGElement>> retVec) throws SVGException
    … …  
    179188    /**
    180189     * Returns the viewing rectangle of this diagram in device coordinates.
     190     * @param rect
     191     * @return
    181192     */
    182193    public Rectangle2D getViewRect(Rectangle2D rect)
    … …  
    224235     * Updates all attributes in this diagram associated with a time event.
    225236     * Ie, all attributes with track information.
     237     * @param curTime
     238     * @throws com.kitfox.svg.SVGException
    226239     */
    227240    public void updateTime(double curTime) throws SVGException
    … …  
    240253     * SVGRoot when its x, y, width or height parameters are specified as
    241254     * percentages.
     255     * @param deviceViewport
    242256     */
    243257    public void setDeviceViewport(Rectangle deviceViewport)
  • trunk/src/com/kitfox/svg/SVGElement.java

    r11525 r14328  
    156156
    157157    /**
     158     * @param retVec
    158159     * @return an ordered list of nodes from the root of the tree to this node
    159160     */
    … …  
    212213     * Searches children for given element. If found, returns index of child.
    213214     * Otherwise returns -1.
     215     * @param child
     216     * @return index of child
    214217     */
    215218    public int indexOfChild(SVGElement child)
    … …  
    221224     * Swaps 2 elements in children.
    222225     *
    223      * @i index of first
    224      * @j index of second
    225      *
    226      * @return true if successful, false otherwise
     226     * @param i index of first child
     227     * @param j index of second child
     228     * @throws com.kitfox.svg.SVGException
    227229     */
    228230    public void swapChildren(int i, int j) throws SVGException
    … …  
    246248     * @param helper - An object passed to all SVG elements involved in this
    247249     * build process to aid in sharing information.
     250     * @param parent
     251     * @throws org.xml.sax.SAXException
    248252     */
    249253    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
    … …  
    317321     * Called after the start element but before the end element to indicate
    318322     * each child tag that has been processed
     323     * @param helper
     324     * @param child
     325     * @throws com.kitfox.svg.SVGElementException
    319326     */
    320327    public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
    … …  
    346353    /**
    347354     * Called during load process to add text scanned within a tag
     355     * @param helper
     356     * @param text
    348357     */
    349358    public void loaderAddText(SVGLoaderHelper helper, String text)
    … …  
    354363     * Called to indicate that this tag and the tags it contains have been
    355364     * completely processed, and that it should finish any load processes.
     365     * @param helper
     366     * @throws com.kitfox.svg.SVGParseException
    356367     */
    357368    public void loaderEndElement(SVGLoaderHelper helper) throws SVGParseException
    … …  
    370381     * Called by internal processes to rebuild the geometry of this node from
    371382     * it's presentation attributes, style attributes and animated tracks.
     383     * @throws com.kitfox.svg.SVGException
    372384     */
    373385    protected void build() throws SVGException
    … …  
    420432     * Hack to allow nodes to temporarily change their parents. The Use tag will
    421433     * need this so it can alter the attributes that a particular node uses.
     434     * @param context
    422435     */
    423436    protected void pushParentContext(SVGElement context)
    … …  
    465478     * style attribute, checks attributes of parents back to root until one
    466479     * found.
     480     * @return
    467481     */
    468482    public boolean getStyle(StyleAttribute attrib, boolean recursive) throws SVGException
    … …  
    523537
    524538    /**
     539     * @param styName
    525540     * @return the raw style value of this attribute. Does not take the
    526541     * presentation value or animation into consideration. Used by animations to
    … …  
    536551     * Copies the presentation attribute into the passed one.
    537552     *
     553     * @param attrib
    538554     * @return - True if attribute was read successfully
     555     * @throws com.kitfox.svg.SVGException
    539556     */
    540557    public boolean getPres(StyleAttribute attrib) throws SVGException
    … …  
    558575
    559576    /**
     577     * @param styName
    560578     * @return the raw presentation value of this attribute. Ignores any
    561579     * modifications applied by style attributes or animation. Used by
    … …  
    810828     * all attributes with track information.
    811829     *
     830     * @param curTime
    812831     * @return - true if this node has changed state as a result of the time
    813832     * update
     833     * @throws com.kitfox.svg.SVGException
    814834     */
    815835    abstract public boolean updateTime(double curTime) throws SVGException;
  • trunk/src/com/kitfox/svg/SVGElementException.java

    r8084 r14328  
    4949    /**
    5050     * Creates a new instance of <code>SVGException</code> without detail message.
     51     * @param element
    5152     */
    5253    public SVGElementException(SVGElement element)
    … …  
    5859    /**
    5960     * Constructs an instance of <code>SVGException</code> with the specified detail message.
     61     * @param element
    6062     * @param msg the detail message.
    6163     */
  • trunk/src/com/kitfox/svg/SVGLoader.java

    r11525 r14328  
    7676
    7777    final boolean verbose;
    78 
    79     /** Creates a new instance of SVGLoader */
     78   
     79    /**
     80     * Creates a new instance of SVGLoader
     81     * @param xmlBase
     82     * @param universe
     83     */
    8084    public SVGLoader(URI xmlBase, SVGUniverse universe)
    8185    {
  • trunk/src/com/kitfox/svg/SVGLoaderHelper.java

    r11525 r14328  
    5757    public final URI xmlBase;
    5858
    59     /** Creates a new instance of SVGLoaderHelper */
     59    /**
     60     * Creates a new instance of SVGLoaderHelper
     61     * @param xmlBase
     62     * @param universe
     63     * @param diagram
     64     */
    6065    public SVGLoaderHelper(URI xmlBase, SVGUniverse universe, SVGDiagram diagram)
    6166    {
  • trunk/src/com/kitfox/svg/SVGUniverse.java

    r11525 r14328  
    105105    XMLReader cachedReader;
    106106
     107    //If true, <imageSVG> elements will only load image data that is included using inline data: uris
     108    private boolean imageDataInlineOnly = false;
     109
    107110    /**
    108111     * Creates a new instance of SVGUniverse
    … …  
    130133        loadedFonts.clear();
    131134        loadedImages.clear();
     135    }
     136
     137    /**
     138     * Returns the current animation time in milliseconds.
     139     */
     140    public double getCurTime()
     141    {
     142        return curTime;
     143    }
     144
     145    public void setCurTime(double curTime)
     146    {
     147        double oldTime = this.curTime;
     148        this.curTime = curTime;
     149        changes.firePropertyChange("curTime", new Double(oldTime), new Double(curTime));
     150    }
     151
     152    /**
     153     * Updates all time influenced style and presentation attributes in all SVG
     154     * documents in this universe.
     155     */
     156    public void updateTime() throws SVGException
     157    {
     158        for (SVGDiagram dia : loadedDocs.values()) {
     159            dia.updateTime(curTime);
     160        }
    132161    }
    133162
    … …  
    648677        return universe;
    649678    }
     679
     680    /**
     681     * @return the imageDataInlineOnly
     682     */
     683    public boolean isImageDataInlineOnly()
     684    {
     685        return imageDataInlineOnly;
     686    }
     687
     688    /**
     689     * @param imageDataInlineOnly the imageDataInlineOnly to set
     690     */
     691    public void setImageDataInlineOnly(boolean imageDataInlineOnly)
     692    {
     693        this.imageDataInlineOnly = imageDataInlineOnly;
     694    }
    650695}
  • trunk/src/com/kitfox/svg/Text.java

    r11525 r14328  
    272272        for (int i = 0; i < families.length; ++i)
    273273        {
    274             font = diagram.getUniverse().getFont(fontFamily);
     274            font = diagram.getUniverse().getFont(families[i]);
    275275            if (font != null)
    276276            {
    … …  
    278278            }
    279279        }
    280        
     280
    281281        if (font == null)
    282282        {
    283             font = new FontSystem(fontFamily, fontStyle, fontWeight, (int)fontSize);
     283            //Check system fonts
     284            font = FontSystem.createFont(fontFamily, fontStyle, fontWeight, (int)fontSize);
     285        }
     286
     287        if (font == null)
     288        {
     289            font = FontSystem.createFont("Serif", fontStyle, fontWeight, fontStyle);
    284290        }
    285291
  • trunk/src/com/kitfox/svg/Tspan.java

    r11525 r14328  
    231231        if (font == null)
    232232        {
    233             font = new FontSystem(fontFamily, fontStyle, fontWeight, (int)fontSize);
     233            font = FontSystem.createFont(fontFamily, fontStyle, fontWeight, (int)fontSize);
    234234        }
    235235
  • trunk/src/com/kitfox/svg/pathcmd/Arc.java

    r11525 r14328  
    104104     * point of the arc.
    105105     *
     106     * @param path The path that the arc will be appended to.
     107     *
    106108     * @param rx the x radius of the ellipse
    107109     * @param ry the y radius of the ellipse
    … …  
    160162     * AffineTransform.getRotateInstance
    161163     *     (angle, arc.getX()+arc.getWidth()/2, arc.getY()+arc.getHeight()/2);
     164     *
     165     * @param x0 origin of arc in x
     166     * @param y0 origin of arc in y
     167     * @param rx radius of arc in x
     168     * @param ry radius of arc in y
     169     * @param angle number of radians in arc
     170     * @param largeArcFlag
     171     * @param sweepFlag
     172     * @param x ending coordinate of arc in x
     173     * @param y ending coordinate of arc in y
     174     * @return arc shape
     175     *
    162176     */
    163177    public static Arc2D computeArc(double x0, double y0,
  • trunk/src/com/kitfox/svg/util/FontSystem.java

    r11525 r14328  
    4242import java.awt.Canvas;
    4343import java.awt.FontMetrics;
     44import java.awt.GraphicsEnvironment;
    4445import java.awt.font.FontRenderContext;
    4546import java.awt.font.GlyphMetrics;
    4647import java.awt.font.GlyphVector;
    4748import java.util.HashMap;
     49import java.util.HashSet;
    4850
    4951/**
    … …  
    5860    HashMap<String, Glyph> glyphCache = new HashMap<String, Glyph>();
    5961   
    60     public FontSystem(String fontFamily, int fontStyle, int fontWeight, int fontSize)
     62    static HashSet<String> sysFontNames = new HashSet<String>();
     63
     64    public static boolean checkIfSystemFontExists(String fontName)
     65    {
     66        if (sysFontNames.isEmpty())
     67        {
     68            for (String name: GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())
     69            {
     70                sysFontNames.add(name);
     71            }
     72        }
     73
     74        return sysFontNames.contains(fontName);
     75    }
     76
     77    public static FontSystem createFont(String fontFamily, int fontStyle, int fontWeight, int fontSize)
     78    {
     79        String[] families = fontFamily.split(",");
     80        for (String fontName: families)
     81        {
     82            if (checkIfSystemFontExists(fontName))
     83            {
     84                return new FontSystem(fontName, fontStyle, fontWeight, fontSize);
     85            }
     86        }
     87
     88        return null;
     89    }
     90
     91    private FontSystem(String fontFamily, int fontStyle, int fontWeight, int fontSize)
    6192    {
    6293        int style;
    … …  
    82113                break;
    83114        }
    84         sysFont = new java.awt.Font(fontFamily, style | weight, (int) fontSize);
     115
     116        sysFont = new java.awt.Font(fontFamily, style | weight, fontSize);
    85117       
    86118        Canvas c = new Canvas();
    … …  
    100132        GlyphVector vec = sysFont.createGlyphVector(frc, unicode);
    101133       
    102         Glyph glyph = (Glyph)glyphCache.get(unicode);
     134        Glyph glyph = glyphCache.get(unicode);
    103135        if (glyph == null)
    104136        {
    … …  
    107139
    108140            GlyphMetrics gm = vec.getGlyphMetrics(0);
    109             glyph.setHorizAdvX((int)gm.getAdvanceX());
    110             glyph.setVertAdvY((int)gm.getAdvanceY());
     141            glyph.setHorizAdvX(gm.getAdvanceX());
     142            glyph.setVertAdvY(gm.getAdvanceY());
    111143            glyph.setVertOriginX(0);
    112144            glyph.setVertOriginY(0);
Note: See TracChangeset for help on using the changeset viewer.