/* libsswf_tag_text.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2008 */

/*

Copyright (c) 2002-2008 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

/** \file
 *
 * \brief The implementation of the sswf::TagText class
 *
 * This file declares the body of the functions which are not
 * inline. It is part of the SSWF library.
 */

#include	"sswf/libsswf.h"

using namespace sswf;




/////////////////////////////////////////// TagText



/** \class sswf::TagText
 *
 * \brief Enter a label (text string) in an SWF file
 *
 * This tag is used to have a string of text rendered in the
 * screen.
 *
 * This is very flexible since each glyph can be rendered
 * pretty much where you want it to be. However, it can
 * be much more tedious to use that an sswf::TagEditText.
 *
 * \sa sswf::TagEditText
 * \sa <a href="../SWFalexref.html#tag_definetext">SWF Alexis' Reference&mdash;DefineText</a>
 * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference&mdash;swf_tag</a>
 */


/** \brief Initializes a text tag.
 *
 * The constructor initializes the text tag so it
 * looks empty.
 *
 * \param[in] parent The sswf::TagHeader this text tag is included in
 */
TagText::TagText(TagBase *parent)
	: TagBaseID("text", parent)
{
	f_new_text = false;
}


/** \brief Set the text matrix.
 *
 * This function saves a copy of the specified matrix in the
 * text tag. It is used to position the text, rotate it
 * and scale it.
 *
 * \param[in] matrix The text matrix
 */
void TagText::SetMatrix(const Matrix& matrix)
{
	f_matrix = matrix;
}


/** \brief Set the graphical bounds of this text.
 *
 * This function is used to define the largest box
 * where the text appears. This is used to know whenever
 * the text object needs to be rerendered on the screen.
 *
 * The bounds can be larger. If smaller, some parts of the
 * text may not get drawn properly.
 *
 * \param[in] bounds The rectangle representing the bounds of this text.
 */
void TagText::SetBounds(const SRectangle& bounds)
{
	f_bounds = bounds;
}


/** \brief Defines the type of a text tag.
 *
 * This function returns a set of flags defining an text tag.
 *
 * \return SWF_TYPE_DEFINE, SWF_TYPE_REFERENCE and SWF_TYPE_HAS_ID
 */
TagBase::swf_type_t TagText::TypeFlags(void) const
{
	return SWF_TYPE_DEFINE | SWF_TYPE_REFERENCE | SWF_TYPE_HAS_ID;
}


TagText::text_setup_t::text_setup_t(void)
#if _MSVC
	/* this is totally wrong... */
	: text_define_t(sswf::TagText::TEXT_ENTRY_SETUP)
#else
	: text_define_t(TEXT_ENTRY_SETUP)
#endif
{
	Unused();
}


TagText::text_setup_t::text_setup_t(const text_setup_t& setup)
	: text_define_t(setup.f_type)
{
	f_has_font = setup.f_has_font;
	f_font = setup.f_font;
	f_font_height = setup.f_font_height;
	f_has_xadjust = setup.f_has_xadjust;
	f_xadjust = setup.f_xadjust;
	f_has_xoffset = setup.f_has_xoffset;
	f_x = setup.f_x;
	f_has_yoffset = setup.f_has_yoffset;
	f_y = setup.f_y;
	f_has_color = setup.f_has_color;
	f_color = setup.f_color;
}


void TagText::text_setup_t::Unused(void)
{
	f_has_font = f_has_xadjust = f_has_xoffset = f_has_yoffset = f_has_color = false;
}


bool TagText::text_setup_t::IsUsed(void) const
{
	return f_has_font || f_has_xadjust || f_has_xoffset || f_has_yoffset || f_has_color;
}


/** \brief Record one setup in the text array.
 *
 * This function records one setup in the array of text setup and text entries.
 *
 * \return An error or ErrorManager::ERROR_CODE_NONE
 */
ErrorManager::error_code_t TagText::RecordSetup(void)
{
	text_setup_t	*s;

	// is there any new setup?
	if(f_setup.IsUsed()) {

		// make sure 1st setup includes a font ref.
		if(f_records.Count() == 0) {
			if(!f_setup.f_has_font) {
				// Ooops!!! The very first entry must be a setup with a font
				return OnError(ErrorManager::ERROR_CODE_INVALID_TEXT_SETUP, "the very first text record must be a setup with a font definition.");
			}

			// NOTE: I'm not too sure this is really the case, but someone complained
			//	 about it so it's included; it actually makes sense to have a color
			//	 for a text to work anyway
			if(!f_setup.f_has_color) {
				// It seems that the very first entry also needs to have a color (2005/01/12)
				return OnError(ErrorManager::ERROR_CODE_INVALID_TEXT_SETUP, "the very first text record must be a setup with a color definition.");
			}
		}

		// record this setup
		s = new text_setup_t(f_setup);
		MemAttach(s, sizeof(text_setup_t), "TagText::RecordSetup() -- setup record in text tag");
		f_records.Set(-1, s);

		// reset the flags for next setup
		f_setup.Unused();
	}

	return ErrorManager::ERROR_CODE_NONE;
}



TagText::text_entry_t::text_entry_t(sswf_ucs4_t *string, long advance)
#ifdef _MSVC
	/* this is totally wrong... */
	: text_define_t(sswf::TagText::TEXT_ENTRY_TEXT)
#else
	: text_define_t(TEXT_ENTRY_TEXT)
#endif
{
	assert(string != 0, "a text entry can't accept a null pointer as a string pointer");
	f_exact_length = f_length = wcslen(string);

	//
	// the following assert() is tested later: i.e. multiple strings
	// one after another wouldn't generate an empty text and also
	// the use of spaces only (ie. empty glyphs) makes a string look
	// non-empty but it may still be found alike since we don't
	// insert spaces in the resulting tag...
	//assert(f_length != 0, "a text entry can't accept an empty string");
	//

	f_text = string;
	f_advance = advance;
	f_entries = 0;
}




/** \brief Add a new text string
 *
 * This function is used to add a new string to the text tag.
 *
 * It is possible to specify the exact space to skip between characters.
 * Set the advance parameter to LONG_MIN to avoid that effect and let
 * the system determine the advance size automatically from the font
 * information.
 *
 * \param[in] string The string of text to add to this tag
 * \param[in] advance The space to skip between characters or LONG_MIN
 *
 * \return An error or ErrorManager::ERROR_CODE_NONE
 */
ErrorManager::error_code_t TagText::AddText(const char *string, long advance)
{
	text_entry_t			*e;
	sswf_ucs4_t			*s, *d;
	size_t				l, sz;
	ErrorManager::error_code_t	ec;

	if(f_records.Count() == 0 && !f_setup.IsUsed()) {
		// Ooops... need a setup to have at least a font definition!
		return OnError(ErrorManager::ERROR_CODE_INVALID_TEXT_SETUP, "cannot record the glyph/string \"%s\" without a font definition.\n", string);
	}

	ec = RecordSetup();
	if(ec != ErrorManager::ERROR_CODE_NONE) {
		return ec;
	}

	// NOTE: we allocate strlen() chars in the wide char string
	// because that's much faster than computing the number of
	// chars in a C string than in a UTF-8 string and we won't
	// lose that much RAM anyway
	l = strlen(string);
	d = s = (sswf_ucs4_t *) MemAlloc(sizeof(sswf_ucs4_t) * (l + 1), "TagText::AddText() -- text entry string buffer");
	sz = l * sizeof(sswf_ucs4_t);
	mbtowc(string, l, d, sz);
	*d = (sswf_ucs4_t) '\0';	// mbtowc() doesn't terminate the strings

	e = new text_entry_t(s, advance);
	MemAttach(e, sizeof(text_entry_t), "TagText::AddText() -- string (glyph) entry in text tag");
	f_records.Set(-1, e);

	f_new_text = true;

	return ErrorManager::ERROR_CODE_NONE;
}


/** \brief Set the specified font to the current setup.
 *
 * This function is used to declare the font and its height.
 *
 * \note
 * The setup will be saved only once a string is added.
 * Until then, calling this function multiple times
 * will simply overwrite what the previous call saved.
 *
 * \param[in] font The font to use to render this text.
 * \param[in] height The height used to render this font.
 */
void TagText::AddFont(const TagFont *font, long height)
{
	f_setup.f_font = font;
	f_setup.f_font_height = (unsigned short) height;
	f_setup.f_has_font = font != 0;
}


/** \brief Set the specified offset in the current setup.
 *
 * This function saves the x and y offsets to skip from the
 * current position.
 *
 * \note
 * The setup will be saved only once a string is added.
 * Until then, calling this function multiple times
 * will simply overwrite what the previous call saved.
 *
 * \param[in] x The horizontal movement
 * \param[in] y The vertical movement
 */
void TagText::AddOffsets(long x, long y)
{
	f_setup.f_x = x;
	f_setup.f_has_xoffset = true;

	f_setup.f_y = y;
	f_setup.f_has_yoffset = true;
}


/** \brief Defines the color in the current setup.
 *
 * This function defines the color of the current setup.
 *
 * If the color transparency is not solid (255) then
 * the movie version is forced to version 3.
 *
 * \note
 * The setup will be saved only once a string is added.
 * Until then, calling this function multiple times
 * will simply overwrite what the previous call saved.
 *
 * \param[in] color The RGBA color for the text.
 */
void TagText::AddColor(Color& color)
{
	f_setup.f_color = color;
	f_setup.f_has_color = true;
}



/** \brief Internal function to work on text entries.
 *
 * This function is used by the PreSave() function to make
 * sure that the text is valid and to initialize the different
 * structures so they are ready to be saved in the output file.
 *
 * \param[in] start The start index
 * \param[in] s The setup text to manage
 * \param[in] font The font used to define this text metrics
 * \param[in] font_height The height used to render this font
 *
 * \return The next index
 */
int TagText::DefineText(int start, text_setup_t *s, const TagFont *font, int font_height)
{
	int			idx, max, sub_max, cnt;
	char			wname[16];
	sswf_ucs4_t		*str;
	text_define_t		*l;
	text_entry_t		*e, *entry;
	TagFont::font_info_t	*info;
#if DEBUG
	TagFont::font_info_t	*end_info;
	bool			info_used;
#endif

	// this is supposedly enforced when you AddText()
	assert(font != 0, "can't save a text entry without a font declaration");

	// include all the texts at once so we don't lose
	// any bytes (as few as possible at least)
	cnt = 0;
	max = f_records.Count();
	for(sub_max = start; sub_max < max; sub_max++) {
		l = dynamic_cast<text_define_t *>(f_records.Get(sub_max));
		if(l->f_type != TEXT_ENTRY_TEXT) {
			break;
		}
		cnt += dynamic_cast<text_entry_t *>(l)->f_length;
	}

	// make sure the info array is allocated
	entry = dynamic_cast<text_entry_t *>(f_records.Get(start));

//
// WARNING: because we remove spaces from the characters to include
//	    in the resulting list of infos, the exact length value
//	    is modified and therefore we need to save it here
//	    each time
//
	entry->f_exact_length = cnt;

	// if empty, return right away
	if(cnt == 0) {
		return sub_max;
	}

	info = entry->f_entries;
	if(info == 0) {
		entry->f_entries = info = (TagFont::font_info_t *) MemAlloc(sizeof(TagFont::font_info_t) * cnt,
				"TagText::DefineText() -- array of entries with all the font information");
	}
#if DEBUG
	else {
		/* otherwise the size should ALWAYS be large enough from the previous allocation */
		assert(Size(info) >= sizeof(TagFont::font_info_t) * cnt, "ERROR: TagText::DefineText() -- info buffer already allocated with too small a size\n");
	}
	end_info = info + cnt;
	info_used = false;
#endif

#if 0
	for(idx = 0; idx < cnt; idx++) {
		info->f_glyph = 'A';
		info->f_advance = 20;
		info->f_index = 0;
		info->f_saved_index = 0;
	}
	return sub_max;
#endif

	// get all the info necessary from the font
	// (this also marks the given glyphs as 'in use' in
	// the font - this ensures that if the given font
	// has been modified, then the availability of the
	// glyphs is checked again)
	for(idx = start; idx < sub_max; idx++) {
		e = dynamic_cast<text_entry_t *>(f_records.Get(idx));
		// NOTE: here str may point to an empty string
		//	 but not a null pointer
		str = e->f_text;
//fprintf(stderr, "     PARSE [%s]\n", str);
		while(*str != (sswf_ucs4_t) '\0') {
#if DEBUG
			info_used = true;
#endif
			info->f_glyph = *str;

//printf("Saving glyph for [%c] %04lX\n", str[0] >= ' ' && str[0] <= 0x7E ? str[0] : '?', str[0]);

			if(!font->FindGlyph(*info)) {
				// TODO: do we want a better way to indicate missing characters?
				OnError(ErrorManager::ERROR_CODE_NO_SUCH_GLYPH,
					"the character '%s' does not exist in the font named \"%s\". (4)",
							wcname(info->f_glyph, wname), font->FontName());
				info->f_index = 0;
				info->f_advance = 0;
			}
			else if(info->f_advance != LONG_MIN) {
				info->f_advance = info->f_advance * font_height / 1024;
			}
			// Not only the space character can be an "invisible"
			// glyph!
			//if(info->f_index == TagFont::FONT_SPACE_INDEX)
			if(info->f_is_empty) {
				// an empty glyph (usually a space) actually
				// messes up the previous advance! (we don't
				// need to draw an invisible glyph, do we?)
				// NOTE: yes! a string with only spaces may
				//	 find itself with its exact length == 0!!!
				if(info->f_advance == LONG_MIN) {
					OnError(ErrorManager::ERROR_CODE_INVALID_GLYPH,
						"the character '%s' does not have any advance information in font named \"%s\".",
							wcname(info->f_glyph, wname), font->FontName());
					// use a default
					info->f_advance = 0;
				}
				e->f_exact_length--;
				if(info == e->f_entries) {
					// TODO: ha! in this case the offset has to be changed...
					// it is doable by changing the previous setup entry.
					if(s->f_has_xadjust) {
						s->f_xadjust += info->f_advance;
					}
					else {
						s->f_xadjust = info->f_advance;
						s->f_has_xadjust = true;
					}
				}
				else {
					info[-1].f_advance += info->f_advance;
//fprintf(stderr, "WARNING: info[-1] modified (%p)\n", info - 1);
				}
			}
			else {
				if(e->f_advance != LONG_MIN) {
					info->f_advance = e->f_advance;
				}
				if(info->f_advance == LONG_MIN) {
					OnError(ErrorManager::ERROR_CODE_INVALID_GLYPH,
						"the character '%s' does not have any advance information (font: \"%s\").",
							wcname(info->f_glyph, wname), font->FontName());
					// use a default
					info->f_advance = 0;
				}
				info++;
#if DEBUG
				info_used = false;
#endif
			}
			str++;
		}
	}

#if DEBUG
	assert(info <= end_info - (info_used ? 1 : 0),
			"too many info entries used -- array overflow");
#endif

// the very last character doesn't need an advance -- this
// is important since the user may have a string with many
// spaces at the end and thus a large advance generating a
// large bit size losing bits all over the place!
	if(entry->f_exact_length > 0) {
		assert(info > entry->f_entries, "info pointer not larger than entries but to be used with [-1]");
		if(info > entry->f_entries) {
			info[-1].f_advance = 0;
//fprintf(stderr, "WARNING: info[-1] modified (%p)\n", info - 1);
		}
	}
#if 0
else {
fprintf(stderr, "length of text entry is 0!\n");
}
#endif

	/* this allows us to skip all the texts at once */
	return sub_max;
}



/** \brief Makes the TagText ready for saving.
 *
 * This function ensures that the TagText can be saved (is valid).
 *
 * \return An error code or ErrorManager::ERROR_CODE_NONE
 */
ErrorManager::error_code_t TagText::PreSave(void)
{
	int			idx, max, last_version, font_height;
	text_define_t		*l;
	text_setup_t		*s;
	text_entry_t		*e;
	const TagFont		*font;

	PreSaveCSMTextSettings();

// reset the necessary parameters to proper values
	last_version = f_version = 1;

	max = f_records.Count();
	if(max == 0) {		// empty texts are accepted - not very useful I don't think!
		return ErrorManager::ERROR_CODE_NONE;
	}

/* if we got a new set of texts, free all the current entries at once */
	if(f_new_text) {
		for(idx = 0; idx < max; idx++) {
			l = dynamic_cast<text_define_t *>(f_records.Get(idx));
			if(l->f_type == TEXT_ENTRY_TEXT) {
				e = dynamic_cast<text_entry_t *>(l);
				MemClean(&e->f_entries);
			}
		}
	}

	font = 0;		// the 1st setup should have a font definition
	font_height = 0;	// including a pointer and a height
	s = 0;
	idx = 0;
	while(idx < max) {
		l = dynamic_cast<text_define_t *>(f_records.Get(idx));
		switch(l->f_type) {
		case TEXT_ENTRY_TEXT:
			// NOTE: the DefineText returns idx + 1 or more
			idx = DefineText(idx, s, font, font_height);
			e = dynamic_cast<text_entry_t *>(l);
			if(e->f_exact_length > 0 && last_version > f_version) {
				f_version = last_version;
			}
			break;

		case TEXT_ENTRY_SETUP:
			s = dynamic_cast<text_setup_t *>(l);
			if(s->f_has_font) {
				font = s->f_font;		// current font & height
				font_height = s->f_font_height;
			}
			// if we have several setups in a row, the version
			// required by one may be shadowed by the next
			if(s->f_has_color && !s->f_color.IsSolid()) {
				last_version = 3;
			}
			else {
				last_version = 1;
			}
			idx++;
			break;

		}
	}

	MinimumVersion(f_version);

	return ErrorManager::ERROR_CODE_NONE;
}



/** \brief Save the TagText in a Data buffer
 *
 * This function saves the TagText in the specified Data buffer.
 *
 * \param[in] data The Data buffer where the TagText is saved
 *
 * \return An error code or ErrorManager::ERROR_CODE_NONE
 */
ErrorManager::error_code_t TagText::Save(Data& data)
{
	Data			sub_data, records;
	int			idx, max, cnt, restart, bit_size, x;
	int			had_setup, glyph_bits, advance_bits;
	text_define_t		*l;
	text_setup_t		*s, setup;
	text_entry_t		*e;
	TagFont::font_info_t	*info, glyph_info;
	Color			text_color;

// in the PreSave() we went through the records so we determined
// these few things:
//	1. do we need to use RGBA instead of RGB?
//	2. number of bits for the glyphs
//	3. number of bits for the advances
//
// NOTE: we cannot save anything until we know the exact number of bits
//	 necessary for the glyphs and the advances... thus we have to
//	 loop twice.
//
	glyph_bits = 1;
	advance_bits = 1;

	max = f_records.Count();
	if(max > 0) {		// empty texts are accepted - not very useful I don't think!
		// get the proper indexes for each glyph
		// (i.e. the font may not save all the characters and
		// thus characters indexes are not automatically the
		// glyph index in the final SWF file)
		s = 0;
		setup.f_font = 0;
		for(idx = 0; idx < max; idx++) {
			l = dynamic_cast<text_define_t *>(f_records.Get(idx));
			switch(l->f_type) {
			case TEXT_ENTRY_TEXT:
				assert(s != 0, "this should be cautgh in the PreSave() loop as a \"no font definition as we receive a text entry\"");
				e = dynamic_cast<text_entry_t *>(l);
				info = e->f_entries;
				cnt = e->f_exact_length;
				// NOTE: when multiple texts follow each other, the info pointer
				// of the 2nd, 3rd, etc. is a null pointer; and the f_exact_length
				// is equal to the total of all the strlen() of these entries in
				// the first entry; the others have an f_exact_length of zero
				if(info != 0 && cnt > 0) {
					do {
						glyph_info.f_index = info->f_index;
						setup.f_font->GlyphInfo(glyph_info);
						info->f_saved_index = glyph_info.f_saved_index;
						bit_size = UIBitSize(info->f_saved_index);
						if(bit_size > glyph_bits) {
							glyph_bits = bit_size;
						}
						bit_size = SIBitSize(info->f_advance);
						if(bit_size > advance_bits) {
							advance_bits = bit_size;
						}
						info++;
						cnt--;
					} while(cnt > 0);
				}
				break;

			case TEXT_ENTRY_SETUP:
				s = dynamic_cast<text_setup_t *>(l);
				if(s->f_has_font) {
					setup.f_font = s->f_font;
				}
				break;

			}
		}


		// the current setup has its flags set later
		setup.f_font = 0;
		setup.f_font_height = 0;
		setup.f_x = 0;
		setup.f_y = 0;
		// not sure what the default really is...
		// it seems to be transparent, but in v1.0
		// and v2.0 it couldn't be transparent!!!
		setup.f_color.Set(0, 0, 0, 0);
		s = 0;
		for(idx = 0; idx < max; idx++) {
			l = dynamic_cast<text_define_t *>(f_records.Get(idx));
			switch(l->f_type) {
			case TEXT_ENTRY_TEXT:
				assert(s != 0, "this should be cautgh in the 1st loop as a \"no font definition as we receive a text entry\"");
				e = dynamic_cast<text_entry_t *>(l);
				info = e->f_entries;
				cnt = e->f_exact_length;
				// NOTE: when multiple texts follow each other, the info pointer
				// of the 2nd, 3rd, etc. is a null pointer, however the f_exact_length
				// is equal to the strlen() of that entry.
				if(info != 0 && cnt > 0) {
					had_setup = 1;
					restart = 0;
					do {
						if(restart == 0) {
							if(had_setup == 1) {
								/* define the new setup */
								setup.f_has_xoffset = false;
								if(s->f_has_xoffset) {
									x = s->f_x;
									if(s->f_has_xadjust) {
										/* found some spaces at the start of a string */
										x += s->f_xadjust;
									}
									if(x != setup.f_x) {
										setup.f_x = x;
										setup.f_has_xoffset = true;
									}
								}
								setup.f_has_yoffset = false;
								if(s->f_has_yoffset && s->f_y != setup.f_y) {
									setup.f_y = s->f_y;
									setup.f_has_yoffset = true;
								}
								setup.f_has_font = false;
								if(s->f_has_font && (s->f_font != setup.f_font || s->f_font_height != setup.f_font_height)) {
									setup.f_font = s->f_font;
									setup.f_font_height = s->f_font_height;
									setup.f_has_font = true;
								}
								setup.f_has_color = false;
								if(setup.f_color.IsInvisible()) {
									// in this case always use (0, 0, 0, 0)
									text_color.Set(0, 0, 0, 0);
								}
								else {
									text_color = setup.f_color;
								}
								if(s->f_has_color && s->f_color != setup.f_color) {
									setup.f_color = s->f_color;
									setup.f_has_color = true;
								}
								/* save the flags */
								records.Align();
								records.WriteBits(1, 1);
								records.WriteBits(0, 3);
								records.WriteBits(setup.f_has_font, 1);
								records.WriteBits(setup.f_has_color, 1);
								records.WriteBits(setup.f_has_yoffset, 1);
								records.WriteBits(setup.f_has_xoffset, 1);
								/* save the new setup data */
								if(setup.f_has_font) {
									setup.f_font->SaveID(records);
								}
								if(setup.f_has_color) {
									setup.f_color.Save(records, f_version == 3);
								}
								if(setup.f_has_xoffset) {
									records.PutShort((short) setup.f_x);
								}
								if(setup.f_has_yoffset) {
									records.PutShort((short) setup.f_y);
								}
								if(setup.f_has_font) {
									records.PutShort(setup.f_font_height);
								}
							}
							else {
								/* insert an empty setup */
								records.Align();
								records.WriteBits(1, 1);
								records.WriteBits(0, 7);
							}
							had_setup = 2;
							records.Align();
							records.WriteBits(0, 1);
							records.WriteBits(cnt > 127 ? 127 : cnt, 7);
							restart = 127;
						}
						records.WriteBits(info->f_saved_index, glyph_bits);
						records.WriteBits(info->f_advance, advance_bits);
						setup.f_x += info->f_advance;
						info++;
						restart--;
						cnt--;
					} while(cnt > 0);
				}
				break;

			case TEXT_ENTRY_SETUP:
				s = dynamic_cast<text_setup_t *>(l);
				break;

			}
		}
	}
	records.Align();
	records.WriteBits(0, 8);

// this object ID
	SaveID(sub_data);

// save the bounds & matrix
	f_bounds.Save(sub_data);
	f_matrix.Save(sub_data);

// save the records
	sub_data.PutByte(glyph_bits);
	sub_data.PutByte(advance_bits);
	sub_data.Append(records);

// put a tag and size in front of all of that
	SaveTag(
		data,
		f_version == 3 ? SWF_TAG_DEFINE_TEXT2 : SWF_TAG_DEFINE_TEXT,
		sub_data.ByteSize());

	data.Append(sub_data);

	f_new_text = false;

// if defined, also save the TagCSMTextSettings
	return SaveCSMTextSettings(data);
}




/* The following options fold the documentation; use 'zi' to turn on and off
 *
 * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
 */
