/**
  @file upmf.c
  
  @brief Functions for manipulating EMF+ files and structures.

  EMF+ is much more object based than is EMF or WMF, so the U_PMR_*_set and most U_PMF_*_set functions
  return a pointer to a PseudoObject.  PseudoObjects are structs that contain a data field to hold the
  object in EMF+ file byte order, size information, and some type information.  This is sufficient to allow
  complex records to be built up from the various sorts of nested objects which they normally contain.
  If something goes wrong a NULL pointer is returned and recsize is set to 0.
  
  EMF+ does not use a separate set of endian functions, _get and _set routines convert from/to
  the EMF+ file byte order on the fly.
    
        WARNING:  Microsoft's EMF+ documentation is little-endian for everything EXCEPT
        bitfields,  which are big-endian.   See EMF+ manual section 1.3.2              
        That documentation also uses 0 as the MOST significant bit, N-1 as the least.  
        This code is little-endian throughout, and 0 is the LEAST significant bit      
  
*/

/*
File:      upmf.c
Version:   0.0.13
Date:      21-MAR-2019
Author:    David Mathog, Biology Division, Caltech
email:     mathog@caltech.edu
Copyright: 2019 David Mathog and California Institute of Technology (Caltech)
*/

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iconv.h>
#include <wchar.h>
#include <errno.h>
#include <string.h>
#include <limits.h> // for INT_MAX, INT_MIN
#include <math.h>   // for sin, cos, tan2, use U_ROUND() instead of roundf() 
#include <stddef.h> /* for offsetof() macro */
#if 0
#include <windef.h>    //Not actually used, looking for collisions
#include <winnt.h>    //Not actually used, looking for collisions
#include <wingdi.h>   //Not actually used, looking for collisions
#endif
#include "upmf.h"         // includes uemf.h
#include "uemf_endian.h"  // for U_swap* functions

//! \cond

/// things Doxygen should not process
/* remove this after debugging is completed */
void dumphex(uint8_t *buf,unsigned int num){
   for(; num; num--,buf++){
      printf("%2.2X",*buf);
   }
}


/* Prototypes for functions used here and defined in uemf_endian.c, but which are not supposed
to be used in end user code. */

void U_swap2(void *ul, unsigned int count);
void U_swap4(void *ul, unsigned int count);
//! \endcond

/**
    \brief  Utility function for writing one or more EMF+ records in a PseudoObject to the EMF output file
    \return 1 on success, 0 on error.
    \param  po          U_PSEUDO_OBJ to write, it is deleted after it is written
    \param  sum         U_PSEUDO_OBJ to use for scratch space
    \param  et          EMFTRACK used to write records to EMF file
*/
int U_PMR_write(U_PSEUDO_OBJ *po, U_PSEUDO_OBJ *sum, EMFTRACK *et){
   char *rec;
   int status = 0;
   sum->Used = 0;                                          /* clean it out, retaining allocated memory         */
   sum       = U_PO_append(sum, "EMF+", 4);                /* indicates that this comment holds an EMF+ record */
   if(!sum)goto end;
   sum       = U_PO_append(sum,  po->Data, po->Used);      /* the EMF+ record itself                           */
   if(!sum)goto end;
   U_PO_free(&po);                                         /* delete the PseudoObject                          */
   rec       = U_EMRCOMMENT_set(sum->Used, sum->Data);     /* stuff it into the EMF comment                    */
   if(!emf_append((PU_ENHMETARECORD)rec, et, 1))goto end;  /* write it to the EMF file, delete the record, check status */
   status = 1;
end:
   return(status);
}

/**
    \brief  Utility function to draw a line.
    \return 1 on success, 0 on error.
    \param  PenID       Index of U_PMF_PEN object to use in the EMF+ object table (0-63, inclusive)
    \param  PathID      Index of U_PMF_PATH object to use in the EMF+ object table (0-63, inclusive)
    \param  Start       U_PMF_POINTF coordinates of start of line.
    \param  End         U_PMF_POINTF coordinates of end of line.
    \param  Dashed      Set if the line is dashed, clear if it is not.
    \param  sum         PseudoObject used for scratch space
    \param  et          EMFTRACK used to write records to EMF file
   
*/
int U_PMR_drawline(uint32_t PenID, uint32_t PathID, U_PMF_POINTF Start, U_PMF_POINTF End, int Dashed, U_PSEUDO_OBJ *sum, EMFTRACK *et){
   U_DPSEUDO_OBJ *dpath;
   U_PSEUDO_OBJ *poPath;
   U_PSEUDO_OBJ *po;
   int           status=0;
   int           PTP_value = ( Dashed ? U_PTP_DashMode : U_PTP_None);
   dpath =  U_PATH_create(0, NULL, 0, 0); /* create an empty path*/
   if(dpath){
      if(U_PATH_moveto(dpath, Start, PTP_value) && U_PATH_lineto(dpath, End, PTP_value)){
         poPath = U_PMF_PATH_set2(U_PMF_GRAPHICSVERSIONOBJ_set(2), dpath);
         if(poPath){
            po = U_PMR_OBJECT_PO_set(PathID, poPath);
            U_PO_free(&poPath);
            if(po){
               U_PMR_write(po, sum, et);
               po = U_PMR_DRAWPATH_set(PathID, PenID);
               if(po){
                  U_PMR_write(po, sum, et);
                  status = 1;
               }
            }
         }
      }
      U_DPO_free(&dpath);
   }
   return(status);
}

/**
    \brief  Utility function for drawing strings onto the baseline in one call.
    \return 1 on success, 0 on error.
    \param  string      Text to draw in UTF-8 format
    \param  Vpos        StringAlignment Enumeration.  Always drawn on baseline, but using one of these three modes.
    \param  FontID      Index of U_PMF_FONT object to use in the EMF+ object table (0-63, inclusive)
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  FormatID    index of U_PMF_STRINGFORMAT object to use in the EMF+ Object Table.
    \param  Sfs         StringFormat structure. Ignored values: StringAlignment, LineAlign, Flags
    \param  FontName    Name of font to draw with
    \param  Height      Height of font in pixels (positive)
    \param  fip         U_FontInfoParams (ascent, descent, and so forth)
    \param  FontFlags   FontStyle Flags
    \param  x           X position in pixels of left side of EM box of first character
    \param  y           Y position in pixels of baseline of first character
    \param  sum         PseudoObject used for scratch space
    \param  et          EMFTRACK used to write records to EMF file
   
    EMF+ manual 2.3.4.14, Microsoft name: EmfPlusDrawString Record, Index 0x1C
    
    For most fonts Ascent and Descent are used to adjust the bounding box to properly position the
    baseline.  Some fonts, like Verdana, are strange and they position the baseline on the bottom of
    the bounding box if that box has the same height as the font.  For those fonts specify 0.0 for
    both Ascent and Descent.
*/
int U_PMR_drawstring( const char *string, int Vpos, uint32_t FontID, const U_PSEUDO_OBJ *BrushID, uint32_t FormatID,
      U_PMF_STRINGFORMAT  Sfs, const char *FontName, U_FLOAT Height, U_FontInfoParams *fip, uint32_t FontFlags,
      U_FLOAT x, U_FLOAT y, U_PSEUDO_OBJ *sum, EMFTRACK *et){
   U_PSEUDO_OBJ  *po;
   U_PSEUDO_OBJ  *poSF;
   U_PSEUDO_OBJ  *poFont;
   U_PSEUDO_OBJ  *poRect;
   U_FLOAT        rx,ry,rw,rh,rd;
   uint16_t      *UFontName;
   uint16_t      *Text16;
   int            slen;
   int            status = 0;
   double         aval, dval;
   
   Sfs.Flags           = U_SF_NoFitBlackBox + U_SF_NoClip;
   
   if(Vpos < U_SA_Near || Vpos > U_SA_Far)return(0);
   Sfs.StringAlignment = U_SA_Near; //  Horizontal
   Sfs.LineAlign       = Vpos; //  Vertical

   UFontName = U_Utf8ToUtf16le(FontName, 0, NULL);
   slen = strlen(FontName);
   poFont = U_PMF_FONT_set(U_PMF_GRAPHICSVERSIONOBJ_set(2), Height, U_UT_World, FontFlags, slen, UFontName);
   if(poFont){
      po = U_PMR_OBJECT_PO_set(FontID, poFont); /* font to use */
      if(po){
         U_PMR_write(po, sum, et);
         
         poSF = U_PMF_STRINGFORMAT_set(&Sfs, NULL);
         if(poSF){
            po = U_PMR_OBJECT_PO_set(FormatID, poSF);
            U_PO_free(&poSF);
            if(po){
               U_PMR_write(po, sum, et);

               rw = 4*Height*slen; /* This could probably be any value */
               rh = Height;
               rx = x;
               if(fip->LineGap > -fip->Descent){ aval = fip->yMax;   } // sylfaen, palatino
               else {                            aval = fip->Ascent; } // others
               if(fip->LineGap && (fip->LineGap < -fip->Descent)){ dval =  ((double) (fip->Descent - fip->LineGap)) / ((double) fip->EmSize); } //shruti and some others
               else {                                              dval =  ((double)  fip->Descent                ) / ((double) fip->EmSize); }
               switch(Vpos){
                  case U_SA_Near:  
                     rd = Height * aval / ((double) fip->EmSize);
                     break;
                  case U_SA_Center:
                     rd = 0.5 * (  Height * aval / ((double) fip->EmSize) +  Height * ( 1.0 +  dval));
                     break;
                  case U_SA_Far:
                     rd = Height * ( 1.0 +  dval);
                     break;
               }
               ry = y - rd;   /* draw from upper left corner, which is shifted to put baseline on y */
               poRect = U_PMF_RECTF4_set(rx, ry, rw, rh); 
#if 0
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x-100, ry}, (U_PMF_POINTF){x+100, ry},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x-100, ry+rh}, (U_PMF_POINTF){x+100, ry+rh},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x-100, ry}, (U_PMF_POINTF){x-100, ry + Height * (double) fip->Ascent / ((double) fip->EmSize)},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 90, ry}, (U_PMF_POINTF){x- 90, ry - Height * (double) fip->Descent / ((double) fip->EmSize)},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 80, ry}, (U_PMF_POINTF){x- 80, ry + Height * (double) fip->yMax / ((double) fip->EmSize)},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 70, ry}, (U_PMF_POINTF){x- 70, ry - Height * (double) fip->yMin / ((double) fip->EmSize)},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 60, ry}, (U_PMF_POINTF){x- 60, ry + Height * (double) fip->LineGap / ((double) fip->EmSize)},  0, sum, et);
(void) U_PMR_drawline(OBJ_PEN_BLACK_1,OBJ_PATH_1, (U_PMF_POINTF){x- 50, ry}, (U_PMF_POINTF){x- 50, ry + Height * ( 1.0 -  (((double) (fip->LineGap - fip->Descent)) / ((double) fip->EmSize)) )},  0, sum, et);
#endif

               Text16 = U_Utf8ToUtf16le(string, 0, NULL);
               slen = strlen(string);
               po = U_PMR_DRAWSTRING_set(FontID, BrushID, FormatID, slen, poRect, Text16);
               if(po){
                  U_PMR_write(po, sum, et);
                  status = 1;                                             /* Success!!!                                       */
               }
               U_PO_free(&poRect);
               free(Text16);
            }
         }
      }
      U_PO_free(&poFont);
   }
   free(UFontName);
   return(status);
}

/**
    \brief Allocate and construct an array of U_POINT16 objects from a set of U_PMF_POINTF objects, endianness in and out is LE
    \returns pointer to an array of U_POINT16 structures.
    \param points  pointer to the source U_POINT structures
    \param count   number of members in points

    If a coordinate is out of range it saturates at boundary.
*/
U_PMF_POINT *POINTF_To_POINT16_LE(U_PMF_POINTF *points, int count){
   U_PMF_POINT *newpts;
   U_PMF_POINTF ptfl;
   int i;
   newpts = (U_PMF_POINT *) malloc(count * sizeof(U_PMF_POINT));

   for(i=0; i<count; i++){
      memcpy(&ptfl, &(points[i]), 8);
      if(U_BYTE_SWAP){  U_swap4(&ptfl,2); } /* on BE platforms swap going in and coming out */
      newpts[i].X = U_MNMX(ptfl.X, INT16_MIN, INT16_MAX);
      newpts[i].Y = U_MNMX(ptfl.Y, INT16_MIN, INT16_MAX);
      if(U_BYTE_SWAP){  U_swap2(&(newpts[i]),2); }
   }
   return(newpts);
}

/**
    \brief Look up the name of the EMR+ record by type.  Returns U_EMR_INVALID if out of range.
        
    \return name of the PMR record, "U_EMR_INVALID" if out of range.
    \param idx  PMR record type WITHOUT the U_PMR_RECFLAG bit. 
    
*/
char *U_pmr_names(unsigned int idx){
   if(idx<U_PMR_MIN || idx > U_PMR_MAX){ idx = 0; }
   static char *U_PMR_NAMES[U_PMR_MAX+1]={
      "U_PMR_INVALID",          
      "U_PMR_Header",
      "U_PMR_EndOfFile",
      "U_PMR_Comment",
      "U_PMR_GetDC",
      "U_PMR_MultiFormatStart",
      "U_PMR_MultiFormatSection",
      "U_PMR_MultiFormatEnd",
      "U_PMR_Object",
      "U_PMR_Clear",
      "U_PMR_FillRects",
      "U_PMR_DrawRects",
      "U_PMR_FillPolygon",
      "U_PMR_DrawLines",
      "U_PMR_FillEllipse",
      "U_PMR_DrawEllipse",
      "U_PMR_FillPie",
      "U_PMR_DrawPie",
      "U_PMR_DrawArc",
      "U_PMR_FillRegion",
      "U_PMR_FillPath",
      "U_PMR_DrawPath",
      "U_PMR_FillClosedCurve",
      "U_PMR_DrawClosedCurve",
      "U_PMR_DrawCurve",
      "U_PMR_DrawBeziers",
      "U_PMR_DrawImage",
      "U_PMR_DrawImagePoints",
      "U_PMR_DrawString",
      "U_PMR_SetRenderingOrigin",
      "U_PMR_SetAntiAliasMode",
      "U_PMR_SetTextRenderingHint",
      "U_PMR_SetTextContrast",
      "U_PMR_SetInterpolationMode",
      "U_PMR_SetPixelOffsetMode",
      "U_PMR_SetCompositingMode",
      "U_PMR_SetCompositingQuality",
      "U_PMR_Save",
      "U_PMR_Restore",
      "U_PMR_BeginContainer",
      "U_PMR_BeginContainerNoParams",
      "U_PMR_EndContainer",
      "U_PMR_SetWorldTransform",
      "U_PMR_ResetWorldTransform",
      "U_PMR_MultiplyWorldTransform",
      "U_PMR_TranslateWorldTransform",
      "U_PMR_ScaleWorldTransform",
      "U_PMR_RotateWorldTransform",
      "U_PMR_SetPageTransform",
      "U_PMR_ResetClip",
      "U_PMR_SetClipRect",
      "U_PMR_SetClipPath",
      "U_PMR_SetClipRegion",
      "U_PMR_OffsetClip",
      "U_PMR_DrawDriverstring",
      "U_PMR_StrokeFillPath",
      "U_PMR_SerializableObject",
      "U_PMR_SetTSGraphics",
      "U_PMR_SetTSClip"
   };
   return(U_PMR_NAMES[idx]);
}

/**
    \brief Convert from PseudoObject OID to ObjectType enumeration.
    \returns OT value on success, 0 if no match
    \param OID   PseudoObject OID (based on EMF+ manual chapter number. )

    Only OTs that may be stored in the EMF+ object table are supported.
*/

int U_OID_To_OT(uint32_t OID){
   int otype;
   if(     OID==U_PMF_BRUSH_OID          ){ otype = U_OT_Brush;           }                    
   else if(OID==U_PMF_PEN_OID            ){ otype = U_OT_Pen;             }         
   else if(OID==U_PMF_PATH_OID           ){ otype = U_OT_Path;            }               
   else if(OID==U_PMF_REGION_OID         ){ otype = U_OT_Region;          }               
   else if(OID==U_PMF_IMAGE_OID          ){ otype = U_OT_Image;           }               
   else if(OID==U_PMF_FONT_OID           ){ otype = U_OT_Font;            }               
   else if(OID==U_PMF_STRINGFORMAT_OID   ){ otype = U_OT_StringFormat;    }               
   else if(OID==U_PMF_IMAGEATTRIBUTES_OID){ otype = U_OT_ImageAttributes; }               
   else if(OID==U_PMF_CUSTOMLINECAP_OID  ){ otype = U_OT_CustomLineCap;   } 
   else {                                   otype = U_OT_Invalid;         }          
   return(otype);
}

/**
    \brief Convert from PseudoObject OID to BrushType enumeration.
    \returns BT value on success, -1 if no match
    \param OID   PseudoObject OID (based on EMF+ manual chapter number. )

    Only OIDs that map to BT's are supported.
*/

int U_OID_To_BT(uint32_t OID){
   int otype;
   if(     OID==U_PMF_HATCHBRUSHDATA_OID          ){ otype = U_BT_HatchFill;      }                    
   else if(OID==U_PMF_LINEARGRADIENTBRUSHDATA_OID ){ otype = U_BT_LinearGradient; }               
   else if(OID==U_PMF_PATHGRADIENTBRUSHDATA_OID   ){ otype = U_BT_PathGradient;   }               
   else if(OID==U_PMF_SOLIDBRUSHDATA_OID          ){ otype = U_BT_SolidColor;     }               
   else if(OID==U_PMF_TEXTUREBRUSHDATA_OID        ){ otype = U_BT_TextureFill;    }               
   else {                                            otype = -1;                  }          
   return(otype);
}

/**
    \brief Convert from PseudoObject OID to CustomLineCapDataType Enumeration.
    \returns BT value on success, -1 if no match
    \param OID   PseudoObject OID (based on EMF+ manual chapter number. )

    Only OIDs that map to CLCDT's are supported.
*/

int U_OID_To_CLCDT(uint32_t OID){
   int otype;
   if(     OID==U_PMF_CUSTOMLINECAPDATA_OID       ){ otype = U_CLCDT_Default;         }                    
   else if(OID==U_PMF_CUSTOMLINECAPARROWDATA_OID ){  otype = U_CLCDT_AdjustableArrow; }               
   else {                                            otype = -1;                      }          
   return(otype);
}

/**
    \brief Convert from PseudoObject OID to ImageDataType Enumeration.
    \returns BT value on success, -1 if no match
    \param OID   PseudoObject OID (based on EMF+ manual chapter number. )

    Only OIDs that map to IDT's are supported.
*/

int U_OID_To_IDT(uint32_t OID){
   int otype;
   if(     OID==U_PMF_BITMAP_OID   ){ otype = U_IDT_Bitmap;   }                    
   else if(OID==U_PMF_METAFILE_OID ){ otype = U_IDT_Metafile; }               
   else {                             otype = -1;             }          
   return(otype);
}

/**
    \brief Convert from PseudoObject OID to RegionNodeDataType Enumeration.
    \returns BT value on success, -1 if no match
    \param OID   PseudoObject OID (based on EMF+ manual chapter number. )

    Only OIDs that map to RNDT's are supported.
*/

int U_OID_To_RNDT(uint32_t OID){
   int otype;
   if(     OID==U_PMF_REGIONNODECHILDNODES_OID   ){ otype = U_RNDT_Kids; }  /* there are 5 types, which must be specified separately */         
   else if(OID==U_PMF_RECTF_OID ){                  otype = U_RNDT_Rect; }               
   else if(OID==U_PMF_REGIONNODEPATH_OID ){         otype = U_RNDT_Path; }               
   else {                                           otype = -1;          }          
   return(otype);
}

/**
    \brief Append data to an U_OBJ_ACCUM structure.
    \param oa    pointer to the U_OBJ_ACCUM structure
    \param data  data to add
    \param size  bytes in data
    \param Type  object type
    \param Id    Object ID
    \returns 0 on success, !0 on error.  -1 on Type change, -2 on Id change
    
    Safe to test for Id, Type changes by calling with size=0.
*/
int U_OA_append(U_OBJ_ACCUM *oa, const char *data, int size, int Type, int Id){
   int tail;
   if(!oa)return(2);
   if(oa->used){
      if(oa->Type != Type)return(-1);
      if(oa->Id   != Id)return(-2);
   }
   tail = oa->used;
   if(oa->used + size >= oa->space){
      oa->space += size;
      char *newaccum = (char *) realloc(oa->accum, oa->space);
      if(!newaccum){
         oa->space -= size; /* put it back the way it was */
         return(1);
      }
      oa->accum = newaccum;
   }
   memcpy(oa->accum + tail,data,size);
   oa->used += size;
   oa->Type  = Type;
   oa->Id    = Id;
   return(0);
}


/**
    \brief Clear an U_OBJ_ACCUM structure.  Accumulated storage is retained.
    \param oa    pointer to the U_OBJ_ACCUM structure
    \returns 0 on success, !0 on error.
*/
int U_OA_clear(U_OBJ_ACCUM *oa){
   if(!oa)return(2);
   oa->used=0;
   /* Type and Id may be ignored as they are reset on the first append */
   return(0);
}

/**
    \brief Release an U_OBJ_ACCUM structure.  Accumulated storage is free'd.
    \param oa    pointer to the U_OBJ_ACCUM structure
    \returns 0 on success, !0 on error.
*/
int U_OA_release(U_OBJ_ACCUM *oa){
   if(!oa)return(2);
   oa->used=0;
   oa->space = 0;
   if(oa->accum)free(oa->accum);
   oa->accum=NULL;
   return(0);
}

/**
    \brief Create and set an U_PSEUDO_OBJ
    \returns pointer to the U_PSEUDO_OBJ, NULL on error
    \param Data  Data to copy into the PseudoObject's data. If NULL, space is allocated, but is cleared instead of filled.
    \param Size  Number of bytes to allocate for Data (may be >Use if padding is present) 
    \param Use   Number of data bytes in Data (whether or not Data is actually copied) 
    \param Type  Type numbers are from manual section: 1.2.3.47 -> 0x01020347 
    
    If Data is NULL and Size is 0 an empty PseudoObject is created.  One byte of storage
       is allocated for Data, Size is set to 1, and Used to 0.

    If Data is NULL and Size is !0 a zero filled PseudoObject is created.

    If Data is !Null and Size is !0 a data filled PseudoObject is created.
*/
U_PSEUDO_OBJ *U_PO_create(char *Data, size_t Size, size_t Use, uint32_t Type){
   if(Use>Size)return(NULL);
   size_t tSize = (Size ? Size : 1);
   U_PSEUDO_OBJ *po = (U_PSEUDO_OBJ *)malloc(sizeof(U_PSEUDO_OBJ));
   if(po){
      po->Data = malloc(tSize);
      if(po->Data){
         po->Size = tSize;
         po->Used = Use;
         po->Type = Type;
         if(Data){ memcpy(po->Data, Data, Use);   } /* if Use < Size uninitialized bytes will be present! */
         else {    memset(po->Data, 0,    tSize); }
      }
      else {
         free(po);
         po=NULL;
      }
   }
   return(po);
}

/**
    \brief Append data to a U_PSEUDO_OBJ object and return it
    \returns pointer to the U_PSEUDO_OBJ object, NULL on error
    \param po    PseudoObject to append to.  Cannot be NULL.
    \param Data  Data to copy into the PseudoObject's data. If NULL, space is allocated (if necessary) and cleared instead of filled.
    \param Size  Number of data bytes in Data
*/
U_PSEUDO_OBJ *U_PO_append(U_PSEUDO_OBJ *po, const char *Data, size_t Size){
   /* po cannot be NULL,as in U_PO_po_append(), because there would be no way to determine the TYPE of the resulting PO */
   if(po){
      if(!po->Data || po->Used + Size > po->Size){
         po->Size = po->Used + Size;
         char *newData = realloc(po->Data, po->Size);
         if(!newData){
            po->Size -= Size; /* put it back the way it was*/
            po=NULL;          /* skip the rest of the actions, does not affect po in caller */
         }
         else {
            po->Data = newData;
         }
      }
      if(po){ /* po->Data ready to append new data */
         if(Data){ memcpy(po->Data + po->Used, Data, Size); }
         else {    memset(po->Data + po->Used, 0,    Size); }
         po->Used += Size;
      }
   }
   return(po);
}

/**
    \brief Append data to a U_PSEUDO_OBJ object and return it
    \returns pointer to the U_PSEUDO_OBJ object, NULL on error
    \param po     PseudoObject to append to.  May be NULL.
    \param Src    PseudoObject to append.
    \param StripE Set: leading Elements in Src->Data is not copied, Clear: it is copied.
*/
U_PSEUDO_OBJ *U_PO_po_append(U_PSEUDO_OBJ *po, U_PSEUDO_OBJ *Src, int StripE){
   if(!Src){ return(NULL); }
   if((StripE && (Src->Used == 4)) || !Src->Used){ return(po); } /* appending nothing is not an error */
   char   *Data = Src->Data;
   size_t  Size = Src->Used;  /* append only what is used */
   U_PSEUDO_OBJ *ipo = po;
   if(StripE){ Size -= 4; }
   if(!ipo){
      ipo = U_PO_create(NULL, 0, 0, Src->Type); /* create an empty pseudoobject */
   }
   if(ipo){
      if(!ipo->Data || ipo->Used + Size > ipo->Size){
         ipo->Size = ipo->Used + Size;
         char *newData = realloc(ipo->Data, ipo->Size);
         if(!newData){
            if(ipo != po)U_PO_free(&ipo);
         }
         else {
            ipo->Data = newData;
         }
      }
      if(ipo){
         if(Data){
            if(StripE){  memcpy(ipo->Data + ipo->Used, Data + 4, Size); } /* Size is already 4 less, skip the leading Elements value */
            else {       memcpy(ipo->Data + ipo->Used, Data,     Size); } /* copy everything */
         }
         else {          memset(ipo->Data + ipo->Used, 0,        Size); } /* set everything  */
         ipo->Used += Size;
      }
   }
   return(ipo);
}

/**
    \brief Free an U_PSEUDO_OBJ structure.  All associated memory is released.
    \param po   Address of a pointer to the U_PSEUDO_OBJ structure, Pointer is set to NULL.
    \returns 1 on success, 0 on error.
*/
int U_PO_free(U_PSEUDO_OBJ **po){
   if(!po)return(0);
   if(!*po)return(1);
   if((*po)->Data)free((*po)->Data);
   free(*po);
   *po=NULL;
   return(1);
}

/** \brief create a PseudoObject with data in the correct byte order for an EMF+ file.
    \returns The PseudoObject on success, NULL on error.

    \param Type  the type of the PseudoObject that is created.  Allowed values are in U_PID_Values.
    \param List  an array of U_SERIAL_DESC structures containing the data to store.

      The U_PMF_SERIAL_set() function should not ever be called directly by end user code.
    
   Each U_SERIAL_DESC element in List consists of Data fields and a description of that data.  List is terminated
     by the first U_SERIAL_DESC element having a TE value of U_XX.
   
   Data fields: an array of a basic type of Units bytes repeated Reps times with the target byte order
      described in TE. 
   
   Ptrs:  Address of the first byte of the data fields.

   Units: Number of bytes of in each data field unit

   Reps:  Number of repeats of the unit in data fields.
           If a Ptr is NULL, and Units*Reps is not zero, then Units*Reps 0x00 bytes are stored.
           If a Ptr is NULL, and Units*Reps is zero, this U_SERIAL_DESC is ignored.
           if a Ptr is NOT NULL, and Units * Reps is not zero, then the data is stored in the indicated byte order.
           If a Ptr is NOT NULL, and Units or Reps is zero an error is signaled.

   TE:   (Target Endian) the byte order in which to store each unit of a data field as defined in U_Endian.
         Byte swapping is only enabled when Units is 2 or 4.  In addition to the byte order values U_XE, U_LE,
         and U_BE, and the array terminator U_XX, the value may also be U_RP.  U_RP means there is only a 
         single unit in the data fields, but it is to be copied to the target Reps times.  That is, the data
         was passed in with a form of run length encoding.

   Creates an empty PseudoObject if all pointers are NULL and all sizes are zero.
 */
U_PSEUDO_OBJ *U_PMF_SERIAL_set(uint32_t Type, const U_SERIAL_DESC *List){
   U_PSEUDO_OBJ        *po=NULL;
   size_t               Total=0;
   size_t               FSize;
   char                *cptr;
   char                *hptr;
   const U_SERIAL_DESC *lptr;
   if(!List)return(NULL);
   for(lptr=List; lptr->TE != U_XX; lptr++){
      if(!(lptr->Units * lptr->Reps) && lptr->Ptr)return(po);
      Total += lptr->Units * lptr->Reps;
   }
   po = U_PO_create(NULL, Total, Total, Type);
   if(po){
      cptr = po->Data;
      for(lptr=List; lptr->TE != U_XX; lptr++){
         FSize = lptr->Units * lptr->Reps;
         if(FSize){ /* Ptr is not NULL, that would have been detected already */
            hptr = cptr;
            if(lptr->TE & U_RP){  U_PMF_REPCPY_DSTSHIFT(&cptr, lptr->Ptr, lptr->Units, lptr->Reps); }
            else {                U_PMF_MEMCPY_DSTSHIFT(&cptr, lptr->Ptr, FSize);                   }
            if(((lptr->TE & U_LE) && U_IS_BE) || ((lptr->TE & U_BE) && U_IS_LE)){
               if(lptr->Units==2){      U_swap2(hptr,lptr->Reps); }
               else if(lptr->Units==4){ U_swap4(hptr,lptr->Reps); }
            }
         }
      }
   }
   return(po);
}

/**
    \brief Create U_DPSEUDO_OBJ's for the Points and Types of a path
    \param Elements Number of elements in Points.  May be zero, which creates an empty path.
    \param Points   Array of U_PMF_POINTF values.
    \param First    Apply to first point,      unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration. Must have U_PPT_Start set.
    \param Others   Apply to all other points, unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration. Must have U_PPT_Line or U_PPT_Bezier set.
    \returns pointer to the DU_PSEUDO_OBJ object, NULL on error
*/
U_DPSEUDO_OBJ *U_PATH_create(int Elements, const U_PMF_POINTF *Points, uint8_t First, uint8_t Others){
   if(Elements){
      if(!Points){                               return(NULL); }
      if( (First & U_PPT_MASK) != U_PPT_Start ){ return(NULL); }
      if(!(Others & U_PPT_Bezier)){              return(NULL); } /* will pass if either line or bezier is set */
   }
    
   U_DPSEUDO_OBJ *Path = (U_DPSEUDO_OBJ *)calloc(sizeof(U_DPSEUDO_OBJ),1); /* make poTypes and poPoints NULL */
   const U_SERIAL_DESC List[] = { {NULL,0,0,U_XX} };
   if(Path){
      Path->Elements = Elements;
      Path->poPoints = U_PMF_SERIAL_set(U_PMF_RAW_OID, List);  /* Empty PO to hold points as raw data */
      if(!Elements){
         Path->poTypes  = U_PMF_SERIAL_set(U_PMF_RAW_OID, List);  /* Empty PO to hold types as raw data */
      }
      else {
         Path->poPoints = U_PO_append(Path->poPoints, (char *)Points, Elements*sizeof(U_PMF_POINTF));
         if(Path->poPoints){
            U_PSEUDO_OBJ *tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First | U_PPT_Start, Others);  /* PO holding types, has leading Elements value */
            Path->poTypes = U_PO_po_append(NULL, tpo, U_PMF_DROP_ELEMENTS); /* remove the leading Elements value*/
            U_PO_free(&tpo);
            if(!Path->poTypes){ U_PO_free(&Path->poPoints); }
         }
         if(!Path->poPoints){ U_DPO_free(&Path); }
      }
   }
   return(Path);
}

/**
    \brief Free U_DPSEUDO_OBJ's
    \returns 1 on success, 0 on error.
*/
int U_DPO_free(U_DPSEUDO_OBJ **dpo){
   if(!dpo){  return(0); }
   if(!*dpo){ return(1); }
   U_DPSEUDO_OBJ *kpo = *dpo;
   if(kpo->poPoints){ U_PO_free(&kpo->poPoints); }
   if(kpo->poTypes){  U_PO_free(&kpo->poTypes);  }
   free(*dpo);
   *dpo=NULL;
   return(1);
}

/**
    \brief Clear U_DPSEUDO_OBJ's.  Memory is retained, Elements and Used values are set to 0.
    \returns 1 on success, 0 on error.
    
    It is much more efficient to clear a DPO and reuse it than to free that DPO and create another.
*/
int U_DPO_clear(U_DPSEUDO_OBJ *dpo){
   if(!dpo){  return(0); }
   if(dpo->poPoints){ dpo->poPoints->Used = 0; }
   if(dpo->poTypes){  dpo->poTypes->Used  = 0; }
   dpo->Elements = 0;
   return(1);
}

/**
    \brief Append a "moveto" point to a path
    \param Path   Address of a DoublePseudoObject holding the path to append to.
    \param Point  Point to move to.
    \param Flags  Flags may be (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, U_PTP_CloseSubpath)
    \returns 1 on success, 0 on error.
*/
int U_PATH_moveto(U_DPSEUDO_OBJ *Path, U_PMF_POINTF Point, uint8_t Flags){
   if(!Path){ return(0); }
   U_PSEUDO_OBJ *tpo;
   U_PSEUDO_OBJ *tpo2;
   uint8_t Type = (Flags & U_PTP_NotClose) | U_PPT_Start;

   tpo = U_PMF_POINTF_set(1, &Point);
   if(!tpo){ return(0); }
   tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poPoints = tpo2;
   

   tpo  = U_PMF_PATHPOINTTYPE_set(1, &Type);
   if(!tpo){ return(0); }
   tpo2= U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poTypes = tpo2;

   Path->Elements++;
   return(1);
}

/**
    \brief Append a "lineto" point to a path
    \param Path    Address of a DoublePseudoObject holding the path to append to.
    \param Point   U_PMF_POINTF point to draw to.
    \param Flags  Flags may be (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, U_PTP_CloseSubpath)
    \returns 1 on success, 0 on error.
*/
int U_PATH_lineto(U_DPSEUDO_OBJ *Path,  U_PMF_POINTF Point, uint8_t Flags){
   if(!Path || !Path->Elements){ return(0); }  /* must be at least one point to extend from */
   if(Path->poTypes->Data[Path->Elements - 1] & U_PTP_CloseSubpath){ return(0); }  /* cannot extend a closed subpath */
   U_PSEUDO_OBJ *tpo;
   U_PSEUDO_OBJ *tpo2;
   uint8_t Type = (Flags & U_PTP_NotClose) | U_PPT_Line;
   tpo = U_PMF_POINTF_set(1, &Point);
   if(!tpo){ return(0); }
   tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poPoints  = tpo2;
   

   tpo  = U_PMF_PATHPOINTTYPE_set(1, &Type);
   if(!tpo){ return(0); }
   tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poTypes  = tpo2;

   Path->Elements++;
   return(1);
}

/**
    \brief Set the closepath bit in the last point
    \param Path    Address of a DoublePseudoObject holding the path to act upon.
    \returns 1 on success, 0 on error.
*/
int U_PATH_closepath(U_DPSEUDO_OBJ *Path){
   if(!Path || !Path->poTypes){ return(0); }
   uint32_t Elements = Path->Elements;
   uint8_t  *Type    = (uint8_t *)(Path->poTypes->Data) + Elements - 1;
   if(*Type & U_PPT_Start){ return(0); } /* single point closed path makes no sense */
   *Type =  *Type | U_PTP_CloseSubpath;
   return(1);
}

/**
    \brief Append a "polylineto" set of point to a path
    \param Path      Address of a DoublePseudoObject holding the path to append to.
    \param Elements  number of Points and Flags
    \param Points    Line points.
    \param Flags     Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
    \param StartSeg  If set, use U_PPT_Start PathPointType enumeration for first point, otherwise use U_PPT_Line.
    \returns 1 on success, 0 on error.
*/
int U_PATH_polylineto(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags, uint8_t StartSeg){
   if(!Path || !Points){ return(0); }
   if(!Elements){ return(1); } /* harmless - do nothing */
   U_PSEUDO_OBJ *tpo;
   U_PSEUDO_OBJ *tpo2;
   uint8_t First, Others;

   tpo = U_PMF_POINTF_set(Elements, Points);
   tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poPoints = tpo2;
   
   if(StartSeg){ First  = (Flags & U_PTP_NotClose) | U_PPT_Start; }
   else {        First  = (Flags & U_PTP_NotClose) | U_PPT_Line;  }
                 Others = (Flags & U_PTP_NotClose) | U_PPT_Line;  
   tpo  = U_PMF_PATHPOINTTYPE_set2(Elements, First, Others);
   if(!tpo){ return(0); }
   tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poTypes = tpo2;

   Path->Elements += Elements;
   return(1);
}


/**
    \brief Append a "polybezierto" set of point to a path
    \param Path      Address of a DoublePseudoObject holding the path to append to.
    \param Elements  number of Points
    \param Points    Bezier points.  Optional starting point, then N sets of 3, example: [P1] (Q12A Q12B P2) (Q23A Q23B P3).
    \param Flags     Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
    \param StartSeg  If set, use U_PPT_Start PathPointType enumeration for first point, otherwise use U_PPT_Bezier.
    \returns 1 on success, 0 on error.
    
    If Start is set   Elements must be 1 + multiple of 3. Ie, P1 Q12A Q12B P2 Q23A Q23B P3

    If Start is clear Elements must be a multiple of 3.   Ie, (P1, already in path) Q12A Q12B P2 Q23A Q23B P3
*/
int U_PATH_polybezierto(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags, uint8_t StartSeg){
   if(!Path || !Points){                  return(0); }
   if(!Elements){
      if(StartSeg){                       return(0); }    /* cannot have both a NEW segment and ZERO points */
      else{                               return(1); }    /* harmless - do nothing */
   }
   if(StartSeg  && ((Elements - 1) % 3)){ return(0); }    /* new segment    must be 1 + N*3 points */
   if(!StartSeg && (Elements % 3)){       return(0); }    /* extend segment must be     N*3 points */
   U_PSEUDO_OBJ *tpo;
   U_PSEUDO_OBJ *tpo2;
   uint8_t First, Others;

   tpo = U_PMF_POINTF_set(Elements, Points);
   tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poPoints = tpo2;
 
   if(StartSeg){ First  = (Flags & U_PTP_NotClose) | U_PPT_Start;  }
   else {        First  = (Flags & U_PTP_NotClose) | U_PPT_Bezier; }
                 Others = (Flags & U_PTP_NotClose) | U_PPT_Bezier;
   tpo  = U_PMF_PATHPOINTTYPE_set2(Elements, First, Others);
   if(!tpo){ return(0); }
   tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS);
   U_PO_free(&tpo);
   if(!tpo2)return(0);
   Path->poTypes = tpo2;

   Path->Elements += Elements;
   return(1);
}

/**
    \brief Append a "polygon" set of points to a path.
    \param Path      Address of a DoublePseudoObject holding the path to append to.
    \param Elements  number of Points and Flags
    \param Points    Line points.
    \param Flags     Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
    \returns 1 on success, 0 on error.
*/
int U_PATH_polygon(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags){
   int status = U_PATH_polylineto(Path, Elements, Points, Flags, U_SEG_NEW);
   if(status){
       status = U_PATH_closepath(Path);
   }
   return(status);
}

//! \cond
// These are not exposed in the API
/* Parameterized Ellipse coordinates */
U_PMF_POINTF U_eparam(U_FLOAT a, U_FLOAT b, U_PMF_POINTF *center, double Ang, double Theta){
   U_PMF_POINTF point;
   point.X = center->X + a*cos(Theta)*cos(Ang) - b*sin(Theta)*sin(Ang);
   point.Y = center->Y + a*sin(Theta)*cos(Ang) + b*cos(Theta)*sin(Ang);
   return(point);
}

/* Parameterized Ellipse derivative  */
U_PMF_POINTF U_eparam2(U_FLOAT a, U_FLOAT b, double Ang, double Theta){
   U_PMF_POINTF point;
   point.X = -a*cos(Theta)*sin(Ang) - b*sin(Theta)*cos(Ang);
   point.Y = -a*sin(Theta)*sin(Ang) + b*cos(Theta)*cos(Ang);
   return(point);
}

double U_aparam(double Ang1, double Ang2){
   double Alpha;
   double t2;
   t2 = tan((Ang2 - Ang1)/2.0);
   t2 *= t2;
   Alpha = sin(Ang2 - Ang1) * (sqrt(4 + 3*t2) -1.0)/3.0;
   return(Alpha);
}

/* Parameterized Bezier point Q1 or Q2  derivative  */
U_PMF_POINTF U_qparam(double Alpha, double a, double b, U_PMF_POINTF *Point, double Ang, double Theta, int mode){
   U_PMF_POINTF Q, Prime;
   Prime = U_eparam2(a,b,Ang,Theta);
   if(mode==1){ /* Q1, anything else is Q2*/
      Q.X = Point->X + Alpha * Prime.X;
      Q.Y = Point->Y + Alpha * Prime.Y;
   }
   else {
      Q.X = Point->X - Alpha * Prime.X;
      Q.Y = Point->Y - Alpha * Prime.Y;
   }
   return(Q);
}
//! \endcond

/**
    \brief Append an "arcto" set of points to a path (Bezier points are calculated, and these are appended
    \param Path      Address of a pointer to the U_PSEUDO_OBJ that holds points
    \param Start     Start angle, >=0.0, degrees clockwise from 3:00
    \param Sweep     Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param Rot       Rotation angle to apply to coordinate system (Start and Rect), positive is degrees clockwise
    \param Rect      U_PMF_RECTF that defines the bounding rectangle.
    \param Flags     Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath)
    \param StartSeg  If set, the arc starts a new segment, if clear, continue the existing segment.  Starting a new segment does not automatically apply U_PATH_closepath to the existing path.
    \returns 1 on success, 0 on error.
    
    Based on Luc Maisonobe's work, http://www.spaceroots.org/documents/ellipse/
*/
int U_PATH_arcto(U_DPSEUDO_OBJ *Path, U_FLOAT Start, U_FLOAT Sweep, U_FLOAT Rot, U_PMF_RECTF *Rect, uint8_t Flags, int StartSeg){
   U_PMF_POINTF Bz[3];
   U_PMF_POINTF Center;
   U_PMF_POINTF P1,P2;
   double a, b;
   int done  = 0;
   int fpoint = 0;
   double L1, L2;     /* These are degrees CounterClockwise from 3:00 */
   double Ang1, Ang2; /* These are parametric angles, derived from L1, L2*/
   double Alpha;      /* Dimensionless number used for Q1, Q2 */
   double Theta;      /* Rot in radians */
   double Slop;       /* do not let rounding errors cause spurious end points */
   if(!Path){   return(0); }
   if((Sweep > 360.0) || (Sweep < -360.0)){ return(0); }
   /* Start should be between 0 and 360 degrees, but it does not really matter because sin, and cos will accept anything */
   /* the sign on Sweep and Start is correct bacause LM's derivation has y positive up, but GDI+ has y positive down. */
   a  = Rect->Width/2.0;
   b  = Rect->Height/2.0;
   if(!a || !b){ return(0); }
   Center.X = Rect->X + a;
   Center.Y = Rect->Y + b;
   /* convert to radians */
   Start = (2.0 * U_PI * Start)/360.0;
   Sweep = (2.0 * U_PI * Sweep)/360.0;
   Theta = (2.0 * U_PI * Rot  )/360.0;
   Slop  = Sweep/100000.0;
   L1 = Start;

   while(!done){
      if(Sweep < 0){
         L2 = L1 - U_PI/2.0;
         if(L2 <= Sweep + Start - Slop){ L2 = Sweep + Start; done = 1; }
         else {done = 0; }
      }
      else {
         L2 = L1 + U_PI/2.0;
         if(L2 >= Sweep + Start + Slop){ L2 = Sweep + Start; done = 1; }
         else {done = 0; }
      }
      Ang1 = atan2(sin(L1)/b, cos(L1)/a);
      Ang2 = atan2(sin(L2)/b, cos(L2)/a);
      Alpha = U_aparam(Ang1, Ang2);
      P1    = U_eparam(a, b, &Center, Ang1, Theta);        /* P1 */
      P2    = U_eparam(a, b, &Center, Ang2, Theta);        /* P2 */
      Bz[0] = U_qparam(Alpha, a, b, &P1, Ang1, Theta, 1);  /* Q1 */
      Bz[1] = U_qparam(Alpha, a, b, &P2, Ang2, Theta, 2);  /* Q2 */
      Bz[2] = P2;
      if(!fpoint){
         if(StartSeg){ U_PATH_moveto(Path, P1, Flags); }
         else {        U_PATH_lineto(Path, P1, Flags); }
         fpoint = 1;
      }
      U_PATH_polybezierto(Path, 3, Bz, Flags, U_SEG_OLD );
      L1 = L2;
   }
   return(1);
}

/**
    \brief Allocate and construct an array of U_PMF_POINTF objects which have been subjected to a U_XFORM
    \returns pointer to an array of U_PMF_POINTF structures.
    \param points  pointer to the source U_PMF_POINTF structures
    \param count   number of members in points
    \param xform   U_XFORM to apply
    
*/
U_PMF_POINTF *pointfs_transform(U_PMF_POINTF *points, int count, U_XFORM xform){
   U_PMF_POINTF *newpts=NULL;;
   int i;
   float X,Y;
   newpts = (U_PMF_POINTF *) malloc(count * sizeof(U_PMF_POINTF));
   if(newpts){
      for(i=0; i<count; i++){
         X = points[i].X;
         Y = points[i].Y;
         newpts[i].X = U_ROUND(X * xform.eM11 + Y * xform.eM21 + xform.eDx);
         newpts[i].Y = U_ROUND(X * xform.eM12 + Y * xform.eM22 + xform.eDy);
      }
   }
   return(newpts);
}

/**
    \brief Allocate and construct an array of U_PMF_RECTF objects which have been subjected to a U_XFORM
    \returns pointer to an array of U_PMF_RECTF structures.
    \param Rects   pointer to the source U_PMF_RECTF structures
    \param Count   number of members in Rects
    \param Xform   U_XFORM to apply.  Rotation is ignored, only translation is applied.
    
*/
U_PMF_RECTF *rectfs_transform(U_PMF_RECTF *Rects, int Count, U_XFORM Xform){
   U_PMF_RECTF *newRects;
   int i;
   newRects = (U_PMF_RECTF *) malloc(Count * sizeof(U_PMF_RECTF));
   if(newRects){
      for(i=0; i<Count; i++){
         newRects[i].X      = U_ROUND(Rects[i].X + Xform.eDx);
         newRects[i].Y      = U_ROUND(Rects[i].Y + Xform.eDy);
         newRects[i].Width  = U_ROUND(Rects[i].Width);
         newRects[i].Height = U_ROUND(Rects[i].Height);
      }
   }
   return(newRects);
}

/**
    \brief  Utility function calculate the transformation matrix needed to make a gradient run precisely corner to corner of a rectangle
    \param  Angle   Rotation in degrees clockwise of the gradient. 0 is horizontal gradient.
    \param  w       Width of the rectangle
    \param  h       Height of the rectangle
    \param  x       X coordinate of upper left corner of rectangle
    \param  y       Y coordinate of upper left corner of rectangle
    \param  Periods Periods of gradient corner to corner.  1.0 is one, corner to corner.
    \return Transformation matrix.  All values are zero if Periods, w, or h are less than or equal to zero.
*/
U_PMF_TRANSFORMMATRIX tm_for_gradrect(U_FLOAT Angle, U_FLOAT w, U_FLOAT h, U_FLOAT x, U_FLOAT y, U_FLOAT Periods){
//! \cond
#define CLOSE_TO_IS_REALLY_ZERO(A) ((A) > 1.0e-10 || (A) < -1.0e-10 ? (A) : 0.0) //! \hideinitializer 
//! \endcond
   U_PMF_TRANSFORMMATRIX tm;
   double dang = Angle * 2*U_PI /360.0;
   double scale;
   double cd,sd;
   if((Periods <=0.0) || (w <= 0.0) || (h <= 0.0)){
      tm.m11 = tm.m12 = tm.m21 = tm.m22 = tm.dX = tm.dY = 0.0;
   }
   else {
      /*
        scale is gradient period divided by w
        The scale value sets the gradient period to match exactly with the inscribed (w,h) rectangle
          in the direction specified by the angle.
        The numberator of scale is the max of the four dot product values of the rotated X basis unit vector with (w,h),
          with each of the vectors {w,h}, {-w,h}, {-w,-h}, {w,h}.  Those vectors run from each corner in turn
          to the opposite corner.  The one most parallel to the rotated unit vector will have both terms positive.
          
        Trig results like cos(pi/2) are not stable between platforms due to minor differences in the
          implementation. Detect these and make them zero, which then allows binary comparison of output files.
          Otherwise the binary comparisons between platforms would fail because of a bunch of insignificant digits.
      */
      cd = CLOSE_TO_IS_REALLY_ZERO(cos(dang));
      sd = CLOSE_TO_IS_REALLY_ZERO(sin(dang));
      scale = (w*fabs(cd) + h*fabs(sd))/(w*Periods);
      tm.m11 =  scale * cd;
      tm.m12 = -scale * sd;                
      tm.m21 =  scale * sd;                
      tm.m22 =  scale * cd; 
      /* offset is to one corner of the square, depending on which quadrant the rotation selects. */
      if(cos(dang)>=0){
         tm.dX = x;
         if(sin(dang)>=0){tm.dY = y + h;  } // LL corner
         else {           tm.dY = y;      } // UL corner
      }
      else {
         tm.dX = x + w;
         if(sin(dang)>=0){ tm.dY = y + h; } // LR corner
         else {            tm.dY = y;     } // UR corner
      }
   }
   return tm; 
#undef CLOSE_TO_IS_REALLY_ZERO
}
/**
    \brief Create a U_PSEUDO_OBJ containing a U_PMR_FILLPATH and U_PMR_DRAWPATH records.
    \returns pointer to U_PSEUDO_OBJ or NULL on error.
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
*/
U_PSEUDO_OBJ *U_PMR_drawfill(uint32_t PathID, uint32_t PenID, const U_PSEUDO_OBJ *BrushID){
   U_PSEUDO_OBJ *po = U_PMR_FILLPATH_set(PathID, BrushID);
   if(po){
      U_PSEUDO_OBJ *tpo = U_PMR_DRAWPATH_set(PathID, PenID);
      po = U_PO_po_append(po, tpo, U_PMF_KEEP_ELEMENTS);
      U_PO_free(&tpo);
   }
   return(po);
}



/**
   \brief Extract a single data field from a source.
   \returns 1 on success, 0 on error.
   \param Src   where the data is coming from.  It is incremented by the number of bytes retrieved.
   \param Dst   where the data will be stored.  This must either be NULL (in which case the Src
           is advanced and nothing is stored, or it must be allocated to Reps * Units bytes!!!!
   \param Units number of bytes in each unit of the data field
   \param Reps  number of repeats of units in the data field.
           If a Ptr is NULL, then Units*Reps 0 bytes are stored.
           If a Ptr is NOT NULL, and Units or Reps, is zero an error is signaled.
           If a Ptr is NULL and Units*Reps is 0, nothing happens.
   \param SE    logical (Source Endian). Only relevant for Sizes of 2 or 4 
         Indicates when Bytes may need to be rearranged when they are retrieved.
         U_XE no change   (this is used when the data has already been set to the proper orientation or it is not known)
         U_LE source is Little Endian
         U_BE source is Big Endian.
         U_XX error
    
*/
int U_PMF_SERIAL_get(const char **Src, void *Dst, size_t Units, size_t Reps, int SE){
   if(!Src || !*Src || SE == U_XX){ return(0); }
   U_PMF_MEMCPY_SRCSHIFT(Dst, Src, Units * Reps);
   if(!Dst){                  return(1); } /* "fake" get, no data was retrieved, so we are done */
   if(SE == U_XE){            return(1); }
   if(SE == U_LE && U_IS_LE){ return(1); }
   if(SE == U_BE && U_IS_BE){ return(1); }
   /* need to swap */
   if(     Units==2){ U_swap2(Dst,Reps); }
   else if(Units==4){ U_swap4(Dst,Reps); }
   return(1);
}

/**
      \brief Conditionally extract an array of data from a source, allocating space to hold it.
      \returns 1 on success, 0 on error.
      \param Src   where the data is coming from.  It is incremented by the number of bytes retrieved.
      \param Dst   Caller must free.  Where the pointer to the data will be stored.  Reps * Units bytes will be allocated,
      \param Units number of bytes in each unit of the data field
      \param Reps  number of repeats of units in the data field.
           If a Ptr is NULL, then Units*Reps 0 bytes are stored.
           If a Ptr is NOT NULL, and Units or Reps, is zero an error is signaled.
           If a Ptr is NULL and Units*Reps is 0, nothing happens.
      \param SE    logical (Source Endian). Only relevant for Sizes of 2 or 4 
         Indicates when Bytes may need to be rearranged when they are retrieved.
         U_XE no change   (this is used when the data has already been set to the proper orientation or it is not known)
         U_LE source is Little Endian
         U_BE source is Big Endian.
         U_XX error
      \param Cond  Store the data into *Dst if true, set *Dst to NULL otherwise.
    
*/
int U_PMF_SERIAL_array_copy_get(const char **Src, void **Dst, size_t Units, size_t Reps, int SE, int Cond){
   if(!Src || !*Src || !Dst || SE == U_XX){ return(0); }
   if(!Cond){
      *Src += Units * Reps;
      *Dst = NULL;
      return(1);
   }
   *Dst = malloc(Units * Reps);
   if(!*Dst){                 return(1); } /* "fake" get, no data was retrieved, so we are done */
   U_PMF_MEMCPY_SRCSHIFT(*Dst, Src, Units * Reps);
   if(SE == U_XE){            return(1); }
   if(SE == U_LE && U_IS_LE){ return(1); }
   if(SE == U_BE && U_IS_BE){ return(1); }
   /* need to swap */
   if(     Units==2){ U_swap2(*Dst,Reps); }
   else if(Units==4){ U_swap4(*Dst,Reps); }
   return(1);
}



/**
    \brief Calculate the length in bytes of a relative path object composed of U_PMF_INTEGER7 and U_PMF_INTER15 values
    \return >=0 length == success, <0  error
    \param  contents   Start of a relative path consisting of int7 and int15 X,Y pairs.
    \param  Elements   number of relative X,Y pairs in the object
*/
int U_PMF_LEN_REL715(const char *contents, int Elements){
   int length=0;
   Elements *= 2; /* N pairs = 2N values */
   for( ; Elements; Elements--){
      /* X or Y value */
      if(*contents & U_TEST_INT7){ contents +=2; length +=2; } //int15
      else {                       contents +=1; length +=1; } //int7
   }
   return(length);
}

/**
    \brief Calculate the length in bytes of objects which are a 4 byte Count followed by Count * float bytes
    \return >=0 length == success, <0  error
    Object types whose size may be derived with this function are:
       U_PMF_COMPOUNDLINEDATA
       U_PMF_DASHEDLINEDATA    
*/
int U_PMF_LEN_FLOATDATA(const char *contents){
   int Size;
   U_PMF_SERIAL_get(&contents, &Size, 4, 1, U_LE);
   Size = 4*Size + 4;
   return(Size);
}

/**
    \brief Calculate the length in bytes of objects which are a 4 byte count followed by count bytes
    \return >=0 length == success, <0  error
    Object types whose size may be derived with this function are:
       U_PMF_BOUNDARYPATHDATA
       U_PMF_BOUNDARYPOINTDATA
       U_PMF_CUSTOMSTARTCAPDATA
       U_PMF_PATH
       U_PMF_LINEPATH
       U_PMF_REGIONNODEPATH
*/
int U_PMF_LEN_BYTEDATA(const char *contents){
   int Size;
   U_PMF_SERIAL_get(&contents, &Size, 4, 1, U_LE);
   Size += 4;
   return(Size);
}

/**
    \brief Create a string containing the curly bracket form of the 16 byte GUID value
    \return number of bytes in record, 0 on error
    \param  GUID   pointer to the 16 unsigned bytes
    \return string in curly bracket form.
    http://msdn.microsoft.com/en-us/library/cc230316.aspx

    Text form is Data1-Data2-Data3-Data4, the first 3 are stored as little endian integers, the last as a string (big endian).
*/
char *U_PMF_CURLYGUID_set(uint8_t *GUID){
   char *string=malloc(64);
   if(string){
      sprintf(string,"{%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X}",         
         GUID[3],GUID[2],GUID[1],GUID[0],
         GUID[5],GUID[4],
         GUID[7],GUID[6],
         GUID[8],GUID[9],
         GUID[10],GUID[11],GUID[12],GUID[13],GUID[14],GUID[15]
      );
   }
   return(string);
}

/**
    \brief Identify a known curly GUID
    \param  string   Curly GUID form.
    \return EmageEffects Enumerator 

    EMF+ manual 2.1.3.1, Microsoft name: ImageEffects Identifier
*/
int U_PMF_KNOWNCURLYGUID_set(const char *string){
   int status; 
   if(     !strcmp(string,"{633C80A4-1843-482B-9EF2-BE2834C5FDD4}")){ status = U_IEE_BlurEffectGuid;                  }                                                     
   else if(!strcmp(string,"{D3A1DBE1-8EC4-4C17-9F4C-EA97AD1C343D}")){ status = U_IEE_BrightnessContrastEffectGuid;    }
   else if(!strcmp(string,"{537E597D-251E-48DA-9664-29CA496B70F8}")){ status = U_IEE_ColorBalanceEffectGuid;          }
   else if(!strcmp(string,"{DD6A0022-58E4-4A67-9D9B-D48EB881A53D}")){ status = U_IEE_ColorCurveEffectGuid;            }
   else if(!strcmp(string,"{A7CE72A9-0F7F-40D7-B3CC-D0C02D5C3212}")){ status = U_IEE_ColorLookupTableEffectGuid;      }
   else if(!strcmp(string,"{718F2615-7933-40E3-A511-5F68FE14DD74}")){ status = U_IEE_ColorMatrixEffectGuid;           }
   else if(!strcmp(string,"{8B2DD6C3-EB07-4D87-A5F0-7108E26A9C5F}")){ status = U_IEE_HueSaturationLightnessEffectGuid;}
   else if(!strcmp(string,"{99C354EC-2A31-4F3A-8C34-17A803B33A25}")){ status = U_IEE_LevelsEffectGuid;                }
   else if(!strcmp(string,"{74D29D05-69A4-4266-9549-3CC52836B632}")){ status = U_IEE_RedEyeCorrectionEffectGuid;      }
   else if(!strcmp(string,"{63CBF3EE-C526-402C-8F71-62C540BF5142}")){ status = U_IEE_SharpenEffectGuid;               }
   else if(!strcmp(string,"{1077AF00-2848-4441-9489-44AD4C2D7A2C}")){ status = U_IEE_TintEffectGuid;                  }
   else {                                                             status = U_IEE_Unknown;                         }
   return(status);
}

/** \brief Load a GUID from text format into EMF+ file binary format.
    \param  string  Curly GUID as text, minus the barckets and dashes.
    \return GUID in EMF+ file binary format.


This accepts a string that is 16 bytes long = 32 characters hex (no dash spaces or brackets) as text.  
Text form is; Data1|Data2|Data3|Data4, first three are stored as little endian integers of 4,2,2 bytes, respectively,
last is stored like a string (big endian), after conversion from text hex to binary.

This function is not normally called by end user code.
*/
uint8_t *U_LOAD_GUID(char *string){
   uint32_t Data1,tData2,tData3,tByte;
   uint16_t Data2,Data3;
   char    *Data4 = string + 16;
   uint8_t *hold  = malloc(16);
   char    *lf    = (char *) hold;
   int      i;
   if(hold){
      Data1=tData2=tData3=0;               
      if(3 != sscanf(string +  0,"%8X",&Data1)  + 
              sscanf(string +  8,"%4X",&tData2) + 
              sscanf(string + 12,"%4X",&tData3)){
         free(hold);
         hold = NULL;
         goto bye;
      }
      Data2=tData2;
      Data3=tData3;
      U_PMF_MEMCPY_DSTSHIFT(&lf, &Data1, 4);
      U_PMF_MEMCPY_DSTSHIFT(&lf, &Data2, 2);
      U_PMF_MEMCPY_DSTSHIFT(&lf, &Data3, 2);
      if(U_IS_BE){ /* these fields are stored little endian */
         U_swap4(hold,1);
         U_swap2(hold+4,2);
      }
      /* remainder is converted byte by byte and stored in that order */
      for(i=0;i<8;i++,Data4+=2,lf++){
         if(1 != sscanf(Data4,"%2X",&tByte)){
            free(hold);
            hold = NULL;
            goto bye;
         }
         *lf=tByte;
      }
   }
bye:
   return(hold);
}

/**
    \brief Generate the 16 byte form from OID of the ImageEffects Identifier
    \param  OID  OID of the ImageEffects Identifier
    \return pointer to 16 byte buffer holding the long GUID binary form, or NULL on error.

    EMF+ manual 2.1.3.1, Microsoft name: ImageEffects Identifier
*/
uint8_t *U_OID_To_GUID(uint32_t OID){
   uint8_t *lf = NULL;
   if(     OID == U_PMF_IE_BLUR_OID                  ){ lf = U_LOAD_GUID("633C80A41843482B9EF2BE2834C5FDD4"); } 
   else if(OID == U_PMF_IE_BRIGHTNESSCONTRAST_OID    ){ lf = U_LOAD_GUID("D3A1DBE18EC44C179F4CEA97AD1C343D"); } 
   else if(OID == U_PMF_IE_COLORBALANCE_OID          ){ lf = U_LOAD_GUID("537E597D251E48DA966429CA496B70F8"); } 
   else if(OID == U_PMF_IE_COLORCURVE_OID            ){ lf = U_LOAD_GUID("DD6A002258E44A679D9BD48EB881A53D"); } 
   else if(OID == U_PMF_IE_COLORLOOKUPTABLE_OID      ){ lf = U_LOAD_GUID("A7CE72A90F7F40D7B3CCD0C02D5C3212"); } 
   else if(OID == U_PMF_IE_COLORMATRIX_OID           ){ lf = U_LOAD_GUID("718F2615793340E3A5115F68FE14DD74"); } 
   else if(OID == U_PMF_IE_HUESATURATIONLIGHTNESS_OID){ lf = U_LOAD_GUID("8B2DD6C3EB074D87A5F07108E26A9C5F"); } 
   else if(OID == U_PMF_IE_LEVELS_OID                ){ lf = U_LOAD_GUID("99C354EC2A314F3A8C3417A803B33A25"); } 
   else if(OID == U_PMF_IE_REDEYECORRECTION_OID      ){ lf = U_LOAD_GUID("74D29D0569A4426695493CC52836B632"); } 
   else if(OID == U_PMF_IE_SHARPEN_OID               ){ lf = U_LOAD_GUID("63CBF3EEC526402C8F7162C540BF5142"); } 
   else if(OID == U_PMF_IE_TINT_OID                  ){ lf = U_LOAD_GUID("1077AF0028484441948944AD4C2D7A2C"); }
   return(lf);
}

/**
    \brief copy data and shift source pointer by the amount of data moved
    \param Dst Destination in memory
    \param Src Source in memory
    \param Size Number of bytes to move
*/
void U_PMF_MEMCPY_SRCSHIFT(void *Dst, const char **Src, size_t Size){
   if(Dst)memcpy(Dst, *Src, Size);
   *Src += Size;
}

/**
    \brief copy data and shift destination pointer by the amount of data moved
    \param Dst Destination in memory (this must not be NULL)
    \param Src Source in memory (if this is NULL, fill with that many zero bytes instead)
    \param Size Number of bytes to move
*/
void U_PMF_MEMCPY_DSTSHIFT(char **Dst, const void *Src, size_t Size){
   if(Src){ memcpy(*Dst, Src, Size); }
   else {   memset(*Dst, 0,   Size); }
   *Dst += Size;
}

/**
    \brief Copy the single instance at Src repeatedly to Dst.
    \param Dst   Destination in memory
    \param Src   Source in memory (if this is NULL, fill with that many zero bytes instead)
    \param Size  number of bytes in single instance that is template.
    \param Reps  Number of instances of the template to opy
*/
void U_PMF_REPCPY_DSTSHIFT(char **Dst, const void *Src, size_t Size, size_t Reps){
   for(;Reps;Reps--){
      if(Src){ memcpy(*Dst, Src, Size); }
      else {   memset(*Dst, 0,   Size); }
      *Dst += Size;
   }
}

/**
    \brief save pointer to data and shift source pointer by the amount of data moved
    \param Dst Destination in memory
    \param Src Source in memory or NULL. If NULL Dst is set to NULL.
    \param Size Number of bytes to move
*/
void U_PMF_PTRSAV_SHIFT(const char **Dst, const char **Src, size_t Size){
   if(*Src){
      if(Dst)*Dst = *Src;
      *Src += Size;
   }
   else {
      if(Dst)*Dst = NULL;
   }
}

/**
    \brief save pointer to data and shift source pointer by the amount of data moved
    \return 1 on sucess, 0 on error
    \param Dst Destination in memory
    \param Src Source in memory or NULL. If NULL Dst is set to NULL.
    \param Doit Assign if true, otherwise, set to NULL
*/
int U_PMF_PTRSAV_COND(const char **Dst, const char *Src, int Doit){
   if(!Dst){ return(0); }
   if(Src && Doit){ *Dst = Src;  }
   else {           *Dst = NULL; }
   return(1);
}
/*

   =====================================================================================
   start of U_PMF_*_get() functions 

*/

/**
    \brief Get the 16 bit unsigned Flags field from a header.
    \param  contents   Record from which to extract data, will be incremented by header size.
    \return Flags field

    In many records the only value needed from the header is Flags.  Rather than mapping
    the entire Header and returning it, in these instances this function may be called to
    just get this one value.
*/
uint16_t U_PMF_HEADERFLAGS_get(const char *contents){
   uint16_t Flags;
   const char *cptr = contents + offsetof(U_PMF_CMN_HDR,Flags);
   U_PMF_SERIAL_get(&cptr, &Flags,  2, 1, U_LE); /* EMF+ manual documents it as BE, but this code implements it as LE*/
   return(Flags);
}

/**
    \brief Retrieve whichever header fields are requested.  NULL pointers do not retrieve.
    \param  contents   Record from which to extract data, will be incremented by header size.
    \param  Type       Record type
    \param  Flags      Record flags
    \param  Size       Records size
    \param  Datasize   Data size
    \return 1 on success, 0 on failure.
*/
int U_PMF_HEADERFIELDS_get(const char *contents,
   uint16_t *Type, uint16_t *Flags, uint32_t *Size, uint32_t *Datasize){
   if(!contents){ return(0); }
   U_PMF_SERIAL_get(&contents, Type,     2, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Flags,    2, 1, U_LE); /* EMF+ manual documents it as BE, but this code implements it as LE*/
   U_PMF_SERIAL_get(&contents, Size,     4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Datasize, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get the entire EMF+ record header.
    \param  contents   Record from which to extract data, will be offset by header size.
    \param  Header     Location to store data (may be NULL)
    \returns 1
    If Header is Null, nothing is stored but contents is still offset.
*/
int U_PMF_CMN_HDR_get(const char **contents, U_PMF_CMN_HDR *Header){
   if(!contents || !*contents){ return(0); }
   if(Header){
      U_PMF_SERIAL_get(contents, &(Header->Type),     2, 1, U_LE);
      U_PMF_SERIAL_get(contents, &(Header->Flags),    2, 1, U_LE); /* EMF+ manual documents it as BE, but this code implements it as LE*/
      U_PMF_SERIAL_get(contents, &(Header->Size),     4, 1, U_LE);
      U_PMF_SERIAL_get(contents, &(Header->DataSize), 4, 1, U_LE);
   }
   else {
      *contents += sizeof(U_PMF_CMN_HDR);
   }
   return(1);
}

/**
    \brief return the size in bytes of the EMF+ record
    \param  contents   Record from which to extract data, will not be modified.
    \returns size, or 0 if contents is Null
*/
int U_PMF_RECORD_SIZE_get(const char *contents){
   if(!contents){ return(0); }
   int Size;
   const char *from = contents + 4;
   U_PMF_SERIAL_get(&from, &Size, 4, 1, U_LE);
   return(Size);
}

/**
    \brief Return the size of a PenData object from an EMF+ record.
    \param  PenData   Address in memory where the PenData object starts.
    \returns size of the object in bytes
*/
int U_PMF_LEN_PENDATA(const char *PenData){
   uint32_t Flags;
   int length=12;  /* Flags, Unit, Width */
   U_PMF_SERIAL_get(&PenData, &Flags,  4, 1, U_LE);
   PenData += 8;  /* skip Unit and Width */
   length  += U_PMF_LEN_OPTPENDATA(PenData, Flags);
   return(length);
}

/**
    \brief Return the size of an OptPenData object from an EMF+ record.
    \param  PenData   Address in memory where the PenData object starts.
    \param  Flags     PenData Flags that indicate which fields are present.
    \returns size of the object in bytes
*/
int U_PMF_LEN_OPTPENDATA(const char *PenData, uint32_t Flags){
   int length=0;
   if(Flags & U_PD_Transform){       length += sizeof(U_PMF_TRANSFORMMATRIX);           }
   if(Flags & U_PD_StartCap){        length += sizeof(int32_t);                         }
   if(Flags & U_PD_EndCap){          length += sizeof(int32_t);                         }
   if(Flags & U_PD_Join){            length += sizeof(uint32_t);                        }
   if(Flags & U_PD_MiterLimit){      length += sizeof(U_FLOAT);                         }
   if(Flags & U_PD_LineStyle){       length += sizeof(int32_t);                         }
   if(Flags & U_PD_DLCap){           length += sizeof(int32_t);                         }
   if(Flags & U_PD_DLOffset){        length += sizeof(int32_t);                         }
   if(Flags & U_PD_DLData){          length += U_PMF_LEN_FLOATDATA(PenData + length);   }
   if(Flags & U_PD_NonCenter){       length += sizeof(int32_t);                         }
   if(Flags & U_PD_CLData){          length += U_PMF_LEN_FLOATDATA(PenData + length);   }
   if(Flags & U_PD_CustomStartCap){  length += U_PMF_LEN_BYTEDATA(PenData + length);    }
   if(Flags & U_PD_CustomEndCap){    length += U_PMF_LEN_BYTEDATA(PenData + length);    }
   return(length);
}

/**
    \brief  Create and set a U_PMF_BRUSH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Bd         U_PSEUDO_OBJ containing one of the 5 types of Brush data

    EMF+ manual 2.2.1.1, Microsoft name: EmfPlusBrush Object
*/
U_PSEUDO_OBJ *U_PMF_BRUSH_set(uint32_t Version, const U_PSEUDO_OBJ *Bd){
   if(!Bd){ return(NULL); }
   int32_t Type = U_OID_To_BT(Bd->Type);
   if(Type < 0){ return(NULL); }
   const U_SERIAL_DESC List[] = {
      {&Version, 4,        1, U_LE},
      {&Type,    4,        1, U_LE},
      {Bd->Data, Bd->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BRUSH_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_CUSTOMLINECAP PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Ld         U_PSEUDO_OBJ containing one of the 2 types of Linecap data

    EMF+ manual 2.2.1.2, Microsoft name: EmfPlusCustomLineCap Object
*/
U_PSEUDO_OBJ *U_PMF_CUSTOMLINECAP_set(uint32_t Version, const U_PSEUDO_OBJ *Ld){
   if(!Ld){ return(NULL); }
   int32_t Type = U_OID_To_CLCDT(Ld->Type);
   if(Type<0){ return(NULL); }
   const U_SERIAL_DESC List[] = {
      {&Version, 4,        1, U_LE},
      {&Type,    4,        1, U_LE},
      {Ld->Data, Ld->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CUSTOMLINECAP_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_FONT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  EmSize     em size in units of SizeUnit
    \param  SizeUnit   UnitType enumeration
    \param  FSFlags    FontStyle flags
    \param  Length     Number of Unicode Characters in FamilyName
    \param  Font       Unicode (UTF-16LE) fontname

    EMF+ manual 2.2.1.3, Microsoft name: EmfPlusFont Object
*/
U_PSEUDO_OBJ *U_PMF_FONT_set(uint32_t Version, U_FLOAT EmSize, uint32_t SizeUnit,
      int32_t FSFlags, uint32_t Length, const uint16_t *Font){
   uint32_t cbFont = 2 * wchar16len(Font); /* this need not be 2*Length parameter */
   uint32_t pad = (0x3 & cbFont ? 2 : 0);
   const U_SERIAL_DESC List[] = {
      {&Version, 4,      1,             U_LE},
      {&EmSize,  4,      1,             U_LE},
      {&SizeUnit,4,      1,             U_LE},
      {&FSFlags, 4,      1,             U_LE},
      {NULL,     4,      1,             U_LE},  /* NULL is for Reserved field */
      {&Length,  4,      1,             U_LE},
      {Font,     cbFont, 1,             U_LE},
      {NULL,     pad,    (pad ? 1 : 0), (pad ? U_XE : U_XX)}, /* Entire record must be a multiple of 4 */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_FONT_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IMAGE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Id         U_PSEUDO_OBJ containing one of the 2 types of image data

    EMF+ manual 2.2.1.4, Microsoft name: EmfPlusImage Object
*/
U_PSEUDO_OBJ *U_PMF_IMAGE_set(uint32_t Version, const U_PSEUDO_OBJ *Id){
   if(!Id){ return(NULL); }
   int32_t Type = U_OID_To_IDT(Id->Type);
   if(Type<0){ return(NULL);}
   const U_SERIAL_DESC List[] = {
      {&Version, 4,        1, U_LE},
      {&Type,    4,        1, U_LE},
      {Id->Data, Id->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IMAGE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IMAGEATTRIBUTES PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version     EmfPlusGraphicsVersion object
    \param  WrapMode    WrapMode object
    \param  ClampColor  EmfPlusARGB object
    \param  ObjectClamp ObjectClamp Identifiers

    EMF+ manual 2.2.1.5, Microsoft name: EmfPlusImageAttributes Object
*/
U_PSEUDO_OBJ *U_PMF_IMAGEATTRIBUTES_set(uint32_t Version, uint32_t WrapMode, uint32_t ClampColor, uint32_t ObjectClamp){
   uint32_t Reserved=0;
   const U_SERIAL_DESC List[] = {
      {&Version,     4, 1, U_LE},
      {&Reserved,    4, 1, U_LE},
      {&WrapMode,    4, 1, U_LE},
      {&ClampColor,  4, 1, U_LE},
      {&ObjectClamp, 4, 1, U_LE},
      {&Reserved,    4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IMAGEATTRIBUTES_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_PATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Points     U_PSEUDO_OBJ containing array of points (of type PMFPointR, PMFPoint, or PMFPointF, determined by U_PPF_P and U_PPF_C bits in Flags)
    \param  Types      U_PSEUDO_OBJ containing array of types (U_PMF_PATHPOINTTYPE or U_PMF_PATHPOINTTYPERLE, determined by  U_PPF_R big in Flags)

    EMF+ manual 2.2.1.6, Microsoft name: EmfPlusPath Object
*/
U_PSEUDO_OBJ *U_PMF_PATH_set(uint32_t Version, const U_PSEUDO_OBJ *Points, const U_PSEUDO_OBJ *Types){
   int ctype, RelAbs, rtype;
   int pad;
   if(Points){
      if(     Points->Type == U_PMF_POINTR_OID){                               RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){           RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){           RelAbs = 0; ctype = 0; }
      else {                                                                   return(NULL);          }
   }
   else {                                                                      return(NULL);          }
   if(Types){
      if(     Types->Type  == (U_PMF_PATHPOINTTYPERLE_OID | U_PMF_ARRAY_OID)){ rtype = 1;             }
      else if(Types->Type  == (U_PMF_PATHPOINTTYPE_OID | U_PMF_ARRAY_OID)){    rtype = 0;             }
      else {                                                                   return(NULL);          }
   }
   else {                                                                      return(NULL);          }
   uint16_t Flags = (rtype ? U_PPF_R : 0) | (ctype ? U_PPF_C : 0)| (RelAbs ? U_PPF_P : 0);
   pad = (0x3 & (Points->Used + Types->Used));
   if(pad){ pad = 4 - pad; }
   const U_SERIAL_DESC List[] = {
      {&Version,         4,                1,             U_LE               },
      {Points->Data,     4,                1,             U_XE               }, /* Elements from Points */
      {&Flags,           2,                1,             U_LE               },
      {NULL,             2,                1,             U_LE               }, /* Reserved field */
      {Points->Data + 4, Points->Used - 4, 1,             U_XE               }, /* omit Points Elements */
      {Types->Data +4,   Types->Used - 4,  1,             U_XE               }, /* omit Types  Elements */
      {NULL,             pad,              (pad ? 1 : 0), (pad ? U_XE : U_XX)}, /* if no padding is needed the Units will be zero and nothing will happen */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATH_OID, List);
   return(po);
}


/**
    \brief  Create and set a U_PMF_PATH PseudoObject that uses U_PMF_POINTF coordinates
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Path       U_DPSEUDO_OBJ containing a path.

    EMF+ manual 2.2.1.6, Microsoft name: EmfPlusPath Object
*/
U_PSEUDO_OBJ *U_PMF_PATH_set2(uint32_t Version, const U_DPSEUDO_OBJ *Path){
   if(!Path || !Path->Elements){ return(NULL); }
   uint16_t Flags = 0;
   int pad = (0x3 & Path->Elements);
   if(pad){ pad = 4 - pad; }
   const U_SERIAL_DESC List[] = {
      {&Version,               4,                1,               U_LE               },
      {&Path->Elements,        4,                1,               U_LE               },
      {&Flags,                 2,                1,               U_LE               },
      {NULL,                   2,                1,               U_LE               }, /*  Reserved field */
      {Path->poPoints->Data,   4,                2*Path->Elements,U_XE               }, /*  raw OID, so no leading Elements to omit */
      {Path->poTypes->Data,    1,                Path->Elements,  U_XE               }, /*  raw OID, so no leading Elements to omit */
      {NULL,                   pad,              (pad ? 1 : 0),   (pad ? U_XE : U_XX)}, /* if no padding is needed the Units will be zero and nothing will happen */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATH_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_PATH PseudoObject that uses U_PMF_POINT (int 16) coordinates
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Path       U_DPSEUDO_OBJ containing a path.

    EMF+ manual 2.2.1.6, Microsoft name: EmfPlusPath Object
*/
U_PSEUDO_OBJ *U_PMF_PATH_set3(uint32_t Version, const U_DPSEUDO_OBJ *Path){
   if(!Path || !Path->Elements){return(NULL);  }
   uint16_t Flags = U_PPF_C;
   int pad = (0x3 & Path->Elements);
   if(pad){ pad = 4 - pad; }
   U_PMF_POINT *Points16 = POINTF_To_POINT16_LE((U_PMF_POINTF *)Path->poPoints->Data, Path->Elements);
   if(!Points16){ return(NULL); }
   const U_SERIAL_DESC List[] = {
      {&Version,               4,                1,               U_LE               },
      {&Path->Elements,        4,                1,               U_LE               },
      {&Flags,                 2,                1,               U_LE               },
      {NULL,                   2,                1,               U_LE               }, /*  Reserved field */
      {Points16,               2,                2*Path->Elements,U_XE               }, /*  raw data, so no leading Elements to omit */
      {Path->poTypes->Data,    1,                Path->Elements,  U_XE               }, /*  raw data, so no leading Elements to omit */
      {NULL,                   pad,              (pad ? 1 : 0),   (pad ? U_XE : U_XX)}, /* if no padding is needed the Units will be zero and nothing will happen */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATH_OID, List);
   free(Points16);
   return(po);
}

/**
    \brief  Create and set a U_PMF_PEN PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  PenData    U_PSEUDO_OBJ containing U_PMF_PENDATA object
    \param  Brush      U_PSEUDO_OBJ containing U_PMF_BRUSH object 

    EMF+ manual 2.2.1.7, Microsoft name: EmfPlusPen Object
*/
U_PSEUDO_OBJ *U_PMF_PEN_set(uint32_t Version, const U_PSEUDO_OBJ *PenData, const U_PSEUDO_OBJ *Brush){
   if(!PenData   || (PenData->Type    != U_PMF_PENDATA_OID)){ return(NULL); }
   if(!Brush     || (Brush->Type      != U_PMF_BRUSH_OID)  ){ return(NULL); }
   const U_SERIAL_DESC List[] = {
      {&Version,        4,             1, U_LE},
      {NULL,            4,             1, U_LE},
      {PenData->Data,   PenData->Used, 1, U_XE},
      {Brush->Data,     Brush->Used,   1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PEN_OID, List);
   return(po);
}


/**
    \brief  Create and set a U_PMF_REGION PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Version    EmfPlusGraphicsVersion object
    \param  Count      Number of CHILD nodes.  This is one less than the total number of U_PMF_REGIONNODE objects in Nodes.
    \param  Nodes      U_PSEUDO_OBJ containing U_PMF_REGIONNODE object (Nodes defining region, may be a single element or a binary tree) 

    EMF+ manual 2.2.1.8, Microsoft name: EmfPlusRegion Object
*/
U_PSEUDO_OBJ *U_PMF_REGION_set(uint32_t Version, uint32_t Count, const U_PSEUDO_OBJ *Nodes){
   if(!Nodes || Nodes->Type  != U_PMF_REGIONNODE_OID)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Version,    4,           1, U_LE},
      {&Count,      4,           1, U_LE},
      {Nodes->Data, Nodes->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_REGION_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_STRINGFORMAT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Sfs       pointer to U_PMF_STRINGFORMAT structure, with no variable part
    \param  Sfd       (optional) U_PSEUDO_OBJ containing U_PMF_STRINGFORMATDATA object

    EMF+ manual 2.2.1.9, Microsoft name: EmfPlusStringFormat Object
*/
U_PSEUDO_OBJ *U_PMF_STRINGFORMAT_set(U_PMF_STRINGFORMAT *Sfs, const U_PSEUDO_OBJ *Sfd){
   if(!Sfs){ return(NULL); }
   if(Sfd){
     if((!Sfs->TabStopCount && !Sfs->RangeCount) || (Sfd->Type  != U_PMF_STRINGFORMATDATA_OID))return(NULL);
   }
   else {
     if(Sfs->TabStopCount || Sfs->RangeCount)return(NULL);
   }
   const U_SERIAL_DESC List[] = {
      {&Sfs->Version,            4,                     1, U_LE},
      {&Sfs->Flags,              4,                     1, U_LE},
      {&Sfs->Language,           4,                     1, U_LE},
      {&Sfs->StringAlignment,    4,                     1, U_LE},
      {&Sfs->LineAlign,          4,                     1, U_LE},
      {&Sfs->DigitSubstitution,  4,                     1, U_LE},
      {&Sfs->DigitLanguage,      4,                     1, U_LE},
      {&Sfs->FirstTabOffset,     4,                     1, U_LE},
      {&Sfs->HotkeyPrefix,       4,                     1, U_LE},
      {&Sfs->LeadingMargin,      4,                     1, U_LE},
      {&Sfs->TrailingMargin,     4,                     1, U_LE},
      {&Sfs->Tracking,           4,                     1, U_LE},
      {&Sfs->Trimming,           4,                     1, U_LE},
      {&Sfs->TabStopCount,       4,                     1, U_LE},
      {&Sfs->RangeCount,         4,                     1, U_LE},
      {(Sfd ? Sfd->Data : NULL), (Sfd ? Sfd->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_STRINGFORMAT_OID, List);
   return(po);
}

/**
    \brief  Create and set a PMF_4NUM PseudoObject (used for BrushID's)
    \return Pointer to PseudoObject, NULL on error
    \param  BrushID     U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
   
*/
U_PSEUDO_OBJ *U_PMF_4NUM_set(uint32_t BrushID){
   if(BrushID>63){ return(NULL); }
   const U_SERIAL_DESC List[] = {
      {&BrushID, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_4NUM_OID, List);
   return(po);
}

/**
    \brief Create and set a U_PMF_ARGB PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Alpha      Alpha       (0-255)
    \param  Red        Red   color (0-255)
    \param  Green      Green color (0-255)
    \param  Blue       Blue  color (0-255)

    EMF+ manual 2.2.2.1, Microsoft name: EmfPlusARGB Object
*/
U_PSEUDO_OBJ *U_PMF_ARGB_set(uint8_t Alpha, uint8_t Red, uint8_t Green, uint8_t Blue){
   const U_SERIAL_DESC List[] = {
      {&Blue,  1, 1, U_XE},
      {&Green, 1, 1, U_XE},
      {&Red,   1, 1, U_XE},
      {&Alpha, 1, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_ARGB_OID, List);
   return(po);
}

/**
    \brief Create and set an Array of U_PMF_ARGB valus in a PseudoObject
    \return Pointer to PseudoObject containing the count, followed by the array of colors, NULL on error
    \param  Count      Number of entries in Colors
    \param  Colors     Array of ARGB values

    EMF+ manual 2.2.2.1, Microsoft name: EmfPlusARGB Object
*/
U_PSEUDO_OBJ *U_PMF_ARGBN_set(uint32_t Count, U_PMF_ARGB *Colors){
   const U_SERIAL_DESC List[] = {
      {&Count, 4, 1,     U_LE},
      {Colors, 4, Count, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_ARGB_OID | U_PMF_ARRAY_OID, List);
   return(po);
}

/**
    \brief Set a U_PMF_ARGB object
    \return Object
    \param  Alpha      Alpha       (0-255)
    \param  Red        Red   color (0-255)
    \param  Green      Green color (0-255)
    \param  Blue       Blue  color (0-255)

    EMF+ manual 2.2.2.1, Microsoft name: EmfPlusARGB Object
*/
U_PMF_ARGB U_PMF_ARGBOBJ_set(uint8_t Alpha, uint8_t Red, uint8_t Green, uint8_t Blue){
   U_PMF_ARGB argb;
   char *ptr = (char *) &argb;
   U_PMF_MEMCPY_DSTSHIFT(&ptr, &Blue,  1);
   U_PMF_MEMCPY_DSTSHIFT(&ptr, &Green, 1);
   U_PMF_MEMCPY_DSTSHIFT(&ptr, &Red,   1);
   U_PMF_MEMCPY_DSTSHIFT(&ptr, &Alpha, 1);
   return(argb);
}

/**
    \brief  Create and set a U_PMF_BITMAP PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Bs       pointer to U_PMF_BITMAP structure, with no variable part
    \param  Bm       U_PSEUDO_OBJ containing an U_PMF_BITMAPDATA or U_PMF_COMPRESSEDIMAGE object 

    EMF+ manual 2.2.2.2, Microsoft name: EmfPlusBitmap Object
*/
U_PSEUDO_OBJ *U_PMF_BITMAP_set(const U_PMF_BITMAP *Bs, const U_PSEUDO_OBJ *Bm){
   if(!Bs)return(NULL);
   if(Bm->Type  != U_PMF_BITMAPDATA_OID &&
      Bm->Type  != U_PMF_COMPRESSEDIMAGE_OID )return(NULL);
   uint32_t Pad = UP4(Bm->Used) - Bm->Used;  /* undocumented padding, must be present for at least PNG */
   const U_SERIAL_DESC List[] = {
      {Bs,       4,               5,             U_LE},
      {Bm->Data, Bm->Used,        1,             U_XE},
      {NULL,     (Pad ? Pad : 0), (Pad ? 1 : 0), U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BITMAP_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_BITMAPDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Ps     (optional) U_PSEUDO_OBJ containing a U_PMF_PALETTE structure
    \param  cbBm   Bytes in Bm
    \param  Bm     An array of bytes, meaning depends on fields in U_PMF_BITMAP object and the PixelFormat enumeration.

    EMF+ manual 2.2.2.3, Microsoft name: EmfPlusBitmapData Object
*/
U_PSEUDO_OBJ *U_PMF_BITMAPDATA_set( const U_PSEUDO_OBJ *Ps, int cbBm, const char *Bm){
   if(Ps && (Ps->Type  != U_PMF_PALETTE_OID))return(NULL);
   if(!Bm && cbBm)return(NULL);
   const U_SERIAL_DESC List[] = {
      {(Ps ? Ps->Data : NULL), (Ps  ? Ps->Used : 0), (Ps ? 1 : 0), U_LE},
      {Bm,                     cbBm,                 1,            U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BITMAPDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_BLENDCOLORS PseudoObject
    \return Pointer to PseudoObject, NULL on Positions and Colors
    \param  Elements   number of elements in Positions, must agree with the number of Colors.
    \param  Positions  positions along gradient line. The first position MUST be 0.0 and the last MUST be 1.0.
    \param  Colors     U_PSEUDO_OBJ containing an array of U_PMF_ARGB objects: object colors at positions on gradient line

    EMF+ manual 2.2.2.4, Microsoft name: EmfPlusBlendColors Object
*/
U_PSEUDO_OBJ *U_PMF_BLENDCOLORS_set(uint32_t Elements, const U_FLOAT *Positions, const U_PSEUDO_OBJ *Colors){
   if(!Colors || !Positions || Colors->Type != (U_PMF_ARGB_OID | U_PMF_ARRAY_OID)){ return(NULL); }
   uint32_t CElements = (Colors->Used - 4)/4;
   if(CElements != Elements){ return(NULL); }
   const U_SERIAL_DESC List[] = {
      {&CElements,       4,                1,          U_LE},
      {Positions,        4,                CElements,  U_LE},
      {Colors->Data + 4, Colors->Used - 4, 1,          U_XE}, /* omit Elements part of this PseudoObject */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BLENDCOLORS_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_BLENDCOLORS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements      members in osition, inluding Start and End (0.0 - 1.0)
    \param  StartColor    Start Color (U_PMF_ARGB)
    \param  EndColor      End Color (U_PMF_ARGB)

    EMF+ manual 2.2.2.5, Microsoft name: EmfPlusBlendFactors Object

    
    Positions always start at 0.0 and always end at 1.0.  It is not well documented but other
    start and end values generally do not work.
*/
U_PSEUDO_OBJ *U_PMF_BLENDCOLORS_linear_set(uint32_t Elements, U_PMF_ARGB StartColor, U_PMF_ARGB EndColor){
   double dP,dR,dG,dB,dA,P,R,G,B,A;
   U_FLOAT  StartPos = 0.0;
   U_FLOAT  EndPos = 1.0;
   U_FLOAT *Positions;
   U_FLOAT *pP;
   U_PMF_ARGB *Colors;
   U_PMF_ARGB *pC;
   unsigned int i;
   if(Elements <= 2 ){ return(NULL); }
   pP = Positions = (U_FLOAT *)malloc(Elements *sizeof(U_FLOAT));
   if(!Positions){ return(NULL); }
   pC = Colors    = (U_PMF_ARGB *)malloc(Elements *sizeof(U_PMF_ARGB));
   if(!Colors){
      free(Positions); 
      return(NULL);
   }
   dP = (EndPos    - StartPos   )/(float)(Elements - 1);
   dB = ((double)EndColor.Blue  - (double)StartColor.Blue )/(double)(Elements - 1);
   dG = ((double)EndColor.Green - (double)StartColor.Green)/(double)(Elements - 1);
   dR = ((double)EndColor.Red   - (double)StartColor.Red  )/(double)(Elements - 1);
   dA = ((double)EndColor.Alpha - (double)StartColor.Alpha)/(double)(Elements - 1);
   P  = StartPos;
   B  = StartColor.Blue; 
   G  = StartColor.Green; 
   R  = StartColor.Red; 
   A  = StartColor.Alpha; 
   for(i=0;i<Elements;i++,pC++,pP++){ /* hopefully the rounding errors are not a problem, used doubles to minimize that */
      *pP  = P;  
      P   += dP;
      *pC  = (U_PMF_ARGB){B,G,R,A};
      B   += dB;
      G   += dG;
      R   += dR;
      A   += dA;
   }
   U_PSEUDO_OBJ *poColors = U_PMF_ARGBN_set(Elements, Colors);
   U_PSEUDO_OBJ *po = U_PMF_BLENDCOLORS_set(Elements, Positions, poColors);
   U_PO_free(&poColors);
   free(Positions);
   free(Colors);
   return(po);
}


/**
    \brief  Create and set a U_PMF_BLENDFACTORS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements   members in each array
    \param  Positions  positions along gradient line.  The first position MUST be 0.0 and the last MUST be 1.0.
    \param  Factors    blending factors, 0.0->1.0 values, inclusive

    EMF+ manual 2.2.2.5, Microsoft name: EmfPlusBlendFactors Object
*/
U_PSEUDO_OBJ *U_PMF_BLENDFACTORS_set(uint32_t Elements, const U_FLOAT *Positions, const U_FLOAT *Factors){
   if(!Positions || !Factors)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements, 4, 1,        U_LE},
      {Positions, 4, Elements, U_LE},
      {Factors,   4, Elements, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BLENDFACTORS_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_BLENDFACTORS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements      members in osition, inluding Start and End (0.0 - 1.0)
    \param  StartFactor   Start Factor (0.0 - 1.0)
    \param  EndFactor     End Factor (0.0 - 1.0)

    EMF+ manual 2.2.2.5, Microsoft name: EmfPlusBlendFactors Object

    
    Positions always start at 0.0 and always end at 1.0.  It is not well documented but other
    start and end values generally do not work.
*/
U_PSEUDO_OBJ *U_PMF_BLENDFACTORS_linear_set(uint32_t Elements, U_FLOAT StartFactor, U_FLOAT EndFactor){
   double dP,dF,P,F;
   U_FLOAT  StartPos = 0.0;
   U_FLOAT  EndPos = 1.0;
   U_FLOAT *Positions;
   U_FLOAT *Factors;
   U_FLOAT *pP;
   U_FLOAT *pF;
   unsigned int i;
   if(Elements <= 2 ){ return(NULL); }
   pP = Positions = (U_FLOAT *)malloc(Elements *sizeof(U_FLOAT));
   if(!Positions){ return(NULL); }
   pF = Factors   = (U_FLOAT *)malloc(Elements *sizeof(U_FLOAT));
   if(!Factors){
      free(Positions); 
      return(NULL);
   }
   dP = (EndPos    - StartPos   )/(float)(Elements - 1);
   dF = (EndFactor - StartFactor)/(float)(Elements - 1);
   P  = StartPos;
   F  = StartFactor;
   for(i=0;i<Elements;i++){ /* hopefully the rounding errors are not a problem, used doubles to minimize that */
      *pP++ = P;  P += dP;
      *pF++ = F;  F += dF;
   }
   U_PSEUDO_OBJ *po = U_PMF_BLENDFACTORS_set(Elements, Positions, Factors);
   free(Positions);
   free(Factors);
   return(po);
}

/**
    \brief  Create and set a U_PMF_BOUNDARYPATHDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Path       U_PSEUDO_OBJ containing U_PMF_PATH object

    EMF+ manual 2.2.2.6, Microsoft name: EmfPlusBoundaryPathData Object
*/
U_PSEUDO_OBJ *U_PMF_BOUNDARYPATHDATA_set(const U_PSEUDO_OBJ *Path){
   if(!Path || Path->Type  != U_PMF_PATH_OID)return(NULL);
   /* PO Used is size_t, might be 8 bytes, value in record must be 4 bytes */
   uint32_t Used = Path->Used;
   const U_SERIAL_DESC List[] = {
      {&Used,      4,          1, U_LE},
      {Path->Data, Path->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BOUNDARYPATHDATA_OID, List);
   return(po);
}


/**
    \brief  Create and set a U_PMF_BOUNDARYPOINTDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements   members in each array
    \param  Points     array of U_PMF_POINTF

    EMF+ manual 2.2.2.7, Microsoft name: EmfPlusBoundaryPointData Object
*/
U_PSEUDO_OBJ *U_PMF_BOUNDARYPOINTDATA_set(uint32_t Elements, const U_PMF_POINTF *Points){
   if(!Points)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements, 4, 1,         U_LE},
      {Points,    4, 2*Elements,U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_BOUNDARYPOINTDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_CHARACTERRANGE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  First      First position in range
    \param  Length     Range length

    EMF+ manual 2.2.2.8, Microsoft name: EmfPlusCharacterRange Object
*/
U_PSEUDO_OBJ *U_PMF_CHARACTERRANGE_set(int32_t First, int32_t Length){
   const U_SERIAL_DESC List[] = {
      {&First,  4, 1, U_LE},
      {&Length, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CHARACTERRANGE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_COMPOUNDLINEDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements   Members in Widths
    \param  Widths     Array of U_FLOAT Line or gap widths (0.0 <-> 1.0, fraction of total line width )

    EMF+ manual 2.2.2.9, Microsoft name: EmfPlusCompoundLineData Object
*/
U_PSEUDO_OBJ *U_PMF_COMPOUNDLINEDATA_set(int32_t Elements, const char *Widths){
   if(!Widths)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements ,4, 1,       U_LE},
      {Widths,    4, Elements,U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_COMPOUNDLINEDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_COMPRESSEDIMAGE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  cbImage     Bytes in Image
    \param  Image       Stored image in one of the supported formats (GIF, PNG, etc.).

    EMF+ manual 2.2.2.10, Microsoft name: EmfPlusCompressedImage Object
*/
U_PSEUDO_OBJ *U_PMF_COMPRESSEDIMAGE_set(int32_t cbImage, const char *Image){
   if(!cbImage || !Image)return(NULL);
   const U_SERIAL_DESC List[] = {
      {Image,    cbImage, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_COMPRESSEDIMAGE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_CUSTOMENDCAPDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Clc       U_PSEUDO_OBJ containing a U_PMF_CUSTOMLINECAP object

    EMF+ manual 2.2.2.11, Microsoft name: EmfPlusCustomEndCapData Object
*/
U_PSEUDO_OBJ *U_PMF_CUSTOMENDCAPDATA_set(const U_PSEUDO_OBJ *Clc){
   if(!Clc || Clc->Type  != U_PMF_CUSTOMLINECAP_OID)return(NULL);
   /* PO Used is size_t, might be 8 bytes, value in record must be 4 bytes */
   uint32_t Used = Clc->Used;
   const U_SERIAL_DESC List[] = {
      {&Used,      4,         1, U_LE},
      {Clc->Data,  Clc->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CUSTOMENDCAPDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_CUSTOMLINECAPARROWDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Width       Arrow cap width (is multiplied by line width before draw)
    \param  Height      Arrow cap length (is multiplied by line width before draw)
    \param  MiddleInset Pixels between outer edge and filled region
    \param  FillState   If set, fill, otherwise, only border
    \param  StartCap    LineCap enumeration (type of cap)
    \param  EndCap      LineCap enumeration
    \param  Join        LineJoin enumeration
    \param  MiterLimit  Maximum (miter length / line width)
    \param  WidthScale  Scale for U_PMF_CUSTOMLINECAP object

    EMF+ manual 2.2.2.12, Microsoft name: EmfPlusCustomLineCapArrowData Object
*/
U_PSEUDO_OBJ *U_PMF_CUSTOMLINECAPARROWDATA_set(U_FLOAT Width, U_FLOAT Height, 
      U_FLOAT MiddleInset, uint32_t FillState, uint32_t StartCap, uint32_t EndCap, uint32_t Join,
      U_FLOAT MiterLimit, U_FLOAT WidthScale
   ){
   const U_SERIAL_DESC List[] = {
      {&Width,       4, 1, U_LE},
      {&Height      ,4, 1, U_LE},
      {&MiddleInset, 4, 1, U_LE},
      {&FillState,   4, 1, U_LE},
      {&StartCap,    4, 1, U_LE},
      {&EndCap,      4, 1, U_LE},
      {&Join,        4, 1, U_LE},
      {&MiterLimit,  4, 1, U_LE},
      {&WidthScale,  4, 1, U_LE},
      {NULL,         8, 2, U_LE}, /* FillHotSpots and LineHotSpots */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CUSTOMLINECAPARROWDATA_OID, List);
   return(po);
}


/**
    \brief  Create and set a U_PMF_CUSTOMLINECAPDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Flags      CustomLineCapData flags
    \param  Cap        LineCap enumeration (type of cap)
    \param  Inset      Distance line cap start -> line end
    \param  StartCap   LineCap enumeration
    \param  EndCap     LineCap enumeration
    \param  Join       LineJoin enumeration
    \param  MiterLimit Maximum (miter length / line width)
    \param  WidthScale Scale for U_PMF_CUSTOMLINECAP object
    \param  Clcod      U_PSEUDO_OBJ containing a U_PMF_CUSTOMLINECAPOPTIONALDATA object

    EMF+ manual 2.2.2.13, Microsoft name: EmfPlusCustomLineCapData Object
*/
U_PSEUDO_OBJ *U_PMF_CUSTOMLINECAPDATA_set(uint32_t Flags, uint32_t Cap, 
      U_FLOAT Inset, uint32_t StartCap, uint32_t EndCap, 
      uint32_t Join, U_FLOAT MiterLimit, U_FLOAT WidthScale, 
      const U_PSEUDO_OBJ *Clcod
   ){
   if(!Clcod || Clcod->Type  != U_PMF_CUSTOMLINECAPOPTIONALDATA_OID)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Flags,      4,           1, U_LE},
      {&Cap,        4,           1, U_LE},
      {&Inset,      4,           1, U_LE},
      {&StartCap,   4,           1, U_LE},
      {&EndCap,     4,           1, U_LE},
      {&Join,       4,           1, U_LE},
      {&MiterLimit, 4,           1, U_LE},
      {&WidthScale, 4,           1, U_LE},
      {NULL,        8,           2, U_LE}, /* FillHotSpots and LineHotSpots */
      {Clcod->Data, Clcod->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CUSTOMLINECAPDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_CUSTOMLINECAPOPTIONALDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Fill   U_PSEUDO_OBJ containing a U_PMF_FILLPATHOBJ object (optional)
    \param  Line   U_PSEUDO_OBJ containing a U_PMF_LINEPATH object (optional)

    EMF+ manual 2.2.2.14, Microsoft name: EmfPlusCustomLineCapOptionalData Object
*/
U_PSEUDO_OBJ *U_PMF_CUSTOMLINECAPOPTIONALDATA_set(const U_PSEUDO_OBJ *Fill, const U_PSEUDO_OBJ *Line){
   if(Fill && (Fill->Type  != U_PMF_FILLPATHOBJ_OID))return(NULL);
   if(Line && (Line->Type  != U_PMF_LINEPATH_OID))return(NULL);
   const U_SERIAL_DESC List[] = {
      {(Fill ? Fill->Data : NULL), (Fill ? Fill->Used : 0), 1, U_XE},
      {(Line ? Line->Data : NULL), (Line ? Line->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CUSTOMLINECAPOPTIONALDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_CUSTOMSTARTCAPDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Clc       U_PSEUDO_OBJ containing a U_PMF_CUSTOMLINECAPDATA object

    EMF+ manual 2.2.2.15, Microsoft name: EmfPlusCustomStartCapData Object
*/
U_PSEUDO_OBJ *U_PMF_CUSTOMSTARTCAPDATA_set(const U_PSEUDO_OBJ *Clc){
   if(!Clc || Clc->Type  != U_PMF_CUSTOMLINECAP_OID)return(NULL);
   const U_SERIAL_DESC List[] = {
      {Clc->Data, Clc->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_CUSTOMSTARTCAPDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_DASHEDLINEDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements   Members in Lengths
    \param  Lengths    Array of U_FLOAT holding lengths of dashes and spaces.

    EMF+ manual 2.2.2.16, Microsoft name: EmfPlusDashedLineData Object
*/
U_PSEUDO_OBJ *U_PMF_DASHEDLINEDATA_set(int32_t Elements, const U_FLOAT *Lengths){
   if(!Lengths)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements, 4, 1,        U_LE},
      {Lengths,   4, Elements, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_DASHEDLINEDATA_OID, List);
   return(po);
}

/**
    \brief  Utility function to create and set a U_PMF_DASHEDLINEDATA PseudoObject from one of a predefined set of patterns
    \return Pointer to PseudoObject, NULL on error
    \param  Unit       Length of the repeat unit
    \param  StdPat     Members in Lengths

    EMF+ manual 2.2.2.16, Microsoft name: EmfPlusDashedLineData Object
*/
U_PSEUDO_OBJ *U_PMF_DASHEDLINEDATA_set2(U_FLOAT Unit, int StdPat){
   uint32_t Elements;
   uint8_t *p;
   U_FLOAT SubUnit;
   U_FLOAT Lengths[8];  /* This is the most dash/spaces that will be needed*/
   int i;
   /* Dot = 1, Dash = 2; Long = 3, 0 = terminate pattern */
   uint8_t SB[U_DD_Types][5] =
      {
         {0,0,0,0,0},  // Solid                   
         {2,0,0,0,0},  // Dash 
         {2,2,0,0,0},  // DashDash
         {2,2,2,0,0},  // DashDashDash
         {2,2,2,2,0},  // DashDashDashDash
         {1,0,0,0,0},  // Dot  
         {1,1,0,0,0},  // DotDot 
         {1,1,1,0,0},  // DotDotDot
         {1,1,1,1,0},  // DotDotDotDot
         {2,1,0,0,0},  // DashDot
         {2,2,1,0,0},  // DashDashDot
         {2,2,1,1,0},  // DashDashDotDot
         {2,2,2,1,0},  // DashDashDashDot
         {2,1,1,0,0},  // DashDotDot
         {2,1,1,1,0},  // DashDotDotDot
         {2,1,2,1,0},  // DashDotDashDot
         {3,0,0,0,0},  // Long 
         {3,3,0,0,0},  // LongLong
         {3,3,3,0,0},  // LongLongLong
         {3,3,3,3,0},  // LongLongLongLong
         {3,1,0,0,0},  // LongDot
         {3,3,1,0,0},  // LongLongDot
         {3,3,1,1,0},  // LongLongDotDot
         {3,3,3,1,0},  // LongLongLongDot
         {3,1,1,0,0},  // LongDotDot
         {3,1,1,1,0},  // LongDotDotDot
         {3,1,3,1,0}   // LongDotLongDot
      };
   if(Unit <= 0                                      ){ return(NULL); }
   if((StdPat <= 0) || (StdPat > U_DD_LongDotLongDot)){ return(NULL); }
   p = &(SB[StdPat][0]);
   for(Elements = 0; *p; p++, Elements++){}
   SubUnit = Unit/((U_FLOAT) Elements);
   Elements *= 2;
   p = &(SB[StdPat][0]);
   for(i=0; *p; p++){
      switch(*p){
         case 0: break;
         case 1: /* dot */
           Lengths[i++] = SubUnit * 0.125;
           Lengths[i++] = SubUnit * 0.875;
           break;
         case 2: /* dash */
           Lengths[i++] = SubUnit * 0.5;
           Lengths[i++] = SubUnit * 0.5;
           break;
         case 3: /* long */
           Lengths[i++] = SubUnit * 0.75;
           Lengths[i++] = SubUnit * 0.25;
           break;
      }
   }

   U_PSEUDO_OBJ *po = U_PMF_DASHEDLINEDATA_set(Elements, Lengths);
   return(po);
}

/**
    \brief  Utility function to create and set a U_PMF_DASHEDLINEDATA PseudoObject from the bits that are set in a uint32_t
    \return Pointer to PseudoObject, NULL on error
    \param  Unit       Length of the repeat unit
    \param  BitPat     uint32_t holding the bit pattern, the lowest order bit MUST be set and the highest order MUST be clear.
    
    Make a line with a dot/dash pattern defined by the bits in the BitPat value.  If a bit is set it is drawn,
    if clear it is not.  Every bit drawn has length Unit/32, and consecutive drawn bits are merged together.  
    The lowest order bit is the first bit that may be drawn, the highest the last.  
    
        Example: if the integer has value 0x13 the pattern produced will be:
        0          ->  2*unit/32 drawn
        2*unit/32  ->  5*unit/32 not drawn
        5*unit/32  ->  6*unit/32 drawn
        6*unit/32  ->  unit      not drawn

    EMF+ manual 2.2.2.16, Microsoft name: EmfPlusDashedLineData Object
*/
U_PSEUDO_OBJ *U_PMF_DASHEDLINEDATA_set3(U_FLOAT Unit, uint32_t BitPat){
   uint32_t Elements=0;
   U_FLOAT SubUnit = Unit/32.0;
   U_FLOAT Lengths[32];  /* This is the most dash/spaces that will be needed*/
   if(!(0x00000001 & BitPat))return(NULL);  /* Pattern must start with a drawn segment, this bit must be set */
   if(  0x80000000 & BitPat )return(NULL);  /* Pattern must end with an undrawn segment, this bit must be clear */
   int i=0;
   int k;
   int lastType=1;
   int newType=0;
   uint32_t j=1;
   Lengths[0]=0;
   for(k=0; k<32; k++, j=j<<1){
      if(j & BitPat){
         if(!lastType){
            newType=1;
         }
      }
      else {
         if(lastType){
            newType=1;
         }
      }
      if(newType){
         i++;
         Lengths[i]=0;
         Elements++;
         lastType = !lastType;
         newType = 0;
      }
      Lengths[i] += SubUnit;
   }
   Elements = i+1;

   U_PSEUDO_OBJ *po = U_PMF_DASHEDLINEDATA_set(Elements, Lengths);
   return(po);
}

/**
    \brief  Create and set a U_PMF_FILLPATHOBJ PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Path       U_PSEUDO_OBJ containing a U_PMF_PATH object

    EMF+ manual 2.2.2.17, Microsoft name: EmfPlusFillPath Object
*/
U_PSEUDO_OBJ *U_PMF_FILLPATHOBJ_set(const U_PSEUDO_OBJ *Path){
   if(!Path || (Path->Type  != U_PMF_PATH_OID))return(NULL);
   const U_SERIAL_DESC List[] = {
      {Path->Data, Path->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_FILLPATHOBJ_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_FOCUSSCALEDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  ScaleX     value 0.0 <-> 1.0
    \param  ScaleY     value 0.0 <-> 1.0

    EMF+ manual 2.2.2.18, Microsoft name: EmfPlusFocusScaleData Object
*/
U_PSEUDO_OBJ *U_PMF_FOCUSSCALEDATA_set(U_FLOAT ScaleX, U_FLOAT ScaleY){
   uint32_t tmp = 2;
   const U_SERIAL_DESC List[] = {
      {&tmp,    4, 1, U_LE},
      {&ScaleX, 4, 1, U_LE},
      {&ScaleY, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_FOCUSSCALEDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_GRAPHICSVERSION object (Signature always set to 0xDBC01)
    \return Pointer to PseudoObject, NULL on error
    \param  GrfVersion GraphicsVersion enumeration

    EMF+ manual 2.2.2.19, Microsoft name: EmfPlusGraphicsVersion Object
*/
U_PSEUDO_OBJ *U_PMF_GRAPHICSVERSION_set(int GrfVersion){
   uint32_t tmp;
   tmp  = U_GFVR_PMF << 12; /* signature, can only have this value */
   tmp |= (GrfVersion & U_GFVR_MASKLO);
   const U_SERIAL_DESC List[] = {
      {&tmp, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_GRAPHICSVERSION_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_GRAPHICSVERSION object Structure (Signature always set to 0xDBC01)
    \return U_PMF_GRAPHICSVERSION
    \param  GrfVersion GraphicsVersion enumeration

    EMF+ manual 2.2.2.19, Microsoft name: EmfPlusGraphicsVersion Object
*/
U_PMF_GRAPHICSVERSION U_PMF_GRAPHICSVERSIONOBJ_set(int GrfVersion){
   uint32_t tmp;
   tmp  = U_GFVR_PMF << 12; /* signature, can only have this value */
   tmp |= (GrfVersion & U_GFVR_MASKLO);
   return(tmp);
}


/**
    \brief  Create and set a U_PMF_HATCHBRUSHDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Style      HatchStyle enumeration
    \param  Fg         U_PSEUDO_OBJ containing a U_ARGB object, Foreground hatch pattern line color
    \param  Bg         U_PSEUDO_OBJ containing a U_ARGB object, Background hatch pattern line color

    EMF+ manual 2.2.2.20, Microsoft name: EmfPlusHatchBrushData Object
*/
U_PSEUDO_OBJ *U_PMF_HATCHBRUSHDATA_set(uint32_t Style, const U_PSEUDO_OBJ *Fg, const U_PSEUDO_OBJ *Bg){
   if(!Fg ||(Fg->Type != U_PMF_ARGB_OID))return(NULL);
   if(!Bg ||(Bg->Type != U_PMF_ARGB_OID))return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Style,   4,        1, U_LE},
      {Fg->Data, Fg->Used, 1, U_XE},
      {Bg->Data, Bg->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_HATCHBRUSHDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_INTEGER7 PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Value 7 bit signed integer (stored in an integer, range 63 <-> -64, inclusive)

    EMF+ manual 2.2.2.21, Microsoft name: EmfPlusInteger7 Object
*/
U_PSEUDO_OBJ *U_PMF_INTEGER7_set(int Value){
   uint8_t utmp;
   if(Value < -64 || Value > 63)return(NULL);
   utmp = U_MASK_INT7 & *(unsigned int *)&Value;
   U_PSEUDO_OBJ *po =  U_PO_create((char *)&utmp, 1, 1, U_PMF_INTEGER7_OID); /* simple method is OK, no possibility of Endian issues */
   return(po);
}

/**
    \brief  Create and set a U_PMF_INTEGER15 PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Value 15 bit signed integer (stored in an integer, range 32677 <-> -32678, inclusive)

    EMF+ manual 2.2.2.22, Microsoft name: EmfPlusInteger15 Object
*/
U_PSEUDO_OBJ *U_PMF_INTEGER15_set(int Value){
   uint16_t utmp;
   if(Value < -32678 || Value > 32677)return(NULL);
   utmp = U_TEST_INT15 | (U_MASK_INT15 & *(unsigned int *)&Value);
   const U_SERIAL_DESC List[] = {
      {&utmp, 2, 1, U_BE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_INTEGER15_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_LANGUAGEIDENTIFIER value in 4 byte unsigned int, in NATIVE byte order
    \return LID value in least significant two bytes and 0 in most significant two bytes.
    \param  SubLId     Example: code for USA
    \param  PriLId     Example: code for English

    EMF+ manual 2.2.2.23, Microsoft name: EmfPlusLanguageIdentifier Object
*/
U_PMF_LANGUAGEIDENTIFIER U_PMF_LANGUAGEIDENTIFIEROBJ_set(int SubLId, int PriLId){
   U_PMF_LANGUAGEIDENTIFIER utmp32;
   utmp32 = ((SubLId & U_FF_MASK_SUBLID) << U_FF_SHFT_SUBLID) | ((PriLId & U_FF_MASK_PRILID) << U_FF_SHFT_PRILID);
   return(utmp32);
}

/**
    \brief  Create and set a U_PMF_LANGUAGEIDENTIFIER PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  LId       Language Identifier as produced by U_PMF_LANGUAGEIDENTIFIEROBJ_set().

    EMF+ manual 2.2.2.23, Microsoft name: EmfPlusLanguageIdentifier Object
*/
U_PSEUDO_OBJ *U_PMF_LANGUAGEIDENTIFIER_set(U_PMF_LANGUAGEIDENTIFIER LId){
   uint16_t utmp16;
   utmp16 = (LId & U_FF_MASK_LID) << U_FF_SHFT_LID;
   const U_SERIAL_DESC List[] = {
      {&utmp16, 2, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_LANGUAGEIDENTIFIER_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_LINEARGRADIENTBRUSHDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Lgbd       U_PMF_LINEARGRADIENTBRUSHDATA object (constant part)
    \param  Lgbod      U_PSEUDO_OBJ containing a U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA object (variable part of a U_PMF_LINEARGRADIENTBRUSHDATA object)
     

    EMF+ manual 2.2.2.24, Microsoft name: EmfPlusLinearGradientBrushData Object
*/
U_PSEUDO_OBJ *U_PMF_LINEARGRADIENTBRUSHDATA_set(const U_PMF_LINEARGRADIENTBRUSHDATA *Lgbd, const U_PSEUDO_OBJ *Lgbod){
   if(!Lgbd || !Lgbod || (Lgbod->Type != U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_OID))return(NULL);
   const U_SERIAL_DESC List[] = {
      {Lgbd,                                4,           6, U_LE},
      {&(Lgbd->StartColor),                 4,           2, U_XE},
      {&(Lgbd->StartColor),                 4,           2, U_XE}, /* repeat the start/end colors.  Supposedly reserved. */
//    {NULL,                                4,           2, U_LE}, /* zero fill the two Reserved fields, no matter what is passed in */
      {(Lgbod->Used ? Lgbod->Data : NULL),  Lgbod->Used, 1, U_XE}, /* optional Data can exist and Used can be zero, SERIAL_set would throw an error on that */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_LINEARGRADIENTBRUSHDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Flags      Bits are set that indicate which of the following were included. The caller must clear before passing it in.
    \param  Tm         (optional) U_PSEUDO_OBJ containing a U_PMF_TRANSFORMMATRIX object
    \param  Bc         (optional) U_PSEUDO_OBJ containing a U_PMF_BLENDCOLORS object or NULL
    \param  BfH        (optional) U_PSEUDO_OBJ containing a U_PMF_BLENDFACTORS (H) object or NULL
    \param  BfV        (optional) U_PSEUDO_OBJ containing a U_PMF_BLENDFACTORS (V) object or NULL (WARNING, GDI+ defines this field but does not render it.  DO NOT USE.)
     

    EMF+ manual 2.2.2.25, Microsoft name: EmfPlusLinearGradientBrushOptionalData Object

    
    The rectangular gradients repeat in a tiled pattern.  Tm can rotate and offset the gradient within each tile.
    The gradient wraps when it is offset.
    
*/
U_PSEUDO_OBJ *U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_set(uint32_t *Flags, const U_PSEUDO_OBJ *Tm, 
      const U_PSEUDO_OBJ *Bc, const U_PSEUDO_OBJ *BfH, const U_PSEUDO_OBJ *BfV){
   if(!Flags                                         )return(NULL);
   if(Tm  && (Tm->Type  != U_PMF_TRANSFORMMATRIX_OID))return(NULL);
   if(Bc  && (Bc->Type  != U_PMF_BLENDCOLORS_OID)    )return(NULL);
   if(BfH && (BfH->Type != U_PMF_BLENDFACTORS_OID)   )return(NULL);
   if(BfV && (BfV->Type != U_PMF_BLENDFACTORS_OID)   )return(NULL);
   if(Bc  && (BfH || BfV)                            )return(NULL);
   const U_SERIAL_DESC List[] = {
      {(Tm  ? Tm->Data  : NULL), (Tm  ? Tm->Used  : 0), 1, U_XE},
      {(Bc  ? Bc->Data  : NULL), (Bc  ? Bc->Used  : 0), 1, U_XE},
      {(BfH ? BfH->Data : NULL), (BfH ? BfH->Used : 0), 1, U_XE},
      {(BfV ? BfV->Data : NULL), (BfV ? BfV->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_OID, List);
   if(Tm ){ *Flags |= U_BD_Transform;     }
   if(Bc ){ *Flags |= U_BD_PresetColors;  }
   if(BfH){ *Flags |= U_BD_BlendFactorsH; }
   if(BfV){ *Flags |= U_BD_BlendFactorsV; }
   return(po);
}

/**
    \brief  Create and set a U_PMF_LINEPATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Path      U_PSEUDO_OBJ containing a U_PMF_PATH object
     

    EMF+ manual 2.2.2.26, Microsoft name: EmfPlusLinePath Object
*/
U_PSEUDO_OBJ *U_PMF_LINEPATH_set(const U_PSEUDO_OBJ *Path){
   if(!Path || (Path->Type != U_PMF_PATH_OID))return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Path->Data, Path->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_LINEPATH_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_METAFILE object (NOT SUPPORTED!)
    \return Null
     

    EMF+ manual 2.2.2.27, Microsoft name: EmfPlusMetafile Object
*/
U_PSEUDO_OBJ *U_PMF_METAFILE_set(void){
   return(NULL);
}

/**
    \brief  Create and set a U_PMF_PALETTE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Flags      PaletteStyle flags
    \param  Elements   Members in Lengths
    \param  Pd         Array of U_PMF_ARGB holding colors of palettes. (Palette Data)

    EMF+ manual 2.2.2.28, Microsoft name: EmfPlusPalette Object
*/
U_PSEUDO_OBJ *U_PMF_PALETTE_set(uint32_t Flags, uint32_t Elements, const U_PMF_ARGB *Pd){
   if(!Pd)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Flags,    4, 1,        U_LE},
      {&Elements, 4, 1,        U_LE},
      {Pd,        4, Elements, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PALETTE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_PATHGRADIENTBRUSHDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Flags        PaletteStyle flags
    \param  WrapMode     WrapMode enumeration
    \param  CenterColor  U_PMF_ARGB Center color
    \param  Center       Center coordinates
    \param  Gradient     U_PSEUDO_OBJ containing an Array of U_PMF_ARGB holding colors of Gradient
    \param  Boundary     U_PSEUDO_OBJ containing a U_PMF_BOUNDARYPATHDATA or U_PMF_BOUNDARYPOINTDATA object.  (Boundary Data)
    \param  Data         variable part of U_PMF_LINEARGRADIENTBRUSHDATA, exact composition depends on Flags

    EMF+ manual 2.2.2.29, Microsoft name: EmfPlusPathGradientBrushData Object
*/
U_PSEUDO_OBJ *U_PMF_PATHGRADIENTBRUSHDATA_set(uint32_t Flags, int32_t WrapMode, U_PMF_ARGB CenterColor, 
      U_PMF_POINTF Center,
      const U_PSEUDO_OBJ *Gradient, const U_PSEUDO_OBJ *Boundary, const U_PSEUDO_OBJ *Data){
   if( (Flags & U_BD_Path) && (!Boundary || (Boundary->Type != U_PMF_BOUNDARYPATHDATA_OID)))return(NULL);
   if(!(Flags & U_BD_Path) && (!Boundary || (Boundary->Type != U_PMF_BOUNDARYPOINTDATA_OID)))return(NULL);
   if(!Gradient || (Gradient->Type != (U_PMF_ARGB_OID | U_PMF_ARRAY_OID)))return(NULL);
   if(!(Flags & U_BD_Transform) && 
      !(Flags & U_BD_PresetColors) && 
      !(Flags & U_BD_BlendFactorsH) && 
      !(Flags & U_BD_FocusScales) && 
      (!Data || (Data->Type != U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_OID)))return(NULL);

   const U_SERIAL_DESC List[] = {
      {&Flags,                         4,                       1, U_LE},
      {&WrapMode,                      4,                       1, U_LE},
      {&CenterColor,                   4,                       1, U_XE},
      {&Center.X,                      4,                       2, U_LE},
      {Gradient->Data,                 Gradient->Used,          1, U_XE}, /* includes Elements */
      {Boundary->Data,                 Boundary->Used,          1, U_XE},
      {(Data ? Data->Data : NULL),     (Data ? Data->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATHGRADIENTBRUSHDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_PATHGRADIENTBRUSHOPTIONALDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Flags        PaletteStyle flags
    \param  Tm           U_PSEUDO_OBJ containing a U_PMF_TRANSFORMMATRIX.  (Transformation matrix)
    \param  Pd           U_PSEUDO_OBJ containing a U_PMF_BLENDCOLORS or U_PMF_BLENDFACTORS object.  (Pattern Data)
    \param  Fsd          U_PSEUDO_OBJ containing a U_PMF_FOCUSSSCALEDATA object.  (Focus Scale Data)

    EMF+ manual 2.2.2.30, Microsoft name: EmfPlusPathGradientBrushOptionalData Object
*/
U_PSEUDO_OBJ *U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_set(uint32_t Flags,
      const U_PSEUDO_OBJ *Tm, const U_PSEUDO_OBJ *Pd, const U_PSEUDO_OBJ *Fsd){
   if(Tm &&  (Tm->Type != U_PMF_TRANSFORMMATRIX_OID))return(NULL);
   if(Pd && !(Flags & (U_BD_PresetColors | U_BD_BlendFactorsH)))return(NULL);
   if( (Flags & U_BD_PresetColors)  && ((Flags & U_BD_BlendFactorsH)  || !Pd || (Pd->Type != U_PMF_BLENDCOLORS_OID) ))return(NULL);
   if( (Flags & U_BD_BlendFactorsH) && ((Flags & U_BD_PresetColors)   || !Pd || (Pd->Type != U_PMF_BLENDFACTORS_OID)))return(NULL);
   if(Fsd && !(Flags & U_BD_FocusScales))return(NULL);
   if( (Flags & U_BD_FocusScales)   && (!Fsd || (Fsd->Type != U_PMF_BLENDCOLORS_OID) ))return(NULL);
   const U_SERIAL_DESC List[] = {
      {(Tm  ? Tm->Data  : NULL), (Tm ?  Tm->Used  : 0), 1, U_XE},
      {(Pd  ? Pd->Data  : NULL), (Pd ?  Pd->Used  : 0), 1, U_XE},
      {(Fsd ? Fsd->Data : NULL), (Fsd ? Fsd->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set an ARRAY of U_PMF_PATHPOINTTYPE objects
    \return Pointer to PseudoObject, NULL on error
    \param  Elements     Number of entries in Flags and Enumerations
    \param  Ppt          Array of unsigned bytes, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration.

    EMF+ manual 2.2.2.31, Microsoft name: EmfPlusPathPointType Object
*/
U_PSEUDO_OBJ *U_PMF_PATHPOINTTYPE_set(uint32_t Elements, const uint8_t *Ppt){
   if(!Elements || !Ppt)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements, 4, 1,        U_LE},
      {Ppt,       1, Elements, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATHPOINTTYPE_OID | U_PMF_ARRAY_OID, List);
   return(po);
}

/**
    \brief  Create and set an ARRAY of U_PMF_PATHPOINTTYPE objects, with a preceding Elements count
    \return Pointer to PseudoObject, NULL on error
    \param  Elements Number of elements to add. First is added once and Others Elements-1 times.
    \param  First    Apply to first point,      unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration.
    \param  Others   Apply to all other points, unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration.

    EMF+ manual 2.2.2.31, Microsoft name: EmfPlusPathPointType Object
*/
U_PSEUDO_OBJ *U_PMF_PATHPOINTTYPE_set2(uint32_t Elements, uint8_t First, uint8_t Others){
   if(!Elements)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements,  4,   1,          U_XE},
      {&First,     1,   1,          U_XE},
      {&Others,    1,   Elements-1, U_RP},  /* replicate the one value N-1 times */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PATHPOINTTYPE_OID | U_PMF_ARRAY_OID, List);
   return(po);
}

/**
    \brief  Create and set an ARRAY of U_PMF_PATHPOINTTYPERLE objects
    \return Pointer to PseudoObject containing first the number of elements, then an array of U_PMF_PATHPOINTTYPERLE, NULL on error
    \param  Elements     Number of entries in the arrays
    \param  Bz           Array of unsigned bytes, if not zero, element has Bezier bit set
    \param  RL           Array of unsigned bytes, Run lengths.
    \param  Ppte         Array of unsigned bytes, PathPointType enumerations.

    EMF+ manual 2.2.2.32, Microsoft name: EmfPlusPathPointTypeRLE Object
*/
U_PSEUDO_OBJ *U_PMF_PATHPOINTTYPERLE_set(uint32_t Elements, const uint8_t *Bz, const uint8_t *RL, const uint8_t *Ppte){
   uint8_t utmp;
   if(!Bz || !RL || !Ppte)return(NULL);
   /* allocate space in the structure but put no data in */
   U_PSEUDO_OBJ *po =  U_PO_create(NULL, 4 + 2*Elements, 0, U_PMF_PATHPOINTTYPERLE_OID | U_PMF_ARRAY_OID);
   U_PSEUDO_OBJ *holdpo = po; 
   if(po){
      U_PSEUDO_OBJ *poi = U_PMF_4NUM_set(Elements);
      if(!poi)goto end;
      po = U_PO_append(po, poi->Data, poi->Used);   
      U_PO_free(&poi);
      if(!po)goto end;

      for( ;Elements; Elements--, Bz++, RL++, Ppte++){
         po = U_PO_append(po, (char *)Ppte, 1);
         if(!po)goto end;
         
         if(*RL > 0x3F) goto end; /* run length too big for field */
           
         utmp = (*Bz ? 1 : 0) | ((*RL & 0x3F)<<2); /* bit 1 is not used and is set to 0 */
         po = U_PO_append(po, (char *)&utmp, 1);
         if(!po)goto end;
      }
   }
end:
   if(!po)U_PO_free(&holdpo);
   return(holdpo);
}

/**
    \brief  Create and set a U_PMF_PENDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Unit      UnitType enumeration
    \param  Width     Width in units set by Unit
    \param  Pod       U_PSEUDO_OBJ containing first the PenData flags then a U_PMF_PENOPTIONALDATA object (the second part
                        may be an empty if Flags is 0)
     

    EMF+ manual 2.2.2.33, Microsoft name: EmfPlusPenData Object
*/
U_PSEUDO_OBJ *U_PMF_PENDATA_set(uint32_t Unit, U_FLOAT Width, const U_PSEUDO_OBJ *Pod){
   if(Pod && ((Pod->Type != U_PMF_PENOPTIONALDATA_OID) || Pod->Used < 4))return(NULL);
   const U_SERIAL_DESC List[] = {
      {(Pod ? Pod->Data : NULL),     4,                         1,             U_XE}, /* the Flags field, clear if no optional data */
      {&Unit,                        4,                         1,             U_LE},
      {&Width,                       4,                         1,             U_LE},
          /* next is the (optional) U_PMF_PENOPTIONALDATA part or a terminator */
      {(Pod ? Pod->Data + 4 : NULL), (Pod ? Pod->Used - 4 : 0), (Pod ? 1 : 0), (Pod ? U_XE : U_XX)},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PENDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_PENOPTIONALDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error.  Returned PO contains first the Flags, then the PO proper.
    \param  Flags           Determines which of the values are stored.
    \param  Tm              U_PSEUDO_OBJ containing a U_PMF_TRANSFORMMATRIX  object (Transformation matrix)
    \param  StartCap        LineCapType enumeration
    \param  EndCap          LineCapType enumeration
    \param  Join            LineJoinType enumeration
    \param  MiterLimit      Maximum (miter length / line width)
    \param  Style           LineStyle enumeration
    \param  DLCap           DashedLineCapType enumeration
    \param  DLOffset        Distance line start to first dash start
    \param  DLData          U_PSEUDO_OBJ containing a U_PMF_DASHEDLINEDATA  object     Dash and space widths
    \param  PenAlignment    PenAlignment enumeration
    \param  CmpndLineData   U_PSEUDO_OBJ containing a U_PMF_COMPOUNDLINEDATA object    Compount Line (parallel lines drawn instead of one)
    \param  CSCapData       U_PSEUDO_OBJ containing a U_PMF_CUSTOMSTARTCAPDATA object  Custom start cap  
    \param  CECapData       U_PSEUDO_OBJ containing a U_PMF_CUSTOMENDCAPDATA object    Custom end cap
     

    EMF+ manual 2.2.2.34, Microsoft name: EmfPlusPenOptionalData Object
*/
U_PSEUDO_OBJ *U_PMF_PENOPTIONALDATA_set(uint32_t Flags, U_PSEUDO_OBJ *Tm, int32_t StartCap, int32_t EndCap, uint32_t Join,
      U_FLOAT MiterLimit, int32_t Style, int32_t DLCap, U_FLOAT DLOffset, 
      U_PSEUDO_OBJ *DLData, int32_t PenAlignment, U_PSEUDO_OBJ *CmpndLineData, U_PSEUDO_OBJ *CSCapData, 
      U_PSEUDO_OBJ *CECapData
   ){

   if((Flags & U_PD_Transform)      && (!Tm            || (Tm->Type            != U_PMF_TRANSFORMMATRIX_OID))   )return(NULL);
   if((Flags & U_PD_DLData)         && (!DLData        || (DLData->Type        != U_PMF_DASHEDLINEDATA_OID))    )return(NULL);
   if((Flags & U_PD_CLData)         && (!CmpndLineData || (CmpndLineData->Type != U_PMF_COMPOUNDLINEDATA_OID))  )return(NULL);
   if((Flags & U_PD_CustomStartCap) && (!CSCapData     || (CSCapData->Type     != U_PMF_CUSTOMSTARTCAPDATA_OID)))return(NULL); 
   if((Flags & U_PD_CustomEndCap)   && (!CECapData     || (CECapData->Type     != U_PMF_CUSTOMENDCAPDATA_OID))  )return(NULL);
   
   /* prepend the Flags field to the PseudoObject proper */
   const U_SERIAL_DESC List[] = {
      {&Flags,                                                         4,                                                         1, U_LE},
      {((Flags & U_PD_Transform)      ? Tm->Data              : NULL), ((Flags & U_PD_Transform)      ? Tm->Used            : 0), 1, U_XE},
      {((Flags & U_PD_StartCap )      ? (char *)&StartCap     : NULL), ((Flags & U_PD_StartCap )      ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_EndCap )        ? (char *)&EndCap       : NULL), ((Flags & U_PD_EndCap )        ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_Join )          ? (char *)&Join         : NULL), ((Flags & U_PD_Join )          ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_MiterLimit )    ? (char *)&MiterLimit   : NULL), ((Flags & U_PD_MiterLimit )    ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_LineStyle )     ? (char *)&Style        : NULL), ((Flags & U_PD_LineStyle )     ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_DLCap )         ? (char *)&DLCap        : NULL), ((Flags & U_PD_DLCap )         ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_DLOffset )      ? (char *)&DLOffset     : NULL), ((Flags & U_PD_DLOffset )      ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_DLData )        ? DLData->Data          : NULL), ((Flags & U_PD_DLData )        ? DLData->Used        : 0), 1, U_XE},
      {((Flags & U_PD_NonCenter )     ? (char *)&PenAlignment : NULL), ((Flags & U_PD_NonCenter )     ? 4                   : 0), 1, U_LE},
      {((Flags & U_PD_CLData )        ? CmpndLineData->Data   : NULL), ((Flags & U_PD_CLData )        ? CmpndLineData->Used : 0), 1, U_XE},
      {((Flags & U_PD_CustomStartCap) ? CSCapData->Data       : NULL), ((Flags & U_PD_CustomStartCap) ? CSCapData->Used     : 0), 1, U_XE},
      {((Flags & U_PD_CustomEndCap )  ? CECapData->Data       : NULL), ((Flags & U_PD_CustomEndCap )  ? CECapData->Used     : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_PENOPTIONALDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set an ARRAY of U_PMF_POINT objects
    \return Pointer to PseudoObject, NULL on error
    \param  Elements     Number of pairs of points in Coords
    \param  Coords       Array of X,Y pairs.

    EMF+ manual 2.2.2.35, Microsoft name: EmfPlusPoint Object
*/
U_PSEUDO_OBJ *U_PMF_POINT_set(uint32_t Elements, const U_PMF_POINT *Coords){
   if(!Elements || !Coords)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements, 4, 1,          U_LE},
      { Coords,   2, 2*Elements, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_POINT_OID | U_PMF_ARRAY_OID, List);
   return(po);
}

/**
    \brief  Create and set an ARRAY of U_PMF_POINTF objects, with a leading Elements value
    \return Pointer to PseudoObject, NULL on error
    \param  Elements     Number of pairs of points in Coords
    \param  Coords       Array of X,Y pairs.

    EMF+ manual 2.2.2.36, Microsoft name: EmfPlusPointF Object
*/
U_PSEUDO_OBJ *U_PMF_POINTF_set(uint32_t Elements, const U_PMF_POINTF *Coords){
   if(!Elements || !Coords)return(NULL);
   const U_SERIAL_DESC List[] = {
      {&Elements, 4, 1,          U_LE},
      {Coords,    4, 2*Elements, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_POINTF_OID | U_PMF_ARRAY_OID, List);
   return(po);
}

/**
    \brief  Create and set an ARRAY of U_PMF_POINTR objects
    \return Pointer to PseudoObject, NULL on error
    \param  Elements     Number of pairs of points in Coords
    \param  Coords       Array of X,Y pairs.  These are absolute coordinates, they are converted to Relative here.

    EMF+ manual 2.2.2.37, Microsoft name: EmfPlusPointR Object
*/
U_PSEUDO_OBJ *U_PMF_POINTR_set(uint32_t Elements, const U_PMF_POINTF *Coords){
   int X,Y;
   U_FLOAT Xf,Yf;
   U_PSEUDO_OBJ *poi;
   /* Worst case scenario it is 4 bytes per coord, plus the count  */
   U_PSEUDO_OBJ *po =  U_PO_create(NULL, 4 + 4*Elements, 0, U_PMF_POINTR_OID); /* not exactly an array, so no U_PMF_ARRAY_OID */
   U_PSEUDO_OBJ *holdpo = po;
   if(!po)goto end;

   poi = U_PMF_4NUM_set(Elements);
   po = U_PO_append(po, poi->Data, poi->Used);      
   U_PO_free(&poi);
   if(!po)goto end;

   for(Xf = Yf = 0.0 ;Elements; Elements--, Coords++){
      Xf = U_ROUND(Coords->X) - Xf;
      Yf = U_ROUND(Coords->Y) - Yf;
      X = ( Xf >= UINT16_MAX ? UINT16_MAX : ( Xf <= INT16_MIN ? INT16_MIN : Xf));
      Y = ( Yf >= UINT16_MAX ? UINT16_MAX : ( Yf <= INT16_MIN ? INT16_MIN : Yf));
      Xf = U_ROUND(Coords->X);
      Yf = U_ROUND(Coords->Y);
      
      /* this is not a very efficient method, too much mucking around with memory */

      poi = U_PMF_INTEGER7_set(X);
      if(!poi)poi = U_PMF_INTEGER15_set(X); /* This one must work because of the range checking, above */
      po = U_PO_append(po, poi->Data, poi->Used);      
      U_PO_free(&poi);
      if(!po)goto end;

      poi = U_PMF_INTEGER7_set(Y);
      if(!poi)poi = U_PMF_INTEGER15_set(Y); /* This one must work because of the range checking, above */
      po = U_PO_append(po, poi->Data, poi->Used);      
      U_PO_free(&poi);
      if(!po)goto end;
   }
   /* Because the values stored were some unpredictable combination of 1 and 2 bytes, the last byte may not end
   on a 4 byte boundary.  Make it do so by padding with up to 3 zero bytes.  */

   int residual;
   residual = 3 & po->Used;
   if(residual){ 
      po = U_PO_append(po, NULL, (4 - residual));
      if(!po)goto end;     
   }

end:
   if(!po)U_PO_free(&holdpo);
   return(holdpo);
}

/**
    \brief  Create and set a U_PMF_RECT object
    \return Pointer to PseudoObject, NULL on error
    \param  X          UL X value
    \param  Y          UL Y value
    \param  Width      Width
    \param  Height     Height

    EMF+ manual 2.2.2.38, Microsoft name: EmfPlusRect Object
*/
U_PSEUDO_OBJ *U_PMF_RECT4_set(int16_t X, int16_t Y, int16_t Width, int16_t Height){
   const U_SERIAL_DESC List[] = {
      {&X,      2, 1, U_LE},
      {&Y,      2, 1, U_LE},
      {&Width,  2, 1, U_LE},
      {&Height, 2, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_RECT_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_RECT object
    \return Pointer to PseudoObject, NULL on error
    \param  Rect       U_PMF_RECT structures

    EMF+ manual 2.2.2.38, Microsoft name: EmfPlusRect Object
*/
U_PSEUDO_OBJ *U_PMF_RECT_set(U_PMF_RECT *Rect){
   const U_SERIAL_DESC List[] = {
      {Rect, 2, 4, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_RECT_OID, List);
   return(po);
}

/**
    \brief  Create and set an array of U_PMF_RECT objects in a PseudoObject
    \return Pointer to PseudoObject, NULL on error.  PseudoObject contains Elements followed by the array of U_PMF_RECT objects.
    \param  Elements   Number of elements in Rects
    \param  Rects      Array of U_PMF_RECT structures

    EMF+ manual 2.2.2.38, Microsoft name: EmfPlusRect Object
*/
U_PSEUDO_OBJ *U_PMF_RECTN_set(uint32_t Elements, U_PMF_RECT *Rects){
   if(!Rects){ return(NULL); }
   uint32_t count = Elements;
   U_SERIAL_DESC *List = (U_SERIAL_DESC *) malloc((Elements + 2) * sizeof(U_SERIAL_DESC));
   U_SERIAL_DESC *Lptr = List;
   if(!List){ return(NULL); }
   *Lptr++ = (U_SERIAL_DESC){&Elements, 4, 1, U_LE};
   for(; count; count--, Lptr++, Rects++){
      *Lptr = (U_SERIAL_DESC){Rects, 2, 4, U_LE};
   }
   *Lptr = (U_SERIAL_DESC){NULL,0,0,U_XX};
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_RECT_OID | U_PMF_ARRAY_OID, List);
   free(List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_RECTF object in a PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  X          UL X value
    \param  Y          UL Y value
    \param  Width      Width
    \param  Height     Height

    EMF+ manual 2.2.2.39, Microsoft name: EmfPlusRectF Object
*/
U_PSEUDO_OBJ *U_PMF_RECTF4_set(U_FLOAT X, U_FLOAT Y, U_FLOAT Width, U_FLOAT Height){
   const U_SERIAL_DESC List[] = {
      {&X,      4, 1, U_LE},
      {&Y,      4, 1, U_LE},
      {&Width,  4, 1, U_LE},
      {&Height, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_RECTF_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_RECTF object in a PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Rect      U_PMF_RECTF structure

    EMF+ manual 2.2.2.39, Microsoft name: EmfPlusRectF Object
*/
U_PSEUDO_OBJ *U_PMF_RECTF_set(U_PMF_RECTF *Rect){
   const U_SERIAL_DESC List[] = {
      {Rect,    4, 4, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_RECTF_OID, List);
   return(po);
}

/**
    \brief  Create and set an array of U_PMF_RECTF objects in a PseudoObject
    \return Pointer to PseudoObject, NULL on error.  PseudoObject contains Elements followed by the array of U_PMF_RECTF objects.
    \param  Elements   Number of elements in Rects
    \param  Rects      Array of U_PMF_RECTF structures

    EMF+ manual 2.2.2.39, Microsoft name: EmfPlusRectF Object
*/
U_PSEUDO_OBJ *U_PMF_RECTFN_set(uint32_t Elements, U_PMF_RECTF *Rects){
   if(!Rects){ return(NULL); }
   uint32_t count = Elements;
   U_SERIAL_DESC *List = (U_SERIAL_DESC *) malloc((Elements + 2) * sizeof(U_SERIAL_DESC));
   U_SERIAL_DESC *Lptr = List;
   if(!List){ return(NULL); }
   *Lptr++ = (U_SERIAL_DESC){&Elements, 4, 1, U_LE};
   for(; count; count--, Lptr++, Rects++){
     *Lptr = (U_SERIAL_DESC){Rects, 4, 4, U_LE}; 
   }
   *Lptr = (U_SERIAL_DESC){NULL,0,0,U_XX};
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_RECTF_OID | U_PMF_ARRAY_OID, List);
   free(List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_REGIONNODE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Type      RegionNodeDataType Enumeration
    \param  Rnd       (optional) U_PSEUDO_OBJ containing a U_PMF_REGIONNODEPATH, U_PMF_RECTF, or U_PMF_REGIONNODECHILDNODES object (Region Node Data)
     

    EMF+ manual 2.2.2.40, Microsoft name: EmfPlusRegionNode Object
*/
U_PSEUDO_OBJ *U_PMF_REGIONNODE_set(int32_t Type, const U_PSEUDO_OBJ *Rnd){
   int32_t pType;
   /* make sure that the type of Rnd agrees with Type */
   if(Rnd){
      pType = U_OID_To_RNDT(Rnd->Type);
      if( pType <  0){                     return(NULL); }
      if((pType >  0) && (pType != Type)){ return(NULL); }
      if((pType == 0) && 
         (
            (Type < U_RNDT_And) || 
            (Type > U_RNDT_Complement)
         )
      ){                                   return(NULL); }
      if((Type == U_RNDT_Rect) && (Rnd->Type != U_PMF_RECTF_OID)){           return(NULL); }
      if((Type == U_RNDT_Path) && (Rnd->Type != U_PMF_REGIONNODEPATH_OID)){  return(NULL); }
      
   }
   else {  /* only U_RNDT_Empty and  U_RNDT_Infinite do not have data */
      if((Type != U_RNDT_Empty)  || 
         (Type != U_RNDT_Infinite)  ){     return(NULL); }
   }
   

   const U_SERIAL_DESC List[] = {
      {&Type,                    4,                     1, U_LE},
      {(Rnd ? Rnd->Data : NULL), (Rnd ? Rnd->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_REGIONNODE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_REGIONNODECHILDNODES PseudoObject
    \return Pointer to PseudoObject containing a U_PMF_REGIONNODECHILDNODES_OID object, NULL on error
    \param  Left      U_PSEUDO_OBJ containing a U_PMF_REGIONNODE object
    \param  Right     U_PSEUDO_OBJ containing a U_PMF_REGIONNODE object
     

    EMF+ manual 2.2.2.41, Microsoft name: EmfPlusRegionNodeChildNodes Object
*/
U_PSEUDO_OBJ *U_PMF_REGIONNODECHILDNODES_set(const U_PSEUDO_OBJ *Left, const U_PSEUDO_OBJ *Right){
   if(!Left  || (Left->Type  != U_PMF_REGIONNODE_OID)){ return(NULL); }
   if(!Right || (Right->Type != U_PMF_REGIONNODE_OID)){ return(NULL); }

   const U_SERIAL_DESC List[] = {
      {Left->Data,  Left->Used,  1, U_XE},
      {Right->Data, Right->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_REGIONNODECHILDNODES_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_REGIONNODEPATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Path     U_PSEUDO_OBJ containing a U_PMF_PATH object
     

    EMF+ manual 2.2.2.42, Microsoft name: EmfPlusRegionNodePath Object
*/
U_PSEUDO_OBJ *U_PMF_REGIONNODEPATH_set(const U_PSEUDO_OBJ *Path){
   if(!Path  || (Path->Type  != U_PMF_PATH_OID)){ return(NULL); }
   /* PO Used is size_t, might be 8 bytes, value in record must be 4 bytes */
   uint32_t Used = Path->Used;

   const U_SERIAL_DESC List[] = {
      {&Used,      4,	       1, U_LE},
      {Path->Data, Path->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_REGIONNODEPATH_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_SOLIDBRUSHDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Color     U_PSEUDO_OBJ containing a U_PMF_ARGB object
     

    EMF+ manual 2.2.2.43, Microsoft name: EmfPlusSolidBrushData Object
*/
U_PSEUDO_OBJ *U_PMF_SOLIDBRUSHDATA_set(const U_PSEUDO_OBJ *Color){
   if(!Color || (Color->Type != U_PMF_ARGB_OID)){ return(NULL); }

   const U_SERIAL_DESC List[] = {
      {Color->Data, Color->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_SOLIDBRUSHDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_STRINGFORMATDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  TabStopCount  Entries in TabStop array
    \param  TabStops      (optional) Array of tabstop locations
    \param  Ranges        (optional) U_PSEUDO_OBJ containing an array of U_PMF_CHARACTERRANGE objects
     

    EMF+ manual 2.2.2.44, Microsoft name: EmfPlusStringFormatData Object
*/
U_PSEUDO_OBJ *U_PMF_STRINGFORMATDATA_set(uint32_t TabStopCount, U_FLOAT *TabStops, const U_PSEUDO_OBJ *Ranges){
   if(Ranges && (Ranges->Type  != (U_PMF_CHARACTERRANGE_OID | U_PMF_ARRAY_OID))){ return(NULL); }

   const U_SERIAL_DESC List[] = {
      {TabStops,                       TabStopCount*4,              1, U_LE},
      {(Ranges ? Ranges->Data : NULL), (Ranges ? Ranges->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_STRINGFORMATDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_TEXTUREBRUSHDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Flags         BrushData flags
    \param  WrapMode      WrapMode enumeration
    \param  Tbod          U_PSEUDO_OBJ containing an U_PMF_TEXTUREBRUSHOPTIONALDATA object
     

    EMF+ manual 2.2.2.45, Microsoft name: EmfPlusTextureBrushData Object
*/
U_PSEUDO_OBJ *U_PMF_TEXTUREBRUSHDATA_set(uint32_t Flags, uint32_t WrapMode, const U_PSEUDO_OBJ *Tbod){
   if(Flags & ~U_BD_MASKB){ return(NULL); }  /* a bit was set that is not supported for this record */
   if(WrapMode > U_WM_Clamp){ return(NULL); }
   if(!Tbod || (Tbod->Type  != (U_PMF_TEXTUREBRUSHOPTIONALDATA_OID))){ return(NULL); }

   const U_SERIAL_DESC List[] = {
      {&Flags,     4,          1, U_LE},
      {&WrapMode,  4,          1, U_LE},
      {Tbod->Data, Tbod->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_TEXTUREBRUSHDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_TEXTUREBRUSHOPTIONALDATA PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Tm            (optional) U_PSEUDO_OBJ containing an U_PMF_TRANSFORMMATRIX object
    \param  Image         (optional) U_PSEUDO_OBJ containing an U_PMF_IMAGE object
     

    EMF+ manual 2.2.2.46, Microsoft name: EmfPlusTextureBrushOptionalData Object
*/
U_PSEUDO_OBJ *U_PMF_TEXTUREBRUSHOPTIONALDATA_set(const U_PSEUDO_OBJ *Tm, const U_PSEUDO_OBJ *Image){
   if(Tm    && (Tm->Type    != (U_PMF_TRANSFORMMATRIX_OID))){ return(NULL); }
   if(Image && (Image->Type != (U_PMF_IMAGE_OID))){           return(NULL); }

   const U_SERIAL_DESC List[] = {
      {(Tm ? Tm->Data : NULL),       (Tm ? Tm->Used : 0),       1, U_XE},
      {(Image ? Image->Data : NULL), (Image ? Image->Used : 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_TEXTUREBRUSHOPTIONALDATA_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_TRANSFORMMATRIX PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Tm            U_PMF_TRANSFORMMATRIX_ object
     

    EMF+ manual 2.2.2.47, Microsoft name: EmfPlusTransformMatrix Object
*/
U_PSEUDO_OBJ *U_PMF_TRANSFORMMATRIX_set(U_PMF_TRANSFORMMATRIX *Tm){
   if(!Tm){ return(NULL); }

   const U_SERIAL_DESC List[] = {
      {Tm, 4, 6, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_TRANSFORMMATRIX_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_BLUR PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Radius     Blur radius in pixels
    \param  ExpandEdge 1: expand bitmap by Radius; 0: bitmap size unchanged
     

    EMF+ manual 2.2.3.1, Microsoft name: BlurEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_BLUR_set(U_FLOAT Radius, uint32_t ExpandEdge){

   const U_SERIAL_DESC List[] = {
      {&Radius,     4, 1, U_LE},
      {&ExpandEdge, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_BLUR_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_BRIGHTNESSCONTRAST PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Brightness  -255 to 255, 0 is unchanged, positive increases, negative decreases
    \param  Contrast    -100 to 100, 0 is unchanged, positive increases, negative decreases
     

    EMF+ manual 2.2.3.2, Microsoft name: BrightnessContrastEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_BRIGHTNESSCONTRAST_set(int32_t Brightness, int32_t Contrast){

   const U_SERIAL_DESC List[] = {
      {&Brightness, 4, 1, U_LE},
      {&Contrast,   4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_BRIGHTNESSCONTRAST_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_COLORBALANCE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  CyanRed      -100 to 100, 0 is unchanged, positive increases Red   & decreases Cyan,    negative is opposite
    \param  MagentaGreen -100 to 100, 0 is unchanged, positive increases Green & decreases Magenta, negative is opposite
    \param  YellowBlue   -100 to 100, 0 is unchanged, positive increases Blue  & decreases Yellow,  negative is opposite
     

    EMF+ manual 2.2.3.3, Microsoft name: ColorBalanceEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_COLORBALANCE_set(int32_t CyanRed, int32_t MagentaGreen, int32_t YellowBlue){

   const U_SERIAL_DESC List[] = {
      {&CyanRed,      4, 1, U_LE},
      {&MagentaGreen, 4, 1, U_LE},
      {&YellowBlue,   4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_COLORBALANCE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_COLORCURVE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Adjust       CurveAdjustment enumeration
    \param  Channel      CurveChannel enumeration
    \param  Intensity    adjustment to apply.  "Adjust" determines what field this is and range values.


    EMF+ manual 2.2.3.4, Microsoft name: ColorCurveEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_COLORCURVE_set(uint32_t Adjust, uint32_t Channel, int32_t Intensity){

   const U_SERIAL_DESC List[] = {
      {&Adjust,    4, 1, U_LE},
      {&Channel,   4, 1, U_LE},
      {&Intensity, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_COLORCURVE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_COLORLOOKUPTABLE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  BLUT       Blue  color lookup table
    \param  GLUT       Green color lookup table
    \param  RLUT       Red   color lookup table
    \param  ALUT       Alpha color lookup table
    
     

    EMF+ manual 2.2.3.5, Microsoft name: ColorLookupTableEffect Object.
    All tables have 256 entries.
*/
U_PSEUDO_OBJ *U_PMF_IE_COLORLOOKUPTABLE_set(const uint8_t *BLUT, const uint8_t *GLUT, const uint8_t *RLUT, const uint8_t *ALUT){
   if(!BLUT || !GLUT || !RLUT || !ALUT)return(NULL);

   const U_SERIAL_DESC List[] = {
      {BLUT, 1, 256, U_XE},
      {GLUT, 1, 256, U_XE},
      {RLUT, 1, 256, U_XE},
      {ALUT, 1, 256, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_COLORLOOKUPTABLE_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_COLORMATRIX PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Matrix     5 x 5 color transformation matrix, First 4 rows are [{4 multiplier values},0.0] for R,G,B,A, last Row is [{4 color translation valuess}, 1.0]
     

    EMF+ manual 2.2.3.6, Microsoft name: ColorMatrixEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_COLORMATRIX_set(const U_FLOAT *Matrix){
   if(!Matrix)return(NULL);

   const U_SERIAL_DESC List[] = {
      {Matrix, 4, 25, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_COLORMATRIX_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_HUESATURATIONLIGHTNESS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Hue          -180 to 180, 0 is unchanged
    \param  Saturation   -100 to 100, 0 is unchanged
    \param  Lightness    -100 to 100, 0 is unchanged


    EMF+ manual 2.2.3.7, Microsoft name: HueSaturationLightnessEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_HUESATURATIONLIGHTNESS_set(int32_t Hue, int32_t Saturation, int32_t Lightness){

   const U_SERIAL_DESC List[] = {
      {&Hue,        4, 1, U_LE},
      {&Saturation, 4, 1, U_LE},
      {&Lightness,  4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_HUESATURATIONLIGHTNESS_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_LEVELS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Highlight      0 to 100, 100 is unchanged
    \param  Midtone     -100 to   0, 0 is unchanged
    \param  Shadow         0 to 100, 0 is unchanged


    EMF+ manual 2.2.3.8, Microsoft name: LevelsEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_LEVELS_set(int32_t Highlight, int32_t Midtone, int32_t Shadow){

   const U_SERIAL_DESC List[] = {
      {&Highlight, 4, 1, U_LE},
      {&Midtone,   4, 1, U_LE},
      {&Shadow,    4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_LEVELS_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_REDEYECORRECTION PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Elements   Number of members in Rects
    \param  Rects      Array of U_RECTL rectangular area(s) to apply red eye correction


    EMF+ manual 2.2.3.9, Microsoft name: RedEyeCorrectionEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_REDEYECORRECTION_set(uint32_t Elements, const U_RECTL *Rects){
   if(!Elements || !Rects){return(NULL);}

   const U_SERIAL_DESC List[] = {
      {&Elements, 4,   1,       U_LE},
      {Rects,     4*4, Elements,U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_REDEYECORRECTION_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_SHARPEN PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Radius     Sharpening radius in pixels
    \param  Sharpen    0 to 100, 0 is unchanged


    EMF+ manual 2.2.3.10, Microsoft name: SharpenEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_SHARPEN_set(U_FLOAT Radius, int32_t Sharpen){

   const U_SERIAL_DESC List[] = {
      {&Radius,  4, 1, U_LE},
      {&Sharpen, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_SHARPEN_OID, List);
   return(po);
}

/**
    \brief  Create and set a U_PMF_IE_TINT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Hue         -180 to 180, [positive==clockwise] rotation in degrees starting from blue
    \param  Amount      -100 [add black] to 100[add white], 0 is unchanged.  Change in hue on specified axis


    EMF+ manual 2.2.3.11, Microsoft name: TintEffect Object
*/
U_PSEUDO_OBJ *U_PMF_IE_TINT_set(const int32_t Hue, const int32_t Amount){

   const U_SERIAL_DESC List[] = {
      {&Hue,    4, 1, U_LE},
      {&Amount, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMF_IE_TINT_OID, List);
   return(po);
}

//! \cond
/* internal routine, not part of the API.
   Returns a PseudoObject containing a U_PMR_CMN_HDR_OID object.
   The type is something like U_PMR_OFFSETCLIP (the record type, NOT the U_PMR_OFFSETCLIP_OID PseudoObject Type!). 
   The U_PMR_RECFLAG bit is added both in the data and in the Type of the PseudoObject.  
   If that bit is already set no harm, no foul.
*/

U_PSEUDO_OBJ *U_PMR_CMN_HDR_set(uint32_t Type, uint16_t Flags, uint32_t DataSize){

   uint32_t Size = 12 + UP4(DataSize); /* The header itself is always 12, PMR records must be a multiple of 4 */
   Type |= U_PMR_RECFLAG;
   uint16_t utmp16 = Type;
   const U_SERIAL_DESC List[] = {
      {&utmp16,   2, 1, U_LE},
      {&Flags,    2, 1, U_LE},    /* Microsoft EMF+ manual is BE, but this field in this implementation is  LE */
      {&Size,     4, 1, U_LE},
      {&DataSize, 4, 1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_CMN_HDR_OID, List);
   return(po);
}
//! \endcond


/**
    \brief  Create and set a U_PMR_STROKEFILLPATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    

    EMF+ manual mentioned in 2.1.1.1, not otherwise documented, Microsoft name: EmfPlusStrokeFillPath Record, Index 0x37
    
    "This record closes any open figures in a path, strokes the outline of 
     the path by using the current pen, and fills its interior by using the current brush."
     
*/
U_PSEUDO_OBJ *U_PMR_STROKEFILLPATH_set(void){
   int Size = 0;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_STROKEFILLPATH,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_STROKEFILLPATH_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_OFFSETCLIP PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  dX          horizontal translation offset to apply to clipping region
    \param  dY          vertical   translation offset to apply to clipping region


    EMF+ manual 2.3.1.1, Microsoft name: EmfPlusOffsetClip Record,  Index 0x35
*/
U_PSEUDO_OBJ *U_PMR_OFFSETCLIP_set(U_FLOAT dX, U_FLOAT dY){
   int Size = 2*4;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_OFFSETCLIP,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&dX,      4,        1, U_LE},
      {&dY,      4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_OFFSETCLIP_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_RESETCLIP PseudoObject
    \return Pointer to PseudoObject, NULL on error


    EMF+ manual 2.3.1.2, Microsoft name: EmfPlusResetClip Record, Index 0x31
*/
U_PSEUDO_OBJ *U_PMR_RESETCLIP_set(void){
   int Size = 0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_RESETCLIP,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_RESETCLIP_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETCLIPPATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  CMenum      CombineMode enumeration..
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)


    EMF+ manual 2.3.1.3, Microsoft name: EmfPlusSetClipPath Record, Index 0x33
*/
U_PSEUDO_OBJ *U_PMR_SETCLIPPATH_set(uint32_t PathID, uint32_t CMenum){
   if(PathID>63)return(NULL);
   int Size=0;
   uint16_t utmp16 =  ((PathID & U_FF_MASK_OID8) << U_FF_SHFT_OID8) | ((CMenum & U_FF_MASK_CM4) << U_FF_SHFT_CM4);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETCLIPPATH,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETCLIPPATH_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETCLIPRECT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  CMenum      CombineMode enumeration..
    \param  Rect        U_PSEUDO_OBJ containing an U_PMF_RECTF object or an array of U_PMF_RECTF objects (the first is used)
    

    EMF+ manual 2.3.1.4, Microsoft name: EmfPlusSetClipRect Record, Index 0x32
*/
U_PSEUDO_OBJ *U_PMR_SETCLIPRECT_set(uint32_t CMenum, const U_PSEUDO_OBJ *Rect){
   int Size=4*4;
   const char *start;
   uint16_t utmp16 = ((CMenum & U_FF_MASK_CM4) << U_FF_SHFT_CM4);
   if(Rect){
      if(        Rect->Type == U_PMF_RECTF_OID){ 
        start = Rect->Data;
      }
      else if(Rect->Type == (U_PMF_RECTF_OID | U_PMF_ARRAY_OID)){ 
        start = Rect->Data + 4;
      }
      else { return(0); }
   }
   else {    return(0); }

   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETCLIPRECT,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,  ph->Used, 1, U_XE},
      {start,     4,        4, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETCLIPRECT_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETCLIPREGION PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)
    \param  CMenum      CombineMode enumeration..
    

    EMF+ manual 2.3.1.5, Microsoft name: EmfPlusSetClipRegion Record, Index 0x34
*/
U_PSEUDO_OBJ *U_PMR_SETCLIPREGION_set(uint32_t PathID, uint32_t CMenum){
   if(PathID>63)return(NULL);
   int Size=0;
   uint16_t utmp16 =  ((PathID & U_FF_MASK_OID8) << U_FF_SHFT_OID8) | ((CMenum & U_FF_MASK_CM4) << U_FF_SHFT_CM4);

   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETCLIPREGION,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETCLIPREGION_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_COMMENT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  cbData      Number of bytes in Data, must be a multiple of 4
    \param  Data        Private data, may be anything.  Stored in PseudoObject without adjusting byte order.
    

    EMF+ manual 2.3.2.1, Microsoft name: EmfPlusComment Record, Index 0x03
*/
U_PSEUDO_OBJ *U_PMR_COMMENT_set(size_t cbData, const void *Data){
   if(UP4(cbData) != cbData){ return(NULL); }
   if(cbData && !Data){       return(NULL); }
   int Size=cbData;

   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_COMMENT,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {Data,     cbData,   1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_COMMENT_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_ENDOFFILE PseudoObject
    \return Pointer to PseudoObject, NULL on error


    EMF+ manual 2.3.3.1, Microsoft name: EmfPlusEndOfFile Record, Index 0x02
*/
U_PSEUDO_OBJ *U_PMR_ENDOFFILE_set(void){
   int Size=0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_ENDOFFILE,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_ENDOFFILE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_GETDC PseudoObject
    \return Pointer to PseudoObject, NULL on error


    EMF+ manual 2.3.3.2, Microsoft name: EmfPlusGetDC Record, Index 0x04
*/
U_PSEUDO_OBJ *U_PMR_GETDC_set(void){
   int Size=0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_GETDC,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_GETDC_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_HEADER PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  IsDual       set = Dual-mode file, clear= EMF+ only file.
    \param  IsVideo      set = video device, clear= printer.  Ignore all other bits.
    \param  Version      U_PSEUDO_OBJ containing a U_PMF_GRAPHICSVERSION object
    \param  LogicalDpiX  Horizontal resolution reference device in DPI
    \param  LogicalDpiY  Vertical   resolution reference device in DPI
    

    EMF+ manual 2.3.3.3, Microsoft name: EmfPlusHeader Record, Index 0x01
*/
U_PSEUDO_OBJ *U_PMR_HEADER_set(int IsDual, int IsVideo, const U_PSEUDO_OBJ *Version, 
      uint32_t LogicalDpiX, uint32_t LogicalDpiY){
   if(!Version || (Version->Type != U_PMF_GRAPHICSVERSION_OID)){ return(NULL); }
   int Size=Version->Used + 3*4;
   uint16_t utmp16 =  (IsDual ? U_PPF_DM : 0);
   uint32_t Flags  =  (IsVideo ? U_PPF_VIDEO : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_HEADER,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {Version->Data, Version->Used, 1, U_XE},
      {&Flags,        4,             1, U_LE},
      {&LogicalDpiX,  4,             1, U_LE},
      {&LogicalDpiY,  4,             1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_HEADER_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_CLEAR PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Color        U_PSEUDO_OBJ containing a U_PMF_ARGB object.
    
   

    EMF+ manual 2.3.4.1, Microsoft name: EmfPlusClear Record, Index 0x09

    Erase everything preceding, set background ARGB to Color.
*/
U_PSEUDO_OBJ *U_PMR_CLEAR_set(const U_PSEUDO_OBJ *Color){
   if(!Color || (Color->Type != U_PMF_ARGB_OID)){ return(NULL); }
   int Size=Color->Used;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_CLEAR,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,    ph->Used,    1, U_XE},
      {Color->Data, Color->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_CLEAR_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWARC PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Start       Start angle, >=0.0, degrees clockwise from 3:00
    \param  Sweep       Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param  Rect        U_PSEUDO_OBJ containing a U_PMF_RECT or U_PMF_RECTF object
   

    EMF+ manual 2.3.4.2, Microsoft name: EmfPlusDrawArc Record, Index 0x12
*/
U_PSEUDO_OBJ *U_PMR_DRAWARC_set(uint32_t PenID, U_FLOAT Start, U_FLOAT Sweep, const U_PSEUDO_OBJ *Rect){
   int ctype;
   if(PenID>63)return(NULL);
   if(!Rect){                                 return(NULL); }
   else {
      if(     Rect->Type == U_PMF_RECT_OID ){ ctype = 1;    }
      else if(Rect->Type == U_PMF_RECTF_OID){ ctype = 0;    }
      else {                                  return(NULL); }
   }
   int Size = 2*4 + Rect->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWARC,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,   ph->Used,   1, U_XE},
      {&Start,     4,          1, U_LE},
      {&Sweep,     4,          1, U_LE},
      {Rect->Data, Rect->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWARC_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWBEZIERS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Points      U_PSEUDO_OBJ containing first Elements, then a U_PMF_POINT, U_PMF_POINTR or U_PMF_POINTF object
   

    EMF+ manual 2.3.4.3, Microsoft name: EmfPlusDrawBeziers Record, Index 0x19
*/
U_PSEUDO_OBJ *U_PMR_DRAWBEZIERS_set(uint32_t PenID, const U_PSEUDO_OBJ *Points){
   int ctype, RelAbs;
   if(PenID>63)return(NULL);
   if(Points){
      if(     Points->Type == U_PMF_POINTR_OID){                     RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 0; }
      else {                                                         return(NULL);          }
   }
   else {                                                            return(NULL);          }
   int Size = Points->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (RelAbs ? U_PPF_P : 0) |(PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWBEZIERS,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,     ph->Used,     1, U_XE},
      {Points->Data, Points->Used, 1, U_XE}, /* Includes Elements */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWBEZIERS_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWCLOSEDCURVE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Tension     Controls splines, 0 is straight line, >0 is curved
    \param  Points      U_PSEUDO_OBJ containing a U_PMF_POINT, U_PMF_POINTR or U_PMF_POINTF object
   

    EMF+ manual 2.3.4.4, Microsoft name: EmfPlusDrawClosedCurve Record, Index 0x17

    Curve is a cardinal spline.

    References sent by MS support:

       http://alvyray.com/Memos/CG/Pixar/spline77.pdf

       http://msdn.microsoft.com/en-us/library/4cf6we5y(v=vs.110).aspx


*/
U_PSEUDO_OBJ *U_PMR_DRAWCLOSEDCURVE_set(uint32_t PenID, U_FLOAT Tension, const U_PSEUDO_OBJ *Points){
   int ctype, RelAbs;
   if(PenID>63)return(NULL);
   if(Points){
      if(     Points->Type == U_PMF_POINTR_OID){                     RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 0; }
      else {                                                         return(NULL);          }
   }
   else {                                                            return(NULL);          }
   int Size = 4 + Points->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (RelAbs ? U_PPF_P : 0) |(PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWCLOSEDCURVE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,     ph->Used,     1, U_XE},
      {&Tension,     4,            1, U_LE},
      {Points->Data, Points->Used, 1, U_XE}, /* includes Elements*/
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWCLOSEDCURVE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWCURVE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Tension     Controls splines, 0 is straight line, >0 is curved
    \param  Offset      The starting point in the list of points, 0 is first.
    \param  NSegs       Number of segments to draw. Starting at Offset go NSegs straight lines, must not run out of points..
    \param  Points      U_PSEUDO_OBJ containing an element count then a series of U_PMF_POINT or U_PMF_POINTF object
   

    EMF+ manual 2.3.4.5, Microsoft name: EmfPlusDrawCurve Record, Index 0x18

    Curve is a cardinal spline, using doubled terminator points to generate curves for the terminal segments.

    References sent by MS support:

       http://alvyray.com/Memos/CG/Pixar/spline77.pdf

       http://msdn.microsoft.com/en-us/library/4cf6we5y(v=vs.110).aspx

*/
U_PSEUDO_OBJ *U_PMR_DRAWCURVE_set(uint32_t PenID, U_FLOAT Tension, uint32_t Offset, uint32_t NSegs, const U_PSEUDO_OBJ *Points){
   int ctype;
   if(PenID>63)return(NULL);
   if(!Points){                                                      return(NULL); }
   if(!NSegs){                                                       return(NULL); }
   else {
      if(     Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ ctype = 1;    }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ ctype = 0;    }
      else {                                                         return(NULL); }
   }
   uint32_t Elements = (Points->Used - 4)/(ctype ? 4 : 8);  /* This way do not need to worry about byte order */
   if(Offset + NSegs + 1 > Elements){   return(NULL); }
   int Size = 3*4 + Points->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) |(PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWCURVE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,         ph->Used,       1, U_XE},
      {&Tension,         4,              1, U_LE},
      {&Offset,          4,              1, U_LE},
      {&NSegs,           4,              1, U_LE},
      {Points->Data,     Points->Used,   1, U_XE}, /* Elements, points */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWCURVE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWDRIVERSTRING PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  FontID      U_PMF_FONT object in the EMF+ object table (0-63, inclusive)
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  DSOFlags    DriverStringOptions flags
    \param  HasMatrix   If 1 record contains a TransformMatrix field, if 0 it does not.
    \param  GlyphCount  The number of Elements in Glyphs, must agree with the number of elements in Points.
    \param  Glyphs      If U_DSO_CmapLookup is set in DSOFlags this is an array of UTF16LE characters, otherwise, it is an array of indices into the U_PMF_FONT object indexed by Object_ID in flags.
    \param  Points      U_PSEUDO_OBJ containing a U_PMF_POINTF object
    \param  Tm          U_PSEUDO_OBJ containing a U_PMF_TRANSFORMMATRIX object. Apply to Glyphs & Positions. Present if HasMatrix is 1
   

    EMF+ manual 2.3.4.6, Microsoft name: EmfPlusDrawDriverString Record, Index 0x36
*/
U_PSEUDO_OBJ *U_PMR_DRAWDRIVERSTRING_set(uint32_t FontID, const U_PSEUDO_OBJ *BrushID, 
      uint32_t DSOFlags, uint32_t HasMatrix, uint32_t GlyphCount,
      const uint16_t *Glyphs, const U_PSEUDO_OBJ *Points, const U_PSEUDO_OBJ *Tm){
   int btype;
   if(FontID>63){                                                           return(NULL); }
   if(!Glyphs){                                                             return(NULL); }
   if(!Points || (Points->Type != (U_PMF_POINTF_OID | U_PMF_ARRAY_OID))){   return(NULL); }
   uint32_t Elements = (Points->Used -4)/4;
   if(GlyphCount != Elements){                                              return(NULL); } 
   if(BrushID){ 
      if(      BrushID->Used != 4){                                         return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){                            btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){                            btype = 0;    }
      else {                                                                return(NULL); }
   }
   else {                                                                   return(NULL); } 
   int Size = 4 + BrushID->Used + 3*4 + Elements*2 + (Points->Used - 4);
   if(HasMatrix){
      if(!Tm){                                                              return(NULL); }
      else if(Tm->Type != (U_PMF_TRANSFORMMATRIX_OID)){                     return(NULL); }
      Size += Tm->Used;
   }
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) |(FontID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWDRIVERSTRING,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,ph->Used, 1, U_XE},
      {BrushID->Data,                  BrushID->Used,              1,        U_XE},
      {&DSOFlags,                      4,                          1,        U_LE},
      {&HasMatrix,                     4,                          1,        U_LE},
      {&Elements,                      4,                          1,        U_LE},
      {Glyphs,                         2,                          Elements, U_LE},
      {Points->Data + 4,               Points->Used - 4,           1,        U_XE},  /* omit Elements */
      {(HasMatrix ? Tm->Data : NULL),  (HasMatrix ? Tm->Used : 0), 1,        U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWDRIVERSTRING_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWELLIPSE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Rect        U_PSEUDO_OBJ containing a U_PMF_RECT or U_PMF_RECTF object
   

    EMF+ manual 2.3.4.7, Microsoft name: EmfPlusDrawEllipse Record, Index 0x0F
*/
U_PSEUDO_OBJ *U_PMR_DRAWELLIPSE_set(uint32_t PenID, const U_PSEUDO_OBJ *Rect){
   if(PenID>63)return(NULL);
   int ctype;
   if(!Rect){                                 return(NULL); }
   else {
      if(     Rect->Type == U_PMF_RECT_OID ){ ctype = 1;    }
      else if(Rect->Type == U_PMF_RECTF_OID){ ctype = 0;    }
      else {                                  return(NULL); }
   }
   int Size = Rect->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWELLIPSE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,   ph->Used,   1, U_XE},
      {Rect->Data, Rect->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWELLIPSE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWIMAGE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  ImgID       U_PMF_IMAGE object in the EMF+ object table (0-63, inclusive)
    \param  ImgAttrID   index of a U_PMF_IMAGEATTRIBUTES object in the object table
    \param  SrcUnit     UnitType enumeration
    \param  SrcRect     U_PSEUDO_OBJ containing a U_PMF_RECTF object, Source region of image 
    \param  DstRect     U_PSEUDO_OBJ containing a U_PMF_RECT or U_PMF_RECTF object
   

    EMF+ manual 2.3.4.8, Microsoft name: EmfPlusDrawImage Record, Index 0x1A
*/
U_PSEUDO_OBJ *U_PMR_DRAWIMAGE_set(uint32_t ImgID,  
      int32_t ImgAttrID, int32_t SrcUnit, const U_PSEUDO_OBJ *SrcRect, const U_PSEUDO_OBJ *DstRect){
   int ctype;
   if(ImgID>63)return(NULL);
   if(!SrcRect || (SrcRect->Type != U_PMF_RECTF_OID)){  return(NULL); }
   if(!DstRect){                                 return(NULL); }
   else {
      if(     DstRect->Type == U_PMF_RECT_OID ){ ctype = 1;    }
      else if(DstRect->Type == U_PMF_RECTF_OID){ ctype = 0;    }
      else {                                     return(NULL); }
   }
   int Size = 2*4 + SrcRect->Used + DstRect->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (ImgID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWIMAGE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {&ImgAttrID,    4,             1, U_LE},
      {&SrcUnit,      4,             1, U_LE},
      {SrcRect->Data, SrcRect->Used, 1, U_XE},
      {DstRect->Data, DstRect->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWIMAGE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWIMAGEPOINTS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  ImgID       U_PMF_IMAGE object in the EMF+ object table (0-63, inclusive)
    \param  etype       Set: effect from previous U_PMR_SERIALIZABLEOBJECT record will be applied; Clear:  no effect applied
    \param  ImgAttrID   index of a U_PMF_IMAGEATTRIBUTES object in the object table
    \param  SrcUnit     UnitType enumeration
    \param  SrcRect     U_PSEUDO_OBJ containing a U_PMF_RECTF object, Source region of image 
    \param  Points      U_PSEUDO_OBJ containing an array of 3 (U_PMF_POINT, U_PMF_POINTF, or U_PMF_POINTF) objects. These points are the UL, UR, and LL vertices of a parallelogram.
   

    EMF+ manual 2.3.4.9, Microsoft name: EmfPlusDrawImagePoints Record, Index 0x1B


   WARNING!  Windows XP Preview does not show filter effects, whether or not U_PPF_E is set.   They are visible if the EMF+
   file is inserted as an image into PowerPoint.
*/
U_PSEUDO_OBJ *U_PMR_DRAWIMAGEPOINTS_set(uint32_t ImgID, int etype,
      int32_t ImgAttrID, int32_t SrcUnit, const U_PSEUDO_OBJ *SrcRect,
      const U_PSEUDO_OBJ *Points){
   int ctype, RelAbs;
   if(ImgID>63){                                                      return(NULL); }
   if(!SrcRect || (SrcRect->Type != U_PMF_RECTF_OID)){                return(NULL); }
   if(Points){
      if(     Points->Type == U_PMF_POINTR_OID){                     RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 0; }
      else {                                                         return(NULL);          }
   }
   else {                                                            return(NULL);          }
   int Size = 2*4 + SrcRect->Used + Points->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (etype ? U_PPF_E : 0) | (RelAbs ? U_PPF_P : 0) | (ImgID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWIMAGEPOINTS,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {&ImgAttrID,    4,             1, U_LE},
      {&SrcUnit,      4,             1, U_LE},
      {SrcRect->Data, SrcRect->Used, 1, U_XE},
      {Points->Data,  Points->Used,  1, U_XE}, /* includes Elements*/
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWIMAGEPOINTS_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWLINES PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  dtype       Set: path must be closed, Clear: path is open
    \param  Points      U_PSEUDO_OBJ containing an array of 3 U_PMF_POINT, U_PMF_POINTR, or U_PMF_POINTF objects
   

    EMF+ manual 2.3.4.10, Microsoft name: EmfPlusDrawLines Record, Index 0x0D
*/
U_PSEUDO_OBJ *U_PMR_DRAWLINES_set(uint32_t PenID, int dtype, const U_PSEUDO_OBJ *Points){
   int ctype, RelAbs;
   if(PenID>63){                                                      return(NULL); }
   if(Points){
      if(     Points->Type == U_PMF_POINTR_OID){                     RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 0; }
      else {                                                         return(NULL);          }
   }
   else {                                                            return(NULL);          }
   int Size = Points->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (dtype ? U_PPF_D : 0) | (RelAbs ? U_PPF_P : 0) | (PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWLINES,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,     ph->Used,     1, U_XE},
      {Points->Data, Points->Used, 1, U_XE}, /* includes Elements */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWLINES_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWPATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    

    EMF+ manual 2.3.4.11, Microsoft name: EmfPlusDrawPath Record, Index 0x15
*/
U_PSEUDO_OBJ *U_PMR_DRAWPATH_set(uint32_t PathID, uint32_t PenID){
   if(PathID>63)return(NULL);
   if(PenID>63)return(NULL);
   int Size = 4;
   uint16_t utmp16 =  (PathID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWPATH,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&PenID,   4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWPATH_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWPIE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Start       Start angle, >=0.0, degrees clockwise from 3:00
    \param  Sweep       Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param  Rect        U_PSEUDO_OBJ containing a U_PMF_RECT or U_PMF_RECTF object
   

    EMF+ manual 2.3.4.12, Microsoft name: EmfPlusDrawPie Record, Index 0x0D
*/
U_PSEUDO_OBJ *U_PMR_DRAWPIE_set(uint32_t PenID, 
      U_FLOAT Start, U_FLOAT Sweep, const U_PSEUDO_OBJ *Rect){
   int ctype;
   if(PenID>63)return(NULL);
   if(!Rect){                                 return(NULL); }
   else {
      if(     Rect->Type == U_PMF_RECT_OID ){ ctype = 1;    }
      else if(Rect->Type == U_PMF_RECTF_OID){ ctype = 0;    }
      else {                                  return(NULL); }
   }
   int Size = 2*4 + Rect->Used;
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0) | (PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWPIE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,   ph->Used,   1, U_XE},
      {&Start,     4,          1, U_LE},
      {&Sweep,     4,          1, U_LE},
      {Rect->Data, Rect->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWPIE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWRECTS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  Rects       U_PSEUDO_OBJ containing 1 rect OR  a count N follwed by N rects.  Rects may be either U_PMF_RECT or U_PMF_RECTF
   

    EMF+ manual 2.3.4.13, Microsoft name: EmfPlusDrawRects Record, Index 0x0B
*/
U_PSEUDO_OBJ *U_PMR_DRAWRECTS_set(uint32_t PenID, const U_PSEUDO_OBJ *Rects){
   int ctype;
   int just1;
   uint32_t Elements=1; /* only used when a single rect is passed in, not an array, not even an array with one member*/
   if(PenID>63){ return(NULL); }
   if(Rects){
      if(      (Rects->Type & U_PMF_MASK_OID) == U_PMF_RECT_OID ){   ctype = 1;    }
      else if( (Rects->Type & U_PMF_MASK_OID) == U_PMF_RECTF_OID){   ctype = 0;    }
      else {                                                         return(NULL); }
   }
   else {                                                            return(NULL); }
   just1 = (Rects->Type & U_PMF_ARRAY_OID ? 0 : 1);
   int Size = Rects->Used + (just1 ? 4 : 0); /* Elements in Rects for array, not for single */
   uint16_t utmp16 =  (ctype ? U_PPF_C : 0)| (PenID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWRECTS,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,                           ph->Used,       1, U_XE},
      {(just1 ? (char *)&Elements : NULL), (just1 ? 4: 0), 1, U_LE}, /* element count if a single Rect was passed in, empty otherwise */ 
      {Rects->Data,                        Rects->Used,    1, U_XE}, /* Elements + Array, already stored in Rects, if an array was passed in, just rect if a single */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWRECTS_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_DRAWSTRING PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  FontID      U_PMF_FONT object in the EMF+ object table (0-63, inclusive)
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  FormatID    U_PMF_STRINGFORMAT object in EMF+ Object Table.
    \param  Length      Number of characters in the string.
    \param  Rect        U_PSEUDO_OBJ containing a U_PMF_RECTF object, string's bounding box
    \param  Text        Array of UFT-16LE unicode characters.
   

    EMF+ manual 2.3.4.14, Microsoft name: EmfPlusDrawString Record, Index 0x1C
*/
U_PSEUDO_OBJ *U_PMR_DRAWSTRING_set(uint32_t FontID, const U_PSEUDO_OBJ *BrushID, 
      uint32_t FormatID, uint32_t Length, const U_PSEUDO_OBJ *Rect, const uint16_t *Text){
   int btype; 
   if(FontID>63){                                 return(NULL); }
   if(!Length){                                   return(NULL); }
   else if (!Text){                               return(NULL); }
   if(!Rect || Rect->Type != U_PMF_RECTF_OID){    return(NULL); }
   if(BrushID){  
      if(      BrushID->Used != 4){               return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){  btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){  btype = 0;    }
      else {                                      return(NULL); }
   }
   else {                                         return(NULL); }
   int Size = BrushID->Used + 2*4 + Rect->Used +2*Length;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0)| (FontID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   uint32_t pad = (0x1 & Length ? 2 : 0);
   Size+=pad;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_DRAWSTRING,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1,             U_XE},
      {BrushID->Data, BrushID->Used, 1,             U_XE},
      {&FormatID,     4,             1,             U_LE},
      {&Length,       4,             1,             U_LE},
      {Rect->Data,    Rect->Used,    1,             U_XE},
      {Text,          2*Length,      1,             U_XE},
      {NULL,          pad,           (pad ? 1 : 0), (pad ? U_XE : U_XX)}, /* Entire record must be a multiple of 4 */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_DRAWSTRING_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLCLOSEDCURVE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  ftype       If U_WINDING use winding fill, else use fill
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  Tension     Controls splines, 0 is straight line, >0 is curved
    \param  Points      U_PSEUDO_OBJ containing a U_PMF_POINT, U_PMF_POINTR or U_PMF_POINTF object
   

    EMF+ manual 2.3.4.15, Microsoft name: EmfPlusFillClosedCurve Record, Index 0x16
*/
U_PSEUDO_OBJ *U_PMR_FILLCLOSEDCURVE_set(int ftype, U_FLOAT Tension, const U_PSEUDO_OBJ *BrushID, const U_PSEUDO_OBJ *Points){
   int btype, ctype, RelAbs;
   int Size=0;
   if(BrushID){ 
      if(      BrushID->Used != 4){              return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){ btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){ btype = 0;    }
      else {                                     return(NULL); }
   }
   else {                                        return(NULL); }
   if(Points){
      if(     Points->Type == U_PMF_POINTR_OID){                     RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 0; }
      else {                                                         return(NULL);          }
   }
   else {                                                            return(NULL);          }
   Size = BrushID->Used + 4 + Points->Used;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) | (ctype ? U_PPF_C : 0) |((ftype == U_WINDING) ? U_PPF_F : 0) |(RelAbs ? U_PPF_P : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLCLOSEDCURVE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {BrushID->Data, BrushID->Used, 1, U_XE},
      {&Tension,      4,             1, U_LE},
      {Points->Data,  Points->Used,  1, U_XE}, /* includes Elements */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLCLOSEDCURVE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLELLIPSE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  Rect        U_PSEUDO_OBJ containing a U_PMF_RECT or U_PMF_RECTF object
   

    EMF+ manual 2.3.4.16, Microsoft name: EmfPlusFillEllipse Record, Index 0x0E
*/
U_PSEUDO_OBJ *U_PMR_FILLELLIPSE_set(const U_PSEUDO_OBJ *BrushID, const U_PSEUDO_OBJ *Rect){
   int btype, ctype;
   if(BrushID){ 
      if(      BrushID->Used != 4){              return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){ btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){ btype = 0;    }
      else {                                     return(NULL); }
   }
   else {                                        return(NULL); } 
   if(Rect){
      if(     Rect->Type == U_PMF_RECT_OID ){    ctype = 1;    }
      else if(Rect->Type == U_PMF_RECTF_OID){    ctype = 0;    }
      else {                                     return(NULL); }
   }
   else {                                        return(NULL); }
   int Size = BrushID->Used + Rect->Used;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) | (ctype ? U_PPF_C : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLELLIPSE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {BrushID->Data, BrushID->Used, 1, U_XE},
      {Rect->Data,    Rect->Used,    1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLELLIPSE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLPATH PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    

    EMF+ manual 2.3.4.17, Microsoft name: EmfPlusFillPath Record, Index 0x14
*/
U_PSEUDO_OBJ *U_PMR_FILLPATH_set(uint32_t PathID, const U_PSEUDO_OBJ *BrushID){
   int btype;
   int Size=0;
   if(PathID>63)return(NULL);
   if(BrushID){ 
      if(      BrushID->Used != 4){              return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){ btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){ btype = 0;    }
      else {                                     return(NULL); }
   }
   else {                                        return(NULL); }
   Size = BrushID->Used;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) | (PathID & U_FF_MASK_OID8) << U_FF_SHFT_OID8 ;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLPATH,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {BrushID->Data, BrushID->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLPATH_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLPIE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Start       Start angle, >=0.0, degrees clockwise from 3:00
    \param  Sweep       Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  Rect        U_PSEUDO_OBJ containing a U_PMF_RECT or U_PMF_RECTF object
   

    EMF+ manual 2.3.4.18, Microsoft name: EmfPlusFillPie Record, Index 0x10
*/
U_PSEUDO_OBJ *U_PMR_FILLPIE_set(U_FLOAT Start, U_FLOAT Sweep, const U_PSEUDO_OBJ *BrushID, const U_PSEUDO_OBJ *Rect){
   int btype, ctype;
   if(BrushID){ 
      if(      BrushID->Used != 4){              return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){ btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){ btype = 0;    }
      else {                                     return(NULL); }
   }
   else {                                        return(NULL); } 
   if(!Rect){                                    return(NULL); }
   else {
      if(     Rect->Type == U_PMF_RECT_OID ){    ctype = 1;    }
      else if(Rect->Type == U_PMF_RECTF_OID){    ctype = 0;    }
      else {                                     return(NULL); }
   }
   int Size = BrushID->Used + 2*4 + Rect->Used;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) | (ctype ? U_PPF_C : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLPIE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {BrushID->Data, BrushID->Used, 1, U_XE},
      {&Start,        4,             1, U_LE},
      {&Sweep,        4,             1, U_LE},
      {Rect->Data,    Rect->Used,    1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLPIE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLPOLYGON PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  Points      U_PSEUDO_OBJ containing an array of 3 U_PMF_POINT, U_PMF_POINTR, or U_PMF_POINTF objects
   

    EMF+ manual 2.3.4.19, Microsoft name: EmfPlusFillPolygon Record, Index 0x0C
*/
U_PSEUDO_OBJ *U_PMR_FILLPOLYGON_set(const U_PSEUDO_OBJ *BrushID, const U_PSEUDO_OBJ *Points){
   int btype, ctype, RelAbs;
   if(BrushID){ 
      if(      BrushID->Used != 4){                                  return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){                     btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){                     btype = 0;    }
      else {                                                         return(NULL); }
   }
   else {                                                            return(NULL); }
   if(Points){
      if(     Points->Type ==  U_PMF_POINTR_OID){                    RelAbs = 1; ctype = 0; }
      else if(Points->Type == (U_PMF_POINT_OID  | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 1; }
      else if(Points->Type == (U_PMF_POINTF_OID | U_PMF_ARRAY_OID)){ RelAbs = 0; ctype = 0; }
      else {                                                         return(NULL);          }
   }
   else {                                                            return(NULL);          }
   int Size = BrushID->Used + Points->Used;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) |  (ctype ? U_PPF_C : 0) |(RelAbs ? U_PPF_P : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLPOLYGON,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {BrushID->Data, BrushID->Used, 1, U_XE},
      {Points->Data,  Points->Used,  1, U_XE}, /* includes Elements */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLPOLYGON_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLRECTS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    \param  Rects       U_PSEUDO_OBJ containing 1 rect OR  a count N followed by N rects.  Rects may be either U_PMF_RECT or U_PMF_RECTF
   

    EMF+ manual 2.3.4.20, Microsoft name: EmfPlusFillRects Record, Index 0x0A
*/
U_PSEUDO_OBJ *U_PMR_FILLRECTS_set(const U_PSEUDO_OBJ *BrushID, const U_PSEUDO_OBJ *Rects){
   int btype, ctype;
   int just1;
   uint32_t Elements=1; /* only used when a single rect is passed in, not an array, not even an array with one member*/
   if(BrushID){ 
      if(      BrushID->Used != 4){                                  return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){                     btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){                     btype = 0;    }
      else {                                                         return(NULL); }
   }
   else {                                                            return(NULL); }
   if(Rects){
      if(      (Rects->Type & U_PMF_MASK_OID) == U_PMF_RECT_OID ){   ctype = 1;    }
      else if( (Rects->Type & U_PMF_MASK_OID) == U_PMF_RECTF_OID){   ctype = 0;    }
      else {                                                         return(NULL); }
   }
   else {                                                            return(NULL); }
   just1 = (Rects->Type & U_PMF_ARRAY_OID ? 0 : 1);
   int Size = BrushID->Used + Rects->Used + (just1 ? 4 : 0); /* Elements in Rects for array, not for single */
   uint16_t utmp16 =  (btype ? U_PPF_B : 0)|(ctype ? U_PPF_C : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLRECTS,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,                           ph->Used,       1, U_XE},
      {BrushID->Data,                      BrushID->Used,  1, U_XE},
      {(just1 ? (char *)&Elements : NULL), (just1 ? 4: 0), 1, U_LE}, /* element count if a single Rect was passed in, empty otherwise */ 
      {Rects->Data,                        Rects->Used,    1, U_XE}, /* Elements + Array, already stored in Rects, if an array was passed in, just rect if a single */
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLRECTS_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_FILLREGION PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  RgnID       U_PMF_REGION object in the EMF+ object table (0-63, inclusive)
    \param  BrushID     U_PSEUDO_OBJ containing a U_PMF_ARGB or a U_PMF_4NUM. Color or U_PMF_BRUSH object in the EMF+ object table (0-63, inclusive)
    

    EMF+ manual 2.3.4.21, Microsoft name: EmfPlusFillRegion Record, Index 0x13
*/
U_PSEUDO_OBJ *U_PMR_FILLREGION_set(uint32_t RgnID, const U_PSEUDO_OBJ *BrushID){
   int btype;
   if(BrushID){ 
      if(      BrushID->Used != 4){                                  return(NULL); }
      else if( BrushID->Type == U_PMF_ARGB_OID){                     btype = 1;    }
      else if( BrushID->Type == U_PMF_4NUM_OID){                     btype = 0;    }
      else {                                                         return(NULL); }
   }
   else {                                                            return(NULL); }
   int Size = BrushID->Used;
   uint16_t utmp16 =  (btype ? U_PPF_B : 0) | (RgnID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_FILLREGION,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {BrushID->Data, BrushID->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_FILLREGION_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_OBJECT PseudoObject from another PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  ObjID       Index for this object in the EMF+ object table (0-63, inclusive)
    \param  Po          U_PSEUDO_OBJ containing an object type that may be stored in the EMF+ object table
*/
U_PSEUDO_OBJ *U_PMR_OBJECT_PO_set(uint32_t ObjID, U_PSEUDO_OBJ *Po){
   if(!Po){ return(NULL); }
   int otype = U_OID_To_OT(Po->Type); /* This will return 0 if the type is not valid for an object */
   if(!otype){ return(NULL); } 
   U_PSEUDO_OBJ *po = U_PMR_OBJECT_set(ObjID, otype, 0, 0, Po->Used, Po->Data); /* 0,0 = rec. not continued, TSize value (ignored)  */
   return(po);
}

/**
    \brief  Create and set a U_PMR_OBJECT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  ObjID       Index for this object in the EMF+ object table (0-63, inclusive)
    \param  otype       ObjectType enumeration for this Object
    \param  ntype       Set: object definition continues in next record; Clear: this is the sole object definition record
    \param  TSize       If ntype is set the total number of data bytes split across multiple records.  If ntype is clear, it is ignored.
    \param  cbData      Object's data size, in bytes.
    \param  Data        Object's data.  Type from otype.
    

    EMF+ manual 2.3.5.1, Microsoft name: EmfPlusObject Record, Index 0x13

    Normally this is only called by U_PMR_OBJECT_PO_set().
    
    U_PMR_OBJECT records can only hold a maximum of 65020 bytes of data.  If the object is larger than that 
      then multiple U_PMR_OBJECT records are created, one after the other.  If this
      happens each record has cbData following ph, and the ntype flag is set.  If all of the data is less than 65020
      then cbData is NOT entered following ph, and the ntype flag is clear.
    
    Call initially in all cases with ntype clear and TSize = 0.  If the record needs to be fragmented
      the function will call itself recursively to do so.
    
*/
U_PSEUDO_OBJ *U_PMR_OBJECT_set(uint32_t ObjID, int otype, int ntype, uint32_t TSize, size_t cbData, const char *Data){
   uint32_t CSize;
   int      Pad   = UP4(TSize) - TSize;   
   if((otype < U_OT_Brush) || (otype > U_OT_CustomLineCap)){ return(NULL); }
   if(ntype && (cbData > U_OBJRECLIM)){                      return(NULL); }
   if(!Data || !cbData){                                     return(NULL); }
   U_PSEUDO_OBJ *po;
   
   if(!ntype && !TSize && (cbData > U_OBJRECLIM)){  
      ntype = 1;
      TSize = cbData;
      po = U_PO_create(NULL, TSize + 16 * (1 + (TSize/cbData)), 0, U_PMR_OBJECT_OID);
      if(po){
         while(cbData){
            CSize = (cbData > U_OBJRECLIM ? U_OBJRECLIM : cbData);
            U_PSEUDO_OBJ *pot = U_PMR_OBJECT_set(ObjID, otype, ntype, TSize, CSize, Data);
            if(!pot)break;
            U_PSEUDO_OBJ *newpo = U_PO_po_append(po, pot, U_PMF_KEEP_ELEMENTS);
            U_PO_free(&pot);
            if(!newpo)break;
            po = newpo; 
            Data   += U_OBJRECLIM;
            cbData -= CSize;
         }
         if(cbData){ /* some error */
            U_PO_free(&po);
         }
      }
   }
   else {
      /* Send in DataSize, U_PMR_CMN_HDR_set will adjust Header Size with 1-3 pad bytes if needed */
      uint16_t utmp16 =  otype << 8 | (ntype ? U_PPF_N : 0) | (ObjID & U_FF_MASK_OID8) << U_FF_SHFT_OID8;
      U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_OBJECT,utmp16,cbData + (ntype ? 4 : 0));
      const U_SERIAL_DESC List[] = {
         {ph->Data,                 ph->Used,       1,               U_XE               },
         {(ntype ? &TSize : NULL), (ntype ? 4 : 0), (ntype ? 1 : 0), U_LE               },
         {Data,                    cbData,          1,               U_XE               },
         {NULL,                    (Pad ? Pad : 0), (Pad ? 1 : 0),   (Pad ? U_XE : U_XX)}, /* Either 1-3 pad bytes or a terminator */
         {NULL,0,0,U_XX} /* terminator, possibly a second in the list, which is harmless */
      };
      po = U_PMF_SERIAL_set(U_PMR_OBJECT_OID, List);
      U_PO_free(&ph);
   }
   return(po);
}

/**
    \brief  Create and set a U_PMR_SERIALIZABLEOBJECT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Siepb       U_PSEUDO_OBJ containing a "Serialized image effects parameter block".  One of the ImageEffects objects.

    EMF+ manual 2.3.5.2, Microsoft name: EmfPlusSerializableObject Record, Index 0x38


    This sets an ImageEffect in the renderer, which will be applied to the next EmfPlusDrawImagePoints 
    record that is encountered.  The image effect is "consumed" by that EmfPlusDrawImagePoints record, resetting
    the renderer to its original state.\n  
    
    WARNING!  Windows XP Preview does not show filter effects, whether or not U_PPF_E is set.   They are visible if the EMF+
    file is inserted as an image into PowerPoint.

*/
U_PSEUDO_OBJ *U_PMR_SERIALIZABLEOBJECT_set(const U_PSEUDO_OBJ *Siepb){
   if(!Siepb){                                               return(NULL); }
   uint8_t *GUID = U_OID_To_GUID(Siepb->Type);
   if(!GUID){ return(NULL); }
   /* PO Used is size_t, might be 8 bytes, value in record must be 4 bytes */
   uint32_t Used = Siepb->Used;
   int Size = 16 + 4 + Siepb->Used;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SERIALIZABLEOBJECT,0,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,    ph->Used,	   1, U_XE},
      {GUID,        16, 	   1, U_XE},
      {&Used,       4,  	   1, U_LE},
      {Siepb->Data, Siepb->Used,   1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SERIALIZABLEOBJECT_OID, List);
   U_PO_free(&ph);
   free(GUID);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETANTIALIASMODE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  SMenum      SmoothingMode enumeration
    \param  aatype      Set: anti-aliasing on; Clear: anti-aliasing off
    

    EMF+ manual 2.3.6.1, Microsoft name: EmfPlusSetAntiAliasMode Record, Index 0x1E
*/
U_PSEUDO_OBJ *U_PMR_SETANTIALIASMODE_set(int SMenum, int aatype){
   int Size = 0;
   uint16_t utmp16 =  (aatype ? U_PPF_AA : 0) | (SMenum & U_FF_MASK_AA)<<U_FF_SHFT_AA;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETANTIALIASMODE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETANTIALIASMODE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETCOMPOSITINGMODE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  CMenum      CompositingMode enumeration
    

    EMF+ manual 2.3.6.2, Microsoft name: EmfPlusSetCompositingMode Record, Index 0x23
*/
U_PSEUDO_OBJ *U_PMR_SETCOMPOSITINGMODE_set(int CMenum){
   int Size = 0;
   uint16_t utmp16 =  (CMenum & U_FF_MASK_CM)<<U_FF_SHFT_CM;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETCOMPOSITINGMODE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETCOMPOSITINGMODE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETCOMPOSITINGQUALITY PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  CQenum      CompositingQuality enumeration
    

    EMF+ manual 2.3.6.3, Microsoft name: EmfPlusSetCompositingQuality Record, Index 0x24
*/
U_PSEUDO_OBJ *U_PMR_SETCOMPOSITINGQUALITY_set(int CQenum){
   int Size = 0;
   uint16_t utmp16 =  (CQenum & U_FF_MASK_CQ)<<U_FF_SHFT_CQ;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETCOMPOSITINGQUALITY,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETCOMPOSITINGQUALITY_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETINTERPOLATIONMODE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  IMenum      InterpolationMode enumeration
    

    EMF+ manual 2.3.6.4, Microsoft name: EmfPlusSetInterpolationMode Record, Index 0x21
*/
U_PSEUDO_OBJ *U_PMR_SETINTERPOLATIONMODE_set(int IMenum){
   int Size = 0;
   uint16_t utmp16 =  (IMenum & U_FF_MASK_IM)<<U_FF_SHFT_IM;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETINTERPOLATIONMODE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETINTERPOLATIONMODE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETPIXELOFFSETMODE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  POMenum     PixelOffsetMode enumeration
    

    EMF+ manual 2.3.6.5, Microsoft name: EmfPlusSetPixelOffsetMode Record, Index 0x22
*/
U_PSEUDO_OBJ *U_PMR_SETPIXELOFFSETMODE_set(int POMenum){
   int Size = 0;
   uint16_t utmp16 =  (POMenum & U_FF_MASK_PxOffM) << U_FF_SHFT_PxOffM;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETPIXELOFFSETMODE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETPIXELOFFSETMODE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETRENDERINGORIGIN PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  X            X coordinate of rendering origin.
    \param  Y            Y coordinate of rendering origin.
    

    EMF+ manual 2.3.6.6, Microsoft name: EmfPlusSetRenderingOrigin Record, Index 0x1D
*/
U_PSEUDO_OBJ *U_PMR_SETRENDERINGORIGIN_set(int32_t X, int32_t Y){
   int Size = 2*4;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETRENDERINGORIGIN,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&X,       4,        1, U_LE},
      {&Y,       4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETRENDERINGORIGIN_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETTEXTCONTRAST PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  TGC    Text Gamma correction value (x 1000).
    

    EMF+ manual 2.3.6.7, Microsoft name: EmfPlusSetTextContrast Record, Index 0x20
*/
U_PSEUDO_OBJ *U_PMR_SETTEXTCONTRAST_set(int TGC){
   int Size = 0;
   uint16_t utmp16 =  (TGC & U_FF_MASK_TGC) << U_FF_SHFT_TGC;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETTEXTCONTRAST,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETTEXTCONTRAST_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETTEXTRENDERINGHINT PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  TRHenum     TextRenderingHint enumeration
    

    EMF+ manual 2.3.6.8, Microsoft name: EmfPlusSetTextRenderingHint Record, Index 0x1F
*/
U_PSEUDO_OBJ *U_PMR_SETTEXTRENDERINGHINT_set(int TRHenum){
   int Size = 0;
   uint16_t utmp16 =  (TRHenum & U_FF_MASK_TRH) << U_FF_SHFT_TRH;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETTEXTRENDERINGHINT,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETTEXTRENDERINGHINT_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_BEGINCONTAINER PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  UTenum      UnitType enumeration
    \param  DstRect     a U_PSEUDO_OBJ  containing a U_PMF_RECTF object.  with SrcRect specifies a transformation
    \param  SrcRect     a U_PSEUDO_OBJ  containing a U_PMF_RECTF object.  with DstRect specifies a transformation
    \param  StackID     EMF+ Object Stack Index to use for this graphics container
    

    EMF+ manual 2.3.7.1, Microsoft name: EmfPlusBeginContainer Record, Index 0x27
*/
U_PSEUDO_OBJ *U_PMR_BEGINCONTAINER_set(int UTenum, U_PSEUDO_OBJ *DstRect, U_PSEUDO_OBJ *SrcRect, uint32_t StackID){
   if(UTenum < U_UT_World || UTenum > U_UT_Millimeter){ return(NULL); }
   if(!DstRect || (DstRect->Type != U_PMF_RECTF_OID)){  return(NULL); }
   if(!SrcRect || (SrcRect->Type != U_PMF_RECTF_OID)){  return(NULL); }
   int Size = DstRect->Used + SrcRect->Used + 4;
   uint16_t utmp16 =  (UTenum & U_FF_MASK_UT) << U_FF_SHFT_UT;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_BEGINCONTAINER,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,      ph->Used,      1, U_XE},
      {DstRect->Data, DstRect->Used, 1, U_XE},
      {SrcRect->Data, SrcRect->Used, 1, U_XE},
      {&StackID,      4,             1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_BEGINCONTAINER_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_BEGINCONTAINERNOPARAMS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  StackID     EMF+ Object Stack Index to use for this graphics container
    

    EMF+ manual 2.3.7.2, Microsoft name: EmfPlusBeginContainerNoParams Record, Index 0x28
*/
U_PSEUDO_OBJ *U_PMR_BEGINCONTAINERNOPARAMS_set(int StackID){
   int Size = 4;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_BEGINCONTAINERNOPARAMS,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&StackID, 4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_BEGINCONTAINERNOPARAMS_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_ENDCONTAINER PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  StackID     EMF+ Object Stack Index to use for this graphics container
    

    EMF+ manual 2.3.7.3, Microsoft name: EmfPlusEndContainer Record, Index 0x29
*/
U_PSEUDO_OBJ *U_PMR_ENDCONTAINER_set(int StackID){
   int Size = 4;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_ENDCONTAINER,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&StackID, 4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_ENDCONTAINER_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_RESTORE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  StackID     EMF+ Graphics State Stack to restore from. Must have been put on the GSS with a U_PMR_SAVE.
    

    EMF+ manual 2.3.7.4, Microsoft name: EmfPlusRestore Record, Index 0x26
*/
U_PSEUDO_OBJ *U_PMR_RESTORE_set(int StackID){
   int Size = 4;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_RESTORE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&StackID, 4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_RESTORE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SAVE PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  StackID     EMF+ Graphics State Stack to restore from. Must have been put on the GSS with a U_PMR_SAVE.
    

    EMF+ manual 2.3.7.5, Microsoft name: EmfPlusSave Record, Index 0x25
*/
U_PSEUDO_OBJ *U_PMR_SAVE_set(int StackID){
   int Size = 4;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SAVE,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&StackID, 4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SAVE_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETTSCLIP PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Rects       a U_PSEUDO_OBJ  containing an array of U_PMF_RECT or U_PMF_RECTF objects.
    

    EMF+ manual 2.3.8.1, Microsoft name: EmfPlusSetTSClip Record, Index 0x3A
*/
U_PSEUDO_OBJ *U_PMR_SETTSCLIP_set(U_PSEUDO_OBJ *Rects){
   int ctype;
   uint32_t Elements;
   if(Rects){
      if(     Rects->Type == (U_PMF_RECT_OID  | U_PMF_ARRAY_OID)){ ctype = 1;  Elements = (Rects->Used - 4)/8;  }
      else if(Rects->Type == (U_PMF_RECTF_OID | U_PMF_ARRAY_OID)){ ctype = 0;  Elements = (Rects->Used - 4)/16; }
      else {                                                       return(NULL); }
   }
   else {                                                          return(NULL); }
   int Size = Rects->Used; /* Rects includes Elements */
   uint16_t utmp16 =  (ctype ? U_PPF_K : 0) | (Elements & U_FF_MASK_TSC) << U_FF_SHFT_TSC;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETTSCLIP,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,    ph->Used,    1, U_XE},
      {Rects->Data, Rects->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETTSCLIP_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETTSGRAPHICS PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  vgatype     Set: Palette is VGA basic colors; Clear: Palette is ???
    \param  Tsg         A U_PMF_SETTSGRAPHICS object
    \param  Palette     (optional) a U_PSEUDO_OBJ containing a U_PMF_PALETTE object.
    

    EMF+ manual 2.3.8.2, Microsoft name: EmfPlusSetTSGraphics Record, Index 0x39
*/
U_PSEUDO_OBJ *U_PMR_SETTSGRAPHICS_set(int vgatype, U_PMF_SETTSGRAPHICS *Tsg, U_PSEUDO_OBJ *Palette){
   if(!Tsg){ return(NULL); }
   int Size = sizeof(U_PMF_SETTSGRAPHICS) + (Palette ? Palette->Used : 0);
   uint16_t utmp16 =  (vgatype ? U_PPF_VGA : 0) | (Palette ? U_PPF_PP : 0) ;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETTSGRAPHICS,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data,                         ph->Used,                     1, U_XE},
      {&(Tsg->AntiAliasMode),            1,                            1, U_XE},
      {&(Tsg->TextRenderHint),           1,                            1, U_XE},
      {&(Tsg->CompositingMode),          1,                            1, U_XE},
      {&(Tsg->CompositingQuality),       1,                            1, U_XE},
      {&(Tsg->RenderOriginX),            2,                            1, U_LE},
      {&(Tsg->RenderOriginY),            2,                            1, U_LE},
      {&(Tsg->TextContrast),             2,                            1, U_LE},
      {&(Tsg->FilterType),               1,                            1, U_XE},
      {&(Tsg->PixelOffset),              1,                            1, U_XE},
      {&(Tsg->WorldToDevice),            4,                            6, U_LE},
      {(Palette ? Palette->Data : NULL), (Palette ? Palette->Used: 0), 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETTSGRAPHICS_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_MULTIPLYWORLDTRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  Tm          a U_PSEUDO_OBJ containing a U_PMF_TRANSFORMMATRIX. (Transformation matrix)
    

    EMF+ manual 2.3.9.1, Microsoft name: EmfPlusMultiplyWorldTransform Record, Index 0x2C
*/
U_PSEUDO_OBJ *U_PMR_MULTIPLYWORLDTRANSFORM_set(int xmtype, U_PSEUDO_OBJ *Tm){
   if(!Tm || (Tm->Type != U_PMF_TRANSFORMMATRIX_OID)){ return(NULL); }
   int Size = Tm->Used;
   uint16_t utmp16 =  (xmtype ? U_PPF_XM : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_MULTIPLYWORLDTRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {Tm->Data, Tm->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_MULTIPLYWORLDTRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_RESETWORLDTRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    

    EMF+ manual 2.3.9.2, Microsoft name: EmfPlusResetWorldTransform Record, Index 0x2B
*/
U_PSEUDO_OBJ *U_PMR_RESETWORLDTRANSFORM_set(void){
   int Size = 0;
   uint16_t utmp16 =  0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_RESETWORLDTRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_RESETWORLDTRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_ROTATEWORLDTRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  Angle       Rotation angle, in degrees
    

    EMF+ manual 2.3.9.3, Microsoft name: EmfPlusRotateWorldTransform Record, Index 0x2F
*/
U_PSEUDO_OBJ *U_PMR_ROTATEWORLDTRANSFORM_set(int xmtype, U_FLOAT Angle){
   int Size = 4;
   uint16_t utmp16 =  (xmtype ? U_PPF_XM : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_ROTATEWORLDTRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&Angle,   4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_ROTATEWORLDTRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SCALEWORLDTRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  X           Scale in X
    \param  Y           Scale in Y

    EMF+ manual 2.3.9.4, Microsoft name: EmfPlusScaleWorldTransform Record, Index 0x2E
*/
U_PSEUDO_OBJ *U_PMR_SCALEWORLDTRANSFORM_set(int xmtype, U_FLOAT X, U_FLOAT Y){
   int Size = 2*4;
   uint16_t utmp16 =  (xmtype ? U_PPF_XM : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SCALEWORLDTRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&X,       4,        1, U_LE},
      {&Y,       4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SCALEWORLDTRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETPAGETRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  PUenum   Page Unit, in UnitType enumeration
    \param  Scale    Scale factor to convert page space to device space
    
    

    EMF+ manual 2.3.9.5, Microsoft name: EmfPlusSetPageTransform Record, Index 0x30

    Defines Page Space -> Device Space transformation
*/
U_PSEUDO_OBJ *U_PMR_SETPAGETRANSFORM_set(int PUenum, U_FLOAT Scale){
   int Size = 4;
   uint16_t utmp16 =  (PUenum & U_FF_MASK_PU) << U_FF_SHFT_PU;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETPAGETRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&Scale,    4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETPAGETRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_SETWORLDTRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  Tm          a U_PSEUDO_OBJ containing a U_PMF_TRANSFORMMATRIX. (Transformation matrix)
    
    

    EMF+ manual 2.3.9.6, Microsoft name: EmfPlusSetWorldTransform Record, Index 0x2A

    Defines  World Space -> Page Space transformation
*/
U_PSEUDO_OBJ *U_PMR_SETWORLDTRANSFORM_set(U_PSEUDO_OBJ *Tm){
   if(!Tm || (Tm->Type != U_PMF_TRANSFORMMATRIX_OID)){ return(NULL); }
   int Size = Tm->Used;
   uint16_t utmp16 = 0;
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_SETWORLDTRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {Tm->Data, Tm->Used, 1, U_XE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_SETWORLDTRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/**
    \brief  Create and set a U_PMR_TRANSLATEWORLDTRANSFORM PseudoObject
    \return Pointer to PseudoObject, NULL on error
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  Dx          X offset
    \param  Dy          Y offset
    

    EMF+ manual 2.3.9.7, Microsoft name: EmfPlusTranslateWorldTransform Record, Index 0x2D
*/
U_PSEUDO_OBJ *U_PMR_TRANSLATEWORLDTRANSFORM_set(int xmtype, U_FLOAT Dx, U_FLOAT Dy){
   int Size = 2*4;
   uint16_t utmp16 =  (xmtype ? U_PPF_XM : 0);
   U_PSEUDO_OBJ *ph = U_PMR_CMN_HDR_set(U_PMR_TRANSLATEWORLDTRANSFORM,utmp16,Size);
   const U_SERIAL_DESC List[] = {
      {ph->Data, ph->Used, 1, U_XE},
      {&Dx,      4,        1, U_LE},
      {&Dy,      4,        1, U_LE},
      {NULL,0,0,U_XX}
   };
   U_PSEUDO_OBJ *po = U_PMF_SERIAL_set(U_PMR_TRANSLATEWORLDTRANSFORM_OID, List);
   U_PO_free(&ph);
   return(po);
}

/*

   end of U_PMF_*_set() functions 
   =====================================================================================
   start of U_PMF_*_get() functions 

      These functions all take a blimit value so that they can check if the data description in the fields
      they process extend beyond the end of the record.

*/


//! \cond
/* core _get functions, not accessed outside of this routine */

/* get copies of up to 0-6 consecutive 4 byte values and a pointer to the rest */
int U_PMF_CORE1_get(const char *contents, void *v1, void *v2, void *v3, void *v4, void *v5, void *v6, const char **vR){
    if(v1){                U_PMF_MEMCPY_SRCSHIFT(v1, &contents, 4);
       if(v2){             U_PMF_MEMCPY_SRCSHIFT(v2, &contents, 4);
          if(v3){          U_PMF_MEMCPY_SRCSHIFT(v3, &contents, 4);
             if(v4){       U_PMF_MEMCPY_SRCSHIFT(v4, &contents, 4);
                if(v5){    U_PMF_MEMCPY_SRCSHIFT(v5, &contents, 4);
                   if(v6){ U_PMF_MEMCPY_SRCSHIFT(v6, &contents, 4); }}}}}}
    if(vR){ *vR = contents; }
    return(1);
}
//! \endcond


/**
    \brief Get data from a U_PMF_BRUSH object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  Type       BrushType Enumeration
    \param  Data       one of the 5 types of Brush data
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.1.1, Microsoft name: EmfPlusBrush Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_BRUSH_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit){
    if(!contents || !Version || !Type || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents,sizeof(U_PMF_BRUSH), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Type,    4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_CUSTOMLINECAP object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  Type       CustomLineCapData Enumeration
    \param  Data       one of the 2 types of Linecap data
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.1.2, Microsoft name: EmfPlusCustomLineCap Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_CUSTOMLINECAP_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit){
    if(!contents || !Version || !Type || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMLINECAP), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Type,    4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_FONT object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  EmSize     em size in units of SizeUnit
    \param  SizeUnit   UnitType enumeration
    \param  FSFlags    FontStyle flags
    \param  Length     Number of Unicode Characters in FamilyName
    \param  Data       Unicode (UTF-16LE) name of font family

    EMF+ manual 2.2.1.3, Microsoft name: EmfPlusFont Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_FONT_get(const char *contents, uint32_t *Version, U_FLOAT *EmSize, uint32_t *SizeUnit,
      int32_t *FSFlags, uint32_t *Length, const char **Data, const char *blimit){
    if(!contents || !Version || !EmSize || !SizeUnit || !FSFlags || !Length || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FONT), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version,  4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, EmSize,   4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, SizeUnit, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, FSFlags,  4, 1, U_LE);
    contents += 4; /* Reserved field, which is ignored */
    U_PMF_SERIAL_get(&contents, Length,   4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}


/**
    \brief Get data from a U_PMF_IMAGE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  Type       ImageDataType Enumeration
    \param  Data       one of the 2 types of image data

    EMF+ manual 2.2.1.4, Microsoft name: EmfPlusImage Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_IMAGE_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit){
    if(!contents || !Version || !Type || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IMAGE), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Type,    4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_IMAGEATTRIBUTES object
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Version     EmfPlusGraphicsVersion object
    \param  WrapMode    WrapMode object
    \param  ClampColor  EmfPlusARGB object
    \param  ObjectClamp ObjectClamp Identifiers

    EMF+ manual 2.2.1.5, Microsoft name: EmfPlusImageAttributes Object
*/
int U_PMF_IMAGEATTRIBUTES_get(const char *contents, uint32_t *Version, uint32_t *WrapMode, uint32_t *ClampColor,
      uint32_t *ObjectClamp, const char *blimit){
    if(!contents || !Version || !WrapMode || !ClampColor || !ObjectClamp || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IMAGEATTRIBUTES), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version,    4, 1, U_LE);
    contents += 4; /* Skip Reserved 1*/
    U_PMF_SERIAL_get(&contents, WrapMode,   4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, ClampColor, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, ObjectClamp,4, 1, U_LE);
    /* Skip Reserved 2*/
    return(1);
}

/**
    \brief Get data from a U_PMF_PATH object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  Count      Number of points and point types in this object
    \param  Flags      PathPoint Flags
    \param  Points     array of points type PMFPointR, PMFPoint, or PMFPointF
    \param  Types      array of U_PMF_PATHPOINTTYPERLE and/or U_PMF_PATHPOINTTYPE

    EMF+ manual 2.2.1.6, Microsoft name: EmfPlusPath Object

    Caller must check Types for possible memory access violations if type can be U_PMF_PATHPOINTTYPERLE.
*/
int U_PMF_PATH_get(const char *contents, uint32_t *Version, uint32_t *Count, uint16_t *Flags, 
      const char **Points, const char **Types, const char *blimit){
    if(!contents || !Version || !Count || !Flags || !Points || !Types || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PATH), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version,    4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Count,      4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Flags,      2, 1, U_LE);
    contents+=2; /* reserved */
    uint32_t sizeP, sizeT;
    if(*Flags      & U_PPF_P){ 
       sizeP  = U_PMF_LEN_REL715(contents,*Count); //DEBUG
       printf("DEBUG U_PMF_PATH_get count:%d LENREL715:%d\n",*Count,sizeP);fflush(stdout);
    }
    else if(*Flags & U_PPF_C){ sizeP = *Count * sizeof(U_PMF_POINT);   }
    else {                     sizeP = *Count * sizeof(U_PMF_POINTF);  }
    if(IS_MEM_UNSAFE(contents, sizeP, blimit))return(0);
    U_PMF_PTRSAV_SHIFT(Points, &contents, 0);
    contents += sizeP;
    /* this limit is correct if there are only U_PMF_PATHPOINTTYPE PointTypes, it is a lower bound if
       there can also be U_PMF_PATHPOINTTYPERLE */
    sizeT = *Count * sizeof(U_PMF_PATHPOINTTYPE);
    if(IS_MEM_UNSAFE(contents, sizeT, blimit))return(0);
    U_PMF_PTRSAV_SHIFT(Types, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_PEN object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  Type       must be zero
    \param  PenData    Pen description
    \param  Brush      Brush Description 

    EMF+ manual 2.2.1.7, Microsoft name: EmfPlusPen Object

    Caller must check Brush and PenData for possible memory access violations.
*/
int U_PMF_PEN_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **PenData, const char **Brush, const char *blimit){
    if(!contents || !Type || !PenData || !Brush || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PEN), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Type,    4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(PenData, &contents, 0);
    *Brush = *PenData + U_PMF_LEN_PENDATA(*PenData);
    return(1);
}
    
/**
    \brief Get data from a U_PMF_REGION object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Version    EmfPlusGraphicsVersion object
    \param  Count      Number of CHILD nodes.  This is one less than the total number of U_PMF_REGIONNODE objects in Nodes.
    \param  Nodes      Nodes defining region

    EMF+ manual 2.2.1.8, Microsoft name: EmfPlusRegion Object
*/
int U_PMF_REGION_get(const char *contents, uint32_t *Version, uint32_t *Count, const char **Nodes, const char *blimit){
    if(!contents || !Version || !Count || !Nodes || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_REGION), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Count,   4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Nodes, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_STRINGFORMAT object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Sfs        pointer to U_PMF_STRINGFORMAT structure, with no variable part
    \param  Data       pointer to variable part

    EMF+ manual 2.2.1.9, Microsoft name: EmfPlusStringFormat Object
*/
int U_PMF_STRINGFORMAT_get(const char *contents, U_PMF_STRINGFORMAT *Sfs, const char **Data, const char *blimit){
    if(!contents || !Sfs || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_STRINGFORMAT), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Sfs, 4, 15, U_LE);
    *Data = contents;
    return(1);
}

/**
    \brief Get data from a U_PMF_ARGB object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Blue       Blue  color (0-255)
    \param  Green      Green color (0-255)
    \param  Red        Red   color (0-255)
    \param  Alpha      Alpha       (0-255)

    EMF+ manual 2.2.2.1, Microsoft name: EmfPlusARGB Object
*/
int U_PMF_ARGB_get(const char *contents, uint8_t *Blue, uint8_t *Green, uint8_t *Red, uint8_t *Alpha, const char *blimit){
    if(!contents || !Blue || !Green || !Red || !Alpha || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_ARGB), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Blue,  1, 1, U_XE);
    U_PMF_SERIAL_get(&contents, Green, 1, 1, U_XE);
    U_PMF_SERIAL_get(&contents, Red,   1, 1, U_XE);
    U_PMF_SERIAL_get(&contents, Alpha, 1, 1, U_XE);
    return(1);
}

/**
    \brief Get data from a U_PMF_BITMAP object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Bs         pointer to U_PMF_BITMAP structure, with no variable part
    \param  Data       pointer to variable part

    EMF+ manual 2.2.2.2, Microsoft name: EmfPlusBitmap Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_BITMAP_get(const char *contents, U_PMF_BITMAP *Bs, const char **Data, const char *blimit){
    if(!contents || !Bs || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BITMAP), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Bs, 4, 5, U_LE); // width, height, stride, pixelformat, type
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0); // bitmapdata
    return(1);
}

/**
    \brief Get data from a U_PMF_BITMAPDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Ps         pointer to U_PMF_PALETTE structure, with no variable part
    \param  Colors     Color part of U_PMF_PALETTE object
    \param  Data       An array of bytes, meaning depends on fields in U_PMF_BITMAP object and the PixelFormat enumeration.

    EMF+ manual 2.2.2.3, Microsoft name: EmfPlusBitmapData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_BITMAPDATA_get(const char *contents, U_PMF_PALETTE *Ps, const char **Colors, const char **Data, const char *blimit){
    if(!contents || !Ps || !Colors || !Data  || !blimit){ return(0); }
    /* this structure is entirely optional */
    if(IS_MEM_UNSAFE(contents, 4*2, blimit))return(0);
    U_PMF_SERIAL_get(&contents, Ps, 4, 2, U_LE);
    U_PMF_PTRSAV_SHIFT(Colors, &contents, Ps->Elements * sizeof(U_PMF_ARGB));
    /* difficult to know how big the actual bitmap will be, just return the pointer to it untested */
    U_PMF_PTRSAV_SHIFT(Data,  &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_BLENDCOLORS object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Elements   Number of members in Positions and Colors
    \param  Positions  Caller must free.  Pointer to memory holding positions along gradient line.  
    \param  Colors     Caller must NOT free memory, Pointer to memory holding colors at positions on gradient line.  

    EMF+ manual 2.2.2.4, Microsoft name: EmfPlusBlendColors Object
*/
int U_PMF_BLENDCOLORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, const char **Colors, const char *blimit){
    if(!contents || !Positions || !Colors || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BLENDCOLORS), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
    if(IS_MEM_UNSAFE(contents, *Elements * 4, blimit))return(0);
    if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Positions, 4, *Elements, U_LE,1)){ return(0); }
    U_PMF_PTRSAV_SHIFT(Colors,  &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_BLENDFACTORS object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Elements   members in each array
    \param  Positions  Caller must free.  Pointer to memory holding positions along gradient line.  
    \param  Factors    Caller must free.  Pointer to memory holding blending factors, 0.0->1.0 values, inclusive along gradient line.  

    EMF+ manual 2.2.2.5, Microsoft name: EmfPlusBlendFactors Object
*/
int U_PMF_BLENDFACTORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, U_FLOAT **Factors, const char *blimit){
    if(!contents || !Elements || !Positions || !Factors || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BLENDFACTORS), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
    if(IS_MEM_UNSAFE(contents, *Elements * 4 * 2, blimit))return(0);
    if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Positions,  4, *Elements, U_LE, 1)){ return(0); }
    if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Factors,    4, *Elements, U_LE, 1)){
       free(*Positions);
       return(0);
    }
    return(1);
}

/**
    \brief Get data from a U_PMF_BOUNDARYPATHDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Size       bytes in Data
    \param  Data       boundary of the brush

    EMF+ manual 2.2.2.6, Microsoft name: EmfPlusBoundaryPathData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_BOUNDARYPATHDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Size || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BOUNDARYPATHDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data,  &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_BOUNDARYPOINTDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Elements   Members in Points
    \param  Points     Caller must free.  Pointer to memory holding points along gradient line. Boundary of the brush.

    EMF+ manual 2.2.2.7, Microsoft name: EmfPlusBoundaryPointData Object
*/
int U_PMF_BOUNDARYPOINTDATA_get(const char *contents, int32_t *Elements, U_PMF_POINTF **Points, const char *blimit){
    if(!contents || !Elements || !Points || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BOUNDARYPOINTDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
    if(IS_MEM_UNSAFE(contents, *Elements * 2, blimit))return(0);
    if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Points, 4, *Elements * 2, U_LE, 1)){ return(0); }
    return(1);
}

/**
    \brief Get data from a U_PMF_CHARACTERRANGE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  First      First position in range
    \param  Length     Range length

    EMF+ manual 2.2.2.8, Microsoft name: EmfPlusCharacterRange Object
*/
int U_PMF_CHARACTERRANGE_get(const char *contents, int32_t *First, int32_t *Length, const char *blimit){
    if(!contents || !First || !Length || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CHARACTERRANGE), blimit))return(0);
    U_PMF_SERIAL_get(&contents, First,  4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Length, 4, 1, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_COMPOUNDLINEDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Elements   Members in the array
    \param  Widths     Caller must free.  Pointer to memory holding Line or gap widths (0.0 <-> 1.0, fraction of total line width ).  

    EMF+ manual 2.2.2.9, Microsoft name: EmfPlusCompoundLineData Object
*/
int U_PMF_COMPOUNDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Widths, const char *blimit){
    if(!contents || !Elements || !Widths || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_COMPOUNDLINEDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
    if(IS_MEM_UNSAFE(contents, *Elements * sizeof(U_FLOAT), blimit))return(0);
    *Widths = (U_FLOAT *)malloc(*Elements * sizeof(U_FLOAT));
    if(!*Widths){ return(0); }
    U_PMF_SERIAL_get(&contents, *Widths, 4, *Elements, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_COMPRESSEDIMAGE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Data       Stored image in one of the supported formats.

    EMF+ manual 2.2.2.10, Microsoft name: EmfPlusCompressedImage Object
    

    This function does not do anything useful, but it is included so that all objects have a corresponding _get().

    Caller must check Data for possible memory access violations.
*/
int U_PMF_COMPRESSEDIMAGE_get(const char *contents, const char **Data, const char *blimit){
    if(!contents || !Data || !blimit){ return(0); }
    if(contents >= blimit)return(0);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_CUSTOMENDCAPDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Size       Bytes in Data
    \param  Data       Description of linecap

    EMF+ manual 2.2.2.11, Microsoft name: EmfPlusCustomEndCapData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_CUSTOMENDCAPDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Size || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMENDCAPDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_CUSTOMLINECAPARROWDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Ccad       pointer to U_PMF_CUSTOMLINECAPARROWDATA structure

    EMF+ manual 2.2.2.12, Microsoft name: EmfPlusCustomLineCapArrowData Object
*/
int U_PMF_CUSTOMLINECAPARROWDATA_get(const char *contents, U_PMF_CUSTOMLINECAPARROWDATA *Ccad, const char *blimit){
    if(!contents || !Ccad || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMLINECAPARROWDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Ccad, 4, 13, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_CUSTOMLINECAPDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Clcd       pointer to U_PMF_CUSTOMLINECAPDATA structure, with no variable part
    \param  Data       variable part of U_PMF_CUSTOMLINECAPDATA

    EMF+ manual 2.2.2.13, Microsoft name: EmfPlusCustomLineCapData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_CUSTOMLINECAPDATA_get(const char *contents, U_PMF_CUSTOMLINECAPDATA *Clcd, const char **Data, const char *blimit){
    if(!contents || !Clcd || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMLINECAPDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Clcd, 4, 12, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_CUSTOMLINECAPOPTIONALDATA object
    \return on success 3,5, or 7 (for varying combinations of data present) or 1 (no data is present), 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      bits set to indicate the presence of FillData and/or LineData
    \param  FillData   Path to fill (optional)
    \param  LineData   Path to stroke (optional)

    EMF+ manual 2.2.2.14, Microsoft name: EmfPlusCustomLineCapOptionalData Object

    Caller must check LineData for possible memory access violations.
*/
int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, const char **FillData, const char **LineData, const char *blimit){
    uint32_t length;
    int status = 1;
    if(!contents || !*FillData || !*LineData || !blimit){ return(0); }
    /* this structure is entirely optional */
    if(Flags & U_CLCD_FillPath){
       if(!FillData){ return(0); }
       if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
       U_PMF_SERIAL_get(&contents, &length, 4, 1, U_LE);
       if(IS_MEM_UNSAFE(contents, length, blimit))return(0);
       contents -= 4;  /* undo the unneeded shift from preceding */
       U_PMF_PTRSAV_SHIFT(FillData, &contents, 4 + length);
       status += 2; 
    }
    else { *FillData = NULL; }

    if(Flags & U_CLCD_LinePath){
       if(!LineData){ return(0); }
       if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
       U_PMF_SERIAL_get(&contents, &length, 4, 1, U_LE);
       if(IS_MEM_UNSAFE(contents, length, blimit))return(0);
       contents -= 4;  /* undo the unneeded shift from preceding */
       U_PMF_PTRSAV_SHIFT(LineData, &contents, 0);
       status += 4;
    }
    else { *LineData = NULL; }
    return(status);
}

/**
    \brief Get data from a U_PMF_CUSTOMSTARTCAPDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Size       Bytes in Data
    \param  Data       Description of linecap

    EMF+ manual 2.2.2.15, Microsoft name: EmfPlusCustomStartCapData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_CUSTOMSTARTCAPDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Size || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMSTARTCAPDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_DASHEDLINEDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Elements   Members in the array
    \param  Lengths    Caller must free.  Pointer to memory holding lengths of dashes and spaces.

    EMF+ manual 2.2.2.16, Microsoft name: EmfPlusDashedLineData Object
*/
int U_PMF_DASHEDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Lengths, const char *blimit){
    if(!contents || !Elements || !Lengths || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_DASHEDLINEDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
    if(IS_MEM_UNSAFE(contents, *Elements * sizeof(U_FLOAT), blimit))return(0);
    *Lengths = (U_FLOAT *)malloc(*Elements * sizeof(U_FLOAT));
    if(!*Lengths){ return(0); }
    U_PMF_SERIAL_get(&contents, *Lengths, 4, *Elements, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_FILLPATHOBJ object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Size       Bytes in Data
    \param  Data       Path specification

    EMF+ manual 2.2.2.17, Microsoft name: EmfPlusFillPath Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_FILLPATHOBJ_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Size || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FILLPATHO), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_FOCUSSCALEDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Count      must be 2
    \param  ScaleX     value 0.0 <-> 1.0
    \param  ScaleY     value 0.0 <-> 1.0

    EMF+ manual 2.2.2.18, Microsoft name: EmfPlusFocusScaleData Object
*/
int U_PMF_FOCUSSCALEDATA_get(const char *contents, uint32_t *Count, U_FLOAT *ScaleX, U_FLOAT *ScaleY, const char *blimit){
    if(!contents || !Count || !ScaleX || !ScaleY || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FOCUSSCALEDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Count,  4, 1, U_LE);
    if(*Count != 2){ return(0); }
    U_PMF_SERIAL_get(&contents, ScaleX, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, ScaleY, 4, 1, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_GRAPHICSVERSION object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Signature  Must be U_GFVR_PMF (0xDBC01)
    \param  GrfVersion GraphicsVersion enumeration

    EMF+ manual 2.2.2.19, Microsoft name: EmfPlusGraphicsVersion Object
*/
int U_PMF_GRAPHICSVERSION_get(const char *contents, int *Signature, int *GrfVersion, const char *blimit){
    uint32_t tmp;
    if(!contents || !Signature || !GrfVersion || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_GRAPHICSVERSION), blimit))return(0);
    memcpy(&tmp, contents, 4);
    *Signature  = tmp >> 12;
    *GrfVersion = tmp & U_GFVR_MASKLO;
    return(1);
}

/**
    \brief Get data from a U_PMF_HATCHBRUSHDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Style      HatchStyle enumeration
    \param  Foreground Hatch pattern line color
    \param  Background Hatch pattern bkground color

    EMF+ manual 2.2.2.20, Microsoft name: EmfPlusHatchBrushData Object
*/
int U_PMF_HATCHBRUSHDATA_get(const char *contents, uint32_t *Style, U_PMF_ARGB *Foreground, U_PMF_ARGB *Background, const char *blimit){
    if(!contents || !Style || !Foreground || !Background || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_HATCHBRUSHDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Style,      4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Foreground, 4, 1, U_XE);
    U_PMF_SERIAL_get(&contents, Background, 4, 1, U_XE);
    return(1);
}

/**
    \brief Get data from a U_PMF_INTEGER7 object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Value      7 bit signed integer (stored in an integer)
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.21, Microsoft name: EmfPlusInteger7 Object
*/
int U_PMF_INTEGER7_get(const char **contents, U_FLOAT *Value, const char *blimit){
    uint8_t tmp;
    if(!contents || !*contents || !Value || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(*contents, 1, blimit))return(0); /* past end of buffer */
    if(**contents & U_TEST_INT7)return(0);  /* this bit must be 0 in this object type */
    U_PMF_SERIAL_get(contents, &tmp, 1, 1, U_XE);
    if(tmp & U_SIGN_INT7){
       tmp |= U_TEST_INT7;  /* now it has the bit pattern of a signed int8_t */
       *Value = *(int8_t *)&tmp;
    }
    else {
       *Value = tmp;
    }
    return(1);
}

/**
    \brief Get data from a U_PMF_INTEGER15 object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Value      15 bit signed integer (stored in an integer)
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.22, Microsoft name: EmfPlusInteger15 Object
*/
int U_PMF_INTEGER15_get(const char **contents, U_FLOAT *Value, const char *blimit){
    if(!contents || !*contents || !Value || !blimit){ return(0); }
    uint16_t tmp;
    if(IS_MEM_UNSAFE(*contents, 2, blimit))return(0); /* past end of buffer */
    if(!(**contents & U_TEST_INT7))return(0);  /* this bit must be 1 in this object type */
    U_PMF_SERIAL_get(contents, &tmp, 2, 1, U_BE);
    tmp &= U_MASK_INT15;   /* drop the 7/15 flag from the most significant bit */
    if(tmp & U_SIGN_INT15){
       tmp |= U_TEST_INT15;  /* now it has the bit pattern of a signed int16_t */
       *Value = *(int16_t *)&tmp;
    }
    else {
       *Value = tmp;
    }
    return(1);
}

/**
    \brief Get data from a U_PMF_LANGUAGEIDENTIFIER object
    \return 1 on success, 0 on error
    \param  LId        U_PMF_LANGUAGEIDENTIFIER from which to extract data
    \param  SubLId     Example: code for USA
    \param  PriLId     Example: code for English
    


    EMF+ manual 2.2.2.23, Microsoft name: EmfPlusLanguageIdentifier Object

    This type is defined as 16 bits in the manual section, but it is only ever used as part of a 32 bit field!
*/
int U_PMF_LANGUAGEIDENTIFIER_get(U_PMF_LANGUAGEIDENTIFIER LId, int *SubLId, int *PriLId){
    if(!SubLId || !PriLId){ return(0); }
    *SubLId = (LId >> U_FF_SHFT_SUBLID ) & U_FF_MASK_SUBLID;         
    *PriLId = (LId >> U_FF_SHFT_PRILID ) & U_FF_MASK_PRILID;
    /* 16 bits above that are not used */
    return(1);
}

/**
    \brief Get data from a U_PMF_LINEARGRADIENTBRUSHDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Lgbd       U_PMF_LINEARGRADIENTBRUSHDATA structure, with no variable part
    \param  Data       variable part of U_PMF_LINEARGRADIENTBRUSHDATA
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.24, Microsoft name: EmfPlusLinearGradientBrushData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_LINEARGRADIENTBRUSHDATA_get(const char *contents, U_PMF_LINEARGRADIENTBRUSHDATA *Lgbd, const char **Data, const char *blimit){
    if(!contents || !Lgbd || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_LINEARGRADIENTBRUSHDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Lgbd,		      4, 6, U_LE); /* Flags, WrapMode, RectF*/
    U_PMF_SERIAL_get(&contents, &(Lgbd->StartColor),  4, 4, U_XE); /* StartColor, EndColor, Reserved1 & 2 */
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      BrushData flags - indicates which of the following date fields are present.
    \param  Tm         Transformation matrix
    \param  Bc         U_PMF_BLENDCOLORS object or NULL
    \param  BfH        U_PMF_BLENDFACTORS (H) object or NULL
    \param  BfV        U_PMF_BLENDFACTORS (V) object or NULL (WARNING, GDI+ defines this field but does not render it.  DO NOT USE.)
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.25, Microsoft name: EmfPlusLinearGradientBrushOptionalData Object

*/
int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Tm, 
      const char **Bc, const char **BfH, const char **BfV, const char *blimit){
    uint32_t Elements;
    if(!contents || !Tm|| !Bc || !BfH || !BfV || !blimit){ return(0); }
    /* all of the fields are optional! */
    *Bc = *BfH = *BfV = NULL;
    if(Flags & U_BD_Transform){
       if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_ROTMATRIX), blimit))return(0);
       U_PMF_SERIAL_get(&contents, Tm, 4, 6, U_LE);
    }
    if(Flags & U_BD_PresetColors){
       if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
       U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE);  /* starts with a 4 byte count*/
       if(IS_MEM_UNSAFE(contents, Elements * ( sizeof(U_FLOAT) + sizeof(U_PMF_ARGB)), blimit))return(0);
       contents-=4; /* back up to the front of the count, as it is part of the data field */
       U_PMF_PTRSAV_SHIFT(Bc, &contents, 0);
    }
    else if(Flags & U_BD_BlendFactorsH){
       U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE);  /* starts with a 4 byte count*/
       if(IS_MEM_UNSAFE(contents, Elements * 2 * sizeof(U_FLOAT), blimit))return(0);
       contents-=4; /* back up to the front of the count, as it is part of the data field */
       U_PMF_PTRSAV_SHIFT(BfH, &contents, 4 + (Elements * 2 * sizeof(U_FLOAT))); /* 4 byte count + 2 * 4bytes * Elements */
       if(Flags & U_BD_BlendFactorsV){
          if(IS_MEM_UNSAFE(contents, Elements * 2 * sizeof(U_FLOAT), blimit))return(0);
          U_PMF_PTRSAV_SHIFT(BfV, &contents, 0);
       }
    }
    else if(Flags & U_BD_BlendFactorsV){
       U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE);  /* starts with a 4 byte count*/
       if(IS_MEM_UNSAFE(contents, Elements * 2 * sizeof(U_FLOAT), blimit))return(0);
       contents-=4; /* back up to the front of the count, as it is part of the data field */
       U_PMF_PTRSAV_SHIFT(BfV, &contents, 0);
    }
    return(1);
}

/**
    \brief Get data from a U_PMF_LINEPATH object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Size       Bytes in Data
    \param  Data       Outline path
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.26, Microsoft name: EmfPlusLinePath Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_LINEPATH_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Size  || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_LINEPATH), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_METAFILE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Type       
    \param  Size       Bytes in Data
    \param  Data       Various types of data, like an EMF metafile, WMF metafile, another EMF+ metafile
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.27, Microsoft name: EmfPlusMetafile Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_METAFILE_get(const char *contents, uint32_t *Type, uint32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Type  || !Size  || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_METAFILE), blimit))return(0);
    U_PMF_SERIAL_get(&contents, &Type, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, &Size, 4, 1, U_LE);
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_PALETTE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      PaletteStyle flags
    \param  Elements   Members in the array
    \param  Colors     Palette data (array of colors)
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.28, Microsoft name: EmfPlusPalette Object
*/
int U_PMF_PALETTE_get(const char *contents, uint32_t *Flags, uint32_t *Elements, const char **Colors, const char *blimit){
    if(!contents || !Flags  || !Elements  || !Colors || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PALETTE), blimit))return(0);
    U_PMF_SERIAL_get(&contents, &Flags,    4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE);
    if(IS_MEM_UNSAFE(contents, *Elements*sizeof(U_RGBQUAD), blimit))return(0);
    U_PMF_PTRSAV_SHIFT(Colors, &contents, 0);
    return(1);
    
}

/**
    \brief Get data from a U_PMF_PATHGRADIENTBRUSHDATA object
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Pgbd         constant part of U_PMF_PATHGRADIENTBRUSHDATA object
    \param  Gradient     variable part of U_PMF_LINEARGRADIENTBRUSHDATA, Color Gradient with Elements members
    \param  Boundary     variable part of U_PMF_LINEARGRADIENTBRUSHDATA, U_PMF_BOUNDARYPATHDATA object if BrushDataPath bit set in Flag, else U_PMF_BOUNDARYPOINTDATA object
    \param  Data         variable part of U_PMF_LINEARGRADIENTBRUSHDATA, exact composition depends on Flags
    \param  blimit       one byte past the end of data

    EMF+ manual 2.2.2.29, Microsoft name: EmfPlusPathGradientBrushData Object

    Caller must check Data for possible memory access violations.

*/
int U_PMF_PATHGRADIENTBRUSHDATA_get(const char *contents, U_PMF_PATHGRADIENTBRUSHDATA *Pgbd, const char **Gradient, 
      const char **Boundary, const char **Data, const char *blimit){
    if(!contents || !Pgbd || !Gradient || !Boundary || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PATHGRADIENTBRUSHDATA), blimit))return(0);
    uint32_t Size;
    U_PMF_SERIAL_get(&contents,  Pgbd,  	       4, 2, U_LE); /* Flags and WrapMode*/
    U_PMF_SERIAL_get(&contents,  &(Pgbd->CenterColor), 4, 1, U_XE);
    U_PMF_SERIAL_get(&contents,  &(Pgbd->Center),      4, 3, U_LE); /* Center and Elements */
    if(IS_MEM_UNSAFE(contents, Pgbd->Elements * sizeof(U_PMF_ARGB), blimit))return(0);
    U_PMF_PTRSAV_SHIFT(Gradient, &contents, Pgbd->Elements * sizeof(U_PMF_ARGB));
    U_PMF_PTRSAV_SHIFT(Boundary, &contents, 0);
    U_PMF_SERIAL_get(&contents,  &Size,                4, 1, U_LE); /* The first 4 bytes of the Boundary are always a size */
    if(Pgbd->Flags & U_BD_Path){ contents += Size;                   } // U_PMF_BOUNDARYPATHDATA
    else {                       contents += Size*2*sizeof(U_FLOAT); } // U_PMF_BOUNDARYPOINTDATA
    if(Pgbd->Flags & (U_BD_Transform |U_BD_PresetColors | U_BD_BlendFactorsH| U_BD_FocusScales)){ // optional data present
      if(contents >= blimit)return(0); // the size is variable but this must still hold
      U_PMF_PTRSAV_SHIFT(Data,     &contents, 0);
    }
    else { *Data = NULL; } // no optional data present
    return(1);
}

/**
    \brief Get data from a U_PMF_PATHGRADIENTBRUSHOPTIONALDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      bits set to indicate the presence of FillData and/or LineData
    \param  Matrix     Transformation matrix
    \param  Pattern    Blend Pattern
    \param  Data       Focus scales for the brush
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.30, Microsoft name: EmfPlusPathGradientBrushOptionalData Object
*/
int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Matrix, 
      const char **Pattern, const char **Data, const char *blimit){
    int varsize;
    if(!contents || !Flags || !Matrix || !Pattern || !Data || !blimit){ return(0); }
    /* this structure is entirely optional */
    if(Flags & U_BD_Transform){
       if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TRANSFORMMATRIX), blimit))return(0);
       U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE);
    }
    if(Flags &  (U_BD_PresetColors | U_BD_BlendFactorsH)){
       if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
       uint32_t Elements; 
       U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE);
       contents -= 4;
       varsize=(Elements * 4 * (Flags & U_BD_BlendFactorsH ? 2 :1));
       if(IS_MEM_UNSAFE(contents, varsize, blimit))return(0);
       U_PMF_PTRSAV_SHIFT(Pattern, &contents, varsize);
    }
    else { *Pattern=NULL; }
    if(Flags & U_BD_FocusScales){
       if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FOCUSSCALEDATA), blimit))return(0);
       U_PMF_PTRSAV_SHIFT(Data, &contents, sizeof(U_PMF_FOCUSSCALEDATA));
    }
    else { *Data=NULL; }
    return(1);
}

/**
    \brief Get data from a U_PMF_PATHPOINTTYPE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      PathPointType flags
    \param  Type       PathPointType enumeration
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.31, Microsoft name: EmfPlusPathPointType Object

    Note:  order of 4bit fields appears to be shown in the LE column, not as
    documented in the BE column.
*/
int U_PMF_PATHPOINTTYPE_get(const char *contents, int *Flags, int *Type, const char *blimit){
    if(!contents || !Flags || !Type || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, 1, blimit))return(0);
    uint8_t tmp;
    memcpy(&tmp, contents, 1);
    *Flags =(tmp & U_PTP_MASK) >> U_PTP_SHIFT;
    *Type = (tmp & U_PPT_MASK);
    return(1);
}

/**
    \brief Get data from a U_PMF_PATHPOINTTYPERLE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Bezier     Set: Bezier curve, Clear: straight line
    \param  RL         Run Length
    \param  Ppt        PathPointType enumeration
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.32, Microsoft name: EmfPlusPathPointTypeRLE Object
*/
int U_PMF_PATHPOINTTYPERLE_get(const char *contents, int *Bezier, int *RL, int *Ppt, const char *blimit){
    if(!contents || !Bezier || !RL || !Ppt || !blimit){ return(0); }
    uint16_t tmp;
    if(IS_MEM_UNSAFE(contents, 2, blimit))return(0);
    U_PMF_SERIAL_get(&contents, &tmp, 2, 1, U_LE);
    *Bezier = tmp & U_PPF_BZ;
    *RL     = (tmp >> U_FF_SHFT_RL) & U_FF_MASK_RL;
    *Ppt    = (tmp >> U_FF_SHFT_PPT) & U_FF_MASK_PPT;
    return(1);
}

/**
    \brief Get data from a U_PMF_PENDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      PenData flags
    \param  Unit       UnitType enumeration
    \param  Width      Width in units set by Unit
    \param  Data       Optional pen data, exact composition depends on Flags
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.33, Microsoft name: EmfPlusPenData Object
*/
int U_PMF_PENDATA_get(const char *contents, uint32_t *Flags, uint32_t *Unit, U_FLOAT *Width, const char **Data, const char *blimit){
    if(!contents || !Flags || !Unit || !Width || !Data || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, 3*4, blimit))return(0);
    U_PMF_SERIAL_get(&contents, Flags, 4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Unit,  4, 1, U_LE);
    U_PMF_SERIAL_get(&contents, Width, 4, 1, U_LE);
    if(contents >= blimit)return(0); // variable data will extend farther, but this much at least must be true
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_PENOPTIONALDATA object
    \return 1 on success, 0 on error
    \param  contents       Record from which to extract data
    \param  Flags;         PenData Flags - indicated which of the many fields are present.
    \param  Matrix;        Transformation matrix
    \param  StartCap       LineCapType enumeration
    \param  EndCap         LineCapType enumeration
    \param  Join           LineJoinType enumeration
    \param  MiterLimit     Maximum (miter length / line width)
    \param  Style          LineStyle enumeration
    \param  DLCap          DashedLineCapType enumeration
    \param  DLOffset       Distance line start to first dash start
    \param  DLData         Dash and space widths
    \param  Alignment      PenAlignment enumeration
    \param  CmpndLineData  Compount Line (parallel lines drawn instead of one)
    \param  CSCapData      Custom start cap  
    \param  CECapData      Custom end cap
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.34, Microsoft name: EmfPlusPenOptionalData Object

    This object consists of a large number of optional and or variable values, which
    are returned, or not, depending on bits in Flags.
*/
int U_PMF_PENOPTIONALDATA_get(
    const char                *contents,
    uint32_t                   Flags,         // determines which fields are filled
    U_PMF_TRANSFORMMATRIX     *Matrix,
    int32_t                   *StartCap,
    int32_t                   *EndCap,
    uint32_t                  *Join,
    U_FLOAT                   *MiterLimit,
    int32_t                   *Style,
    int32_t                   *DLCap,
    U_FLOAT                   *DLOffset,
    const char               **DLData,
    int32_t                   *Alignment,
    const char               **CmpndLineData,
    const char               **CSCapData,
    const char               **CECapData,
    const char                *blimit){
    if(!contents  ||
       !Flags     || !Matrix     || !StartCap  || !EndCap        ||
       !Join      || !MiterLimit || !Style     || !DLCap         ||
       !DLOffset  || !DLData     || !Alignment || !CmpndLineData || 
       !CSCapData || !CECapData  || !blimit){ return(0); }

    if(Flags & U_PD_Transform){       if(IS_MEM_UNSAFE(contents, 4*6, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, Matrix,         4, 6, U_LE); 
    }
    if(Flags & U_PD_StartCap){        if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, StartCap,       4, 1, U_LE); 
    }
    if(Flags & U_PD_EndCap){          if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, EndCap,         4, 1, U_LE); 
    }
    if(Flags & U_PD_Join){            if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, Join,           4, 1, U_LE); 
    }
    if(Flags & U_PD_MiterLimit){      if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, MiterLimit,     4, 1, U_LE); 
    }
    if(Flags & U_PD_LineStyle){       if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, Style,          4, 1, U_LE); 
    }
    if(Flags & U_PD_DLCap){           if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, DLCap,          4, 1, U_LE); 
    }
    if(Flags & U_PD_DLOffset){        if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, DLOffset,       4, 1, U_LE); 
    }
    if(Flags & U_PD_DLData){          if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      if(IS_MEM_UNSAFE(contents, U_PMF_LEN_FLOATDATA(contents), blimit))return(0);
                                      U_PMF_PTRSAV_SHIFT(   DLData,         &contents, U_PMF_LEN_FLOATDATA(contents)); 
    }
    if(Flags & U_PD_NonCenter){       if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      U_PMF_SERIAL_get(&contents, Alignment,      4, 1, U_LE); }
    if(Flags & U_PD_CLData){          if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      if(IS_MEM_UNSAFE(contents, U_PMF_LEN_FLOATDATA(contents), blimit))return(0);
                                      U_PMF_PTRSAV_SHIFT(   CmpndLineData,  &contents, U_PMF_LEN_FLOATDATA(contents)); 
    }
    if(Flags & U_PD_CustomStartCap){  if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      if(IS_MEM_UNSAFE(contents, U_PMF_LEN_BYTEDATA(contents), blimit))return(0);
                                      U_PMF_PTRSAV_SHIFT(   CSCapData,      &contents, U_PMF_LEN_BYTEDATA(contents));  
    }
    if(Flags & U_PD_CustomEndCap){    if(IS_MEM_UNSAFE(contents, 4, blimit))return(0);
                                      if(IS_MEM_UNSAFE(contents, U_PMF_LEN_BYTEDATA(contents), blimit))return(0);
                                      U_PMF_PTRSAV_SHIFT(   CECapData,      &contents, U_PMF_LEN_BYTEDATA(contents));  
    }
    return(1);
}

/**
    \brief Get data from a U_PMF_POINT object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data.  On return position is offset by sizeof(U_PMF_POINT).
    \param  X          X coordinate
    \param  Y          Y coordinate
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.35, Microsoft name: EmfPlusPoint Object
*/
int U_PMF_POINT_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit){
    if(!contents || !X || !Y || !blimit){ return(0); }
    int16_t tmp;
    if(IS_MEM_UNSAFE(*contents, 2*2, blimit))return(0);
    U_PMF_SERIAL_get(contents, &tmp, 2, 1, U_LE); *X = tmp;
    U_PMF_SERIAL_get(contents, &tmp, 2, 1, U_LE); *Y = tmp;
    return(1);
}

/**
    \brief Get data from a U_PMF_POINTF object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data.  On return position is offset by sizeof(U_PMF_POINTF).
    \param  X          X coordinate
    \param  Y          Y coordinate
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.36, Microsoft name: EmfPlusPointF Object
*/
int U_PMF_POINTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit){
    if(!contents || !X || !Y || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(*contents, 4*2, blimit))return(0);
    U_PMF_SERIAL_get(contents, X, 4, 1, U_LE);
    U_PMF_SERIAL_get(contents, Y, 4, 1, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_POINTR object
    \return size in bytes traversed on success, 0 on error
    \param  contents   Record from which to extract data.  On return position is offset by returned size. 
    \param  X          X coordinate
    \param  Y          Y coordinate
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.37, Microsoft name: EmfPlusPointR Object
*/
int U_PMF_POINTR_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit){
    if(!contents || !*contents || !X || !Y || !blimit){ return(0); }
    int size=0;

    if(     U_PMF_INTEGER7_get( contents, X, blimit)){ size +=1; } 
    else if(U_PMF_INTEGER15_get(contents, X, blimit)){ size +=2; }
    else {  return(0); }

    if(     U_PMF_INTEGER7_get( contents, Y, blimit)){ size +=1; } 
    else if(U_PMF_INTEGER15_get(contents, Y, blimit)){ size +=2; }
    else {  return(0); }

    return(size);
}

/**
    \brief Get data from a variable POINTS object, which may be U_PMF_POINTS, U_PMF_POINTF, or U_PMF_POINTR.
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      Record flags (bits U_PPF_C and U_PPF_P are referenced)
    \param  Elements   Number of points to retrieve.
    \param  Points     Caller must free.  Array of U_PMF_POINTF coordinates.
    \param  blimit     one byte past the end of data
    
    This function should never be called directly by end user code.
*/
int U_PMF_VARPOINTS_get(const char *contents, uint16_t Flags, int Elements, U_PMF_POINTF **Points, const char *blimit){
   int status = 0;
   if(!contents  || !Points || !Elements || !blimit){ return(status); }
   U_PMF_POINTF *pts = (U_PMF_POINTF *)malloc(Elements * sizeof(U_PMF_POINTF));
   if(!pts){ return(status); }
   *Points = pts;
   U_FLOAT XF, YF;
   U_FLOAT XFS, YFS;
   
   if(Flags & U_PPF_P){ 
      for(XFS = YFS = 0.0; Elements; Elements--, pts++){
         if(!U_PMF_POINTR_get(&contents, &XF, &YF, blimit))return(0); /* this should never happen */
         XFS      += XF; /* position relative to previous point, first point is always 0,0 */
         YFS      += YF;
         pts->X    = XFS;
         pts->Y    = YFS; 
      }
   }
   else if(Flags & U_PPF_C){
      for(XF = YF = 0.0; Elements; Elements--, pts++){
         if(!U_PMF_POINT_get(&contents, &XF, &XF, blimit))break; /* this should never happen */
         pts->X    = XF;
         pts->Y    = YF; 
      }
   }
   else {
      for(XF = YF = 0.0; Elements; Elements--, pts++){
         (void) U_PMF_POINTF_get(&contents, &XF, &YF, blimit); 
         pts->X    = XF;
         pts->Y    = YF; 
      }
   }
   if(Elements){ /* some error in the preceding */
      free(*Points);
      *Points = NULL;
   }
   else {
      status = 1;
   }
   return(status);
}

/**
    \brief Get data from a U_PMF_RECT object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  X          UL X value
    \param  Y          UL Y value
    \param  Width      Width
    \param  Height     Height
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.38, Microsoft name: EmfPlusRect Object
*/
int U_PMF_RECT_get(const char **contents, int16_t *X, int16_t *Y, int16_t *Width, int16_t *Height, const char *blimit){
    if(!contents || !X || !Y|| !Width || !Height){ return(0); }
    if(IS_MEM_UNSAFE(*contents, 2*4, blimit))return(0);
    U_PMF_SERIAL_get(contents, X,      2, 1, U_LE);
    U_PMF_SERIAL_get(contents, Y,      2, 1, U_LE);
    U_PMF_SERIAL_get(contents, Width,  2, 1, U_LE);
    U_PMF_SERIAL_get(contents, Height, 2, 1, U_LE);
    return(1);
}

/**
    \brief Get data from a U_PMF_RECTF object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  X          UL X value
    \param  Y          UL Y value
    \param  Width      Width
    \param  Height     Height
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.39, Microsoft name: EmfPlusRectF Object
*/
int U_PMF_RECTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, U_FLOAT *Width, U_FLOAT *Height, const char *blimit){
    if(!contents || !X || !Y|| !Width || !Height){ return(0); }
    if(IS_MEM_UNSAFE(*contents, 4*4, blimit))return(0);
    U_PMF_SERIAL_get(contents, X,      4, 1, U_LE);
    U_PMF_SERIAL_get(contents, Y,      4, 1, U_LE);
    U_PMF_SERIAL_get(contents, Width,  4, 1, U_LE);
    U_PMF_SERIAL_get(contents, Height, 4, 1, U_LE);
    return(1);
}

/**
    \brief Get data from a variable RECTS object, which may be U_PMF_RECT or U_PMF_RECTF
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      Record flags (bit U_PPF_C is referenced)
    \param  Elements   Number of rects to retrieve.
    \param  Rects      Caller must free.  Array of U_PMF_RECTF coordinates.
    \param  blimit     one byte past the end of data

    Rects in record may be either U_PMF_RECT or U_PMF_RECTF, but this function always
    returns U_PMF_RECTF
*/
int U_PMF_VARRECTS_get(const char **contents, uint16_t Flags, int Elements, U_PMF_RECTF **Rects, const char *blimit){
   int16_t X16, Y16, Width, Height;
   if(!contents || !*contents || !Rects || !blimit){ return(0); }
   U_PMF_RECTF *rts = (U_PMF_RECTF *)malloc(Elements * sizeof(U_PMF_RECTF));
   if(!rts){
      *Rects = NULL;
      return(0);
   }
   
   *Rects = rts;
   if(Flags & U_PPF_C){
      if(IS_MEM_UNSAFE(*contents, Elements*sizeof(U_PMF_RECT), blimit)){
         free(rts);
         return(0);
      }
   }
   else {
      if(IS_MEM_UNSAFE(*contents, Elements*sizeof(U_PMF_RECT), blimit)){
         free(rts);
         return(0);
      }
   }
   for(; Elements; Elements--, rts++){
      if(Flags & U_PPF_C){
         (void) U_PMF_RECT_get(contents, &X16, &Y16, &Width, &Height, blimit);  
         rts->X      = X16;
         rts->Y      = Y16;
         rts->Width  = Width;
         rts->Height = Height;
      }
      else {
         (void) U_PMF_RECTF_get(contents, &(rts->X), &(rts->Y), &(rts->Width), &(rts->Height), blimit); 
      }
   }
   return(1);
}

/**
    \brief Get data from a U_PMF_REGIONNODE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Type       RegionNodeDataType
    \param  Data       Depending on Type: U_PMF_REGIONNODEPATH, U_PMF_RECTF, or U_PMF_REGIONNODECHILDNODES
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.40, Microsoft name: EmfPlusRegionNode Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_REGIONNODE_get(const char *contents, uint32_t *Type, const char **Data, const char *blimit){
    if(!contents || !Type || !Data || !blimit){ return(0); }
    /* Important!  This only checks the constant part, the caller must check that returned data doesn't exceed blimit */
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_REGIONNODE), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Type, 4, 1, U_LE);
    if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true
    U_PMF_PTRSAV_COND(Data, contents, !(*Type == U_RNDT_Empty || *Type == U_RNDT_Infinite ));
    return(1);
}

/**
    There is no U_PMF_REGIONNODECHILDNODES_get!
      
    The Region object is recursive allowing U_PMF_REGIONNODECHILDNODES -> 
       U_PMF_REGIONNODE -> U_PMF_REGIONNODECHILDNODES etc.
    So the data stored in each node must be handled as the tree is followed recursively.

    See U_PMF_REGIONNODECHILDNODES_print() and U_PMF_REGIONNODE_print() for an example.


    EMF+ manual 2.2.2.41, Microsoft name: EmfPlusRegionNodeChildNodes Object
*/

/**
    \brief Get data from a U_PMF_REGIONNODEPATH object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Size       Bytes in Data
    \param  Data       Boundary of region node
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.42, Microsoft name: EmfPlusRegionNodePath Object
*/
int U_PMF_REGIONNODEPATH_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){
    if(!contents || !Size || !Data || !blimit){ return(0); }
    /* Important!  This only checks the constant part, the caller must check that returned data doesn't exceed blimit */
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_REGIONNODEPATH), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE);
    if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true
    U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
    return(1);
}

/**
    \brief Get data from a U_PMF_SOLIDBRUSHDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Color      Color of brush
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.43, Microsoft name: EmfPlusSolidBrushData Object
*/
int U_PMF_SOLIDBRUSHDATA_get(const char *contents, U_PMF_ARGB *Color, const char *blimit){
    if(!contents || !Color || !blimit){ return(0); }
    if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_SOLIDBRUSHDATA), blimit))return(0);
    U_PMF_SERIAL_get(&contents, Color, 4, 1, U_XE);
    return(1);
}

/**
    \brief Get data from a U_PMF_STRINGFORMATDATA object
    \return 1 on success, 0 on error
    \param  contents      Record from which to extract data
    \param  TabStopCount  Entries in TabStop array
    \param  RangeCount    Entries in CharRange array
    \param  TabStops      Array of tabstop locations
    \param  CharRange     Array of character ranges in the text
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.44, Microsoft name: EmfPlusStringFormatData Object
*/
int U_PMF_STRINGFORMATDATA_get(const char *contents, uint32_t TabStopCount, uint32_t RangeCount, 
      const U_FLOAT **TabStops, const U_PMF_CHARACTERRANGE **CharRange, const char *blimit){
   if(!contents || !TabStops|| !CharRange || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, (TabStopCount + 2*RangeCount)*4, blimit))return(0);
   *TabStops = NULL;
   if(TabStopCount > 0){ U_PMF_SERIAL_get(&contents, TabStops,  4, TabStopCount, U_LE); }
   *CharRange = NULL;
   if(RangeCount   > 0){ U_PMF_SERIAL_get(&contents, CharRange, 4, 2*RangeCount, U_LE); }
   return(1);
}

/**
    \brief Get data from a U_PMF_TEXTUREBRUSHDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Flags      BrushData flags
    \param  WrapMode   WrapMode enumeration
    \param  Data       Optional texture data
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.45, Microsoft name: EmfPlusTextureBrushData Object

    Caller must check Data for possible memory access violations.
*/
int U_PMF_TEXTUREBRUSHDATA_get(const char *contents, uint32_t *Flags, int32_t *WrapMode, const char **Data, const char *blimit){
   if(!contents || !Flags || !WrapMode || !Data || !blimit){ return(0); }
   /* Important!  This only checks the constant part, the caller must check that returned data doesn't exceed blimit */
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TEXTUREBRUSHDATA), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Flags,    4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, WrapMode, 4, 1, U_LE);
   if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true
   U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
   return(1);
}

/**
    \brief Get data from a U_PMF_TEXTUREBRUSHOPTIONALDATA object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  HasImage   True if this object has an Image
    \param  Matrix     Transformation matrix, NULL if Flag BrushDataTransform is not set.
    \param  Image      Image that contains the texture.
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.46, Microsoft name: EmfPlusTextureBrushOptionalData Object

    Caller must check Image for possible memory access violations.
*/
int U_PMF_TEXTUREBRUSHOPTIONALDATA_get(const char *contents, int HasImage, U_PMF_TRANSFORMMATRIX *Matrix, 
      const char **Image, const char *blimit){
   if(!contents || !Image || !blimit){ return(0); }
   if(Matrix){
      if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TRANSFORMMATRIX), blimit))return(0);
      U_PMF_SERIAL_get(&contents, Matrix,   4, 6, U_LE);
   }
   if(HasImage){
      if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true
      U_PMF_PTRSAV_COND(Image, contents, HasImage);
   }
   return(1);
}

/**
    \brief Get data from a U_PMF_TRANSFORMMATRIX object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Matrix     Transformation matrix, present if Flag BrushDataTransform is set.
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.2.47, Microsoft name: EmfPlusTransformMatrix Object
*/
int U_PMF_TRANSFORMMATRIX_get(const char *contents, U_PMF_TRANSFORMMATRIX *Matrix, const char *blimit){
   if(!contents || !Matrix || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TRANSFORMMATRIX), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_BLUR object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Radius     Blur radius in pixels
    \param  ExpandEdge 1: expand bitmap by Radius; 0: bitmap size unchanged
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.1, Microsoft name: BlurEffect Object
*/
int U_PMF_IE_BLUR_get(const char *contents, U_FLOAT *Radius, uint32_t *ExpandEdge, const char *blimit){
   if(!contents || !Radius || !ExpandEdge || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_BLUR), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Radius,     4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, ExpandEdge, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_BRIGHTNESSCONTRAST object
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Brightness  -255 to 255, 0 is unchanged, positive increases, negative decreases
    \param  Contrast    -100 to 100, 0 is unchanged, positive increases, negative decreases
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.2, Microsoft name: BrightnessContrastEffect Object
*/
int U_PMF_IE_BRIGHTNESSCONTRAST_get(const char *contents, int32_t *Brightness, int32_t *Contrast, const char *blimit){
   if(!contents || !Brightness || !Contrast || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_BRIGHTNESSCONTRAST), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Brightness, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Contrast,   4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_COLORBALANCE object
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  CyanRed      -100 to 100, 0 is unchanged, positive increases Red   & decreases Cyan,    negative is opposite
    \param  MagentaGreen -100 to 100, 0 is unchanged, positive increases Green & decreases Magenta, negative is opposite
    \param  YellowBlue   -100 to 100, 0 is unchanged, positive increases Blue  & decreases Yellow,  negative is opposite
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.3, Microsoft name: ColorBalanceEffect Object
*/
int U_PMF_IE_COLORBALANCE_get(const char *contents, int32_t *CyanRed, int32_t *MagentaGreen, int32_t *YellowBlue, const char *blimit){
   if(!contents || !CyanRed || !MagentaGreen || !YellowBlue || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORBALANCE), blimit))return(0);
   U_PMF_SERIAL_get(&contents, CyanRed,      4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, MagentaGreen, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, YellowBlue,   4, 1, U_LE);
   return(1);
}


/**
    \brief Get data from a U_PMF_IE_COLORCURVE object
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Adjust       CurveAdjustment enumeration
    \param  Channel      CurveChannel enumeration
    \param  Intensity    adjustment to apply.  "Adjust" determines what field this is and range values.
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.4, Microsoft name: ColorCurveEffect Object
*/
int U_PMF_IE_COLORCURVE_get(const char *contents, uint32_t *Adjust, uint32_t *Channel, int32_t *Intensity, const char *blimit){
   if(!contents || !Adjust || !Channel || !Intensity || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORCURVE), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Adjust,    4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Channel,   4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Intensity, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_COLORLOOKUPTABLE object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  BLUT       Blue  color lookup table
    \param  GLUT       Green color lookup table
    \param  RLUT       Red   color lookup table
    \param  ALUT       Alpha color lookup table
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.5, Microsoft name: ColorLookupTableEffect Object
*/
int U_PMF_IE_COLORLOOKUPTABLE_get(const char *contents, 
      const uint8_t **BLUT, const uint8_t **GLUT, const uint8_t **RLUT, const uint8_t **ALUT, const char *blimit){
   if(!contents || !BLUT || !GLUT || !RLUT || !ALUT || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORLOOKUPTABLE) + 4 * 256, blimit))return(0);
   U_PMF_PTRSAV_SHIFT((const char **)BLUT, &contents, 256);
   U_PMF_PTRSAV_SHIFT((const char **)GLUT, &contents, 256);
   U_PMF_PTRSAV_SHIFT((const char **)RLUT, &contents, 256);
   U_PMF_PTRSAV_SHIFT((const char **)ALUT, &contents, 256);    
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_COLORMATRIX object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Matrix     5 x 5 color transformation matrix, First 4 rows are [{4 multiplier values},0.0] for R,G,B,A, last Row is [{4 color translation valuess}, 1.0]
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.6, Microsoft name: ColorMatrixEffect Object
*/
int U_PMF_IE_COLORMATRIX_get(const char *contents, U_PMF_IE_COLORMATRIX *Matrix, const char *blimit){
   if(!contents || !Matrix || !blimit){ return(0); }
   /* Important!  This only checks the constant part, the caller must check that returned data doesn't exceed blimit */
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORMATRIX), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Matrix, 4, 5*5, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_HUESATURATIONLIGHTNESS object
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Hue          -180 to 180, 0 is unchanged
    \param  Saturation   -100 to 100, 0 is unchanged
    \param  Lightness    -100 to 100, 0 is unchanged
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.7, Microsoft name: HueSaturationLightnessEffect Object
*/
int U_PMF_IE_HUESATURATIONLIGHTNESS_get(const char *contents, int32_t *Hue, int32_t *Saturation, int32_t *Lightness, const char *blimit){
   if(!contents || !Hue || !Saturation || !Lightness || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_HUESATURATIONLIGHTNESS), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Hue,        4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Saturation, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Lightness,  4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_LEVELS object
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Highlight    0 to 100, 100 is unchanged
    \param  Midtone      -100 to 100, 0 is unchanged
    \param  Shadow       0 to 100, 0 is unchanged
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.8, Microsoft name: LevelsEffect Object
*/
int U_PMF_IE_LEVELS_get(const char *contents, int32_t *Highlight, int32_t *Midtone, int32_t *Shadow, const char *blimit){
   if(!contents || !Highlight || !Midtone || !Shadow || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_LEVELS), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Highlight, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Midtone,   4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Shadow,    4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_REDEYECORRECTION object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Elements   Number of members in Rects
    \param  Rects      Caller must free.  Pointer to memory holding an array of U_RECTL.
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.9, Microsoft name: RedEyeCorrectionEffect Object
*/
int U_PMF_IE_REDEYECORRECTION_get(const char *contents, int32_t *Elements, U_RECTL **Rects, const char *blimit){
   if(!contents || !Elements || !Rects || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_REDEYECORRECTION), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   if(IS_MEM_UNSAFE(contents, *Elements * 4, blimit))return(0);
   *Rects = (U_RECTL *) malloc(*Elements * sizeof(U_RECTL));
   if(!*Rects){ return(0); }
   U_PMF_SERIAL_get(&contents, *Rects, 4, *Elements * 4, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_SHARPEN object
    \return 1 on success, 0 on error
    \param  contents   Record from which to extract data
    \param  Radius     Sharpening radius in pixels
    \param  Sharpen    0 to 100, 0 is unchanged
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.10, Microsoft name: SharpenEffect Object
*/
int U_PMF_IE_SHARPEN_get(const char *contents, U_FLOAT *Radius, int32_t *Sharpen, const char *blimit){
   if(!contents || !Radius || !Sharpen || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_SHARPEN), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Radius,  4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Sharpen, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMF_IE_TINT object
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Hue         -180 to 180, [positive==clockwise] rotation in degrees starting from blue
    \param  Amount      -100 [add black] to 100[add white], 0 is unchanged.  Change in hue on specified axis
    \param  blimit     one byte past the end of data

    EMF+ manual 2.2.3.11, Microsoft name: TintEffect Object
*/
int U_PMF_IE_TINT_get(const char *contents, int32_t *Hue, int32_t *Amount, const char *blimit){
   if(!contents || !Hue || !Amount || !blimit){ return(0); }
   if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_TINT), blimit))return(0);
   U_PMF_SERIAL_get(&contents, Hue,    4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Amount, 4, 1, U_LE);
   return(1);
}

/*

   end of U_PMF_*_get() functions 
   =====================================================================================
   start of U_PMR_*_get() functions 

      These functions all assume that the size field in the common EMF+ header has already
      been checked, so that the extent the record claims exists in the data read in for the file.
      Consequently none of them takes a blimit parameter.  They generate a new one from the
      header size field and contents if needed.

*/

int U_PMR_common_stack_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){
   if(!contents || !StackID){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_RESTORE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE);
   return(1);
}

/* for records that have a type but no associated flag bits or data */
int U_PMR_common_header_get(const char *contents, U_PMF_CMN_HDR *Header){
   /* memory access safe, only uses the common header */
   if(!contents){ return(0); }
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_OFFSETCLIP record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header (ignore flags)
    \param  dX          horizontal translation offset to apply to clipping region
    \param  dY          vertical   translation offset to apply to clipping region

    EMF+ manual 2.3.1.1, Microsoft name: EmfPlusOffsetClip Record,  Index 0x35
*/
int U_PMR_OFFSETCLIP_get(const char *contents, U_PMF_CMN_HDR *Header,
      U_FLOAT *dX, U_FLOAT *dY){
   if(!contents){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_OFFSETCLIP))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_SERIAL_get(&contents, dX, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, dY, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_RESETCLIP record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header (ignore flags)

    EMF+ manual 2.3.1.2, Microsoft name: EmfPlusResetClip Record, Index 0x31
*/
int U_PMR_RESETCLIP_get(const char *contents, U_PMF_CMN_HDR *Header){
   if(!contents){ return(0); }
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETCLIPPATH record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  CMenum      CombineMode enumeration..
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)

    EMF+ manual 2.3.1.3, Microsoft name: EmfPlusSetClipPath Record, Index 0x33
*/
int U_PMR_SETCLIPPATH_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PathID, int *CMenum){
   if(!contents || !PathID || !CMenum){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETCLIPPATH))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *CMenum  = (lclHeader.Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4;
   *PathID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   return(1);
}

/**
    \brief Get data from a U_PMR_SETCLIPRECT record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header (ignore flags)
    \param  CMenum      Combine mode enumeration.
    \param  Rect        Rectangle used with CombineMode enumeration from Header.Flags

    EMF+ manual 2.3.1.4, Microsoft name: EmfPlusSetClipRect Record, Index 0x32
*/
int U_PMR_SETCLIPRECT_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *CMenum,
      U_PMF_RECTF *Rect){
   if(!contents || !CMenum || !Rect ){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETCLIPRECT))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *CMenum  = (lclHeader.Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4;
   U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETCLIPREGION record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  CMenum      CombineMode enumeration..
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)

    EMF+ manual 2.3.1.5, Microsoft name: EmfPlusSetClipRegion Record, Index 0x34
*/
int U_PMR_SETCLIPREGION_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PathID, int *CMenum){
   if(!contents || !PathID || !CMenum){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETCLIPREGION))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *CMenum  = (lclHeader.Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4;
   *PathID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   return(1);
}

/**
    \brief Get data from a U_PMR_COMMENT record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header (ignore flags)
    \param  Data        Private data, may be anything

    EMF+ manual 2.3.2.1, Microsoft name: EmfPlusComment Record, Index 0x03
    
    Caller must check Data for possible memory access violations.
*/
int U_PMR_COMMENT_get(const char *contents, U_PMF_CMN_HDR *Header,
      const char **Data){
   if(!contents || !Data){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_COMMENT))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
   return(1);
}

/**
    \brief Get data from a U_PMR_ENDOFFILE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header  (ignore flags)

    EMF+ manual 2.3.3.1, Microsoft name: EmfPlusEndOfFile Record, Index 0x02
*/
int U_PMR_ENDOFFILE_get(const char *contents, U_PMF_CMN_HDR *Header){
   if(!contents){ return(0); }
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_GETDC record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header  (ignore flags)

    EMF+ manual 2.3.3.2, Microsoft name: EmfPlusGetDC Record, Index 0x04
*/
int U_PMR_GETDC_get(const char *contents, U_PMF_CMN_HDR *Header){
   if(!contents){ return(0); }
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_HEADER record
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Header       Common header  (ignore flags)
    \param  Version      EmfPlusGraphicsVersion object
    \param  IsDual       set = Dual-mode file, clear= EMF+ only file.
    \param  IsVideo      set = video device, clear= printer.  Ignore all other bits.
    \param  LogicalDpiX  Horizontal resolution reference device in DPI
    \param  LogicalDpiY  Vertical   resolution reference device in DPI

    EMF+ manual 2.3.3.3, Microsoft name: EmfPlusHeader Record, Index 0x01
*/
int U_PMR_HEADER_get(const char *contents, U_PMF_CMN_HDR *Header,
      U_PMF_GRAPHICSVERSION *Version, int *IsDual, int *IsVideo, uint32_t *LogicalDpiX, uint32_t *LogicalDpiY){
   if(!contents || !Version || !IsDual || !IsVideo || !LogicalDpiX || !LogicalDpiY){ return(0); }
   uint32_t tmp;

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_HEADER))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *IsDual = (lclHeader.Flags & U_PPF_DM ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, Version,     4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, &tmp,        4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, LogicalDpiX, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, LogicalDpiY, 4, 1, U_LE);
   *IsVideo = (tmp & U_PPF_VIDEO ? 1 : 0 );
   return(1);
}

/**
    \brief Get data from a U_PMR_CLEAR record
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Header       Common header  (ignore flags)
    \param  Color        Erase everything preceding, set background ARGB color.

    EMF+ manual 2.3.4.1, Microsoft name: EmfPlusClear Record, Index 0x09
*/
int U_PMR_CLEAR_get(const char *contents, U_PMF_CMN_HDR *Header,
      U_PMF_ARGB *Color){
   if(!contents || !Color){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_CLEAR))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_SERIAL_get(&contents, Color, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWARC record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  Start       Start angle, >=0.0, degrees clockwise from 3:00
    \param  Sweep       Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param  Rect        Caller must free.  Bounding rectangle.  Coordinate type set by ctype.

    EMF+ manual 2.3.4.2, Microsoft name: EmfPlusDrawArc Record, Index 0x12
*/
int U_PMR_DRAWARC_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype, 
      U_FLOAT *Start, U_FLOAT *Sweep,
      U_PMF_RECTF *Rect){
   if(!contents || !PenID || !ctype || !Start || !Sweep || !Rect){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWARC))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Start, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Sweep, 4, 1, U_LE);
   U_PMF_RECTF *Rects = NULL;
   if(!U_PMF_VARRECTS_get(&contents, lclHeader.Flags, 1, &Rects, blimit))return(0);
   memcpy(Rect,Rects,sizeof(U_PMF_RECTF));
   free(Rects);
   return(1);
}


/**
    \brief Get data from a U_PMR_DRAWBEZIERS record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  RelAbs      Set: Coordinates are relative; Clear: Coordinates are absolute and their type is set by ctype
    \param  Elements    Number of members in the Data array
    \param  Points      Caller must free.  Array of U_POINT_F = Sequence of points to connect.  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.3, Microsoft name: EmfPlusDrawBeziers Record, Index 0x19
*/
int U_PMR_DRAWBEZIERS_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype, int *RelAbs, 
      uint32_t *Elements, 
      U_PMF_POINTF **Points){
   if(!contents || !PenID || !ctype || !RelAbs || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWBEZIERS))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 );
   *PenID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   int status = U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit );
   return(status);
}

/**
    \brief Get data from a U_PMR_DRAWCLOSEDCURVE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  RelAbs      Set: Coordinates are relative; Clear: Coordinates are absolute and their type is set by ctype
    \param  Tension     Controls splines, 0 is straight line, >0 is curved
    \param  Elements    Number of members in the Data array
    \param  Points      Caller must free.  Array of U_POINT_F = Sequence of points to connect.  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.4, Microsoft name: EmfPlusDrawClosedCurve Record, Index 0x17
*/
int U_PMR_DRAWCLOSEDCURVE_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype, int *RelAbs,
      U_FLOAT *Tension, uint32_t *Elements,
      U_PMF_POINTF **Points){
   if(!contents || !PenID || !ctype || !RelAbs || !Tension || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWCLOSEDCURVE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 );
   *PenID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Tension,  4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWCURVE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  Tension     Controls splines, 0 is straight line, >0 is curved
    \param  Offset      Element in Points that is the spline's starting point
    \param  NSegs       Number of segments
    \param  Elements    Number of members in Data array
    \param  Points      Caller must free.  Array of U_POINT_F = Sequence of points to connect.  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.5, Microsoft name: EmfPlusDrawCurve Record, Index 0x18
*/
int U_PMR_DRAWCURVE_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype,
      U_FLOAT *Tension, uint32_t *Offset, uint32_t *NSegs, uint32_t *Elements,
      U_PMF_POINTF **Points){
   if(!contents || !PenID || !ctype || !Tension || !Offset || !NSegs || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWCURVE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *PenID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Tension,  4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Offset,   4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, NSegs,    4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWDRIVERSTRING record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  FontID      U_PMF_FONT object in the EMF+ object table (0-63, inclusive)
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: index of U_PMF_BRUSH object in EMF+ object table.
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depends on Flags bit0
    \param  DSOFlags    DriverStringOptions flags
    \param  HasMatrix   If 1 record contains a TransformMatrix field, if 0 it does not.
    \param  Elements    Number of members in Glyphs and Positions array
    \param  Glyphs      Caller must free.  If U_DSO_CmapLookup is set in DSOFlags this is an array of UTF16LE characters, otherwise, it is an array of indices into the U_PMF_FONT object indexed by Object_ID in flags.
    \param  Points      Caller must free.  Coordinates of each member of Glyphs.  U_DSO_RealizedAdvance set in DSOFlags Relative then positions are calculated relative to the first glyph which is stored in Positions, otherwise, all glyph positions are stored in Positions.
    \param  Matrix      Caller must free.  Transformation to apply to Glyphs & Positions. Present if HasMatrix is 1

    EMF+ manual 2.3.4.6, Microsoft name: EmfPlusDrawDriverString Record, Index 0x36
*/
int U_PMR_DRAWDRIVERSTRING_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *FontID, int *btype,
      uint32_t *BrushID, uint32_t *DSOFlags, uint32_t *HasMatrix, uint32_t *Elements,
      uint16_t **Glyphs, U_PMF_POINTF **Points, U_PMF_TRANSFORMMATRIX **Matrix){
   if(!contents || !FontID || !btype || !BrushID || 
      !DSOFlags || !HasMatrix || !Elements || !Glyphs || !Points || !Matrix){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWDRIVERSTRING))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *FontID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, BrushID,   4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, DSOFlags,  4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, HasMatrix, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Elements,  4, 1, U_LE);
   if(IS_MEM_UNSAFE(contents, *Elements*2 + *Elements*2*4 + 24, blimit))return(0);
   if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Glyphs, 2, *Elements,    U_LE, (*DSOFlags & U_DSO_CmapLookup))){      return(0); }
   if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Points, 4, *Elements *2, U_LE, (*DSOFlags & U_DSO_RealizedAdvance))){ return(0); }
   if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Matrix, 4, 6,            U_LE, (*HasMatrix))){                        return(0); }
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWELLIPSE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  Rect        Caller must free.  Bounding rectangle.  Coordinate type set by ctype.

    EMF+ manual 2.3.4.7, Microsoft name: EmfPlusDrawEllipse Record, Index 0x0F
*/
int U_PMR_DRAWELLIPSE_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype, 
      U_PMF_RECTF *Rect){
   if(!contents || !PenID || !ctype || !Rect){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWELLIPSE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *PenID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Rect,  4, 4, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWIMAGE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  ImgID       U_PMF_IMAGE object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  ImgAttrID   index of a U_PMF_IMAGEATTRIBUTES object in the object table
    \param  SrcUnit     UnitType enumeration
    \param  SrcRect     Region of image 
    \param  DstRect     Destination rectangle for image.  Coordinate type set by ctype.

    EMF+ manual 2.3.4.8, Microsoft name: EmfPlusDrawImage Record, Index 0x1A
*/
int U_PMR_DRAWIMAGE_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *ImgID, int *ctype,
      uint32_t *ImgAttrID, int32_t *SrcUnit, U_PMF_RECTF *SrcRect,
      U_PMF_RECTF *DstRect){
   if(!contents || !ImgID || !ctype || !ImgAttrID || !SrcUnit || !SrcRect || !DstRect){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWIMAGE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *ImgID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, ImgAttrID, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, SrcUnit,   4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, SrcRect,   4, 4, U_LE);
   U_PMF_SERIAL_get(&contents, DstRect,   4, 4, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWIMAGEPOINTS record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  ImgID       U_PMF_IMAGE object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  etype       Set: effect from previous U_PMR_SERIALIZABLEOBJECT record will be applied; Clear:  no effect applied
    \param  RelAbs      Set: Data is relative, Clear: if it is absolute
    \param  ImgAttrID   EmfPlusImageAttributes object 
    \param  SrcUnit     UnitType enumeration
    \param  SrcRect     Region of image 
    \param  Elements    Number of members in Points, must be 3
    \param  Points      Caller must free.  3 points of a parallelogram..  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.9, Microsoft name: EmfPlusDrawImagePoints Record, Index 0x1B
*/
int U_PMR_DRAWIMAGEPOINTS_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *ImgID, int *ctype, int *etype, int *RelAbs,
      uint32_t *ImgAttrID, int32_t *SrcUnit, U_PMF_RECTF *SrcRect, uint32_t *Elements, 
      U_PMF_POINTF **Points){
   if(!contents || !ImgID || !ctype || !etype || !RelAbs || !ImgAttrID || !SrcUnit || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWIMAGEPOINTS))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *etype  = (lclHeader.Flags & U_PPF_E ? 1 : 0 );
   *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 );
   *ImgID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, ImgAttrID, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, SrcUnit,   4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, SrcRect,   4, 4, U_LE);
   U_PMF_SERIAL_get(&contents, Elements,  4, 1, U_LE);
   U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWLINES record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  dtype       Set: path must be closed, Clear: path is open
    \param  RelAbs      Set: Coordinates are relative; Clear: Coordinates are absolute and their type is set by ctype
    \param  Elements    Number of members in Points
    \param  Points      Caller must free.  Array of U_POINT_F = Sequence of points to connect.  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.10, Microsoft name: EmfPlusDrawLines Record, Index 0x0D
*/
int U_PMR_DRAWLINES_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype, int *dtype, int *RelAbs,
      uint32_t *Elements,
      U_PMF_POINTF **Points){
   if(!contents || !PenID || !ctype || !dtype || !RelAbs || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWLINES))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *dtype  = (lclHeader.Flags & U_PPF_D ? 1 : 0 );
   *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 );
   *PenID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWPATH record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)

    EMF+ manual 2.3.4.11, Microsoft name: EmfPlusDrawPath Record, Index 0x15
*/
int U_PMR_DRAWPATH_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PathID, uint32_t *PenID){
   if(!contents || !PathID || !PenID){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWPATH))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *PathID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, PenID, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWPIE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  Start       Start angle, >=0.0, degrees clockwise from 3:00
    \param  Sweep       Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param  Rect        Caller must free.  Bounding rectangle.  Coordinate type set by ctype.

    EMF+ manual 2.3.4.12, Microsoft name: EmfPlusDrawPie Record, Index 0x0D
*/
int U_PMR_DRAWPIE_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype,
      U_FLOAT *Start, U_FLOAT *Sweep,
      U_PMF_RECTF *Rect){
   if(!contents || !PenID || !ctype || !Start || !Sweep || !Rect){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWPIE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, Start, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Sweep, 4, 1, U_LE);
   U_PMF_RECTF *Rects = NULL;
   if(!U_PMF_VARRECTS_get(&contents, lclHeader.Flags, 1, &Rects, blimit))return(0);
   memcpy(Rect,Rects,sizeof(U_PMF_RECTF));
   free(Rects);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWRECTS record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PenID       U_PMF_PEN object in the EMF+ object table (0-63, inclusive)
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  Elements    Number of members in Rects
    \param  Rects       Caller must free.  Array of U_PMF_RECTF rectangles to draw.

    EMF+ manual 2.3.4.13, Microsoft name: EmfPlusDrawRects Record, Index 0x0B
    
    Rects in record may be either U_PMF_RECT or U_PMF_RECTF, but this function always
    returns U_PMF_RECTF
*/
int U_PMR_DRAWRECTS_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PenID, int *ctype,
      uint32_t *Elements,
      U_PMF_RECTF **Rects){
   if(!contents || !PenID || !Elements || !Rects){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWPIE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *PenID =  (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARRECTS_get(&contents, lclHeader.Flags, *Elements, Rects, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_DRAWSTRING record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  FontID      U_PMF_FONT object in the EMF+ object table (0-63, inclusive)
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: index of U_PMF_BRUSH object in EMF+ object table.
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.
    \param  FormatID    U_PMF_STRINGFORMAT object in EMF+ Object Table.
    \param  Elements    Number of characters in the string.
    \param  Rect        String's bounding box.
    \param  String      Caller must free.  Array of UFT-16LE unicode characters. 

    EMF+ manual 2.3.4.14, Microsoft name: EmfPlusDrawString Record, Index 0x1C
*/
int U_PMR_DRAWSTRING_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *FontID, int *btype,
      uint32_t *BrushID, uint32_t *FormatID, uint32_t *Elements, U_PMF_RECTF *Rect,
      uint16_t **String){
   if(!contents || !FontID || !btype || !BrushID || !FormatID || !Elements || !String){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWPIE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *FontID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, BrushID,  4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, FormatID, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Rect,     4, 4, U_LE);
   if(IS_MEM_UNSAFE(contents, *Elements * 2, blimit))return(0);
   if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)String, 2, *Elements, U_XE, 1)){ return(0); }
   return(1);
}

/**
    \brief Get data from a U_PMR_FILLCLOSEDCURVE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  ftype       Set: winding fill; Clear: alternate fill
    \param  RelAbs      Set: Coordinates are relative; Clear: Coordinates are absolute and their type is set by ctype
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.
    \param  Tension     Controls splines, 0 is straight line, >0 is curved
    \param  Elements    Number of members in Points
    \param  Points      Caller must free.  Array of U_POINT_F = Sequence of points to connect.  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.15, Microsoft name: EmfPlusFillClosedCurve Record, Index 0x16
*/
int U_PMR_FILLCLOSEDCURVE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *btype, int *ctype, int *ftype, int *RelAbs,
      uint32_t *BrushID, U_FLOAT *Tension, uint32_t *Elements,
      U_PMF_POINTF **Points){
   if(!contents || !btype || !ctype || !ftype || !RelAbs || !BrushID || !Tension || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWLINES))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *ftype  = (lclHeader.Flags & U_PPF_F ? 1 : 0 );
   *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, BrushID,  4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, Tension,  4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_FILLELLIPSE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.
    \param  Rect        Caller must free.  Bounding box for elliptical pie segment being drawn.  Coordinate type set by ctype.

    EMF+ manual 2.3.4.16, Microsoft name: EmfPlusFillEllipse Record, Index 0x0E
*/
int U_PMR_FILLELLIPSE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *btype, int *ctype,
      uint32_t *BrushID,
      U_PMF_RECTF *Rect){
   if(!contents || !btype || !ctype || !BrushID || !Rect){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_FILLELLIPSE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, Rect,    4, 4, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_FILLPATH record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  PathID      U_PMF_PATH object in the EMF+ object table (0-63, inclusive)
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.

    EMF+ manual 2.3.4.17, Microsoft name: EmfPlusFillPath Record, Index 0x14 

    Note: U_PMF_FILLPATHOBJ is the object, U_PMF_FILLPATH is the file record
*/
int U_PMR_FILLPATH_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *PathID, int *btype,
      uint32_t *BrushID){
   if(!contents || !PathID || !btype || !BrushID){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_FILLPATH))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *PathID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   return(1);
}

/**
    \brief Get data from a U_PMR_FILLPIE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.
    \param  Start       Start angle, >=0.0, degrees clockwise from 3:00
    \param  Sweep       Sweep angle, -360<= angle <=360, degrees clockwise from Start
    \param  Rect        Bounding box for elliptical pie segment being filled.  Coordinate type set by ctype.

    EMF+ manual 2.3.4.18, Microsoft name: EmfPlusFillPie Record, Index 0x10
*/
int U_PMR_FILLPIE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *btype, int *ctype,
      uint32_t *BrushID, U_FLOAT *Start, U_FLOAT *Sweep,
      U_PMF_RECTF *Rect){
   if(!contents || !btype || !ctype || !BrushID || !Start || !Sweep || !Rect){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_FILLPIE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, Start,   4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Sweep,   4, 1, U_LE);
   U_PMF_RECTF *Rects = NULL;
   if(!U_PMF_VARRECTS_get(&contents, lclHeader.Flags, 1, &Rects, blimit))return(0);
   memcpy(Rect,Rects,sizeof(U_PMF_RECTF));
   free(Rects);
   return(1);
}

/**
    \brief Get data from a U_PMR_FILLPOLYGON record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  RelAbs      Set: U_PMF_PathPointTypeRLE and/or U_PMF_PathPointType objects; Clear: only U_PMF_PathPointType
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.
    \param  Elements    Number of members in Data.
    \param  Points      Sequence of points to connect with line segments.  Coordinate type set by ctype and RelAbs.

    EMF+ manual 2.3.4.19, Microsoft name: EmfPlusFillPolygon Record, Index 0x0C
*/
int U_PMR_FILLPOLYGON_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *btype, int *ctype, int *RelAbs,
      uint32_t *BrushID, uint32_t *Elements,
      U_PMF_POINTF **Points){
   if(!contents || !btype || !ctype || !RelAbs || !BrushID || !Elements || !Points){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_DRAWLINES))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *btype   = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *ctype   = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *RelAbs  = (lclHeader.Flags & U_PPF_R ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, BrushID,  4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_FILLRECTS record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.
    \param  Elements    Number of members in Data.
    \param  Rects       Caller must free.  Array of U_PMF_RECTF rectangles to draw.

    EMF+ manual 2.3.4.20, Microsoft name: EmfPlusFillRects Record, Index 0x0A
    
    EMF+ files have been encountered where BrushID must be a color, because it has a value like FFFF0000 but
    the flags are set wrong, so that U_PPF_B is not set.  Detect these by BrushID >63 for btype=0 and correct.
    If the opposite problem occurs it cannot be reliably detected, so it cannot be corrected.

    Rects in record may be either U_PMF_RECT or U_PMF_RECTF, but this function always
    returns U_PMF_RECTF
*/
int U_PMR_FILLRECTS_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *btype, int *ctype,
      uint32_t *BrushID, uint32_t *Elements,
      U_PMF_RECTF **Rects){
   if(!contents || !btype || !ctype || !BrushID || !Elements || !Rects){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_FILLRECTS))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, BrushID,  4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE);
   U_PMF_VARRECTS_get(&contents, lclHeader.Flags, *Elements, Rects, blimit);
   /* correct btype, if necessary, for invalid EMF+ input */
   if((*BrushID > 63) & !*btype)*btype=1;
   return(1);
}


/**
    \brief Get data from a U_PMR_FILLREGION record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  RgnID       U_PMF_REGION object in the EMF+ object table (0-63, inclusive)
    \param  btype       Set: BrushID is an U_PFM_ARGB; Clear: is index of U_PMF_BRUSH object in EMF+ object table.
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  BrushID     Color or index of  U_PMF_BRUSH object in the EMF+ object table, depending on btype.

    EMF+ manual 2.3.4.21, Microsoft name: EmfPlusFillRegion Record, Index 0x13
*/
int U_PMR_FILLREGION_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *RgnID, int *btype, int *ctype,
      uint32_t *BrushID){
   if(!contents || !RgnID || !btype || !ctype || !BrushID){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_FILLREGION))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *btype  = (lclHeader.Flags & U_PPF_B ? 1 : 0 );
   *ctype  = (lclHeader.Flags & U_PPF_C ? 1 : 0 );
   *RgnID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */
   return(1);
}

/**
    \brief Get data from a U_PMR_OBJECT record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  ObjID       Index for this object in the EMF+ object table (0-63, inclusive)
    \param  otype       ObjectType enumeration
    \param  ntype       Set: object definition continue bit is set
    \param  TSize       If ntype is set, holds the total number of data bytes split across multiple records.  If ntype is clear, has no meaning.
    \param  Data        Object's data.  Type from otype.

    EMF+ manual 2.3.5.1, Microsoft name: EmfPlusObject Record, Index 0x13
    
    Caller must check Data for possible memory access violations.

    OTHER NOTES:
    All objects are to be stored in the same table and retrieved by index.
    Documentation indicates that this table contains only 64 slots, although the index
       field which references it can code for values 0-127.
    If a new object has the same index as an existing object the old one is deleted and
       the new one goes into its storage slot.
    The continuation bit (U_PPF_N) is documented as indicating that the object is continued into
       the next record.  Examination of emf+ records in emf files produced by PowerPoint 2003
       show that changing the ObjID also serves as a continued record terminator, and that it apparently
       overrides the value for the continue bit.  That is, even though the preceding records said
       that it was continued, the change of ObjID terminates that preceding record without adding
       any more data to it.  In one example the sequential emf+ records were:
       ObjID   type  size  continue
       0       5     65008 Y
       0       5     65008 Y
       0       5     63104 Y
       1       8        24 N
       A DrawImagePoints record followed that referenced ObjID 0. 
    Examination of the records with continue set showed that data in each 
       was preceded by a uint32_t size value equivalent to the size of the 
       data that had been split across multiple records, in this case 
       0x0002F254 = 193108.  It is not clear at present if this size value 
       will also be present at the end of a continued series that terminates 
       by not using the continue bit, rather than changing the ObjID. 
*/
int U_PMR_OBJECT_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint32_t *ObjID, int *otype, int *ntype, uint32_t *TSize,
      const char **Data){
   if(!contents || !ObjID || !otype || !ntype || !Data){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_OBJECT))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *ntype  = (lclHeader.Flags & U_PPF_N ? 1 : 0 );
   *ObjID  = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8;
   *otype  = (lclHeader.Flags >> U_FF_SHFT_OT) & U_FF_MASK_OT;
   if(*ntype){  U_PMF_SERIAL_get(&contents, TSize, 4, 1,  U_LE); }
   else {       *TSize = 0; }
   U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
   return(1);
}
  
/**
    \brief Get data from a U_PMR_SERIALIZABLEOBJECT record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  GUID        ImageEffects identifier.
    \param  Size        Bytes in Data.
    \param  Data        "Serialized image effects parameter block".  One of the ImageEffects objects.

    EMF+ manual 2.3.5.2, Microsoft name: EmfPlusSerializableObject Record, Index 0x38

    Caller must check Data for possible memory access violations.
*/
int U_PMR_SERIALIZABLEOBJECT_get(const char *contents, U_PMF_CMN_HDR *Header,
      uint8_t *GUID, uint32_t *Size,
      const char **Data){
   if(!contents || !GUID || !Size || !Data){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SERIALIZABLEOBJECT))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_SERIAL_get(&contents, GUID, 1, 16, U_XE);
   U_PMF_SERIAL_get(&contents, Size, 4, 1,  U_LE);
   U_PMF_PTRSAV_SHIFT(Data, &contents, 0);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETANTIALIASMODE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  SMenum      SmoothingMode enumeration
    \param  aatype      Set: anti-aliasing on; Clear: anti-aliasing off

    EMF+ manual 2.3.6.1, Microsoft name: EmfPlusSetAntiAliasMode Record, Index 0x1E
*/
int U_PMR_SETANTIALIASMODE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *SMenum, int *aatype){
   if(!contents || !SMenum || !aatype){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETANTIALIASMODE))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *aatype = (lclHeader.Flags & U_PPF_AA ? 1 : 0 );
   *SMenum = (lclHeader.Flags >> U_FF_SHFT_AA) & U_FF_MASK_AA;
   return(1);
}

/**
    \brief Get data from a U_PMR_SETCOMPOSITINGMODE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  CMenum      CompositingMode enumeration

    EMF+ manual 2.3.6.2, Microsoft name: EmfPlusSetCompositingMode Record, Index 0x23
*/
int U_PMR_SETCOMPOSITINGMODE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *CMenum){
   /* memory access safe, only uses the common header */
   if(!contents || !CMenum){ return(0); }
   uint16_t Flags = U_PMF_HEADERFLAGS_get(contents);
   *CMenum  = (Flags >> U_FF_SHFT_CM) & U_FF_MASK_CM;
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETCOMPOSITINGQUALITY record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  CQenum      CompositingQuality enumeration

    EMF+ manual 2.3.6.3, Microsoft name: EmfPlusSetCompositingQuality Record, Index 0x24
*/
int U_PMR_SETCOMPOSITINGQUALITY_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *CQenum){
   /* memory access safe, only uses the common header */
   if(!contents || !CQenum){ return(0); }
   uint16_t Flags = U_PMF_HEADERFLAGS_get(contents);
   *CQenum  = (Flags >> U_FF_SHFT_CQ) & U_FF_MASK_CQ;
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETINTERPOLATIONMODE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  IMenum      InterpolationMode enumeration

    EMF+ manual 2.3.6.4, Microsoft name: EmfPlusSetInterpolationMode Record, Index 0x21
*/
int U_PMR_SETINTERPOLATIONMODE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *IMenum){
   /* memory access safe, only uses the common header */
   if(!contents || !IMenum){ return(0); }
   uint16_t Flags = U_PMF_HEADERFLAGS_get(contents);
   *IMenum  = (Flags >> U_FF_SHFT_IM) & U_FF_MASK_IM;
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETPIXELOFFSETMODE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  POMenum     PixelOffsetMode enumeration.

    EMF+ manual 2.3.6.5, Microsoft name: EmfPlusSetPixelOffsetMode Record, Index 0x22
*/
int U_PMR_SETPIXELOFFSETMODE_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *POMenum){
   /* memory access safe, only uses the common header */
   if(!contents || !POMenum){ return(0); }
   uint16_t Flags = U_PMF_HEADERFLAGS_get(contents);
   *POMenum  = (Flags >> U_FF_SHFT_PxOffM) & U_FF_MASK_PxOffM;
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETRENDERINGORIGIN record
    \return 1 on success, 0 on error
    \param  contents     Record from which to extract data
    \param  Header       Common header.
    \param  X            X coordinate of rendering origin.
    \param  Y            Y coordinate of rendering origin.

    EMF+ manual 2.3.6.6, Microsoft name: EmfPlusSetRenderingOrigin Record, Index 0x1D
*/
int U_PMR_SETRENDERINGORIGIN_get(const char *contents, U_PMF_CMN_HDR *Header,
      int32_t *X, int32_t *Y){
   if(!contents || !X || !Y){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   if(!U_PMF_CMN_HDR_get(&contents, &lclHeader))return(0);
   if(lclHeader.Size < sizeof(U_PMF_SETRENDERINGORIGIN))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_SERIAL_get(&contents, X, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Y, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETTEXTCONTRAST record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header.
    \param  TGC         Text Gamma correction value (x 1000).

    EMF+ manual 2.3.6.7, Microsoft name: EmfPlusSetTextContrast Record, Index 0x20
*/
int U_PMR_SETTEXTCONTRAST_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *TGC){
   /* memory access safe, only uses the common header */
   if(!contents || !TGC){ return(0); }
   uint16_t Flags = U_PMF_HEADERFLAGS_get(contents);
   *TGC  = (Flags >> U_FF_SHFT_TGC) & U_FF_MASK_TGC;
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETTEXTRENDERINGHINT record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header.
    \param  TRHenum     TextRenderingHint enumeration

    EMF+ manual 2.3.6.8, Microsoft name: EmfPlusSetTextRenderingHint Record, Index 0x1F
*/
int U_PMR_SETTEXTRENDERINGHINT_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *TRHenum){
   /* memory access safe, only uses the common header */
   if(!contents || !TRHenum){ return(0); }
   uint16_t Flags = U_PMF_HEADERFLAGS_get(contents);
   *TRHenum  = (Flags >> U_FF_SHFT_TRH) & U_FF_MASK_TRH;
   U_PMF_CMN_HDR_get(&contents, Header);
   return(1);
}

/**
    \brief Get data from a U_PMR_BEGINCONTAINER record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  UTenum      UnitType enumeration
    \param  DstRect     with SrcRect specifies a transformation
    \param  SrcRect     with DstRect specifies a transformation
    \param  StackID     EMF+ Object Stack Index to use for this graphics container

    EMF+ manual 2.3.7.1, Microsoft name: EmfPlusBeginContainer Record, Index 0x27
*/
int U_PMR_BEGINCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *UTenum,
      U_PMF_RECTF *DstRect, U_PMF_RECTF *SrcRect, uint32_t *StackID){
   if(!contents || !UTenum || !DstRect || !SrcRect || !StackID){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETCLIPREGION))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *UTenum  = (lclHeader.Flags >> U_FF_SHFT_UT) & U_FF_MASK_UT;
   U_PMF_SERIAL_get(&contents, DstRect, 4, 4, U_LE);
   U_PMF_SERIAL_get(&contents, SrcRect, 4, 4, U_LE);
   U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_BEGINCONTAINERNOPARAMS record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  StackID     EMF+ Object Stack Index to use for this graphics container

    EMF+ manual 2.3.7.2, Microsoft name: EmfPlusBeginContainerNoParams Record, Index 0x28
*/
int U_PMR_BEGINCONTAINERNOPARAMS_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){
   return(U_PMR_common_stack_get(contents, Header, StackID));
}

/**
    \brief Get data from a U_PMR_ENDCONTAINER record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  StackID     EMF+ Object Stack Index of this graphics container

    EMF+ manual 2.3.7.3, Microsoft name: EmfPlusEndContainer Record, Index 0x29
*/
int U_PMR_ENDCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){
   return(U_PMR_common_stack_get(contents, Header, StackID));
}

/**
    \brief Get data from a U_PMR_RESTORE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  StackID     State (level) to restore from the EMF+ Graphics Stack. Must have been put on the GS with a U_PMR_SAVE.

    EMF+ manual 2.3.7.4, Microsoft name: EmfPlusRestore Record, Index 0x26
*/
int U_PMR_RESTORE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){
    return(U_PMR_common_stack_get(contents, Header, StackID));
}

/**
    \brief Get data from a U_PMR_SAVE record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  StackID     State (level) to save.on the EMF+ Graphics Stack

    EMF+ manual 2.3.7.5, Microsoft name: EmfPlusSave Record, Index 0x25
*/
int U_PMR_SAVE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){
   return(U_PMR_common_stack_get(contents, Header, StackID));
}

/**
    \brief Get data from a U_PMR_SETTSCLIP record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  ctype       Set: int16_t coordinates; Clear: U_FLOAT coordinates
    \param  Elements    Number of members in Data.
    \param  Rects       Caller must free.  Array of rectangles to draw.  Coordinate type set by ctype.

    EMF+ manual 2.3.8.1, Microsoft name: EmfPlusSetTSClip Record, Index 0x3A 
*/
int U_PMR_SETTSCLIP_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *ctype, uint32_t *Elements,
      U_PMF_RECTF **Rects){
   if(!contents || !ctype || !Elements || !Rects){ return(0); }

   const char *blimit = contents;
   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETTSCLIP))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }
   blimit += lclHeader.Size;

   *ctype     = (lclHeader.Flags & U_PPF_K ? 1 : 0 );
   *Elements  = (lclHeader.Flags >> U_FF_SHFT_TSC) & U_FF_MASK_TSC;
   U_PMF_VARRECTS_get(&contents, lclHeader.Flags, *Elements, Rects, blimit);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETTSGRAPHICS record
    \return 1 on success, 0 on error
    \param  contents             Record from which to extract data
    \param  Header               Common header
    \param  vgatype              Set: Palette is VGA basic colors; Clear: Palette is ???
    \param  pptype               Set: Palette is present; Clear: Palette is absent.
    \param  AntiAliasMode        SmoothingMode enumeration
    \param  TextRenderHint       TextRenderingHint enumeration
    \param  CompositingMode      CompositingMode enumeration
    \param  CompositingQuality   CompositingQuality enumeration
    \param  RenderOriginX        Origin X for halftoning and dithering
    \param  RenderOriginY        Origin Y for halftoning and dithering
    \param  TextContrast         Gamma correction, range 0 to 12
    \param  FilterType           FilterType enumeraton
    \param  PixelOffset          PixelOffsetMode enumeration
    \param  WorldToDevice        world to device transform
    \param  Data                 Palette (optional)

    EMF+ manual 2.3.8.2, Microsoft name: EmfPlusSetTSGraphics Record, Index 0x39 

    Caller must check Data for possible memory access violations.
*/
int U_PMR_SETTSGRAPHICS_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *vgatype, int *pptype,
      uint8_t *AntiAliasMode, uint8_t *TextRenderHint, uint8_t  *CompositingMode, uint8_t *CompositingQuality,
      int16_t *RenderOriginX, int16_t *RenderOriginY,  uint16_t *TextContrast,    uint8_t *FilterType,
      uint8_t *PixelOffset,   U_PMF_TRANSFORMMATRIX *WorldToDevice,
      const char **Data){
   if(!contents || !vgatype || !pptype ||
      !AntiAliasMode || !TextRenderHint || !CompositingMode || !CompositingQuality ||
      !RenderOriginX || !RenderOriginY ||  !TextContrast ||    !FilterType ||
      !PixelOffset ||   !WorldToDevice || !Data){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETTSGRAPHICS))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *vgatype     = (lclHeader.Flags & U_PPF_VGA ? 1 : 0 );
   *pptype      = (lclHeader.Flags & U_PPF_PP  ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, AntiAliasMode,      1, 1, U_XE);
   U_PMF_SERIAL_get(&contents, TextRenderHint,     1, 1, U_XE);
   U_PMF_SERIAL_get(&contents, CompositingMode,    1, 1, U_XE);
   U_PMF_SERIAL_get(&contents, CompositingQuality, 1, 1, U_XE);
   U_PMF_SERIAL_get(&contents, RenderOriginX,      2, 1, U_LE);
   U_PMF_SERIAL_get(&contents, RenderOriginY,      2, 1, U_LE);
   U_PMF_SERIAL_get(&contents, TextContrast,       2, 1, U_LE);
   U_PMF_SERIAL_get(&contents, FilterType,         1, 1, U_XE);
   U_PMF_SERIAL_get(&contents, WorldToDevice,      4, 6, U_LE);
   U_PMF_PTRSAV_COND(Data, contents, *pptype);
   return(1);
}

/**
    \brief Get data from a U_PMR_MULTIPLYWORLDTRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  Matrix      Transformation matrix

    EMF+ manual 2.3.9.1, Microsoft name: EmfPlusMultiplyWorldTransform Record, Index 0x2C 
*/
int U_PMR_MULTIPLYWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *xmtype,
      U_PMF_TRANSFORMMATRIX *Matrix){
   if(!contents || !xmtype || !Matrix){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_MULTIPLYWORLDTRANSFORM))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *xmtype     = (lclHeader.Flags & U_PPF_XM ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_RESETWORLDTRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header

    EMF+ manual 2.3.9.2, Microsoft name: EmfPlusResetWorldTransform Record, Index 0x2B
*/
int U_PMR_RESETWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header){
   return( U_PMR_common_header_get(contents,Header));
}

/**
    \brief Get data from a U_PMR_ROTATEWORLDTRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  Angle       Rotation angle, in degrees

    EMF+ manual 2.3.9.3, Microsoft name: EmfPlusRotateWorldTransform Record, Index 0x2F
*/
int U_PMR_ROTATEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *xmtype,
      U_FLOAT *Angle){
   if(!contents || !xmtype || !Angle){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_ROTATEWORLDTRANSFORM))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *xmtype     = (lclHeader.Flags & U_PPF_XM ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, Angle, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_SCALEWORLDTRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  xmtype      Set: Post multiply; Clear: Pre multiply.
    \param  Sx          X scale factor.
    \param  Sy          Y scale factor.

    EMF+ manual 2.3.9.4, Microsoft name: EmfPlusScaleWorldTransform Record, Index 0x2E
*/
int U_PMR_SCALEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *xmtype,
      U_FLOAT *Sx, U_FLOAT *Sy){
   if(!contents || !xmtype || !Sx || !Sy){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SCALEWORLDTRANSFORM))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *xmtype     = (lclHeader.Flags & U_PPF_XM ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, Sx, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Sy, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETPAGETRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  PUenum      Page Unit, UnitType enumeration
    \param  Scale       Scale factor to convert page space to device space

    EMF+ manual 2.3.9.5, Microsoft name: EmfPlusSetPageTransform Record, Index 0x30
*/
int U_PMR_SETPAGETRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *PUenum,
      U_FLOAT *Scale){
   if(!contents || !PUenum || !Scale){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETPAGETRANSFORM))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *PUenum  = (lclHeader.Flags >> U_FF_SHFT_PU) & U_FF_MASK_PU;
   U_PMF_SERIAL_get(&contents, Scale, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_SETWORLDTRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  Matrix      Transformation matrix

    EMF+ manual 2.3.9.6, Microsoft name: EmfPlusSetWorldTransform Record, Index 0x2A
*/
int U_PMR_SETWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header,
      U_PMF_TRANSFORMMATRIX *Matrix){
   if(!contents || !Matrix){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_SETWORLDTRANSFORM))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_TRANSLATEWORLDTRANSFORM record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header
    \param  xmtype      Set: Post multiply; Clear: Pre multiply
    \param  Dx          X offset
    \param  Dy          Y offset

    EMF+ manual 2.3.9.7, Microsoft name: EmfPlusTranslateWorldTransform Record, Index 0x2D
*/
int U_PMR_TRANSLATEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header,
      int *xmtype,
      U_FLOAT *Dx, U_FLOAT *Dy){
   if(!contents || !xmtype || !Dx || !Dy){ return(0); }

   U_PMF_CMN_HDR lclHeader;
   U_PMF_CMN_HDR_get(&contents, &lclHeader);
   if(lclHeader.Size < sizeof(U_PMF_TRANSLATEWORLDTRANSFORM))return(0);
   if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); }

   *xmtype     = (lclHeader.Flags & U_PPF_XM ? 1 : 0 );
   U_PMF_SERIAL_get(&contents, Dx, 4, 1, U_LE);
   U_PMF_SERIAL_get(&contents, Dy, 4, 1, U_LE);
   return(1);
}

/**
    \brief Get data from a U_PMR_STROKEFILLPATH record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header  (ignore flags)
    

    EMF+ manual mentioned in 2.1.1.1, not otherwise documented, Microsoft name: EmfPlusStrokeFillPath Record, Index 0x37
    
    "This record closes any open figures in a path, strokes the outline of 
     the path by using the current pen, and fills its interior by using the current brush."
*/
int U_PMR_STROKEFILLPATH_get(const char *contents, U_PMF_CMN_HDR *Header){
   return( U_PMR_common_header_get(contents,Header));
}

/**
    \brief Get data from a U_PMR_MULTIFORMATSTART record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header  (ignore flags)

    EMF+ manual mentioned in 2.1.1.1, reserved, not otherwise documented, Microsoft name: EmfPlusMultiFormatStart Record, Index 0x05 
*/
int U_PMR_MULTIFORMATSTART_get(const char *contents, U_PMF_CMN_HDR *Header){
   return( U_PMR_common_header_get(contents,Header));
}

/**
    \brief Get data from a U_PMR_MULTIFORMATSECTION record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header  (ignore flags)

    EMF+ manual mentioned in 2.1.1.1, reserved, not otherwise documented, Microsoft name: EmfPlusMultiFormatSection Record, Index 0x06 
*/
int U_PMR_MULTIFORMATSECTION_get(const char *contents, U_PMF_CMN_HDR *Header){
   return( U_PMR_common_header_get(contents,Header));
}

/**
    \brief Get data from a U_PMR_MULTIFORMATEND record
    \return 1 on success, 0 on error
    \param  contents    Record from which to extract data
    \param  Header      Common header  (ignore flags)

    EMF+ manual mentioned in 2.1.1.1, reserved, not otherwise documented, Microsoft name: EmfPlusMultiFormatEnd Record, Index 0x06
*/
int U_PMR_MULTIFORMATEND_get(const char *contents, U_PMF_CMN_HDR *Header){
   return( U_PMR_common_header_get(contents,Header));
}


#ifdef __cplusplus
}
#endif
