BStore Container msofbtBstoreContainer

The images and pictures in a drawing can be dominate the size of a drawing. Consequently, Escher handles these objects in a special way. As an abstraction, Escher names these objects BLIPs for Big Large Image or Picture. Currently, there are five types of blips supported in Escher: Windows Metafiles, Enhanced Metafiles, JPEG Interchange Format, Portable Network Graphics (PNG) and Macintosh PICT. Other types may be defined in future versions.

Escher stores all the BLIPs in a document in a separate container called the BStore. It reference counts the BLIPs, so that if a picture is inserted multiple times in a document it is only stored once in the BStore but is multiply referenced by different shapes.

The host may choose to store the blip data in a separate delay stream. If a delay stream is used, Escher can incrementally load the blips as they are displayed, not when the document is loaded. (As of Office 97, Word and PowerPoint use a delay stream, and Excel does not.)

The BStore container is just an array of Blip Store Entry (BSE) records. Each shape stores indices into the array for the BLIPs they use. BLIPs are used not only for inserted pictures, but also for the textured and pictures fills of the shape.

BLIP Store Entry Record msofbtBSE

Each BLIP in the BStore is serialized to a File BLIP Store Entry (FBSE) record. The instance field encodes the type of the blip. A fixed size header contains the rest of the common information about the BLIP. If the cbName field in the FBSE is nonzero, a null-terminated Unicode string is written immediately after the FBSE in the file.

// FBSE - File Blip Store Entry
typedef struct _FBSE
   {
   BYTE      btWin32;    // Required type on Win32
   BYTE      btMacOS;    // Required type on Mac
   BYTE      rgbUid[16]; // Identifier of blip
   WORD      tag;        // currently unused
   ULONG     size;       // Blip size in stream
   ULONG     cRef;       // Reference count on the blip
   MSOFO     foDelay;    // File offset in the delay stream
   BYTE      usage;      // How this blip is used (MSOBLIPUSAGE)
   BYTE      cbName;     // length of the blip name
   BYTE      unused2;    // for the future
   BYTE      unused3;    // for the future
   } FBSE;

typedef enum
   {
   msoblipUsageDefault,  // All non-texture fill blips get this.
   msoblipUsageTexture,
   msoblipUsageMax = 255 // Since this is stored in a byte
   } MSOBLIPUSAGE;

typedef enum
   {                          // GEL provided types...
   msoblipERROR = 0,          // An error occured during loading
   msoblipUNKNOWN,            // An unknown blip type
   msoblipEMF,                // Windows Enhanced Metafile
   msoblipWMF,                // Windows Metafile
   msoblipPICT,               // Macintosh PICT
   msoblipJPEG,               // JFIF
   msoblipPNG,                // PNG
   msoblipDIB,                // Windows DIB
   msoblipFirstClient = 32,   // First client defined blip type
   msoblipLastClient  = 255   // Last client defined blip type
   } MSOBLIPTYPE;

typedef enum
   {
   msobiUNKNOWN = 0,
   msobiWMF  = 0x216,      // Metafile header then compressed WMF
   msobiEMF  = 0x3D4,      // Metafile header then compressed EMF
   msobiPICT = 0x542,      // Metafile header then compressed PICT
   msobiPNG  = 0x6E0,      // One byte tag then PNG data
   msobiJFIF = 0x46A,      // One byte tag then JFIF data
   msobiJPEG = msobiJFIF,
   msobiDIB  = 0x7A8,      // One byte tag then DIB data
   msobiClient=0x800,      // Clients should set this bit
   }
MSOBI;                     // Blip signature as encoded in the MSOFBH.inst

The btWin32 and btMacOS fields store the MSOBLIPTYPE for the respective operating systems. When the OS blip type doesn't match the blip type of stored, Escher will attempt to convert the blip. For example, a PICT will be stored as a msoblipPICT with a btWin32 field of msoblipWMF and a btMacOS field of msoblipPICT. When the PICT blip is loaded on Windows, the stored field will not match the OS field, so PICTtoWMF filter will be called to create a msoblipWMF BLIP.

A few additional facts are worth noting. Clients can define their own BLIP types. When loading client defined blip types Escher calls the clients to load the blips. Each BSE contains a 16-byte checksum that is used to quickly compare a BLIP with other BLIPs in the store. Any algorithm could be used for this checksum. Escher uses the RSA Data Security, Inc. MD4 Message-Digest Algorithm for the checksums of its BLIP types. Finally, the cRef field can be 0, indicating an empty slot in the BStore.

If a delay stream is not used, then the BLIP data follows the BSE header in a separate record. The FBT of the BLIP record is the MSOBLIPTYPE plus msofbtBlipFirst (0xF018). (If a delay stream is being used, the BLIP's record header and data are both written there instead.)

Metafile/PICT Blips

Those blips have one of the following signatures: msobiEMF, msobiWMF, or msobiPICT. They are normally stored in a compressed format using the LZ compression algorithm in the format used by GNU Zip deflate/inflate with a 32k window. The format is zlib format . The only metafile compression version number currently defined identifies this format and is analogous to the PNG compression type value in the PNG file format. The filter values (MSOBLIPFILTER) define pre-filtering of metafile data to give better compression. Currently no pre-filtering is done (it is likely that filtering on a per-record basis will give substantially better compression in the future).

However, if there is an exception due to out-of-memory or out-of-disk space when saving those blips, the compression operation is skipped and the blips are then saved in a non-compressed format- in this case the compressed bits are simply the original metafile data.. When the blips are loaded back in memory, a check is performed based on a "compression status" flag (MSOBLIPCOMPRESSION) that follows the blip header encoded as follows:

typedef enum
   {
   msocompressionDeflate = 0,
   msocompressionNone = 254,    // Used only if compression fails
   msocompressionTest = 255,    // For testing only
   }
MSOBLIPCOMPRESSION;

typedef enum
   {
   msofilterAdaptive = 0,       // PNG type - not used/supported for metafile
   msofilterNone = 254,
   msofilterTest = 255,         // For testing only
   }
MSOBLIPFILTER;

/* The secondary, or data, UID - should always be set. */
BYTE  m_rgbUid[16];
/* The primary UID - this defaults to 0, in which case the primary ID is
   that of the internal data. NOTE!: The primary UID is only saved to disk
   if (blip_instance ^ blip_signature == 1). Blip_instance is MSOFBH.inst and 
   blip_signature is one of the values defined in MSOBI */
BYTE  m_rgbUidPrimary[16]; /   / optional based on the above check

/* Metafile Blip overhead = 34 bytes. m_cb gives the number of
   bytes required to store an uncompressed version of the file, m_cbSave
   is the compressed size.  m_mfBounds gives the boundary of all the
   drawing calls within the metafile (this may just be the bounding box
   or it may allow some whitespace, for a WMF this comes from the
   SetWindowOrg and SetWindowExt records of the metafile). */
int           m_cb;           // Cache of the metafile size
RECT          m_rcBounds;     // Boundary of metafile drawing commands
POINT         m_ptSize;       // Size of metafile in EMUs
int           m_cbSave;       // Cache of saved size (size of m_pvBits)
BYTE          m_fCompression; // MSOBLIPCOMPRESSION
BYTE          m_fFilter;      // always msofilterNone
void         *m_pvBits;       // Compressed bits of metafile.
Bitmap Blips

Those blips have one of the following signatures: msobiJPEG, msobiPNG, or msobiDIB. They have the same UID header as described in the Metafile Blip case. The data after the header is just a single BYTE "tag" value and is followed by the compressed data of the bitmap in the relevant format (JFIF or PNG, bytes as would be stored in a file). For the msobiDIB format, the data is in the standard DIB format as a BITMAPINFOHEADER, BITMAPCOREHEADER or BITMAPV4HEADER followed by the color map (DIB_RGB_COLORS) and the bits. This data is not compressed (the format is used for very small DIB bitmaps only).

To determine where the bits are located, refer to the following header:

/* The secondary, or data, UID - should always be set. */
BYTE  m_rgbUid[16];
/* The primary UID - this defaults to 0, in which case the primary ID is
   that of the internal data. NOTE!: The primary UID is only saved to disk
   if (blip_instance ^ blip_signature == 1). Blip_instance is MSOFBH.finst and 
   blip_signature is one of the values defined in MSOBI*/
BYTE  m_rgbUidPrimary[16];    // optional based on the above check
BYTE  m_bTag;            
void  *m_pvBits;              // raster bits of the blip.