Mercurial > otakunoraifu
comparison font/font_layout.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | 35ce1a30f3f9 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:223b71206888 |
---|---|
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 | |
31 #include<vector> | |
32 #include<map> | |
33 #include<iostream> | |
34 | |
35 using namespace std; | |
36 | |
37 #include"font.h" | |
38 #include"text.h" | |
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 { | |
81 typedef TextStream::Iterator Iterator; | |
82 typedef TextGlyphStream::iterator iterator; | |
83 | |
84 TextGlyphStream* data; | |
85 | |
86 // information for rendering | |
87 unsigned char r, g, b; | |
88 XKFont::Face* face; | |
89 XKFont::Face* ruby_face; | |
90 XKFont::Font* font; | |
91 | |
92 public: | |
93 int min_lineheight; | |
94 TextGlyphStreamHelper(XKFont::Font* font); | |
95 // helper functions | |
96 void Init(TextGlyphStream* data); | |
97 Iterator Add(int& x, Iterator begin, Iterator end, int max_x = 0); | |
98 Iterator AddRuby(int& x, Iterator begin, Iterator end); | |
99 int CharWidth(int code); | |
100 void SetGroup(iterator begin, iterator end); | |
101 void CalcHeight(int& ascent, int& descent, iterator begin, iterator end); | |
102 void AdjustPosition(int xstart_add, int xend_add, int y_add, iterator begin, iterator end); | |
103 }; | |
104 | |
105 TextGlyphStreamHelper::TextGlyphStreamHelper(XKFont::Font* __font) { | |
106 font = __font; | |
107 face = font->FaceLoad(1.0); | |
108 ruby_face = 0; | |
109 r = 255; g = 255; b = 255; | |
110 min_lineheight = font->vsize; | |
111 } | |
112 | |
113 void TextGlyphStreamHelper::Init(TextGlyphStream* __data) { | |
114 r = 255; g = 255; b = 255; | |
115 face = font->FaceLoad(1.0); | |
116 data = __data; | |
117 data->clear(); | |
118 data->font = font; | |
119 } | |
120 | |
121 TextGlyphStreamHelper::Iterator | |
122 TextGlyphStreamHelper::Add(int& x, TextGlyphStreamHelper::Iterator begin, TextGlyphStreamHelper::Iterator end, int max_x) { | |
123 /* text を glyph に変換する */ | |
124 TextGlyph gl; | |
125 Iterator it; | |
126 gl.x = x; gl.y = 0; gl.r = r; gl.g = g; gl.b = b; gl.flag = TextGlyph::Flag(0); gl.is_rev = false; | |
127 for (it = begin; it != end; it++) { | |
128 if (it->type != TextElem::glyph) { | |
129 if (it->type == TextElem::color) { | |
130 gl.r = r = it->impl.Color.r; | |
131 gl.g = g = it->impl.Color.g; | |
132 gl.b = b = it->impl.Color.b; | |
133 } else if (it->type == TextElem::size) { | |
134 face = font->FaceLoad(it->impl.Size.scale); | |
135 } else if (it->type == TextElem::escape) { | |
136 x = gl.x; | |
137 return it; | |
138 } | |
139 continue; | |
140 } | |
141 try { | |
142 gl.glyph = face->GlyphLoad(it->impl.Glyph.code); | |
143 if (max_x > 0 && gl.x + gl.glyph->advance.x > max_x) { | |
144 x = gl.x; | |
145 return it; | |
146 } | |
147 if ( Kinsoku(it->impl.Glyph.code) == KinsokuTail) | |
148 gl.flag = TextGlyph::Flag(gl.flag | TextGlyph::Kinsoku); | |
149 else | |
150 gl.flag = TextGlyph::Flag(0); | |
151 data->push_back(gl); | |
152 gl.x += gl.glyph->advance.x; | |
153 } catch(...) {} | |
154 } | |
155 x = gl.x; | |
156 return it; | |
157 } | |
158 | |
159 TextGlyphStreamHelper::Iterator TextGlyphStreamHelper::AddRuby(int& x, TextGlyphStreamHelper::Iterator sbegin, TextGlyphStreamHelper::Iterator send) { | |
160 Iterator it; | |
161 it = sbegin; | |
162 if (it == send) return it; | |
163 if (it->type != TextElem::escape || it->impl.Escape.type != TextElem::ruby_start) return sbegin; | |
164 it++; | |
165 /* まず、本文描画 */ | |
166 int str_firstpos = data->size(); | |
167 int str_width = 0; | |
168 it = Add(str_width, it, send); | |
169 if (it == send || it->type != TextElem::escape || it->impl.Escape.type != TextElem::ruby_startruby) { | |
170 // ありえないはずだが、取り合えずなにもしないで終了 | |
171 cerr << "TextGlyphStream::AddRuby : invalid operation; fallback to the upeer level"<<endl; | |
172 data->erase(data->begin()+str_firstpos, data->end()); | |
173 return sbegin+1; | |
174 } | |
175 it++; | |
176 int str_lastpos = data->size()-1; | |
177 TextGlyph& str_first = data->begin()[str_firstpos]; | |
178 TextGlyph& str_last = data->back(); | |
179 // 次に、フォントを取りかえてルビ描画 | |
180 int ruby_firstpos = data->size(); | |
181 XKFont::Face* save_font = face; | |
182 if (ruby_face == 0) ruby_face = font->FaceLoad(ruby_scale); | |
183 face = ruby_face; | |
184 int ruby_width = 0; | |
185 it = Add(ruby_width, it, send); | |
186 if (it->type != TextElem::escape || it->impl.Escape.type != TextElem::ruby_end) { | |
187 /* ありえないはずだが、取り合えずなにもしないで終了 */ | |
188 cerr << "TextGlyphStream::AddRuby : invalid operation; fallback to the upeer level"<<endl; | |
189 data->erase(data->begin()+str_firstpos, data->end()); | |
190 return sbegin+1; | |
191 } | |
192 it++; | |
193 face = save_font; | |
194 TextGlyph& ruby_first = (*data)[ruby_firstpos]; | |
195 TextGlyph& ruby_last = data->back(); | |
196 | |
197 /* ルビを移動すべき高さを求める */ | |
198 int dummy, str_ascent, ruby_descent; | |
199 CalcHeight(str_ascent, dummy, data->begin()+str_firstpos, data->begin()+ruby_firstpos); | |
200 CalcHeight(dummy, ruby_descent, data->begin()+ruby_firstpos, data->end()); | |
201 int ruby_height = str_ascent + ruby_descent + ruby_textskip; | |
202 | |
203 /* センタリングした場合の、ルビの左側、右側のマージン */ | |
204 int leftmergin, rightmergin; | |
205 leftmergin = str_first.glyph->advance.x/2 - (ruby_first.glyph->advance.x+1)/2; | |
206 rightmergin = str_last.glyph->advance.x/2 - (ruby_last.glyph->advance.x+1)/2; | |
207 | |
208 /* ルビ、本文の横方向の移動 */ | |
209 int ruby_xstart_add = 0, ruby_xend_add = 0, str_xstart_add=0, str_xend_add = 0; | |
210 if (ruby_width+leftmergin+rightmergin <= str_width) { // ルビの方が小さい | |
211 ruby_xstart_add = leftmergin; | |
212 ruby_xend_add = str_width-rightmergin-ruby_width; | |
213 } else if (ruby_width <= str_width) { // マージンを減らす必要あり | |
214 leftmergin = (str_width-ruby_width)/2; | |
215 ruby_xstart_add = leftmergin; | |
216 ruby_xend_add = str_width-leftmergin-ruby_width; | |
217 } else { // ルビの方が大きい | |
218 int str_count = ruby_firstpos - str_firstpos; | |
219 str_xstart_add = ruby_width/str_count/2 - str_first.glyph->advance.x/2; | |
220 str_xend_add = (ruby_width-str_width) - (ruby_width/str_count/2-str_last.glyph->advance.x/2); | |
221 str_width = ruby_width; | |
222 } | |
223 AdjustPosition(str_xstart_add+x, str_xend_add+x, 0, data->begin()+str_firstpos, data->begin()+ruby_firstpos); | |
224 AdjustPosition(ruby_xstart_add+x, ruby_xend_add+x, -ruby_height, data->begin()+ruby_firstpos, data->end()); | |
225 | |
226 /* 本文が一文字ずつ表示されるように glyph の順番を入れかえ、グループ化 */ | |
227 vector<TextGlyph> save; | |
228 save.assign(data->begin()+str_firstpos, data->end()); | |
229 iterator it_str = save.begin(); | |
230 iterator it_ruby = save.begin()+(ruby_firstpos-str_firstpos); | |
231 iterator dit = data->begin()+str_firstpos; | |
232 int str_count = it_ruby-it_str; | |
233 int ruby_count = save.end()-it_ruby; | |
234 int i,j = 0; | |
235 for (i=0; i<str_count; i++) { | |
236 iterator charstart = dit; | |
237 int jend = (i+1)*ruby_count/str_count; | |
238 for (; j<jend; j++) { | |
239 *dit++ = *it_ruby++; | |
240 } | |
241 *dit++ = *it_str++; | |
242 SetGroup(charstart, dit); | |
243 } | |
244 x += str_width; | |
245 return it; | |
246 } | |
247 | |
248 | |
249 void TextGlyphStreamHelper::SetGroup(TextGlyphStreamHelper::iterator begin, TextGlyphStreamHelper::iterator end) { | |
250 iterator it; | |
251 for (it = begin; it+1 != end; it++) | |
252 it->flag = TextGlyph::Flag(it->flag |TextGlyph::Group); | |
253 it->flag = TextGlyph::Flag(it->flag & ~TextGlyph::Group); | |
254 return; | |
255 } | |
256 | |
257 void TextGlyphStreamHelper::AdjustPosition(int xstart_add, int xend_add, int y_add, TextGlyphStreamHelper::iterator begin, TextGlyphStreamHelper::iterator end) { | |
258 iterator it; | |
259 /* 文字数を数える */ | |
260 int total_count = 0; | |
261 for (it = begin; it != end; it++) { | |
262 if (it->flag & TextGlyph::Group) continue; | |
263 total_count++; | |
264 } | |
265 /* 文字間のギャップを変更 */ | |
266 int incr = 0; | |
267 if (total_count != 1) incr = (xend_add - xstart_add) * 256 / (total_count-1); | |
268 int cur = xstart_add * 256; | |
269 for (it = begin; it != end; it++) { | |
270 it->x += cur / 256; | |
271 it->y += y_add; | |
272 if (it->flag & TextGlyph::Group) continue; | |
273 cur += incr; | |
274 } | |
275 return; | |
276 } | |
277 void TextGlyphStreamHelper::CalcHeight(int& ascent_r, int& descent_r, TextGlyphStreamHelper::iterator begin, TextGlyphStreamHelper::iterator end) { | |
278 iterator it; | |
279 /* 最大の descent, ascent を計算 */ | |
280 int ascent = 0; | |
281 int descent = 0; | |
282 for (it = begin; it != end; it++) { | |
283 | |
284 int y_top = it->y - it->glyph->bitmap_top; | |
285 int y_bottom = it->y + it->glyph->bitmap.rows - it->glyph->bitmap_top; | |
286 | |
287 if (descent < y_bottom) descent = y_bottom; | |
288 if (ascent < -y_top) ascent = -y_top; | |
289 } | |
290 ascent_r = ascent; | |
291 descent_r = descent; | |
292 return; | |
293 } | |
294 | |
295 int TextGlyphStreamHelper::CharWidth(int code) { | |
296 try { | |
297 XKFont::Glyph* g = face->GlyphLoad(code); | |
298 return g->advance.x; | |
299 } catch(...) { | |
300 return 0; | |
301 } | |
302 } | |
303 | |
304 class TextHorizLayout { | |
305 typedef TextStream::Iterator Iterator; | |
306 | |
307 Iterator pos; | |
308 Iterator end; | |
309 TextGlyphStream* data; | |
310 TextGlyphStreamHelper helper; | |
311 int tab_width; | |
312 int cur_y; | |
313 | |
314 void SetName(void); | |
315 void SetLineHead(void); | |
316 void MakeLine(int line_first, int width, vector<int>& lineheights); | |
317 public: | |
318 TextHorizLayout(XKFont::Font* font); | |
319 void Layout(TextStream& stream, TextGlyphStream& glyph, vector<int>& lineheights, int width); | |
320 }; | |
321 | |
322 TextHorizLayout::TextHorizLayout(XKFont::Font* font) : | |
323 helper(font), tab_width(0), cur_y(0) { | |
324 } | |
325 | |
326 void TextHorizLayout::Layout(TextStream& stream, TextGlyphStream& glyph, vector<int>& lineheights, int width) { | |
327 pos = stream.container.begin(); | |
328 end = stream.container.end(); | |
329 data = &glyph; | |
330 | |
331 helper.Init(data); | |
332 tab_width = 0; | |
333 cur_y = 0; | |
334 int prev_y = 0; | |
335 int line_start = glyph.size(); | |
336 while(pos != end) { | |
337 /* | |
338 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;} | |
339 if (pos->type == TextElem::escape) { cout<<"escape "<<pos->impl.Escape.type<<endl;} | |
340 */ | |
341 SetName(); | |
342 SetLineHead(); | |
343 MakeLine(line_start, width, lineheights); | |
344 if (line_start != glyph.size()) { | |
345 data->back().flag = TextGlyph::Flag(data->back().flag | TextGlyph::PhraseEnd | TextGlyph::LineEnd); | |
346 } | |
347 prev_y = cur_y; | |
348 if (pos != end && pos->type == TextElem::escape && pos->impl.Escape.type == TextElem::ret) pos++; | |
349 line_start = glyph.size(); | |
350 } | |
351 return; | |
352 } | |
353 | |
354 void TextHorizLayout::SetName(void) { | |
355 Iterator it; | |
356 | |
357 tab_width = 0; | |
358 /* 行頭が名前なら、処理開始 */ | |
359 for (; pos != end; pos++) { | |
360 if (pos->type == TextElem::escape || pos->type == TextElem::glyph) break; | |
361 int x = 0; | |
362 helper.Add(x, pos, pos+1); | |
363 } | |
364 | |
365 if (pos->type != TextElem::escape || pos->impl.Escape.type != TextElem::name_start) return; | |
366 | |
367 /* 名前をセットし、行頭の「の分を含めてタブ幅を設定する */ | |
368 pos++; | |
369 for (it = pos; it != end; it++) { | |
370 if (it->type == TextElem::escape && it->impl.Escape.type == TextElem::name_end) break; | |
371 } | |
372 if (it == end) return; | |
373 int line_firstpos = data->size(); | |
374 pos = helper.Add(tab_width, pos, it); | |
375 pos++; | |
376 helper.SetGroup(data->begin() + line_firstpos, data->end()); | |
377 | |
378 // 行頭の「分を開ける | |
379 try { | |
380 tab_width += helper.CharWidth(0xa1d6); /* 「 */ | |
381 } catch(...) {} | |
382 | |
383 return; | |
384 }; | |
385 | |
386 void TextHorizLayout::SetLineHead(void) { | |
387 | |
388 /* 行頭は 「などか? */ | |
389 | |
390 for (; pos != end; pos++) { | |
391 if (pos->type == TextElem::escape || pos->type == TextElem::glyph) break; | |
392 int x = 0; | |
393 helper.Add(x, pos, pos+1); | |
394 } | |
395 if (pos->type != TextElem::glyph || Kinsoku(pos->impl.Glyph.code) != KinsokuHead) return; | |
396 | |
397 /* 「なので、処理する */ | |
398 if (tab_width != 0) tab_width -= helper.CharWidth(pos->impl.Glyph.code); | |
399 int line_firstpos = data->size(); | |
400 pos = helper.Add(tab_width, pos, pos+1); | |
401 return; | |
402 } | |
403 | |
404 void TextHorizLayout::MakeLine(int line_start, int width, vector<int>& lineheights) { | |
405 | |
406 int x = tab_width; | |
407 /* まず、全文字描画する */ | |
408 while(pos != end) { | |
409 pos = helper.Add(x, pos, end); | |
410 if (pos->type == TextElem::escape && pos->impl.Escape.type == TextElem::ruby_start) { | |
411 pos = helper.AddRuby(x, pos, end); | |
412 } | |
413 if (pos != end && pos->type == TextElem::escape) { | |
414 if (pos->impl.Escape.type == TextElem::ret) break; | |
415 if (pos->impl.Escape.type != TextElem::ruby_start) pos++; | |
416 } | |
417 } | |
418 /* 行に分割していく */ | |
419 TextGlyphStream::iterator it_start = data->begin() + line_start; | |
420 TextGlyphStream::iterator it_end = data->end(); | |
421 TextGlyphStream::iterator it = it_start; | |
422 | |
423 TextGlyphStream::iterator group_head = it_start; | |
424 int xstart = tab_width; | |
425 int xend = width; | |
426 while(it != it_end) { | |
427 // この行の終わりを決める | |
428 bool is_ruby = false; | |
429 TextGlyphStream::iterator it_line_start = it; | |
430 for (; it != it_end; it++) { | |
431 if (it->x + it->glyph->advance.x > xend) break; | |
432 if (it->flag & TextGlyph::Group) is_ruby = true; | |
433 if (!(it->flag & TextGlyph::Group)) group_head = it; | |
434 } | |
435 // 水平移動の大きさを決める。デフォルトでタブ位置まで戻す | |
436 int xadd_start = -xstart + tab_width; | |
437 int xadd_end = xadd_start; | |
438 // it == 次行の先頭なので、今行の末尾へ戻す | |
439 // ただし、 最低一文字の表示は保証 | |
440 if (it != it_line_start && it != it_line_start+1 && it != it_end) it--; | |
441 if (it != it_end) { | |
442 // グループ化されている文字で終了したら、前の文字に戻す | |
443 if (it->flag & TextGlyph::Group) it = group_head; | |
444 // 次が行頭禁則文字ならこの行に入れる | |
445 if ( (it+1) != it_end && (it+1)->flag & TextGlyph::Kinsoku) it++; | |
446 // 移動する大きさを決める | |
447 // 行端ぞろえ、行末文字なら半文字分だけ突き出る | |
448 int glyph_xend = it->x + it->glyph->advance.x; | |
449 if (it != it_line_start && (it-1)->flag & TextGlyph::Group) { // グループ化文字の場合、1文字前も見る | |
450 if (glyph_xend < (it-1)->x + (it-1)->glyph->advance.x) | |
451 glyph_xend = (it-1)->x + (it-1)->glyph->advance.x; | |
452 } | |
453 xadd_end += xend - glyph_xend; | |
454 if (it->flag & TextGlyph::Kinsoku) | |
455 xadd_end += it->glyph->advance.x / 2; | |
456 } | |
457 if (it != it_end) { | |
458 it->flag = TextGlyph::Flag(it->flag | TextGlyph::LineEnd); | |
459 it++; // it == 次行の先頭へ | |
460 } | |
461 int ascent, descent; | |
462 helper.CalcHeight(ascent, descent, it_start, it); | |
463 if (ascent+descent < helper.min_lineheight) { | |
464 int dif = helper.min_lineheight-(ascent+descent); | |
465 ascent += dif/2; | |
466 descent += dif-(dif/2); | |
467 } | |
468 if (is_ruby) ascent+=ruby_lineskip; | |
469 helper.AdjustPosition(xadd_start, xadd_end, cur_y+ascent+1, it_start, it); | |
470 cur_y += ascent + descent + line_skip; | |
471 lineheights.push_back(ascent+descent+line_skip); | |
472 | |
473 /* 次の行へ */ | |
474 if (it != it_end) { | |
475 it_start = it; | |
476 group_head = it_start; | |
477 /* 1文字目がグループ化されていれば、グループの先頭文字にする */ | |
478 xstart = it->x; | |
479 if (it->flag & TextGlyph::Group) { | |
480 TextGlyphStream::iterator jit; | |
481 for (jit = it; jit != it_end; jit++) { | |
482 if (xstart > jit->x) xstart = jit->x; | |
483 if (!(jit->flag & TextGlyph::Group)) break; | |
484 } | |
485 } | |
486 xend = it->x + width-tab_width; | |
487 } | |
488 } | |
489 return; | |
490 } | |
491 | |
492 namespace XKFont { | |
493 | |
494 HorizLayout::HorizLayout(const char* fontname, int size) { | |
495 font = new Font(fontname, size); | |
496 pimpl = new ::TextHorizLayout(font); | |
497 } | |
498 | |
499 HorizLayout::~HorizLayout() { | |
500 delete pimpl; | |
501 delete font; | |
502 } | |
503 | |
504 void HorizLayout::Layout(TextStream& stream, TextGlyphStream& glyph, vector<int>& lineheights, int width) { | |
505 pimpl->Layout(stream, glyph, lineheights, width); | |
506 }; | |
507 TextGlyphStream HorizLayout::Layout(const char* str, int width, int r, int gc, int b) { | |
508 TextStream s; | |
509 s.SetColor(r,gc,b); | |
510 s.Add(str); | |
511 TextGlyphStream g; | |
512 vector<int> h; | |
513 Layout(s, g, h, width); | |
514 return g; | |
515 } | |
516 | |
517 }; | |
518 | |
519 int TextGlyphStream::width(void) { | |
520 if (empty()) return 0; | |
521 iterator it; | |
522 int xmax = 0; | |
523 for (it=begin(); it!=end(); it++) { | |
524 int x = it->x + it->glyph->advance.x; | |
525 if (x > xmax) xmax = x; | |
526 } | |
527 return xmax + 1; | |
528 } | |
529 | |
530 int TextGlyphStream::height(void) { | |
531 if (empty()) return 0; | |
532 iterator it; | |
533 int ymax = 0; | |
534 it = end(); | |
535 while(1) { | |
536 it--; | |
537 int y = it->y + it->glyph->bitmap.rows - it->glyph->bitmap_top; | |
538 if (ymax < y) ymax = y; | |
539 if (it == begin()) break; | |
540 if (it->flag & TextGlyph::LineEnd) { | |
541 if (!(it->flag & TextGlyph::PhraseEnd)) break; // PhraseEnd は最後の文字 | |
542 } | |
543 } | |
544 return ymax + 1; | |
545 } | |
546 |