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