public WpfTextStringFormat(FlowDirection direction, TextTrimming trimming, WpfTextAnchor anchor) { this.Direction = direction; this.Trimming = trimming; this.Anchor = anchor; }
public override void Render(WpfDrawingRenderer renderer) { if (_drawGroup == null || _drawContext == null) { return; } Point ctp = new Point(0, 0); // current text position WpfTextPlacement placement = WpfTextRenderer.GetCurrentTextPosition(_textElement, ctp); ctp = placement.Location; double rotate = placement.Rotation; if (!placement.HasPositions) { placement = null; // render it useless } string sBaselineShift = _textElement.GetPropertyValue("baseline-shift").Trim(); double shiftBy = 0; if (sBaselineShift.Length > 0) { double textFontSize = WpfTextRenderer.GetComputedFontSize(_textElement); if (sBaselineShift.EndsWith("%", StringComparison.OrdinalIgnoreCase)) { shiftBy = SvgNumber.ParseNumber(sBaselineShift.Substring(0, sBaselineShift.Length - 1)) / 100 * textFontSize; } else if (sBaselineShift == "sub") { shiftBy = -0.6F * textFontSize; } else if (sBaselineShift == "super") { shiftBy = 0.6F * textFontSize; } else if (sBaselineShift == "baseline") { shiftBy = 0; } else { shiftBy = SvgNumber.ParseNumber(sBaselineShift); } } XmlNodeType nodeType = XmlNodeType.None; bool isVertical = false; string writingMode = _textElement.GetPropertyValue("writing-mode"); if (!string.IsNullOrWhiteSpace(writingMode) && string.Equals(writingMode, "tb", StringComparison.OrdinalIgnoreCase)) { isVertical = true; } if (_svgElement.ChildNodes.Count == 1) { XmlNode child = _svgElement.ChildNodes[0]; nodeType = child.NodeType; if (nodeType == XmlNodeType.Text || nodeType == XmlNodeType.CDATA) { if (isVertical) { ctp.X -= shiftBy; RenderSingleLineTextV(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderSingleLineTextH(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.Y += shiftBy; } } else if (nodeType == XmlNodeType.Element) { string nodeName = child.Name; if (string.Equals(nodeName, "tref")) { AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, true); } else if (string.Equals(nodeName, "tspan")) { AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, true); } else if (string.Equals(nodeName, "textPath")) { RenderTextPath((SvgTextPathElement)child, ref ctp, rotate, placement); } } else if (nodeType == XmlNodeType.Whitespace) { if (isVertical) { ctp.X -= shiftBy; RenderSingleLineTextV(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderSingleLineTextH(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.Y += shiftBy; } } } else { string textAnchor = _textElement.GetPropertyValue("text-anchor"); WpfTextAnchor anchor = WpfTextAnchor.None; if (textAnchor == "middle") { anchor = WpfTextAnchor.Middle; } else if (textAnchor == "end") { anchor = WpfTextAnchor.End; } XmlNodeList nodeList = _svgElement.ChildNodes; // This is a very simply hack to change centered text to left align, since for // text containing spans, different font weights may be applied to the spans... if (anchor == WpfTextAnchor.Middle) { // Suspend the rendering... _isMeasuring = true; foreach (XmlNode child in nodeList) { nodeType = child.NodeType; if (nodeType == XmlNodeType.Text) { if (isVertical) { ctp.X -= shiftBy; RenderTextRunV(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderTextRunH(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.Y += shiftBy; } } else if (nodeType == XmlNodeType.Element) { string nodeName = child.Name; if (string.Equals(nodeName, "tref")) { AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, false); } else if (string.Equals(nodeName, "tspan")) { AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false); } else if (string.Equals(nodeName, "textPath")) { RenderTextPath((SvgTextPathElement)child, ref ctp, rotate, placement); } } else if (nodeType == XmlNodeType.Whitespace) { if (isVertical) { ctp.X -= shiftBy; //RenderTextRunV(_textElement, ref ctp, GetText(_textElement, child)); RenderTextRunV(_textElement, ref ctp, Whitespace, rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; //RenderTextRunH(_textElement, ref ctp, GetText(_textElement, child)); RenderTextRunH(_textElement, ref ctp, Whitespace, rotate, placement); ctp.Y += shiftBy; } } } ctp.X -= (_textWidth / 2d); // Resume the rendering... _isMeasuring = false; } bool textRendered = false; for (int i = 0; i < nodeList.Count; i++) { XmlNode child = nodeList[i]; nodeType = child.NodeType; if (nodeType == XmlNodeType.Text) { if (isVertical) { ctp.X -= shiftBy; RenderTextRunV(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderTextRunH(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.Y += shiftBy; } textRendered = true; } else if (nodeType == XmlNodeType.Element) { string nodeName = child.Name; if (string.Equals(nodeName, "tref")) { AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, false); textRendered = true; } else if (string.Equals(nodeName, "tspan")) { AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false); textRendered = true; } else if (string.Equals(nodeName, "textPath")) { RenderTextPath((SvgTextPathElement)child, ref ctp, rotate, placement); textRendered = false; } } else if (nodeType == XmlNodeType.Whitespace) { if (textRendered) { if (isVertical) { ctp.X -= shiftBy; //RenderTextRunV(_textElement, ref ctp, GetText(_textElement, child)); RenderTextRunV(_textElement, ref ctp, Whitespace, rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; //RenderTextRunH(_textElement, ref ctp, GetText(_textElement, child)); RenderTextRunH(_textElement, ref ctp, Whitespace, rotate, placement); ctp.Y += shiftBy; } textRendered = false; } } } } }
public override void Render(WpfDrawingRenderer renderer) { if (_drawGroup == null || _drawContext == null) { return; } var comparer = StringComparison.OrdinalIgnoreCase; Point ctp = new Point(0, 0); // current text position WpfTextPlacement placement = WpfTextPlacement.Create(_textElement, ctp, _textContext.IsTextPath); ctp = placement.Location; double rotate = placement.Rotation; if (!placement.HasPositions) { placement = null; // render it useless } string sBaselineShift = _textElement.GetPropertyValue("baseline-shift").Trim(); double shiftBy = 0; if (sBaselineShift.Length > 0) { double textFontSize = WpfTextRenderer.GetComputedFontSize(_textElement); if (sBaselineShift.EndsWith("%", comparer)) { shiftBy = SvgNumber.ParseNumber(sBaselineShift.Substring(0, sBaselineShift.Length - 1)) / 100 * textFontSize; } else if (string.Equals(sBaselineShift, "sub", comparer)) { shiftBy = -0.6F * textFontSize; } else if (string.Equals(sBaselineShift, "super", comparer)) { shiftBy = 0.6F * textFontSize; } else if (string.Equals(sBaselineShift, "baseline", comparer)) { shiftBy = 0; } else { shiftBy = SvgNumber.ParseNumber(sBaselineShift); } } // For for fonts loading in the background... var svgDoc = _svgElement.OwnerDocument; if (svgDoc.IsFontsLoaded == false) { //TODO: Use of SpinUntil is known to CPU heavy, but will work for now... //SpinWait.SpinUntil(() => svgDoc.IsFontsLoaded == true); var svgWnd = svgDoc.Window as SvgWindow; if (svgWnd != null) { svgWnd.AwaitTasks("SvgDocument"); } } XmlNodeType nodeType = XmlNodeType.None; bool isVertical = false; string writingMode = _textElement.GetPropertyValue("writing-mode"); if (!string.IsNullOrWhiteSpace(writingMode) && string.Equals(writingMode, "tb", comparer)) { isVertical = true; } if (_svgElement.ChildNodes.Count == 1) { XmlNode child = _svgElement.ChildNodes[0]; nodeType = child.NodeType; if (nodeType == XmlNodeType.Text || nodeType == XmlNodeType.CDATA) { if (isVertical) { ctp.X -= shiftBy; RenderVertText(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderHorzText(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.Y += shiftBy; } } else if (nodeType == XmlNodeType.Element) { string nodeName = child.Name; if (string.Equals(nodeName, "tref", comparer)) { AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, true); } else if (string.Equals(nodeName, "tspan", comparer)) { AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, true); } else if (string.Equals(nodeName, "textPath", comparer)) { RenderTextPath((SvgTextPathElement)child, ref ctp, rotate, placement); } else if (string.Equals(nodeName, "altGlyph", comparer)) { AddAltGlyphElementRun((SvgAltGlyphElement)child, ref ctp, isVertical, true); } } else if (nodeType == XmlNodeType.Whitespace) { if (isVertical) { ctp.X -= shiftBy; RenderVertText(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderHorzText(_textElement, ref ctp, WpfTextRenderer.GetText(_textElement, child), rotate, placement); ctp.Y += shiftBy; } } } else { string textAnchor = _textElement.GetPropertyValue("text-anchor"); WpfTextAnchor anchor = WpfTextAnchor.None; if (string.Equals(textAnchor, "middle", comparer)) { anchor = WpfTextAnchor.Middle; } else if (string.Equals(textAnchor, "end", comparer)) { anchor = WpfTextAnchor.End; } XmlNodeList nodeList = _svgElement.ChildNodes; int nodeCount = nodeList.Count; // This is a very simply hack to change centered text to left align, since for // text containing spans, different font weights may be applied to the spans... if (anchor == WpfTextAnchor.Middle) { // Suspend the rendering... _isMeasuring = true; Point savedPt = new Point(ctp.X, ctp.Y); _textContext.BeginMeasure(nodeCount); for (int i = 0; i < nodeCount; i++) { XmlNode child = nodeList[i]; nodeType = child.NodeType; //if (i == 0 && nodeType == XmlNodeType.Whitespace) //{ // continue; //} if (nodeType == XmlNodeType.Text) { var nodeText = WpfTextRenderer.GetText(_textElement, child); if (i == (nodeCount - 1)) { // No need to render the last white space... nodeText = nodeText.TrimEnd(); } else if (i == 0) { nodeText = nodeText.Trim(); } if (isVertical) { ctp.X -= shiftBy; RenderVertTextRun(_textElement, ref ctp, nodeText, rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderHorzTextRun(_textElement, ref ctp, nodeText, rotate, placement); ctp.Y += shiftBy; } } else if (nodeType == XmlNodeType.Element) { string nodeName = child.Name; if (string.Equals(nodeName, "tref", comparer)) { AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, false); } else if (string.Equals(nodeName, "tspan", comparer)) { bool isAdded = false; if ((i + 1) < nodeCount - 1) { XmlNode nextChild = nodeList[i + 1]; if (nextChild.NodeType == XmlNodeType.Whitespace) { isAdded = true; AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false, nextChild); i++; } } if (!isAdded) { AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false); } } else if (string.Equals(nodeName, "textPath", comparer)) { RenderTextPath((SvgTextPathElement)child, ref ctp, rotate, placement); } else if (string.Equals(nodeName, "altGlyph", comparer)) { AddAltGlyphElementRun((SvgAltGlyphElement)child, ref ctp, isVertical, false); } } else if (nodeType == XmlNodeType.Whitespace) { if (isVertical) { ctp.X -= shiftBy; RenderVertTextRun(_textElement, ref ctp, Whitespace, rotate, placement, true); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderHorzTextRun(_textElement, ref ctp, Whitespace, rotate, placement, true); ctp.Y += shiftBy; } } } _textContext.EndMeasure(); ctp = savedPt; ctp.X -= (_textWidth / 2d); // Resume the rendering... _isMeasuring = false; } bool textRendered = false; for (int i = 0; i < nodeCount; i++) { XmlNode child = nodeList[i]; nodeType = child.NodeType; //if (i == 0 && nodeType == XmlNodeType.Whitespace) //{ // continue; //} if (nodeType == XmlNodeType.Text) { var nodeText = WpfTextRenderer.GetText(_textElement, child); if (i == (nodeCount - 1)) { // No need to render the last white space... nodeText = nodeText.TrimEnd(); } else if (i == 0) { nodeText = nodeText.Trim(); } if (isVertical) { ctp.X -= shiftBy; RenderVertTextRun(_textElement, ref ctp, nodeText, rotate, placement); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderHorzTextRun(_textElement, ref ctp, nodeText, rotate, placement); ctp.Y += shiftBy; } textRendered = true; } else if (nodeType == XmlNodeType.Element) { string nodeName = child.Name; if (string.Equals(nodeName, "tref", comparer)) { AddTRefElementRun((SvgTRefElement)child, ref ctp, isVertical, false); textRendered = true; } else if (string.Equals(nodeName, "tspan", comparer)) { bool isAdded = false; if ((i + 1) < nodeCount - 1) { XmlNode nextChild = nodeList[i + 1]; if (nextChild.NodeType == XmlNodeType.Whitespace) { isAdded = true; AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false, nextChild); i++; } } if (!isAdded) { AddTSpanElementRun((SvgTSpanElement)child, ref ctp, isVertical, false); } textRendered = true; } else if (string.Equals(nodeName, "textPath", comparer)) { RenderTextPath((SvgTextPathElement)child, ref ctp, rotate, placement); textRendered = false; } else if (string.Equals(nodeName, "altGlyph", comparer)) { AddAltGlyphElementRun((SvgAltGlyphElement)child, ref ctp, isVertical, false); } } else if (nodeType == XmlNodeType.Whitespace) { if (textRendered) { if (isVertical) { ctp.X -= shiftBy; RenderVertTextRun(_textElement, ref ctp, Whitespace, rotate, placement, true); ctp.X += shiftBy; } else { ctp.Y -= shiftBy; RenderHorzTextRun(_textElement, ref ctp, Whitespace, rotate, placement, true); ctp.Y += shiftBy; } textRendered = false; } } } } // Register this drawing with the Drawing-Document... if (_drawGroup != null) { this.Rendered(_drawGroup); } }