1 // -*- C++ -*-
2 
3 /*
4  * Gnome Chemistry Utils
5  * gccv/text-tag.cc
6  *
7  * Copyright (C) 2008-2010 Jean Bréfort <jean.brefort@normalesup.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 3 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
22  * USA
23  */
24 
25 #include "config.h"
26 #include "text-tag.h"
27 #include <list>
28 #include <map>
29 
30 namespace gccv {
31 
32 ////////////////////////////////////////////////////////////////////////////////
33 // Base tag class
34 
TextTag(Tag tag,TagPriority priority)35 TextTag::TextTag (Tag tag, TagPriority priority):
36 	m_Tag (tag),
37 	m_Priority (priority),
38 	m_StartIndex (0),
39 	m_EndIndex (0),
40 	m_Stacked (false),
41 	m_NewLine (false)
42 {
43 }
44 
~TextTag()45 TextTag::~TextTag ()
46 {
47 }
48 
49 Tag TextTag::MaxTag = gccv::MaxTag;
50 
RegisterTagType()51 Tag TextTag::RegisterTagType ()
52 {
53 	Tag result = MaxTag;
54 	MaxTag = static_cast <Tag> (MaxTag + 1);
55 	return result;
56 }
57 
Order(TextTag * first,TextTag * last)58 bool TextTag::Order (TextTag *first, TextTag *last)
59 {
60 	if (first->GetStartIndex () < last->GetStartIndex ())
61 		return true;
62 	else if (first->GetStartIndex () > last->GetStartIndex ())
63 		return false;
64 	if (first->GetEndIndex () > last->GetEndIndex ())
65 		return true;
66 	else if (first->GetEndIndex () < last->GetEndIndex ())
67 		return false;
68 	return first->GetTag () < last->GetTag ();
69 }
70 
Restrict(TextTag * tag)71 TextTag *TextTag::Restrict (TextTag *tag)
72 {
73 	if (tag->GetTag () == GetTag () && tag->GetEndIndex () > GetStartIndex () && tag->GetStartIndex () < GetEndIndex ()) {
74 		if (*tag == *this) {
75 			if (m_StartIndex > tag->GetStartIndex ())
76 				m_StartIndex = tag->GetStartIndex ();
77 			if (m_EndIndex < tag->GetEndIndex ())
78 				m_EndIndex = tag->GetEndIndex ();
79 			tag->SetEndIndex (m_StartIndex); // makes tag invalid
80 			return NULL;
81 		}
82 		if (tag->GetEndIndex () > GetEndIndex ()) {
83 			if (tag->GetStartIndex () < GetStartIndex ()) {
84 				// split tag
85 				TextTag *new_tag = tag->Duplicate ();
86 				new_tag->SetStartIndex (m_EndIndex);
87 				new_tag->SetEndIndex (tag->GetEndIndex ());
88 				tag->SetEndIndex (m_StartIndex);
89 				return new_tag;
90 			}
91 			tag->SetStartIndex (m_EndIndex);
92 			return NULL;
93 		} else {
94 			tag->SetEndIndex (m_StartIndex);
95 			return NULL;
96 		}
97 	}
98 	return NULL;
99 }
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 // Font family tag class
103 
FamilyTextTag(std::string const & family)104 FamilyTextTag::FamilyTextTag (std::string const &family):
105 	TextTag (Family),
106 	m_Family (family)
107 {
108 }
109 
FamilyTextTag(char const * family)110 FamilyTextTag::FamilyTextTag (char const *family):
111 	TextTag (Family),
112 	m_Family (family)
113 {
114 }
115 
~FamilyTextTag()116 FamilyTextTag::~FamilyTextTag ()
117 {
118 }
119 
Filter(PangoAttrList * l,unsigned start,unsigned end)120 void FamilyTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
121 {
122 	PangoAttribute *attr = pango_attr_family_new (m_Family.c_str ());
123 	attr->start_index = start;
124 	attr->end_index = end;
125 	pango_attr_list_insert (l, attr);
126 }
127 
operator ==(TextTag const & tag) const128 bool FamilyTextTag::operator== (TextTag const& tag) const
129 {
130 	if (tag.GetTag () != Family)
131 		return false;
132 	return static_cast <FamilyTextTag const&> (tag).m_Family == m_Family;
133 }
134 
Duplicate() const135 TextTag *FamilyTextTag::Duplicate () const
136 {
137 	return new FamilyTextTag (m_Family);
138 }
139 
140 ////////////////////////////////////////////////////////////////////////////////
141 // Font size tag class
142 
SizeTextTag(double size)143 SizeTextTag::SizeTextTag (double size):
144 	TextTag (Size),
145 	m_Size (size)
146 {
147 }
148 
~SizeTextTag()149 SizeTextTag::~SizeTextTag ()
150 {
151 }
152 
Filter(PangoAttrList * l,unsigned start,unsigned end)153 void SizeTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
154 {
155 	PangoAttribute *attr = pango_attr_size_new (m_Size);
156 	attr->start_index = start;
157 	attr->end_index = end;
158 	pango_attr_list_insert (l, attr);
159 }
160 
operator ==(TextTag const & tag) const161 bool SizeTextTag::operator== (TextTag const& tag) const
162 {
163 	if (tag.GetTag () != Size)
164 		return false;
165 	return static_cast <SizeTextTag const&> (tag).m_Size == m_Size;
166 }
167 
Duplicate() const168 TextTag *SizeTextTag::Duplicate () const
169 {
170 	return new SizeTextTag (m_Size);
171 }
172 
173 ////////////////////////////////////////////////////////////////////////////////
174 // Font style tag class
175 
StyleTextTag(PangoStyle style)176 StyleTextTag::StyleTextTag (PangoStyle style):
177 	TextTag (Style),
178 	m_Style (style)
179 {
180 }
181 
~StyleTextTag()182 StyleTextTag::~StyleTextTag ()
183 {
184 }
185 
Filter(PangoAttrList * l,unsigned start,unsigned end)186 void StyleTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
187 {
188 	PangoAttribute *attr = pango_attr_style_new (m_Style);
189 	attr->start_index = start;
190 	attr->end_index = end;
191 	pango_attr_list_insert (l, attr);
192 }
193 
operator ==(TextTag const & tag) const194 bool StyleTextTag::operator== (TextTag const& tag) const
195 {
196 	if (tag.GetTag () != Style)
197 		return false;
198 	return static_cast <StyleTextTag const&> (tag).m_Style == m_Style;
199 }
200 
Duplicate() const201 TextTag *StyleTextTag::Duplicate () const
202 {
203 	return new StyleTextTag (m_Style);
204 }
205 
206 ////////////////////////////////////////////////////////////////////////////////
207 // Font weight tag class
208 
WeightTextTag(PangoWeight weight)209 WeightTextTag::WeightTextTag (PangoWeight weight):
210 	TextTag (Weight),
211 	m_Weight (weight)
212 {
213 }
214 
~WeightTextTag()215 WeightTextTag::~WeightTextTag ()
216 {
217 }
218 
Filter(PangoAttrList * l,unsigned start,unsigned end)219 void WeightTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
220 {
221 	PangoAttribute *attr = pango_attr_weight_new (m_Weight);
222 	attr->start_index = start;
223 	attr->end_index = end;
224 	pango_attr_list_insert (l, attr);
225 }
226 
operator ==(TextTag const & tag) const227 bool WeightTextTag::operator== (TextTag const& tag) const
228 {
229 	if (tag.GetTag () != Weight)
230 		return false;
231 	return static_cast <WeightTextTag const&> (tag).m_Weight == m_Weight;
232 }
233 
Duplicate() const234 TextTag *WeightTextTag::Duplicate () const
235 {
236 	return new WeightTextTag (m_Weight);
237 }
238 
239 ////////////////////////////////////////////////////////////////////////////////
240 // Font variant tag class
241 
VariantTextTag(PangoVariant variant)242 VariantTextTag::VariantTextTag (PangoVariant variant):
243 	TextTag (Variant),
244 	m_Variant (variant)
245 {
246 }
247 
~VariantTextTag()248 VariantTextTag::~VariantTextTag ()
249 {
250 }
251 
Filter(PangoAttrList * l,unsigned start,unsigned end)252 void VariantTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
253 {
254 	PangoAttribute *attr = pango_attr_variant_new (m_Variant);
255 	attr->start_index = start;
256 	attr->end_index = end;
257 	pango_attr_list_insert (l, attr);
258 }
259 
operator ==(TextTag const & tag) const260 bool VariantTextTag::operator== (TextTag const& tag) const
261 {
262 	if (tag.GetTag () != Variant)
263 		return false;
264 	return static_cast <VariantTextTag const&> (tag).m_Variant == m_Variant;
265 }
266 
Duplicate() const267 TextTag *VariantTextTag::Duplicate () const
268 {
269 	return new VariantTextTag (m_Variant);
270 }
271 
272 ////////////////////////////////////////////////////////////////////////////////
273 // Font strecth tag class
274 
StretchTextTag(PangoStretch stretch)275 StretchTextTag::StretchTextTag (PangoStretch stretch):
276 	TextTag (Stretch),
277 	m_Stretch (stretch)
278 {
279 }
280 
~StretchTextTag()281 StretchTextTag::~StretchTextTag ()
282 {
283 }
284 
Filter(PangoAttrList * l,unsigned start,unsigned end)285 void StretchTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
286 {
287 	PangoAttribute *attr = pango_attr_stretch_new (m_Stretch);
288 	attr->start_index = start;
289 	attr->end_index = end;
290 	pango_attr_list_insert (l, attr);
291 }
292 
operator ==(TextTag const & tag) const293 bool StretchTextTag::operator== (TextTag const& tag) const
294 {
295 	if (tag.GetTag () != Stretch)
296 		return false;
297 	return static_cast <StretchTextTag const&> (tag).m_Stretch == m_Stretch;
298 }
299 
Duplicate() const300 TextTag *StretchTextTag::Duplicate () const
301 {
302 	return new StretchTextTag (m_Stretch);
303 }
304 
305 ////////////////////////////////////////////////////////////////////////////////
306 // Font underline tag class
307 
UnderlineTextTag(TextDecoration underline,GOColor color)308 UnderlineTextTag::UnderlineTextTag (TextDecoration underline, GOColor color):
309 	TextTag (Underline),
310 	m_Underline (underline),
311 	m_Color (color)
312 {
313 }
314 
~UnderlineTextTag()315 UnderlineTextTag::~UnderlineTextTag ()
316 {
317 }
318 
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)319 void UnderlineTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
320 {
321 }
322 
operator ==(TextTag const & tag) const323 bool UnderlineTextTag::operator== (TextTag const& tag) const
324 {
325 	if (tag.GetTag () != Underline)
326 		return false;
327 	return static_cast <UnderlineTextTag const&> (tag).m_Underline == m_Underline;
328 }
329 
Duplicate() const330 TextTag *UnderlineTextTag::Duplicate () const
331 {
332 	return new UnderlineTextTag (m_Underline);
333 }
334 
335 ////////////////////////////////////////////////////////////////////////////////
336 // Font overline tag class
337 
OverlineTextTag(TextDecoration overline,GOColor color)338 OverlineTextTag::OverlineTextTag (TextDecoration overline, GOColor color):
339 	TextTag (Overline),
340 	m_Overline (overline),
341 	m_Color (color)
342 {
343 }
344 
~OverlineTextTag()345 OverlineTextTag::~OverlineTextTag ()
346 {
347 }
348 
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)349 void OverlineTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
350 {
351 }
352 
operator ==(TextTag const & tag) const353 bool OverlineTextTag::operator== (TextTag const& tag) const
354 {
355 	if (tag.GetTag () != Overline)
356 		return false;
357 	return static_cast <OverlineTextTag const&> (tag).m_Overline == m_Overline;
358 }
359 
Duplicate() const360 TextTag *OverlineTextTag::Duplicate () const
361 {
362 	return new OverlineTextTag (m_Overline);
363 }
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 // Font strikethrough tag class
367 
StrikethroughTextTag(TextDecoration strikethrough,GOColor color)368 StrikethroughTextTag::StrikethroughTextTag (TextDecoration strikethrough, GOColor color):
369 	TextTag (Strikethrough),
370 	m_Strikethrough (strikethrough),
371 	m_Color (color)
372 {
373 }
374 
~StrikethroughTextTag()375 StrikethroughTextTag::~StrikethroughTextTag ()
376 {
377 }
378 
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)379 void StrikethroughTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
380 {
381 }
382 
operator ==(TextTag const & tag) const383 bool StrikethroughTextTag::operator== (TextTag const& tag) const
384 {
385 	if (tag.GetTag () != Strikethrough)
386 		return false;
387 	return static_cast <StrikethroughTextTag const&> (tag).m_Strikethrough == m_Strikethrough;
388 }
389 
Duplicate() const390 TextTag *StrikethroughTextTag::Duplicate () const
391 {
392 	return new StrikethroughTextTag (m_Strikethrough);
393 }
394 
395 ////////////////////////////////////////////////////////////////////////////////
396 // Font foreground tag class
397 
ForegroundTextTag(GOColor color)398 ForegroundTextTag::ForegroundTextTag (GOColor color):
399 	TextTag (Foreground),
400 	m_Color (color)
401 {
402 }
403 
~ForegroundTextTag()404 ForegroundTextTag::~ForegroundTextTag ()
405 {
406 }
407 
Filter(PangoAttrList * l,unsigned start,unsigned end)408 void ForegroundTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
409 {
410 	// this might be not enough since we use a global attribute with SetColor().
411 	PangoAttribute *attr = pango_attr_foreground_new (GO_COLOR_UINT_R (m_Color) * 0x101, GO_COLOR_UINT_G (m_Color) * 0x101, GO_COLOR_UINT_B (m_Color) * 0x101);
412 	attr->start_index = start;
413 	attr->end_index = end;
414 	pango_attr_list_insert (l, attr);
415 }
416 
operator ==(TextTag const & tag) const417 bool ForegroundTextTag::operator== (TextTag const& tag) const
418 {
419 	if (tag.GetTag () != Foreground)
420 		return false;
421 	return static_cast <ForegroundTextTag const&> (tag).m_Color == m_Color;
422 }
423 
Duplicate() const424 TextTag *ForegroundTextTag::Duplicate () const
425 {
426 	return new ForegroundTextTag (m_Color);
427 }
428 
429 ////////////////////////////////////////////////////////////////////////////////
430 // Font background tag class
431 
BackgroundTextTag(GOColor color)432 BackgroundTextTag::BackgroundTextTag (GOColor color):
433 	TextTag (Background),
434 	m_Color (color)
435 {
436 }
437 
~BackgroundTextTag()438 BackgroundTextTag::~BackgroundTextTag ()
439 {
440 }
441 
Filter(PangoAttrList * l,unsigned start,unsigned end)442 void BackgroundTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
443 {
444 	PangoAttribute *attr = pango_attr_background_new (GO_COLOR_UINT_R (m_Color) * 0x101, GO_COLOR_UINT_G (m_Color) * 0x101, GO_COLOR_UINT_B (m_Color) * 0x101);
445 	attr->start_index = start;
446 	attr->end_index = end;
447 	pango_attr_list_insert (l, attr);
448 }
449 
operator ==(TextTag const & tag) const450 bool BackgroundTextTag::operator== (TextTag const& tag) const
451 {
452 	if (tag.GetTag () != Background)
453 		return false;
454 	return static_cast <BackgroundTextTag const&> (tag).m_Color == m_Color;
455 }
456 
Duplicate() const457 TextTag *BackgroundTextTag::Duplicate () const
458 {
459 	return new BackgroundTextTag (m_Color);
460 }
461 
462 ////////////////////////////////////////////////////////////////////////////////
463 // Font rise tag class
464 
RiseTextTag(double rise)465 RiseTextTag::RiseTextTag (double rise):
466 	TextTag (Rise),
467 	m_Rise (rise)
468 {
469 }
470 
~RiseTextTag()471 RiseTextTag::~RiseTextTag ()
472 {
473 }
474 
Filter(PangoAttrList * l,unsigned start,unsigned end)475 void RiseTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
476 {
477 	PangoAttribute *attr = pango_attr_rise_new (m_Rise);
478 	attr->start_index = start;
479 	attr->end_index = end;
480 	pango_attr_list_insert (l, attr);
481 }
482 
operator ==(TextTag const & tag) const483 bool RiseTextTag::operator== (TextTag const& tag) const
484 {
485 	if (tag.GetTag () != Rise)
486 		return false;
487 	return static_cast <RiseTextTag const&> (tag).m_Rise == m_Rise;
488 }
489 
Duplicate() const490 TextTag *RiseTextTag::Duplicate () const
491 {
492 	return new RiseTextTag (m_Rise);
493 }
494 
495 ////////////////////////////////////////////////////////////////////////////////
496 // Position tag class (normal, subscript, superscript,...)
497 
PositionTextTag(TextPosition position,double size,bool stacked,Tag tag)498 PositionTextTag::PositionTextTag (TextPosition position, double size, bool stacked, Tag tag):
499 	TextTag (tag, TagPriorityLast),
500 	m_Position (position),
501 	m_Size (size)
502 {
503 	m_Stacked = stacked;
504 }
505 
~PositionTextTag()506 PositionTextTag::~PositionTextTag ()
507 {
508 }
509 
510 struct position_data {
511 	guint start, end;
512 	std::list <PangoAttribute *>extra;
513 	std::map <unsigned, int> sizes, rises;
514 };
515 
516 static gboolean
position_filter(PangoAttribute * attr,gpointer _data)517 position_filter (PangoAttribute *attr, gpointer _data)
518 {
519 	struct position_data *data = static_cast <struct position_data *> (_data);
520 	int start = MAX (data->start, attr->start_index);
521 
522 	if (attr->end_index <= data->start || attr->start_index >= data->end)
523 		return false;
524 	switch (attr->klass->type) {
525 	case PANGO_ATTR_SIZE:
526 		data->sizes[start] = reinterpret_cast <PangoAttrSize *> (attr)->size;
527 		break;
528 	case PANGO_ATTR_RISE:
529 		data->rises[start] = reinterpret_cast <PangoAttrInt *> (attr)->value;
530 		break;
531 	default:
532 		return false;
533 	}
534 	if (attr->end_index > data->end || attr->start_index < data->start) {
535 		// add a new attribute
536 		PangoAttribute *new_attr = pango_attribute_copy (attr);
537 		new_attr->start_index = data->end;
538 		new_attr->end_index = attr->end_index;
539 		attr->end_index = data->start;
540 		data->extra.push_back (new_attr);
541 	} else if (attr->start_index < data->start)
542 		attr->end_index = data->start;
543 	else
544 		attr->start_index = data->end;
545 	return false;
546 }
547 
Filter(PangoAttrList * l,unsigned start,unsigned end)548 void PositionTextTag::Filter (PangoAttrList *l, unsigned start, unsigned end)
549 {
550 	if (m_Position == Normalscript)
551 		return;
552 	struct position_data data;
553 	data.start = start;
554 	data.end = end;
555 	data.sizes[start] = m_Size * PANGO_SCALE;
556 	data.rises[start] = 0.;
557 	pango_attr_list_filter (l, position_filter, &data);
558 	// Build and apply the new attributes
559 	std::map <unsigned, int>::iterator i, j, iend = data.sizes.end (), jend = data.rises.end (), nexti, nextj;
560 	unsigned cur_start = start, cur_end;
561 	j =  data.rises.begin ();
562 	double rise_f = 1.;
563 	switch (m_Position) {
564 	case Subscript:
565 		rise_f = -3.;
566 		break;
567 	case Superscript:
568 		rise_f = +3/2.;
569 		break;
570 	case Normalscript:
571 		break;
572 	}
573 	for (i =  data.sizes.begin (); i != iend; i++) {
574 		nexti = i;
575 		nexti++;
576 		for (; j!= jend && (*j).first < ((nexti == iend)? end: (*nexti).first); j++) {
577 			nextj = j;
578 			nextj++;
579 			cur_end = MIN (((nexti == iend)? end: (*nexti).first), ((nextj == jend)? end: (*nextj).first));
580 			PangoAttribute *attr = pango_attr_size_new ((*i).second * 2 / 3);
581 			attr->start_index = cur_start;
582 			attr->end_index = cur_end;
583 			pango_attr_list_insert (l, attr);
584 			attr = pango_attr_rise_new ((*j).second + (*i).second / rise_f);
585 			attr->start_index = cur_start;
586 			attr->end_index = cur_end;
587 			pango_attr_list_insert (l, attr);
588 			cur_start = cur_end;
589 		}
590 		//WARNING: might be buggy
591 	}
592 	// Apply the extra attributes.
593 	std::list <PangoAttribute *>::iterator k, kend = data.extra.end ();
594 	for (k = data.extra.begin (); k != kend; k++)
595 		pango_attr_list_insert (l, *k);
596 }
597 
operator ==(TextTag const & tag) const598 bool PositionTextTag::operator== (TextTag const& tag) const
599 {
600 	if (tag.GetTag () != GetTag ())
601 		return false;
602 	return static_cast <PositionTextTag const&> (tag).m_Position == m_Position;
603 }
604 
Duplicate() const605 TextTag *PositionTextTag::Duplicate () const
606 {
607 	return new PositionTextTag (m_Position, m_Size, m_Stacked, GetTag ());
608 }
609 
610 ////////////////////////////////////////////////////////////////////////////////
611 // Position tag class (normal, subscript, superscript,...)
612 
NewLineTextTag()613 NewLineTextTag::NewLineTextTag ():
614 	TextTag (NewLine)
615 {
616 	m_NewLine = true;
617 }
618 
~NewLineTextTag()619 NewLineTextTag::~NewLineTextTag ()
620 {
621 }
622 
Filter(G_GNUC_UNUSED PangoAttrList * l,G_GNUC_UNUSED unsigned start,G_GNUC_UNUSED unsigned end)623 void NewLineTextTag::Filter (G_GNUC_UNUSED PangoAttrList *l, G_GNUC_UNUSED unsigned start, G_GNUC_UNUSED unsigned end)
624 {
625 	// Nothing to do
626 }
627 
operator ==(G_GNUC_UNUSED TextTag const & tag) const628 bool NewLineTextTag::operator== (G_GNUC_UNUSED TextTag const& tag) const
629 {
630 	return false;
631 }
632 
Duplicate() const633 TextTag *NewLineTextTag::Duplicate () const
634 {
635 	return new NewLineTextTag ();
636 }
637 
638 ////////////////////////////////////////////////////////////////////////////////
639 // Tags list class
TextTagList()640 TextTagList::TextTagList (): std::list <TextTag *> ()
641 {
642 }
643 
~TextTagList()644 TextTagList::~TextTagList ()
645 {
646 	std::list <TextTag *>::iterator i, iend = end ();
647 	for (i = begin (); i != iend; i++)
648 		delete (*i);
649 }
650 
651 }
652