49 static const PN_stdfloat small_accent_scale = 0.6f;
52 static const PN_stdfloat tiny_accent_scale = 0.4;
55 static const PN_stdfloat squash_accent_scale_x = 0.8f;
56 static const PN_stdfloat squash_accent_scale_y = 0.5f;
60 static const PN_stdfloat small_squash_accent_scale_x = 0.6f;
61 static const PN_stdfloat small_squash_accent_scale_y = 0.3;
65 static const PN_stdfloat ligature_advance_scale = 0.6f;
73 isspacew(
unsigned int ch) {
74 return isascii(ch) && isspace(ch);
82 isbreakpoint(
unsigned int ch) {
83 return (ch ==
' ' || ch ==
'\t' ||
84 ch == (
unsigned int)text_soft_hyphen_key ||
85 ch == (
unsigned int)text_soft_break_key);
95 _usage_hint(
Geom::UH_static),
97 _dynamic_merge(text_dynamic_merge),
109 _initial_cprops(copy._initial_cprops),
110 _text_string(copy._text_string),
111 _text_block(copy._text_block),
114 _next_row_ypos(copy._next_row_ypos),
115 _encoder(copy._encoder),
116 _usage_hint(copy._usage_hint),
117 _max_rows(copy._max_rows),
118 _dynamic_merge(copy._dynamic_merge),
119 _multiline_mode(copy._multiline_mode)
128 _initial_cprops = copy._initial_cprops;
129 _text_string = copy._text_string;
130 _text_block = copy._text_block;
133 _next_row_ypos = copy._next_row_ypos;
134 _encoder = copy._encoder;
135 _usage_hint = copy._usage_hint;
136 _max_rows = copy._max_rows;
137 _dynamic_merge = copy._dynamic_merge;
138 _multiline_mode = copy._multiline_mode;
155 _next_row_ypos = 0.0f;
157 _text_string.clear();
175 wstring::const_iterator si = wtext.begin();
176 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
178 while (si != wtext.end()) {
183 <<
"pop_properties encountered without preceding push_properties.\n";
184 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
188 return wordwrap_text();
206 nassertr(start >= 0 && start <= (
int)_text_string.size(),
false);
207 nassertr(count >= 0 && start + count <= (
int)_text_string.size(),
false);
212 wstring::const_iterator si = wtext.begin();
213 scan_wtext(substr, si, wtext.end(), _initial_cprops);
214 while (si != wtext.end()) {
216 <<
"pop_properties encountered without preceding push_properties.\n";
217 scan_wtext(substr, si, wtext.end(), _initial_cprops);
220 _text_string.erase(_text_string.begin() + start, _text_string.begin() + start + count);
221 _text_string.insert(_text_string.begin() + start, substr.begin(), substr.end());
223 return wordwrap_text();
239 TextString::const_iterator si;
240 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
241 const TextCharacter &tch = (*si);
242 if (tch._graphic ==
nullptr) {
243 wtext += tch._character;
266 TextBlock::const_iterator bi;
267 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
268 const TextRow &row = (*bi);
269 if (bi != _text_block.begin()) {
273 TextString::const_iterator si;
274 for (si = row._string.begin(); si != row._string.end(); ++si) {
275 const TextCharacter &tch = (*si);
276 if (tch._graphic ==
nullptr) {
277 wtext += tch._character;
297 PT(ComputedProperties) current_cprops = _initial_cprops;
299 TextString::const_iterator si;
300 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
301 const TextCharacter &tch = (*si);
302 current_cprops->append_delta(wtext, tch._cprops);
303 if (tch._graphic ==
nullptr) {
304 wtext += tch._character;
306 wtext.push_back(text_embed_graphic_key);
307 wtext += tch._graphic_wname;
308 wtext.push_back(text_embed_graphic_key);
310 current_cprops = tch._cprops;
312 current_cprops->append_delta(wtext, _initial_cprops);
334 PT(ComputedProperties) current_cprops = _initial_cprops;
336 TextBlock::const_iterator bi;
337 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
338 const TextRow &row = (*bi);
339 if (bi != _text_block.begin()) {
340 current_cprops->append_delta(wtext, _initial_cprops);
341 current_cprops = _initial_cprops;
345 TextString::const_iterator si;
346 for (si = row._string.begin(); si != row._string.end(); ++si) {
347 const TextCharacter &tch = (*si);
348 current_cprops->append_delta(wtext, tch._cprops);
349 if (tch._graphic ==
nullptr) {
350 wtext += tch._character;
352 wtext.push_back(text_embed_graphic_key);
353 wtext += tch._graphic_wname;
354 wtext.push_back(text_embed_graphic_key);
356 current_cprops = tch._cprops;
359 current_cprops->append_delta(wtext, _initial_cprops);
375 nassertr(n >= 0 && n <= (
int)_text_string.size(),
false);
377 if (n == (
int)_text_string.size()) {
379 if (_text_string.empty()) {
383 r = _text_block.size() - 1;
384 c = _text_block[r]._string.size();
396 while (r + 1 < (
int)_text_block.size() &&
397 _text_block[r + 1]._row_start < n) {
401 const TextRow &row = _text_block[r];
402 bool is_real_char =
true;
404 nassertr(n > 0,
false);
405 if (row._got_soft_hyphens) {
409 int i = row._row_start;
411 if (_text_string[i]._character != text_soft_hyphen_key &&
412 _text_string[i]._character != text_soft_break_key) {
417 if (_text_string[n - 1]._character != text_soft_hyphen_key &&
418 _text_string[n - 1]._character != text_soft_break_key) {
420 if (_text_string[n - 1]._character ==
'\n') {
421 is_real_char =
false;
424 is_real_char =
false;
429 c = min(n - row._row_start, (
int)row._string.size());
430 if (_text_string[n - 1]._character ==
'\n') {
431 is_real_char =
false;
448 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0);
449 if (r == (
int)_text_block.size()) {
451 return _text_string.size();
454 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0);
455 const TextRow &row = _text_block[r];
457 if (row._got_soft_hyphens) {
460 int n = row._row_start;
462 if (_text_string[n]._character != text_soft_hyphen_key &&
463 _text_string[n]._character != text_soft_break_key) {
472 return row._row_start + c;
487 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0.0f);
488 if (r == (
int)_text_block.size()) {
489 nassertr(c == 0, 0.0f);
493 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0.0f);
494 const TextRow &row = _text_block[r];
495 PN_stdfloat xpos = row._xpos;
496 for (
int i = 0; i < c; ++i) {
512 PlacedGlyphs placed_glyphs;
513 assemble_paragraph(placed_glyphs);
521 shadow_node->add_child(shadow_geom_node);
525 text_node->add_child(text_geom_node);
532 bool any_shadow =
false;
534 GeomCollectorMap geom_collector_map;
535 GeomCollectorMap geom_shadow_collector_map;
537 QuadMap quad_shadow_map;
539 PlacedGlyphs::const_iterator pgi;
540 for (pgi = placed_glyphs.begin(); pgi != placed_glyphs.end(); ++pgi) {
541 const GlyphPlacement &placement = (*pgi);
543 if (placement._properties != properties) {
545 properties = placement._properties;
548 if (properties->has_shadow()) {
553 shadow_state.clear();
557 if (!placement._glyph.is_null()) {
558 if (properties->has_shadow()) {
559 if (_dynamic_merge) {
560 if (placement._glyph->has_quad()) {
561 placement.assign_quad_to(quad_shadow_map, shadow_state, shadow);
563 placement.assign_append_to(geom_shadow_collector_map, shadow_state, shadow);
566 placement.assign_to(shadow_geom_node, shadow_state, shadow);
576 if (_dynamic_merge) {
577 if (placement._glyph->has_quad()) {
578 placement.assign_quad_to(quad_map, text_state);
580 placement.assign_append_to(geom_collector_map, text_state);
583 placement.assign_to(text_geom_node, text_state);
586 placement.copy_graphic_to(text_node, text_state);
588 placed_glyphs.clear();
593 parent_node->add_child(shadow_node);
596 GeomCollectorMap::iterator gc;
597 for (gc = geom_collector_map.begin(); gc != geom_collector_map.end(); ++gc) {
598 (*gc).second.append_geom(text_geom_node, (*gc).first._state);
601 generate_quads(text_geom_node, quad_map);
604 for (gc = geom_shadow_collector_map.begin();
605 gc != geom_shadow_collector_map.end();
607 (*gc).second.append_geom(shadow_geom_node, (*gc).first._state);
610 generate_quads(shadow_geom_node, quad_shadow_map);
613 parent_node->add_child(text_node);
627 if (character ==
' ') {
630 nassertr(font !=
nullptr, 0.0f);
637 UnicodeLatinMap::AccentType accent_type;
638 int additional_flags;
639 PN_stdfloat glyph_scale;
640 PN_stdfloat advance_scale;
641 get_character_glyphs(character, &properties,
642 got_glyph, first_glyph, second_glyph, accent_type,
643 additional_flags, glyph_scale, advance_scale);
645 PN_stdfloat advance = 0.0f;
647 if (first_glyph !=
nullptr) {
648 advance = first_glyph->get_advance() * advance_scale;
650 if (second_glyph !=
nullptr) {
651 advance += second_glyph->get_advance();
656 return advance * glyph_scale;
682 if (character ==
' ' || character ==
'\n') {
689 nassertr(font !=
nullptr,
false);
692 return font->get_glyph(character, glyph);
706 if (character ==
' ' || character ==
'\n') {
715 UnicodeLatinMap::AccentType accent_type;
716 int additional_flags;
717 PN_stdfloat glyph_scale;
718 PN_stdfloat advance_scale;
719 get_character_glyphs(character, &properties,
720 got_glyph, first_glyph, second_glyph, accent_type,
721 additional_flags, glyph_scale, advance_scale);
741 if (character ==
' ' || character ==
'\n') {
748 nassertr(font !=
nullptr,
false);
751 if (!font->get_glyph(character, glyph)) {
755 return glyph->is_whitespace();
765 wstring::const_iterator &si,
766 const wstring::const_iterator &send,
767 TextAssembler::ComputedProperties *current_cprops) {
769 if ((*si) == text_push_properties_key) {
775 while (si != send && (*si) != text_push_properties_key) {
783 <<
"Unclosed push_properties in text.\n";
790 PT(ComputedProperties) new_cprops =
791 new ComputedProperties(current_cprops, wname, _encoder);
794 scan_wtext(output_string, si, send, new_cprops);
796 if (text_cat.is_debug()) {
802 <<
"push_properties not matched by pop_properties.\n";
806 }
else if ((*si) == text_pop_properties_key) {
812 }
else if ((*si) == text_embed_graphic_key) {
817 wstring graphic_wname;
819 while (si != send && (*si) != text_embed_graphic_key) {
820 graphic_wname += (*si);
827 <<
"Unclosed embed_graphic in text.\n";
835 std::string graphic_name = _encoder->
encode_wtext(graphic_wname);
842 if (named_graphic !=
nullptr) {
843 output_string.push_back(TextCharacter(named_graphic, graphic_wname, current_cprops));
847 <<
"Unknown TextGraphic: " << graphic_name <<
"\n";
852 output_string.push_back(TextCharacter(*si, current_cprops));
874 if (_text_string.empty()) {
881 _text_block.push_back(TextRow(p));
884 PN_stdfloat initial_width = 0.0f;
885 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
886 if (_text_string[p]._character ==
'\n') {
887 initial_width = 0.0f;
888 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
892 _text_block.back()._eol_cprops = _text_string[p]._cprops;
893 _text_block.push_back(TextRow(p + 1));
896 _text_block.back()._string.push_back(_text_string[p]);
900 bool needs_newline =
false;
902 while (p < _text_string.size()) {
903 nassertr(!isspacew(_text_string[p]._character),
false);
909 bool any_spaces =
false;
910 size_t last_space = 0;
911 PN_stdfloat last_space_width = 0.0f;
913 bool any_hyphens =
false;
914 size_t last_hyphen = 0;
915 bool output_hyphen =
false;
917 bool overflow =
false;
918 PN_stdfloat wordwrap_width = -1.0f;
920 bool last_was_space =
false;
921 PN_stdfloat width = initial_width;
922 while (q < _text_string.size() && _text_string[q]._character !=
'\n') {
923 if (_text_string[q]._cprops->_properties.has_wordwrap()) {
924 wordwrap_width = _text_string[q]._cprops->_properties.get_wordwrap();
926 wordwrap_width = -1.0f;
929 if (isspacew(_text_string[q]._character) ||
930 _text_string[q]._character == text_soft_break_key) {
931 if (!last_was_space) {
938 last_space_width = width;
939 last_was_space =
true;
942 last_was_space =
false;
947 if (_text_string[q]._character == text_soft_hyphen_key) {
948 if (wordwrap_width > 0.0f) {
952 if (q != p && width + calc_hyphen_width(_text_string[q]) <= wordwrap_width) {
964 if (wordwrap_width > 0.0f && width > wordwrap_width) {
976 nassertr(wordwrap_width > 0.0f,
false);
978 if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) {
987 output_hyphen =
true;
989 }
else if (any_spaces) {
998 while ((
int)i < text_max_never_break && q - i > p &&
999 get_text_never_break_before().find(_text_string[q - i]._character) != wstring::npos) {
1002 if ((
int)i < text_max_never_break) {
1009 size_t next_start = q;
1010 while (next_start < _text_string.size() &&
1011 isbreakpoint(_text_string[next_start]._character)) {
1016 while (q > p && isspacew(_text_string[q - 1]._character)) {
1020 if (next_start == p) {
1025 if (initial_width == 0.0f) {
1031 while (next_start < _text_string.size() &&
1032 isbreakpoint(_text_string[next_start]._character)) {
1038 if (needs_newline) {
1039 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1043 _text_block.push_back(TextRow(p));
1046 needs_newline =
true;
1049 if (_text_string[next_start - 1]._cprops->_properties.get_preserve_trailing_whitespace()) {
1053 for (
size_t pi = p; pi < q; pi++) {
1054 if (_text_string[pi]._character != text_soft_hyphen_key &&
1055 _text_string[pi]._character != text_soft_break_key) {
1056 _text_block.back()._string.push_back(_text_string[pi]);
1058 _text_block.back()._got_soft_hyphens =
true;
1061 if (output_hyphen) {
1062 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1063 wstring::const_iterator wi;
1064 for (wi = text_soft_hyphen_output.begin();
1065 wi != text_soft_hyphen_output.end();
1067 _text_block.back()._string.push_back(TextCharacter(*wi, _text_string[last_hyphen]._cprops));
1073 if (next_start < _text_string.size() && _text_string[next_start]._character ==
'\n') {
1075 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1079 _text_block.back()._eol_cprops = _text_string[next_start]._cprops;
1081 _text_block.push_back(TextRow(next_start));
1082 needs_newline =
false;
1087 initial_width = 0.0f;
1088 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
1089 if (_text_string[p]._character ==
'\n') {
1090 initial_width = 0.0f;
1091 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1095 _text_block.back()._eol_cprops = _text_string[p]._cprops;
1096 _text_block.push_back(TextRow(p + 1));
1098 initial_width +=
calc_width(_text_string[p]);
1099 _text_block.back()._string.push_back(_text_string[p]);
1112 PN_stdfloat TextAssembler::
1113 calc_hyphen_width(
const TextCharacter &tch) {
1114 TextFont *font = tch._cprops->_properties.get_font();
1115 nassertr(font !=
nullptr, 0.0f);
1117 PN_stdfloat hyphen_width = 0.0f;
1118 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1119 wstring::const_iterator wi;
1120 for (wi = text_soft_hyphen_output.begin();
1121 wi != text_soft_hyphen_output.end();
1123 hyphen_width +=
calc_width(*wi, tch._cprops->_properties);
1126 return hyphen_width;
1132 void TextAssembler::
1133 generate_quads(
GeomNode *geom_node,
const QuadMap &quad_map) {
1134 QuadMap::const_iterator qmi;
1135 for (qmi = quad_map.begin(); qmi != quad_map.end(); ++qmi) {
1136 const QuadDefs &quads = qmi->second;
1138 glyphs.reserve(quads.size());
1150 vtx_handle->unclean_set_num_rows(quads.size() * 4);
1152 unsigned char *write_ptr = vtx_handle->get_write_pointer();
1156 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float32);
1158 PN_float32 *vtx_ptr = (PN_float32 *)
1159 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1160 PN_float32 *tex_ptr = (PN_float32 *)
1161 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1163 for (
const QuadDef &quad : quads) {
1164 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1166 vtx_ptr[2] = quad._dimensions[3];
1169 tex_ptr[0] = quad._uvs[0];
1170 tex_ptr[1] = quad._uvs[3];
1173 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1175 vtx_ptr[2] = quad._dimensions[1];
1178 tex_ptr[0] = quad._uvs[0];
1179 tex_ptr[1] = quad._uvs[1];
1182 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1184 vtx_ptr[2] = quad._dimensions[3];
1187 tex_ptr[0] = quad._uvs[2];
1188 tex_ptr[1] = quad._uvs[3];
1191 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1193 vtx_ptr[2] = quad._dimensions[1];
1196 tex_ptr[0] = quad._uvs[2];
1197 tex_ptr[1] = quad._uvs[1];
1200 glyphs.push_back(move(quad._glyph));
1204 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float64);
1206 PN_float64 *vtx_ptr = (PN_float64 *)
1207 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1208 PN_float64 *tex_ptr = (PN_float64 *)
1209 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1211 for (
const QuadDef &quad : quads) {
1212 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1214 vtx_ptr[2] = quad._dimensions[3];
1217 tex_ptr[0] = quad._uvs[0];
1218 tex_ptr[1] = quad._uvs[3];
1221 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1223 vtx_ptr[2] = quad._dimensions[1];
1226 tex_ptr[0] = quad._uvs[0];
1227 tex_ptr[1] = quad._uvs[1];
1230 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1232 vtx_ptr[2] = quad._dimensions[3];
1235 tex_ptr[0] = quad._uvs[2];
1236 tex_ptr[1] = quad._uvs[3];
1239 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1241 vtx_ptr[2] = quad._dimensions[1];
1244 tex_ptr[0] = quad._uvs[2];
1245 tex_ptr[1] = quad._uvs[1];
1248 glyphs.push_back(move(quad._glyph));
1254 int vtx_count = quads.size() * 4;
1256 if (vtx_count > 65535) {
1257 tris->set_index_type(GeomEnums::NT_uint32);
1259 tris->set_index_type(GeomEnums::NT_uint16);
1263 idx_handle->unclean_set_num_rows(quads.size() * 6);
1264 if (tris->get_index_type() == GeomEnums::NT_uint16) {
1266 uint16_t *idx_ptr = (uint16_t *)idx_handle->get_write_pointer();
1268 for (
int i = 0; i < vtx_count; i += 4) {
1269 *(idx_ptr++) = i + 0;
1270 *(idx_ptr++) = i + 1;
1271 *(idx_ptr++) = i + 2;
1272 *(idx_ptr++) = i + 2;
1273 *(idx_ptr++) = i + 1;
1274 *(idx_ptr++) = i + 3;
1278 uint32_t *idx_ptr = (uint32_t *)idx_handle->get_write_pointer();
1280 for (
int i = 0; i < vtx_count; i += 4) {
1281 *(idx_ptr++) = i + 0;
1282 *(idx_ptr++) = i + 1;
1283 *(idx_ptr++) = i + 2;
1284 *(idx_ptr++) = i + 2;
1285 *(idx_ptr++) = i + 1;
1286 *(idx_ptr++) = i + 3;
1292 tris->set_minmax(0, vtx_count - 1,
nullptr,
nullptr);
1295 geom->_glyphs.swap(glyphs);
1296 geom->add_primitive(tris);
1297 geom_node->
add_geom(geom, qmi->first);
1305 void TextAssembler::
1307 _ul.set(0.0f, 0.0f);
1308 _lr.set(0.0f, 0.0f);
1311 PN_stdfloat ypos = 0.0f;
1312 _next_row_ypos = 0.0f;
1313 TextBlock::iterator bi;
1314 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
1315 TextRow &row = (*bi);
1318 size_t first_glyph = placed_glyphs.size();
1321 PN_stdfloat row_width, line_height, wordwrap;
1322 TextProperties::Alignment align;
1323 assemble_row(row, placed_glyphs,
1324 row_width, line_height, align, wordwrap);
1328 if (num_rows == 0) {
1330 _ul[1] = 0.8f * line_height;
1335 ypos -= line_height;
1337 _lr[1] = ypos - 0.2 * line_height;
1344 PN_stdfloat xpos = 0;
1346 case TextProperties::A_left:
1347 _lr[0] = max(_lr[0], row_width);
1350 case TextProperties::A_right:
1352 _ul[0] = min(_ul[0], xpos);
1355 case TextProperties::A_center:
1356 xpos = -0.5f * row_width;
1357 _ul[0] = min(_ul[0], xpos);
1358 _lr[0] = max(_lr[0], -xpos);
1361 case TextProperties::A_boxed_left:
1362 _lr[0] = max(_lr[0], max(row_width, wordwrap));
1365 case TextProperties::A_boxed_right:
1366 xpos = wordwrap - row_width;
1367 _ul[0] = min(_ul[0], xpos);
1370 case TextProperties::A_boxed_center:
1371 xpos = -0.5f * row_width;
1372 if (wordwrap > row_width) xpos += (wordwrap * 0.5f);
1373 _ul[0] = min(_ul[0], max(xpos,(wordwrap * 0.5f)));
1374 _lr[0] = max(_lr[0], min(-xpos,-(wordwrap * 0.5f)));
1382 for (
size_t i = first_glyph; i < placed_glyphs.size(); ++i) {
1383 placed_glyphs[i]._xpos += xpos;
1384 placed_glyphs[i]._ypos += ypos;
1389 _next_row_ypos = ypos - line_height;
1403 void TextAssembler::
1404 assemble_row(TextAssembler::TextRow &row,
1406 PN_stdfloat &row_width, PN_stdfloat &line_height,
1407 TextProperties::Alignment &align, PN_stdfloat &wordwrap) {
1411 PN_stdfloat xpos = 0.0f;
1412 align = TextProperties::A_left;
1417 bool underscore =
false;
1418 PN_stdfloat underscore_start = 0.0f;
1421 #ifdef HAVE_HARFBUZZ 1422 const ComputedProperties *prev_cprops =
nullptr;
1423 hb_buffer_t *harfbuff =
nullptr;
1426 TextString::const_iterator si;
1427 for (si = row._string.begin(); si != row._string.end(); ++si) {
1428 const TextCharacter &tch = (*si);
1429 wchar_t character = tch._character;
1434 (underscore && (properties->get_text_color() != underscore_properties->get_text_color() ||
1437 if (underscore && underscore_start != xpos) {
1438 draw_underscore(placed_glyphs, underscore_start, xpos,
1439 underscore_properties);
1442 underscore_start = xpos;
1443 underscore_properties = properties;
1447 nassertv(font !=
nullptr);
1450 if ((align == TextProperties::A_left) &&
1451 (properties->get_align() != TextProperties::A_left)) {
1452 align = properties->get_align();
1456 if (properties->get_wordwrap() > 0.0f) {
1457 wordwrap = properties->get_wordwrap();
1462 if (graphic !=
nullptr) {
1464 line_height = max(line_height, frame[3] - frame[2]);
1469 #ifdef HAVE_HARFBUZZ 1470 if (tch._cprops != prev_cprops || graphic !=
nullptr) {
1471 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1473 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1474 hb_buffer_reset(harfbuff);
1476 }
else if (harfbuff ==
nullptr && text_use_harfbuzz &&
1477 font->
is_of_type(DynamicTextFont::get_class_type())) {
1478 harfbuff = hb_buffer_create();
1480 prev_cprops = tch._cprops;
1483 if (graphic ==
nullptr && harfbuff !=
nullptr) {
1484 hb_buffer_add(harfbuff, character, character);
1489 if (character ==
' ') {
1494 }
else if (character ==
'\t') {
1497 xpos = (floor(xpos / tab_width) + 1.0f) * tab_width;
1500 }
else if (character == text_soft_hyphen_key) {
1503 }
else if (graphic !=
nullptr) {
1505 GlyphPlacement placement;
1512 model_node->set_preserve_transform(ModelNode::PT_no_touch);
1513 model_node->add_child(model);
1514 placement._graphic_model = model_node.p();
1518 placement._graphic_model = model->copy_subgraph();
1524 PN_stdfloat advance = (frame[1] - frame[0]);
1529 placement._xpos = (xpos - frame[0]);
1531 placement._slant = properties->
get_slant();
1532 placement._properties = properties;
1534 placed_glyphs.push_back(placement);
1536 xpos += advance * glyph_scale;
1544 UnicodeLatinMap::AccentType accent_type;
1545 int additional_flags;
1546 PN_stdfloat glyph_scale;
1547 PN_stdfloat advance_scale;
1548 get_character_glyphs(character, properties,
1549 got_glyph, first_glyph, second_glyph, accent_type,
1550 additional_flags, glyph_scale, advance_scale);
1554 sprintf(buffer,
"U+%04x", character);
1556 <<
"No definition in " << font->get_name()
1557 <<
" for character " << buffer;
1558 if (character < 128 && isprint((
unsigned int)character)) {
1559 text_cat.warning(
false)
1560 <<
" ('" << (char)character <<
"')";
1562 text_cat.warning(
false)
1570 if (prev_char != -1) {
1571 xpos += font->
get_kerning(prev_char, character) * glyph_scale;
1573 prev_char = character;
1580 GlyphPlacement placement;
1582 placement._glyph =
nullptr;
1583 placement._scale = glyph_scale;
1584 placement._xpos = xpos;
1586 placement._slant = properties->
get_slant();
1587 placement._properties = properties;
1589 PN_stdfloat advance = 0.0f;
1591 if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
1594 LPoint3 min_vert, max_vert;
1595 bool found_any =
false;
1596 if (first_glyph !=
nullptr) {
1597 first_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1600 if (second_glyph !=
nullptr) {
1601 second_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1606 LPoint3 centroid = (min_vert + max_vert) / 2.0f;
1608 if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
1619 placement._scale *= -1;
1620 placement._xpos += centroid[0] * 2;
1621 placement._ypos += centroid[2] * 2;
1624 if (accent_type != UnicodeLatinMap::AT_none) {
1625 GlyphPlacement accent_placement(placement);
1626 tack_on_accent(accent_type, min_vert, max_vert, centroid,
1627 properties, accent_placement);
1628 placed_glyphs.push_back(accent_placement);
1633 if (first_glyph !=
nullptr) {
1634 advance = first_glyph->get_advance() * advance_scale;
1635 if (!first_glyph->is_whitespace()) {
1636 std::swap(placement._glyph, first_glyph);
1637 placed_glyphs.push_back(placement);
1643 if (second_glyph !=
nullptr) {
1644 placement._xpos += advance * glyph_scale;
1645 advance += second_glyph->get_advance();
1646 std::swap(placement._glyph, second_glyph);
1647 placed_glyphs.push_back(placement);
1650 xpos += advance * glyph_scale;
1654 #ifdef HAVE_HARFBUZZ 1655 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1656 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1658 hb_buffer_destroy(harfbuff);
1661 if (underscore && underscore_start != xpos) {
1662 draw_underscore(placed_glyphs, underscore_start, xpos,
1663 underscore_properties);
1668 if (row._eol_cprops !=
nullptr) {
1673 const TextProperties *properties = &(row._eol_cprops->_properties);
1675 nassertv(font !=
nullptr);
1677 if (line_height == 0.0f) {
1679 line_height = max(line_height, font->
get_line_height() * glyph_scale);
1687 void TextAssembler::
1688 shape_buffer(hb_buffer_t *buf, PlacedGlyphs &placed_glyphs, PN_stdfloat &xpos,
1691 #ifdef HAVE_HARFBUZZ 1694 hb_direction_t direction = HB_DIRECTION_INVALID;
1697 case TextProperties::D_ltr:
1698 direction = HB_DIRECTION_LTR;
1700 case TextProperties::D_rtl:
1701 direction = HB_DIRECTION_RTL;
1705 hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
1706 hb_buffer_set_direction(buf, direction);
1707 hb_buffer_guess_segment_properties(buf);
1709 DynamicTextFont *font = DCAST(DynamicTextFont, properties.
get_font());
1710 hb_font_t *hb_font = font->get_hb_font();
1711 hb_shape(hb_font, buf,
nullptr, 0);
1714 PN_stdfloat scale = glyph_scale / (font->get_pixels_per_unit() * font->get_scale_factor() * 64.0);
1716 unsigned int glyph_count;
1717 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
1718 hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
1720 for (
unsigned int i = 0; i < glyph_count; ++i) {
1721 int character = glyph_info[i].cluster;
1722 int glyph_index = glyph_info[i].codepoint;
1725 if (!font->get_glyph_by_index(character, glyph_index, glyph)) {
1727 sprintf(buffer,
"U+%04x", character);
1729 <<
"No definition in " << font->get_name()
1730 <<
" for character " << buffer;
1731 if (character < 128 && isprint((
unsigned int)character)) {
1732 text_cat.warning(
false)
1733 <<
" ('" << (char)character <<
"')";
1735 text_cat.warning(
false)
1739 PN_stdfloat advance = glyph_pos[i].x_advance * scale;
1740 if (glyph->is_whitespace()) {
1746 PN_stdfloat x_offset = glyph_pos[i].x_offset * scale;
1747 PN_stdfloat y_offset = glyph_pos[i].y_offset * scale;
1753 GlyphPlacement placement;
1754 placement._glyph = move(glyph);
1755 placement._scale = glyph_scale;
1756 placement._xpos = xpos + x_offset;
1758 placement._slant = properties.
get_slant();
1759 placement._properties = &properties;
1760 placed_glyphs.push_back(placement);
1771 void TextAssembler::
1773 PN_stdfloat underscore_start, PN_stdfloat underscore_end,
1779 vdata->unclean_set_num_rows(2);
1784 vertex.set_data3(underscore_start, 0.0f, y);
1785 color.set_data4(underscore_properties->get_text_color());
1786 vertex.set_data3(underscore_end, 0.0f, y);
1787 color.set_data4(underscore_properties->get_text_color());
1790 lines->add_next_vertices(2);
1791 lines->close_primitive();
1794 geom->add_primitive(lines);
1803 GlyphPlacement placement;
1804 placement._glyph = move(glyph);
1805 placement._xpos = 0;
1806 placement._ypos = 0;
1807 placement._scale = 1;
1808 placement._slant = 0;
1809 placement._properties = underscore_properties;
1810 placed_glyphs.push_back(placement);
1825 void TextAssembler::
1826 get_character_glyphs(
int character,
const TextProperties *properties,
1829 UnicodeLatinMap::AccentType &accent_type,
1830 int &additional_flags,
1831 PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale) {
1833 nassertv_always(font !=
nullptr);
1837 second_glyph =
nullptr;
1838 accent_type = UnicodeLatinMap::AT_none;
1839 additional_flags = 0;
1841 advance_scale = 1.0f;
1847 if (map_entry !=
nullptr) {
1849 map_entry->_toupper_character != character) {
1850 character = map_entry->_toupper_character;
1856 got_glyph = font->get_glyph(character, glyph);
1857 if (!got_glyph && map_entry !=
nullptr && map_entry->_ascii_equiv != 0) {
1860 if (map_entry->_ascii_equiv ==
'i') {
1862 got_glyph = font->get_glyph(0x0131, glyph) ||
1863 font->get_glyph(
'i', glyph);
1865 }
else if (map_entry->_ascii_equiv ==
'j') {
1867 got_glyph = font->get_glyph(0x0237, glyph) ||
1868 font->get_glyph(
'j', glyph);
1871 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1874 if (!got_glyph && map_entry->_toupper_character != character) {
1876 character = map_entry->_toupper_character;
1878 if (map_entry !=
nullptr) {
1879 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1884 accent_type = map_entry->_accent_type;
1885 additional_flags = map_entry->_additional_flags;
1887 bool got_second_glyph =
false;
1888 if (map_entry->_ascii_additional != 0) {
1891 font->get_glyph(map_entry->_ascii_additional, second_glyph);
1894 if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
1899 advance_scale = ligature_advance_scale;
1902 if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
1914 void TextAssembler::
1915 tack_on_accent(UnicodeLatinMap::AccentType accent_type,
1916 const LPoint3 &min_vert,
const LPoint3 &max_vert,
1917 const LPoint3 ¢roid,
1919 TextAssembler::GlyphPlacement &placement)
const {
1923 if (combine_char != 0 &&
1924 tack_on_accent(combine_char, CP_above, CT_none, min_vert, max_vert,
1925 centroid, properties, placement)) {
1930 switch (accent_type) {
1931 case UnicodeLatinMap::AT_grave:
1936 tack_on_accent(
'/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
1937 properties, placement);
1940 case UnicodeLatinMap::AT_acute:
1941 tack_on_accent(
'/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
1942 properties, placement);
1945 case UnicodeLatinMap::AT_breve:
1946 tack_on_accent(
')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1947 properties, placement);
1950 case UnicodeLatinMap::AT_inverted_breve:
1951 tack_on_accent(
'(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1952 properties, placement);
1955 case UnicodeLatinMap::AT_circumflex:
1956 tack_on_accent(
'^', CP_above, CT_none, min_vert, max_vert, centroid,
1957 properties, placement) ||
1958 tack_on_accent(
'v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
1959 properties, placement);
1962 case UnicodeLatinMap::AT_circumflex_below:
1963 tack_on_accent(
'^', CP_below, CT_none, min_vert, max_vert, centroid,
1964 properties, placement) ||
1965 tack_on_accent(
'v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
1966 properties, placement);
1969 case UnicodeLatinMap::AT_caron:
1970 tack_on_accent(
'^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
1971 properties, placement) ||
1972 tack_on_accent(
'v', CP_above, CT_squash, min_vert, max_vert, centroid,
1973 properties, placement);
1977 case UnicodeLatinMap::AT_tilde:
1978 tack_on_accent(
'~', CP_above, CT_none, min_vert, max_vert, centroid,
1979 properties, placement) ||
1980 tack_on_accent(
's', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1981 properties, placement);
1985 case UnicodeLatinMap::AT_tilde_below:
1986 tack_on_accent(
'~', CP_below, CT_none, min_vert, max_vert, centroid,
1987 properties, placement) ||
1988 tack_on_accent(
's', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1989 properties, placement);
1992 case UnicodeLatinMap::AT_diaeresis:
1993 tack_on_accent(
':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
1994 properties, placement);
1997 case UnicodeLatinMap::AT_diaeresis_below:
1998 tack_on_accent(
':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
1999 properties, placement);
2002 case UnicodeLatinMap::AT_dot_above:
2003 tack_on_accent(
'.', CP_above, CT_none, min_vert, max_vert, centroid,
2004 properties, placement);
2007 case UnicodeLatinMap::AT_dot_below:
2008 tack_on_accent(
'.', CP_below, CT_none, min_vert, max_vert, centroid,
2009 properties, placement);
2012 case UnicodeLatinMap::AT_macron:
2013 tack_on_accent(
'-', CP_above, CT_none, min_vert, max_vert, centroid,
2014 properties, placement);
2017 case UnicodeLatinMap::AT_line_below:
2018 tack_on_accent(
'-', CP_below, CT_none, min_vert, max_vert, centroid,
2019 properties, placement);
2022 case UnicodeLatinMap::AT_ring_above:
2023 tack_on_accent(
'o', CP_top, CT_tiny, min_vert, max_vert, centroid,
2024 properties, placement);
2027 case UnicodeLatinMap::AT_ring_below:
2028 tack_on_accent(
'o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
2029 properties, placement);
2032 case UnicodeLatinMap::AT_cedilla:
2033 tack_on_accent(0xb8, CP_below, CT_none, min_vert, max_vert, centroid,
2034 properties, placement) ||
2035 tack_on_accent(
'c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
2036 properties, placement);
2041 case UnicodeLatinMap::AT_comma_below:
2042 tack_on_accent(
',', CP_below, CT_none, min_vert, max_vert, centroid,
2043 properties, placement);
2046 case UnicodeLatinMap::AT_ogonek:
2047 tack_on_accent(
',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
2048 properties, placement);
2051 case UnicodeLatinMap::AT_stroke:
2052 tack_on_accent(
'/', CP_within, CT_none, min_vert, max_vert, centroid,
2053 properties, placement);
2067 bool TextAssembler::
2068 tack_on_accent(
wchar_t accent_mark, TextAssembler::CheesyPosition position,
2069 TextAssembler::CheesyTransform transform,
2070 const LPoint3 &min_vert,
const LPoint3 &max_vert,
2071 const LPoint3 ¢roid,
2073 TextAssembler::GlyphPlacement &placement)
const {
2075 nassertr(font !=
nullptr,
false);
2080 if (font->get_glyph(accent_mark, accent_glyph) ||
2081 font->get_glyph(toupper(accent_mark), accent_glyph)) {
2082 if (!accent_glyph->is_whitespace()) {
2083 LPoint3 min_accent, max_accent;
2084 bool found_any =
false;
2085 accent_glyph->calc_tight_bounds(min_accent, max_accent, found_any,
2089 LMatrix4 accent_mat;
2090 bool has_mat =
true;
2092 switch (transform) {
2098 accent_mat = LMatrix4::scale_mat(-1.0f, -1.0f, 1.0f);
2100 min_accent[0] = -max_accent[0];
2105 accent_mat = LMatrix4::scale_mat(1.0f, -1.0f, -1.0f);
2107 min_accent[2] = -max_accent[2];
2112 accent_mat.set_rotate_mat_normaxis(90.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2116 max_accent[0] = -min_accent[2];
2117 min_accent[0] = -max_accent[2];
2124 placement._scale *= -1;
2126 min_accent[0] = -max_accent[0];
2129 min_accent[2] = -max_accent[2];
2134 accent_mat.set_rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2138 min_accent[0] = min_accent[2];
2139 max_accent[0] = max_accent[2];
2145 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, 1.0f, squash_accent_scale_y);
2146 min_accent[0] *= squash_accent_scale_x;
2147 max_accent[0] *= squash_accent_scale_x;
2148 min_accent[2] *= squash_accent_scale_y;
2149 max_accent[2] *= squash_accent_scale_y;
2152 case CT_squash_mirror_y:
2153 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, -1.0f, -squash_accent_scale_y);
2154 min_accent[0] *= squash_accent_scale_x;
2155 max_accent[0] *= squash_accent_scale_x;
2157 min_accent[2] = -max_accent[2] * squash_accent_scale_y;
2158 max_accent[2] = -t * squash_accent_scale_y;
2161 case CT_squash_mirror_diag:
2163 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2164 LMatrix4::scale_mat(-squash_accent_scale_x, -1.0f, squash_accent_scale_y);
2169 min_accent[0] = min_accent[2] * -squash_accent_scale_x;
2170 max_accent[0] = max_accent[2] * -squash_accent_scale_x;
2171 min_accent[2] = -u * squash_accent_scale_y;
2172 max_accent[2] = -t * squash_accent_scale_y;
2175 case CT_small_squash:
2176 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
2177 min_accent[0] *= small_squash_accent_scale_x;
2178 max_accent[0] *= small_squash_accent_scale_x;
2179 min_accent[2] *= small_squash_accent_scale_y;
2180 max_accent[2] *= small_squash_accent_scale_y;
2183 case CT_small_squash_mirror_y:
2184 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, -1.0f, -small_squash_accent_scale_y);
2185 min_accent[0] *= small_squash_accent_scale_x;
2186 max_accent[0] *= small_squash_accent_scale_x;
2188 min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
2189 max_accent[2] = -t * small_squash_accent_scale_y;
2192 case CT_small_squash_mirror_diag:
2194 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2195 LMatrix4::scale_mat(-small_squash_accent_scale_x, -1.0f, small_squash_accent_scale_y);
2200 min_accent[0] = min_accent[2] * -small_squash_accent_scale_x;
2201 max_accent[0] = max_accent[2] * -small_squash_accent_scale_x;
2202 min_accent[2] = -u * small_squash_accent_scale_y;
2203 max_accent[2] = -t * small_squash_accent_scale_y;
2208 placement._scale *= small_accent_scale;
2209 min_accent *= small_accent_scale;
2210 max_accent *= small_accent_scale;
2213 case CT_small_rotate_270:
2215 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2216 LMatrix4::scale_mat(small_accent_scale);
2221 min_accent[0] = min_accent[2] * small_accent_scale;
2222 max_accent[0] = max_accent[2] * small_accent_scale;
2223 min_accent[2] = -u * small_accent_scale;
2224 max_accent[2] = -t * small_accent_scale;
2229 placement._scale *= tiny_accent_scale;
2230 min_accent *= tiny_accent_scale;
2231 max_accent *= tiny_accent_scale;
2234 case CT_tiny_mirror_x:
2235 accent_mat = LMatrix4::scale_mat(-tiny_accent_scale, -1.0f, tiny_accent_scale);
2238 min_accent[0] = -max_accent[0] * tiny_accent_scale;
2239 max_accent[0] = -t * tiny_accent_scale;
2240 min_accent[2] *= tiny_accent_scale;
2241 max_accent[2] *= tiny_accent_scale;
2244 case CT_tiny_rotate_270:
2246 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2247 LMatrix4::scale_mat(tiny_accent_scale);
2252 min_accent[0] = min_accent[2] * tiny_accent_scale;
2253 max_accent[0] = max_accent[2] * tiny_accent_scale;
2254 min_accent[2] = -u * tiny_accent_scale;
2255 max_accent[2] = -t * tiny_accent_scale;
2264 LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
2265 PN_stdfloat accent_height = max_accent[2] - min_accent[2] - total_margin * 2;
2266 PN_stdfloat accent_x = centroid[0] - accent_centroid[0];
2267 PN_stdfloat accent_y = 0;
2268 PN_stdfloat min_y = min_vert[2] + total_margin;
2269 PN_stdfloat max_y = max_vert[2] - total_margin;
2274 accent_y = max_y - accent_centroid[2] + accent_height * 0.75f;
2279 accent_y = min_y - accent_centroid[2] - accent_height * 0.75f;
2284 accent_y = max_y - accent_centroid[2];
2289 accent_y = min_y - accent_centroid[2];
2294 accent_y = centroid[2] - accent_centroid[2];
2298 placement._xpos += placement._scale * (accent_x + placement._slant * accent_y);
2299 placement._ypos += placement._scale * accent_y;
2303 PT(
Geom) accent_geom = accent_glyph->get_geom(_usage_hint);
2304 accent_geom->transform_vertices(accent_mat);
2305 placement._glyph =
new TextGlyph(0, accent_geom, accent_glyph->get_state(), 0);
2308 placement._glyph = accent_glyph;
2322 void TextAssembler::ComputedProperties::
2323 append_delta(wstring &wtext, TextAssembler::ComputedProperties *other) {
2324 if (
this != other) {
2325 if (_depth > other->_depth) {
2327 nassertv(_based_on !=
nullptr);
2329 wtext.push_back(text_pop_properties_key);
2330 _based_on->append_delta(wtext, other);
2332 }
else if (other->_depth > _depth) {
2334 nassertv(other->_based_on !=
nullptr);
2336 append_delta(wtext, other->_based_on);
2337 wtext.push_back(text_push_properties_key);
2338 wtext += other->_wname;
2339 wtext.push_back(text_push_properties_key);
2341 }
else if (_depth != 0) {
2343 nassertv(_based_on !=
nullptr && other->_based_on !=
nullptr);
2345 wtext.push_back(text_pop_properties_key);
2346 _based_on->append_delta(wtext, other->_based_on);
2347 wtext.push_back(text_push_properties_key);
2348 wtext += other->_wname;
2349 wtext.push_back(text_push_properties_key);
2358 void TextAssembler::GlyphPlacement::
2360 const LVector2 &offset)
const {
2362 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2363 0.0f, 1.0f, 0.0f, 0.0f,
2364 _slant * _scale, 0.0f, _scale, 0.0f,
2365 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2367 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2368 geom->transform_vertices(xform);
2369 geom_node->
add_geom(geom, state->compose(_glyph->get_state()));
2377 void TextAssembler::GlyphPlacement::
2378 assign_append_to(GeomCollectorMap &geom_collector_map,
2380 const LVector2 &offset)
const {
2382 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2383 0.0f, 1.0f, 0.0f, 0.0f,
2384 _slant * _scale, 0.0f, _scale, 0.0f,
2385 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2387 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2392 CPT(
RenderState) rs = _glyph->get_state()->compose(state);
2393 GeomCollectorKey key(rs, vdata->
get_format());
2395 GeomCollectorMap::iterator mi = geom_collector_map.find(key);
2396 if (mi == geom_collector_map.end()) {
2397 mi = geom_collector_map.insert(GeomCollectorMap::value_type(key, GeomCollector(vdata->
get_format()))).first;
2399 GeomCollector &geom_collector = (*mi).second;
2400 geom_collector.count_geom(geom);
2404 VertexIndexMap vimap;
2406 for (
size_t p = 0; p < geom->get_num_primitives(); ++p) {
2407 CPT(
GeomPrimitive) primitive = geom->get_primitive(p)->decompose();
2410 GeomPrimitive *new_prim = geom_collector.get_primitive(primitive->get_type());
2413 for (sp = 0; sp < primitive->get_num_primitives(); sp++) {
2415 e = primitive->get_primitive_end(sp);
2418 for (i = s; i < e; i++) {
2419 int vi = primitive->get_vertex(i);
2422 std::pair<VertexIndexMap::iterator, bool> added = vimap.insert(VertexIndexMap::value_type(vi, 0));
2427 new_vertex = geom_collector.append_vertex(vdata, vi, xform);
2429 (*(added.first)).second = new_vertex;
2435 new_vertex = (*(added.first)).second;
2448 void TextAssembler::GlyphPlacement::
2449 assign_quad_to(QuadMap &quad_map,
const RenderState *state,
2450 const LVector2 &offset)
const {
2453 if (_glyph->get_quad(quad._dimensions, quad._uvs)) {
2454 quad._dimensions *= _scale;
2455 quad._slantl = quad._dimensions[1] * _slant;
2456 quad._slanth = quad._dimensions[3] * _slant;
2457 quad._dimensions += LVecBase4(_xpos, _ypos, _xpos, _ypos);
2458 quad._dimensions += LVecBase4(offset[0], -offset[1], offset[0], -offset[1]);
2459 quad._glyph = _glyph;
2461 quad_map[state->compose(_glyph->get_state())].push_back(move(quad));
2469 void TextAssembler::GlyphPlacement::
2471 if (_graphic_model !=
nullptr) {
2474 node->add_child(intermediate_node);
2476 intermediate_node->set_transform(
2477 TransformState::make_pos_hpr_scale_shear(
2478 LVecBase3(_xpos, 0, _ypos),
2480 LVecBase3(_scale, 1, _scale),
2481 LVecBase3(0, _slant, 0)
2484 intermediate_node->set_state(state);
2485 intermediate_node->add_child(_graphic_model);
2493 TextAssembler::GeomCollector::
2503 TextAssembler::GeomCollector::
2504 GeomCollector(
const TextAssembler::GeomCollector ©) :
2505 _vdata(copy._vdata),
2517 if (prim_type == GeomTriangles::get_class_type()) {
2518 if (_triangles ==
nullptr) {
2520 _geom->add_primitive(_triangles);
2524 }
else if (prim_type == GeomLines::get_class_type()) {
2525 if (_lines ==
nullptr) {
2526 _lines =
new GeomLines(Geom::UH_static);
2527 _geom->add_primitive(_lines);
2531 }
else if (prim_type == GeomPoints::get_class_type()) {
2532 if (_points ==
nullptr) {
2534 _geom->add_primitive(_points);
2539 nassert_raise(
"unexpected primitive type");
2547 int TextAssembler::GeomCollector::
2549 const LMatrix4 &xform) {
2550 int new_row = _vdata->get_num_rows();
2551 _vdata->copy_row_from(new_row, orig_vdata, orig_row, Thread::get_current_thread());
2554 vertex_rewriter.set_row_unsafe(new_row);
2555 LPoint3 point = vertex_rewriter.get_data3();
2556 vertex_rewriter.set_data3(point * xform);
2565 void TextAssembler::GeomCollector::
2567 if (_geom->get_num_primitives() > 0) {
bool close_primitive()
Indicates that the previous n calls to add_vertex(), since the last call to close_primitive(),...
get_small_caps_scale
Returns the scale factor applied to lowercase letters from their uppercase equivalents,...
virtual PN_stdfloat get_kerning(int first, int second) const
Returns the amount by which to offset the second glyph when it directly follows the first glyph.
A basic node of the scene graph or data graph.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
get_font
Returns the font currently in use, if any.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool has_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font or can be synthesized by Panda,...
const RenderState * get_text_state() const
Returns a RenderState object suitable for rendering text with these properties.
Defines a series of disconnected points.
This class mainly serves as a container for a largish table of the subset of the Unicode character se...
This class can be used to convert text between multiple representations, e.g.
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
get_instance_flag
Returns the instance_flag.
bool set_wsubstr(const std::wstring &wtext, int start, int count)
Replaces the 'count' characters from 'start' of the current text with the indicated replacement text.
get_direction
Returns the direction of the text as specified by set_direction().
static wchar_t get_combining_accent(AccentType accent)
Returns the unicode code point for the combining character corresponding with the given accent type,...
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_shadow
Returns the offset of the shadow as set by set_shadow().
bool set_wtext(const std::wstring &wtext)
Accepts a new text string and associated properties structure, and precomputes the wordwrapping layou...
static bool is_whitespace(wchar_t character, const TextProperties &properties)
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
int calc_index(int r, int c) const
Computes the character index of the character at the rth row and cth column position.
get_underscore
Returns the underscore flag.
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static TextPropertiesManager * get_global_ptr()
Returns the pointer to the global TextPropertiesManager object.
PN_stdfloat get_total_poly_margin() const
Returns the total margin between the edge of the glyph and the edge of the cards.
get_tab_width
Returns the width set via set_tab_width().
get_slant
Returns the factor by which the text is specified to slant to the right.
An encapsulation of a font; i.e.
get_glyph_scale
Returns the scale factor of each letter as specified by set_glyph_scale().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_format
Returns a pointer to the GeomVertexFormat structure that defines this data.
static bool has_exact_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font exactly as named, false otherwise.
void add_vertex(int vertex)
Adds the indicated vertex to the list of vertex indices used by the graphics primitive type.
get_current_thread
Returns a pointer to the currently-executing Thread object.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static const Entry * look_up(char32_t character)
Returns the Entry associated with the indicated character, if there is one.
This node is placed at key points within the scene graph to indicate the roots of "models": subtrees ...
int get_primitive_start(int n) const
Returns the element within the _vertices list at which the nth primitive starts.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static PN_stdfloat calc_width(wchar_t character, const TextProperties &properties)
Returns the width of a single character, according to its associated font.
get_model
Returns the NodePath associated with the graphic, that renders the desired image.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
This class is not normally used directly by user code, but is used by the TextNode to lay out a block...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A representation of a single glyph (character) from a font.
PN_stdfloat get_xpos(int r, int c) const
Returns the x position of the origin of the character or graphic object at the indicated position in ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(PandaNode) TextAssembler
Actually assembles all of the text into a GeomNode, and returns the node (or possibly a parent of the...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
get_line_height
Returns the number of units high each line of text is.
Defines a series of disconnected line segments.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string encode_wtext(const std::wstring &wtext) const
Encodes a wide-text string into a single-char string, according to the current encoding.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool calc_r_c(int &r, int &c, int n) const
Computes the row and column index of the nth character or graphic object in the text.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear()
Reinitializes the contents of the TextAssembler.
A thread; that is, a lightweight process.
get_frame
Returns the frame specified for the graphic.
This defines the set of visual properties that may be assigned to the individual characters of the te...
Defines a series of disconnected triangles.
get_underscore_height
Returns the vertical height of the underscore; see set_underscore_height().
get_space_advance
Returns the number of units wide a space is.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
std::wstring get_wtext() const
Returns a wstring that represents the contents of the text.
get_glyph_shift
Returns the vertical shift of each letter as specified by set_glyph_shift().
TypeHandle is the identifier used to differentiate C++ class types.
get_text_scale
Returns the scale factor of the text as specified by set_text_scale().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This defines all of the TextProperties structures that might be referenced by name from an embedded t...
std::wstring get_plain_wtext() const
Returns a wstring that represents the contents of the text, without any embedded properties character...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::wstring get_wordwrapped_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
This is a specialization on Geom for containing a primitive intended to represent a TextGlyph.
get_multiline_mode
Returns the multline_mode flag.
const TextGraphic * get_graphic_ptr(const std::string &name)
Returns a pointer to the TextGraphic with the indicated name, or NULL if there is no graphic with tha...
This defines a special model that has been constructed for the purposes of embedding an arbitrary gra...
A node that holds Geom objects, renderable pieces of geometry.
const RenderState * get_shadow_state() const
Returns a RenderState object suitable for rendering the shadow of this text with these properties.
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::wstring get_wordwrapped_plain_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_small_caps
Returns the small_caps flag.