001package armyc2.c5isr.renderer;
002
003import android.graphics.Bitmap;
004import android.graphics.Bitmap.Config;
005import android.graphics.Canvas;
006import android.graphics.Paint;
007import android.graphics.Paint.FontMetrics;
008import android.graphics.Point;
009import android.graphics.PointF;
010import android.graphics.Rect;
011import android.graphics.RectF;
012import android.util.Log;
013
014import com.caverock.androidsvg.SVG;
015
016import java.util.HashMap;
017import java.util.Map;
018
019import armyc2.c5isr.renderer.utilities.Color;
020import armyc2.c5isr.renderer.utilities.DrawRules;
021import armyc2.c5isr.renderer.utilities.ErrorLogger;
022import armyc2.c5isr.renderer.utilities.ImageInfo;
023import armyc2.c5isr.renderer.utilities.MSInfo;
024import armyc2.c5isr.renderer.utilities.MSLookup;
025import armyc2.c5isr.renderer.utilities.MilStdAttributes;
026import armyc2.c5isr.renderer.utilities.Modifiers;
027import armyc2.c5isr.renderer.utilities.RectUtilities;
028import armyc2.c5isr.renderer.utilities.RendererSettings;
029import armyc2.c5isr.renderer.utilities.RendererUtilities;
030import armyc2.c5isr.renderer.utilities.SVGInfo;
031import armyc2.c5isr.renderer.utilities.SVGLookup;
032import armyc2.c5isr.renderer.utilities.SVGSymbolInfo;
033import armyc2.c5isr.renderer.utilities.SettingsChangedEvent;
034import armyc2.c5isr.renderer.utilities.SettingsChangedEventListener;
035import armyc2.c5isr.renderer.utilities.SymbolDimensionInfo;
036import armyc2.c5isr.renderer.utilities.SymbolID;
037import armyc2.c5isr.renderer.utilities.SymbolUtilities;
038
039public class SinglePointSVGRenderer implements SettingsChangedEventListener
040{
041
042    private final String TAG = "SinglePointRenderer";
043    private static SinglePointSVGRenderer _instance = null;
044
045    private final Object _SinglePointCacheMutex = new Object();
046    private final Object _UnitCacheMutex = new Object();
047
048    private Paint _modifierFont = new Paint();
049    private Paint _modifierOutlineFont = new Paint();
050    private float _modifierDescent = 2;
051    private float _modifierFontHeight = 10;
052    private int _deviceDPI = 72;
053
054
055    private SinglePointSVGRenderer()
056    {
057        RendererSettings.getInstance().addEventListener(this);
058        
059        //get modifier font values.
060        onSettingsChanged(new SettingsChangedEvent(SettingsChangedEvent.EventType_FontChanged));
061    }
062
063    public static synchronized SinglePointSVGRenderer getInstance()
064    {
065        if (_instance == null)
066        {
067            _instance = new SinglePointSVGRenderer();
068        }
069
070        return _instance;
071    }
072
073    /**
074     *
075     * @param symbolID
076     * @param modifiers
077     * @return
078     */
079    public SVGSymbolInfo RenderUnit(String symbolID, Map<String,String> modifiers, Map<String,String> attributes)
080    {
081        SVGSymbolInfo si = null;
082        SymbolDimensionInfo newSDI = null;
083
084        String lineColor = null;//SymbolUtilitiesD.getLineColorOfAffiliation(symbolID);
085        String fillColor = null;
086
087        if(SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_MineWarfare && RendererSettings.getInstance().getSeaMineRenderMethod()==RendererSettings.SeaMineRenderMethod_MEDAL)
088        {
089            lineColor = RendererUtilities.colorToHexString(SymbolUtilities.getLineColorOfAffiliation(symbolID), false);
090            fillColor = RendererUtilities.colorToHexString(SymbolUtilities.getFillColorOfAffiliation(symbolID), true);
091        }
092
093        String iconColor = null;
094
095        int alpha = 255;
096
097        //SVG values
098        String frameID = null;
099        String iconID = null;
100        String mod1ID = null;
101        String mod2ID = null;
102        SVGInfo siFrame = null;
103        SVGInfo siIcon = null;
104        SVGInfo siMod1 = null;
105        SVGInfo siMod2 = null;
106        SVG mySVG = null;
107        int top = 0;
108        int left = 0;
109        int width = 0;
110        int height = 0;
111        String svgStart = null;
112        String strSVG = null;
113        String strSVGFrame = null;
114
115
116        Rect symbolBounds = null;
117        Rect fullBounds = null;
118        Bitmap fullBMP = null;
119
120        boolean hasDisplayModifiers = false;
121        boolean hasTextModifiers = false;
122
123        int pixelSize = -1;
124        boolean keepUnitRatio = true;
125        boolean icon = false;
126        boolean noFrame = false;
127
128        int ver = SymbolID.getVersion(symbolID);
129
130        // <editor-fold defaultstate="collapsed" desc="Parse Attributes">
131        try
132        {
133            if(attributes != null)
134            {
135                if (attributes.containsKey(MilStdAttributes.PixelSize)) {
136                    pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
137                } else {
138                    pixelSize = RendererSettings.getInstance().getDefaultPixelSize();
139                }
140
141                if (attributes.containsKey(MilStdAttributes.KeepUnitRatio)) {
142                    keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
143                }
144
145                if (attributes.containsKey(MilStdAttributes.DrawAsIcon)) {
146                    icon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
147                }
148
149                if (icon)//icon won't show modifiers or display icons
150                {
151                    //TODO: symbolID modifications as necessary
152                    keepUnitRatio = false;
153                    hasDisplayModifiers = false;
154                    hasTextModifiers = false;
155                    //symbolID = symbolID.substring(0, 10) + "-----";
156                } else {
157                    hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers);
158                    hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers);
159                }
160
161                if (attributes.containsKey(MilStdAttributes.LineColor)) {
162                    lineColor = (attributes.get(MilStdAttributes.LineColor));
163                }
164                if (attributes.containsKey(MilStdAttributes.FillColor)) {
165                    fillColor = (attributes.get(MilStdAttributes.FillColor));
166                }
167                if (attributes.containsKey(MilStdAttributes.IconColor)) {
168                    iconColor = (attributes.get(MilStdAttributes.IconColor));
169                }//*/
170                if (attributes.containsKey(MilStdAttributes.Alpha)) {
171                    alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
172                }
173            }
174        }
175        catch (Exception excModifiers)
176        {
177            ErrorLogger.LogException("SinglePointSVGRenderer", "RenderUnit", excModifiers);
178        }
179        // </editor-fold>
180
181        try
182        {
183
184            //if not, generate symbol
185            if (si == null)//*/
186            {
187                int version = SymbolID.getVersion(symbolID);
188                //Get SVG pieces of symbol
189                frameID = SVGLookup.getFrameID(symbolID);
190                iconID = SVGLookup.getMainIconID(symbolID);
191                mod1ID = SVGLookup.getMod1ID(symbolID);
192                mod2ID = SVGLookup.getMod2ID(symbolID);
193                siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version);
194                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
195
196                if(siFrame == null)
197                {
198                    frameID = SVGLookup.getFrameID(SymbolUtilities.reconcileSymbolID(symbolID));
199                    siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version);
200                    if(siFrame == null)//still no match, get unknown frame
201                    {
202                        frameID = SVGLookup.getFrameID(SymbolID.setSymbolSet(symbolID,SymbolID.SymbolSet_Unknown));
203                        siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version);
204                    }
205                }
206
207                if(siIcon == null)
208                {
209                        if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_Unknown)
210                            siIcon = SVGLookup.getInstance().getSVGLInfo("00000000", version);//question mark
211                        /*else if(iconID.substring(2,8).equals("000000")==false && MSLookup.getInstance().getMSLInfo(symbolID) == null)
212                            siIcon = SVGLookup.getInstance().getSVGLInfo("98100000", version);//inverted question mark//*/
213                }
214
215                if(RendererSettings.getInstance().getScaleMainIcon())
216                    siIcon = RendererUtilities.scaleIcon(symbolID,siIcon);
217
218                siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version);
219                siMod2 = SVGLookup.getInstance().getSVGLInfo(mod2ID, version);
220                top = Math.round(siFrame.getBbox().top);
221                left = Math.round(siFrame.getBbox().left);
222                width = Math.round(siFrame.getBbox().width());
223                height = Math.round(siFrame.getBbox().height());
224                if(siFrame.getBbox().bottom > 400)
225                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
226                else
227                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
228
229                //update line and fill color of frame SVG
230                if(lineColor != null || fillColor != null)
231                    strSVGFrame = RendererUtilities.setSVGFrameColors(symbolID,siFrame.getSVG(),RendererUtilities.getColorFromHexString(lineColor),RendererUtilities.getColorFromHexString(fillColor));
232                else
233                    strSVGFrame = siFrame.getSVG();
234
235                if(frameID.equals("octagon"))//for the 1 unit symbol that doesn't have a frame: 30 + 15000
236                {
237                    noFrame = true;
238                    strSVGFrame = strSVGFrame.replaceFirst("<g id=\"octagon\">", "<g id=\"octagon\" display=\"none\">");
239                }
240
241
242                //get SVG dimensions and target dimensions
243                symbolBounds = RectUtilities.makeRect(left,top,width,height);
244                Rect rect = new Rect(symbolBounds);
245                float ratio = -1;
246
247                if (pixelSize > 0 && keepUnitRatio == true)
248                {
249                    float heightRatio = SymbolUtilities.getUnitRatioHeight(symbolID);
250                    float widthRatio = SymbolUtilities.getUnitRatioWidth(symbolID);
251
252                    if(noFrame == true)//using octagon with display="none" as frame for a 1x1 shape
253                    {
254                        heightRatio = 1.0f;
255                        widthRatio = 1.0f;
256                    }
257
258                    if (heightRatio > widthRatio)
259                    {
260                        pixelSize = (int) ((pixelSize / 1.5f) * heightRatio);
261                    }
262                    else
263                    {
264                        pixelSize = (int) ((pixelSize / 1.5f) * widthRatio);
265                    }
266                }
267                if (pixelSize > 0)
268                {
269                    float p = pixelSize;
270                    float h = rect.height();
271                    float w = rect.width();
272
273                    ratio = Math.min((p / h), (p / w));
274
275                    symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio);
276                }
277
278                //StringBuilder sbGroupUnit = new StringBuilder();
279                String sbGroupUnit = "";
280                if(siFrame != null)
281                {
282                    sbGroupUnit += ("<g transform=\"translate(" + (siFrame.getBbox().left * -ratio) + ',' + (siFrame.getBbox().top * -ratio) + ") scale(" + ratio + "," + ratio + ")\"" + ">");
283                    if(siFrame != null)
284                        sbGroupUnit += (strSVGFrame);//(siFrame.getSVG());
285
286                    String color = "";
287                    if(iconColor != null)
288                    {
289                        //make sure string is properly formatted.
290                        iconColor = RendererUtilities.colorToHexString(RendererUtilities.getColorFromHexString(iconColor),false);
291                        if(iconColor != null && iconColor != "#000000" && iconColor != "")
292                            color = " fill=\"" + iconColor + "\" ";
293                        else
294                            iconColor = null;
295                    }
296                    String unit = "<g" + color + ">";
297                    if (siIcon != null)
298                        unit += (siIcon.getSVG());
299                    if (siMod1 != null)
300                        unit += (siMod1.getSVG());
301                    if (siMod2 != null)
302                        unit += (siMod2.getSVG());
303                    if(iconColor != null)
304                        unit = unit.replaceAll("#000000",iconColor);
305                    unit += "</g>";
306
307                    sbGroupUnit += unit + "</g>";
308                }
309
310                //center of octagon is the center of all unit symbols
311                Point centerOctagon = new Point(306, 396);
312                centerOctagon.offset(-left,-top);//offset for the symbol bounds x,y
313                //scale center point by same ratio as the symbol
314                centerOctagon = new Point((int)(centerOctagon.x * ratio), (int)(centerOctagon.y * ratio));
315
316                //set centerpoint of the image
317                Point centerPoint = centerOctagon;
318                Point centerCache = new Point(centerOctagon.x, centerOctagon.y);
319
320                //y offset to get centerpoint so we set back to zero when done.
321                //symbolBounds.top = 0;
322                RectUtilities.shift(symbolBounds,0,(int)-symbolBounds.top);
323
324                //Add core symbol to SVGSymbolInfo
325                si =  new SVGSymbolInfo(sbGroupUnit.toString(), centerPoint,symbolBounds,symbolBounds);
326
327                hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers);
328                hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers);
329
330                //process display modifiers
331                if (hasDisplayModifiers)
332                {
333                    newSDI = ModifierRenderer.processUnitDisplayModifiers(si, symbolID, modifiers, hasTextModifiers, attributes);
334                    if(newSDI != null)
335                    {
336                        si = (SVGSymbolInfo) newSDI;
337                        newSDI = null;
338                    }
339                }
340            }
341
342            //process text modifiers
343            if (hasTextModifiers)
344            {
345                newSDI = ModifierRenderer.processSPTextModifiers(si, symbolID, modifiers, attributes);
346            }
347
348            if (newSDI != null)
349            {
350                si = (SVGSymbolInfo) newSDI;
351            }
352            newSDI = null;
353
354            if(modifiers != null)
355                si = (SVGSymbolInfo) ModifierRenderer.processSpeedLeader(si,symbolID,modifiers,attributes);
356
357            int widthOffset = 0;
358            if(hasTextModifiers)
359                widthOffset = 2;//add for the text outline
360
361            int svgWidth = (int)(si.getImageBounds().width() + widthOffset);
362            int svgHeight = (int)si.getImageBounds().height();
363            //add SVG tag with dimensions
364            //draw unit from SVG
365            String svgAlpha = "";
366            if(alpha >=0 && alpha <= 255)
367                svgAlpha = " opacity=\"" + alpha/255f + "\"";
368            svgStart = "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" + svgWidth + "\" height=\"" + svgHeight +"\" viewBox=\"" + 0 + " " + 0 + " " + svgWidth + " " + svgHeight + "\"" + svgAlpha + ">\n";
369            String svgTranslateGroup = null;
370
371            double transX = si.getImageBounds().left * -1;
372            double transY = si.getImageBounds().top * -1;
373            Point anchor = si.getCenterPoint();
374            Rect imageBounds = si.getImageBounds();
375            if(transX > 0 || transY > 0)
376            {
377                anchor.offset((int)transX,(int)transY);
378                //ShapeUtilities.offset(anchor,transX,transY);
379                RectUtilities.shift(symbolBounds,(int)transX,(int)transY);
380                //ShapeUtilities.offset(symbolBounds,transX,transY);
381                RectUtilities.shift(imageBounds,(int)transX,(int)transY);
382                //ShapeUtilities.offset(imageBounds,transX,transY);
383                svgTranslateGroup = "<g transform=\"translate(" + transX + "," + transY + ")" +"\">\n";
384            }
385            imageBounds = RectUtilities.makeRect(imageBounds.left,imageBounds.top,svgWidth,svgHeight);
386
387            si = new SVGSymbolInfo(si.getSVG(),anchor,symbolBounds,imageBounds);
388            StringBuilder sbSVG = new StringBuilder();
389            sbSVG.append(svgStart);
390            sbSVG.append(makeDescTag(si));
391            sbSVG.append(makeMetadataTag(symbolID, si));
392            if(svgTranslateGroup != null)
393                sbSVG.append(svgTranslateGroup);
394            sbSVG.append(si.getSVG());
395            if(svgTranslateGroup != null)
396                sbSVG.append("\n</g>");
397            sbSVG.append("\n</svg>");
398            si =  new SVGSymbolInfo(sbSVG.toString(),anchor,symbolBounds,imageBounds);
399
400        }
401        catch (Exception exc)
402        {
403            ErrorLogger.LogException("SinglePointSVGRenderer", "RenderUnit", exc);
404        }
405        return si;
406    }
407
408    /**
409     *
410     * @param symbolID
411     * @param modifiers
412     * @return
413     */
414    @SuppressWarnings("unused")
415    public SVGSymbolInfo RenderSP(String symbolID, Map<String,String> modifiers, Map<String,String> attributes)
416    {
417
418        SVGSymbolInfo si = null;
419
420        ImageInfo temp = null;
421        String basicSymbolID = null;
422
423        Color lineColor = SymbolUtilities.getDefaultLineColor(symbolID);
424        Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID);
425        int outlineWidth = RendererUtilities.calculateOutlineWidth();
426        int alpha = -1;
427
428
429        //SVG rendering variables
430        MSInfo msi = null;
431        String iconID = null;
432        SVGInfo siIcon = null;
433        String mod1ID = null;
434        SVGInfo siMod1 = null;
435        int top = 0;
436        int left = 0;
437        int width = 0;
438        int height = 0;
439        String svgStart = null;
440        String strSVG = null;
441        SVG mySVG = null;
442
443        float ratio = 0;
444
445        RectF symbolBounds = null;
446        RectF imageBounds = null;
447
448
449        boolean drawAsIcon = false;
450        int pixelSize = -1;
451        boolean keepUnitRatio = true;
452        boolean hasDisplayModifiers = false;
453        boolean hasTextModifiers = false;
454        boolean drawCustomOutline = false;
455
456
457        msi = MSLookup.getInstance().getMSLInfo(symbolID);
458
459        int ss = SymbolID.getSymbolSet(symbolID);
460        int ec = SymbolID.getEntityCode(symbolID);
461        int mod1 = 0;
462        int drawRule = 0;
463        if (msi != null) {
464            drawRule = msi.getDrawRule();
465        }
466        boolean hasAPFill = false;
467        if(RendererSettings.getInstance().getActionPointDefaultFill()) {
468            if (SymbolUtilities.isActionPoint(symbolID) || //action points
469                    ec/100 == 2135 || //sonobuoy
470                    ec == 180100 || ec == 180200 || ec == 180400) //ACP, CCP, PUP
471            {
472                if (SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_ControlMeasure) {
473                    lineColor = Color.BLACK;
474                    hasAPFill = true;
475                }
476            }
477        }
478        if(lineColor==null)
479            lineColor = SymbolUtilities.getDefaultLineColor(symbolID);
480
481        try
482        {
483            if (modifiers == null)
484                modifiers = new HashMap<>();
485
486
487
488            //get symbol info
489
490            msi = MSLookup.getInstance().getMSLInfo(symbolID);
491
492            if (msi == null)//if lookup fails, fix code/use unknown symbol code.
493            {
494                //TODO: change symbolID to Action Point with bad symbolID  in the T or H field
495            }
496
497
498            if (attributes != null) {
499                if (attributes.containsKey(MilStdAttributes.KeepUnitRatio)) {
500                    keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
501                }
502
503                if (attributes.containsKey(MilStdAttributes.LineColor)) {
504                    lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor));
505                }
506
507                if (attributes.containsKey(MilStdAttributes.FillColor)) {
508                    fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor));
509                }
510
511                if (attributes.containsKey(MilStdAttributes.Alpha)) {
512                    alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
513                }
514
515                if (attributes.containsKey(MilStdAttributes.DrawAsIcon)) {
516                    drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
517                }
518
519                if (attributes.containsKey(MilStdAttributes.PixelSize)) {
520                    pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
521                } else {
522                    pixelSize = RendererSettings.getInstance().getDefaultPixelSize();
523                }
524                /*if (keepUnitRatio == true && msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && msi.getGeometry().equalsIgnoreCase("point")) {
525                    if(msi.getDrawRule() == DrawRules.POINT1)//Action Points
526                        pixelSize = (int)Math.ceil((pixelSize/1.5f) * 2.0f);
527                    else if(SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_ControlMeasure &&
528                            ec/100 == 2135)//Sonobuoy
529                    {
530                        pixelSize = (int)Math.ceil((pixelSize/1.5f) * 2.0f);
531                    }
532                    else
533                        pixelSize = (int)Math.ceil((pixelSize/1.5f) * 1.2f);
534                }//*/
535
536                if(!(drawAsIcon==true || hasAPFill==true))//don't outline icons because they're not going on the map and icons with fills don't need it
537                {
538                    if (attributes.containsKey(MilStdAttributes.OutlineSymbol))
539                        drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol));
540                    else
541                        drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures();
542
543                    //Protection of Cultural Property doesn't get outlined
544                    if(ss==25 && ec >= 360000 && ec < 360400)
545                        drawCustomOutline = false;
546                }
547
548                if (SymbolUtilities.isMultiPoint(symbolID))
549                    drawCustomOutline = false;//icon previews for multipoints do not need outlines since they shouldn't be on the map
550            }
551
552            if (drawAsIcon)//icon won't show modifiers or display icons
553            {
554                keepUnitRatio = false;
555                hasDisplayModifiers = false;
556                hasTextModifiers = false;
557                drawCustomOutline = false;
558            } else {
559                hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers);
560                hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers);
561            }
562
563            //Check if we need to set 'N' to "ENY"
564            int aff = SymbolID.getAffiliation(symbolID);
565            //int ss = msi.getSymbolSet();
566            if (ss == SymbolID.SymbolSet_ControlMeasure &&
567                    (aff == SymbolID.StandardIdentity_Affiliation_Hostile_Faker ||
568                            aff == SymbolID.StandardIdentity_Affiliation_Suspect_Joker) &&
569                    modifiers.containsKey(Modifiers.N_HOSTILE) &&
570                    drawAsIcon == false) {
571                modifiers.put(Modifiers.N_HOSTILE, "ENY");
572            }
573
574        } catch (Exception excModifiers) {
575            ErrorLogger.LogException("SinglePointSVGRenderer", "RenderSP-ParseModifiers", excModifiers);
576        }
577
578        try
579        {
580            int intFill = -1;
581            if (fillColor != null) {
582                intFill = fillColor.toInt();
583            }
584
585
586            if (msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure)
587                lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined.
588
589
590            if (SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_ControlMeasure && SymbolID.getEntityCode(symbolID) == 270701)//static depiction
591            {
592                //add mine fill to image
593                mod1 = SymbolID.getModifier1(symbolID);
594                if (!(mod1 >= 13 && mod1 <= 50))
595                    symbolID = SymbolID.setModifier1(symbolID, 13);
596            }
597
598
599            //if not, generate symbol.
600            if (si == null)//*/
601            {
602                int version = SymbolID.getVersion(symbolID);
603                //check symbol size////////////////////////////////////////////
604                Rect rect = null;
605                iconID = SVGLookup.getMainIconID(symbolID);
606                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
607                if(siIcon==null) {
608                    return null;
609                }
610                mod1ID = SVGLookup.getMod1ID(symbolID);
611                siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version);
612                float borderPadding = 0;
613                if (drawCustomOutline) {
614                    borderPadding = (int)Math.ceil(outlineWidth/2f);
615                    if(borderPadding % 2 > 0)
616                        borderPadding++;
617                }
618
619                //Oceanographic / Bottom Feature - essentially italic serif fonts need more vertical space
620                //pixel sizes above 150 it's fine, which is weird
621                if(SymbolUtilities.getBasicSymbolID(symbolID).startsWith("461206"))
622                {
623                    double va = siIcon.getBbox().height() * 0.025;
624                    double ha = siIcon.getBbox().width() * 0.025;//some also need to be slightly wider
625                    Rect adjustment = RectUtilities.makeRect((float)(siIcon.getBbox().left),(float)(siIcon.getBbox().top - va),(float)(siIcon.getBbox().width() + ha),(float)(siIcon.getBbox().height() + va));
626                    siIcon.getBbox().set(adjustment);
627                }
628
629                top = (int)Math.floor(siIcon.getBbox().top);
630                left = (int)Math.floor(siIcon.getBbox().left);
631                width = (int)Math.ceil(siIcon.getBbox().width() + (siIcon.getBbox().left - left));
632                height = (int)Math.ceil(siIcon.getBbox().height() + (siIcon.getBbox().top - top));
633                if (siIcon.getBbox().bottom > 400)
634                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
635                else
636                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
637
638                String strSVGIcon = null;
639
640                if(keepUnitRatio)
641                {
642                    double scaler = Math.max(width/(float)height, height/(float)width);
643                    if (scaler < 1.2)
644                        scaler = 1.2;
645                    if (scaler > 2)
646                        scaler = 2;
647
648                    if(!SymbolUtilities.isCBRNEvent(symbolID))
649                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * scaler);
650
651                    /*
652                    double min = Math.min(width/(float)height, height/(float)width);
653                    if (min < 0.6)//Rectangle
654                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 2.0f);
655                    else if(min < 0.85)
656                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.8f);
657                    else //more of a square
658                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.2f);//*/
659                }
660
661                if (hasAPFill) //action points and a few others //Sonobuoy //ACP, CCP, PUP
662                {
663                    String apFill;
664                    if (fillColor != null)
665                        apFill = RendererUtilities.colorToHexString(fillColor, false);
666                    else
667                        apFill = RendererUtilities.colorToHexString(SymbolUtilities.getFillColorOfAffiliation(symbolID), false);
668                    siIcon = new SVGInfo(siIcon.getID(), siIcon.getBbox(), siIcon.getSVG().replaceAll("fill=\"none\"", "fill=\"" + apFill + "\""));
669                }
670
671                //Set dash array depending on affiliation and status
672                siIcon = RendererUtilities.setAffiliationDashArray(symbolID, siIcon);
673
674                //Generate Affiliation Planned Circle for version 16
675                SVGSymbolInfo circle = ModifierRenderer.createPlannedCircle(siIcon.getBbox(),symbolID);
676                if(circle != null)
677                {
678                    symbolBounds = circle.getSymbolBoundsF();
679                    top = (int)Math.floor(circle.getImageBoundsF().top);
680                    left = (int)Math.floor(circle.getImageBoundsF().left);
681                    width = (int)Math.round(Math.ceil(circle.getImageBoundsF().width() + (circle.getImageBoundsF().left - left)));
682                    height = (int)Math.round(Math.ceil(circle.getImageBoundsF().height() + (circle.getImageBoundsF().top - top)));
683                    if(keepUnitRatio)
684                        pixelSize = (int)(pixelSize * (width / Math.max(siIcon.getBbox().width(),siIcon.getBbox().height())));
685                    String newSVG = siIcon.getSVG().substring(0,siIcon.getSVG().lastIndexOf("</g>"));
686                    newSVG += circle.getSVG() + "</g>";
687                    siIcon = new SVGInfo(siIcon.getID(),circle.getImageBoundsF(), newSVG);
688                }
689
690                //update line and fill color of frame SVG
691                if (msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) {
692                    if (drawCustomOutline) {
693                        // create outline with larger stroke-width first (if selected)
694                        strSVGIcon = RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), RendererUtilities.getIdealOutlineColor(lineColor), fillColor, true,siIcon.getBbox(),pixelSize,outlineWidth);
695                    }
696
697                    // append normal symbol SVG to be layered on top of outline
698                    strSVGIcon += RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), lineColor, fillColor);
699                } else//weather symbol (don't change color of weather graphics)
700                    strSVGIcon = siIcon.getSVG();
701
702                //If symbol is Static Depiction, add internal mine graphic based on sector modifier 1
703                if (SymbolID.getEntityCode(symbolID) == 270701 && siMod1 != null) {
704                    if (drawCustomOutline) {
705                        // create outline with larger stroke-width first (if selected)
706                        strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), RendererUtilities.getIdealOutlineColor(RendererUtilities.getColorFromHexString("#00A651")), RendererUtilities.getColorFromHexString("#00A651"), true,siIcon.getBbox(),pixelSize,outlineWidth);
707                    }
708                    //strSVGIcon += siMod1.getSVG();
709                    strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), lineColor, fillColor);
710                }
711
712                if (pixelSize > 0)
713                {
714                    imageBounds = RectUtilities.makeRectF(left,top,width,height);
715                    if(circle != null)
716                        symbolBounds = circle.getSymbolBoundsF();
717                    else
718                        symbolBounds = new RectF(imageBounds);
719
720                    rect = RectUtilities.makeRectFromRectF(imageBounds);
721
722                    //adjust size
723                    float p = pixelSize;
724                    float h = rect.height();
725                    float w = rect.width();
726
727                    ratio = Math.min((p / h), (p / w));
728
729                    //measurement of target size/location of symbol after being translated/scaled into the new SVG
730                    symbolBounds = RectUtilities.makeRectF((symbolBounds.left - imageBounds.left)*ratio, (symbolBounds.top - imageBounds.top)*ratio, symbolBounds.width() * ratio, symbolBounds.height() * ratio);
731                    imageBounds = RectUtilities.makeRectF(0f, 0f, w * ratio, h * ratio);
732
733                    //make sure border padding isn't excessive.
734                    w = imageBounds.width();
735                    h = imageBounds.height();
736
737                    /*if (h / (h + borderPadding) > 0.10) {
738                        borderPadding = (float) (h * 0.03);
739                    } else if (w / (w + borderPadding) > 0.10) {
740                        borderPadding = (float) (w * 0.03);
741                    }*/
742
743                }
744
745                Rect borderPaddingBounds = null;
746                int offset = 0;
747                if(msi.getSymbolSet()==SymbolID.SymbolSet_ControlMeasure && drawCustomOutline && borderPadding != 0)
748                {
749                    RectUtilities.grow(rect, (int)Math.ceil(borderPadding / ratio));
750                    offset = (int)Math.ceil(borderPadding);
751                }
752
753                imageBounds = RectUtilities.makeRectF(0, 0, (imageBounds.width() + 0.5f + Math.round(borderPadding)*2), (imageBounds.height() + 0.5f + Math.round(borderPadding)*2));
754                RectUtilities.shift(symbolBounds,offset,offset);
755
756                String strLineJoin = "";
757
758                if(SymbolUtilities.isActionPoint(symbolID))//smooth out action points
759                    strLineJoin = " stroke-linejoin=\"round\" ";
760
761                StringBuilder sbGroupUnit = new StringBuilder();
762                if(siIcon != null)
763                {
764                    sbGroupUnit.append("<g transform=\"translate(" + (rect.left * -ratio) + ',' + (rect.top * -ratio) + ") scale(" + ratio + "," + ratio + ")\"" + strLineJoin + ">");
765                    sbGroupUnit.append(strSVGIcon);//(siIcon.getSVG());
766                    sbGroupUnit.append("</g>");
767                }
768
769                //Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID, RectUtilities.makeRectangle2DFromRect(offset, offset, symbolBounds.getWidth()-offset, symbolBounds.getHeight()-offset));
770                PointF centerPoint = SymbolUtilities.getCMSymbolAnchorPointF(symbolID, symbolBounds);
771                centerPoint.set(Math.round(centerPoint.x),Math.round(centerPoint.y));
772                //Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,RectUtilities.makeRectFromRectF(symbolBounds));
773
774                //now that we're done building symbol and applying outlines if needed,
775                //imageBounds and symbolBounds can be considered to be the same
776                symbolBounds = new RectF(imageBounds);//circle.getSymbolBounds();
777
778                si = new SVGSymbolInfo(sbGroupUnit.toString(), centerPoint,symbolBounds,imageBounds);
779
780            }
781
782            //Process Modifiers
783            SVGSymbolInfo siNew = null;
784            if (drawAsIcon == false && (hasTextModifiers || hasDisplayModifiers)) {
785                SymbolDimensionInfo sdiTemp = null;
786                if (SymbolUtilities.isSPWithSpecialModifierLayout(symbolID))//(SymbolUtilitiesD.isTGSPWithSpecialModifierLayout(symbolID))
787                {
788                    sdiTemp = ModifierRenderer.ProcessTGSPWithSpecialModifierLayout(si, symbolID, modifiers, attributes, lineColor);
789                } else {
790                    sdiTemp = ModifierRenderer.ProcessTGSPModifiers(si, symbolID, modifiers, attributes, lineColor);
791                }
792                siNew = (sdiTemp instanceof SVGSymbolInfo ? (SVGSymbolInfo)sdiTemp : null);
793
794            }
795
796            if (siNew != null) {
797                si = siNew;
798            }
799
800            //add SVG tag with dimensions
801            //draw unit from SVG
802            String svgAlpha = "";
803            if(alpha >=0 && alpha <= 255)
804                svgAlpha = " opacity=\"" + alpha/255f + "\"";
805            svgStart = "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"" + (int)si.getImageBounds().width() + "\" height=\"" + (int)si.getImageBounds().height() +"\" viewBox=\"" + 0 + " " + 0 + " " + (int)si.getImageBounds().width() + " " + (int)si.getImageBounds().height() + "\"" + svgAlpha + ">\n";
806            String svgTranslateGroup = null;
807
808            float transX = si.getImageBoundsF().left * -1;
809            float transY = si.getImageBoundsF().top * -1;
810            PointF anchor = si.getCenterPointF();
811            imageBounds = si.getImageBoundsF();
812            if(transX > 0 || transY > 0)
813            {
814                //ShapeUtilities.offset(anchor,transX,transY);
815                anchor.offset(transX,transY);
816                //ShapeUtilities.offset(symbolBounds,transX,transY);
817                symbolBounds.offset(transX,transY);
818                //ShapeUtilities.offset(imageBounds,transX,transY);
819                imageBounds.offset(transX,transY);
820
821                svgTranslateGroup = "<g transform=\"translate(" + transX + "," + transY + ")" +"\">\n";
822            }
823            si = new SVGSymbolInfo(si.getSVG(),anchor,symbolBounds,imageBounds);
824            StringBuilder sbSVG = new StringBuilder();
825            sbSVG.append(svgStart);
826            sbSVG.append(makeDescTag(si));
827            sbSVG.append(makeMetadataTag(symbolID, si));
828            if(svgTranslateGroup != null)
829                sbSVG.append(svgTranslateGroup);
830            sbSVG.append(si.getSVG());
831            if(svgTranslateGroup != null)
832                sbSVG.append("\n</g>");
833            sbSVG.append("\n</svg>");
834            si =  new SVGSymbolInfo(sbSVG.toString(),anchor,symbolBounds,imageBounds);
835
836            //cleanup
837            //bmp.recycle();
838            symbolBounds = null;
839            mySVG = null;
840
841
842        } catch (Exception exc) {
843            ErrorLogger.LogException("SinglePointSVGRenderer", "RenderSP", exc);
844            return null;
845        }
846
847        return si;
848
849    }
850
851
852    /**
853     *
854     * @param symbolID
855     * @return
856     */
857    @SuppressWarnings("unused")
858    public ImageInfo RenderModifier(String symbolID, Map<String,String> attributes)
859    {
860        ImageInfo temp = null;
861        String basicSymbolID = null;
862
863        Color lineColor = null;
864        Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID);
865
866        int alpha = -1;
867
868
869        //SVG rendering variables
870        MSInfo msi = null;
871        String iconID = null;
872        SVGInfo siIcon = null;
873        int top = 0;
874        int left = 0;
875        int width = 0;
876        int height = 0;
877        String svgStart = null;
878        String strSVG = null;
879        SVG mySVG = null;
880
881        float ratio = 0;
882
883        Rect symbolBounds = null;
884        RectF fullBounds = null;
885        Bitmap fullBMP = null;
886
887        boolean drawAsIcon = false;
888        int pixelSize = -1;
889        boolean keepUnitRatio = true;
890        boolean hasDisplayModifiers = false;
891        boolean hasTextModifiers = false;
892        int symbolOutlineWidth = RendererSettings.getInstance().getSinglePointSymbolOutlineWidth();
893        boolean drawCustomOutline = false;
894
895        try
896        {
897
898            msi = MSLookup.getInstance().getMSLInfo(symbolID);
899            if (attributes != null)
900            {
901                if (attributes.containsKey(MilStdAttributes.KeepUnitRatio))
902                {
903                    keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
904                }
905
906                if (attributes.containsKey(MilStdAttributes.LineColor))
907                {
908                    lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor));
909                }
910
911                if (attributes.containsKey(MilStdAttributes.FillColor))
912                {
913                    fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor));
914                }
915
916                if (attributes.containsKey(MilStdAttributes.Alpha))
917                {
918                    alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
919                }
920
921                if (attributes.containsKey(MilStdAttributes.DrawAsIcon))
922                {
923                    drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
924                }
925
926                if (attributes.containsKey(MilStdAttributes.PixelSize))
927                {
928                    pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
929                    if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure)
930                    {
931                        if(SymbolID.getEntityCode(symbolID)==270701)//static depiction
932                            pixelSize = (int)(pixelSize * 0.9);//try to scale to be somewhat in line with units
933                    }
934                }
935
936                if(drawAsIcon==false)//don't outline icons because they're not going on the map
937                {
938                    if(attributes.containsKey(MilStdAttributes.OutlineSymbol))
939                        drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol));
940                    else
941                        drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures();
942                }
943
944                if(SymbolUtilities.isMultiPoint(symbolID))
945                    drawCustomOutline=false;//icon previews for multipoints do not need outlines since they shouldn't be on the map
946
947                /*if (attributes.containsKey(MilStdAttributes.OutlineWidth)>=0)
948                 symbolOutlineWidth = Integer.parseInt(attributes.get(MilStdAttributes.OutlineWidth));//*/
949            }
950
951            int outlineOffset = symbolOutlineWidth;
952            if (drawCustomOutline && outlineOffset > 2)
953            {
954                outlineOffset = (outlineOffset - 1) / 2;
955            }
956            else
957            {
958                outlineOffset = 0;
959            }
960
961        }
962        catch (Exception excModifiers)
963        {
964            ErrorLogger.LogException("SinglePointSVGRenderer", "RenderModifier", excModifiers);
965        }
966
967        try
968        {
969            ImageInfo ii = null;
970            int intFill = -1;
971            if (fillColor != null)
972            {
973                intFill = fillColor.toInt();
974            }
975
976
977            if(msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure)
978                lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined.
979
980
981            //if not, generate symbol
982            if (ii == null)//*/
983            {
984                int version = SymbolID.getVersion(symbolID);
985                //check symbol size////////////////////////////////////////////
986                Rect rect = null;
987
988                iconID = SVGLookup.getMod1ID(symbolID);
989                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
990                top = Math.round(siIcon.getBbox().top);
991                left = Math.round(siIcon.getBbox().left);
992                width = Math.round(siIcon.getBbox().width());
993                height = Math.round(siIcon.getBbox().height());
994                if(siIcon.getBbox().bottom > 400)
995                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
996                else
997                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
998
999                String strSVGIcon = null;
1000                String strSVGOutline = null;
1001
1002                //update line and fill color of frame SVG
1003                if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null))
1004                    strSVGIcon = RendererUtilities.setSVGFrameColors(symbolID,siIcon.getSVG(),lineColor,fillColor);
1005                else
1006                    strSVGIcon = siIcon.getSVG();
1007
1008                if (pixelSize > 0)
1009                {
1010                    symbolBounds = RectUtilities.makeRect(left,top,width,height);
1011                    rect = new Rect(symbolBounds);
1012
1013                    //adjust size
1014                    float p = pixelSize;
1015                    float h = rect.height();
1016                    float w = rect.width();
1017
1018                    ratio = Math.min((p / h), (p / w));
1019
1020                    symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio);
1021
1022                }
1023
1024
1025                //TODO: figure out how to draw an outline and adjust the symbol bounds accordingly
1026
1027                //Draw glyphs to bitmap
1028                Bitmap bmp = Bitmap.createBitmap((symbolBounds.width()), (symbolBounds.height()), Config.ARGB_8888);
1029                Canvas canvas = new Canvas(bmp);
1030
1031                symbolBounds = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
1032
1033                strSVG = svgStart + strSVGIcon + "</svg>";
1034                mySVG = SVG.getFromString(strSVG);
1035                mySVG.setDocumentViewBox(left,top,width,height);
1036                mySVG.renderToCanvas(canvas);
1037
1038                Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,new RectF(0, 0, symbolBounds.right, symbolBounds.bottom));
1039
1040                ii = new ImageInfo(bmp, centerPoint, symbolBounds);
1041
1042
1043                /*if (drawAsIcon == false && pixelSize <= 100)
1044                {
1045                    _tgCache.put(key, ii);
1046                }//*/
1047            }
1048
1049
1050            //cleanup
1051            //bmp.recycle();
1052            symbolBounds = null;
1053            fullBMP = null;
1054            fullBounds = null;
1055            mySVG = null;
1056
1057
1058            if (drawAsIcon)
1059            {
1060                return ii.getSquareImageInfo();
1061            }
1062            else
1063            {
1064                return ii;
1065            }
1066
1067        }
1068        catch (Exception exc)
1069        {
1070            ErrorLogger.LogException("SinglePointSVGRenderer", "RenderModifier", exc);
1071        }
1072        return null;
1073    }
1074
1075    private String makeDescTag(SVGSymbolInfo si)
1076    {
1077        StringBuilder sbDesc = new StringBuilder();
1078
1079        if(si != null)
1080        {
1081            Rect bounds = si.getSymbolBounds();
1082            Rect iBounds = si.getImageBounds();
1083            sbDesc.append("<desc>").append(si.getCenterX()).append(" ").append(si.getCenterY()).append(" ");
1084            sbDesc.append(bounds.left).append(" ").append(bounds.top).append(" ").append(bounds.width()).append(" ").append(bounds.height()).append(" ");
1085            sbDesc.append(iBounds.left).append(" ").append(iBounds.top).append(" ").append(iBounds.width()).append(" ").append(iBounds.height());
1086            sbDesc.append("</desc>\n");
1087        }
1088        return sbDesc.toString();
1089    }
1090
1091    private String makeMetadataTag(String symbolID, SVGSymbolInfo si)
1092    {
1093        StringBuilder sbDesc = new StringBuilder();
1094
1095        if(si != null)
1096        {
1097            Rect bounds = si.getSymbolBounds();
1098            Rect iBounds = si.getImageBounds();
1099            sbDesc.append("<metadata>\n");
1100            sbDesc.append("<symbolID>").append(symbolID).append("</symbolID>\n");
1101            sbDesc.append("<anchor>").append(si.getCenterX()).append(" ").append(si.getCenterY()).append("</anchor>\n");
1102            sbDesc.append("<symbolBounds>").append(bounds.left).append(" ").append(bounds.top).append(" ").append(bounds.width()).append(" ").append(bounds.height()).append("</symbolBounds>\n");
1103            sbDesc.append("<imageBounds>").append(iBounds.left).append(" ").append(iBounds.top).append(" ").append(iBounds.width()).append(" ").append(iBounds.height()).append("</imageBounds>\n");;
1104            sbDesc.append("</metadata>\n");
1105        }
1106        return sbDesc.toString();
1107    }
1108
1109    public void logError(String tag, Throwable thrown)
1110    {
1111        if (tag == null || tag.equals(""))
1112        {
1113            tag = "singlePointRenderer";
1114        }
1115
1116        String message = thrown.getMessage();
1117        String stack = getStackTrace(thrown);
1118        if (message != null)
1119        {
1120            Log.e(tag, message);
1121        }
1122        if (stack != null)
1123        {
1124            Log.e(tag, stack);
1125        }
1126    }
1127
1128    public String getStackTrace(Throwable thrown)
1129    {
1130        try
1131        {
1132            if (thrown != null)
1133            {
1134                if (thrown.getStackTrace() != null)
1135                {
1136                    String eol = System.getProperty("line.separator");
1137                    StringBuilder sb = new StringBuilder();
1138                    sb.append(thrown.toString());
1139                    sb.append(eol);
1140                    for (StackTraceElement element : thrown.getStackTrace())
1141                    {
1142                        sb.append("        at ");
1143                        sb.append(element);
1144                        sb.append(eol);
1145                    }
1146                    return sb.toString();
1147                }
1148                else
1149                {
1150                    return thrown.getMessage() + "- no stack trace";
1151                }
1152            }
1153            else
1154            {
1155                return "no stack trace";
1156            }
1157        }
1158        catch (Exception exc)
1159        {
1160            Log.e("getStackTrace", exc.getMessage());
1161        }
1162        return thrown.getMessage();
1163    }//
1164
1165    /*
1166     private static String PrintList(ArrayList list)
1167     {
1168     String message = "";
1169     for(Object item : list)
1170     {
1171
1172     message += item.toString() + "\n";
1173     }
1174     return message;
1175     }//*/
1176    /*
1177     private static String PrintObjectMap(Map<String, Object> map)
1178     {
1179     Iterator<Object> itr = map.values().iterator();
1180     String message = "";
1181     String temp = null;
1182     while(itr.hasNext())
1183     {
1184     temp = String.valueOf(itr.next());
1185     if(temp != null)
1186     message += temp + "\n";
1187     }
1188     //ErrorLogger.LogMessage(message);
1189     return message;
1190     }//*/
1191    @Override
1192    public void onSettingsChanged(SettingsChangedEvent sce)
1193    {
1194
1195        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_FontChanged))
1196        {
1197            synchronized (_modifierFont)
1198            {
1199                _modifierFont = RendererSettings.getInstance().getModiferFont();
1200                _modifierOutlineFont = RendererSettings.getInstance().getModiferFont();
1201                FontMetrics fm = new FontMetrics();
1202                fm = _modifierFont.getFontMetrics();
1203                _modifierDescent = fm.descent;
1204                //_modifierFontHeight = fm.top + fm.bottom;
1205                _modifierFontHeight = fm.bottom - fm.top;
1206
1207                _modifierFont.setStrokeWidth(RendererSettings.getInstance().getTextOutlineWidth());
1208                _modifierOutlineFont.setColor(Color.white.toInt());
1209                _deviceDPI = RendererSettings.getInstance().getDeviceDPI();
1210
1211                ModifierRenderer.setModifierFont(_modifierFont, _modifierFontHeight, _modifierDescent);
1212
1213            }
1214        }
1215    }
1216}