merge r67228-r67235, r67237, r67251 and r67256-67259 to trunk (they are
[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
88
89                 float WrapWidth {
90                         get { return (_format.IsVertical ? Height : Width) - (Margin * 2); }
91                 }
92
93                 internal float WrapHeight {
94                         get { return (_format.IsVertical ? Width : Height); }
95                 }
96
97                 internal float Width {
98                         get { return _width; }
99                 }
100
101                 internal float Height {
102                         get { return _height; }
103                 }
104
105                 internal StringFormat Format {
106                         get { return _format; }
107                 }
108
109                 internal float Margin {
110                         get { return _margin; }
111                 }
112
113                 internal int CharsConsumed {
114                         get { return _charsConsumed; }
115                 }
116
117                 internal int CurrentRun {
118                         get { return _currentRun; }
119                 }
120
121                 internal int CurrentPosition {
122                         get { return _currentPos; }
123                 }
124
125                 internal float AccumulatedHeight {
126                         get { return _accumulatedHeight; }
127                 }
128
129                 internal float GetAdvanceBetween(int start, int limit) {
130                         return _measurer.getAdvanceBetween(start, limit);
131                 }
132
133                 internal geom.AffineTransform Transform {
134                         get { return Format.IsVertical ? Rotate90Transform : Matrix.IdentityTransform.NativeObject; }
135                 }
136
137                 #endregion
138
139                 #region Methods
140
141                 LineLayout NextTextLayoutFromMeasurer() {
142                         if (_accumulatedHeight >= WrapHeight) {
143                                 _charsConsumed += _currentPos;
144                                 return null;
145                         }
146
147                         int limit = _measurer.getLineBreakIndex(_currentPos, WrapWidth);
148
149                         int wordBreak = limit;
150                         if (wordBreak < _currentRun) {
151                                 while (wordBreak >= _currentPos && char.IsLetterOrDigit(_s, _charsConsumed + wordBreak))
152                                         wordBreak--;
153
154                                 if (wordBreak > _currentPos)
155                                         limit = wordBreak + 1;
156                         }
157                         font.TextLayout layout = _measurer.getLayout(_currentPos, limit);
158                         
159                         LineLayout lineLayout = new LineLayout(
160                                 layout,
161                                 this,
162                                 _accumulatedHeight);
163
164                         float lineHeight = lineLayout.Ascent + lineLayout.Descent;
165                         
166                         if (Format.LineLimit && (_accumulatedHeight + lineHeight > WrapHeight)) {
167                                 _charsConsumed += _currentPos;
168                                 return null;
169                         }
170
171                         _accumulatedHeight += lineHeight + lineLayout.Leading;
172
173                         _currentPos = limit;
174
175                         while (_currentPos < _currentRun) {
176                                 if (char.IsWhiteSpace(_s, _charsConsumed + _currentPos))
177                                         _currentPos++;
178                                 else
179                                         break;
180                         }
181                         return lineLayout;
182                 }
183
184                 internal LineLayout NextLine() {
185                         if (_currentPos < _currentRun && !Format.NoWrap)
186                                 return NextTextLayoutFromMeasurer();
187
188                         _charsConsumed += _currentRun;
189                         if (_charsConsumed >= _s.Length)\r
190                                 return null;\r
191 \r
192                         string s;\r
193                         int lineBreakIndex = _s.IndexOf(NewLine, _charsConsumed);\r
194                         if (lineBreakIndex >= 0) {\r
195                                 s = _s.Substring(_charsConsumed, lineBreakIndex - _charsConsumed + NewLine.Length);\r
196                         }\r
197                         else\r
198                                 s = _s.Substring(_charsConsumed);\r
199 \r
200                         _currentRun = s.Length;\r
201                         _currentPos = 0;\r
202 \r
203                         text.AttributedString aS = new text.AttributedString(s);\r
204                                 \r
205                         // TODO: add more attribs according to StringFormat\r
206                         aS.addAttribute(font.TextAttribute.FONT, _font.NativeObject);\r
207                         if((_font.Style & FontStyle.Underline) != FontStyle.Regular)
208                                 aS.addAttribute(font.TextAttribute.UNDERLINE, font.TextAttribute.UNDERLINE_ON);
209                         if((_font.Style & FontStyle.Strikeout) != FontStyle.Regular)
210                                 aS.addAttribute(font.TextAttribute.STRIKETHROUGH, font.TextAttribute.STRIKETHROUGH_ON);\r
211 \r
212                         text.AttributedCharacterIterator charIter = aS.getIterator();\r
213
214                         _measurer = new font.TextMeasurer(charIter, _frc);
215                         return NextTextLayoutFromMeasurer();
216                 }
217
218                 internal geom.AffineTransform CalcLineAlignmentTransform() {
219                         if (Format.LineAlignment == StringAlignment.Near)\r
220                                 return null;\r
221                         float height = WrapHeight;\r
222                         if (float.IsPositiveInfinity(height))\r
223                                 height = 0;\r
224 \r
225                         float shift = height - AccumulatedHeight;\r
226                         if (height > 0 && shift <= 0)\r
227                                 return null;\r
228 \r
229                         if (Format.LineAlignment == StringAlignment.Center)\r
230                                 shift /= 2;\r
231                         else\r
232                                 if (Format.IsVertical && Format.IsRightToLeft)\r
233                                 return null;\r
234 \r
235                         return Format.IsVertical ?\r
236                                 geom.AffineTransform.getTranslateInstance(shift, 0) :\r
237                                 geom.AffineTransform.getTranslateInstance(0, shift);
238                 }
239
240                 #endregion
241         }
242 }