/** @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 #include #include #include #include #include #include #include // for INT_MAX, INT_MIN #include // for sin, cos, tan2, use U_ROUND() instead of roundf() #include /* for offsetof() macro */ #if 0 #include //Not actually used, looking for collisions #include //Not actually used, looking for collisions #include //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 U_PMR_MAX){ idx = 0; } static char *U_PMR_NAMES[U_PMR_MAX+1]={ "U_PMR_INVALID", "U_PMR_Header", "U_PMR_EndOfFile", "U_PMR_Comment", "U_PMR_GetDC", "U_PMR_MultiFormatStart", "U_PMR_MultiFormatSection", "U_PMR_MultiFormatEnd", "U_PMR_Object", "U_PMR_Clear", "U_PMR_FillRects", "U_PMR_DrawRects", "U_PMR_FillPolygon", "U_PMR_DrawLines", "U_PMR_FillEllipse", "U_PMR_DrawEllipse", "U_PMR_FillPie", "U_PMR_DrawPie", "U_PMR_DrawArc", "U_PMR_FillRegion", "U_PMR_FillPath", "U_PMR_DrawPath", "U_PMR_FillClosedCurve", "U_PMR_DrawClosedCurve", "U_PMR_DrawCurve", "U_PMR_DrawBeziers", "U_PMR_DrawImage", "U_PMR_DrawImagePoints", "U_PMR_DrawString", "U_PMR_SetRenderingOrigin", "U_PMR_SetAntiAliasMode", "U_PMR_SetTextRenderingHint", "U_PMR_SetTextContrast", "U_PMR_SetInterpolationMode", "U_PMR_SetPixelOffsetMode", "U_PMR_SetCompositingMode", "U_PMR_SetCompositingQuality", "U_PMR_Save", "U_PMR_Restore", "U_PMR_BeginContainer", "U_PMR_BeginContainerNoParams", "U_PMR_EndContainer", "U_PMR_SetWorldTransform", "U_PMR_ResetWorldTransform", "U_PMR_MultiplyWorldTransform", "U_PMR_TranslateWorldTransform", "U_PMR_ScaleWorldTransform", "U_PMR_RotateWorldTransform", "U_PMR_SetPageTransform", "U_PMR_ResetClip", "U_PMR_SetClipRect", "U_PMR_SetClipPath", "U_PMR_SetClipRegion", "U_PMR_OffsetClip", "U_PMR_DrawDriverstring", "U_PMR_StrokeFillPath", "U_PMR_SerializableObject", "U_PMR_SetTSGraphics", "U_PMR_SetTSClip" }; return(U_PMR_NAMES[idx]); } /** \brief Convert from PseudoObject OID to ObjectType enumeration. \returns OT value on success, 0 if no match \param OID PseudoObject OID (based on EMF+ manual chapter number. ) Only OTs that may be stored in the EMF+ object table are supported. */ int U_OID_To_OT(uint32_t OID){ int otype; if( OID==U_PMF_BRUSH_OID ){ otype = U_OT_Brush; } else if(OID==U_PMF_PEN_OID ){ otype = U_OT_Pen; } else if(OID==U_PMF_PATH_OID ){ otype = U_OT_Path; } else if(OID==U_PMF_REGION_OID ){ otype = U_OT_Region; } else if(OID==U_PMF_IMAGE_OID ){ otype = U_OT_Image; } else if(OID==U_PMF_FONT_OID ){ otype = U_OT_Font; } else if(OID==U_PMF_STRINGFORMAT_OID ){ otype = U_OT_StringFormat; } else if(OID==U_PMF_IMAGEATTRIBUTES_OID){ otype = U_OT_ImageAttributes; } else if(OID==U_PMF_CUSTOMLINECAP_OID ){ otype = U_OT_CustomLineCap; } else { otype = U_OT_Invalid; } return(otype); } /** \brief Convert from PseudoObject OID to BrushType enumeration. \returns BT value on success, -1 if no match \param OID PseudoObject OID (based on EMF+ manual chapter number. ) Only OIDs that map to BT's are supported. */ int U_OID_To_BT(uint32_t OID){ int otype; if( OID==U_PMF_HATCHBRUSHDATA_OID ){ otype = U_BT_HatchFill; } else if(OID==U_PMF_LINEARGRADIENTBRUSHDATA_OID ){ otype = U_BT_LinearGradient; } else if(OID==U_PMF_PATHGRADIENTBRUSHDATA_OID ){ otype = U_BT_PathGradient; } else if(OID==U_PMF_SOLIDBRUSHDATA_OID ){ otype = U_BT_SolidColor; } else if(OID==U_PMF_TEXTUREBRUSHDATA_OID ){ otype = U_BT_TextureFill; } else { otype = -1; } return(otype); } /** \brief Convert from PseudoObject OID to CustomLineCapDataType Enumeration. \returns BT value on success, -1 if no match \param OID PseudoObject OID (based on EMF+ manual chapter number. ) Only OIDs that map to CLCDT's are supported. */ int U_OID_To_CLCDT(uint32_t OID){ int otype; if( OID==U_PMF_CUSTOMLINECAPDATA_OID ){ otype = U_CLCDT_Default; } else if(OID==U_PMF_CUSTOMLINECAPARROWDATA_OID ){ otype = U_CLCDT_AdjustableArrow; } else { otype = -1; } return(otype); } /** \brief Convert from PseudoObject OID to ImageDataType Enumeration. \returns BT value on success, -1 if no match \param OID PseudoObject OID (based on EMF+ manual chapter number. ) Only OIDs that map to IDT's are supported. */ int U_OID_To_IDT(uint32_t OID){ int otype; if( OID==U_PMF_BITMAP_OID ){ otype = U_IDT_Bitmap; } else if(OID==U_PMF_METAFILE_OID ){ otype = U_IDT_Metafile; } else { otype = -1; } return(otype); } /** \brief Convert from PseudoObject OID to RegionNodeDataType Enumeration. \returns BT value on success, -1 if no match \param OID PseudoObject OID (based on EMF+ manual chapter number. ) Only OIDs that map to RNDT's are supported. */ int U_OID_To_RNDT(uint32_t OID){ int otype; if( OID==U_PMF_REGIONNODECHILDNODES_OID ){ otype = U_RNDT_Kids; } /* there are 5 types, which must be specified separately */ else if(OID==U_PMF_RECTF_OID ){ otype = U_RNDT_Rect; } else if(OID==U_PMF_REGIONNODEPATH_OID ){ otype = U_RNDT_Path; } else { otype = -1; } return(otype); } /** \brief Append data to an U_OBJ_ACCUM structure. \param oa pointer to the U_OBJ_ACCUM structure \param data data to add \param size bytes in data \param Type object type \param Id Object ID \returns 0 on success, !0 on error. -1 on Type change, -2 on Id change Safe to test for Id, Type changes by calling with size=0. */ int U_OA_append(U_OBJ_ACCUM *oa, const char *data, int size, int Type, int Id){ int tail; if(!oa)return(2); if(oa->used){ if(oa->Type != Type)return(-1); if(oa->Id != Id)return(-2); } tail = oa->used; if(oa->used + size >= oa->space){ oa->space += size; char *newaccum = (char *) realloc(oa->accum, oa->space); if(!newaccum){ oa->space -= size; /* put it back the way it was */ return(1); } oa->accum = newaccum; } memcpy(oa->accum + tail,data,size); oa->used += size; oa->Type = Type; oa->Id = Id; return(0); } /** \brief Clear an U_OBJ_ACCUM structure. Accumulated storage is retained. \param oa pointer to the U_OBJ_ACCUM structure \returns 0 on success, !0 on error. */ int U_OA_clear(U_OBJ_ACCUM *oa){ if(!oa)return(2); oa->used=0; /* Type and Id may be ignored as they are reset on the first append */ return(0); } /** \brief Release an U_OBJ_ACCUM structure. Accumulated storage is free'd. \param oa pointer to the U_OBJ_ACCUM structure \returns 0 on success, !0 on error. */ int U_OA_release(U_OBJ_ACCUM *oa){ if(!oa)return(2); oa->used=0; oa->space = 0; if(oa->accum)free(oa->accum); oa->accum=NULL; return(0); } /** \brief Create and set an U_PSEUDO_OBJ \returns pointer to the U_PSEUDO_OBJ, NULL on error \param Data Data to copy into the PseudoObject's data. If NULL, space is allocated, but is cleared instead of filled. \param Size Number of bytes to allocate for Data (may be >Use if padding is present) \param Use Number of data bytes in Data (whether or not Data is actually copied) \param Type Type numbers are from manual section: 1.2.3.47 -> 0x01020347 If Data is NULL and Size is 0 an empty PseudoObject is created. One byte of storage is allocated for Data, Size is set to 1, and Used to 0. If Data is NULL and Size is !0 a zero filled PseudoObject is created. If Data is !Null and Size is !0 a data filled PseudoObject is created. */ U_PSEUDO_OBJ *U_PO_create(char *Data, size_t Size, size_t Use, uint32_t Type){ if(Use>Size)return(NULL); size_t tSize = (Size ? Size : 1); U_PSEUDO_OBJ *po = (U_PSEUDO_OBJ *)malloc(sizeof(U_PSEUDO_OBJ)); if(po){ po->Data = malloc(tSize); if(po->Data){ po->Size = tSize; po->Used = Use; po->Type = Type; if(Data){ memcpy(po->Data, Data, Use); } /* if Use < Size uninitialized bytes will be present! */ else { memset(po->Data, 0, tSize); } } else { free(po); po=NULL; } } return(po); } /** \brief Append data to a U_PSEUDO_OBJ object and return it \returns pointer to the U_PSEUDO_OBJ object, NULL on error \param po PseudoObject to append to. Cannot be NULL. \param Data Data to copy into the PseudoObject's data. If NULL, space is allocated (if necessary) and cleared instead of filled. \param Size Number of data bytes in Data */ U_PSEUDO_OBJ *U_PO_append(U_PSEUDO_OBJ *po, const char *Data, size_t Size){ /* po cannot be NULL,as in U_PO_po_append(), because there would be no way to determine the TYPE of the resulting PO */ if(po){ if(!po->Data || po->Used + Size > po->Size){ po->Size = po->Used + Size; char *newData = realloc(po->Data, po->Size); if(!newData){ po->Size -= Size; /* put it back the way it was*/ po=NULL; /* skip the rest of the actions, does not affect po in caller */ } else { po->Data = newData; } } if(po){ /* po->Data ready to append new data */ if(Data){ memcpy(po->Data + po->Used, Data, Size); } else { memset(po->Data + po->Used, 0, Size); } po->Used += Size; } } return(po); } /** \brief Append data to a U_PSEUDO_OBJ object and return it \returns pointer to the U_PSEUDO_OBJ object, NULL on error \param po PseudoObject to append to. May be NULL. \param Src PseudoObject to append. \param StripE Set: leading Elements in Src->Data is not copied, Clear: it is copied. */ U_PSEUDO_OBJ *U_PO_po_append(U_PSEUDO_OBJ *po, U_PSEUDO_OBJ *Src, int StripE){ if(!Src){ return(NULL); } if((StripE && (Src->Used == 4)) || !Src->Used){ return(po); } /* appending nothing is not an error */ char *Data = Src->Data; size_t Size = Src->Used; /* append only what is used */ U_PSEUDO_OBJ *ipo = po; if(StripE){ Size -= 4; } if(!ipo){ ipo = U_PO_create(NULL, 0, 0, Src->Type); /* create an empty pseudoobject */ } if(ipo){ if(!ipo->Data || ipo->Used + Size > ipo->Size){ ipo->Size = ipo->Used + Size; char *newData = realloc(ipo->Data, ipo->Size); if(!newData){ if(ipo != po)U_PO_free(&ipo); } else { ipo->Data = newData; } } if(ipo){ if(Data){ if(StripE){ memcpy(ipo->Data + ipo->Used, Data + 4, Size); } /* Size is already 4 less, skip the leading Elements value */ else { memcpy(ipo->Data + ipo->Used, Data, Size); } /* copy everything */ } else { memset(ipo->Data + ipo->Used, 0, Size); } /* set everything */ ipo->Used += Size; } } return(ipo); } /** \brief Free an U_PSEUDO_OBJ structure. All associated memory is released. \param po Address of a pointer to the U_PSEUDO_OBJ structure, Pointer is set to NULL. \returns 1 on success, 0 on error. */ int U_PO_free(U_PSEUDO_OBJ **po){ if(!po)return(0); if(!*po)return(1); if((*po)->Data)free((*po)->Data); free(*po); *po=NULL; return(1); } /** \brief create a PseudoObject with data in the correct byte order for an EMF+ file. \returns The PseudoObject on success, NULL on error. \param Type the type of the PseudoObject that is created. Allowed values are in U_PID_Values. \param List an array of U_SERIAL_DESC structures containing the data to store. The U_PMF_SERIAL_set() function should not ever be called directly by end user code. Each U_SERIAL_DESC element in List consists of Data fields and a description of that data. List is terminated by the first U_SERIAL_DESC element having a TE value of U_XX. Data fields: an array of a basic type of Units bytes repeated Reps times with the target byte order described in TE. Ptrs: Address of the first byte of the data fields. Units: Number of bytes of in each data field unit Reps: Number of repeats of the unit in data fields. If a Ptr is NULL, and Units*Reps is not zero, then Units*Reps 0x00 bytes are stored. If a Ptr is NULL, and Units*Reps is zero, this U_SERIAL_DESC is ignored. if a Ptr is NOT NULL, and Units * Reps is not zero, then the data is stored in the indicated byte order. If a Ptr is NOT NULL, and Units or Reps is zero an error is signaled. TE: (Target Endian) the byte order in which to store each unit of a data field as defined in U_Endian. Byte swapping is only enabled when Units is 2 or 4. In addition to the byte order values U_XE, U_LE, and U_BE, and the array terminator U_XX, the value may also be U_RP. U_RP means there is only a single unit in the data fields, but it is to be copied to the target Reps times. That is, the data was passed in with a form of run length encoding. Creates an empty PseudoObject if all pointers are NULL and all sizes are zero. */ U_PSEUDO_OBJ *U_PMF_SERIAL_set(uint32_t Type, const U_SERIAL_DESC *List){ U_PSEUDO_OBJ *po=NULL; size_t Total=0; size_t FSize; char *cptr; char *hptr; const U_SERIAL_DESC *lptr; if(!List)return(NULL); for(lptr=List; lptr->TE != U_XX; lptr++){ if(!(lptr->Units * lptr->Reps) && lptr->Ptr)return(po); Total += lptr->Units * lptr->Reps; } po = U_PO_create(NULL, Total, Total, Type); if(po){ cptr = po->Data; for(lptr=List; lptr->TE != U_XX; lptr++){ FSize = lptr->Units * lptr->Reps; if(FSize){ /* Ptr is not NULL, that would have been detected already */ hptr = cptr; if(lptr->TE & U_RP){ U_PMF_REPCPY_DSTSHIFT(&cptr, lptr->Ptr, lptr->Units, lptr->Reps); } else { U_PMF_MEMCPY_DSTSHIFT(&cptr, lptr->Ptr, FSize); } if(((lptr->TE & U_LE) && U_IS_BE) || ((lptr->TE & U_BE) && U_IS_LE)){ if(lptr->Units==2){ U_swap2(hptr,lptr->Reps); } else if(lptr->Units==4){ U_swap4(hptr,lptr->Reps); } } } } } return(po); } /** \brief Create U_DPSEUDO_OBJ's for the Points and Types of a path \param Elements Number of elements in Points. May be zero, which creates an empty path. \param Points Array of U_PMF_POINTF values. \param First Apply to first point, unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration. Must have U_PPT_Start set. \param Others Apply to all other points, unsigned byte, lower 4 bits hold the PathPointType flag upper 4 bits hold the PathPointType enumeration. Must have U_PPT_Line or U_PPT_Bezier set. \returns pointer to the DU_PSEUDO_OBJ object, NULL on error */ U_DPSEUDO_OBJ *U_PATH_create(int Elements, const U_PMF_POINTF *Points, uint8_t First, uint8_t Others){ if(Elements){ if(!Points){ return(NULL); } if( (First & U_PPT_MASK) != U_PPT_Start ){ return(NULL); } if(!(Others & U_PPT_Bezier)){ return(NULL); } /* will pass if either line or bezier is set */ } U_DPSEUDO_OBJ *Path = (U_DPSEUDO_OBJ *)calloc(sizeof(U_DPSEUDO_OBJ),1); /* make poTypes and poPoints NULL */ const U_SERIAL_DESC List[] = { {NULL,0,0,U_XX} }; if(Path){ Path->Elements = Elements; Path->poPoints = U_PMF_SERIAL_set(U_PMF_RAW_OID, List); /* Empty PO to hold points as raw data */ if(!Elements){ Path->poTypes = U_PMF_SERIAL_set(U_PMF_RAW_OID, List); /* Empty PO to hold types as raw data */ } else { Path->poPoints = U_PO_append(Path->poPoints, (char *)Points, Elements*sizeof(U_PMF_POINTF)); if(Path->poPoints){ U_PSEUDO_OBJ *tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First | U_PPT_Start, Others); /* PO holding types, has leading Elements value */ Path->poTypes = U_PO_po_append(NULL, tpo, U_PMF_DROP_ELEMENTS); /* remove the leading Elements value*/ U_PO_free(&tpo); if(!Path->poTypes){ U_PO_free(&Path->poPoints); } } if(!Path->poPoints){ U_DPO_free(&Path); } } } return(Path); } /** \brief Free U_DPSEUDO_OBJ's \returns 1 on success, 0 on error. */ int U_DPO_free(U_DPSEUDO_OBJ **dpo){ if(!dpo){ return(0); } if(!*dpo){ return(1); } U_DPSEUDO_OBJ *kpo = *dpo; if(kpo->poPoints){ U_PO_free(&kpo->poPoints); } if(kpo->poTypes){ U_PO_free(&kpo->poTypes); } free(*dpo); *dpo=NULL; return(1); } /** \brief Clear U_DPSEUDO_OBJ's. Memory is retained, Elements and Used values are set to 0. \returns 1 on success, 0 on error. It is much more efficient to clear a DPO and reuse it than to free that DPO and create another. */ int U_DPO_clear(U_DPSEUDO_OBJ *dpo){ if(!dpo){ return(0); } if(dpo->poPoints){ dpo->poPoints->Used = 0; } if(dpo->poTypes){ dpo->poTypes->Used = 0; } dpo->Elements = 0; return(1); } /** \brief Append a "moveto" point to a path \param Path Address of a DoublePseudoObject holding the path to append to. \param Point Point to move to. \param Flags Flags may be (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, U_PTP_CloseSubpath) \returns 1 on success, 0 on error. */ int U_PATH_moveto(U_DPSEUDO_OBJ *Path, U_PMF_POINTF Point, uint8_t Flags){ if(!Path){ return(0); } U_PSEUDO_OBJ *tpo; U_PSEUDO_OBJ *tpo2; uint8_t Type = (Flags & U_PTP_NotClose) | U_PPT_Start; tpo = U_PMF_POINTF_set(1, &Point); if(!tpo){ return(0); } tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poPoints = tpo2; tpo = U_PMF_PATHPOINTTYPE_set(1, &Type); if(!tpo){ return(0); } tpo2= U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poTypes = tpo2; Path->Elements++; return(1); } /** \brief Append a "lineto" point to a path \param Path Address of a DoublePseudoObject holding the path to append to. \param Point U_PMF_POINTF point to draw to. \param Flags Flags may be (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, U_PTP_CloseSubpath) \returns 1 on success, 0 on error. */ int U_PATH_lineto(U_DPSEUDO_OBJ *Path, U_PMF_POINTF Point, uint8_t Flags){ if(!Path || !Path->Elements){ return(0); } /* must be at least one point to extend from */ if(Path->poTypes->Data[Path->Elements - 1] & U_PTP_CloseSubpath){ return(0); } /* cannot extend a closed subpath */ U_PSEUDO_OBJ *tpo; U_PSEUDO_OBJ *tpo2; uint8_t Type = (Flags & U_PTP_NotClose) | U_PPT_Line; tpo = U_PMF_POINTF_set(1, &Point); if(!tpo){ return(0); } tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poPoints = tpo2; tpo = U_PMF_PATHPOINTTYPE_set(1, &Type); if(!tpo){ return(0); } tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poTypes = tpo2; Path->Elements++; return(1); } /** \brief Set the closepath bit in the last point \param Path Address of a DoublePseudoObject holding the path to act upon. \returns 1 on success, 0 on error. */ int U_PATH_closepath(U_DPSEUDO_OBJ *Path){ if(!Path || !Path->poTypes){ return(0); } uint32_t Elements = Path->Elements; uint8_t *Type = (uint8_t *)(Path->poTypes->Data) + Elements - 1; if(*Type & U_PPT_Start){ return(0); } /* single point closed path makes no sense */ *Type = *Type | U_PTP_CloseSubpath; return(1); } /** \brief Append a "polylineto" set of point to a path \param Path Address of a DoublePseudoObject holding the path to append to. \param Elements number of Points and Flags \param Points Line points. \param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath) \param StartSeg If set, use U_PPT_Start PathPointType enumeration for first point, otherwise use U_PPT_Line. \returns 1 on success, 0 on error. */ int U_PATH_polylineto(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags, uint8_t StartSeg){ if(!Path || !Points){ return(0); } if(!Elements){ return(1); } /* harmless - do nothing */ U_PSEUDO_OBJ *tpo; U_PSEUDO_OBJ *tpo2; uint8_t First, Others; tpo = U_PMF_POINTF_set(Elements, Points); tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poPoints = tpo2; if(StartSeg){ First = (Flags & U_PTP_NotClose) | U_PPT_Start; } else { First = (Flags & U_PTP_NotClose) | U_PPT_Line; } Others = (Flags & U_PTP_NotClose) | U_PPT_Line; tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First, Others); if(!tpo){ return(0); } tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poTypes = tpo2; Path->Elements += Elements; return(1); } /** \brief Append a "polybezierto" set of point to a path \param Path Address of a DoublePseudoObject holding the path to append to. \param Elements number of Points \param Points Bezier points. Optional starting point, then N sets of 3, example: [P1] (Q12A Q12B P2) (Q23A Q23B P3). \param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath) \param StartSeg If set, use U_PPT_Start PathPointType enumeration for first point, otherwise use U_PPT_Bezier. \returns 1 on success, 0 on error. If Start is set Elements must be 1 + multiple of 3. Ie, P1 Q12A Q12B P2 Q23A Q23B P3 If Start is clear Elements must be a multiple of 3. Ie, (P1, already in path) Q12A Q12B P2 Q23A Q23B P3 */ int U_PATH_polybezierto(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags, uint8_t StartSeg){ if(!Path || !Points){ return(0); } if(!Elements){ if(StartSeg){ return(0); } /* cannot have both a NEW segment and ZERO points */ else{ return(1); } /* harmless - do nothing */ } if(StartSeg && ((Elements - 1) % 3)){ return(0); } /* new segment must be 1 + N*3 points */ if(!StartSeg && (Elements % 3)){ return(0); } /* extend segment must be N*3 points */ U_PSEUDO_OBJ *tpo; U_PSEUDO_OBJ *tpo2; uint8_t First, Others; tpo = U_PMF_POINTF_set(Elements, Points); tpo2 = U_PO_po_append(Path->poPoints, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poPoints = tpo2; if(StartSeg){ First = (Flags & U_PTP_NotClose) | U_PPT_Start; } else { First = (Flags & U_PTP_NotClose) | U_PPT_Bezier; } Others = (Flags & U_PTP_NotClose) | U_PPT_Bezier; tpo = U_PMF_PATHPOINTTYPE_set2(Elements, First, Others); if(!tpo){ return(0); } tpo2 = U_PO_po_append(Path->poTypes, tpo, U_PMF_DROP_ELEMENTS); U_PO_free(&tpo); if(!tpo2)return(0); Path->poTypes = tpo2; Path->Elements += Elements; return(1); } /** \brief Append a "polygon" set of points to a path. \param Path Address of a DoublePseudoObject holding the path to append to. \param Elements number of Points and Flags \param Points Line points. \param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath) \returns 1 on success, 0 on error. */ int U_PATH_polygon(U_DPSEUDO_OBJ *Path, uint32_t Elements, const U_PMF_POINTF *Points, uint8_t Flags){ int status = U_PATH_polylineto(Path, Elements, Points, Flags, U_SEG_NEW); if(status){ status = U_PATH_closepath(Path); } return(status); } //! \cond // These are not exposed in the API /* Parameterized Ellipse coordinates */ U_PMF_POINTF U_eparam(U_FLOAT a, U_FLOAT b, U_PMF_POINTF *center, double Ang, double Theta){ U_PMF_POINTF point; point.X = center->X + a*cos(Theta)*cos(Ang) - b*sin(Theta)*sin(Ang); point.Y = center->Y + a*sin(Theta)*cos(Ang) + b*cos(Theta)*sin(Ang); return(point); } /* Parameterized Ellipse derivative */ U_PMF_POINTF U_eparam2(U_FLOAT a, U_FLOAT b, double Ang, double Theta){ U_PMF_POINTF point; point.X = -a*cos(Theta)*sin(Ang) - b*sin(Theta)*cos(Ang); point.Y = -a*sin(Theta)*sin(Ang) + b*cos(Theta)*cos(Ang); return(point); } double U_aparam(double Ang1, double Ang2){ double Alpha; double t2; t2 = tan((Ang2 - Ang1)/2.0); t2 *= t2; Alpha = sin(Ang2 - Ang1) * (sqrt(4 + 3*t2) -1.0)/3.0; return(Alpha); } /* Parameterized Bezier point Q1 or Q2 derivative */ U_PMF_POINTF U_qparam(double Alpha, double a, double b, U_PMF_POINTF *Point, double Ang, double Theta, int mode){ U_PMF_POINTF Q, Prime; Prime = U_eparam2(a,b,Ang,Theta); if(mode==1){ /* Q1, anything else is Q2*/ Q.X = Point->X + Alpha * Prime.X; Q.Y = Point->Y + Alpha * Prime.Y; } else { Q.X = Point->X - Alpha * Prime.X; Q.Y = Point->Y - Alpha * Prime.Y; } return(Q); } //! \endcond /** \brief Append an "arcto" set of points to a path (Bezier points are calculated, and these are appended \param Path Address of a pointer to the U_PSEUDO_OBJ that holds points \param Start Start angle, >=0.0, degrees clockwise from 3:00 \param Sweep Sweep angle, -360<= angle <=360, degrees clockwise from Start \param Rot Rotation angle to apply to coordinate system (Start and Rect), positive is degrees clockwise \param Rect U_PMF_RECTF that defines the bounding rectangle. \param Flags Flags (U_PTP_None, U_PTP_DashMode, U_PTP_PathMarker, U_PTP_NoBit, but NOT U_PTP_CloseSubpath) \param StartSeg If set, the arc starts a new segment, if clear, continue the existing segment. Starting a new segment does not automatically apply U_PATH_closepath to the existing path. \returns 1 on success, 0 on error. Based on Luc Maisonobe's work, http://www.spaceroots.org/documents/ellipse/ */ int U_PATH_arcto(U_DPSEUDO_OBJ *Path, U_FLOAT Start, U_FLOAT Sweep, U_FLOAT Rot, U_PMF_RECTF *Rect, uint8_t Flags, int StartSeg){ U_PMF_POINTF Bz[3]; U_PMF_POINTF Center; U_PMF_POINTF P1,P2; double a, b; int done = 0; int fpoint = 0; double L1, L2; /* These are degrees CounterClockwise from 3:00 */ double Ang1, Ang2; /* These are parametric angles, derived from L1, L2*/ double Alpha; /* Dimensionless number used for Q1, Q2 */ double Theta; /* Rot in radians */ double Slop; /* do not let rounding errors cause spurious end points */ if(!Path){ return(0); } if((Sweep > 360.0) || (Sweep < -360.0)){ return(0); } /* Start should be between 0 and 360 degrees, but it does not really matter because sin, and cos will accept anything */ /* the sign on Sweep and Start is correct bacause LM's derivation has y positive up, but GDI+ has y positive down. */ a = Rect->Width/2.0; b = Rect->Height/2.0; if(!a || !b){ return(0); } Center.X = Rect->X + a; Center.Y = Rect->Y + b; /* convert to radians */ Start = (2.0 * U_PI * Start)/360.0; Sweep = (2.0 * U_PI * Sweep)/360.0; Theta = (2.0 * U_PI * Rot )/360.0; Slop = Sweep/100000.0; L1 = Start; while(!done){ if(Sweep < 0){ L2 = L1 - U_PI/2.0; if(L2 <= Sweep + Start - Slop){ L2 = Sweep + Start; done = 1; } else {done = 0; } } else { L2 = L1 + U_PI/2.0; if(L2 >= Sweep + Start + Slop){ L2 = Sweep + Start; done = 1; } else {done = 0; } } Ang1 = atan2(sin(L1)/b, cos(L1)/a); Ang2 = atan2(sin(L2)/b, cos(L2)/a); Alpha = U_aparam(Ang1, Ang2); P1 = U_eparam(a, b, &Center, Ang1, Theta); /* P1 */ P2 = U_eparam(a, b, &Center, Ang2, Theta); /* P2 */ Bz[0] = U_qparam(Alpha, a, b, &P1, Ang1, Theta, 1); /* Q1 */ Bz[1] = U_qparam(Alpha, a, b, &P2, Ang2, Theta, 2); /* Q2 */ Bz[2] = P2; if(!fpoint){ if(StartSeg){ U_PATH_moveto(Path, P1, Flags); } else { U_PATH_lineto(Path, P1, Flags); } fpoint = 1; } U_PATH_polybezierto(Path, 3, Bz, Flags, U_SEG_OLD ); L1 = L2; } return(1); } /** \brief Allocate and construct an array of U_PMF_POINTF objects which have been subjected to a U_XFORM \returns pointer to an array of U_PMF_POINTF structures. \param points pointer to the source U_PMF_POINTF structures \param count number of members in points \param xform U_XFORM to apply */ U_PMF_POINTF *pointfs_transform(U_PMF_POINTF *points, int count, U_XFORM xform){ U_PMF_POINTF *newpts=NULL;; int i; float X,Y; newpts = (U_PMF_POINTF *) malloc(count * sizeof(U_PMF_POINTF)); if(newpts){ for(i=0; i 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;i1.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;iType != 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)<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)<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)<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)<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