VectSharp  2.2.1
A light library for C# vector graphics
Line.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Text;
21 
22 namespace VectSharp.Markdown
23 {
24  internal abstract class LineFragment
25  {
26  public abstract void Translate(double deltaX, double deltaY);
27  public string Tag { get; protected set; }
28  }
29 
30  internal class TextFragment : LineFragment
31  {
32  public string Text { get; }
33  public Point Position { get; private set; }
34  public Font Font { get; }
35  public Colour Colour { get; }
36 
37  public TextFragment(Point position, string text, Font font, Colour colour, string tag)
38  {
39  this.Position = position;
40  this.Text = text;
41  this.Font = font;
42  this.Colour = colour;
43  this.Tag = tag;
44  }
45 
46  public override void Translate(double deltaX, double deltaY)
47  {
48  this.Position = new Point(this.Position.X + deltaX, this.Position.Y + deltaY);
49  }
50  }
51 
52  internal class UnderlineFragment : LineFragment
53  {
54  public Point Start { get; private set; }
55  public Point End { get; private set; }
56  public Colour Colour { get; }
57  public double Thickness { get; }
58 
59  public UnderlineFragment(Point start, Point end, Colour colour, double thickness, string tag)
60  {
61  this.Start = start;
62  this.End = end;
63  this.Colour = colour;
64  this.Thickness = thickness;
65  this.Tag = tag;
66  }
67 
68  public override void Translate(double deltaX, double deltaY)
69  {
70  this.Start = new Point(this.Start.X + deltaX, this.Start.Y + deltaY);
71  this.End = new Point(this.End.X + deltaX, this.End.Y + deltaY);
72  }
73  }
74 
75  internal class RectangleFragment : LineFragment
76  {
77  public Point TopLeft { get; private set; }
78  public Size Size { get; }
79  public Colour Colour { get; }
80 
81  public RectangleFragment(Point topLeft, Size size, Colour colour, string tag)
82  {
83  this.TopLeft = topLeft;
84  this.Size = size;
85  this.Colour = colour;
86  this.Tag = tag;
87  }
88 
89  public override void Translate(double deltaX, double deltaY)
90  {
91  this.TopLeft = new Point(this.TopLeft.X + deltaX, this.TopLeft.Y + deltaY);
92  }
93  }
94 
95  internal class GraphicsFragment : LineFragment
96  {
97  public Point Origin { get; private set; }
98  public Graphics Graphics { get; }
99  public double Ascent { get; }
100 
101  public GraphicsFragment(Point origin, Graphics graphics, double ascent)
102  {
103  this.Origin = origin;
104  this.Graphics = graphics;
105  this.Ascent = ascent;
106  }
107 
108  public override void Translate(double deltaX, double deltaY)
109  {
110  this.Origin = new Point(this.Origin.X + deltaX, this.Origin.Y + deltaY);
111  }
112  }
113 
114  internal class Line
115  {
116  public List<LineFragment> Fragments { get; }
117  public double InitialAscent { get; }
118 
119  public Line(double initialAscent)
120  {
121  this.InitialAscent = initialAscent;
122  this.Fragments = new List<LineFragment>();
123  }
124 
125  public void Render(ref Graphics graphics, ref MarkdownContext context, MarkdownRenderer.NewPageAction newPageAction, double pageMaxY)
126  {
127  double deltaY = 0;
128  double maxY = 0;
129 
130  foreach (LineFragment fragment in this.Fragments)
131  {
132  if (fragment is TextFragment text)
133  {
134  deltaY = Math.Max(deltaY, text.Font.Ascent - this.InitialAscent);
135  maxY = Math.Max(maxY, text.Position.Y - text.Font.Descent);
136  }
137  else if (fragment is UnderlineFragment underline)
138  {
139  maxY = Math.Max(maxY, Math.Max(underline.Start.Y, underline.End.Y));
140  }
141  else if (fragment is RectangleFragment rectangle)
142  {
143  maxY = Math.Max(maxY, rectangle.TopLeft.Y + rectangle.Size.Height);
144  }
145  else if (fragment is GraphicsFragment graphicsFragment)
146  {
147  deltaY = Math.Max(deltaY, graphicsFragment.Ascent - this.InitialAscent);
148  maxY = Math.Max(maxY, graphicsFragment.Origin.Y);
149  }
150  }
151 
152  maxY += deltaY;
153 
154  if (maxY > pageMaxY)
155  {
156  double currCursY = context.Cursor.Y;
157 
158  newPageAction(ref context, ref graphics);
159 
160  double currDelta = deltaY;
161 
162  context.Cursor = new Point(context.Cursor.X, context.Cursor.Y + this.InitialAscent + currDelta);
163 
164  deltaY -= currCursY - context.Cursor.Y + currDelta;
165 
166  context.Cursor = new Point(context.Cursor.X, context.Cursor.Y);
167  }
168  else
169  {
170  context.Cursor = new Point(context.Cursor.X, context.Cursor.Y + deltaY);
171  }
172 
173 
174  for (int i = 0; i < this.Fragments.Count; i++)
175  {
176  LineFragment fragment = this.Fragments[i];
177  if (fragment is TextFragment text)
178  {
179  Size size = text.Font.MeasureText(text.Text);
180  context.BottomRight = new Point(Math.Max(context.BottomRight.X, size.Width + text.Position.X + context.Translation.X), Math.Max(context.BottomRight.Y, text.Position.Y + size.Height + deltaY + context.Translation.Y));
181  graphics.FillText(text.Position.X, text.Position.Y + deltaY, text.Text, text.Font, text.Colour, TextBaselines.Baseline, tag: fragment.Tag);
182  }
183  else if (fragment is UnderlineFragment underline)
184  {
185  graphics.StrokePath(new GraphicsPath().MoveTo(underline.Start.X, underline.Start.Y + deltaY).LineTo(underline.End.X, underline.End.Y + deltaY), underline.Colour, underline.Thickness, tag: fragment.Tag);
186  }
187  else if (fragment is RectangleFragment rectangle)
188  {
189  graphics.FillRectangle(rectangle.TopLeft.X, rectangle.TopLeft.Y + deltaY, rectangle.Size.Width, rectangle.Size.Height, rectangle.Colour, tag: fragment.Tag);
190  }
191  else if (fragment is GraphicsFragment graphicsFragment)
192  {
193  graphics.DrawGraphics(graphicsFragment.Origin.X, graphicsFragment.Origin.Y + deltaY, graphicsFragment.Graphics);
194  }
195  }
196  }
197  }
198 }
VectSharp.TextBaselines
TextBaselines
Represent text baselines.
Definition: Enums.cs:24
VectSharp.SegmentType.Line
@ Line
The segment represents a straight line from the current point to a new point.
VectSharp.Markdown
Definition: HtmlTag.cs:26