summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/libuemf/upmf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/libuemf/upmf.c')
-rw-r--r--src/3rdparty/libuemf/upmf.c8661
1 files changed, 8661 insertions, 0 deletions
diff --git a/src/3rdparty/libuemf/upmf.c b/src/3rdparty/libuemf/upmf.c
new file mode 100644
index 0000000..20a87ae
--- /dev/null
+++ b/src/3rdparty/libuemf/upmf.c
@@ -0,0 +1,8661 @@
+/**
+ @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++){
+ FSize = lptr->Units * lptr->Reps;
+ if(!FSize && lptr->Ptr)return(po);
+ Total += FSize;
+ }
+ 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