コード例 #1
0
ファイル: SymDef.cs プロジェクト: jonc/carto
        // Determine where each grapheme (essentially character) will go when drawing text along a path.
        // The algorithm for how text is laid out along a path makes sense, with one MAJOR weirdness.
        // The basic algorithm is that the width of each character to draw is determined. Starting where the last
        // character ends, the path is followed for that character width (around corners if needed), and the point along
        // the path at that point is connected to the start point of the character with a line. The character is drawn along that
        // baseline, starting at the start point. The end point is used as the start of the next character. If there are bends,
        // of course, the character won't end right at that end point, but we ignore that.
        //
        // THe weirdness is this: instead of measuring distance along the path correctly with the Pythagorian formula, a
        // strange alternate metric of dx + 1/2 dy is used, where dx is the larger ordinate delta and dy is the smaller ordinate
        // delta. Thus, text along diagonals is squished together more than it should be. I have no explanation as to why
        // this might work but it reproduces what OCAD does. See the function "BizzarroDistance" in SymPath.
        private List<GraphemePlacement> GetLineTextGraphemePlacement(SymPath path, string text)
        {
            float totalWidth = 0;
            List<GraphemePlacement> graphemeList = new List<GraphemePlacement>();
            float pathLength = path.BizzarroLength;
            if (pathLength == 0)
                return graphemeList;            // nothing to draw.

            // First, determine all the graphemes and their width
            TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text);
            while (enumerator.MoveNext()) {
                string grapheme = enumerator.GetTextElement();
                float graphemeWidth;

                if (grapheme == " ")
                    graphemeWidth = wordSpacing * spaceWidth;
                else {
                    float width = MeasureStringWidth(grapheme);
                    graphemeWidth = width + charSpacing * spaceWidth;
                }

                graphemeList.Add(new GraphemePlacement(grapheme, graphemeWidth, new PointF(), 0));
                totalWidth += graphemeWidth;
                if (totalWidth + 0.01F >= pathLength && fontAlign != TextSymDefAlignment.Justified)
                    break;          // We don't have any room for more characters. (0.01 prevents a very small tail at the end.)
            }

            // For OCAD compatibility, truncate right aligned text if too big to fit so the whole
            // string fits. (Note that left-aligned text will typically show one more character than this.)
            if (pathLength < totalWidth && fontAlign != TextSymDefAlignment.Left && fontAlign != TextSymDefAlignment.Justified) {
                totalWidth -= graphemeList[graphemeList.Count - 1].width;
                if (fontAlign == TextSymDefAlignment.Right)
                    graphemeList.RemoveAt(graphemeList.Count - 1);
            }

            // Where does the text begin?
            float startingDistance = 0;
            if (fontAlign == TextSymDefAlignment.Left || fontAlign == TextSymDefAlignment.Justified)
                startingDistance = 0;
            else if (fontAlign == TextSymDefAlignment.Right)
                startingDistance = pathLength - totalWidth;
            else if (fontAlign == TextSymDefAlignment.Center)
                startingDistance = (pathLength - totalWidth) / 2;

            // For justified (all-line) text, adjust the widths of each character so they all fit.
            if (fontAlign == TextSymDefAlignment.Justified && graphemeList.Count > 1) {
                if (charSpacing > 0) {
                    // last character doesn't have space added.
                    GraphemePlacement graphemePlacement = graphemeList[graphemeList.Count - 1];
                    graphemePlacement.width -= charSpacing * spaceWidth;
                    totalWidth -= charSpacing * spaceWidth;
                    graphemeList[graphemeList.Count - 1] = graphemePlacement;
                }

                float adjustment = (pathLength - totalWidth) / (graphemeList.Count - 1);
                for (int i = 0; i < graphemeList.Count - 1; ++i) {
                    GraphemePlacement graphemePlacement = graphemeList[i];
                    graphemePlacement.width += adjustment;
                    graphemeList[i] = graphemePlacement;
                }
            }

            // Find points along the path that are the start/end of each grapheme.
            PointF[] points = new PointF[graphemeList.Count + 1];
            float curDistance = startingDistance;
            for (int i = 0; i < graphemeList.Count; ++i) {
                points[i] = path.PointAtLengthBizzarro(curDistance);
                curDistance += graphemeList[i].width;
            }
            points[graphemeList.Count] = path.PointAtLengthBizzarro(Math.Min(curDistance, pathLength));

            // Fill in graphemeList with start points and angles
            for (int i = 0; i < graphemeList.Count; ++i) {
                GraphemePlacement graphemePlacement = graphemeList[i];
                graphemePlacement.pointStart = points[i];
                float distX = points[i + 1].X - points[i].X;
                float distY = points[i + 1].Y - points[i].Y;
                graphemePlacement.angle = (float) (Math.Atan2(distY, distX) * 360.0 / (Math.PI * 2));
                graphemeList[i] = graphemePlacement;
            }

            return graphemeList;
        }