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