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