00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "textAssembler.h"
00016 #include "textGlyph.h"
00017 #include "cullFaceAttrib.h"
00018 #include "colorAttrib.h"
00019 #include "cullBinAttrib.h"
00020 #include "textureAttrib.h"
00021 #include "transparencyAttrib.h"
00022 #include "textPropertiesManager.h"
00023 #include "textEncoder.h"
00024 #include "config_text.h"
00025 #include "geomTriangles.h"
00026 #include "geomLines.h"
00027 #include "geomPoints.h"
00028 #include "geomVertexReader.h"
00029 #include "geomVertexWriter.h"
00030 #include "geomLines.h"
00031 #include "geomVertexFormat.h"
00032 #include "geomVertexData.h"
00033 #include "geom.h"
00034 #include "modelNode.h"
00035
00036 #include <ctype.h>
00037 #include <stdio.h>
00038
00039
00040 static const PN_stdfloat small_accent_scale = 0.6f;
00041
00042
00043 static const PN_stdfloat tiny_accent_scale = 0.4;
00044
00045
00046 static const PN_stdfloat squash_accent_scale_x = 0.8f;
00047 static const PN_stdfloat squash_accent_scale_y = 0.5f;
00048
00049
00050 static const PN_stdfloat small_squash_accent_scale_x = 0.6f;
00051 static const PN_stdfloat small_squash_accent_scale_y = 0.3;
00052
00053
00054
00055 static const PN_stdfloat ligature_advance_scale = 0.6f;
00056
00057
00058
00059
00060
00061
00062
00063 static INLINE bool
00064 isspacew(unsigned int ch) {
00065 return isascii(ch) && isspace(ch);
00066 }
00067
00068
00069
00070
00071
00072
00073
00074 static INLINE bool
00075 isbreakpoint(unsigned int ch) {
00076 return (ch == ' ' || ch == '\t' ||
00077 ch == (unsigned int)text_soft_hyphen_key ||
00078 ch == (unsigned int)text_soft_break_key);
00079 }
00080
00081
00082
00083
00084
00085
00086
00087 TextAssembler::
00088 TextAssembler(TextEncoder *encoder) :
00089 _encoder(encoder),
00090 _usage_hint(Geom::UH_static),
00091 _max_rows(0),
00092 _dynamic_merge(text_dynamic_merge),
00093 _multiline_mode(true)
00094 {
00095 _initial_cprops = new ComputedProperties(TextProperties());
00096 clear();
00097 }
00098
00099
00100
00101
00102
00103
00104 TextAssembler::
00105 TextAssembler(const TextAssembler ©) :
00106 _initial_cprops(copy._initial_cprops),
00107 _text_string(copy._text_string),
00108 _text_block(copy._text_block),
00109 _ul(copy._ul),
00110 _lr(copy._lr),
00111 _next_row_ypos(copy._next_row_ypos),
00112 _encoder(copy._encoder),
00113 _usage_hint(copy._usage_hint),
00114 _max_rows(copy._max_rows),
00115 _dynamic_merge(copy._dynamic_merge),
00116 _multiline_mode(copy._multiline_mode)
00117 {
00118 }
00119
00120
00121
00122
00123
00124
00125 void TextAssembler::
00126 operator = (const TextAssembler ©) {
00127 _initial_cprops = copy._initial_cprops;
00128 _text_string = copy._text_string;
00129 _text_block = copy._text_block;
00130 _ul = copy._ul;
00131 _lr = copy._lr;
00132 _next_row_ypos = copy._next_row_ypos;
00133 _encoder = copy._encoder;
00134 _usage_hint = copy._usage_hint;
00135 _max_rows = copy._max_rows;
00136 _dynamic_merge = copy._dynamic_merge;
00137 _multiline_mode = copy._multiline_mode;
00138 }
00139
00140
00141
00142
00143
00144
00145 TextAssembler::
00146 ~TextAssembler() {
00147 }
00148
00149
00150
00151
00152
00153
00154 void TextAssembler::
00155 clear() {
00156 _ul.set(0.0f, 0.0f);
00157 _lr.set(0.0f, 0.0f);
00158 _next_row_ypos = 0.0f;
00159
00160 _text_string.clear();
00161 _text_block.clear();
00162 }
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 bool TextAssembler::
00177 set_wtext(const wstring &wtext) {
00178 clear();
00179
00180
00181
00182 wstring::const_iterator si = wtext.begin();
00183 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
00184
00185 while (si != wtext.end()) {
00186
00187
00188
00189
00190 text_cat.warning()
00191 << "pop_properties encountered without preceding push_properties.\n";
00192 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
00193 }
00194
00195
00196 return wordwrap_text();
00197 }
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216 bool TextAssembler::
00217 set_wsubstr(const wstring &wtext, int start, int count) {
00218 nassertr(start >= 0 && start <= (int)_text_string.size(), false);
00219 nassertr(count >= 0 && start + count <= (int)_text_string.size(), false);
00220
00221
00222
00223 TextString substr;
00224 wstring::const_iterator si = wtext.begin();
00225 scan_wtext(substr, si, wtext.end(), _initial_cprops);
00226 while (si != wtext.end()) {
00227 text_cat.warning()
00228 << "pop_properties encountered without preceding push_properties.\n";
00229 scan_wtext(substr, si, wtext.end(), _initial_cprops);
00230 }
00231
00232 _text_string.erase(_text_string.begin() + start, _text_string.begin() + start + count);
00233 _text_string.insert(_text_string.begin() + start, substr.begin(), substr.end());
00234
00235 return wordwrap_text();
00236 }
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 wstring TextAssembler::
00252 get_plain_wtext() const {
00253 wstring wtext;
00254
00255 TextString::const_iterator si;
00256 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
00257 const TextCharacter &tch = (*si);
00258 if (tch._graphic == (TextGraphic *)NULL) {
00259 wtext += tch._character;
00260 } else {
00261 wtext.push_back(0);
00262 }
00263 }
00264
00265 return wtext;
00266 }
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283 wstring TextAssembler::
00284 get_wordwrapped_plain_wtext() const {
00285 wstring wtext;
00286
00287 TextBlock::const_iterator bi;
00288 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
00289 const TextRow &row = (*bi);
00290 if (bi != _text_block.begin()) {
00291 wtext += '\n';
00292 }
00293
00294 TextString::const_iterator si;
00295 for (si = row._string.begin(); si != row._string.end(); ++si) {
00296 const TextCharacter &tch = (*si);
00297 if (tch._graphic == (TextGraphic *)NULL) {
00298 wtext += tch._character;
00299 } else {
00300 wtext.push_back(0);
00301 }
00302 }
00303 }
00304
00305 return wtext;
00306 }
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 wstring TextAssembler::
00320 get_wtext() const {
00321 wstring wtext;
00322 PT(ComputedProperties) current_cprops = _initial_cprops;
00323
00324 TextString::const_iterator si;
00325 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
00326 const TextCharacter &tch = (*si);
00327 current_cprops->append_delta(wtext, tch._cprops);
00328 if (tch._graphic == (TextGraphic *)NULL) {
00329 wtext += tch._character;
00330 } else {
00331 wtext.push_back(text_embed_graphic_key);
00332 wtext += tch._graphic_wname;
00333 wtext.push_back(text_embed_graphic_key);
00334 }
00335 current_cprops = tch._cprops;
00336 }
00337 current_cprops->append_delta(wtext, _initial_cprops);
00338
00339 return wtext;
00340 }
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361 wstring TextAssembler::
00362 get_wordwrapped_wtext() const {
00363 wstring wtext;
00364
00365 PT(ComputedProperties) current_cprops = _initial_cprops;
00366
00367 TextBlock::const_iterator bi;
00368 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
00369 const TextRow &row = (*bi);
00370 if (bi != _text_block.begin()) {
00371 current_cprops->append_delta(wtext, _initial_cprops);
00372 current_cprops = _initial_cprops;
00373 wtext += '\n';
00374 }
00375
00376 TextString::const_iterator si;
00377 for (si = row._string.begin(); si != row._string.end(); ++si) {
00378 const TextCharacter &tch = (*si);
00379 current_cprops->append_delta(wtext, tch._cprops);
00380 if (tch._graphic == (TextGraphic *)NULL) {
00381 wtext += tch._character;
00382 } else {
00383 wtext.push_back(text_embed_graphic_key);
00384 wtext += tch._graphic_wname;
00385 wtext.push_back(text_embed_graphic_key);
00386 }
00387 current_cprops = tch._cprops;
00388 }
00389 }
00390 current_cprops->append_delta(wtext, _initial_cprops);
00391
00392 return wtext;
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408 bool TextAssembler::
00409 calc_r_c(int &r, int &c, int n) const {
00410 nassertr(n >= 0 && n <= (int)_text_string.size(), false);
00411
00412 if (n == (int)_text_string.size()) {
00413
00414 if (_text_string.empty()) {
00415 r = 0;
00416 c = 0;
00417 } else {
00418 r = _text_block.size() - 1;
00419 c = _text_block[r]._string.size();
00420 }
00421 return true;
00422
00423 } else if (n == 0) {
00424
00425 r = 0;
00426 c = 0;
00427 return true;
00428 }
00429
00430 r = 0;
00431 while (r + 1 < (int)_text_block.size() &&
00432 _text_block[r + 1]._row_start < n) {
00433 r += 1;
00434 }
00435
00436 const TextRow &row = _text_block[r];
00437 bool is_real_char = true;
00438
00439 nassertr(n > 0, false);
00440 if (row._got_soft_hyphens) {
00441
00442
00443 c = 0;
00444 int i = row._row_start;
00445 while (i < n - 1) {
00446 if (_text_string[i]._character != text_soft_hyphen_key &&
00447 _text_string[i]._character != text_soft_break_key) {
00448 ++c;
00449 }
00450 ++i;
00451 }
00452 if (_text_string[n - 1]._character != text_soft_hyphen_key &&
00453 _text_string[n - 1]._character != text_soft_break_key) {
00454 ++c;
00455 if (_text_string[n - 1]._character == '\n') {
00456 is_real_char = false;
00457 }
00458 } else {
00459 is_real_char = false;
00460 }
00461
00462 } else {
00463
00464
00465 c = min(n - row._row_start, (int)row._string.size());
00466 if (_text_string[n - 1]._character == '\n') {
00467 is_real_char = false;
00468 }
00469 }
00470
00471 return is_real_char;
00472 }
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485 int TextAssembler::
00486 calc_index(int r, int c) const {
00487 nassertr(r >= 0 && r <= (int)_text_block.size(), 0);
00488 if (r == (int)_text_block.size()) {
00489 nassertr(c == 0, 0);
00490 return _text_string.size();
00491
00492 } else {
00493 nassertr(c >= 0 && c <= (int)_text_block[r]._string.size(), 0);
00494 const TextRow &row = _text_block[r];
00495
00496 if (row._got_soft_hyphens) {
00497
00498
00499 int n = row._row_start;
00500 while (c > 0) {
00501 if (_text_string[n]._character != text_soft_hyphen_key &&
00502 _text_string[n]._character != text_soft_break_key) {
00503 --c;
00504 }
00505 ++n;
00506 }
00507 return n;
00508
00509 } else {
00510
00511
00512 return row._row_start + c;
00513 }
00514 }
00515 }
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528 PN_stdfloat TextAssembler::
00529 get_xpos(int r, int c) const {
00530 nassertr(r >= 0 && r <= (int)_text_block.size(), 0.0f);
00531 if (r == (int)_text_block.size()) {
00532 nassertr(c == 0, 0.0f);
00533 return 0.0f;
00534
00535 } else {
00536 nassertr(c >= 0 && c <= (int)_text_block[r]._string.size(), 0.0f);
00537 const TextRow &row = _text_block[r];
00538 PN_stdfloat xpos = row._xpos;
00539 for (int i = 0; i < c; ++i) {
00540 xpos += calc_width(row._string[i]);
00541 }
00542 return xpos;
00543 }
00544 }
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555 PT(PandaNode) TextAssembler::
00556 assemble_text() {
00557
00558 PlacedGlyphs placed_glyphs;
00559 assemble_paragraph(placed_glyphs);
00560
00561
00562
00563 PT(PandaNode) parent_node = new PandaNode("common");
00564
00565 PT(PandaNode) shadow_node = new PandaNode("shadow");
00566 PT(GeomNode) shadow_geom_node = new GeomNode("shadow_geom");
00567 shadow_node->add_child(shadow_geom_node);
00568
00569 PT(PandaNode) text_node = new PandaNode("text");
00570 PT(GeomNode) text_geom_node = new GeomNode("text_geom");
00571 text_node->add_child(text_geom_node);
00572
00573 const TextProperties *properties = NULL;
00574 CPT(RenderState) text_state;
00575 CPT(RenderState) shadow_state;
00576 LMatrix4 shadow_xform;
00577
00578 bool any_shadow = false;
00579
00580 GeomCollectorMap geom_collector_map;
00581 GeomCollectorMap geom_shadow_collector_map;
00582
00583 PlacedGlyphs::const_iterator pgi;
00584 for (pgi = placed_glyphs.begin(); pgi != placed_glyphs.end(); ++pgi) {
00585 const GlyphPlacement *placement = (*pgi);
00586
00587 if (placement->_properties != properties) {
00588
00589 properties = placement->_properties;
00590 text_state = RenderState::make_empty();
00591 shadow_state = RenderState::make_empty();
00592 shadow_xform = LMatrix4::ident_mat();
00593
00594 if (properties->has_text_color()) {
00595 text_state = text_state->add_attrib(ColorAttrib::make_flat(properties->get_text_color()));
00596 if (properties->get_text_color()[3] != 1.0) {
00597 text_state = text_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00598 }
00599 }
00600
00601 if (properties->has_bin()) {
00602 text_state = text_state->add_attrib(CullBinAttrib::make(properties->get_bin(), properties->get_draw_order() + 2));
00603 }
00604
00605 if (properties->has_shadow()) {
00606 shadow_state = shadow_state->add_attrib(ColorAttrib::make_flat(properties->get_shadow_color()));
00607 if (properties->get_shadow_color()[3] != 1.0) {
00608 shadow_state = shadow_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00609 }
00610
00611 if (properties->has_bin()) {
00612 shadow_state = shadow_state->add_attrib(CullBinAttrib::make(properties->get_bin(), properties->get_draw_order() + 1));
00613 }
00614
00615 LVector2 offset = properties->get_shadow();
00616 shadow_xform = LMatrix4::translate_mat(offset[0], 0.0f, -offset[1]);
00617 }
00618 }
00619
00620
00621
00622
00623 if (properties->has_shadow()) {
00624 if (_dynamic_merge) {
00625 placement->assign_append_to(geom_shadow_collector_map, shadow_state, shadow_xform);
00626 } else {
00627 placement->assign_copy_to(shadow_geom_node, shadow_state, shadow_xform);
00628 }
00629
00630
00631
00632
00633
00634 any_shadow = true;
00635 }
00636
00637 if (_dynamic_merge) {
00638 placement->assign_append_to(geom_collector_map, text_state, LMatrix4::ident_mat());
00639 } else {
00640 placement->assign_to(text_geom_node, text_state);
00641 }
00642 placement->copy_graphic_to(text_node, text_state, LMatrix4::ident_mat());
00643 delete placement;
00644 }
00645 placed_glyphs.clear();
00646
00647 if (any_shadow) {
00648
00649
00650 parent_node->add_child(shadow_node);
00651 }
00652
00653 GeomCollectorMap::iterator gc;
00654 for (gc = geom_collector_map.begin(); gc != geom_collector_map.end(); ++gc) {
00655 (*gc).second.append_geom(text_geom_node, (*gc).first._state);
00656 }
00657
00658 if (any_shadow) {
00659 for (gc = geom_shadow_collector_map.begin();
00660 gc != geom_shadow_collector_map.end();
00661 ++gc) {
00662 (*gc).second.append_geom(shadow_geom_node, (*gc).first._state);
00663 }
00664 }
00665
00666 parent_node->add_child(text_node);
00667
00668 return parent_node;
00669 }
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679 PN_stdfloat TextAssembler::
00680 calc_width(wchar_t character, const TextProperties &properties) {
00681 if (character == ' ') {
00682
00683 TextFont *font = properties.get_font();
00684 nassertr(font != (TextFont *)NULL, 0.0f);
00685 return font->get_space_advance() * properties.get_glyph_scale() * properties.get_text_scale();
00686 }
00687
00688 bool got_glyph;
00689 const TextGlyph *first_glyph = NULL;
00690 const TextGlyph *second_glyph = NULL;
00691 UnicodeLatinMap::AccentType accent_type;
00692 int additional_flags;
00693 PN_stdfloat glyph_scale;
00694 PN_stdfloat advance_scale;
00695 get_character_glyphs(character, &properties,
00696 got_glyph, first_glyph, second_glyph, accent_type,
00697 additional_flags, glyph_scale, advance_scale);
00698
00699 PN_stdfloat advance = 0.0f;
00700
00701 if (first_glyph != (TextGlyph *)NULL) {
00702 advance = first_glyph->get_advance() * advance_scale;
00703 }
00704 if (second_glyph != (TextGlyph *)NULL) {
00705 advance += second_glyph->get_advance();
00706 }
00707
00708 glyph_scale *= properties.get_glyph_scale() * properties.get_text_scale();
00709
00710 return advance * glyph_scale;
00711 }
00712
00713
00714
00715
00716
00717
00718 PN_stdfloat TextAssembler::
00719 calc_width(const TextGraphic *graphic, const TextProperties &properties) {
00720 LVecBase4 frame = graphic->get_frame();
00721 return (frame[1] - frame[0]) * properties.get_glyph_scale() * properties.get_text_scale();
00722 }
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742 bool TextAssembler::
00743 has_exact_character(wchar_t character, const TextProperties &properties) {
00744 if (character == ' ' || character == '\n') {
00745
00746
00747 return true;
00748 }
00749
00750 TextFont *font = properties.get_font();
00751 nassertr(font != (TextFont *)NULL, false);
00752
00753 const TextGlyph *glyph = NULL;
00754 return font->get_glyph(character, glyph);
00755 }
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770 bool TextAssembler::
00771 has_character(wchar_t character, const TextProperties &properties) {
00772 if (character == ' ' || character == '\n') {
00773
00774
00775 return true;
00776 }
00777
00778 bool got_glyph;
00779 const TextGlyph *first_glyph = NULL;
00780 const TextGlyph *second_glyph = NULL;
00781 UnicodeLatinMap::AccentType accent_type;
00782 int additional_flags;
00783 PN_stdfloat glyph_scale;
00784 PN_stdfloat advance_scale;
00785 get_character_glyphs(character, &properties,
00786 got_glyph, first_glyph, second_glyph, accent_type,
00787 additional_flags, glyph_scale, advance_scale);
00788 return got_glyph;
00789 }
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812 bool TextAssembler::
00813 is_whitespace(wchar_t character, const TextProperties &properties) {
00814 if (character == ' ' || character == '\n') {
00815
00816 return true;
00817 }
00818
00819
00820 TextFont *font = properties.get_font();
00821 nassertr(font != (TextFont *)NULL, false);
00822
00823 const TextGlyph *glyph = NULL;
00824 if (!font->get_glyph(character, glyph)) {
00825 return false;
00826 }
00827
00828 return glyph->is_whitespace();
00829 }
00830
00831 #ifndef CPPPARSER // interrogate has a bit of trouble with wstring.
00832
00833
00834
00835
00836
00837
00838
00839 void TextAssembler::
00840 scan_wtext(TextAssembler::TextString &output_string,
00841 wstring::const_iterator &si,
00842 const wstring::const_iterator &send,
00843 TextAssembler::ComputedProperties *current_cprops) {
00844 while (si != send) {
00845 if ((*si) == text_push_properties_key) {
00846
00847
00848
00849 wstring wname;
00850 ++si;
00851 while (si != send && (*si) != text_push_properties_key) {
00852 wname += (*si);
00853 ++si;
00854 }
00855
00856 if (si == send) {
00857
00858
00859 text_cat.warning()
00860 << "Unclosed push_properties in text.\n";
00861 return;
00862 }
00863
00864 ++si;
00865
00866
00867 PT(ComputedProperties) new_cprops =
00868 new ComputedProperties(current_cprops, wname, _encoder);
00869
00870
00871 scan_wtext(output_string, si, send, new_cprops);
00872
00873 if (text_cat.is_debug()) {
00874 if (si == send) {
00875
00876
00877
00878 text_cat.debug()
00879 << "push_properties not matched by pop_properties.\n";
00880 }
00881 }
00882
00883 } else if ((*si) == text_pop_properties_key) {
00884
00885
00886 ++si;
00887 return;
00888
00889 } else if ((*si) == text_embed_graphic_key) {
00890
00891
00892
00893
00894 wstring graphic_wname;
00895 ++si;
00896 while (si != send && (*si) != text_embed_graphic_key) {
00897 graphic_wname += (*si);
00898 ++si;
00899 }
00900
00901 if (si == send) {
00902
00903
00904 text_cat.warning()
00905 << "Unclosed embed_graphic in text.\n";
00906 return;
00907 }
00908
00909 ++si;
00910
00911
00912
00913 string graphic_name = _encoder->encode_wtext(graphic_wname);
00914
00915 TextPropertiesManager *manager =
00916 TextPropertiesManager::get_global_ptr();
00917
00918
00919 const TextGraphic *named_graphic = manager->get_graphic_ptr(graphic_name);
00920 if (named_graphic != (TextGraphic *)NULL) {
00921 output_string.push_back(TextCharacter(named_graphic, graphic_wname, current_cprops));
00922
00923 } else {
00924 text_cat.warning()
00925 << "Unknown TextGraphic: " << graphic_name << "\n";
00926 }
00927
00928 } else {
00929
00930 output_string.push_back(TextCharacter(*si, current_cprops));
00931 ++si;
00932 }
00933 }
00934 }
00935 #endif // CPPPARSER
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953 bool TextAssembler::
00954 wordwrap_text() {
00955 _text_block.clear();
00956
00957 if (_text_string.empty()) {
00958
00959 return true;
00960 }
00961
00962 size_t p = 0;
00963
00964 _text_block.push_back(TextRow(p));
00965
00966
00967 PN_stdfloat initial_width = 0.0f;
00968 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
00969 if (_text_string[p]._character == '\n') {
00970 initial_width = 0.0f;
00971 if (_max_rows > 0 && (int)_text_block.size() >= _max_rows) {
00972
00973 return false;
00974 }
00975 _text_block.back()._eol_cprops = _text_string[p]._cprops;
00976 _text_block.push_back(TextRow(p + 1));
00977 } else {
00978 initial_width += calc_width(_text_string[p]);
00979 _text_block.back()._string.push_back(_text_string[p]);
00980 }
00981 p++;
00982 }
00983 bool needs_newline = false;
00984
00985 while (p < _text_string.size()) {
00986 nassertr(!isspacew(_text_string[p]._character), false);
00987
00988
00989
00990
00991 size_t q = p;
00992 bool any_spaces = false;
00993 size_t last_space = 0;
00994 PN_stdfloat last_space_width = 0.0f;
00995
00996 bool any_hyphens = false;
00997 size_t last_hyphen = 0;
00998 bool output_hyphen = false;
00999
01000 bool overflow = false;
01001 PN_stdfloat wordwrap_width = -1.0f;
01002
01003 bool last_was_space = false;
01004 PN_stdfloat width = initial_width;
01005 while (q < _text_string.size() && _text_string[q]._character != '\n') {
01006 if (_text_string[q]._cprops->_properties.has_wordwrap()) {
01007 wordwrap_width = _text_string[q]._cprops->_properties.get_wordwrap();
01008 } else {
01009 wordwrap_width = -1.0f;
01010 }
01011
01012 if (isspacew(_text_string[q]._character) ||
01013 _text_string[q]._character == text_soft_break_key) {
01014 if (!last_was_space) {
01015 any_spaces = true;
01016
01017
01018
01019 any_hyphens = false;
01020 last_space = q;
01021 last_space_width = width;
01022 last_was_space = true;
01023 }
01024 } else {
01025 last_was_space = false;
01026 }
01027
01028
01029
01030 if (_text_string[q]._character == text_soft_hyphen_key) {
01031 if (wordwrap_width > 0.0f) {
01032
01033
01034
01035
01036 if (q != p && width + calc_hyphen_width(_text_string[q]) <= wordwrap_width) {
01037 any_hyphens = true;
01038 last_hyphen = q;
01039 }
01040 }
01041 } else {
01042
01043 width += calc_width(_text_string[q]);
01044 }
01045
01046 q++;
01047
01048 if (wordwrap_width > 0.0f && width > wordwrap_width) {
01049
01050 q--;
01051 overflow = true;
01052 break;
01053 }
01054 }
01055
01056 if (overflow) {
01057
01058
01059
01060 nassertr(wordwrap_width > 0.0f, false);
01061
01062 if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) {
01063
01064
01065 any_hyphens = false;
01066 }
01067
01068 if (any_hyphens) {
01069
01070 q = last_hyphen;
01071 output_hyphen = true;
01072
01073 } else if (any_spaces) {
01074
01075 q = last_space;
01076
01077 } else {
01078
01079
01080
01081 size_t i = 0;
01082 while ((int)i < text_max_never_break && q - i > p &&
01083 get_text_never_break_before().find(_text_string[q - i]._character) != wstring::npos) {
01084 i++;
01085 }
01086 if ((int)i < text_max_never_break) {
01087 q -= i;
01088 }
01089 }
01090 }
01091
01092
01093 size_t next_start = q;
01094 while (next_start < _text_string.size() &&
01095 isbreakpoint(_text_string[next_start]._character)) {
01096 next_start++;
01097 }
01098
01099
01100 while (q > p && isspacew(_text_string[q - 1]._character)) {
01101 q--;
01102 }
01103
01104 if (next_start == p) {
01105
01106
01107
01108
01109 if (initial_width == 0.0f) {
01110
01111
01112
01113 q++;
01114 next_start++;
01115 while (next_start < _text_string.size() &&
01116 isbreakpoint(_text_string[next_start]._character)) {
01117 next_start++;
01118 }
01119 }
01120 }
01121
01122 if (needs_newline) {
01123 if (_max_rows > 0 && (int)_text_block.size() >= _max_rows) {
01124
01125 return false;
01126 }
01127 _text_block.push_back(TextRow(p));
01128 }
01129 if (get_multiline_mode()){
01130 needs_newline = true;
01131 }
01132
01133 if (_text_string[next_start - 1]._cprops->_properties.get_preserve_trailing_whitespace()) {
01134 q = next_start;
01135 }
01136
01137 for (size_t pi = p; pi < q; pi++) {
01138 if (_text_string[pi]._character != text_soft_hyphen_key &&
01139 _text_string[pi]._character != text_soft_break_key) {
01140 _text_block.back()._string.push_back(_text_string[pi]);
01141 } else {
01142 _text_block.back()._got_soft_hyphens = true;
01143 }
01144 }
01145 if (output_hyphen) {
01146 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
01147 wstring::const_iterator wi;
01148 for (wi = text_soft_hyphen_output.begin();
01149 wi != text_soft_hyphen_output.end();
01150 ++wi) {
01151 _text_block.back()._string.push_back(TextCharacter(*wi, _text_string[last_hyphen]._cprops));
01152 }
01153 }
01154
01155
01156
01157 if (next_start < _text_string.size() && _text_string[next_start]._character == '\n') {
01158
01159 if (_max_rows > 0 && (int)_text_block.size() >= _max_rows) {
01160
01161 return false;
01162 }
01163 _text_block.back()._eol_cprops = _text_string[next_start]._cprops;
01164 next_start++;
01165 _text_block.push_back(TextRow(next_start));
01166 needs_newline = false;
01167 }
01168 p = next_start;
01169
01170
01171 initial_width = 0.0f;
01172 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
01173 if (_text_string[p]._character == '\n') {
01174 initial_width = 0.0f;
01175 if (_max_rows > 0 && (int)_text_block.size() >= _max_rows) {
01176
01177 return false;
01178 }
01179 _text_block.back()._eol_cprops = _text_string[p]._cprops;
01180 _text_block.push_back(TextRow(p + 1));
01181 } else {
01182 initial_width += calc_width(_text_string[p]);
01183 _text_block.back()._string.push_back(_text_string[p]);
01184 }
01185 p++;
01186 }
01187 }
01188
01189 return true;
01190 }
01191
01192
01193
01194
01195
01196
01197
01198
01199 PN_stdfloat TextAssembler::
01200 calc_hyphen_width(const TextCharacter &tch) {
01201 TextFont *font = tch._cprops->_properties.get_font();
01202 nassertr(font != (TextFont *)NULL, 0.0f);
01203
01204 PN_stdfloat hyphen_width = 0.0f;
01205 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
01206 wstring::const_iterator wi;
01207 for (wi = text_soft_hyphen_output.begin();
01208 wi != text_soft_hyphen_output.end();
01209 ++wi) {
01210 hyphen_width += calc_width(*wi, tch._cprops->_properties);
01211 }
01212
01213 return hyphen_width;
01214 }
01215
01216
01217
01218
01219
01220
01221
01222
01223 void TextAssembler::
01224 assemble_paragraph(TextAssembler::PlacedGlyphs &placed_glyphs) {
01225 _ul.set(0.0f, 0.0f);
01226 _lr.set(0.0f, 0.0f);
01227 int num_rows = 0;
01228
01229 PN_stdfloat ypos = 0.0f;
01230 _next_row_ypos = 0.0f;
01231 TextBlock::iterator bi;
01232 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
01233 TextRow &row = (*bi);
01234
01235
01236 PlacedGlyphs row_placed_glyphs;
01237 PN_stdfloat row_width, line_height, wordwrap;
01238 TextProperties::Alignment align;
01239 assemble_row(row, row_placed_glyphs,
01240 row_width, line_height, align, wordwrap);
01241
01242
01243 LMatrix4 mat = LMatrix4::ident_mat();
01244
01245 if (num_rows == 0) {
01246
01247 _ul[1] = 0.8f * line_height;
01248
01249 } else {
01250
01251
01252 ypos -= line_height;
01253 }
01254 _lr[1] = ypos - 0.2 * line_height;
01255
01256
01257
01258
01259 PN_stdfloat xpos;
01260 switch (align) {
01261 case TextProperties::A_left:
01262 xpos = 0.0f;
01263 _lr[0] = max(_lr[0], row_width);
01264 break;
01265
01266 case TextProperties::A_right:
01267 xpos = -row_width;
01268 _ul[0] = min(_ul[0], xpos);
01269 break;
01270
01271 case TextProperties::A_center:
01272 xpos = -0.5f * row_width;
01273 _ul[0] = min(_ul[0], xpos);
01274 _lr[0] = max(_lr[0], -xpos);
01275 break;
01276
01277 case TextProperties::A_boxed_left:
01278 xpos = 0.0f;
01279 _lr[0] = max(_lr[0], max(row_width, wordwrap));
01280 break;
01281
01282 case TextProperties::A_boxed_right:
01283 xpos = wordwrap - row_width;
01284 _ul[0] = min(_ul[0], xpos);
01285 break;
01286
01287 case TextProperties::A_boxed_center:
01288 xpos = -0.5f * row_width;
01289 if (wordwrap > row_width) xpos += (wordwrap * 0.5f);
01290 _ul[0] = min(_ul[0], max(xpos,(wordwrap * 0.5f)));
01291 _lr[0] = max(_lr[0], min(-xpos,-(wordwrap * 0.5f)));
01292 break;
01293 }
01294
01295 mat.set_row(3, LVector3(xpos, 0.0f, ypos));
01296 row._xpos = xpos;
01297 row._ypos = ypos;
01298
01299
01300 PlacedGlyphs::iterator pi;
01301 for (pi = row_placed_glyphs.begin(); pi != row_placed_glyphs.end(); ++pi) {
01302 (*pi)->_xform *= mat;
01303 placed_glyphs.push_back(*pi);
01304 }
01305
01306
01307 num_rows++;
01308 _next_row_ypos = ypos - line_height;
01309 }
01310
01311
01312
01313 }
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325 void TextAssembler::
01326 assemble_row(TextAssembler::TextRow &row,
01327 TextAssembler::PlacedGlyphs &row_placed_glyphs,
01328 PN_stdfloat &row_width, PN_stdfloat &line_height,
01329 TextProperties::Alignment &align, PN_stdfloat &wordwrap) {
01330 Thread *current_thread = Thread::get_current_thread();
01331
01332 line_height = 0.0f;
01333 PN_stdfloat xpos = 0.0f;
01334 align = TextProperties::A_left;
01335
01336 bool underscore = false;
01337 PN_stdfloat underscore_start = 0.0f;
01338 const TextProperties *underscore_properties = NULL;
01339
01340 TextString::const_iterator si;
01341 for (si = row._string.begin(); si != row._string.end(); ++si) {
01342 const TextCharacter &tch = (*si);
01343 wchar_t character = tch._character;
01344 const TextGraphic *graphic = tch._graphic;
01345 const TextProperties *properties = &(tch._cprops->_properties);
01346
01347 if (properties->get_underscore() != underscore ||
01348 (underscore && (properties->get_text_color() != underscore_properties->get_text_color() ||
01349 properties->get_underscore_height() != underscore_properties->get_underscore_height()))) {
01350
01351 if (underscore && underscore_start != xpos) {
01352 draw_underscore(row_placed_glyphs, underscore_start, xpos,
01353 underscore_properties);
01354 }
01355 underscore = properties->get_underscore();
01356 underscore_start = xpos;
01357 underscore_properties = properties;
01358 }
01359
01360 TextFont *font = properties->get_font();
01361 nassertv(font != (TextFont *)NULL);
01362
01363
01364 if ((align == TextProperties::A_left) &&
01365 (properties->get_align() != TextProperties::A_left)) {
01366 align = properties->get_align();
01367 }
01368
01369
01370
01371 if (graphic != (TextGraphic *)NULL) {
01372 LVecBase4 frame = graphic->get_frame();
01373 line_height = max(line_height, frame[3] - frame[2]);
01374 } else {
01375
01376
01377 }
01378
01379 if (character == ' ') {
01380
01381 xpos += properties->get_glyph_scale() * properties->get_text_scale() * font->get_space_advance();
01382
01383 } else if (character == '\t') {
01384
01385 PN_stdfloat tab_width = properties->get_tab_width();
01386 xpos = (floor(xpos / tab_width) + 1.0f) * tab_width;
01387
01388 } else if (character == text_soft_hyphen_key) {
01389
01390
01391 } else if (graphic != (TextGraphic *)NULL) {
01392
01393 GlyphPlacement *placement = new GlyphPlacement;
01394 row_placed_glyphs.push_back(placement);
01395
01396 PT(PandaNode) model = graphic->get_model().node();
01397 if (graphic->get_instance_flag()) {
01398
01399
01400 PT(ModelNode) model_node = new ModelNode("");
01401 model_node->set_preserve_transform(ModelNode::PT_no_touch);
01402 model_node->add_child(model);
01403 placement->_graphic_model = model_node.p();
01404 } else {
01405
01406
01407
01408 placement->_graphic_model = model->copy_subgraph();
01409 }
01410
01411 LVecBase4 frame = graphic->get_frame();
01412 PN_stdfloat glyph_scale = properties->get_glyph_scale() * properties->get_text_scale();
01413
01414 PN_stdfloat advance = (frame[1] - frame[0]);
01415
01416
01417
01418 LMatrix4 glyph_xform = LMatrix4::scale_mat(glyph_scale);
01419
01420 glyph_xform(3, 0) += (xpos - frame[0]);
01421 glyph_xform(3, 2) += (properties->get_glyph_shift() - frame[2]);
01422
01423 if (properties->has_slant()) {
01424 LMatrix4 shear(1.0f, 0.0f, 0.0f, 0.0f,
01425 0.0f, 1.0f, 0.0f, 0.0f,
01426 properties->get_slant(), 0.0f, 1.0f, 0.0f,
01427 0.0f, 0.0f, 0.0f, 1.0f);
01428 glyph_xform = shear * glyph_xform;
01429 }
01430
01431 placement->_xform = glyph_xform;
01432 placement->_properties = properties;
01433
01434 xpos += advance * glyph_scale;
01435
01436 } else {
01437
01438 bool got_glyph;
01439 const TextGlyph *first_glyph;
01440 const TextGlyph *second_glyph;
01441 UnicodeLatinMap::AccentType accent_type;
01442 int additional_flags;
01443 PN_stdfloat glyph_scale;
01444 PN_stdfloat advance_scale;
01445 get_character_glyphs(character, properties,
01446 got_glyph, first_glyph, second_glyph, accent_type,
01447 additional_flags, glyph_scale, advance_scale);
01448
01449 if (!got_glyph) {
01450 char buffer[512];
01451 sprintf(buffer, "U+%04x", character);
01452 text_cat.warning()
01453 << "No definition in " << font->get_name()
01454 << " for character " << buffer;
01455 if (character < 128 && isprint((unsigned int)character)) {
01456 text_cat.warning(false)
01457 << " ('" << (char)character << "')";
01458 }
01459 text_cat.warning(false)
01460 << "\n";
01461 }
01462
01463
01464
01465
01466
01467 GlyphPlacement *placement = new GlyphPlacement;
01468 row_placed_glyphs.push_back(placement);
01469
01470 PN_stdfloat advance = 0.0f;
01471
01472 if (first_glyph != (TextGlyph *)NULL) {
01473 PT(Geom) first_char_geom = first_glyph->get_geom(_usage_hint);
01474 if (first_char_geom != (Geom *)NULL) {
01475 placement->add_piece(first_char_geom, first_glyph->get_state());
01476 }
01477 advance = first_glyph->get_advance() * advance_scale;
01478 }
01479 if (second_glyph != (TextGlyph *)NULL) {
01480 PT(Geom) second_char_geom = second_glyph->get_geom(_usage_hint);
01481 if (second_char_geom != (Geom *)NULL) {
01482 second_char_geom->transform_vertices(LMatrix4::translate_mat(advance, 0.0f, 0.0f));
01483 placement->add_piece(second_char_geom, second_glyph->get_state());
01484 }
01485 advance += second_glyph->get_advance();
01486 }
01487
01488 glyph_scale *= properties->get_glyph_scale() * properties->get_text_scale();
01489
01490 if (properties->get_wordwrap() > 0.0f) {
01491 wordwrap = properties->get_wordwrap();
01492 }
01493
01494
01495 LMatrix4 glyph_xform = LMatrix4::scale_mat(glyph_scale);
01496
01497 if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
01498
01499
01500
01501 LPoint3 min_vert, max_vert;
01502 bool found_any = false;
01503 placement->calc_tight_bounds(min_vert, max_vert, found_any,
01504 current_thread);
01505
01506 if (found_any) {
01507 LPoint3 centroid = (min_vert + max_vert) / 2.0f;
01508 tack_on_accent(accent_type, min_vert, max_vert, centroid,
01509 properties, placement);
01510
01511 if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
01512
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523 LMatrix4 rotate =
01524 LMatrix4::translate_mat(-centroid) *
01525 LMatrix4::rotate_mat_normaxis(180.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
01526 LMatrix4::translate_mat(centroid);
01527 glyph_xform *= rotate;
01528 }
01529 }
01530 }
01531
01532 glyph_xform(3, 0) += xpos;
01533 glyph_xform(3, 2) += properties->get_glyph_shift();
01534
01535 if (properties->has_slant()) {
01536 LMatrix4 shear(1.0f, 0.0f, 0.0f, 0.0f,
01537 0.0f, 1.0f, 0.0f, 0.0f,
01538 properties->get_slant(), 0.0f, 1.0f, 0.0f,
01539 0.0f, 0.0f, 0.0f, 1.0f);
01540 glyph_xform = shear * glyph_xform;
01541 }
01542
01543 placement->_xform = glyph_xform;
01544 placement->_properties = properties;
01545
01546 xpos += advance * glyph_scale;
01547 line_height = max(line_height, font->get_line_height() * glyph_scale);
01548 }
01549 }
01550
01551 if (underscore && underscore_start != xpos) {
01552 draw_underscore(row_placed_glyphs, underscore_start, xpos,
01553 underscore_properties);
01554 }
01555
01556 row_width = xpos;
01557
01558 if (row._eol_cprops != (ComputedProperties *)NULL) {
01559
01560
01561
01562
01563 const TextProperties *properties = &(row._eol_cprops->_properties);
01564 TextFont *font = properties->get_font();
01565 nassertv(font != (TextFont *)NULL);
01566
01567 if (line_height == 0.0f) {
01568 PN_stdfloat glyph_scale = properties->get_glyph_scale() * properties->get_text_scale();
01569 line_height = max(line_height, font->get_line_height() * glyph_scale);
01570 }
01571 }
01572 }
01573
01574
01575
01576
01577
01578
01579
01580 void TextAssembler::
01581 draw_underscore(TextAssembler::PlacedGlyphs &row_placed_glyphs,
01582 PN_stdfloat underscore_start, PN_stdfloat underscore_end,
01583 const TextProperties *underscore_properties) {
01584 CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
01585 PT(GeomVertexData) vdata =
01586 new GeomVertexData("text", format, Geom::UH_static);
01587 vdata->reserve_num_rows(2);
01588 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
01589 GeomVertexWriter color(vdata, InternalName::get_color());
01590
01591 PN_stdfloat y = underscore_properties->get_underscore_height();
01592 vertex.add_data3(underscore_start, 0.0f, y);
01593 color.add_data4(underscore_properties->get_text_color());
01594 vertex.add_data3(underscore_end, 0.0f, y);
01595 color.add_data4(underscore_properties->get_text_color());
01596
01597 PT(GeomLines) lines = new GeomLines(Geom::UH_static);
01598 lines->add_vertices(0, 1);
01599 lines->close_primitive();
01600
01601 PT(Geom) geom = new Geom(vdata);
01602 geom->add_primitive(lines);
01603
01604 GlyphPlacement *placement = new GlyphPlacement;
01605 placement->add_piece(geom, RenderState::make_empty());
01606 placement->_xform = LMatrix4::ident_mat();
01607 placement->_properties = underscore_properties;
01608
01609 row_placed_glyphs.push_back(placement);
01610 }
01611
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631 void TextAssembler::
01632 get_character_glyphs(int character, const TextProperties *properties,
01633 bool &got_glyph, const TextGlyph *&glyph,
01634 const TextGlyph *&second_glyph,
01635 UnicodeLatinMap::AccentType &accent_type,
01636 int &additional_flags,
01637 PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale) {
01638 TextFont *font = properties->get_font();
01639 nassertv_always(font != (TextFont *)NULL);
01640
01641 got_glyph = false;
01642 glyph = NULL;
01643 second_glyph = NULL;
01644 accent_type = UnicodeLatinMap::AT_none;
01645 additional_flags = 0;
01646 glyph_scale = 1.0f;
01647 advance_scale = 1.0f;
01648
01649
01650
01651 const UnicodeLatinMap::Entry *map_entry =
01652 UnicodeLatinMap::look_up(character);
01653 if (map_entry != NULL) {
01654 if (properties->get_small_caps() &&
01655 map_entry->_toupper_character != character) {
01656 character = map_entry->_toupper_character;
01657 map_entry = UnicodeLatinMap::look_up(character);
01658 glyph_scale = properties->get_small_caps_scale();
01659 }
01660 }
01661
01662 got_glyph = font->get_glyph(character, glyph);
01663 if (!got_glyph && map_entry != NULL && map_entry->_ascii_equiv != 0) {
01664
01665
01666 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
01667
01668 if (!got_glyph && map_entry->_toupper_character != character) {
01669
01670
01671 character = map_entry->_toupper_character;
01672 map_entry = UnicodeLatinMap::look_up(character);
01673 if (map_entry != NULL) {
01674 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
01675 }
01676 }
01677
01678 if (got_glyph) {
01679 accent_type = map_entry->_accent_type;
01680 additional_flags = map_entry->_additional_flags;
01681
01682 bool got_second_glyph = false;
01683 if (map_entry->_ascii_additional != 0) {
01684
01685 got_second_glyph =
01686 font->get_glyph(map_entry->_ascii_additional, second_glyph);
01687 }
01688
01689 if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
01690 got_second_glyph) {
01691
01692
01693 additional_flags &= ~UnicodeLatinMap::AF_ligature;
01694 advance_scale = ligature_advance_scale;
01695 }
01696
01697 if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
01698 additional_flags &= ~UnicodeLatinMap::AF_smallcap;
01699 glyph_scale = properties->get_small_caps_scale();
01700 }
01701 }
01702 }
01703 }
01704
01705
01706
01707
01708
01709
01710
01711
01712
01713 void TextAssembler::
01714 tack_on_accent(UnicodeLatinMap::AccentType accent_type,
01715 const LPoint3 &min_vert, const LPoint3 &max_vert,
01716 const LPoint3 ¢roid,
01717 const TextProperties *properties,
01718 TextAssembler::GlyphPlacement *placement) const {
01719 switch (accent_type) {
01720 case UnicodeLatinMap::AT_grave:
01721
01722
01723
01724
01725 tack_on_accent('/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
01726 properties, placement);
01727 break;
01728
01729 case UnicodeLatinMap::AT_acute:
01730 tack_on_accent('/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
01731 properties, placement);
01732 break;
01733
01734 case UnicodeLatinMap::AT_breve:
01735 tack_on_accent(')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
01736 properties, placement);
01737 break;
01738
01739 case UnicodeLatinMap::AT_inverted_breve:
01740 tack_on_accent('(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
01741 properties, placement);
01742 break;
01743
01744 case UnicodeLatinMap::AT_circumflex:
01745 tack_on_accent('^', CP_above, CT_none, min_vert, max_vert, centroid,
01746 properties, placement) ||
01747 tack_on_accent('v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
01748 properties, placement);
01749 break;
01750
01751 case UnicodeLatinMap::AT_circumflex_below:
01752 tack_on_accent('^', CP_below, CT_none, min_vert, max_vert, centroid,
01753 properties, placement) ||
01754 tack_on_accent('v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
01755 properties, placement);
01756 break;
01757
01758 case UnicodeLatinMap::AT_caron:
01759 tack_on_accent('^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
01760 properties, placement) ||
01761 tack_on_accent('v', CP_above, CT_squash, min_vert, max_vert, centroid,
01762 properties, placement);
01763
01764 break;
01765
01766 case UnicodeLatinMap::AT_tilde:
01767 tack_on_accent('~', CP_above, CT_none, min_vert, max_vert, centroid,
01768 properties, placement) ||
01769 tack_on_accent('s', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
01770 properties, placement);
01771
01772 break;
01773
01774 case UnicodeLatinMap::AT_tilde_below:
01775 tack_on_accent('~', CP_below, CT_none, min_vert, max_vert, centroid,
01776 properties, placement) ||
01777 tack_on_accent('s', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
01778 properties, placement);
01779 break;
01780
01781 case UnicodeLatinMap::AT_diaeresis:
01782 tack_on_accent(':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
01783 properties, placement);
01784 break;
01785
01786 case UnicodeLatinMap::AT_diaeresis_below:
01787 tack_on_accent(':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
01788 properties, placement);
01789 break;
01790
01791 case UnicodeLatinMap::AT_dot_above:
01792 tack_on_accent('.', CP_above, CT_none, min_vert, max_vert, centroid,
01793 properties, placement);
01794 break;
01795
01796 case UnicodeLatinMap::AT_dot_below:
01797 tack_on_accent('.', CP_below, CT_none, min_vert, max_vert, centroid,
01798 properties, placement);
01799 break;
01800
01801 case UnicodeLatinMap::AT_macron:
01802 tack_on_accent('-', CP_above, CT_none, min_vert, max_vert, centroid,
01803 properties, placement);
01804 break;
01805
01806 case UnicodeLatinMap::AT_line_below:
01807 tack_on_accent('-', CP_below, CT_none, min_vert, max_vert, centroid,
01808 properties, placement);
01809 break;
01810
01811 case UnicodeLatinMap::AT_ring_above:
01812 tack_on_accent('o', CP_top, CT_tiny, min_vert, max_vert, centroid,
01813 properties, placement);
01814 break;
01815
01816 case UnicodeLatinMap::AT_ring_below:
01817 tack_on_accent('o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
01818 properties, placement);
01819 break;
01820
01821 case UnicodeLatinMap::AT_cedilla:
01822 tack_on_accent('c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
01823 properties, placement);
01824
01825
01826 break;
01827
01828 case UnicodeLatinMap::AT_comma_below:
01829 tack_on_accent(',', CP_below, CT_none, min_vert, max_vert, centroid,
01830 properties, placement);
01831 break;
01832
01833 case UnicodeLatinMap::AT_ogonek:
01834 tack_on_accent(',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
01835 properties, placement);
01836 break;
01837
01838 case UnicodeLatinMap::AT_stroke:
01839 tack_on_accent('/', CP_within, CT_none, min_vert, max_vert, centroid,
01840 properties, placement);
01841 break;
01842
01843 default:
01844
01845 break;
01846 }
01847 }
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857 bool TextAssembler::
01858 tack_on_accent(char accent_mark, TextAssembler::CheesyPosition position,
01859 TextAssembler::CheesyTransform transform,
01860 const LPoint3 &min_vert, const LPoint3 &max_vert,
01861 const LPoint3 ¢roid,
01862 const TextProperties *properties,
01863 TextAssembler::GlyphPlacement *placement) const {
01864 TextFont *font = properties->get_font();
01865 nassertr(font != (TextFont *)NULL, false);
01866
01867 Thread *current_thread = Thread::get_current_thread();
01868
01869 const TextGlyph *accent_glyph;
01870 if (font->get_glyph(accent_mark, accent_glyph) ||
01871 font->get_glyph(toupper(accent_mark), accent_glyph)) {
01872 PT(Geom) accent_geom = accent_glyph->get_geom(_usage_hint);
01873 if (accent_geom != (Geom *)NULL) {
01874 LPoint3 min_accent, max_accent;
01875 bool found_any = false;
01876 accent_geom->calc_tight_bounds(min_accent, max_accent, found_any,
01877 current_thread);
01878 if (found_any) {
01879 PN_stdfloat t, u;
01880 LMatrix4 accent_mat;
01881
01882
01883
01884 bool mirrored = false;
01885
01886 switch (transform) {
01887 case CT_none:
01888 accent_mat = LMatrix4::ident_mat();
01889 break;
01890
01891 case CT_mirror_x:
01892 accent_mat = LMatrix4::scale_mat(-1.0f, 1.0f, 1.0f);
01893 t = min_accent[0];
01894 min_accent[0] = -max_accent[0];
01895 max_accent[0] = -t;
01896 mirrored = true;
01897 break;
01898
01899 case CT_mirror_y:
01900 accent_mat = LMatrix4::scale_mat(1.0f, 1.0f, -1.0f);
01901 t = min_accent[2];
01902 min_accent[2] = -max_accent[2];
01903 max_accent[2] = -t;
01904 mirrored = true;
01905 break;
01906
01907 case CT_rotate_90:
01908 accent_mat.set_rotate_mat_normaxis(90.0f, LVecBase3(0.0f, -1.0f, 0.0f));
01909
01910 t = min_accent[0];
01911 u = max_accent[0];
01912 max_accent[0] = -min_accent[2];
01913 min_accent[0] = -max_accent[2];
01914 max_accent[2] = u;
01915 min_accent[2] = t;
01916 break;
01917
01918 case CT_rotate_180:
01919 accent_mat = LMatrix4::scale_mat(-1.0f, -1.0f, 1.0f);
01920
01921 t = min_accent[0];
01922 min_accent[0] = -max_accent[0];
01923 max_accent[0] = -t;
01924 t = min_accent[2];
01925 min_accent[2] = -max_accent[2];
01926 max_accent[2] = -t;
01927 break;
01928
01929 case CT_rotate_270:
01930 accent_mat.set_rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f));
01931
01932 t = min_accent[0];
01933 u = max_accent[0];
01934 min_accent[0] = min_accent[2];
01935 max_accent[0] = max_accent[2];
01936 min_accent[2] = -u;
01937 max_accent[2] = -t;
01938 break;
01939
01940 case CT_squash:
01941 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, 1.0f, squash_accent_scale_y);
01942 min_accent[0] *= squash_accent_scale_x;
01943 max_accent[0] *= squash_accent_scale_x;
01944 min_accent[2] *= squash_accent_scale_y;
01945 max_accent[2] *= squash_accent_scale_y;
01946 break;
01947
01948 case CT_squash_mirror_y:
01949 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, 1.0f, -squash_accent_scale_y);
01950 min_accent[0] *= squash_accent_scale_x;
01951 max_accent[0] *= squash_accent_scale_x;
01952 t = min_accent[2];
01953 min_accent[2] = -max_accent[2] * squash_accent_scale_y;
01954 max_accent[2] = -t * squash_accent_scale_y;
01955 mirrored = true;
01956 break;
01957
01958 case CT_squash_mirror_diag:
01959 accent_mat =
01960 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
01961 LMatrix4::scale_mat(-squash_accent_scale_x, 1.0f, squash_accent_scale_y);
01962
01963
01964 t = min_accent[0];
01965 u = max_accent[0];
01966 min_accent[0] = min_accent[2] * -squash_accent_scale_x;
01967 max_accent[0] = max_accent[2] * -squash_accent_scale_x;
01968 min_accent[2] = -u * squash_accent_scale_y;
01969 max_accent[2] = -t * squash_accent_scale_y;
01970 mirrored = true;
01971 break;
01972
01973 case CT_small_squash:
01974 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
01975 min_accent[0] *= small_squash_accent_scale_x;
01976 max_accent[0] *= small_squash_accent_scale_x;
01977 min_accent[2] *= small_squash_accent_scale_y;
01978 max_accent[2] *= small_squash_accent_scale_y;
01979 break;
01980
01981 case CT_small_squash_mirror_y:
01982 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, -small_squash_accent_scale_y);
01983 min_accent[0] *= small_squash_accent_scale_x;
01984 max_accent[0] *= small_squash_accent_scale_x;
01985 t = min_accent[2];
01986 min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
01987 max_accent[2] = -t * small_squash_accent_scale_y;
01988 mirrored = true;
01989 break;
01990
01991 case CT_small_squash_mirror_diag:
01992 accent_mat =
01993 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
01994 LMatrix4::scale_mat(-small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
01995
01996
01997 t = min_accent[0];
01998 u = max_accent[0];
01999 min_accent[0] = min_accent[2] * -small_squash_accent_scale_x;
02000 max_accent[0] = max_accent[2] * -small_squash_accent_scale_x;
02001 min_accent[2] = -u * small_squash_accent_scale_y;
02002 max_accent[2] = -t * small_squash_accent_scale_y;
02003 mirrored = true;
02004 break;
02005
02006 case CT_small:
02007 accent_mat = LMatrix4::scale_mat(small_accent_scale);
02008 min_accent *= small_accent_scale;
02009 max_accent *= small_accent_scale;
02010 break;
02011
02012 case CT_small_rotate_270:
02013 accent_mat =
02014 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
02015 LMatrix4::scale_mat(small_accent_scale);
02016
02017
02018 t = min_accent[0];
02019 u = max_accent[0];
02020 min_accent[0] = min_accent[2] * small_accent_scale;
02021 max_accent[0] = max_accent[2] * small_accent_scale;
02022 min_accent[2] = -u * small_accent_scale;
02023 max_accent[2] = -t * small_accent_scale;
02024 break;
02025
02026 case CT_tiny:
02027 accent_mat = LMatrix4::scale_mat(tiny_accent_scale);
02028 min_accent *= tiny_accent_scale;
02029 max_accent *= tiny_accent_scale;
02030 break;
02031
02032 case CT_tiny_mirror_x:
02033 accent_mat = LMatrix4::scale_mat(-tiny_accent_scale, 1.0f, tiny_accent_scale);
02034
02035 t = min_accent[0];
02036 min_accent[0] = -max_accent[0] * tiny_accent_scale;
02037 max_accent[0] = -t * tiny_accent_scale;
02038 min_accent[2] *= tiny_accent_scale;
02039 max_accent[2] *= tiny_accent_scale;
02040 mirrored = true;
02041 break;
02042
02043 case CT_tiny_rotate_270:
02044 accent_mat =
02045 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
02046 LMatrix4::scale_mat(tiny_accent_scale);
02047
02048
02049 t = min_accent[0];
02050 u = max_accent[0];
02051 min_accent[0] = min_accent[2] * tiny_accent_scale;
02052 max_accent[0] = max_accent[2] * tiny_accent_scale;
02053 min_accent[2] = -u * tiny_accent_scale;
02054 max_accent[2] = -t * tiny_accent_scale;
02055 break;
02056 }
02057
02058 LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
02059 PN_stdfloat accent_height = max_accent[2] - min_accent[2];
02060 LVector3 trans;
02061 switch (position) {
02062 case CP_above:
02063
02064 trans.set(centroid[0] - accent_centroid[0], 0.0f,
02065 max_vert[2] - accent_centroid[2] + accent_height * 0.5);
02066 break;
02067
02068 case CP_below:
02069
02070 trans.set(centroid[0] - accent_centroid[0], 0.0f,
02071 min_vert[2] - accent_centroid[2] - accent_height * 0.5);
02072 break;
02073
02074 case CP_top:
02075
02076 trans.set(centroid[0] - accent_centroid[0], 0.0f,
02077 max_vert[2] - accent_centroid[2]);
02078 break;
02079
02080 case CP_bottom:
02081
02082 trans.set(centroid[0] - accent_centroid[0], 0.0f,
02083 min_vert[2] - accent_centroid[2]);
02084 break;
02085
02086 case CP_within:
02087
02088 trans.set(centroid[0] - accent_centroid[0], 0.0f,
02089 centroid[2] - accent_centroid[2]);
02090 break;
02091 }
02092
02093 accent_mat.set_row(3, trans);
02094 accent_geom->transform_vertices(accent_mat);
02095
02096 if (mirrored) {
02097
02098
02099 static CPT(RenderState) disable_backface;
02100 if (disable_backface == (const RenderState *)NULL) {
02101 disable_backface = RenderState::make
02102 (CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
02103 }
02104
02105 CPT(RenderState) state =
02106 accent_glyph->get_state()->compose(disable_backface);
02107 placement->add_piece(accent_geom, state);
02108 } else {
02109 placement->add_piece(accent_geom, accent_glyph->get_state());
02110 }
02111
02112 return true;
02113 }
02114 }
02115 }
02116 return false;
02117 }
02118
02119
02120
02121
02122
02123
02124
02125
02126 void TextAssembler::ComputedProperties::
02127 append_delta(wstring &wtext, TextAssembler::ComputedProperties *other) {
02128 if (this != other) {
02129 if (_depth > other->_depth) {
02130
02131 nassertv(_based_on != NULL);
02132
02133 wtext.push_back(text_pop_properties_key);
02134 _based_on->append_delta(wtext, other);
02135
02136 } else if (other->_depth > _depth) {
02137
02138 nassertv(other->_based_on != NULL);
02139
02140 append_delta(wtext, other->_based_on);
02141 wtext.push_back(text_push_properties_key);
02142 wtext += other->_wname;
02143 wtext.push_back(text_push_properties_key);
02144
02145 } else if (_depth != 0) {
02146
02147 nassertv(_based_on != NULL && other->_based_on != NULL);
02148
02149 wtext.push_back(text_pop_properties_key);
02150 _based_on->append_delta(wtext, other->_based_on);
02151 wtext.push_back(text_push_properties_key);
02152 wtext += other->_wname;
02153 wtext.push_back(text_push_properties_key);
02154 }
02155 }
02156 }
02157
02158
02159
02160
02161
02162
02163
02164
02165
02166
02167 void TextAssembler::GlyphPlacement::
02168 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
02169 bool &found_any, Thread *current_thread) const {
02170 Pieces::const_iterator pi;
02171 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
02172 (*pi)._geom->calc_tight_bounds(min_point, max_point, found_any,
02173 current_thread);
02174 }
02175 }
02176
02177
02178
02179
02180
02181
02182
02183
02184 void TextAssembler::GlyphPlacement::
02185 assign_to(GeomNode *geom_node, const RenderState *state) const {
02186 Pieces::const_iterator pi;
02187 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
02188 (*pi)._geom->transform_vertices(_xform);
02189 geom_node->add_geom((*pi)._geom, state->compose((*pi)._state));
02190 }
02191 }
02192
02193
02194
02195
02196
02197
02198
02199
02200
02201 void TextAssembler::GlyphPlacement::
02202 assign_copy_to(GeomNode *geom_node, const RenderState *state,
02203 const LMatrix4 &extra_xform) const {
02204 LMatrix4 new_xform = _xform * extra_xform;
02205 Pieces::const_iterator pi;
02206 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
02207 const Geom *geom = (*pi)._geom;
02208 PT(Geom) new_geom = geom->make_copy();
02209 new_geom->transform_vertices(new_xform);
02210 geom_node->add_geom(new_geom, state->compose((*pi)._state));
02211 }
02212 }
02213
02214
02215
02216
02217
02218
02219
02220
02221
02222 void TextAssembler::GlyphPlacement::
02223 assign_append_to(GeomCollectorMap &geom_collector_map,
02224 const RenderState *state,
02225 const LMatrix4 &extra_xform) const {
02226 LMatrix4 new_xform = _xform * extra_xform;
02227 Pieces::const_iterator pi;
02228
02229 int p, sp, s, e, i;
02230 for (pi = _pieces.begin(); pi != _pieces.end(); ++pi) {
02231 const Geom *geom = (*pi)._geom;
02232 const GeomVertexData *vdata = geom->get_vertex_data();
02233 CPT(RenderState) rs = (*pi)._state->compose(state);
02234 GeomCollectorKey key(rs, vdata->get_format());
02235
02236 GeomCollectorMap::iterator mi = geom_collector_map.find(key);
02237 if (mi == geom_collector_map.end()) {
02238 mi = geom_collector_map.insert(GeomCollectorMap::value_type(key, GeomCollector(vdata->get_format()))).first;
02239 }
02240 GeomCollector &geom_collector = (*mi).second;
02241 geom_collector.count_geom(geom);
02242
02243
02244
02245
02246 VertexIndexMap vimap;
02247
02248 for (p = 0; p < geom->get_num_primitives(); p++) {
02249 CPT(GeomPrimitive) primitive = geom->get_primitive(p)->decompose();
02250
02251
02252 GeomPrimitive *new_prim = geom_collector.get_primitive(primitive->get_type());
02253
02254
02255
02256 for (sp = 0; sp < primitive->get_num_primitives(); sp++) {
02257 s = primitive->get_primitive_start(sp);
02258 e = primitive->get_primitive_end(sp);
02259
02260
02261 for (i = s; i < e; i++) {
02262 int vi = primitive->get_vertex(i);
02263
02264
02265 pair<VertexIndexMap::iterator, bool> added = vimap.insert(VertexIndexMap::value_type(vi, 0));
02266 int new_vertex;
02267 if (added.second) {
02268
02269
02270 new_vertex = geom_collector.append_vertex(vdata, vi, new_xform);
02271
02272 (*(added.first)).second = new_vertex;
02273
02274 } else {
02275
02276
02277
02278
02279 new_vertex = (*(added.first)).second;
02280 }
02281 new_prim->add_vertex(new_vertex);
02282 }
02283 new_prim->close_primitive();
02284 }
02285 }
02286 }
02287 }
02288
02289
02290
02291
02292
02293
02294
02295 void TextAssembler::GlyphPlacement::
02296 copy_graphic_to(PandaNode *node, const RenderState *state,
02297 const LMatrix4 &extra_xform) const {
02298 if (_graphic_model != (PandaNode *)NULL) {
02299 LMatrix4 new_xform = _xform * extra_xform;
02300
02301
02302 PT(PandaNode) intermediate_node = new PandaNode("");
02303 node->add_child(intermediate_node);
02304
02305 intermediate_node->set_transform(TransformState::make_mat(new_xform));
02306 intermediate_node->set_state(state);
02307 intermediate_node->add_child(_graphic_model);
02308 }
02309 }
02310
02311
02312
02313
02314
02315
02316
02317 TextAssembler::GeomCollector::
02318 GeomCollector(const GeomVertexFormat *format) :
02319 _vdata(new GeomVertexData("merged_geom", format, Geom::UH_static)),
02320 _geom(new GeomTextGlyph(_vdata))
02321 {
02322 }
02323
02324
02325
02326
02327
02328
02329 TextAssembler::GeomCollector::
02330 GeomCollector(const TextAssembler::GeomCollector ©) :
02331 _vdata(copy._vdata),
02332 _geom(copy._geom)
02333 {
02334 }
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344 GeomPrimitive *TextAssembler::GeomCollector::
02345 get_primitive(TypeHandle prim_type) {
02346 if (prim_type == GeomTriangles::get_class_type()) {
02347 if (_triangles == (GeomPrimitive *)NULL) {
02348 _triangles = new GeomTriangles(Geom::UH_static);
02349 _geom->add_primitive(_triangles);
02350 }
02351 return _triangles;
02352
02353 } else if (prim_type == GeomLines::get_class_type()) {
02354 if (_lines == (GeomPrimitive *)NULL) {
02355 _lines = new GeomLines(Geom::UH_static);
02356 _geom->add_primitive(_lines);
02357 }
02358 return _lines;
02359
02360 } else if (prim_type == GeomPoints::get_class_type()) {
02361 if (_points == (GeomPrimitive *)NULL) {
02362 _points = new GeomPoints(Geom::UH_static);
02363 _geom->add_primitive(_points);
02364 }
02365 return _points;
02366 }
02367
02368 nassertr(false, NULL);
02369 return NULL;
02370 }
02371
02372
02373
02374
02375
02376
02377
02378 int TextAssembler::GeomCollector::
02379 append_vertex(const GeomVertexData *orig_vdata, int orig_row,
02380 const LMatrix4 &xform) {
02381 int new_row = _vdata->get_num_rows();
02382 _vdata->copy_row_from(new_row, orig_vdata, orig_row, Thread::get_current_thread());
02383
02384 GeomVertexRewriter vertex_rewriter(_vdata, InternalName::get_vertex());
02385 vertex_rewriter.set_row_unsafe(new_row);
02386 LPoint3 point = vertex_rewriter.get_data3();
02387 vertex_rewriter.set_data3(point * xform);
02388
02389 return new_row;
02390 }
02391
02392
02393
02394
02395
02396
02397
02398
02399 void TextAssembler::GeomCollector::
02400 append_geom(GeomNode *geom_node, const RenderState *state) {
02401 if (_geom->get_num_primitives() > 0) {
02402 geom_node->add_geom(_geom, state);
02403 }
02404 }
02405