Add support for ETO_PDY in WMF/EMF
Currently it is implemented by making all characters different text arrays. Unit test included. Change-Id: I850bf192cf5d978a126d3f37b1084021d37bdf30 Reviewed-on: https://gerrit.libreoffice.org/32490 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
parent
767ec2f138
commit
fa96ffbc6b
@ -421,15 +421,18 @@ void MetafileXmlDump::writeXml(const GDIMetaFile& rMetaFile, XmlWriter& rWriter)
|
||||
rWriter.attribute("index", aIndex);
|
||||
rWriter.attribute("length", aLength);
|
||||
|
||||
rWriter.startElement("dxarray");
|
||||
OUString sDxLengthString;
|
||||
for (sal_Int32 i = 0; i < aLength; ++i)
|
||||
if (pMetaTextArrayAction->GetDXArray())
|
||||
{
|
||||
sDxLengthString += OUString::number(pMetaTextArrayAction->GetDXArray()[aIndex+i]);
|
||||
sDxLengthString += " ";
|
||||
rWriter.startElement("dxarray");
|
||||
OUString sDxLengthString;
|
||||
for (sal_Int32 i = 0; i < aLength; ++i)
|
||||
{
|
||||
sDxLengthString += OUString::number(pMetaTextArrayAction->GetDXArray()[aIndex + i]);
|
||||
sDxLengthString += " ";
|
||||
}
|
||||
rWriter.content(sDxLengthString);
|
||||
rWriter.endElement();
|
||||
}
|
||||
rWriter.content(sDxLengthString);
|
||||
rWriter.endElement();
|
||||
|
||||
rWriter.startElement("text");
|
||||
rWriter.content(pMetaTextArrayAction->GetText());
|
||||
|
BIN
vcl/qa/cppunit/wmf/data/ETO_PDY.emf
Normal file
BIN
vcl/qa/cppunit/wmf/data/ETO_PDY.emf
Normal file
Binary file not shown.
BIN
vcl/qa/cppunit/wmf/data/ETO_PDY.wmf
Normal file
BIN
vcl/qa/cppunit/wmf/data/ETO_PDY.wmf
Normal file
Binary file not shown.
@ -60,6 +60,7 @@ public:
|
||||
void testTdf93750();
|
||||
void testTdf99402();
|
||||
void testTdf39894();
|
||||
void testETO_PDY();
|
||||
|
||||
CPPUNIT_TEST_SUITE(WmfTest);
|
||||
CPPUNIT_TEST(globalSetUp);
|
||||
@ -71,6 +72,7 @@ public:
|
||||
CPPUNIT_TEST(testTdf93750);
|
||||
CPPUNIT_TEST(testTdf99402);
|
||||
CPPUNIT_TEST(testTdf39894);
|
||||
CPPUNIT_TEST(testETO_PDY);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
|
||||
@ -289,6 +291,30 @@ void WmfTest::testTdf39894()
|
||||
}
|
||||
}
|
||||
|
||||
void WmfTest::testETO_PDY()
|
||||
{
|
||||
OUString files[] = { "ETO_PDY.wmf", "ETO_PDY.emf" };
|
||||
for (const auto& file: files)
|
||||
{
|
||||
SvFileStream aFileStream(getFullUrl(file), StreamMode::READ);
|
||||
GDIMetaFile aGDIMetaFile;
|
||||
ReadWindowMetafile(aFileStream, aGDIMetaFile);
|
||||
|
||||
MetafileXmlDump dumper;
|
||||
xmlDocPtr pDoc = dumper.dumpAndParse(aGDIMetaFile);
|
||||
|
||||
CPPUNIT_ASSERT(pDoc);
|
||||
|
||||
// The y position of following text
|
||||
// must be smaller than that of previous
|
||||
auto y1 = getXPath(pDoc, "/metafile/push[2]/textarray[1]", "y");
|
||||
auto y2 = getXPath(pDoc, "/metafile/push[2]/textarray[2]", "y");
|
||||
auto y3 = getXPath(pDoc, "/metafile/push[2]/textarray[3]", "y");
|
||||
CPPUNIT_ASSERT_MESSAGE(file.toUtf8().getStr(), y2.toInt32() < y1.toInt32());
|
||||
CPPUNIT_ASSERT_MESSAGE(file.toUtf8().getStr(), y3.toInt32() < y2.toInt32());
|
||||
}
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(WmfTest);
|
||||
|
||||
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||
|
@ -1547,7 +1547,6 @@ bool EnhWMFReader::ReadEnhWMF()
|
||||
sal_Int32 nLeft, nTop, nRight, nBottom, ptlReferenceX, ptlReferenceY, nGfxMode, nXScale, nYScale;
|
||||
sal_uInt32 nOffString, nOptions, offDx;
|
||||
sal_Int32 nLen;
|
||||
std::vector<long> aDX;
|
||||
|
||||
nCurPos = pWMF->Tell() - 8;
|
||||
|
||||
@ -1568,23 +1567,6 @@ bool EnhWMFReader::ReadEnhWMF()
|
||||
bool bOffStringSane = nOffString <= nEndPos - nCurPos;
|
||||
if (bLenSane && bOffStringSane)
|
||||
{
|
||||
sal_Int32 nDxSize = nLen * ((nOptions & ETO_PDY) ? 8 : 4);
|
||||
if ( offDx && (( nCurPos + offDx + nDxSize ) <= nNextPos ) && nNextPos <= nEndPos )
|
||||
{
|
||||
pWMF->Seek( nCurPos + offDx );
|
||||
aDX.resize(nLen);
|
||||
for (sal_Int32 i = 0; i < nLen; ++i)
|
||||
{
|
||||
sal_Int32 val(0);
|
||||
pWMF->ReadInt32(val);
|
||||
aDX[i] = val;
|
||||
if (nOptions & ETO_PDY)
|
||||
{
|
||||
pWMF->ReadInt32(val);
|
||||
// TODO: Use Dy value
|
||||
}
|
||||
}
|
||||
}
|
||||
pWMF->Seek( nCurPos + nOffString );
|
||||
OUString aText;
|
||||
if ( bFlag )
|
||||
@ -1594,22 +1576,6 @@ bool EnhWMFReader::ReadEnhWMF()
|
||||
std::unique_ptr<sal_Char[]> pBuf(new sal_Char[ nLen ]);
|
||||
pWMF->ReadBytes(pBuf.get(), nLen);
|
||||
aText = OUString(pBuf.get(), nLen, pOut->GetCharSet());
|
||||
pBuf.reset();
|
||||
|
||||
if ( aText.getLength() != nLen )
|
||||
{
|
||||
std::vector<long> aOldDX(aText.getLength());
|
||||
aOldDX.swap(aDX);
|
||||
sal_Int32 nDXLen = std::min<sal_Int32>(nLen, aOldDX.size());
|
||||
for (sal_Int32 i = 0, j = 0; i < aText.getLength(); ++i)
|
||||
{
|
||||
sal_Unicode cUniChar = aText[i];
|
||||
OString aCharacter(&cUniChar, 1, pOut->GetCharSet());
|
||||
aDX[i] = 0;
|
||||
for (sal_Int32 k = 0; ( k < aCharacter.getLength() ) && ( j < nDXLen ) && ( i < aText.getLength() ); ++k)
|
||||
aDX[ i ] += aOldDX[j++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1630,7 +1596,53 @@ bool EnhWMFReader::ReadEnhWMF()
|
||||
aText = OUString(pBuf.get(), nLen);
|
||||
}
|
||||
}
|
||||
pOut->DrawText(aPos, aText, aDX.data(), bRecordPath, nGfxMode);
|
||||
|
||||
std::unique_ptr<long[]> pDXAry, pDYAry;
|
||||
sal_Int32 nDxSize = nLen * ((nOptions & ETO_PDY) ? 8 : 4);
|
||||
if ( offDx && (( nCurPos + offDx + nDxSize ) <= nNextPos ) && nNextPos <= nEndPos )
|
||||
{
|
||||
pWMF->Seek( nCurPos + offDx );
|
||||
pDXAry.reset( new long[aText.getLength()] );
|
||||
if (nOptions & ETO_PDY)
|
||||
{
|
||||
pDYAry.reset( new long[aText.getLength()] );
|
||||
}
|
||||
|
||||
for (sal_Int32 i = 0; i < aText.getLength(); ++i)
|
||||
{
|
||||
sal_Int32 nDxCount = 1;
|
||||
if (aText.getLength() != nLen)
|
||||
{
|
||||
sal_Unicode cUniChar = aText[i];
|
||||
OString aTmp(&cUniChar, 1, pOut->GetCharSet());
|
||||
if (aTmp.getLength() > 1)
|
||||
{
|
||||
nDxCount = aTmp.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
sal_Int32 nDx = 0, nDy = 0;
|
||||
while (nDxCount--)
|
||||
{
|
||||
sal_Int32 nDxTmp = 0;
|
||||
pWMF->ReadInt32(nDxTmp);
|
||||
nDx += nDxTmp;
|
||||
if (nOptions & ETO_PDY)
|
||||
{
|
||||
sal_Int32 nDyTmp = 0;
|
||||
pWMF->ReadInt32(nDyTmp);
|
||||
nDy += nDyTmp;
|
||||
}
|
||||
}
|
||||
|
||||
pDXAry[i] = nDx;
|
||||
if (nOptions & ETO_PDY)
|
||||
{
|
||||
pDYAry[i] = nDy;
|
||||
}
|
||||
}
|
||||
}
|
||||
pOut->DrawText(aPos, aText, pDXAry.get(), pDYAry.get(), bRecordPath, nGfxMode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1348,7 +1348,7 @@ void WinMtfOutput::DrawPolyBezier( tools::Polygon& rPolygon, bool bTo, bool bRec
|
||||
}
|
||||
}
|
||||
|
||||
void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, bool bRecordPath, sal_Int32 nGfxMode )
|
||||
void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, long* pDYArry, bool bRecordPath, sal_Int32 nGfxMode )
|
||||
{
|
||||
UpdateClipRegion();
|
||||
rPosition = ImplMap( rPosition );
|
||||
@ -1357,18 +1357,25 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
|
||||
|
||||
if (pDXArry)
|
||||
{
|
||||
sal_Int32 i;
|
||||
sal_Int32 nSum = 0;
|
||||
sal_Int32 nLen = rText.getLength();
|
||||
|
||||
for (i = 0; i < nLen; i++ )
|
||||
sal_Int32 nSumX = 0, nSumY = 0;
|
||||
for (sal_Int32 i = 0; i < rText.getLength(); i++ )
|
||||
{
|
||||
nSum += pDXArry[i];
|
||||
nSumX += pDXArry[i];
|
||||
|
||||
// #i121382# Map DXArray using WorldTransform
|
||||
const Size aSize(ImplMap(Size(nSum, 0)));
|
||||
const basegfx::B2DVector aVector(aSize.Width(), aSize.Height());
|
||||
pDXArry[i] = basegfx::fround(aVector.getLength());
|
||||
const Size aSizeX(ImplMap(Size(nSumX, 0)));
|
||||
const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height());
|
||||
pDXArry[i] = basegfx::fround(aVectorX.getLength()) * (nSumX >= 0 ? 1 : -1);
|
||||
|
||||
if (pDYArry)
|
||||
{
|
||||
nSumY += pDYArry[i];
|
||||
|
||||
const Size aSizeY(ImplMap(Size(0, nSumY)));
|
||||
const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height());
|
||||
// Reverse Y
|
||||
pDYArry[i] = basegfx::fround(aVectorY.getLength()) * (nSumY >= 0 ? -1 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( mnLatestTextLayoutMode != mnTextLayoutMode )
|
||||
@ -1377,18 +1384,18 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
|
||||
mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
|
||||
}
|
||||
SetGfxMode( nGfxMode );
|
||||
TextAlign eTextAlign;
|
||||
if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE )
|
||||
eTextAlign = ALIGN_BASELINE;
|
||||
else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM )
|
||||
eTextAlign = ALIGN_BOTTOM;
|
||||
else
|
||||
eTextAlign = ALIGN_TOP;
|
||||
bool bChangeFont = false;
|
||||
if ( mnLatestTextAlign != mnTextAlign )
|
||||
{
|
||||
bChangeFont = true;
|
||||
mnLatestTextAlign = mnTextAlign;
|
||||
TextAlign eTextAlign;
|
||||
if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE )
|
||||
eTextAlign = ALIGN_BASELINE;
|
||||
else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM )
|
||||
eTextAlign = ALIGN_BOTTOM;
|
||||
else
|
||||
eTextAlign = ALIGN_TOP;
|
||||
mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) );
|
||||
}
|
||||
if ( maLatestTextColor != maTextColor )
|
||||
@ -1422,12 +1429,7 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
|
||||
else
|
||||
aTmp.SetTransparent( false );
|
||||
|
||||
if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE )
|
||||
aTmp.SetAlignment( ALIGN_BASELINE );
|
||||
else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM )
|
||||
aTmp.SetAlignment( ALIGN_BOTTOM );
|
||||
else
|
||||
aTmp.SetAlignment( ALIGN_TOP );
|
||||
aTmp.SetAlignment( eTextAlign );
|
||||
|
||||
if ( nGfxMode == GM_ADVANCED )
|
||||
{
|
||||
@ -1455,7 +1457,8 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
|
||||
// #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
|
||||
SolarMutexGuard aGuard;
|
||||
ScopedVclPtrInstance< VirtualDevice > pVDev;
|
||||
sal_Int32 nTextWidth, nActPosDeltaX = 0;
|
||||
sal_Int32 nTextWidth;
|
||||
Point aActPosDelta;
|
||||
pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
|
||||
pVDev->SetFont( maFont );
|
||||
if( pDXArry )
|
||||
@ -1465,23 +1468,33 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
|
||||
if( nLen > 1 )
|
||||
nTextWidth += pDXArry[ nLen - 2 ];
|
||||
// tdf#39894: We should consider the distance to next character cell origin
|
||||
nActPosDeltaX = pDXArry[ nLen - 1 ];
|
||||
aActPosDelta.X() = pDXArry[ nLen - 1 ];
|
||||
if ( pDYArry )
|
||||
{
|
||||
aActPosDelta.Y() = pDYArry[ nLen - 1 ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nTextWidth = pVDev->GetTextWidth( rText );
|
||||
aActPosDelta.X() = nTextWidth;
|
||||
}
|
||||
|
||||
if( mnTextAlign & TA_UPDATECP )
|
||||
rPosition = maActPos;
|
||||
|
||||
if ( mnTextAlign & TA_RIGHT_CENTER )
|
||||
{
|
||||
double fLength = ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1;
|
||||
rPosition.X() -= (sal_Int32)( fLength * cos( maFont.GetOrientation() * F_PI1800 ) );
|
||||
rPosition.Y() -= (sal_Int32)(-( fLength * sin( maFont.GetOrientation() * F_PI1800 ) ) );
|
||||
Point aDisplacement( ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1, 0 );
|
||||
Point().RotateAround(aDisplacement.X(), aDisplacement.Y(), maFont.GetOrientation());
|
||||
rPosition -= aDisplacement;
|
||||
}
|
||||
|
||||
if( mnTextAlign & TA_UPDATECP )
|
||||
maActPos.X() = rPosition.X() + (pDXArry ? nActPosDeltaX : nTextWidth);
|
||||
{
|
||||
Point().RotateAround(aActPosDelta.X(), aActPosDelta.Y(), maFont.GetOrientation());
|
||||
maActPos = rPosition + aActPosDelta;
|
||||
}
|
||||
}
|
||||
if ( bChangeFont || ( maLatestFont != aTmp ) )
|
||||
{
|
||||
@ -1497,22 +1510,34 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
|
||||
}
|
||||
else
|
||||
{
|
||||
/* because text without dx array is badly scaled, we
|
||||
will create such an array if necessary */
|
||||
long* pDX = pDXArry;
|
||||
if (!pDXArry)
|
||||
if ( pDXArry && pDYArry )
|
||||
{
|
||||
// #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
|
||||
SolarMutexGuard aGuard;
|
||||
ScopedVclPtrInstance< VirtualDevice > pVDev;
|
||||
pDX = new long[ rText.getLength() ];
|
||||
pVDev->SetMapMode( MapUnit::Map100thMM );
|
||||
pVDev->SetFont( maLatestFont );
|
||||
pVDev->GetTextArray( rText, pDX, 0, rText.getLength());
|
||||
for (sal_Int32 i = 0; i < rText.getLength(); ++i)
|
||||
{
|
||||
Point aCharDisplacement( i ? pDXArry[i-1] : 0, i ? pDYArry[i-1] : 0 );
|
||||
Point().RotateAround(aCharDisplacement.X(), aCharDisplacement.Y(), maFont.GetOrientation());
|
||||
mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), nullptr, 0, 1 ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* because text without dx array is badly scaled, we
|
||||
will create such an array if necessary */
|
||||
long* pDX = pDXArry;
|
||||
if (!pDXArry)
|
||||
{
|
||||
// #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
|
||||
SolarMutexGuard aGuard;
|
||||
ScopedVclPtrInstance< VirtualDevice > pVDev;
|
||||
pDX = new long[ rText.getLength() ];
|
||||
pVDev->SetMapMode( MapUnit::Map100thMM );
|
||||
pVDev->SetFont( maLatestFont );
|
||||
pVDev->GetTextArray( rText, pDX, 0, rText.getLength());
|
||||
}
|
||||
mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) );
|
||||
if ( !pDXArry ) // this means we have created our own array
|
||||
delete[] pDX; // which must be deleted
|
||||
}
|
||||
mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) );
|
||||
if ( !pDXArry ) // this means we have created our own array
|
||||
delete[] pDX; // which must be deleted
|
||||
}
|
||||
SetGfxMode( nOldGfxMode );
|
||||
}
|
||||
|
@ -600,6 +600,7 @@ public:
|
||||
void DrawText( Point& rPosition,
|
||||
OUString& rString,
|
||||
long* pDXArry = nullptr,
|
||||
long* pDYArry = nullptr,
|
||||
bool bRecordPath = false,
|
||||
sal_Int32 nGraphicsMode = GM_COMPATIBLE);
|
||||
|
||||
|
@ -551,7 +551,7 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
|
||||
// dxAry will not fit
|
||||
if ( nNewTextLen )
|
||||
{
|
||||
std::unique_ptr<long[]> pDXAry;
|
||||
std::unique_ptr<long[]> pDXAry, pDYAry;
|
||||
sal_uInt32 nMaxStreamPos = nRecordPos + ( nRecordSize << 1 );
|
||||
sal_Int32 nDxArySize = nMaxStreamPos - pWMF->Tell();
|
||||
sal_Int32 nDxAryEntries = nDxArySize >> 1;
|
||||
@ -561,6 +561,10 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
|
||||
{
|
||||
sal_uInt16 i; // needed just outside the for
|
||||
pDXAry.reset(new long[ nNewTextLen ]);
|
||||
if ( nOptions & ETO_PDY )
|
||||
{
|
||||
pDYAry.reset(new long[ nNewTextLen ]);
|
||||
}
|
||||
for (i = 0; i < nNewTextLen; i++ )
|
||||
{
|
||||
if ( pWMF->Tell() >= nMaxStreamPos )
|
||||
@ -568,15 +572,15 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
|
||||
sal_Int32 nDxCount = 1;
|
||||
if ( nNewTextLen != nOriginalTextLen )
|
||||
{
|
||||
sal_Unicode nUniChar = aText[i];
|
||||
OString aTmp(&nUniChar, 1, pOut->GetCharSet());
|
||||
sal_Unicode cUniChar = aText[i];
|
||||
OString aTmp(&cUniChar, 1, pOut->GetCharSet());
|
||||
if ( aTmp.getLength() > 1 )
|
||||
{
|
||||
nDxCount = aTmp.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
sal_Int16 nDx = 0;
|
||||
sal_Int16 nDx = 0, nDy = 0;
|
||||
while ( nDxCount-- )
|
||||
{
|
||||
if ( ( pWMF->Tell() + 2 ) > nMaxStreamPos )
|
||||
@ -590,17 +594,21 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
|
||||
break;
|
||||
sal_Int16 nDyTmp = 0;
|
||||
pWMF->ReadInt16(nDyTmp);
|
||||
// TODO: use Dy offset
|
||||
nDy += nDyTmp;
|
||||
}
|
||||
}
|
||||
|
||||
pDXAry[ i ] = nDx;
|
||||
if ( nOptions & ETO_PDY )
|
||||
{
|
||||
pDYAry[i] = nDy;
|
||||
}
|
||||
}
|
||||
if ( i == nNewTextLen )
|
||||
bUseDXAry = true;
|
||||
}
|
||||
if ( pDXAry && bUseDXAry )
|
||||
pOut->DrawText( aPosition, aText, pDXAry.get() );
|
||||
pOut->DrawText( aPosition, aText, pDXAry.get(), pDYAry.get() );
|
||||
else
|
||||
pOut->DrawText( aPosition, aText );
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user