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.
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.)
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.
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.