Commit 72b7e35b authored by Ilya.Kirillov's avatar Ilya.Kirillov Committed by Alexander Trofimov

Добавлен класс для работы с карандашом.

git-svn-id: svn://fileserver/activex/AVS/Sources/TeamlabOffice/trunk/ServerComponents@62042 954022d7-b5bf-4e40-9824-e11837661b57
parent 7c5e3c8e
#ifndef _EMF_FILE_H
#define _EMF_FILE_H
#include "../Wmf/WmfUtils.h"
#include "../Wmf/WmfTypes.h"
......@@ -63,7 +65,6 @@ namespace Metafile
m_pOutput = NULL;
m_oStream.SetStream(NULL, 0);
m_bError = false;
m_oPlayer.Clear();
m_pDC = m_oPlayer.GetDC();
}
......@@ -141,6 +142,36 @@ namespace Metafile
Read_EMR_POLYGON16();
break;
}
case EMR_POLYPOLYGON16:
{
Read_EMR_POLYPOLYGON16();
break;
}
case EMR_LINETO:
{
Read_EMR_LINETO();
break;
}
case EMR_POLYBEZIERTO16:
{
Read_EMR_POLYBEZIERTO16();
break;
}
case EMR_POLYLINETO16:
{
Read_EMR_POLYLINETO16();
break;
}
case EMR_STROKEANDFILLPATH:
{
Read_EMR_STROKEANDFILLPATH();
break;
}
case EMR_STROKEPATH:
{
Read_EMR_STROKEPATH();
break;
}
//-----------------------------------------------------------
// 2.3.7 Object Creation
//-----------------------------------------------------------
......@@ -149,11 +180,21 @@ namespace Metafile
Read_EMR_CREATEBRUSHINDIRECT();
break;
}
case EMR_CREATEPEN:
{
Read_EMR_CREATEPEN();
break;
}
case EMR_EXTCREATEFONTINDIRECTW:
{
Read_EMR_EXTCREATEFONTINDIRECTW();
break;
}
case EMR_EXTCREATEPEN:
{
Read_EMR_EXTCREATEPEN();
break;
}
//-----------------------------------------------------------
// 2.3.8 Object Manipulation
//-----------------------------------------------------------
......@@ -168,8 +209,46 @@ namespace Metafile
break;
}
//-----------------------------------------------------------
// 2.3.10 Path Bracket
//-----------------------------------------------------------
case EMR_BEGINPATH:
{
Read_EMR_BEGINPATH();
break;
}
case EMR_ENDPATH:
{
Read_EMR_ENDPATH();
break;
}
case EMR_CLOSEFIGURE:
{
Read_EMR_CLOSEFIGURE();
break;
}
case EMR_FLATTENPATH:
{
Read_EMR_FLATTENPATH();
break;
}
case EMR_WIDENPATH:
{
Read_EMR_WIDENPATH();
break;
}
case EMR_ABORTPATH:
{
Read_EMR_ABORTPATH();
break;
}
//-----------------------------------------------------------
// 2.3.11 State
//-----------------------------------------------------------
case EMR_MOVETOEX:
{
Read_EMR_MOVETOEX();
break;
}
case EMR_SAVEDC:
{
Read_EMR_SAVEDC();
......@@ -195,6 +274,16 @@ namespace Metafile
Read_EMR_SETBKMODE();
break;
}
case EMR_SETMITERLIMIT:
{
Read_EMR_SETMITERLIMIT();
break;
}
case EMR_SETPOLYFILLMODE:
{
Read_EMR_SETPOLYFILLMODE();
break;
}
//-----------------------------------------------------------
// 2.3.12 Transform
//-----------------------------------------------------------
......@@ -286,6 +375,62 @@ namespace Metafile
TEmfStretchDIBITS oBitmap;
m_oStream >> oBitmap;
long lHeaderOffset = oBitmap.offBmiSrc - sizeof(TEmfStretchDIBITS) - 8;
unsigned long ulHeaderSize = oBitmap.cbBmiSrc;
long lBitsOffset = oBitmap.offBitsSrc - oBitmap.offBmiSrc - oBitmap.cbBmiSrc;
unsigned long ulBitsSize = oBitmap.cbBitsSrc;
if (ulHeaderSize <= 0 || ulBitsSize <= 0 || lHeaderOffset < 0 || lBitsOffset < 0)
{
// TODO: , oBitmap.BitBltRasterOperation
if (lHeaderOffset > 0)
m_oStream.Skip(lHeaderOffset);
m_oStream.Skip(ulHeaderSize);
if (lBitsOffset > 0)
m_oStream.Skip(lBitsOffset);
m_oStream.Skip(ulBitsSize);
return;
}
m_oStream.Skip(lHeaderOffset);
BYTE* pHeaderBuffer = new BYTE[ulHeaderSize];
if (!pHeaderBuffer)
return SetError();
m_oStream.ReadBytes(pHeaderBuffer, ulHeaderSize);
m_oStream.Skip(lBitsOffset);
BYTE* pBitsBuffer = new BYTE[ulBitsSize];
if (!pBitsBuffer)
{
delete[] pHeaderBuffer;
return SetError();
}
m_oStream.ReadBytes(pBitsBuffer, ulBitsSize);
BYTE* pBgraBuffer;
unsigned long ulWidth, ulHeight;
ReadImage(pHeaderBuffer, ulHeaderSize, pBitsBuffer, ulBitsSize, &pBgraBuffer, &ulWidth, &ulHeight);
if (m_pOutput)
m_pOutput->DrawBitmap(oBitmap.xDest, oBitmap.yDest, oBitmap.cxDest, oBitmap.cyDest, pBgraBuffer, ulWidth, ulHeight);
if (pBgraBuffer)
delete[] pBgraBuffer;
delete[] pBitsBuffer;
delete[] pHeaderBuffer;
}
void Read_EMR_BITBLT()
{
TEmfBitBlt oBitmap;
m_oStream >> oBitmap;
long lHeaderOffset = oBitmap.offBmiSrc - sizeof(TEmfBitBlt) - 8;
unsigned long ulHeaderSize = oBitmap.cbBmiSrc;
long lBitsOffset = oBitmap.offBitsSrc - oBitmap.offBmiSrc - oBitmap.cbBmiSrc;
......@@ -436,7 +581,7 @@ namespace Metafile
std::wstring wsText((wchar_t*)pUnicode);
if (m_pOutput)
m_pOutput->DrawText(wsText.c_str(), ulCharsCount, oText.wEmrText.Reference.x, oText.wEmrText.Reference.x);
m_pOutput->DrawText(wsText.c_str(), ulCharsCount, oText.wEmrText.Reference.x, oText.wEmrText.Reference.y);
delete[] pUnicode;
delete[] pDx;
......@@ -517,61 +662,226 @@ namespace Metafile
// . , ,
// , .
}
void Read_EMR_BITBLT()
void Read_EMR_SETMITERLIMIT()
{
TEmfBitBlt oBitmap;
m_oStream >> oBitmap;
long lHeaderOffset = oBitmap.offBmiSrc - sizeof(TEmfBitBlt) - 8;
unsigned long ulHeaderSize = oBitmap.cbBmiSrc;
long lBitsOffset = oBitmap.offBitsSrc - oBitmap.offBmiSrc - oBitmap.cbBmiSrc;
unsigned long ulBitsSize = oBitmap.cbBitsSrc;
if (ulHeaderSize <= 0 || ulBitsSize <= 0 || lHeaderOffset < 0 || lBitsOffset < 0)
unsigned long ulMiterLimit;
m_oStream >> ulMiterLimit;
m_pDC->SetMiterLimit(ulMiterLimit);
}
void Read_EMR_EXTCREATEPEN()
{
// TODO: , oBitmap.BitBltRasterOperation
unsigned long ulPenIndex;
m_oStream >> ulPenIndex;
if (lHeaderOffset > 0)
m_oStream.Skip(lHeaderOffset);
m_oStream.Skip(4); // offBmi
m_oStream.Skip(4); // cbBmi
m_oStream.Skip(4); // offBits
m_oStream.Skip(4); // cbBits
m_oStream.Skip(ulHeaderSize);
m_ulRecordSize -= 20;
if (lBitsOffset > 0)
m_oStream.Skip(lBitsOffset);
CEmfLogPen* pPen = new CEmfLogPen();
if (!pPen)
return SetError();
m_oStream.Skip(ulBitsSize);
// LogPenEx
m_oStream >> pPen->PenStyle;
m_oStream >> pPen->Width;
m_oStream.Skip(4); // BrushStyle
m_oStream >> pPen->Color;
m_oStream.Skip(4); // BrushHatch
m_oStream >> pPen->NumStyleEntries;
return;
m_ulRecordSize -= 24;
if (pPen->NumStyleEntries > 0)
{
m_ulRecordSize -= pPen->NumStyleEntries * 4;
pPen->StyleEntry = new unsigned long[pPen->NumStyleEntries];
if (!pPen->StyleEntry)
{
delete pPen;
return SetError();
}
m_oStream.Skip(lHeaderOffset);
for (unsigned long ulIndex = 0; ulIndex < pPen->NumStyleEntries; ulIndex++)
{
m_oStream >> pPen->StyleEntry[ulIndex];
}
}
else
{
pPen->StyleEntry = NULL;
}
BYTE* pHeaderBuffer = new BYTE[ulHeaderSize];
if (!pHeaderBuffer)
// ,
m_oStream.Skip(m_ulRecordSize);
m_oPlayer.RegisterObject(ulPenIndex, (CEmfObjectBase*)pPen);
}
void Read_EMR_CREATEPEN()
{
unsigned long ulPenIndex;
m_oStream >> ulPenIndex;
CEmfLogPen* pPen = new CEmfLogPen();
if (!pPen)
return SetError();
m_oStream.ReadBytes(pHeaderBuffer, ulHeaderSize);
m_oStream >> pPen->PenStyle;
m_oStream >> pPen->Width;
m_oStream.Skip(4); // Width.y
m_oStream >> pPen->Color;
m_oPlayer.RegisterObject(ulPenIndex, (CEmfObjectBase*)pPen);
}
void Read_EMR_SETPOLYFILLMODE()
{
unsigned long ulFillMode;
m_oStream >> ulFillMode;
m_pDC->SetFillMode(ulFillMode);
}
void Read_EMR_POLYPOLYGON16()
{
TEmfRectL oBounds;
m_oStream >> oBounds;
unsigned long ulNumberOfPolygons;
m_oStream >> ulNumberOfPolygons;
unsigned long ulTotalPointsCount;
m_oStream >> ulTotalPointsCount;
m_oStream.Skip(lBitsOffset);
BYTE* pBitsBuffer = new BYTE[ulBitsSize];
if (!pBitsBuffer)
unsigned long* pPolygonPointCount = new unsigned long[ulNumberOfPolygons];
if (!pPolygonPointCount)
return SetError();
for (unsigned long ulIndex = 0; ulIndex < ulNumberOfPolygons; ulIndex++)
{
delete[] pHeaderBuffer;
m_oStream >> pPolygonPointCount[ulIndex];
}
TEmfPointS* pPoints = new TEmfPointS[ulTotalPointsCount];
if (!pPoints)
{
delete[] pPolygonPointCount;
return SetError();
}
m_oStream.ReadBytes(pBitsBuffer, ulBitsSize);
BYTE* pBgraBuffer;
unsigned long ulWidth, ulHeight;
ReadImage(pHeaderBuffer, ulHeaderSize, pBitsBuffer, ulBitsSize, &pBgraBuffer, &ulWidth, &ulHeight);
for (unsigned long ulIndex = 0; ulIndex < ulTotalPointsCount; ulIndex++)
{
m_oStream >> pPoints[ulIndex];
}
if (m_pOutput)
m_pOutput->DrawBitmap(oBitmap.xDest, oBitmap.yDest, oBitmap.cxDest, oBitmap.cyDest, pBgraBuffer, ulWidth, ulHeight);
{
for (unsigned long ulPolygonIndex = 0, unStartPointIndex = 0; ulPolygonIndex < ulNumberOfPolygons; ulPolygonIndex++)
{
m_pOutput->StartPath();
for (unsigned long ulPointIndex = 0; ulPointIndex < pPolygonPointCount[ulPolygonIndex]; ulPointIndex++)
{
unsigned long ulRealPointIndex = ulPointIndex + unStartPointIndex;
if (ulRealPointIndex >= ulTotalPointsCount)
{
delete[] pPolygonPointCount;
delete[] pPoints;
return SetError();
}
if (pBgraBuffer)
delete[] pBgraBuffer;
if (0 == ulPointIndex)
m_pOutput->MoveTo(pPoints[ulRealPointIndex].x, pPoints[ulRealPointIndex].y);
else
m_pOutput->LineTo(pPoints[ulRealPointIndex].x, pPoints[ulRealPointIndex].y);
}
m_pOutput->ClosePath();
m_pOutput->DrawPath();
m_pOutput->EndPath();
}
}
delete[] pBitsBuffer;
delete[] pHeaderBuffer;
delete[] pPolygonPointCount;
delete[] pPoints;
}
void Read_EMR_BEGINPATH()
{
//
}
void Read_EMR_ENDPATH()
{
//
}
void Read_EMR_CLOSEFIGURE()
{
//
}
void Read_EMR_FLATTENPATH()
{
//
}
void Read_EMR_WIDENPATH()
{
//
}
void Read_EMR_ABORTPATH()
{
//
}
void Read_EMR_MOVETOEX()
{
TEmfPointL oPoint;
m_oStream >> oPoint;
// TODO:
}
void Read_EMR_LINETO()
{
TEmfPointL oPoint;
m_oStream >> oPoint;
// TODO:
}
void Read_EMR_POLYBEZIERTO16()
{
TEmfRectL oBounds;
m_oStream >> oBounds;
unsigned long ulCount;
m_oStream >> ulCount;
TEmfPointS* pPoints = new TEmfPointS[ulCount];
if (!pPoints)
return SetError();
for (unsigned long ulIndex = 0; ulIndex < ulCount; ulIndex++)
{
m_oStream >> pPoints[ulIndex];
}
delete[] pPoints;
}
void Read_EMR_POLYLINETO16()
{
TEmfRectL oBounds;
m_oStream >> oBounds;
unsigned long ulCount;
m_oStream >> ulCount;
TEmfPointS* pPoints = new TEmfPointS[ulCount];
if (!pPoints)
return SetError();
for (unsigned long ulIndex = 0; ulIndex < ulCount; ulIndex++)
{
m_oStream >> pPoints[ulIndex];
}
delete[] pPoints;
}
void Read_EMR_STROKEANDFILLPATH()
{
TEmfRectL oBounds;
m_oStream >> oBounds;
}
void Read_EMR_STROKEPATH()
{
TEmfRectL oBounds;
m_oStream >> oBounds;
}
private:
......
......@@ -10,7 +10,8 @@ namespace Metafile
{
EMF_OBJECT_UNKNOWN = 0x00,
EMF_OBJECT_BRUSH = 0x01,
EMF_OBJECT_FONT = 0x02
EMF_OBJECT_FONT = 0x02,
EMF_OBJECT_PEN = 0x03
} EEmfObjectType;
class CEmfObjectBase
......@@ -69,6 +70,32 @@ namespace Metafile
TEmfLogFontEx LogFontEx;
TEmfDesignVector DesignVector;
};
class CEmfLogPen : public CEmfObjectBase
{
public:
CEmfLogPen()
{
StyleEntry = NULL;
}
virtual ~CEmfLogPen()
{
if (StyleEntry)
delete[] StyleEntry;
}
virtual EEmfObjectType GetType()
{
return EMF_OBJECT_PEN;
}
public:
unsigned long PenStyle;
unsigned long Width;
TEmfColor Color;
unsigned long NumStyleEntries;
unsigned long* StyleEntry;
};
}
#endif // _EMF_OBJECTS_H
\ No newline at end of file
......@@ -117,6 +117,7 @@ namespace Metafile
{
case EMF_OBJECT_BRUSH: m_pDC->SetBrush((CEmfLogBrushEx*)pObject); break;
case EMF_OBJECT_FONT: m_pDC->SetFont((CEmfLogFont*)pObject); break;
case EMF_OBJECT_PEN: m_pDC->SetPen((CEmfLogPen*)pObject); break;
}
}
}
......@@ -138,7 +139,14 @@ namespace Metafile
pNewDC->m_oTransform.Copy(&m_oTransform);
pNewDC->m_oTextColor.Copy(&m_oTextColor);
pNewDC->m_oBgColor.Copy(&m_oBgColor);
pNewDC->m_pBrush = m_pBrush;
pNewDC->m_pFont = m_pFont;
pNewDC->m_pPen = m_pPen;
pNewDC->m_ulTextAlign = m_ulTextAlign;
pNewDC->m_ulBgMode = m_ulBgMode;
pNewDC->m_ulMiterLimit = m_ulMiterLimit;
pNewDC->m_ulFillMode = m_ulFillMode;
return pNewDC;
}
......@@ -194,4 +202,29 @@ namespace Metafile
{
return m_oBgColor;
}
void CEmfDC::SetMiterLimit(unsigned long ulMiter)
{
m_ulMiterLimit = ulMiter;
}
unsigned long CEmfDC::GetMiterLimit()
{
return m_ulMiterLimit;
}
void CEmfDC::SetFillMode(unsigned long ulFillMode)
{
m_ulFillMode = ulFillMode;
}
unsigned long CEmfDC::GetFillMode()
{
return m_ulFillMode;
}
void CEmfDC::SetPen(CEmfLogPen* pPen)
{
m_pPen = pPen;
}
CEmfLogPen* CEmfDC::GetPen()
{
return m_pPen;
}
}
\ No newline at end of file
......@@ -54,16 +54,25 @@ namespace Metafile
unsigned long GetBgMode();
void SetBgColor(TEmfColor& oColor);
TEmfColor& GetBgColor();
void SetMiterLimit(unsigned long ulMiter);
unsigned long GetMiterLimit();
void SetFillMode(unsigned long ulFillMode);
unsigned long GetFillMode();
void SetPen(CEmfLogPen* pPen);
CEmfLogPen* GetPen();
private:
CEmfLogBrushEx* m_pBrush;
CEmfLogPen* m_pPen;
CEmfLogFont* m_pFont;
TEmfXForm m_oTransform;
TEmfColor m_oTextColor;
TEmfColor m_oBgColor;
unsigned long m_ulTextAlign;
unsigned long m_ulBgMode;
unsigned long m_ulMiterLimit;
unsigned long m_ulFillMode;
};
}
......
......@@ -32,6 +32,14 @@ namespace Metafile
return;
m_pRenderer = pRenderer;
long lL = m_pEmfFile->m_oHeader.oBounds.lLeft;
long lR = m_pEmfFile->m_oHeader.oBounds.lRight;
long lT = m_pEmfFile->m_oHeader.oBounds.lTop;
long lB = m_pEmfFile->m_oHeader.oBounds.lBottom;
m_dScaleX = (lR - lL <= 0) ? 1 : m_dW / (double)(lR - lL);
m_dScaleY = (lB - lT <= 0) ? 1 : m_dH / (double)(lB - lT);
}
~CEmfRendererOutput()
{
......@@ -242,7 +250,24 @@ namespace Metafile
void StartPath()
{
UpdateTransform();
UpdateBrush();
m_lDrawPathType = -1;
if (true == UpdateBrush())
{
CEmfDC* pDC = m_pEmfFile->GetDC();
if (ALTERNATE == pDC->GetFillMode())
m_lDrawPathType = c_nEvenOddFillMode;
else// if (WINDING == pDC->GetFillMode())
m_lDrawPathType = c_nWindingFillMode;
}
if (true == UpdatePen())
{
if (-1 == m_lDrawPathType)
m_lDrawPathType = c_nStroke;
else
m_lDrawPathType |= c_nStroke;
}
m_pRenderer->BeginCommand(c_nPathType);
m_pRenderer->PathCommandStart();
......@@ -265,7 +290,8 @@ namespace Metafile
}
void DrawPath()
{
m_pRenderer->DrawPath(c_nWindingFillMode);
if (-1 != m_lDrawPathType)
m_pRenderer->DrawPath(m_lDrawPathType);
}
void EndPath()
{
......@@ -278,22 +304,12 @@ namespace Metafile
double TransX(long lX)
{
long lL = m_pEmfFile->m_oHeader.oBounds.lLeft;
long lR = m_pEmfFile->m_oHeader.oBounds.lRight;
if (lR - lL <= 0)
return 0;
return m_dW * (double)(lX - lL) / (double)(lR - lL);
return m_dScaleX * (double)(lX - lL);
}
double TransY(long lY)
{
long lT = m_pEmfFile->m_oHeader.oBounds.lTop;
long lB = m_pEmfFile->m_oHeader.oBounds.lBottom;
if (lB - lT <= 0)
return 0;
return m_dH * (double)(lY - lT) / (double)(lB - lT);
return m_dScaleY * (double)(lY - lT);
}
bool UpdateBrush()
......@@ -325,22 +341,88 @@ namespace Metafile
if (!pDC)
return;
long lL = m_pEmfFile->m_oHeader.oBounds.lLeft;
long lR = m_pEmfFile->m_oHeader.oBounds.lRight;
long lT = m_pEmfFile->m_oHeader.oBounds.lTop;
long lB = m_pEmfFile->m_oHeader.oBounds.lBottom;
if (lB - lT <= 0 || lR - lL <= 0)
return;
double dKoefX = m_dW / (double)(lR - lL);
double dKoefY = m_dW / (double)(lR - lL);
double dKoefX = m_dScaleX;
double dKoefY = m_dScaleY;
TEmfXForm* pMatrix = pDC->GetTransform();
m_pRenderer->ResetTransform();
m_pRenderer->SetTransform(pMatrix->M11, pMatrix->M12 * dKoefY / dKoefX, pMatrix->M21 * dKoefX / dKoefY, pMatrix->M22, pMatrix->Dx * dKoefX, pMatrix->Dy * dKoefY);
}
bool UpdatePen()
{
CEmfDC* pDC = m_pEmfFile->GetDC();
if (!pDC)
return false;
CEmfLogPen* pPen = pDC->GetPen();
if (!pPen)
return false;
long lColor = METAFILE_RGBA(pPen->Color.r, pPen->Color.g, pPen->Color.b);
// TODO: dWidth PS_GEOMETRIC
double dWidth = pPen->Width * m_dScaleX;
if (dWidth <= 0.01)
dWidth = 0;
unsigned long ulPenType = pPen->PenStyle & PS_TYPE_MASK;
unsigned long ulPenEndCap = pPen->PenStyle & PS_ENDCAP_MASK;
unsigned long ulPenJoin = pPen->PenStyle & PS_JOIN_MASK;
unsigned long ulPenStyle = pPen->PenStyle & PS_STYLE_MASK;
BYTE nCapStyle = 0;
if (0 == ulPenEndCap)
nCapStyle = 2;
else if (1 == ulPenEndCap)
nCapStyle = 1;
else if (2 == ulPenEndCap)
nCapStyle = 0;
BYTE nJoinStyle = 0;
if (0 == ulPenJoin)
nJoinStyle = 2;
else if (1 == ulPenJoin)
nJoinStyle = 1;
else if (2 == ulPenJoin)
nJoinStyle = 2;
double dMiterLimit = pDC->GetMiterLimit() * m_dScaleX;
// TODO: , PS_SOLID.
// TODO: PS_USERSTYLE
unsigned long ulDashStyle;
if (PS_ALTERNATE == ulPenStyle || PS_USERSTYLE == ulPenStyle || PS_INSIDEFRAME == ulPenStyle)
ulDashStyle = (BYTE)PS_SOLID;
else if (PS_NULL != ulPenStyle)
ulDashStyle = (BYTE)ulPenStyle;
m_pRenderer->put_PenDashStyle(ulDashStyle);
m_pRenderer->put_PenLineJoin(nJoinStyle);
m_pRenderer->put_PenLineStartCap(nCapStyle);
m_pRenderer->put_PenLineEndCap(nCapStyle);
m_pRenderer->put_PenColor(lColor);
m_pRenderer->put_PenSize(dWidth);
m_pRenderer->put_PenAlpha(255);
m_pRenderer->put_PenMiterLimit(dMiterLimit);
//// TO DO: AVSRenderer, ushROPMode
//// .
//// Pen'a, .
//switch (pDC->ushROPMode)
//{
// case R2_BLACK: m_pRenderer->put_PenColor(0); break;
// case R2_NOP: m_pRenderer->put_PenAlpha(0); break;
// case R2_COPYPEN: break;
// case R2_WHITE: m_pRenderer->put_PenColor(METAFILE_RGBA(255, 255, 255)); break;
//}
if (PS_NULL == ulPenStyle)
return false;
return true;
}
private:
......@@ -355,6 +437,8 @@ namespace Metafile
double m_dY; //
double m_dW; // /,
double m_dH; // .
double m_dScaleX;
double m_dScaleY;
CEmfFile* m_pEmfFile;
};
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment