#include "annotlib.h" #include "assert.h" #pragma hdrstop #if !defined(ARRAYSIZE) #define ARRAYSIZE(x) (sizeof((x))/sizeof((x)[0])) #endif // Private definitions of tag types and values // // Each entry in the annotation has a type // #define DEFAULTDATA 2 #define ANNOTMARK 5 #define MARKBLOCK 6 // Reserved names for named blocks. case sensitive static const char c_szAnoDat[] = "OiAnoDat"; static const char c_szFilNam[] = "OiFilNam"; static const char c_szDIB[] = "OiDIB"; static const char c_szGroup[] = "OiGroup"; static const char c_szIndex[] = "OiIndex"; static const char c_szAnText[] = "OiAnText"; static const char c_szHypLnk[] = "OiHypLnk"; static const char c_szDefaultGroup[] = "[Untitled]"; #define CBHEADER 8 // unused 4 bytes plus int size specifier #define CBDATATYPE 8 // type specifier plus data size #define CBNAMEDBLOCK 12 // name of block + sizeof block #define CBINDEX 10 // length of the index string #define CBBLOCKNAME 8 // length of the name of the named block static const SIZE_T c_cbDefaultData = 144; static const BYTE c_pDefaultData[] = { 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4f, 0x69, 0x55, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x5b, 0x55, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x64, 0x5d, 0x00, 0x00, 0x5b, 0x00, 0x55, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x64, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4f, 0x69, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x5b, 0x55, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x64, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4f, 0x69, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const BYTE c_pDefaultUGroup [] = { 0x4f, 0x69, 0x55, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x5b, 0x55, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x64, 0x5d, 0x00, 0x00, 0x5b, 0x00, 0x55, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x64, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const INT AnnotHeader[] = { 0, 1 }; void NormalizeRect (RECT *prect) { int nTemp; if (prect->left > prect->right) { nTemp = prect->left; prect->left = prect->right; prect->right = nTemp; } if (prect->top > prect->bottom) { nTemp = prect->top; prect->top = prect->bottom; prect->bottom = nTemp; } } static void RotateHelper(LPPOINT ppoint, int cSize, int nNewImageWidth, int nNewImageHeight, BOOL bClockwise) { int nNewX, nNewY; for(int i=0;iRender (hdc); } } } CAnnotation* CAnnotationSet::GetAnnotation(INT_PTR nIndex) { if(_dpaMarks == NULL) return NULL; if (nIndex >= 0 && nIndex < DPA_GetPtrCount(_dpaMarks)) { CAnnotation *pCur; pCur = (CAnnotation *)DPA_GetPtr(_dpaMarks, nIndex); return pCur; } return NULL; } BOOL CAnnotationSet::AddAnnotation(CAnnotation *pMark) { DPA_AppendPtr(_dpaMarks, pMark); return true; } BOOL CAnnotationSet::RemoveAnnotation(CAnnotation *pMark) { CAnnotation *pCur; if(_dpaMarks == NULL) return true; for (int i=0;iGetRawDataFormat(&guidFmt); _ClearMarkList(); if (ImageFormatTIFF == guidFmt) { _BuildMarkList(pimg); } } // // This function reassembles the in-file representation of the current // annotations and writes it to the IPropertyStorage // HRESULT CAnnotationSet::CommitAnnotations(IShellImageData * pSID) { HRESULT hr = E_OUTOFMEMORY; SIZE_T cbItem; CAnnotation *pItem; LPBYTE pData; if (NULL == _dpaMarks || DPA_GetPtrCount(_dpaMarks) == 0) { hr = _SaveAnnotationProperty(pSID, NULL, 0); return hr; } // // First, calculate the size of the buffer needed // Begin with the header and the size of the default data // SIZE_T cbBuffer = CBHEADER+_cbDefaultData; // // Now query the individual items' sizes // for (INT_PTR i=0;iGetBlob(cbItem, NULL, c_szDefaultGroup, NULL))) { // cbItem includes the named blocks of the item as well // as the ANNOTATIONMARK struct cbBuffer += CBDATATYPE + cbItem; } } } // // Allocate the buffer to hold the annotations // pData = new BYTE[cbBuffer]; if (pData) { LPBYTE pCur = pData; // // Copy in the header and the int size // CopyMemory(pCur, AnnotHeader, CBHEADER); pCur+=CBHEADER; // // Copy in the default data // CopyMemory(pCur, _pDefaultData, _cbDefaultData); pCur+=_cbDefaultData; // // Scan through the items again and have them copy in their data // for (INT_PTR i=0;iGetBlob(cbItem, pCur+CBDATATYPE, c_szDefaultGroup, szIndex))) { *(UNALIGNED UINT *)pCur = ANNOTMARK; // next item is an ANNOTATIONMARK *(UNALIGNED UINT *)(pCur+4) = sizeof(ANNOTATIONMARK); // size of the mark pCur+=CBDATATYPE + cbItem; } } } // // Now save the annotation blob as a property // hr = _SaveAnnotationProperty(pSID, pData, cbBuffer); } delete [] pData; return hr; } void CAnnotationSet::ClearAllMarks() { _ClearMarkList(); } // // _BuildMarkList reads the PROPVARIANT for tag 32932 from the image. // It walks through the data building a list of CAnnotation-derived objects // void CAnnotationSet::_BuildMarkList(IShellImageData * pSID) { if(!pSID) { return; } pSID->GetResolution(&_xDPI, &_yDPI); IPropertySetStorage * pss; if(SUCCEEDED(pSID->GetProperties(STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pss))) { IPropertyStorage * pstg; if(SUCCEEDED( pss->Open(FMTID_ImageProperties, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pstg))) { _dpaMarks = DPA_Create(16); PROPVARIANT pv = {0}; PROPSPEC ps; ps.propid = ANNOTATION_IMAGE_TAG; ps.ulKind = PRSPEC_PROPID; if(S_OK == pstg->ReadMultiple(1, &ps, &pv)) { if (pv.vt != VT_NULL && pv.vt != VT_EMPTY) { LPVOID pData = NULL; long lUBound; // // This property is a SAFEARRAY of bytes // assert(pv.vt ==(VT_UI1 | VT_ARRAY)); SafeArrayGetUBound(pv.parray, 1, &lUBound); SafeArrayAccessData(pv.parray, &pData); if(pData) { _BuildListFromData(pData, SafeArrayGetElemsize(pv.parray)*(lUBound+1)); } SafeArrayUnaccessData(pv.parray); } PropVariantClear (&pv); } pstg->Release(); } pss->Release(); } } // Given the raw annotation data, do set up and then call _BuildListFromData HRESULT CAnnotationSet::BuildAllMarksFromData(LPVOID pData, UINT cbSize, ULONG xDPI, ULONG yDPI) { // check for bad params if (!pData) { return E_INVALIDARG; } // First, clear out any old marks... _ClearMarkList(); // Set up DPI info _xDPI = xDPI; _yDPI = yDPI; // Create DPA if it doesn't exist if (!_dpaMarks) { _dpaMarks = DPA_Create(16); if (!_dpaMarks) { return E_OUTOFMEMORY; } } // build list of marks _BuildListFromData(pData,cbSize); return S_OK; } // Given the raw annotation data, swizzle it to in-memory CAnnotation objects // and add those object pointers to our list void CAnnotationSet::_BuildListFromData(LPVOID pData, UINT cbSize) { ANNOTATIONDESCRIPTOR *pDesc; LPBYTE pNextData =(LPBYTE)pData; LPBYTE pDefaultData; CAnnotation *pMark; if(!_dpaMarks) { return; } // Skip the 4 byte header pNextData += 4; // Make sure the int size is 32 bits if(!((UNALIGNED int *)*pNextData)) { return; } // skip the int size marker pNextData += 4; pDefaultData = pNextData; // skip the default data. It gets stored for future use, as it will be appended to all // new marks the user creates on this image. pNextData += _NamedBlockDataSize(2,pNextData,(LPBYTE)pData+cbSize); _cbDefaultData = (SIZE_T)(pNextData-pDefaultData); _pDefaultData = new BYTE[_cbDefaultData]; if(_pDefaultData) { CopyMemory(_pDefaultData, pDefaultData, _cbDefaultData); } // pNextData now points to the first mark in the data. do { // Create a descriptor from the raw mark data pDesc = _ReadMark(pNextData, &pNextData,(LPBYTE)pData+cbSize); if(pDesc) { // Now create a CAnnotation from the descriptor and add it to the list pMark = CAnnotation::CreateAnnotation(pDesc, _yDPI); if(pMark) { DPA_AppendPtr(_dpaMarks, pMark); } delete pDesc; } }while(pNextData &&(((LPBYTE)pData+cbSize) > pNextData) ); } #define CHECKEOD if(pCur>pEOD)return -1; INT CAnnotationSet::_NamedBlockDataSize(UINT uType, LPBYTE pData, LPBYTE pEOD) { LPBYTE pCur = pData; UINT cbSkip=0; while(pCur < pEOD && *(UNALIGNED UINT*)pCur == uType) { pCur+=4; CHECKEOD // skip type and size cbSkip +=8+*(UNALIGNED UINT*)pCur; pCur+=4; //skip name pCur+=8; CHECKEOD // skip size plus the actual data cbSkip+=*(UNALIGNED UINT*)pCur; pCur+=4+*(UNALIGNED UINT*)pCur; } return cbSkip; } ANNOTATIONDESCRIPTOR *CAnnotationSet::_ReadMark(LPBYTE pMark, LPBYTE *ppNext, LPBYTE pEOD) { assert(*(UNALIGNED UINT*)pMark == 5); LPBYTE pBegin; UINT cbMark; // size of the ANNOTATIONMARK in pMark UINT cbNamedBlocks= -1; // size of the named blocks in pMark UINT cbDesc = sizeof(UINT); // size of the ANNOTATIONDESCRIPTOR ANNOTATIONDESCRIPTOR *pDesc = NULL; *ppNext = NULL; if (pMark+8+sizeof(ANNOTATIONMARK)+sizeof(UINT) < pEOD) { // skip the type pMark+=4; //point pBegin at the ANNOTATIONMARK struct pBegin=pMark+4; cbMark = *(UNALIGNED UINT*)pMark; assert(cbMark == sizeof(ANNOTATIONMARK)); cbDesc+=cbMark; pMark+=4+cbMark; cbNamedBlocks = _NamedBlockDataSize(6, pMark, pEOD); } if (-1 != cbNamedBlocks) { cbDesc+=cbNamedBlocks; // Allocate the descriptor pDesc =(ANNOTATIONDESCRIPTOR *)new BYTE[cbDesc]; } if(pDesc) { UINT uOffset = 0; // populate the descriptor pDesc->cbSize = cbDesc; CopyMemory(&pDesc->mark, pBegin, sizeof(pDesc->mark)); // Set pBegin at the beginning of the named blocks and read them in pBegin+=cbMark; NAMEDBLOCK *pBlock =(NAMEDBLOCK*)(&pDesc->blocks); while(uOffset < cbNamedBlocks) { assert(*(UNALIGNED UINT*)(pBegin+uOffset) == 6); uOffset += 4; assert(*(UNALIGNED UINT*)(pBegin+uOffset) = 12); // name plus data size uOffset+=4; // Copy in the name of the block lstrcpynA(pBlock->szType,(LPCSTR)(pBegin+uOffset), ARRAYSIZE(pBlock->szType)); uOffset+=8; cbMark = *(UNALIGNED UINT*)(pBegin+uOffset); // Calculate the total size of the NAMEDBLOCK structure pBlock->cbSize = sizeof(pBlock->cbSize)+sizeof(pBlock->szType)+cbMark; uOffset+=4; CopyMemory(&pBlock->data,pBegin+uOffset, cbMark); uOffset+=cbMark; // move our block pointer to the next chunk pBlock =(NAMEDBLOCK*)((LPBYTE)pBlock+pBlock->cbSize); } *ppNext =(LPBYTE)(pBegin+cbNamedBlocks); } return pDesc; } void CAnnotationSet::_ClearMarkList() { if(_dpaMarks) { DPA_DestroyCallback(_dpaMarks, _FreeMarks, NULL); _dpaMarks = NULL; } if (_pDefaultData != c_pDefaultData) { delete[] _pDefaultData; } _pDefaultData = (LPBYTE)c_pDefaultData; _cbDefaultData = c_cbDefaultData; } int CALLBACK CAnnotationSet::_FreeMarks(LPVOID pMark, LPVOID pUnused) { delete (CAnnotation*)pMark; return 1; } HRESULT CAnnotationSet::_SaveAnnotationProperty(IShellImageData * pSID, LPBYTE pData, SIZE_T cbBuffer) { IPropertySetStorage * pss; HRESULT hr = pSID->GetProperties(STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &pss); if (SUCCEEDED(hr)) { IPropertyStorage * pstg; hr = pss->Open(FMTID_ImageProperties, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pstg); if (SUCCEEDED(hr)) { PROPVARIANT pv; static PROPSPEC ps = {PRSPEC_PROPID, ANNOTATION_IMAGE_TAG}; SAFEARRAYBOUND bound; bound.cElements = (ULONG)cbBuffer; bound.lLbound = 0; PropVariantInit(&pv); if (pData != NULL) { pv.vt = VT_UI1 | VT_ARRAY; pv.parray = SafeArrayCreate(VT_UI1,1,&bound); if (pv.parray) { LPVOID pBits; hr = SafeArrayAccessData(pv.parray, &pBits); if (SUCCEEDED(hr)) { CopyMemory(pBits, pData, cbBuffer); SafeArrayUnaccessData(pv.parray); if (S_OK != pstg->WriteMultiple(1, &ps, &pv, 1024)) { hr = E_FAIL; } } } else { hr = E_OUTOFMEMORY; } } else { pv.vt = VT_NULL; if (S_OK != pstg->WriteMultiple(1, &ps, &pv, 1024)) { hr = E_FAIL; } } PropVariantClear(&pv); pstg->Release(); } pss->Release(); } return hr; } CAnnotation::CAnnotation(ANNOTATIONDESCRIPTOR *pDescriptor) { NAMEDBLOCK *pb; CopyMemory(&_mark, &pDescriptor->mark, sizeof(_mark)); // every annotation read from the image should have a group name // and an index _szGroup = NULL; pb = _FindNamedBlock("OiGroup", pDescriptor); if(pb) { _szGroup = new char[pb->cbSize-sizeof(pb->szType)]; if(_szGroup) { lstrcpyA(_szGroup,(LPCSTR)(pb->data)); } } _pUGroup = (FILENAMEDBLOCK*)c_pDefaultUGroup; pb = _FindNamedBlock("OiUGroup", pDescriptor); if (pb) { _pUGroup = (FILENAMEDBLOCK*)new BYTE[pb->cbSize-1]; if (_pUGroup) { CopyMemory(_pUGroup->szType, pb->szType, ARRAYSIZE(_pUGroup->szType)); _pUGroup->cbSize = pb->cbSize-CBNAMEDBLOCK-1; CopyMemory(_pUGroup->data, pb->data, _pUGroup->cbSize); } else { _pUGroup = (FILENAMEDBLOCK*)c_pDefaultUGroup; } } } // return a blank annotation object CAnnotation *CAnnotation::CreateAnnotation(UINT type, ULONG uCreationScale) { ANNOTATIONDESCRIPTOR desc; ZeroMemory(&desc, sizeof(desc)); desc.cbSize = sizeof(desc.cbSize)+sizeof(desc.mark)+sizeof(desc.blocks); desc.mark.uType = type; // MSDN mentions this required permission value desc.mark.dwPermissions = 0x0ff83f; desc.mark.bVisible = 1; return CreateAnnotation(&desc, uCreationScale); } CAnnotation *CAnnotation::CreateAnnotation(ANNOTATIONDESCRIPTOR *pDescriptor, ULONG uCreationScale) { CAnnotation *pNew = NULL; switch(pDescriptor->mark.uType) { case MT_IMAGEEMBED: case MT_IMAGEREF: pNew = new CImageMark(pDescriptor, pDescriptor->mark.uType == MT_IMAGEEMBED); break; case MT_STRAIGHTLINE: case MT_FREEHANDLINE: pNew = new CLineMark(pDescriptor, pDescriptor->mark.uType == MT_FREEHANDLINE); break; case MT_FILLRECT: case MT_HOLLOWRECT: pNew = new CRectMark(pDescriptor); break; case MT_TYPEDTEXT: pNew = new CTypedTextMark(pDescriptor, uCreationScale); break; case MT_FILETEXT: pNew = new CFileTextMark(pDescriptor, uCreationScale); break; case MT_STAMP: pNew = new CTextStampMark(pDescriptor, uCreationScale); break; case MT_ATTACHANOTE: pNew = new CAttachNoteMark(pDescriptor, uCreationScale); break; default: break; } return pNew; } void CAnnotation::Resize(RECT rectNewSize) { _mark.lrBounds = rectNewSize; NormalizeRect(&_mark.lrBounds); } NAMEDBLOCK *CAnnotation::_FindNamedBlock(LPCSTR szName, ANNOTATIONDESCRIPTOR *pDescriptor) { NAMEDBLOCK *pCur; NAMEDBLOCK *pRet = NULL; UINT uOffset; LPBYTE pb =(LPBYTE)pDescriptor; uOffset = sizeof(pDescriptor->cbSize)+sizeof(pDescriptor->mark); while(!pRet && uOffset < pDescriptor->cbSize) { pCur =(NAMEDBLOCK*)(pb+uOffset); if(!lstrcmpA(pCur->szType, szName)) { pRet = pCur; } else { if (pCur->cbSize == 0) return NULL; uOffset+=pCur->cbSize; } } return pRet; } CAnnotation::~CAnnotation() { if(_szGroup) { delete _szGroup; } if (_pUGroup && _pUGroup != (FILENAMEDBLOCK*)c_pDefaultUGroup) { delete [] (BYTE*)_pUGroup; } } // GetBlob writes out the ANNOTATIONMARK plus the group and index blocks // It then queries the subclass through a virtual function to get // extra named blocks // HRESULT CAnnotation::GetBlob(SIZE_T &cbSize, LPBYTE pBuffer, LPCSTR szDefaultGroup, LPCSTR szNextIndex) { SIZE_T cbExtra = 0; HRESULT hr = S_OK; LPCSTR szGroup = _szGroup; if (szGroup == NULL) szGroup = szDefaultGroup; // add in the ANNOTATIONMARK cbSize = sizeof(_mark); // for the group and index, add in the cbSize += 2*(CBDATATYPE+CBNAMEDBLOCK); // add in the length of the group name cbSize += lstrlenA(szGroup)+1; // add in the size of the index string cbSize += CBINDEX; if (_pUGroup) { cbSize += CBDATATYPE+CBNAMEDBLOCK+_pUGroup->cbSize; } // Add in the size of any named blocks from the subclass _WriteBlocks(cbExtra, NULL); cbSize += cbExtra; if (pBuffer) { // now write the data CopyMemory (pBuffer, &_mark, sizeof(_mark)); pBuffer += sizeof(_mark); // write the mark-specific blocks before the group and index blocks if (cbExtra) { if (SUCCEEDED(_WriteBlocks(cbExtra, pBuffer))) { pBuffer+=cbExtra; } } // write the group and index blocks if (_pUGroup) { *(UNALIGNED UINT*)pBuffer = 6; *(UNALIGNED UINT*)(pBuffer + 4) = CBNAMEDBLOCK; CopyMemory(pBuffer+CBDATATYPE,_pUGroup, CBNAMEDBLOCK+_pUGroup->cbSize); pBuffer += CBDATATYPE + CBNAMEDBLOCK+_pUGroup->cbSize; } pBuffer += _WriteStringBlock(pBuffer, 6, c_szGroup, szGroup, lstrlenA(szGroup)+1); pBuffer += _WriteStringBlock(pBuffer, 6, c_szIndex, szNextIndex, CBINDEX); } return hr; } void CAnnotation::Rotate(int nNewImageWidth, int nNewImageHeight, BOOL bClockwise) { RECT rect = _mark.lrBounds; RotateHelper((LPPOINT)&rect, 2, nNewImageWidth, nNewImageHeight, bClockwise); NormalizeRect(&rect); _mark.lrBounds = rect; } void CAnnotation::GetFont(LOGFONTW& lfFont) { lfFont.lfHeight = _mark.lfFont.lfHeight; lfFont.lfWidth = _mark.lfFont.lfWidth; lfFont.lfEscapement = _mark.lfFont.lfEscapement; lfFont.lfOrientation = _mark.lfFont.lfOrientation; lfFont.lfWeight = _mark.lfFont.lfWeight; lfFont.lfItalic = _mark.lfFont.lfItalic; lfFont.lfUnderline = _mark.lfFont.lfUnderline; lfFont.lfStrikeOut = _mark.lfFont.lfStrikeOut; lfFont.lfCharSet = _mark.lfFont.lfCharSet; lfFont.lfOutPrecision = _mark.lfFont.lfOutPrecision; lfFont.lfClipPrecision = _mark.lfFont.lfClipPrecision; lfFont.lfQuality = _mark.lfFont.lfQuality; lfFont.lfPitchAndFamily = _mark.lfFont.lfPitchAndFamily; ::MultiByteToWideChar(CP_ACP, 0, _mark.lfFont.lfFaceName, LF_FACESIZE, lfFont.lfFaceName, LF_FACESIZE); } void CAnnotation::SetFont(LOGFONTW& lfFont) { _mark.lfFont.lfHeight = lfFont.lfHeight; _mark.lfFont.lfWidth = lfFont.lfWidth; _mark.lfFont.lfEscapement = lfFont.lfEscapement; _mark.lfFont.lfOrientation = lfFont.lfOrientation; _mark.lfFont.lfWeight = lfFont.lfWeight; _mark.lfFont.lfItalic = lfFont.lfItalic; _mark.lfFont.lfUnderline = lfFont.lfUnderline; _mark.lfFont.lfStrikeOut = lfFont.lfStrikeOut; _mark.lfFont.lfCharSet = lfFont.lfCharSet; _mark.lfFont.lfOutPrecision = lfFont.lfOutPrecision; _mark.lfFont.lfClipPrecision = lfFont.lfClipPrecision; _mark.lfFont.lfQuality = lfFont.lfQuality; _mark.lfFont.lfPitchAndFamily = lfFont.lfPitchAndFamily; ::WideCharToMultiByte(CP_ACP, 0, lfFont.lfFaceName, LF_FACESIZE, _mark.lfFont.lfFaceName, LF_FACESIZE, NULL, NULL); } SIZE_T CAnnotation::_WriteStringBlock(LPBYTE pBuffer, UINT uType, LPCSTR szName, LPCSTR szData, SIZE_T len) { if (pBuffer) { *(UNALIGNED UINT*)pBuffer = uType; *(UNALIGNED UINT*)(pBuffer + 4) = CBNAMEDBLOCK; lstrcpynA((LPSTR)(pBuffer + CBDATATYPE), szName, CBNAMEDBLOCK+1); // named block name *(UNALIGNED UINT*)(pBuffer + CBDATATYPE + 8) = (UINT)len; // the named block name isn't null terminated CopyMemory(pBuffer + CBDATATYPE + CBNAMEDBLOCK, szData, len); } return CBDATATYPE + CBNAMEDBLOCK + len; } SIZE_T CAnnotation::_WritePointsBlock(LPBYTE pBuffer, UINT uType, const POINT *ppts, int nPoints, int nMaxPoints) { UINT cbAnPoints = sizeof(int)+sizeof(int)+nPoints*sizeof(POINT); if (pBuffer) { *(UNALIGNED UINT *)pBuffer = uType; *(UNALIGNED UINT *)(pBuffer + 4) = CBNAMEDBLOCK; lstrcpynA((LPSTR)(pBuffer + CBDATATYPE), c_szAnoDat, CBNAMEDBLOCK+1); pBuffer += CBDATATYPE + 8; *(UNALIGNED UINT *)pBuffer = cbAnPoints; pBuffer+=4; // Write out the ANPOINTS equivalent *(UNALIGNED int*)pBuffer = nMaxPoints; *(UNALIGNED int*)(pBuffer+4) = nPoints; CopyMemory(pBuffer+8, ppts, nPoints*sizeof(POINT)); } return CBDATATYPE + CBNAMEDBLOCK + cbAnPoints; } SIZE_T CAnnotation::_WriteRotateBlock(LPBYTE pBuffer, UINT uType, const ANROTATE *pRotate) { if (pBuffer) { *(UNALIGNED UINT *)pBuffer = uType; *(UNALIGNED UINT *)(pBuffer + 4) = CBNAMEDBLOCK; lstrcpynA((LPSTR)(pBuffer + CBDATATYPE), c_szAnoDat, CBNAMEDBLOCK+1); *(UNALIGNED UINT *)(pBuffer + CBDATATYPE + 8) = sizeof(ANROTATE); CopyMemory(pBuffer + CBDATATYPE + CBNAMEDBLOCK, pRotate, sizeof(ANROTATE)); } return CBDATATYPE + CBNAMEDBLOCK + sizeof(ANROTATE); } SIZE_T CAnnotation::_WriteTextBlock(LPBYTE pBuffer, UINT uType, int nOrient, UINT uScale, LPCSTR szText, int nMaxLen) { LPCSTR pText = szText ? szText : ""; UINT cbString = min(lstrlenA(pText)+1, nMaxLen); UINT cbPrivData = sizeof(ANTEXTPRIVDATA)+cbString; if (pBuffer) { *(UNALIGNED UINT *)pBuffer = uType; *(UNALIGNED UINT *)(pBuffer + 4) = CBNAMEDBLOCK; lstrcpynA((LPSTR)(pBuffer + CBDATATYPE), c_szAnText, CBNAMEDBLOCK+1); *(UNALIGNED UINT *)(pBuffer + CBDATATYPE + 8) = cbPrivData; // write out the ANTEXTPRIVDATA equivalent pBuffer += CBDATATYPE + CBNAMEDBLOCK; *(UNALIGNED int*)pBuffer = nOrient; *(UNALIGNED UINT *)(pBuffer+4) = 1000; *(UNALIGNED UINT *)(pBuffer+8) = uScale; *(UNALIGNED UINT *)(pBuffer+12) = cbString; lstrcpynA((LPSTR)(pBuffer+16), pText, nMaxLen); } return CBDATATYPE + CBNAMEDBLOCK + cbPrivData; } SIZE_T CAnnotation::_WriteImageBlock(LPBYTE pBuffer, UINT uType, LPBYTE pDib, SIZE_T cbDib) { if (pBuffer) { *(UNALIGNED UINT *)pBuffer = uType; *(UNALIGNED UINT *)(pBuffer+4) = CBNAMEDBLOCK; lstrcpynA((LPSTR)(pBuffer + CBDATATYPE), c_szAnText, CBNAMEDBLOCK+1); /* REVIEW_SDK Now that I think about it, it might make sense to define a struct that could make this more clear. Something like: struct AnnoBlock { UINT uBlockType; UINT uBlockSize; CHAR sName[8]; // Not NULL terminated UINT uVariableDataSize; BYTE Data[]; }; */ *(UNALIGNED UINT *)(pBuffer + CBDATATYPE + 8) = (UINT)cbDib; CopyMemory(pBuffer + CBDATATYPE + CBNAMEDBLOCK, pDib, cbDib); } return CBDATATYPE + CBNAMEDBLOCK + cbDib; } CRectMark::CRectMark(ANNOTATIONDESCRIPTOR *pDescriptor) : CAnnotation(pDescriptor) { // rects have no named blocks to read } void CRectMark::Render(HDC hdc) { int nROP = R2_COPYPEN; if (_mark.bHighlighting) nROP = R2_MASKPEN; int nOldROP = ::SetROP2(hdc, nROP); HPEN hPen = NULL; HPEN hOldPen = NULL; HBRUSH hBrush = NULL; HBRUSH hOldBrush = NULL; if (_mark.uType == MT_HOLLOWRECT) { hPen = ::CreatePen(PS_INSIDEFRAME, max(1, _mark.uLineSize), RGB(_mark.rgbColor1.rgbRed, _mark.rgbColor1.rgbGreen, _mark.rgbColor1.rgbBlue)); if(hPen) hOldPen =(HPEN)::SelectObject(hdc, hPen); hOldBrush = (HBRUSH)::SelectObject(hdc, ::GetStockObject(NULL_BRUSH)); } else { hBrush = CreateSolidBrush(RGB(_mark.rgbColor1.rgbRed, _mark.rgbColor1.rgbGreen, _mark.rgbColor1.rgbBlue)); if (hBrush) hOldBrush = (HBRUSH)::SelectObject(hdc, hBrush); hOldPen =(HPEN)::SelectObject(hdc, GetStockObject(NULL_PEN)); } ::Rectangle(hdc, _mark.lrBounds.left, _mark.lrBounds.top, _mark.lrBounds.right, _mark.lrBounds.bottom); if (hOldPen) ::SelectObject(hdc, hOldPen); if (hOldBrush) ::SelectObject(hdc, hOldBrush); if (hPen) ::DeleteObject(hPen); if (hBrush) ::DeleteObject(hBrush); ::SetROP2(hdc, nOldROP); } CImageMark::CImageMark(ANNOTATIONDESCRIPTOR *pDescriptor, bool bEmbedded) : CAnnotation(pDescriptor), _hDibSection(NULL), _pDib(NULL) { ZeroMemory(&_rotation, sizeof(_rotation)); NAMEDBLOCK *pb = _FindNamedBlock(c_szAnoDat, pDescriptor); UINT cb; _cbDib = 0; _bRotate = false; if (pb) { CopyMemory(&_rotation, pb->data, sizeof(_rotation)); } pb= _FindNamedBlock(c_szFilNam, pDescriptor); if (pb) { cb = pb->cbSize-sizeof(pb->cbSize)-sizeof(pb->szType); _szFilename = new char[cb+1]; if (_szFilename) { lstrcpynA (_szFilename, (LPCSTR)(pb->data), cb+1); } } pb = _FindNamedBlock(c_szDIB, pDescriptor); if (pb) { assert (bEmbedded); cb = pb->cbSize-sizeof(pb->cbSize)-sizeof(pb->szType); _pDib = new BYTE[cb]; if (_pDib) { CopyMemory (_pDib, pb->data, cb); _cbDib = cb; } // what do we do if allocation fails? } // If an image has IoAnoDat, the structure is a rotation structure pb = _FindNamedBlock(c_szAnoDat, pDescriptor); if (pb) { assert(pb->cbSize-sizeof(pb->cbSize)-sizeof(pb->szType) == sizeof(_rotation)); _bRotate = true; CopyMemory(&_rotation, pb->data, sizeof(_rotation)); } } CImageMark::~CImageMark() { if (_pDib) { delete [] _pDib; } if (_szFilename) { delete [] _szFilename; } } HRESULT CImageMark::_WriteBlocks(SIZE_T &cbSize, LPBYTE pBuffer) { cbSize = 0; if (_szFilename) { cbSize += _WriteStringBlock(pBuffer, 6, c_szFilNam, _szFilename, lstrlenA(_szFilename)+1); } if (_pDib) { cbSize += _WriteImageBlock(pBuffer, 6, _pDib, _cbDib); } if (_bRotate) { cbSize += _WriteRotateBlock(pBuffer, 6, &_rotation); } return S_OK; } void CImageMark::Render(HDC hdc) { } CLineMark::CLineMark(ANNOTATIONDESCRIPTOR *pDescriptor, bool bFreehand) : CAnnotation(pDescriptor) { NAMEDBLOCK *pb=_FindNamedBlock(c_szAnoDat, pDescriptor); _points = NULL; _nPoints = 0; if (pb) { ANPOINTS *ppts = (ANPOINTS*)&pb->data; _iMaxPts = bFreehand ? ppts->nMaxPoints : 2; assert(_nPoints > 2?bFreehand:TRUE); _points = new POINT[_iMaxPts]; if (_points) { _nPoints = ppts->nPoints; CopyMemory (_points, &ppts->ptPoint, sizeof(POINT)*_nPoints); // each point is relative to the upper left cornder of _mark.lrBounds for (int i=0;i<_nPoints;i++) { _points[i].x += _mark.lrBounds.left; _points[i].y += _mark.lrBounds.top; } } } } CLineMark::~CLineMark() { if (_points) { delete [] _points; } } void CLineMark::SetPoints(POINT* pPoints, int cPoints) { assert(_mark.uType == MT_FREEHANDLINE); if (_points != NULL) delete[] _points; _points = pPoints; _nPoints = cPoints; _iMaxPts = _nPoints; RECT rect; rect.left = _points[0].x; rect.top = _points[0].y; rect.right = _points[0].x; rect.bottom= _points[0].y; for(int i = 1; i < _nPoints; i++) { if (rect.left > _points[i].x) rect.left = _points[i].x; else if (rect.right < _points[i].x) rect.right = _points[i].x; if (rect.top > _points[i].y) rect.top = _points[i].y; else if (rect.bottom < _points[i].y) rect.bottom = _points[i].y; } _mark.lrBounds = rect; } void CLineMark::Render(HDC hdc) { int nROP = R2_COPYPEN; if (_mark.bHighlighting) nROP = R2_MASKPEN; int nOldROP = ::SetROP2(hdc, nROP); HPEN hPen = NULL; HPEN hOldPen = NULL; hPen = ::CreatePen(PS_SOLID, max(1, _mark.uLineSize), RGB(_mark.rgbColor1.rgbRed, _mark.rgbColor1.rgbGreen, _mark.rgbColor1.rgbBlue)); if(hPen) hOldPen =(HPEN)::SelectObject(hdc, hPen); ::Polyline(hdc, _points, _nPoints); if (hOldPen) ::SelectObject(hdc, hOldPen); if (hPen) ::DeleteObject(hPen); ::SetROP2(hdc, nOldROP); } void CLineMark::GetRect(RECT &rect) { int nPadding = (_mark.uLineSize / 2) + 6; // one because LineTo is inclusive // one for rounding error on odd line widths // one for rounding error in scaling large files // and three more just so we don't have to tweak this again rect = _mark.lrBounds; InflateRect(&rect, nPadding , nPadding); } // Usually we are interested in the bounding rect of the line above // but if we are directly manipulating the line we need a way to get // to the unadjusted points (left, top) and (right, bottom) void CLineMark::GetPointsRect(RECT &rect) { if (_nPoints != 2) return; rect.top = _points[0].y; rect.left = _points[0].x; rect.bottom = _points[1].y; rect.right = _points[1].x; } void CLineMark::Move(SIZE sizeOffset) { _points[0].x += sizeOffset.cx; _points[0].y += sizeOffset.cy; RECT rect; rect.left = _points[0].x; rect.top = _points[0].y; rect.right = _points[0].x; rect.bottom = _points[0].y; for(int i = 1; i < _nPoints; i++) { _points[i].x += sizeOffset.cx; if (rect.left > _points[i].x) rect.left = _points[i].x; else if (rect.right < _points[i].x) rect.right = _points[i].x; _points[i].y += sizeOffset.cy; if (rect.top > _points[i].y) rect.top = _points[i].y; else if (rect.bottom < _points[i].y) rect.bottom = _points[i].y; } _mark.lrBounds = rect; } void CLineMark::Resize(RECT rectNewSize) { if ((_points == NULL) && (_mark.uType == MT_STRAIGHTLINE)) { _iMaxPts = _nPoints = 2; _points = new POINT[_iMaxPts]; } if ((_nPoints == 2) && (_points != NULL)) { _points[0].y = rectNewSize.top; _points[0].x = rectNewSize.left; _points[1].y = rectNewSize.bottom; _points[1].x = rectNewSize.right; _mark.lrBounds = rectNewSize; NormalizeRect(&_mark.lrBounds); } } void CLineMark::Rotate(int nNewImageWidth, int nNewImageHeight, BOOL bClockwise) { RotateHelper(_points, _nPoints, nNewImageWidth, nNewImageHeight, bClockwise); RECT rect; rect.left = _points[0].x; rect.top = _points[0].y; rect.right = _points[0].x; rect.bottom= _points[0].y; for(int i = 1; i < _nPoints; i++) { if (rect.left > _points[i].x) rect.left = _points[i].x; else if (rect.right < _points[i].x) rect.right = _points[i].x; if (rect.top > _points[i].y) rect.top = _points[i].y; else if (rect.bottom < _points[i].y) rect.bottom = _points[i].y; } _mark.lrBounds = rect; } HRESULT CLineMark::_WriteBlocks(SIZE_T &cbSize, LPBYTE pBuffer) { if (_points) { for (int i=0;i<_nPoints;i++) { _points[i].x -= _mark.lrBounds.left; _points[i].y -= _mark.lrBounds.top; } cbSize = _WritePointsBlock(pBuffer, 6, _points, _nPoints, _iMaxPts); for (int i=0;i<_nPoints;i++) { _points[i].x += _mark.lrBounds.left; _points[i].y += _mark.lrBounds.top; } } return S_OK; } CTextAnnotation::CTextAnnotation(ANNOTATIONDESCRIPTOR *pDescriptor, ULONG uCreationScale, UINT nMaxLen, bool bUseColor2 ) : CAnnotation(pDescriptor) { NAMEDBLOCK *pb = _FindNamedBlock(c_szAnText, pDescriptor); _nCurrentOrientation = 0; _uCreationScale = 0; _uAnoTextLength = 0; _szText = NULL; _nMaxText = nMaxLen; _bUseColor2 = bUseColor2; if (pb) { ANTEXTPRIVDATA *pData = (ANTEXTPRIVDATA*)&pb->data; _szText = new char[pData->uAnoTextLength+1]; if (_szText) { _nCurrentOrientation = pData->nCurrentOrientation; _uCreationScale = pData->uCreationScale; _uAnoTextLength = pData->uAnoTextLength; lstrcpynA (_szText, pData->szAnoText, _uAnoTextLength+1); } } if (_uCreationScale == 0) { _uCreationScale = 72000 / uCreationScale; } } CTextAnnotation::~CTextAnnotation() { if (_szText) { delete [] _szText; } } void CTextAnnotation::Render(HDC hdc) { COLORREF crOld; int nOldROP = ::SetROP2(hdc, R2_COPYPEN); if (_mark.uType == MT_ATTACHANOTE) { HPEN hOldPen =(HPEN)::SelectObject(hdc, GetStockObject(NULL_PEN)); HBRUSH hBrush = ::CreateSolidBrush(RGB(_mark.rgbColor1.rgbRed, _mark.rgbColor1.rgbGreen, _mark.rgbColor1.rgbBlue)); if (hBrush != NULL) { HBRUSH hOldBrush = (HBRUSH)::SelectObject(hdc, hBrush); ::Rectangle(hdc, _mark.lrBounds.left, _mark.lrBounds.top, _mark.lrBounds.right, _mark.lrBounds.bottom); ::SelectObject(hdc, hOldBrush); ::DeleteObject(hBrush); } ::SelectObject(hdc, hOldPen); crOld = ::SetTextColor(hdc, RGB(_mark.rgbColor2.rgbRed, _mark.rgbColor2.rgbGreen, _mark.rgbColor2.rgbBlue)); } else { crOld = ::SetTextColor(hdc, RGB(_mark.rgbColor1.rgbRed, _mark.rgbColor1.rgbGreen, _mark.rgbColor1.rgbBlue)); } int nOldBkMode = ::SetBkMode(hdc, TRANSPARENT); LOGFONT lf; GetFont(lf); lf.lfHeight = GetFontHeight(hdc); HFONT hFont = CreateFontIndirect(&lf); HFONT hOldFont = NULL; if (hFont != NULL) hOldFont = (HFONT)::SelectObject(hdc, hFont); BSTR bstrText = GetText(); // Handle angle of orientation in 1/10s of a degree if (_nCurrentOrientation != 0) { XFORM xForm; RECT rectSource = _mark.lrBounds; ::LPtoDP(hdc, (LPPOINT)&rectSource, 2); if (_nCurrentOrientation == 900) { xForm.eM11 = (FLOAT)0.0; xForm.eM12 = (FLOAT)-1.0; xForm.eM21 = (FLOAT)1.0; xForm.eM22 = (FLOAT)0.0; // Rotate Source (left, top) to (left, bottom) int nTmp = rectSource.bottom; rectSource.bottom = rectSource.top; rectSource.top = nTmp; } else if (_nCurrentOrientation == 1800) { xForm.eM11 = (FLOAT)-1.0; xForm.eM12 = (FLOAT)0.0; xForm.eM21 = (FLOAT)0.0; xForm.eM22 = (FLOAT)-1.0; // Rotate Source (left, top) to (right, bottom) int nTmp = rectSource.right; rectSource.right = rectSource.left; rectSource.left = nTmp; nTmp = rectSource.bottom; rectSource.bottom = rectSource.top; rectSource.top = nTmp; } else { xForm.eM11 = (FLOAT)0.0; xForm.eM12 = (FLOAT)1.0; xForm.eM21 = (FLOAT)-1.0; xForm.eM22 = (FLOAT)0.0; // Rotate Source (left, top) to (right, top) int nTmp = rectSource.right; rectSource.right = rectSource.left; rectSource.left = nTmp; } xForm.eDx = (FLOAT)0.0; xForm.eDy = (FLOAT)0.0; int nOldGraphicsMode = ::SetGraphicsMode(hdc, GM_ADVANCED); ::SetWorldTransform(hdc, &xForm); RECT rectTarget = rectSource; ::DPtoLP(hdc, (LPPOINT)&rectTarget, 2); ::DrawText(hdc, bstrText, -1, &rectTarget, DT_LEFT | DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK); ::ModifyWorldTransform(hdc, &xForm, MWT_IDENTITY); ::SetGraphicsMode(hdc, nOldGraphicsMode); } else { ::DrawText(hdc, bstrText, -1, &_mark.lrBounds, DT_LEFT | DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK); } if (hFont != NULL) { ::SelectObject(hdc, hOldFont); ::DeleteObject(hFont); } if (nOldBkMode != 0) ::SetBkMode(hdc, nOldBkMode); if (crOld != CLR_INVALID) ::SetTextColor (hdc, crOld); ::SetROP2(hdc, nOldROP); if (bstrText) { SysFreeString(bstrText); } } LONG CTextAnnotation::GetFontHeight(HDC hdc) { LONG lHeight = MulDiv(_mark.lfFont.lfHeight, 96, 72); //> REVIEW : This needs work, the 1000 below is rather random and should be fixed after Beta1 lHeight = MulDiv(lHeight, 1000, _uCreationScale); lHeight = max(lHeight, 2); return lHeight; } BSTR CTextAnnotation::GetText() { if (_szText == NULL) return NULL; int nLen = ::MultiByteToWideChar(CP_ACP, 0, _szText, -1, NULL, NULL); BSTR bstrResult = ::SysAllocStringLen(NULL, nLen); if (bstrResult != NULL) { ::MultiByteToWideChar(CP_ACP, 0, _szText, -1, bstrResult, nLen); } return bstrResult; } void CTextAnnotation::SetText(BSTR bstrText) { UINT nLen = ::WideCharToMultiByte(CP_ACP, 0, bstrText, -1, NULL, 0, NULL, NULL); if (nLen > _nMaxText) return; if (nLen > _uAnoTextLength) { if (_szText != NULL) { delete [] _szText; } _uAnoTextLength = nLen - 1; _szText = new char[_uAnoTextLength+1]; } if (_szText) ::WideCharToMultiByte(CP_ACP, 0, bstrText, -1, _szText, nLen, NULL, NULL); } void CTextAnnotation::Rotate(int nNewImageWidth, int nNewImageHeight, BOOL bClockwise) { RECT rect = _mark.lrBounds; RotateHelper((LPPOINT)&rect, 2, nNewImageWidth, nNewImageHeight, bClockwise); _mark.lrBounds = rect; NormalizeRect(&_mark.lrBounds); if (bClockwise) _nCurrentOrientation += 2700; else _nCurrentOrientation += 900; _nCurrentOrientation = _nCurrentOrientation % 3600; } HRESULT CTextAnnotation::_WriteBlocks(SIZE_T &cbSize, LPBYTE pBuffer) { cbSize = _WriteTextBlock(pBuffer, 6, _nCurrentOrientation, _uCreationScale, _szText, _nMaxText); return S_OK; } CTypedTextMark::CTypedTextMark (ANNOTATIONDESCRIPTOR *pDescriptor, ULONG uCreationScale) : CTextAnnotation(pDescriptor, uCreationScale) { } CFileTextMark::CFileTextMark (ANNOTATIONDESCRIPTOR *pDescriptor, ULONG uCreationScale) : CTextAnnotation(pDescriptor, uCreationScale) { } CTextStampMark::CTextStampMark (ANNOTATIONDESCRIPTOR *pDescriptor, ULONG uCreationScale) : CTextAnnotation(pDescriptor, uCreationScale, 255) { } CAttachNoteMark::CAttachNoteMark (ANNOTATIONDESCRIPTOR *pDescriptor, ULONG uCreationScale) : CTextAnnotation(pDescriptor, uCreationScale, 65536, true) { }