The following license covers the entire SSWF project.
Copyright (c) 2002-2009 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.
- Brief History
At the very beginning, a company created the SWF format to generate
small vector animations on the Internet. It also included images.
This company was bought by Macromedia around 1997 (if I recall properly).
This is when Flash v3 was created. Since then, Macromedia created a new
version about once a year up to version 8. At that time (in 2005/2006),
Macromedia sealed a deal with Adobe which wanted to use the SWF format
in their PDF files.
Today (May 1st, 2008), the SWF format is available for free to all.
It can be downloaded from the Adobe web pages as follow:
There is also a new project called
Open Screen Project
that will help everyone to get access to the Flash craze. That should help
and plus Adobe is releasing many information and making more and more things
free. That rocks, in a way.
- What is SWF?
SWF (pronounced like swiff by some, but really is is S, W, F)
is a file format used to describe movies built of mainly two graphical elements:
vector based objects and images.
The newest versions also accept external modules, sound, video and interaction with the
end user using ActionScript.
The file format was first created by a small company that Macromedia
purchased early on.
The main goal of the format was and still is to create small files of
highly intertaining animations.
The idea was to have a format which could be reused by a player running
on any system and which would work with slower network (such as
a browser connected to the Internet with a slow modem).
The format is fairly simple also.
This document presents the SWF format and includes code examples
for really difficult points (like bit fields) and it explains with words
what is really not clear otherwise.
I hope this document will help you in developing your own
players and/or generators of SWF file formats.
- The geometry in SWF
The SWF file formats uses several types of objects.
The ones used the most are called shapes. These are vector
based objects which can be rendered really fast in 2D.
The other type of graphical objects are images, fonts,
colors and matrices. More information about the SWF
geometry is given in the Appendix A below.
In different versions of SWF they also added different
graphical enhancements.
In version 6, they added support (somewhat flaky, fixed
in version 7) for internationalization.
In version 7 they added much better support for small fonts.
In version 8 they added support for transparent videos.
You can see the evolution by looking at the different tags
and the tag structures (many times, a tag was enhanced in
a version without the need to create a new tag.)
- Multimedia content in SWF
The SWF file format has evolved to support more and more multimedia
formats. It started with 2 audio formats (raw uncompressed and ADPCM)
and it now supports many audio and video formats.
Because multimedia files tend to be large, the SWF format was also
enhanced to allow you to load separate multimedia files as required.
This is done using the FLV files. These files can also include
scripts.
- Interactivity support in SWF
At the very beginning, SWF was only for animations. You started it,
it played a loop forever until you'd move on to another web page.
In version 3, better support for keyboard and mouse clicks was added.
This was rough and didn't offer much possibilities beyond a simple switch
(i.e. if you click start playing B instead of A). Since version 4,
Macromedia added support for a scripting language. This is very
similar to what Sun has done with Java. This is an interpreted language
which runs within the Flash player in its own environment.
Real interactivity came with version 4, but real scripting came only
in version 5. That is, since version 5 you got real objects. At that time
Macromedia decided to be more compliant with what ECMAScript described
in their specification. Yet, they used the free Netscape interpreter
available (if I'm correct) in Netscape 4. This was pretty bogus. They
kept trying to enhance that interpretor until version 7. In version 8
they finally did a full rewrite (or did they get a new free interpreter
from somewhere?) to really support ECMAScript properly.
This means there are some inconsistencies between older versions and
version 8 in the scripting language, even though the encoding is the
same, running your older scripts may fail in version 8. So don't be too
surprised!
With version 8, they also very much improved their library coming with
their Flash builder product.
In the table below, the tags are sorted by version and numbers.
To find a tag by name, use the list under Descriptions of each tag
in the TABLE OF CONTENT.
The file header is found at the very beginning of the file.
It should be used to determine whether a file is an SWF
file or not. Also, it contains information about the frame
size, the speed at which is should be played and the version
(which determines which tags and actions are possibly used
in the file).
struct swf_header {
unsigned char f_magic[3]; 'FWS' or 'CWS'
unsigned char f_version;
unsigned long f_file_length;
}
struct swf_header_movie {
swf_rect f_frame_size;
unsigned short fixed f_frame_rate;
unsigned short f_frame_count;
};
The f_magic[3] array is defined as the characters: 'FWS' (it is going backward
probably because it was supposed to be read in a little endian as a long word).
A movie can be compressed when the version is set to 6. In this case, the
magic characters are: 'CWS'.
The f_version is a value from 1 to 6 (the maximum at time of writing,
the maximum will continue to increase).
The f_file_length is exactly what it says. That's useful for all these
network connections which don't give you the size of the file. In case of a
compressed movie, this is the total length of the uncompressed movie
(useful to allocate the destination buffer for zlib).
The f_frame_size is a rectangle definition in TWIPS used to set the
size of the frame on the screen. The minx and miny are usually not necessary
in this rectangle definition.
The parameter in the swf_header_movie structure are part of the
buffer which gets compressed in a V6.x movie (in other words, only the very
first 8 bytes of the resulting file aren't compressed).
The f_frame_rate is a fixed value of 8.8 bits. It represents the number
of frames per second the movie should be played at. Since version 8 of SWF, it
is defined as an unsigned short fixed point value instead of an
unsigned short. This value should never be set to zero.
The f_frame_count is a counter of the number of SHOW FRAME within that
movie. Most of the tools will compute this number automatically and it can
usually be wrong and the movie will still play just fine.
CSMTextSettings (V8.0)
The CSMTextSettings are used to change the
rendering mode of glyphs in a
DefineText,
DefineText2 and
DefineEditText
struct swf_csmtextsettings {
swf_tag f_tag; /* 74 */
unsigned short f_text_id_ref;
unsigned f_use_flag_type : 2;
unsigned f_grid_fit : 3;
unsigned f_reserved : 3;
long float f_thickness;
long float f_sharpness;
unsigned char f_reserved;
};
The f_text_id_ref is a reference to a tag holding some
texts which glyphs need to be tweaked with these settings.
The f_use_flag_type defines which of the system (0) or Flash (1)
font renderer should be used.
| Value |
Renderer |
Version |
| 0 |
System |
8 |
| 1 |
Internal Flash Type |
8 |
|
The f_grid_fit defines whether the glyphs should be moved
to fit on a grid (i.e. to look less blurry.)
| Value |
Mode |
Version |
| 0 |
No alignment |
8 |
| 1 |
Pixel alignment (for left aligned text only — go figure!) |
8 |
| 2 |
1/3rd pixel for LCD displays |
8 |
|
The f_thickness and f_sharpness are used to compute
the external and internal cutoff. According to Macromedia they
compute these values as follow:
External Cutoff = ( 0.5 × f_sharpness - f_thickness) × f_font_height
Internal Cutoff = (-0.5 × f_sharpness - f_thickness) × f_font_height
DebugID (V9.0?)
The DebugID tag is used to match a debug file
(.swd) with a Flash animation (.swf). This is used by the Flash
environment and is not required to create movies otherwise.
struct swf_debugid {
swf_tag f_tag; /* 63 */
unsigned char f_uuid[<variable size>];
};
The f_uuid is a universally unique identifier. The size should be 128 bytes.
It is otherwise defined by the size of the tag.
DefineBinaryData (V9.0)
The DefineBinaryData tag is used to save any arbitrary user
defined binary data in an SWF movie. The Flash player itself ignores that data.
The size of the data is not specifically limited.
struct swf_definebinarydata {
swf_tag f_tag; /* 87 */
unsigned short f_data_id;
unsigned long f_reserved; /* must be zero */
unsigned char f_data[<variable size>];
};
The f_data_id is this object identifier. The identifier is the same type
as any identifier (like a sprite identifier.) It is used in ActionScripts to
reference the data.
The f_reversed area is 32 bits and it must be set to zero in version 9.
The size of the f_data buffer is defined as the size of the tag minus the
f_data_id and f_reserved fields. This is where the raw binary data goes.
DefineBitsJPEG (V1.0)
DefineBitsJPEG2 (V2.0)
DefineBitsJPEG3 (V3.0)
DefineBitsJPEG4 (V10.0)
These tags define an image saved using the JPEG compression scheme.
DefineBitsJPEG (V1.0) does not include the encoding
tables which are defined in the unique
JPEGTables tag
instead. All the DefineBitsJPEG of an SWF file use
the only JPEGTables tag.
The other tags incorporate their own version of the JPEG encoding tables.
The DefineBitsJPEG3 also supports an alpha channel
bitplane (8 bits). This alpha channel is compressed using the ZLIB
scheme as with the
DefineBitsLossless
image formats.
With Flash 10, DefineBitsJPEG4 was introduced to
support a deblocking filter parameter. This parameter should be set to a
value between 0.0 and 1.0 (0x0000 and 0x0100--so really a value from 0 to 256
inclusive.)
WARNING: These tags require you to save
the swf_tag in long format (i.e. f_tag_and_size & 0x3F == 0x3F even
if the size is smaller.)
struct swf_definebitsjpeg {
swf_tag f_tag; /* 6, 21, 35 or 90 */
unsigned short f_image_id;
if(f_tag == DefineBitsJPEG3) {
/* sizeof(f_encoding_tables) + sizeof(f_image_data) */
unsigned long f_offset_to_alpha;
}
if(f_tag == DefineBitsJPEG4) {
unsigned short fixed f_deblocking_filter_parameter;
}
if(f_tag != DefineBitsJPEG) {
/* when DefineBitsJPEG, use JPEGTables instead */
unsigned char f_encoding_tables[<variable size>];
}
unsigned char f_image_data[<variable size>];
if(f_tag == DefineBitsJPEG3) {
unsigned char f_alpha[<variable size>];
}
};
The f_encoding should include 0xFF 0xDB and 0xFF 0xC4 entries.
The f_image_data buffer should include the 0xFF 0xE0, 0xFF 0xC0
and 0xFF 0xDA.
Since Flash 10 the f_encoding and f_image_data fields
defined in the DefineJPEG2,
DefineJPEG3 and
DefineJPEG4 tags, are viewed as one single
large buffer and it can be a verbatim JPEG, PNG or GIF89a file.
When the buffer represents a JPEG, it starts with 0xFF 0xD8 and ends
with 0xFF 0xD9.
When the buffer represents a PNG, it starts with
0x89 0x50 'P' 0x4E 'N' 0x47 'G' 0x0D '\r' 0x0A '\n' 0x1A '^Z' 0x0A '\n'.
When the buffer represents a GIF89a, it starts with
0x47 'G' 0x49 'I' 0x46 'F' 0x38 '8' 0x39 '9' 0x61 'a'.
WARNING: Up to Flash 7, both buffers (f_encoding
and f_image_data) need to start with a 0xFF 0xD8 (SOI) and end
with 0xFF 0xD9 (EOI). Since Flash 8, this practice should not be used
anymore.
The f_alpha buffer is compressed with ZLIB as defined in the
DefineBitsLossless
tag (this is similar to the PNG format). WARNING:
this field only works with JPEG data. A PNG or GIF89a cannot make use of
this field (but they can make use of their own alpha channel.)
| Note: |
|
The Flash 10 documentation says that the f_alpha field is
optional. This means you can save a JPEG in a DefineImageJPEG4
without the Alpha Channel but still make use of the deblocking filter parameter.
Before Flash 10, use DefineImageJPEG2 instead. |
Also, the DefineBitsJPEG tag may fail if it includes
any encoding tables. These tables shall be defined within the
JPEGTables
instead.
Note that the SWF player better enforces the correctness of these tags
since version 8.
DefineBitsLossless (V2.0)
DefineBitsLossless2 (V3.0)
These tags declares a loss-less image bitmap. It has a small header
followed by an optional colormap and the bitmap data. When we have
a colormap, the bitmap data is an array of indices in the colormap
which is aligned to 32 bits on a per row basis.
There are three supported formats:
Format No. (bits) |
Color Format |
Comments |
Without Alpha |
With Alpha |
3 (8 bits(1)) |
RGB |
RGBA |
Uses a colormap with up to 256 entries of 24 or 32 bits colors |
4 (16 bits(1)) |
RGB555 |
RGB555 |
There is no alpha available in this format. The data is saved in
big endian (it is NOT a U16 like some doc. mention). The colors looks like
this (most significant bit first): 0RRRRRGGGGGBBBBB. You should certainly
always use the DefineBitsLossless tag for this format. |
5 (32 bits) |
XRGB |
ARGB |
Uses a strange scheme for colors. Most probably because the alpha was
added later and thus inserted in place of the X to keep some backward
compatibility with older versions. |
(1) the data must be 32 bits aligned (4 bytes) on a per row basis. In 8 bits,
you may have to add up to three bytes at the end of each row (
4 - width & 3 when
width & 3 is not zero.).
In 16 bits, you need to add two bytes at the end of each row
when the width of the image is odd.
The f_colormap, f_indices and f_bitmap are all compressed with the
ZLIB scheme.
WATCH OUT: the f_colormap and f_indices are
compressed as one large block.
WARNING: These tags require you to save
the swf_tag in long format (i.e. f_tag_and_size & 0x3F == 0x3F even
if the size is smaller.)
WARNING: An image cannot always be scaled
more than 64×. Trying to enlarge it more may result in a
rectangle of one color. The 64× is cumulative. So a sprite
of an image × 3 inside another sprite × 10 inside
another sprite × 4 results in scaling of 120 and this is
likely to break the image. This seems to be true mainly
when there is a rotation or skew.
struct swf_definebitslossless {
swf_tag f_tag; /* 20 or 36 */
unsigned short f_image_id;
unsigned char f_format; /* 3, 4 or 5 */
unsigned short f_width;
unsigned short f_height;
if(f_format == 3) {
unsigned char f_colormap_count;
if(f_tag == DefineBitsLossless) {
swf_rgb f_colormap[f_colormap_count];
}
else {
swf_rgba f_colormap[f_colormap_count];
}
unsigned char f_indices[((f_width + 3) & -4) * f_height];
}
else {
if(f_tag == DefineBitsLossless) {
swf_xrgb f_bitmap[f_width * f_height];
}
else {
swf_argb f_bitmap[f_width * f_height];
}
}
};
DefineButton (V1.0)
The interactivity of the SWF format comes from the buttons. All
the buttons have an ID and can be placed in the display list like
any other shape.
A buttons has different states. Some states can be entered only
when the button was in a specific state before (like a button
being pushed).
Buttons can be represented graphically in any manner you want.
Each state can use a different edit text, shape, sprite or text
to render the button.
struct swf_definebutton {
swf_tag f_tag; /* 7 */
unsigned short f_button_id;
swf_button f_buttons;
swf_action f_actions;
};
The f_buttons and f_actions are null terminated arrays
(the end marker in either case is a byte set to zero).
There will always be at least one f_buttons since the object
require at least one shape to be drawn (though the shape can very well be
transparent and empty).
There is no need for any action. The action(s) are executed whenever the
button is pushed. Note that it is possible to execute actions also when
the mouse moves over a button (in, out, over) with the use of a sprite
in V5.0+. However, in this case it is certainly preferable to use a
DefineButton2 instead.
DefineButton2 (V3.0)
The DefineButton2 is very similar to the DefineButton tag.
The list of actions was however changed in a list of actions to execute
on a condition. Whenever an event occur, the plugin checks for that
condition within all the buttons which can possibly catch that event at
the time. For all the matches it finds, the corresponding actions are
executed.
struct swf_definebutton2 {
swf_tag f_tag; /* 34 */
unsigned short f_button_id;
unsigned f_reserved : 7;
unsigned f_menu : 1;
unsigned short f_buttons_size;
swf_button f_buttons;
swf_condition f_conditions;
};
The f_buttons_size is equal to the size of the f_buttons buffer
plus 2 (the size of the f_buttons_size field itself). Note
however that if you don't have any conditions, the f_buttons_size
field will be zero (0). This is similar to the list of conditions which
also ends with a condition having a size of zero (0). You can still
deduce the size of the f_buttons when the f_button_size
is zero by using the total tag size minus the offset where the
f_buttons declarations start.
DefineButtonCxform (V2.0)
The DefineButton doesn't include any means to transform the
colors of the shapes it includes. This tag was thus added just so
one can transform a button colors. It is wise to use the new
DefineButton2 instead so the transformation can be applied
on a per state basis.
struct swf_definebuttoncxform {
swf_tag f_tag; /* 23 */
unsigned short f_button_id_ref;
swf_color_transform f_color_transform;
};
The f_button_id_ref is a reference to the button which is to be
transformed with the specified color matrix.
DefineButtonSound (V2.0)
The DefineButtonSound can be used to emit a sound when an event
occur on the specified button. It is likely better to use sprites that
you display using actions than to use this tag. You will have access
to more events and conditions and also can avoid sound effects on the
conditions included in this tag.
enum {
DEFINE_BUTTON_SOUND_CONDITION_POINTER_LEAVE = 0,
DEFINE_BUTTON_SOUND_CONDITION_POINTER_ENTER = 1,
DEFINE_BUTTON_SOUND_CONDITION_POINTER_PUSH = 2,
DEFINE_BUTTON_SOUND_CONDITION_POINTER_RELEASE_INSIDE = 3,
DEFINE_BUTTON_SOUND_CONDITION_MAX = 4
};
struct swf_definebuttonsound {
swf_tag f_tag; /* 17 */
unsigned short f_button_id_ref;
swf_soundinfo f_button_sound_condition[DEFINE_BUTTON_SOUND_CONDITION_MAX];
};
The f_button_id_ref is a reference to the button which will get the
given sound effects.
There are four f_button_sound_condition. Each have a reference to
a sound and some information on how to play it. The four conditions
are given in the enumeration preceeding the DefineButtonSound
structure.
DefineEditText (V4.0)
Additional interactivity has been added in V4.0 of the SWF format.
This is given by the use of edit boxes which offer the end users
a way to enter new text as if the SWF movie was in fact an
interactive form.
The text is defined in a variable (accessible in action scripts).
It can be dynamically assigned and retreived. It is legal to have
an empty string as the variable name (not dynamically accessible).
Since version 8, the text drawn by a DefineEditText
tag can be tweaked by adding a
CSMTextSettings tag.
struct swf_defineedittext {
swf_tag f_tag; /* 37 */
unsigned short f_edit_id;
swf_rect f_rect;
unsigned f_edit_has_text : 1;
unsigned f_edit_word_wrap : 1;
unsigned f_edit_multiline : 1;
unsigned f_edit_password : 1;
unsigned f_edit_readonly : 1;
unsigned f_edit_has_color : 1;
unsigned f_edit_has_max_length : 1;
unsigned f_edit_has_font : 1;
if(version >= 6) {
unsigned f_edit_reserved : 1;
unsigned f_edit_auto_size : 1;
}
else {
unsigned f_edit_reserved : 2;
}
unsigned f_edit_has_layout : 1;
unsigned f_edit_no_select : 1;
unsigned f_edit_border : 1;
unsigned f_edit_reserved : 1;
unsigned f_edit_html : 1;
unsigned f_edit_use_outlines : 1;
if(f_edit_has_font) {
unsigned short f_edit_font_id_ref;
unsigned short f_edit_font_height;
}
if(f_edit_has_color) {
swf_rgba f_edit_color;
}
if(f_edit_has_max_length) {
unsigned short f_edit_max_length;
}
if(f_edit_has_layout) {
unsigned char f_edit_align;
unsigned short f_edit_left_margin;
unsigned short f_edit_right_margin;
signed short f_edit_indent;
signed short f_edit_leading;
}
string f_edit_variable_name;
if(f_edit_has_text) {
string f_edit_initial_text;
}
};
The f_edit_word_wrap flag will be set to true (1) in order to
have words which go beyond the right side of the box to appear
on the next line instead. This only works if you have the
f_edit_multiline flag also set to true.
The f_edit_multiline flag can be used to create an edit text field
which accepts new lines and can wrap words.
The f_edit_readonly flag ensure that the end user can't modify
the text in the edit area.
The f_edit_has_color & f_edit_color will be used to
indicate the color of the text. Note that it is possible to ask for a
border and a background to be drawn (see the f_edit_border flag
below) but these items colors can't be defined.
The f_edit_has_max_length & f_edit_max_length can
be used to ensure the user can't type more than a certain number
of letters and digits.
The f_edit_password flag is used to visually transform the
typed characters to asterisks. The edit text field variable has the
proper typed characters.
The f_edit_border will be used to not only draw a border, but also have
a white background. Make sure you don't select a white color for your
font or you won't see any text in this case. The color of the border
is likely to be black. If you want to have better control of these
colors you will have to draw your own background and borders.
The f_edit_auto_size flag requests the player to automatically
resize the object to the text. Thus, you don't need to know the size
of the text at the time you create an edit text, plus different fonts
from different platforms will always fit the edit text (but maybe not
the screen...).
The f_edit_use_outlines flag will be used to tell
whether the specified SWF internal font should be used.
When not set, a default font is choosen by the plugin.
The font should include a mapping so it can be drawn
properly.
The f_edit_align can be set to the following values:
| Alignment |
Value |
| Left |
0x00 |
| Right |
0x01 |
| Center |
0x02 |
| Justify(1) |
0x03 |
|
(1) justification doesn't seem to work yet.
The f_edit_indent is the first line indentation in a multiline box of
text. This is added to the left margin.
The f_edit_leading is the number of extra pixels to skip
to reach the following line. It should be put to zero to have the default
font leading value.
The f_edit_left/right_margin indicate how many TWIPS to not use on
the sides. If you don't use a border, these are rather useless.
The f_edit_html flag, when set, means the contents of this edit
text is a basic HTML text. The following table shows you the tags
which the Macromedia plugin understands.
| Tag |
Accepted Attributes |
Comments |
| Open | Close |
<A> |
</A> |
HREF=url
[ TARGET=name ] |
Defines an hyperlink |
<B> |
</B> |
none |
Write in bold |
<BR> |
n.a. |
none |
Inserts a line break |
<FONT> |
</FONT> |
[ FACE=name ]
[ SIZE=[+|-][0-9]+ ]
[ COLOR=#RRGGBB ] |
Change the font face. The face name must
match a DefineFont2 name. The size is in TWIPS.
The color only supports #RRGGBB triplets. |
<I> |
</I> |
none |
Write in italic |
<LI> |
</LI> |
none |
Defines a list item |
<P> |
</P> |
[ ALIGN=left|right|center ] |
Defines a paragraph |
<TAB> |
n.a. |
none |
Inserts a tab character (see TEXTFORMAT also) |
<TEXTFORMAT> |
</TEXTFORMAT> |
[ BLOCKINDENT=[0-9]+ ]
[ INDENT=[0-9]+ ]
[ LEADING=[0-9]+ ]
[ LEFTMARGIN=[0-9]+ ]
[ RIGHTMARGIN=[0-9]+ ]
[ TABSTOPS=[0-9]+{,[0-9]+} ] |
Change the different parameters as indicated.
The sizes are all in TWIPs. There can be multiple positions
for the tab stops. These are seperated by commas. |
<U> |
</U> |
none |
Write with an underline |
|
For more information about HTML, please, refer to a full HTML
documentation. You can find the complete specification
at http://www.w3.org/. It
was written by the MIT, INRIA and Keio and that's very well
written!
|
WARNING:
|
There seems to be a problem with the use of a system font when
that font doesn't exist on your system. At this time I do not
know if it only happens with this object or whether others
would also be affected. Anyway, when it happens you get
nothing in the text area.
|
DefineFont (V1.0)
It is common to use the DefineFont tag in order to create an array of
shapes later re-used to draw strings of text on the screen. Note that the
definition of the shape within a font is limited since it can't include any
specific fill and/or line style. Also, each shape is assumed to be defined
within a 1024x1024 square. This square is called the EM Square.
Fig 1. below shows you the EM Square and how it is used. The
characters baseline can be placed anywhere within the EM Square (it certainly
can be outside too if you wish?!?). The baseline is the position where the
Y coordinate of the font is set to 0. The characters have to
be drawn over that line to be properly defined. Only letters such as g, j, p
and q will have a part drawn below. This means all the main characters will
use negative Y coordinates. The Y coordinates increase
from top to bottom (opposite the TrueType fonts and possibly others too).
The width gives the number of TWIPs between this character and the next to
be drawn on the right. The drawing should not go outside the EM Square
(what happens in this case is not specified, it is likely that what
is drawn outside will be lost but it can have some side effects too).
Though it is possible to define a font which draws from right to left (such
as an Arabic or Farsi font), it may cause problems (I didn't try yet...)
Fig 1. Font EM Square
With SSWF, you can see the EM Square of a character adding
this code in your glyph definition (where <descent>
is the descent value as saved in the layout of the font):
glyph "test" {
...
move: 0, -<descent>;
points { 0, 1024; 1024, 1024; 1024, 0; 0, 0; };
...
};
The font structure defines the font ID (which is common with a corresponding
DefineFontInfo) an array of offsets and an
array of glyphs. Note that if a DefineFontInfo tag is to be saved,
you need to have the glyphs ordered in ascending order ('a' before 'b',
etc.) This is important for the definition of the map present in the
DefineFontInfo.
You must use a DefineFont2 if a
DefineEditText references a font.
It will either fail or crash a plugin if you use this font definition
instead.
Note that an embedded font can be rotated. A system font (also called a device font)
cannot be rotated. Also, the scaling and translation of a system font does not
always respect the exact position. It is likely that the font will be moved to
the next pixel left or right to avoid bluriness. That means it will look quite
jaggy if you try to have a quite smooth move.
struct swf_definefont {
swf_tag f_tag; /* 10 */
unsigned short f_font_id;
/* there is always at least one glyph */
f_font_glyphs_count = f_font_offsets[0] / 2;
unsigned short f_font_offsets[f_font_glyphs_count];
swf_shape f_font_shapes[f_font_glyphs_count];
};
The f_offsets array is a list of byte offsets given from the beginning of
the f_offsets array itself to the beginning of the corresponding shape.
(If it were possible to write such structure in C, then ...) In C one
would write the following to find the shape in the font tag:
struct swf_definefont *df;
df = ...
character67 = (struct swf_shape *) ((char *) df->f_offsets + df->f_offsets[67]);
DefineFont2 (V3.0)
DefineFont3 (V8.0)
It is common to use the DefineFont2 tag in order to create an array of
shapes later re-used to draw strings of text on the screen. This tag must
be used whenever a DefineEditText
references a font; and in that case it is suggested you include a full description
of the font with layouts.
The array of glyphs must be ordered in ascending order (the smaller glyph number
saved first; thus 'a' must be saved before 'b', etc.).
All the characters should be defined in a 1024x1024 square (in pixels) to be
drawn with the best possible quality. This square is called the EM square.
The DefineFont3 tag has the exact same definition as
the DefineFont2 tag. The difference lies in the shapes
being referenced. These have a precision 20 times higher. This gives you a
font with that much higher precision (each pixel can be divided in a 400
sub-pixels.) The other difference is that a DefineFont3
can be referenced by a
DefineFontAlignZones
tag. That one can be used to properly align characters on a pixel boundary.
Note that an embedded font can be rotated. A system font (also called a device font)
cannot be rotated. Also, the scaling and translation of a system font does not
always respect the exact position. It is likely that the font will be moved to
the next pixel left or right to avoid bluriness. That means it will look quite
jaggy if you try to have a quite smooth move.
struct swf_definefont2 {
swf_tag f_tag; /* 48 or 75 */
unsigned short f_font2_id;
unsigned f_font2_has_layout : 1;
if(version >= 6) {
unsigned f_font2_reserved : 1;
if(version >= 7) {
unsigned f_font2_small_text : 1;
}
unsigned f_font2_reserved : 1;
}
else {
unsigned f_font2_shiftjis : 1;
unsigned f_font2_unicode : 1;
unsigned f_font2_ansii : 1;
}
unsigned f_font2_wide_offsets : 1;
unsigned f_font2_wide : 1; /* always 1 in v6.x+ */
unsigned f_font2_italic : 1;
unsigned f_font2_bold : 1;
if(version >= 6) {
unsigned char f_font2_language;
}
else {
unsigned char f_font2_reserved;
}
unsigned char f_font2_name_length;
unsigned char f_font2_name[f_font2_name_length];
unsigned short f_font2_glyphs_count;
if(f_font2_wide_offsets) {
unsigned long f_font2_offsets[f_font2_glyphs_count];
unsigned long f_font2_map_offset;
}
else {
unsigned short f_font2_offsets[f_font2_glyphs_count];
unsigned short f_font2_map_offset;
}
swf_shape