Mercurial > otakunoraifu
annotate font/font_layout.cc @ 60:e16e13d8cd68
Replaced SATURATE -> ADD, implemented objComposite, corrected minor things
author | Thibaut GIRKA <thib@sitedethib.com> |
---|---|
date | Fri, 18 Dec 2009 20:41:38 +0100 |
parents | ddbcbd000206 |
children | 4416cfac86ae |
rev | line source |
---|---|
0 | 1 /* layout2.cc |
2 * テキストの禁則処理、レイアウトなどを行う | |
3 */ | |
4 /* | |
5 * Copyright (c) 2004-2006 Kazunori "jagarl" Ueno | |
6 * All rights reserved. | |
7 * | |
8 * Redistribution and use in source and binary forms, with or without | |
9 * modification, are permitted provided that the following conditions | |
10 * are met: | |
11 * 1. Redistributions of source code must retain the above copyright | |
12 * notice, this list of conditions and the following disclaimer. | |
13 * 2. Redistributions in binary form must reproduce the above copyright | |
14 * notice, this list of conditions and the following disclaimer in the | |
15 * documentation and/or other materials provided with the distribution. | |
16 * 3. The name of the author may not be used to endorse or promote products | |
17 * derived from this software without specific prior written permission. | |
18 * | |
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
52 | 31 #include <vector> |
32 #include <map> | |
33 #include <iostream> | |
0 | 34 |
35 using namespace std; | |
36 | |
52 | 37 #include "font.h" |
38 #include "text.h" | |
0 | 39 |
40 const int line_skip = 1; // 行と行の間の間隔 | |
41 const int ruby_textskip = 0; // 文字とルビの間の間隔 | |
42 const int ruby_lineskip = 1; // ルビがあるときに行間に加える値 | |
43 const double ruby_scale = 0.4; // ルビのスケール | |
44 | |
45 class TextGlyphStreamHelper; | |
46 | |
47 enum KinsokuType { KinsokuHead = 1, KinsokuTail = 2}; | |
48 static int kinsoku_table1[] = { | |
49 /* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 */ | |
50 0,0,2,2,2,2,0,0, /* X 、。,.・: */ | |
51 0,2,2,0,0,0,0,0, /* ;?!゛゜´`¨ */ | |
52 0,0,0,0,0,0,0,0, /* ^ ̄_ヽヾゝゞ〃 */ | |
53 0,0,0,0,2,0,0,0, /* 仝々〆〇ー―‐/ */ | |
54 0,2,0,0,2,2,1,2, /* \〜‖|…‥‘’ */ | |
55 1,2,1,2,1,2,1,2, /* “”()〔〕[] */ | |
56 1,2,1,2,1,2,1,2, /* {}〈〉《》「」 */ | |
57 1,2,1,2,0,0,0,0, /* 『』【】+−±× */ | |
58 0,0,0,0,0,0,0,0, /* ÷=≠<>≦≧∞ */ | |
59 0,0,0,0,0,0,0,0, /* ∴♂♀°′″℃¥ */ | |
60 0,0,0,0,0,0,0,0, /* $¢£%#&*@ */ | |
61 0,0,0,0,0,0,0,0, /* §☆★○●◎◇X */ | |
62 0 | |
63 }; | |
64 static int kinsoku_table2[] = { | |
65 0,2,0,2,0,2,0,2,0,2,0,0,0,0,0,0, /* ぁあぃいぅうぇえぉおかがきぎく */ | |
66 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ぐけげこごさざしじすずせぜそぞた */ | |
67 0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, /* だちぢっつづてでとどなにぬねのは */ | |
68 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ばぱひびぴふぶぷへべぺほぼぽまみ */ | |
69 0,0,0,2,0,2,0,2,0,0,0,0,0,0,2,0, /* むめもゃやゅゆょよらりるれろゎわ */ | |
70 0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0, /* ゐゑをんヴヵヶ */ | |
71 0 | |
72 }; | |
73 | |
74 inline int Kinsoku(int code) { | |
75 if ( (code&0xff80) == 0xa180) return kinsoku_table1[ (code&0xff) - 0xa0]; | |
76 if ( (code&0xfe80) == 0xa480) return kinsoku_table2[ (code&0xff) - 0xa0]; /* code = 0xa400 / 0xa500 */ | |
77 return 0; | |
78 } | |
79 | |
80 class TextGlyphStreamHelper { | |
52 | 81 private: |
82 typedef TextStream::Iterator Iterator; | |
83 typedef TextGlyphStream::iterator iterator; | |
0 | 84 |
52 | 85 TextGlyphStream* data; |
0 | 86 |
52 | 87 // information for rendering |
88 unsigned char r, g, b; | |
89 XKFont::Face* face; | |
90 XKFont::Face* ruby_face; | |
91 XKFont::Font* font; | |
0 | 92 |
52 | 93 public: |
94 int min_lineheight; | |
95 TextGlyphStreamHelper(XKFont::Font* font); | |
96 // helper functions | |
97 void Init(TextGlyphStream* data); | |
98 Iterator Add(int& x, Iterator begin, Iterator end, int max_x = 0); | |
99 Iterator AddRuby(int& x, Iterator begin, Iterator end); | |
100 int CharWidth(int code); | |
101 void SetGroup(iterator begin, iterator end); | |
102 void CalcHeight(int& ascent, int& descent, iterator begin, iterator end); | |
103 void AdjustPosition(int xstart_add, int xend_add, int y_add, iterator begin, iterator end); | |
0 | 104 }; |
105 | |
106 TextGlyphStreamHelper::TextGlyphStreamHelper(XKFont::Font* __font) { | |
107 font = __font; | |
108 face = font->FaceLoad(1.0); | |
109 ruby_face = 0; | |
110 r = 255; g = 255; b = 255; | |
111 min_lineheight = font->vsize; | |
112 } | |
113 | |
114 void TextGlyphStreamHelper::Init(TextGlyphStream* __data) { | |
115 r = 255; g = 255; b = 255; | |
116 face = font->FaceLoad(1.0); | |
117 data = __data; | |
118 data->clear(); | |
119 data->font = font; | |
120 } | |
121 | |
122 TextGlyphStreamHelper::Iterator | |
123 TextGlyphStreamHelper::Add(int& x, TextGlyphStreamHelper::Iterator begin, TextGlyphStreamHelper::Iterator end, int max_x) { | |
124 /* text を glyph に変換する */ | |
125 TextGlyph gl; | |
126 Iterator it; | |
127 gl.x = x; gl.y = 0; gl.r = r; gl.g = g; gl.b = b; gl.flag = TextGlyph::Flag(0); gl.is_rev = false; | |
128 for (it = begin; it != end; it++) { | |
129 if (it->type != TextElem::glyph) { | |
130 if (it->type == TextElem::color) { | |
131 gl.r = r = it->impl.Color.r; | |
132 gl.g = g = it->impl.Color.g; | |
133 gl.b = b = it->impl.Color.b; | |
134 } else if (it->type == TextElem::size) { | |
50 | 135 delete face; |
0 | 136 face = font->FaceLoad(it->impl.Size.scale); |
137 } else if (it->type == TextElem::escape) { | |
138 x = gl.x; | |
139 return it; | |
140 } | |
141 continue; | |
142 } | |
143 try { | |
144 gl.glyph = face->GlyphLoad(it->impl.Glyph.code); | |
145 if (max_x > 0 && gl.x + gl.glyph->advance.x > max_x) { | |
146 x = gl.x; | |
147 return it; | |
148 } | |
149 if ( Kinsoku(it->impl.Glyph.code) == KinsokuTail) | |
150 gl.flag = TextGlyph::Flag(gl.flag | TextGlyph::Kinsoku); | |
151 else | |
152 gl.flag = TextGlyph::Flag(0); | |
153 data->push_back(gl); | |
154 gl.x += gl.glyph->advance.x; | |
155 } catch(...) {} | |
156 } | |
157 x = gl.x; | |
158 return it; | |
159 } | |
160 | |
161 TextGlyphStreamHelper::Iterator TextGlyphStreamHelper::AddRuby(int& x, TextGlyphStreamHelper::Iterator sbegin, TextGlyphStreamHelper::Iterator send) { | |
162 Iterator it; | |
163 it = sbegin; | |
164 if (it == send) return it; | |
165 if (it->type != TextElem::escape || it->impl.Escape.type != TextElem::ruby_start) return sbegin; | |
166 it++; | |
167 /* まず、本文描画 */ | |
168 int str_firstpos = data->size(); | |
169 int str_width = 0; | |
170 it = Add(str_width, it, send); | |
171 if (it == send || it->type != TextElem::escape || it->impl.Escape.type != TextElem::ruby_startruby) { | |
172 // ありえないはずだが、取り合えずなにもしないで終了 | |
173 cerr << "TextGlyphStream::AddRuby : invalid operation; fallback to the upeer level"<<endl; | |
174 data->erase(data->begin()+str_firstpos, data->end()); | |
175 return sbegin+1; | |
176 } | |
177 it++; | |
178 int str_lastpos = data->size()-1; | |
179 TextGlyph& str_first = data->begin()[str_firstpos]; | |
180 TextGlyph& str_last = data->back(); | |
181 // 次に、フォントを取りかえてルビ描画 | |
182 int ruby_firstpos = data->size(); | |
183 XKFont::Face* save_font = face; | |
184 if (ruby_face == 0) ruby_face = font->FaceLoad(ruby_scale); | |
185 face = ruby_face; | |
186 int ruby_width = 0; | |
187 it = Add(ruby_width, it, send); | |
188 if (it->type != TextElem::escape || it->impl.Escape.type != TextElem::ruby_end) { | |
189 /* ありえないはずだが、取り合えずなにもしないで終了 */ | |
190 cerr << "TextGlyphStream::AddRuby : invalid operation; fallback to the upeer level"<<endl; | |
191 data->erase(data->begin()+str_firstpos, data->end()); | |
192 return sbegin+1; | |
193 } | |
194 it++; | |
195 face = save_font; | |
196 TextGlyph& ruby_first = (*data)[ruby_firstpos]; | |
197 TextGlyph& ruby_last = data->back(); | |
198 | |
199 /* ルビを移動すべき高さを求める */ | |
200 int dummy, str_ascent, ruby_descent; | |
201 CalcHeight(str_ascent, dummy, data->begin()+str_firstpos, data->begin()+ruby_firstpos); | |
202 CalcHeight(dummy, ruby_descent, data->begin()+ruby_firstpos, data->end()); | |
203 int ruby_height = str_ascent + ruby_descent + ruby_textskip; | |
204 | |
205 /* センタリングした場合の、ルビの左側、右側のマージン */ | |
206 int leftmergin, rightmergin; | |
207 leftmergin = str_first.glyph->advance.x/2 - (ruby_first.glyph->advance.x+1)/2; | |
208 rightmergin = str_last.glyph->advance.x/2 - (ruby_last.glyph->advance.x+1)/2; | |
209 | |
210 /* ルビ、本文の横方向の移動 */ | |
211 int ruby_xstart_add = 0, ruby_xend_add = 0, str_xstart_add=0, str_xend_add = 0; | |
212 if (ruby_width+leftmergin+rightmergin <= str_width) { // ルビの方が小さい | |
213 ruby_xstart_add = leftmergin; | |
214 ruby_xend_add = str_width-rightmergin-ruby_width; | |
215 } else if (ruby_width <= str_width) { // マージンを減らす必要あり | |
216 leftmergin = (str_width-ruby_width)/2; | |
217 ruby_xstart_add = leftmergin; | |
218 ruby_xend_add = str_width-leftmergin-ruby_width; | |
219 } else { // ルビの方が大きい | |
220 int str_count = ruby_firstpos - str_firstpos; | |
221 str_xstart_add = ruby_width/str_count/2 - str_first.glyph->advance.x/2; | |
222 str_xend_add = (ruby_width-str_width) - (ruby_width/str_count/2-str_last.glyph->advance.x/2); | |
223 str_width = ruby_width; | |
224 } | |
225 AdjustPosition(str_xstart_add+x, str_xend_add+x, 0, data->begin()+str_firstpos, data->begin()+ruby_firstpos); | |
226 AdjustPosition(ruby_xstart_add+x, ruby_xend_add+x, -ruby_height, data->begin()+ruby_firstpos, data->end()); | |
227 | |
228 /* 本文が一文字ずつ表示されるように glyph の順番を入れかえ、グループ化 */ | |
229 vector<TextGlyph> save; | |
230 save.assign(data->begin()+str_firstpos, data->end()); | |
231 iterator it_str = save.begin(); | |
232 iterator it_ruby = save.begin()+(ruby_firstpos-str_firstpos); | |
233 iterator dit = data->begin()+str_firstpos; | |
234 int str_count = it_ruby-it_str; | |
235 int ruby_count = save.end()-it_ruby; | |
236 int i,j = 0; | |
237 for (i=0; i<str_count; i++) { | |
238 iterator charstart = dit; | |
239 int jend = (i+1)*ruby_count/str_count; | |
240 for (; j<jend; j++) { | |
241 *dit++ = *it_ruby++; | |
242 } | |
243 *dit++ = *it_str++; | |
244 SetGroup(charstart, dit); | |
245 } | |
246 x += str_width; | |
247 return it; | |
248 } | |
249 | |
250 | |
251 void TextGlyphStreamHelper::SetGroup(TextGlyphStreamHelper::iterator begin, TextGlyphStreamHelper::iterator end) { | |
252 iterator it; | |
253 for (it = begin; it+1 != end; it++) | |
254 it->flag = TextGlyph::Flag(it->flag |TextGlyph::Group); | |
255 it->flag = TextGlyph::Flag(it->flag & ~TextGlyph::Group); | |
256 return; | |
257 } | |
258 | |
259 void TextGlyphStreamHelper::AdjustPosition(int xstart_add, int xend_add, int y_add, TextGlyphStreamHelper::iterator begin, TextGlyphStreamHelper::iterator end) { | |
260 iterator it; | |
261 /* 文字数を数える */ | |
262 int total_count = 0; | |
263 for (it = begin; it != end; it++) { | |
264 if (it->flag & TextGlyph::Group) continue; | |
265 total_count++; | |
266 } | |
267 /* 文字間のギャップを変更 */ | |
268 int incr = 0; | |
269 if (total_count != 1) incr = (xend_add - xstart_add) * 256 / (total_count-1); | |
270 int cur = xstart_add * 256; | |
271 for (it = begin; it != end; it++) { | |
272 it->x += cur / 256; | |
273 it->y += y_add; | |
274 if (it->flag & TextGlyph::Group) continue; | |
275 cur += incr; | |
276 } | |
277 return; | |
278 } | |
279 void TextGlyphStreamHelper::CalcHeight(int& ascent_r, int& descent_r, TextGlyphStreamHelper::iterator begin, TextGlyphStreamHelper::iterator end) { | |
280 iterator it; | |
281 /* 最大の descent, ascent を計算 */ | |
282 int ascent = 0; | |
283 int descent = 0; | |
284 for (it = begin; it != end; it++) { | |
285 | |
286 int y_top = it->y - it->glyph->bitmap_top; | |
287 int y_bottom = it->y + it->glyph->bitmap.rows - it->glyph->bitmap_top; | |
288 | |
289 if (descent < y_bottom) descent = y_bottom; | |
290 if (ascent < -y_top) ascent = -y_top; | |
291 } | |
292 ascent_r = ascent; | |
293 descent_r = descent; | |
294 return; | |
295 } | |
296 | |
297 int TextGlyphStreamHelper::CharWidth(int code) { | |
298 try { | |
299 XKFont::Glyph* g = face->GlyphLoad(code); | |
300 return g->advance.x; | |
301 } catch(...) { | |
302 return 0; | |
303 } | |
304 } | |
305 | |
306 class TextHorizLayout { | |
307 typedef TextStream::Iterator Iterator; | |
308 | |
309 Iterator pos; | |
310 Iterator end; | |
311 TextGlyphStream* data; | |
312 TextGlyphStreamHelper helper; | |
313 int tab_width; | |
314 int cur_y; | |
315 | |
316 void SetName(void); | |
317 void SetLineHead(void); | |
318 void MakeLine(int line_first, int width, vector<int>& lineheights); | |
319 public: | |
320 TextHorizLayout(XKFont::Font* font); | |
321 void Layout(TextStream& stream, TextGlyphStream& glyph, vector<int>& lineheights, int width); | |
322 }; | |
323 | |
324 TextHorizLayout::TextHorizLayout(XKFont::Font* font) : | |
325 helper(font), tab_width(0), cur_y(0) { | |
326 } | |
327 | |
328 void TextHorizLayout::Layout(TextStream& stream, TextGlyphStream& glyph, vector<int>& lineheights, int width) { | |
329 pos = stream.container.begin(); | |
330 end = stream.container.end(); | |
331 data = &glyph; | |
332 | |
333 helper.Init(data); | |
334 tab_width = 0; | |
335 cur_y = 0; | |
336 int prev_y = 0; | |
337 int line_start = glyph.size(); | |
338 while(pos != end) { | |
339 /* | |
340 if (pos->type == TextElem::glyph) { int c = pos->impl.Glyph.code; char cc[3]={0,0,0};cc[0]=c>>8;cc[1]=c;cout<<"glyph "<<cc<<endl;} | |
341 if (pos->type == TextElem::escape) { cout<<"escape "<<pos->impl.Escape.type<<endl;} | |
342 */ | |
343 SetName(); | |
344 SetLineHead(); | |
345 MakeLine(line_start, width, lineheights); | |
346 if (line_start != glyph.size()) { | |
347 data->back().flag = TextGlyph::Flag(data->back().flag | TextGlyph::PhraseEnd | TextGlyph::LineEnd); | |
348 } | |
349 prev_y = cur_y; | |
350 if (pos != end && pos->type == TextElem::escape && pos->impl.Escape.type == TextElem::ret) pos++; | |
351 line_start = glyph.size(); | |
352 } | |
353 return; | |
354 } | |
355 | |
356 void TextHorizLayout::SetName(void) { | |
357 Iterator it; | |
358 | |
359 tab_width = 0; | |
360 /* 行頭が名前なら、処理開始 */ | |
361 for (; pos != end; pos++) { | |
362 if (pos->type == TextElem::escape || pos->type == TextElem::glyph) break; | |
363 int x = 0; | |
364 helper.Add(x, pos, pos+1); | |
365 } | |
366 | |
367 if (pos->type != TextElem::escape || pos->impl.Escape.type != TextElem::name_start) return; | |
368 | |
369 /* 名前をセットし、行頭の「の分を含めてタブ幅を設定する */ | |
370 pos++; | |
371 for (it = pos; it != end; it++) { | |
372 if (it->type == TextElem::escape && it->impl.Escape.type == TextElem::name_end) break; | |
373 } | |
374 if (it == end) return; | |
375 int line_firstpos = data->size(); | |
376 pos = helper.Add(tab_width, pos, it); | |
377 pos++; | |
378 helper.SetGroup(data->begin() + line_firstpos, data->end()); | |
379 | |
380 // 行頭の「分を開ける | |
381 try { | |
382 tab_width += helper.CharWidth(0xa1d6); /* 「 */ | |
383 } catch(...) {} | |
384 | |
385 return; | |
386 }; | |
387 | |
388 void TextHorizLayout::SetLineHead(void) { | |
389 | |
390 /* 行頭は 「などか? */ | |
391 | |
392 for (; pos != end; pos++) { | |
393 if (pos->type == TextElem::escape || pos->type == TextElem::glyph) break; | |
394 int x = 0; | |
395 helper.Add(x, pos, pos+1); | |
396 } | |
397 if (pos->type != TextElem::glyph || Kinsoku(pos->impl.Glyph.code) != KinsokuHead) return; | |
398 | |
399 /* 「なので、処理する */ | |
400 if (tab_width != 0) tab_width -= helper.CharWidth(pos->impl.Glyph.code); | |
401 int line_firstpos = data->size(); | |
402 pos = helper.Add(tab_width, pos, pos+1); | |
403 return; | |
404 } | |
405 | |
406 void TextHorizLayout::MakeLine(int line_start, int width, vector<int>& lineheights) { | |
407 | |
408 int x = tab_width; | |
409 /* まず、全文字描画する */ | |
410 while(pos != end) { | |
411 pos = helper.Add(x, pos, end); | |
412 if (pos->type == TextElem::escape && pos->impl.Escape.type == TextElem::ruby_start) { | |
413 pos = helper.AddRuby(x, pos, end); | |
414 } | |
415 if (pos != end && pos->type == TextElem::escape) { | |
416 if (pos->impl.Escape.type == TextElem::ret) break; | |
417 if (pos->impl.Escape.type != TextElem::ruby_start) pos++; | |
418 } | |
419 } | |
420 /* 行に分割していく */ | |
421 TextGlyphStream::iterator it_start = data->begin() + line_start; | |
422 TextGlyphStream::iterator it_end = data->end(); | |
423 TextGlyphStream::iterator it = it_start; | |
424 | |
425 TextGlyphStream::iterator group_head = it_start; | |
426 int xstart = tab_width; | |
427 int xend = width; | |
428 while(it != it_end) { | |
429 // この行の終わりを決める | |
430 bool is_ruby = false; | |
431 TextGlyphStream::iterator it_line_start = it; | |
432 for (; it != it_end; it++) { | |
433 if (it->x + it->glyph->advance.x > xend) break; | |
434 if (it->flag & TextGlyph::Group) is_ruby = true; | |
435 if (!(it->flag & TextGlyph::Group)) group_head = it; | |
436 } | |
437 // 水平移動の大きさを決める。デフォルトでタブ位置まで戻す | |
438 int xadd_start = -xstart + tab_width; | |
439 int xadd_end = xadd_start; | |
440 // it == 次行の先頭なので、今行の末尾へ戻す | |
441 // ただし、 最低一文字の表示は保証 | |
442 if (it != it_line_start && it != it_line_start+1 && it != it_end) it--; | |
443 if (it != it_end) { | |
444 // グループ化されている文字で終了したら、前の文字に戻す | |
445 if (it->flag & TextGlyph::Group) it = group_head; | |
446 // 次が行頭禁則文字ならこの行に入れる | |
447 if ( (it+1) != it_end && (it+1)->flag & TextGlyph::Kinsoku) it++; | |
448 // 移動する大きさを決める | |
449 // 行端ぞろえ、行末文字なら半文字分だけ突き出る | |
450 int glyph_xend = it->x + it->glyph->advance.x; | |
451 if (it != it_line_start && (it-1)->flag & TextGlyph::Group) { // グループ化文字の場合、1文字前も見る | |
452 if (glyph_xend < (it-1)->x + (it-1)->glyph->advance.x) | |
453 glyph_xend = (it-1)->x + (it-1)->glyph->advance.x; | |
454 } | |
455 xadd_end += xend - glyph_xend; | |
456 if (it->flag & TextGlyph::Kinsoku) | |
457 xadd_end += it->glyph->advance.x / 2; | |
458 } | |
459 if (it != it_end) { | |
460 it->flag = TextGlyph::Flag(it->flag | TextGlyph::LineEnd); | |
461 it++; // it == 次行の先頭へ | |
462 } | |
463 int ascent, descent; | |
464 helper.CalcHeight(ascent, descent, it_start, it); | |
465 if (ascent+descent < helper.min_lineheight) { | |
466 int dif = helper.min_lineheight-(ascent+descent); | |
467 ascent += dif/2; | |
468 descent += dif-(dif/2); | |
469 } | |
470 if (is_ruby) ascent+=ruby_lineskip; | |
471 helper.AdjustPosition(xadd_start, xadd_end, cur_y+ascent+1, it_start, it); | |
472 cur_y += ascent + descent + line_skip; | |
473 lineheights.push_back(ascent+descent+line_skip); | |
474 | |
475 /* 次の行へ */ | |
476 if (it != it_end) { | |
477 it_start = it; | |
478 group_head = it_start; | |
479 /* 1文字目がグループ化されていれば、グループの先頭文字にする */ | |
480 xstart = it->x; | |
481 if (it->flag & TextGlyph::Group) { | |
482 TextGlyphStream::iterator jit; | |
483 for (jit = it; jit != it_end; jit++) { | |
484 if (xstart > jit->x) xstart = jit->x; | |
485 if (!(jit->flag & TextGlyph::Group)) break; | |
486 } | |
487 } | |
488 xend = it->x + width-tab_width; | |
489 } | |
490 } | |
491 return; | |
492 } | |
493 | |
494 namespace XKFont { | |
495 | |
52 | 496 HorizLayout::HorizLayout(const char* fontname, int size) { |
497 font = new Font(fontname, size); | |
498 pimpl = new ::TextHorizLayout(font); | |
499 } | |
0 | 500 |
52 | 501 HorizLayout::~HorizLayout() { |
502 delete pimpl; | |
503 delete font; | |
504 } | |
0 | 505 |
52 | 506 void HorizLayout::Layout(TextStream& stream, TextGlyphStream& glyph, vector<int>& lineheights, int width) { |
507 pimpl->Layout(stream, glyph, lineheights, width); | |
508 }; | |
509 | |
510 TextGlyphStream HorizLayout::Layout(const char* str, int width, int r, int gc, int b) { | |
511 TextStream s; | |
512 s.SetColor(r,gc,b); | |
513 s.Add(str); | |
53
ddbcbd000206
* MuSys, AyuSysConfig, FileSearcher (former FILESEARCHER) and KeyHolder (former KEYHOLDER) are now singletons
thib
parents:
52
diff
changeset
|
514 return Layout(s, width); |
ddbcbd000206
* MuSys, AyuSysConfig, FileSearcher (former FILESEARCHER) and KeyHolder (former KEYHOLDER) are now singletons
thib
parents:
52
diff
changeset
|
515 } |
ddbcbd000206
* MuSys, AyuSysConfig, FileSearcher (former FILESEARCHER) and KeyHolder (former KEYHOLDER) are now singletons
thib
parents:
52
diff
changeset
|
516 |
ddbcbd000206
* MuSys, AyuSysConfig, FileSearcher (former FILESEARCHER) and KeyHolder (former KEYHOLDER) are now singletons
thib
parents:
52
diff
changeset
|
517 TextGlyphStream HorizLayout::Layout(TextStream s, int width) { |
52 | 518 TextGlyphStream g; |
519 vector<int> h; | |
520 Layout(s, g, h, width); | |
521 return g; | |
522 } | |
0 | 523 |
524 }; | |
525 | |
526 int TextGlyphStream::width(void) { | |
527 if (empty()) return 0; | |
528 iterator it; | |
529 int xmax = 0; | |
530 for (it=begin(); it!=end(); it++) { | |
531 int x = it->x + it->glyph->advance.x; | |
532 if (x > xmax) xmax = x; | |
533 } | |
534 return xmax + 1; | |
535 } | |
536 | |
537 int TextGlyphStream::height(void) { | |
538 if (empty()) return 0; | |
539 iterator it; | |
540 int ymax = 0; | |
541 it = end(); | |
542 while(1) { | |
543 it--; | |
544 int y = it->y + it->glyph->bitmap.rows - it->glyph->bitmap_top; | |
545 if (ymax < y) ymax = y; | |
546 if (it == begin()) break; | |
547 if (it->flag & TextGlyph::LineEnd) { | |
548 if (!(it->flag & TextGlyph::PhraseEnd)) break; // PhraseEnd は最後の文字 | |
549 } | |
550 } | |
551 return ymax + 1; | |
552 } | |
553 |