Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.GrammaticalOptimization/SharpVectorRenderingWpf/Wpf/WpfTextRendering.cs @ 13777

Last change on this file since 13777 was 12762, checked in by aballeit, 9 years ago

#2283 GUI updates, Tree-chart, MCTS Version 2 (prune leaves)

File size: 28.8 KB
Line 
1using System;
2using System.Xml;
3using System.Linq;
4using System.Text;
5using System.Text.RegularExpressions;
6using System.Diagnostics;
7using System.Collections.Generic;
8
9using System.Windows;
10using System.Windows.Media;
11
12using SharpVectors.Dom.Css;
13using SharpVectors.Dom.Svg;
14
15using SharpVectors.Renderers.Texts;
16
17using SharpVectors.Runtime;
18
19namespace SharpVectors.Renderers.Wpf
20{
21    public sealed class WpfTextRendering : WpfRendering
22    {
23        #region Private Fields
24
25        private const string Whitespace = " ";
26
27        private readonly static Regex _tabNewline    = new Regex(@"[\n\f\t]");
28        private readonly static Regex _decimalNumber = new Regex(@"^\d");
29
30        private bool _isMeasuring;
31
32        private bool _isGroupAdded;
33
34        private bool _isTextPath;
35
36        private double _textWidth;
37
38        private SvgTextElement _textElement;
39
40        private DrawingGroup      _drawGroup;
41        private DrawingContext    _drawContext;
42        private WpfDrawingContext _context;
43
44        private WpfHorzTextRenderer _horzRenderer;
45        private WpfVertTextRenderer _vertRenderer;
46        private WpfPathTextRenderer _pathRenderer;
47
48        #endregion
49
50        #region Constructors and Destructor
51
52        public WpfTextRendering(SvgElement element)
53            : base(element)
54    {
55            _textElement = element as SvgTextElement;
56            if (_textElement == null)
57            {
58                throw new InvalidOperationException();
59            }
60
61            _horzRenderer = new WpfHorzTextRenderer(_textElement, this);
62            _vertRenderer = new WpfVertTextRenderer(_textElement, this);
63            _pathRenderer = new WpfPathTextRenderer(_textElement, this);
64        }
65
66        #endregion
67
68        #region Public Properties
69
70        public override bool IsRecursive
71        {
72            get
73            {
74                return true;
75            }
76        }
77
78        public bool IsMeasuring
79        {
80            get
81            {
82                return _isMeasuring;
83            }
84        }
85
86        public bool IsTextPath
87        {
88            get
89            {
90                return _isTextPath;
91            }
92            set
93            {
94                _isTextPath = value;
95            }
96        }
97
98        public double TextWidth
99        {
100            get
101            {
102                return _textWidth;
103            }
104        }
105
106        #endregion
107
108        #region Public Methods
109
110        #region BeforeRender Method
111
112        public override void BeforeRender(WpfDrawingRenderer renderer)
113        {
114            _isTextPath   = false;
115            _isGroupAdded = false;
116            _textWidth    = 0;
117            _isMeasuring  = false;
118
119            WpfDrawingContext context = renderer.Context;
120
121            SvgRenderingHint hint = _svgElement.RenderingHint;
122            if (hint == SvgRenderingHint.Clipping)
123            {
124                return;
125            }
126            // We do not directly render the contents of the clip-path, unless specifically requested...
127            if (String.Equals(_svgElement.ParentNode.LocalName, "clipPath") &&
128                !context.RenderingClipRegion)
129            {
130                return;
131            }
132
133            string sVisibility = _textElement.GetPropertyValue("visibility");
134            string sDisplay    = _textElement.GetPropertyValue("display");
135            if (String.Equals(sVisibility, "hidden") || String.Equals(sDisplay, "none"))
136            {
137                return;
138            }
139
140            _context = renderer.Context;
141
142            SetQuality(context);
143            SetTransform(context);
144
145            SetClip(_context);
146
147            SetMask(_context);
148
149            _drawGroup = new DrawingGroup();
150
151            string elementId = this.GetElementName();
152            if (!String.IsNullOrEmpty(elementId) && !context.IsRegisteredId(elementId))
153            {
154                _drawGroup.SetValue(FrameworkElement.NameProperty, elementId);
155
156                context.RegisterId(elementId);
157
158                if (context.IncludeRuntime)
159                {
160                    SvgObject.SetId(_drawGroup, elementId);
161                }
162            }
163
164            Transform textTransform = this.Transform;
165            if (textTransform != null && !textTransform.Value.IsIdentity)
166            {
167                _drawGroup.Transform = textTransform;
168            }
169            else
170            {
171                textTransform = null; // render any identity transform useless...
172            }
173            Geometry textClip = this.ClipGeometry;
174            if (textClip != null && !textClip.IsEmpty())
175            {
176                _drawGroup.ClipGeometry = textClip;
177            }
178            else
179            {
180                textClip = null; // render any empty geometry useless...
181            }
182            Brush textMask = this.Masking;
183            if (textMask != null)
184            {
185                _drawGroup.OpacityMask = textMask;
186            }
187
188            if (textTransform != null || textClip != null || textMask != null)
189            {
190                DrawingGroup curGroup = _context.Peek();
191                Debug.Assert(curGroup != null);
192                if (curGroup != null)
193                {
194                    curGroup.Children.Add(_drawGroup);
195                    context.Push(_drawGroup);
196
197                    _isGroupAdded = true;
198                }
199            }
200
201            _drawContext = _drawGroup.Open();
202
203            _horzRenderer.Initialize(_drawContext, _context);
204            _vertRenderer.Initialize(_drawContext, _context);
205            _pathRenderer.Initialize(_drawContext, _context);
206        }
207
208        #endregion
209
210        #region Render Method
211
212        public override void Render(WpfDrawingRenderer renderer)
213        {
214            if (_drawGroup == null || _drawContext == null)
215            {
216                return;
217            }
218
219            Point ctp = new Point(0, 0); // current text position
220
221            WpfTextPlacement placement = WpfTextRenderer.GetCurrentTextPosition(_textElement, ctp);
222            ctp           = placement.Location;
223            double rotate = placement.Rotation;
224            if (!placement.HasPositions)
225            {
226                placement = null; // render it useless
227            }
228            string sBaselineShift = _textElement.GetPropertyValue("baseline-shift").Trim();
229            double shiftBy = 0;
230
231            if (sBaselineShift.Length > 0)
232            {
233                double textFontSize = WpfTextRenderer.GetComputedFontSize(_textElement);
234                if (sBaselineShift.EndsWith("%"))
235                {
236                    shiftBy = SvgNumber.ParseNumber(sBaselineShift.Substring(0,
237                        sBaselineShift.Length - 1)) / 100 * textFontSize;
238                }
239                else if (sBaselineShift == "sub")
240                {
241                    shiftBy = -0.6F * textFontSize;
242                }
243                else if (sBaselineShift == "super")
244                {
245                    shiftBy = 0.6F * textFontSize;
246                }
247                else if (sBaselineShift == "baseline")
248                {
249                    shiftBy = 0;
250                }
251                else
252                {
253                    shiftBy = SvgNumber.ParseNumber(sBaselineShift);
254                }
255            }
256
257            XmlNodeType nodeType = XmlNodeType.None;
258
259            bool isVertical    = false;
260            string writingMode = _textElement.GetPropertyValue("writing-mode");
261            if (!String.IsNullOrEmpty(writingMode) &&
262                String.Equals(writingMode, "tb", StringComparison.OrdinalIgnoreCase))
263            {
264                isVertical = true;
265            }
266
267            if (_svgElement.ChildNodes.Count == 1)
268            {   
269                XmlNode child = _svgElement.ChildNodes[0];
270                nodeType = child.NodeType;
271                if (nodeType == XmlNodeType.Text)
272                {
273                    if (isVertical)
274                    {
275                        ctp.X -= shiftBy;
276                        RenderSingleLineTextV(_textElement, ref ctp,
277                            WpfTextRenderer.GetText(_textElement, child), rotate, placement);
278                        ctp.X += shiftBy;
279                    }
280                    else
281                    {
282                        ctp.Y -= shiftBy;
283                        RenderSingleLineTextH(_textElement, ref ctp,
284                            WpfTextRenderer.GetText(_textElement, child), rotate, placement);
285                        ctp.Y += shiftBy;
286                    }
287                }
288                else if (nodeType == XmlNodeType.Element)
289                {
290                    string nodeName = child.Name;
291                    if (String.Equals(nodeName, "tref"))
292                    {
293                        AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, true);
294                    }
295                    else if (String.Equals(nodeName, "tspan"))
296                    {
297                        AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, true);
298                    }
299                    else if (String.Equals(nodeName, "textPath"))
300                    {
301                        RenderTextPath(_textElement, (SvgTextPathElement)child, ref ctp,
302                            rotate, placement);
303                    }
304                }
305                else if (nodeType == XmlNodeType.Whitespace)
306                {   
307                    if (isVertical)
308                    {
309                        ctp.X -= shiftBy;
310                        RenderSingleLineTextV(_textElement, ref ctp,
311                            WpfTextRenderer.GetText(_textElement, child), rotate, placement);
312                        ctp.X += shiftBy;
313                    }
314                    else
315                    {
316                        ctp.Y -= shiftBy;
317                        RenderSingleLineTextH(_textElement, ref ctp,
318                            WpfTextRenderer.GetText(_textElement, child), rotate, placement);
319                        ctp.Y += shiftBy;
320                    }
321                }
322            }
323            else
324            {
325                string textAnchor = _textElement.GetPropertyValue("text-anchor");
326
327                WpfTextAnchor anchor = WpfTextAnchor.None;
328
329                if (textAnchor == "middle")
330                    anchor = WpfTextAnchor.Middle;
331                else if (textAnchor == "end")
332                    anchor = WpfTextAnchor.End;
333
334                XmlNodeList nodeList = _svgElement.ChildNodes;
335                // This is a very simply hack to change centered text to left align, since for
336                // text containing spans, different font weights may be applied to the spans...
337                if (anchor == WpfTextAnchor.Middle)
338                {
339                    // Suspend the rendering...
340                    _isMeasuring = true;
341
342                    foreach (XmlNode child in nodeList)
343                    {
344                        nodeType = child.NodeType;
345                        if (nodeType == XmlNodeType.Text)
346                        {
347                            if (isVertical)
348                            {
349                                ctp.X -= shiftBy;
350                                RenderTextRunV(_textElement, ref ctp,
351                                    WpfTextRenderer.GetText(_textElement, child), rotate, placement);
352                                ctp.X += shiftBy;
353                            }
354                            else
355                            {
356                                ctp.Y -= shiftBy;
357                                RenderTextRunH(_textElement, ref ctp,
358                                    WpfTextRenderer.GetText(_textElement, child), rotate, placement);
359                                ctp.Y += shiftBy;
360                            }
361                        }
362                        else if (nodeType == XmlNodeType.Element)
363                        {
364                            string nodeName = child.Name;
365                            if (String.Equals(nodeName, "tref"))
366                            {
367                                AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, false);
368                            }
369                            else if (String.Equals(nodeName, "tspan"))
370                            {
371                                AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false);
372                            }
373                            else if (String.Equals(nodeName, "textPath"))
374                            {
375                                RenderTextPath(_textElement, (SvgTextPathElement)child, ref ctp,
376                                    rotate, placement);
377                            }
378                        }
379                        else if (nodeType == XmlNodeType.Whitespace)
380                        {
381                            if (isVertical)
382                            {
383                                ctp.X -= shiftBy;
384                                //RenderTextRunV(_textElement, ref ctp, GetText(_textElement, child));
385                                RenderTextRunV(_textElement, ref ctp, Whitespace, rotate, placement);
386                                ctp.X += shiftBy;
387                            }
388                            else
389                            {
390                                ctp.Y -= shiftBy;
391                                //RenderTextRunH(_textElement, ref ctp, GetText(_textElement, child));
392                                RenderTextRunH(_textElement, ref ctp, Whitespace, rotate, placement);
393                                ctp.Y += shiftBy;
394                            }
395                        }
396                    }
397
398                    ctp.X -= (_textWidth / 2d);
399
400                    // Resume the rendering...
401                    _isMeasuring = false;
402                }
403
404                bool textRendered = false;
405
406                for (int i = 0; i < nodeList.Count; i++)
407                {
408                    XmlNode child = nodeList[i];
409                    nodeType = child.NodeType;
410                    if (nodeType == XmlNodeType.Text)
411                    {
412                        if (isVertical)
413                        {
414                            ctp.X -= shiftBy;
415                            RenderTextRunV(_textElement, ref ctp,
416                                WpfTextRenderer.GetText(_textElement, child), rotate, placement);
417                            ctp.X += shiftBy;
418                        }
419                        else
420                        {
421                            ctp.Y -= shiftBy;
422                            RenderTextRunH(_textElement, ref ctp,
423                                WpfTextRenderer.GetText(_textElement, child), rotate, placement);
424                            ctp.Y += shiftBy;
425                        }
426
427                        textRendered = true;
428                    }
429                    else if (nodeType == XmlNodeType.Element)
430                    {
431                        string nodeName = child.Name;
432                        if (String.Equals(nodeName, "tref"))
433                        {
434                            AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, false);
435
436                            textRendered = true;
437                        }
438                        else if (String.Equals(nodeName, "tspan"))
439                        {
440                            AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false);
441                           
442                            textRendered = true;
443                        }
444                        else if (String.Equals(nodeName, "textPath"))
445                        {
446                            RenderTextPath(_textElement, (SvgTextPathElement)child, ref ctp,
447                                rotate, placement);
448                           
449                            textRendered = false;
450                        }
451                    }
452                    else if (nodeType == XmlNodeType.Whitespace)
453                    {
454                        if (textRendered)
455                        {
456                            if (isVertical)
457                            {
458                                ctp.X -= shiftBy;
459                                //RenderTextRunV(_textElement, ref ctp, GetText(_textElement, child));
460                                RenderTextRunV(_textElement, ref ctp, Whitespace, rotate, placement);
461                                ctp.X += shiftBy;
462                            }
463                            else
464                            {
465                                ctp.Y -= shiftBy;
466                                //RenderTextRunH(_textElement, ref ctp, GetText(_textElement, child));
467                                RenderTextRunH(_textElement, ref ctp, Whitespace, rotate, placement);
468                                ctp.Y += shiftBy;
469                            }
470
471                            textRendered = false;
472                        }
473                    }
474                }
475            }
476        }
477
478        #endregion
479
480        #region AfterRender Method
481
482        private static void ResetGuidelineSet(DrawingGroup group)
483        {
484            DrawingCollection drawings = group.Children;
485            int itemCount = drawings.Count;
486            for (int i = 0; i < itemCount; i++)
487            {
488                DrawingGroup childGroup = drawings[i] as DrawingGroup;
489                if (childGroup != null)
490                {
491                    childGroup.GuidelineSet = null;
492
493                    ResetGuidelineSet(childGroup);
494                }
495            }
496        }
497
498        public override void AfterRender(WpfDrawingRenderer renderer)
499        {
500            if (_horzRenderer != null)
501            {
502                _horzRenderer.Uninitialize();
503                _horzRenderer = null;
504            }
505            if (_vertRenderer != null)
506            {
507                _vertRenderer.Uninitialize();
508                _vertRenderer = null;
509            }
510            if (_pathRenderer != null)
511            {
512                _pathRenderer.Uninitialize();
513                _pathRenderer = null;
514            }
515
516            if (_drawContext != null)
517            {
518                _drawContext.Close();
519                _drawContext = null;
520            }
521
522            WpfDrawingContext context = renderer.Context;
523
524            // TODO-PAUL: Testing this for validity...
525            // Remove the GuidelineSet from the groups added by the FormattedText to reduced the
526            // size of output XAML...
527            if (_drawGroup != null)
528            {
529                ResetGuidelineSet(_drawGroup);
530            }
531
532            if (context.IncludeRuntime)
533            {   
534                if (_drawGroup != null)
535                {
536                    // Add the element/object type...
537                    SvgObject.SetType(_drawGroup, SvgObjectType.Text);
538
539                    // Add title for tooltips, if any...
540                    SvgTitleElement titleElement = _svgElement.SelectSingleNode("title") as SvgTitleElement;
541                    if (titleElement != null)
542                    {
543                        string titleValue = titleElement.InnerText;
544                        if (!String.IsNullOrEmpty(titleValue))
545                        {
546                            SvgObject.SetTitle(_drawGroup, titleValue);
547                        }
548                    }
549                }
550            }
551
552            if (!_isGroupAdded)
553            {   
554                if (_drawGroup != null)
555                {
556                    if (_isTextPath || _drawGroup.Transform != null || _drawGroup.ClipGeometry != null)
557                    {
558                        DrawingGroup curGroup = _context.Peek();
559                        Debug.Assert(curGroup != null);
560                        if (curGroup != null)
561                        {
562                            curGroup.Children.Add(_drawGroup);
563                        }
564                    }
565                    else if (_drawGroup.Children.Count != 0)
566                    {
567                        DrawingGroup firstGroup = _drawGroup.Children[0] as DrawingGroup;
568                        if (firstGroup != null && firstGroup.Children.Count != 0)
569                        {
570                            //Drawing firstDrawing = firstGroup.Children[0];
571
572                            DrawingGroup curGroup = _context.Peek();
573                            Debug.Assert(curGroup != null);
574                            if (curGroup != null)
575                            {
576                                curGroup.Children.Add(_drawGroup);
577                            }
578                        }
579                    }
580                }
581            }
582            else
583            {
584                if (_drawGroup != null)
585                {
586                    DrawingGroup currentGroup = context.Peek();
587
588                    if (currentGroup == null || currentGroup != _drawGroup)
589                    {
590                        throw new InvalidOperationException("An existing group is expected.");
591                    }
592
593                    context.Pop();
594                }
595            }
596
597            _context   = null;
598            _drawGroup = null;
599
600            base.AfterRender(renderer);
601        }
602
603        #endregion
604
605        public void SetTextWidth(double textWidth)
606        {
607            _textWidth = textWidth;
608        }
609
610        public void AddTextWidth(double textWidth)
611        {
612            _textWidth += textWidth;
613        }
614
615        #endregion
616
617        #region Private Methods
618
619        #region Horizontal Render Methods
620
621        private void RenderSingleLineTextH(SvgTextContentElement element, ref Point ctp,
622            string text, double rotate, WpfTextPlacement placement)
623        {
624            if (String.IsNullOrEmpty(text) || _horzRenderer == null)
625                return;
626
627            string targetText = text.Trim();
628            if (placement != null)
629            {
630                placement.UpdatePositions(targetText);
631            }
632            _horzRenderer.RenderSingleLineText(element, ref ctp, targetText, rotate, placement);
633        }
634
635        private void RenderTextRunH(SvgTextContentElement element, ref Point ctp,
636            string text, double rotate, WpfTextPlacement placement)
637        {
638            if (String.IsNullOrEmpty(text) || _horzRenderer == null)
639                return;
640
641            if (placement != null)
642            {
643                placement.UpdatePositions(text);
644            }
645            _horzRenderer.RenderTextRun(element, ref ctp, text, rotate, placement);
646        }
647
648        #endregion
649
650        #region Vertical Render Methods
651
652        private void RenderSingleLineTextV(SvgTextContentElement element, ref Point ctp,
653            string text, double rotate, WpfTextPlacement placement)
654        {
655            if (String.IsNullOrEmpty(text) || _vertRenderer == null)
656                return;
657
658            string targetText = text.Trim();
659            if (placement != null)
660            {
661                placement.UpdatePositions(targetText);
662            }
663            _vertRenderer.RenderSingleLineText(element, ref ctp, targetText, rotate, placement);
664        }
665
666        private void RenderTextRunV(SvgTextContentElement element, ref Point ctp,
667            string text, double rotate, WpfTextPlacement placement)
668        {
669            if (String.IsNullOrEmpty(text) || _vertRenderer == null)
670                return;
671
672            if (placement != null)
673            {
674                placement.UpdatePositions(text);
675            }
676            _vertRenderer.RenderTextRun(element, ref ctp, text, rotate, placement);
677        }
678
679        #endregion
680
681        #region Text Path Methods
682
683        private void RenderTextPath(SvgTextElement element, SvgTextPathElement textPath,
684            ref Point ctp, double rotate, WpfTextPlacement placement)
685        {
686            if (_pathRenderer == null)
687            {
688                return;
689            }
690
691            _pathRenderer.RenderSingleLineText(textPath, ref ctp, String.Empty, rotate, placement);
692        }
693
694        #endregion
695
696        #region TRef/TSpan Methods
697
698        private void AddTRefElementRun(SvgTRefElement element, ref Point ctp,
699            bool isVertical, bool isSingleLine)
700        {
701            WpfTextPlacement placement = WpfTextRenderer.GetCurrentTextPosition(element, ctp);
702            ctp           = placement.Location;
703            double rotate = placement.Rotation;
704            if (!placement.HasPositions)
705            {
706                placement = null; // render it useless
707            }
708
709            if (isVertical)
710            {
711                if (isSingleLine)
712                {
713                    this.RenderSingleLineTextV(element, ref ctp,
714                        WpfTextRenderer.GetTRefText(element), rotate, placement);
715                }
716                else
717                {
718                    this.RenderTextRunV(element, ref ctp,
719                        WpfTextRenderer.GetTRefText(element), rotate, placement);
720                }
721            }
722            else
723            {
724                if (isSingleLine)
725                {
726                    this.RenderSingleLineTextH(element, ref ctp,
727                        WpfTextRenderer.GetTRefText(element), rotate, placement);
728                }
729                else
730                {
731                    this.RenderTextRunH(element, ref ctp,
732                        WpfTextRenderer.GetTRefText(element), rotate, placement);
733                }
734            }
735        }
736
737        private void AddTSpanElementRun(SvgTSpanElement element, ref Point ctp,
738            bool isVertical, bool isSingleLine)
739        {
740            WpfTextPlacement placement = WpfTextRenderer.GetCurrentTextPosition(element, ctp);
741            ctp           = placement.Location;
742            double rotate = placement.Rotation;
743            if (!placement.HasPositions)
744            {
745                placement = null; // render it useless
746            }
747
748            string sBaselineShift = element.GetPropertyValue("baseline-shift").Trim();
749            double shiftBy = 0;
750
751            if (sBaselineShift.Length > 0)
752            {
753                double textFontSize = WpfTextRenderer.GetComputedFontSize(_textElement);
754                if (sBaselineShift.EndsWith("%"))
755                {
756                    shiftBy = SvgNumber.ParseNumber(sBaselineShift.Substring(0,
757                        sBaselineShift.Length - 1)) / 100f * textFontSize;
758                }
759                else if (sBaselineShift == "sub")
760                {
761                    shiftBy = -0.6F * textFontSize;
762                }
763                else if (sBaselineShift == "super")
764                {
765                    shiftBy = 0.6F * textFontSize;
766                }
767                else if (sBaselineShift == "baseline")
768                {
769                    shiftBy = 0;
770                }
771                else
772                {
773                    shiftBy = SvgNumber.ParseNumber(sBaselineShift);
774                }
775            }
776
777            foreach (XmlNode child in element.ChildNodes)
778            {
779                if (child.NodeType == XmlNodeType.Text)
780                {
781                    if (isVertical)
782                    {
783                        ctp.X += shiftBy;
784                        if (isSingleLine)
785                        {
786                            RenderSingleLineTextV(element, ref ctp,
787                                WpfTextRenderer.GetText(element, child), rotate, placement);
788                        }
789                        else
790                        {
791                            RenderTextRunV(element, ref ctp,
792                                WpfTextRenderer.GetText(element, child), rotate, placement);
793                        }
794                        ctp.X -= shiftBy;
795                    }
796                    else
797                    {
798                        ctp.Y -= shiftBy;
799                        if (isSingleLine)
800                        {
801                            RenderSingleLineTextH(element, ref ctp,
802                                WpfTextRenderer.GetText(element, child), rotate, placement);
803                        }
804                        else
805                        {
806                            RenderTextRunH(element, ref ctp,
807                                WpfTextRenderer.GetText(element, child), rotate, placement);
808                        }
809                        ctp.Y += shiftBy;
810                    }
811                }
812            }
813        }
814
815        #endregion
816
817        #endregion
818    }
819}
Note: See TracBrowser for help on using the repository browser.