You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1534 lines
45 KiB
1534 lines
45 KiB
#include "annotlib.h"
|
|
#include "assert.h"
|
|
#include "shlwapi.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;i<cSize;i++)
|
|
{
|
|
if (bClockwise)
|
|
{
|
|
nNewX = nNewImageWidth - ppoint[i].y;
|
|
nNewY = ppoint[i].x;
|
|
}
|
|
else
|
|
{
|
|
nNewX = ppoint[i].y;
|
|
nNewY = nNewImageHeight - ppoint[i].x;
|
|
}
|
|
ppoint[i].x = nNewX;
|
|
ppoint[i].y = nNewY;
|
|
}
|
|
}
|
|
|
|
CAnnotationSet::CAnnotationSet()
|
|
: _dpaMarks(NULL)
|
|
{
|
|
_pDefaultData = (LPBYTE)c_pDefaultData;
|
|
_cbDefaultData = c_cbDefaultData;
|
|
}
|
|
|
|
CAnnotationSet::~CAnnotationSet()
|
|
{
|
|
_ClearMarkList ();
|
|
}
|
|
|
|
void CAnnotationSet::RenderAllMarks(HDC hdc)
|
|
{
|
|
CAnnotation *pCur;
|
|
|
|
if(_dpaMarks == NULL)
|
|
return;
|
|
|
|
for (INT_PTR i=0;i<DPA_GetPtrCount(_dpaMarks);i++)
|
|
{
|
|
pCur = (CAnnotation*)DPA_GetPtr(_dpaMarks, i);
|
|
if (pCur)
|
|
{
|
|
pCur->Render (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;i<DPA_GetPtrCount(_dpaMarks);i++)
|
|
{
|
|
pCur = (CAnnotation*)DPA_GetPtr(_dpaMarks, i);
|
|
if (pCur == pMark)
|
|
{
|
|
DPA_DeletePtr(_dpaMarks, i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CAnnotationSet::SetImageData(IShellImageData *pimg)
|
|
{
|
|
GUID guidFmt;
|
|
pimg->GetRawDataFormat(&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;i<DPA_GetPtrCount(_dpaMarks);i++)
|
|
{
|
|
pItem = (CAnnotation*)DPA_GetPtr(_dpaMarks, i);
|
|
if (pItem)
|
|
{
|
|
if (SUCCEEDED(pItem->GetBlob(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;i<DPA_GetPtrCount(_dpaMarks);i++)
|
|
{
|
|
pItem = (CAnnotation*)DPA_GetPtr(_dpaMarks, i);
|
|
if (pItem)
|
|
{
|
|
UINT nIndex = (UINT)i;
|
|
CHAR szIndex[11];
|
|
ZeroMemory(szIndex, 11);
|
|
|
|
wnsprintfA(szIndex, ARRAYSIZE(szIndex), "%d", nIndex );
|
|
|
|
if (SUCCEEDED(pItem->GetBlob(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 (5 != *(UNALIGNED UINT*)pMark)
|
|
{
|
|
return pDesc;
|
|
}
|
|
|
|
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));
|
|
if (sizeof(ANNOTATIONMARK) == cbMark)
|
|
{
|
|
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)
|
|
{
|
|
BOOL bFailed = FALSE;
|
|
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(!bFailed && uOffset < cbNamedBlocks)
|
|
{
|
|
assert(*(UNALIGNED UINT*)(pBegin+uOffset) == 6);
|
|
if (6 == *(UNALIGNED UINT*)(pBegin+uOffset))
|
|
{
|
|
uOffset += 4;
|
|
assert(*(UNALIGNED UINT*)(pBegin+uOffset) == 12); // name plus data size
|
|
if (12 == *(UNALIGNED UINT*)(pBegin+uOffset))
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
bFailed = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFailed = TRUE;
|
|
}
|
|
}
|
|
if (!bFailed)
|
|
{
|
|
*ppNext =(LPBYTE)(pBegin+cbNamedBlocks);
|
|
}
|
|
else // annotations are malformed, don't try to load them
|
|
{
|
|
delete [] (BYTE*) pDesc;
|
|
pDesc = NULL;
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
lstrcpynA(_szGroup,(LPCSTR)(pb->data),pb->cbSize-sizeof(pb->szType));
|
|
}
|
|
}
|
|
_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, ARRAYSIZE(_mark.lfFont.lfFaceName), lfFont.lfFaceName, ARRAYSIZE(lfFont.lfFaceName));
|
|
}
|
|
|
|
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, ARRAYSIZE(lfFont.lfFaceName), _mark.lfFont.lfFaceName, ARRAYSIZE(_mark.lfFont.lfFaceName), 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)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|