Merge pull request #364 from directhex/master
[mono.git] / mcs / class / System.Drawing / System.Drawing.Text / TextLineIterator.jvm.cs
1 //
2 // System.Drawing.Test.TextLineIterator.jvm.cs
3 //
4 // Author:
5 // Konstantin Triger <kostat@mainsoft.com>
6 //
7 // Copyright (C) 2005 Mainsoft Corporation, (http://www.mainsoft.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Drawing.Drawing2D;
31
32 using font = java.awt.font;\r
33 using text = java.text;
34 using awt = java.awt;\r
35 using geom = java.awt.geom;
36
37 namespace System.Drawing.Text {
38         internal sealed class TextLineIterator {
39
40                 #region Fields
41
42                 readonly float _width;\r
43                 readonly float _height;\r
44                 readonly StringFormat _format;\r
45                 readonly font.FontRenderContext _frc;
46                 readonly string _s;
47                 readonly Font _font;
48                 readonly float _margin;
49
50                 static readonly string NewLine;\r
51 \r
52                 static readonly geom.AffineTransform Rotate90Transform = \r
53                         geom.AffineTransform.getRotateInstance(Math.PI/2);\r
54 \r
55                 font.TextMeasurer _measurer;\r
56                 int _charsConsumed = 0;\r
57                 int _currentPos = 0;\r
58                 int _currentRun = 0;\r
59                 float _accumulatedHeight = 0;\r
60 \r
61                 #endregion\r
62 \r
63                 #region ctors\r
64 \r
65                 static TextLineIterator() {\r
66                         string newLine = Environment.NewLine;\r
67                         if (newLine == null || newLine.Length == 0 || newLine[newLine.Length - 1] == '\n')\r
68                                 newLine = "\n";\r
69 \r
70                         NewLine = newLine;\r
71                 }
72
73                 internal TextLineIterator(string s, Font font, font.FontRenderContext frc, StringFormat format, float width, float height) {\r
74                         _format = (format != null) ? format : new StringFormat();\r
75                         _font = font;\r
76                         _s = (s != null) ? s : String.Empty;\r
77                         _frc = frc;\r
78                         FontFamily ff = font.FontFamily;\r
79                         _margin = font.Size*ff.GetDrawMargin(font.Style)/ff.GetEmHeight(font.Style);\r
80 \r
81                         _width = width;\r
82                         _height = height;\r
83                 }
84
85                 #endregion
86
87                 #region Properties\r
88 \r
89                 float WrapWidth\r
90                 {\r
91                         get {\r
92                                 float widthOrHeight = _format.IsVertical ? Height : Width;\r
93                                 if (!_format.IsGenericTypographic) {\r
94                                         widthOrHeight =\r
95                                                 ((_format.IsVertical ? Height : Width) - (0.463f * FontSize)) / 1.028f;\r
96                                 }\r
97                                 return widthOrHeight;\r
98                         }\r
99                 }
100
101                 internal float WrapHeight {\r
102                         get {\r
103                                 float widthOrHeight = _format.IsVertical ? Width : Height;\r
104                                 if (!_format.IsGenericTypographic) {\r
105                                         widthOrHeight = (_format.IsVertical ? Width : Height) / 1.08864f;\r
106                                 }\r
107                                 return widthOrHeight;
108                         }
109                 }
110
111                 internal float Width {
112                         get { return _width; }
113                 }
114
115                 internal float Height {
116                         get { return _height; }
117                 }
118
119                 internal StringFormat Format {
120                         get { return _format; }
121                 }\r
122 \r
123                 internal float PadWidth (float origWidth)\r
124                 {\r
125                         if (Format.IsGenericTypographic)\r
126                                 return origWidth;\r
127 \r
128                         //This is a proximity to .NET calculated Width.\r
129                         return origWidth * 1.028f + 0.463f * FontSize;\r
130                 }\r
131 \r
132                 internal float PadHeight (float origHeight)\r
133                 {\r
134                         if (Format.IsGenericTypographic)\r
135                                 return origHeight;\r
136 \r
137                         //This is a proximity to .NET calculated Height.\r
138                         return 1.08864f * origHeight;\r
139                 }               \r
140 \r
141                 internal float FontSize\r
142                 {\r
143                         get {\r
144                                 return _font.Size;\r
145                         }\r
146                 }
147
148                 internal int CharsConsumed {
149                         get { return _charsConsumed; }
150                 }
151
152                 internal int CurrentRun {
153                         get { return _currentRun; }
154                 }
155
156                 internal int CurrentPosition {
157                         get { return _currentPos; }
158                 }
159
160                 internal float AccumulatedHeight {
161                         get { return _accumulatedHeight; }
162                 }
163
164                 internal float GetAdvanceBetween(int start, int limit) {
165                         return _measurer.getAdvanceBetween(start, limit);
166                 }
167
168                 internal geom.AffineTransform Transform {
169                         get { return Format.IsVertical ? Rotate90Transform : Matrix.IdentityTransform.NativeObject; }
170                 }
171
172                 #endregion
173
174                 #region Methods
175
176                 LineLayout NextTextLayoutFromMeasurer() {
177                         if (_accumulatedHeight >= WrapHeight) {
178                                 _charsConsumed += _currentPos;
179                                 return null;
180                         }
181
182                         int limit = _measurer.getLineBreakIndex(_currentPos, WrapWidth);
183
184                         int wordBreak = limit;
185                         if (wordBreak < _currentRun) {
186                                 while (wordBreak >= _currentPos && char.IsLetterOrDigit(_s, _charsConsumed + wordBreak))
187                                         wordBreak--;
188
189                                 if (wordBreak > _currentPos)
190                                         limit = wordBreak + 1;
191                         }
192                         font.TextLayout layout = _measurer.getLayout(_currentPos, limit);
193                         
194                         LineLayout lineLayout = new LineLayout(
195                                 layout,
196                                 this,
197                                 _accumulatedHeight);\r
198 \r
199                         float lineHeight = PadHeight (lineLayout.Ascent + lineLayout.Descent + lineLayout.Leading);
200                         
201                         if (Format.LineLimit && (_accumulatedHeight + lineHeight > WrapHeight)) {
202                                 _charsConsumed += _currentPos;
203                                 return null;
204                         }
205
206                         _accumulatedHeight += lineHeight + lineLayout.Leading;
207
208                         _currentPos = limit;
209
210                         while (_currentPos < _currentRun) {
211                                 if (char.IsWhiteSpace(_s, _charsConsumed + _currentPos))
212                                         _currentPos++;
213                                 else
214                                         break;
215                         }
216                         return lineLayout;
217                 }
218
219                 internal LineLayout NextLine() {
220                         if (_currentPos < _currentRun && !Format.NoWrap)
221                                 return NextTextLayoutFromMeasurer();
222
223                         _charsConsumed += _currentRun;
224                         if (_charsConsumed >= _s.Length)\r
225                                 return null;\r
226 \r
227                         string s;\r
228                         int lineBreakIndex = _s.IndexOf(NewLine, _charsConsumed);\r
229                         if (lineBreakIndex >= 0) {\r
230                                 s = _s.Substring(_charsConsumed, lineBreakIndex - _charsConsumed + NewLine.Length);\r
231                         }\r
232                         else\r
233                                 s = _s.Substring(_charsConsumed);\r
234 \r
235                         _currentRun = s.Length;\r
236                         _currentPos = 0;\r
237 \r
238                         text.AttributedString aS = new text.AttributedString(s);\r
239                                 \r
240                         // TODO: add more attribs according to StringFormat\r
241                         aS.addAttribute(font.TextAttribute.FONT, _font.NativeObject);\r
242                         if((_font.Style & FontStyle.Underline) != FontStyle.Regular)
243                                 aS.addAttribute(font.TextAttribute.UNDERLINE, font.TextAttribute.UNDERLINE_ON);
244                         if((_font.Style & FontStyle.Strikeout) != FontStyle.Regular)
245                                 aS.addAttribute(font.TextAttribute.STRIKETHROUGH, font.TextAttribute.STRIKETHROUGH_ON);\r
246 \r
247                         text.AttributedCharacterIterator charIter = aS.getIterator();\r
248
249                         _measurer = new font.TextMeasurer(charIter, _frc);
250                         return NextTextLayoutFromMeasurer();
251                 }
252
253                 internal geom.AffineTransform CalcLineAlignmentTransform() {
254                         if (Format.LineAlignment == StringAlignment.Near)\r
255                                 return null;\r
256                         float height = WrapHeight;\r
257                         if (float.IsPositiveInfinity(height))\r
258                                 height = 0;\r
259 \r
260                         float shift = height - AccumulatedHeight;\r
261                         if (height > 0 && shift <= 0)\r
262                                 return null;\r
263 \r
264                         if (Format.LineAlignment == StringAlignment.Center)\r
265                                 shift /= 2;\r
266                         else\r
267                                 if (Format.IsVertical && Format.IsRightToLeft)\r
268                                 return null;\r
269 \r
270                         return Format.IsVertical ?\r
271                                 geom.AffineTransform.getTranslateInstance(shift, 0) :\r
272                                 geom.AffineTransform.getTranslateInstance(0, shift);
273                 }
274
275                 #endregion
276         }
277 }