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:
Mike Kaganski
2016-12-29 11:45:38 +03:00
parent 767ec2f138
commit fa96ffbc6b
8 changed files with 166 additions and 91 deletions

View File

@@ -421,6 +421,8 @@ void MetafileXmlDump::writeXml(const GDIMetaFile& rMetaFile, XmlWriter& rWriter)
rWriter.attribute("index", aIndex); rWriter.attribute("index", aIndex);
rWriter.attribute("length", aLength); rWriter.attribute("length", aLength);
if (pMetaTextArrayAction->GetDXArray())
{
rWriter.startElement("dxarray"); rWriter.startElement("dxarray");
OUString sDxLengthString; OUString sDxLengthString;
for (sal_Int32 i = 0; i < aLength; ++i) for (sal_Int32 i = 0; i < aLength; ++i)
@@ -430,6 +432,7 @@ void MetafileXmlDump::writeXml(const GDIMetaFile& rMetaFile, XmlWriter& rWriter)
} }
rWriter.content(sDxLengthString); rWriter.content(sDxLengthString);
rWriter.endElement(); rWriter.endElement();
}
rWriter.startElement("text"); rWriter.startElement("text");
rWriter.content(pMetaTextArrayAction->GetText()); rWriter.content(pMetaTextArrayAction->GetText());

Binary file not shown.

Binary file not shown.

View File

@@ -60,6 +60,7 @@ public:
void testTdf93750(); void testTdf93750();
void testTdf99402(); void testTdf99402();
void testTdf39894(); void testTdf39894();
void testETO_PDY();
CPPUNIT_TEST_SUITE(WmfTest); CPPUNIT_TEST_SUITE(WmfTest);
CPPUNIT_TEST(globalSetUp); CPPUNIT_TEST(globalSetUp);
@@ -71,6 +72,7 @@ public:
CPPUNIT_TEST(testTdf93750); CPPUNIT_TEST(testTdf93750);
CPPUNIT_TEST(testTdf99402); CPPUNIT_TEST(testTdf99402);
CPPUNIT_TEST(testTdf39894); CPPUNIT_TEST(testTdf39894);
CPPUNIT_TEST(testETO_PDY);
CPPUNIT_TEST_SUITE_END(); 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_TEST_SUITE_REGISTRATION(WmfTest);
CPPUNIT_PLUGIN_IMPLEMENT(); CPPUNIT_PLUGIN_IMPLEMENT();

View File

@@ -1547,7 +1547,6 @@ bool EnhWMFReader::ReadEnhWMF()
sal_Int32 nLeft, nTop, nRight, nBottom, ptlReferenceX, ptlReferenceY, nGfxMode, nXScale, nYScale; sal_Int32 nLeft, nTop, nRight, nBottom, ptlReferenceX, ptlReferenceY, nGfxMode, nXScale, nYScale;
sal_uInt32 nOffString, nOptions, offDx; sal_uInt32 nOffString, nOptions, offDx;
sal_Int32 nLen; sal_Int32 nLen;
std::vector<long> aDX;
nCurPos = pWMF->Tell() - 8; nCurPos = pWMF->Tell() - 8;
@@ -1568,23 +1567,6 @@ bool EnhWMFReader::ReadEnhWMF()
bool bOffStringSane = nOffString <= nEndPos - nCurPos; bool bOffStringSane = nOffString <= nEndPos - nCurPos;
if (bLenSane && bOffStringSane) 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 ); pWMF->Seek( nCurPos + nOffString );
OUString aText; OUString aText;
if ( bFlag ) if ( bFlag )
@@ -1594,22 +1576,6 @@ bool EnhWMFReader::ReadEnhWMF()
std::unique_ptr<sal_Char[]> pBuf(new sal_Char[ nLen ]); std::unique_ptr<sal_Char[]> pBuf(new sal_Char[ nLen ]);
pWMF->ReadBytes(pBuf.get(), nLen); pWMF->ReadBytes(pBuf.get(), nLen);
aText = OUString(pBuf.get(), nLen, pOut->GetCharSet()); 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 else
@@ -1630,7 +1596,53 @@ bool EnhWMFReader::ReadEnhWMF()
aText = OUString(pBuf.get(), nLen); 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; break;

View File

@@ -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(); UpdateClipRegion();
rPosition = ImplMap( rPosition ); rPosition = ImplMap( rPosition );
@@ -1357,18 +1357,25 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
if (pDXArry) if (pDXArry)
{ {
sal_Int32 i; sal_Int32 nSumX = 0, nSumY = 0;
sal_Int32 nSum = 0; for (sal_Int32 i = 0; i < rText.getLength(); i++ )
sal_Int32 nLen = rText.getLength();
for (i = 0; i < nLen; i++ )
{ {
nSum += pDXArry[i]; nSumX += pDXArry[i];
// #i121382# Map DXArray using WorldTransform // #i121382# Map DXArray using WorldTransform
const Size aSize(ImplMap(Size(nSum, 0))); const Size aSizeX(ImplMap(Size(nSumX, 0)));
const basegfx::B2DVector aVector(aSize.Width(), aSize.Height()); const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height());
pDXArry[i] = basegfx::fround(aVector.getLength()); 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 ) if ( mnLatestTextLayoutMode != mnTextLayoutMode )
@@ -1377,11 +1384,6 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) ); mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
} }
SetGfxMode( nGfxMode ); SetGfxMode( nGfxMode );
bool bChangeFont = false;
if ( mnLatestTextAlign != mnTextAlign )
{
bChangeFont = true;
mnLatestTextAlign = mnTextAlign;
TextAlign eTextAlign; TextAlign eTextAlign;
if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE ) if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE )
eTextAlign = ALIGN_BASELINE; eTextAlign = ALIGN_BASELINE;
@@ -1389,6 +1391,11 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
eTextAlign = ALIGN_BOTTOM; eTextAlign = ALIGN_BOTTOM;
else else
eTextAlign = ALIGN_TOP; eTextAlign = ALIGN_TOP;
bool bChangeFont = false;
if ( mnLatestTextAlign != mnTextAlign )
{
bChangeFont = true;
mnLatestTextAlign = mnTextAlign;
mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) ); mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) );
} }
if ( maLatestTextColor != maTextColor ) if ( maLatestTextColor != maTextColor )
@@ -1422,12 +1429,7 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
else else
aTmp.SetTransparent( false ); aTmp.SetTransparent( false );
if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE ) aTmp.SetAlignment( eTextAlign );
aTmp.SetAlignment( ALIGN_BASELINE );
else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM )
aTmp.SetAlignment( ALIGN_BOTTOM );
else
aTmp.SetAlignment( ALIGN_TOP );
if ( nGfxMode == GM_ADVANCED ) 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 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
SolarMutexGuard aGuard; SolarMutexGuard aGuard;
ScopedVclPtrInstance< VirtualDevice > pVDev; ScopedVclPtrInstance< VirtualDevice > pVDev;
sal_Int32 nTextWidth, nActPosDeltaX = 0; sal_Int32 nTextWidth;
Point aActPosDelta;
pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) ); pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
pVDev->SetFont( maFont ); pVDev->SetFont( maFont );
if( pDXArry ) if( pDXArry )
@@ -1465,23 +1468,33 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
if( nLen > 1 ) if( nLen > 1 )
nTextWidth += pDXArry[ nLen - 2 ]; nTextWidth += pDXArry[ nLen - 2 ];
// tdf#39894: We should consider the distance to next character cell origin // 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 else
{
nTextWidth = pVDev->GetTextWidth( rText ); nTextWidth = pVDev->GetTextWidth( rText );
aActPosDelta.X() = nTextWidth;
}
if( mnTextAlign & TA_UPDATECP ) if( mnTextAlign & TA_UPDATECP )
rPosition = maActPos; rPosition = maActPos;
if ( mnTextAlign & TA_RIGHT_CENTER ) if ( mnTextAlign & TA_RIGHT_CENTER )
{ {
double fLength = ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1; Point aDisplacement( ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1, 0 );
rPosition.X() -= (sal_Int32)( fLength * cos( maFont.GetOrientation() * F_PI1800 ) ); Point().RotateAround(aDisplacement.X(), aDisplacement.Y(), maFont.GetOrientation());
rPosition.Y() -= (sal_Int32)(-( fLength * sin( maFont.GetOrientation() * F_PI1800 ) ) ); rPosition -= aDisplacement;
} }
if( mnTextAlign & TA_UPDATECP ) 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 ) ) if ( bChangeFont || ( maLatestFont != aTmp ) )
{ {
@@ -1496,6 +1509,17 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
// TODO // TODO
} }
else else
{
if ( pDXArry && pDYArry )
{
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 /* because text without dx array is badly scaled, we
will create such an array if necessary */ will create such an array if necessary */
@@ -1514,6 +1538,7 @@ void WinMtfOutput::DrawText( Point& rPosition, OUString& rText, long* pDXArry, b
if ( !pDXArry ) // this means we have created our own array if ( !pDXArry ) // this means we have created our own array
delete[] pDX; // which must be deleted delete[] pDX; // which must be deleted
} }
}
SetGfxMode( nOldGfxMode ); SetGfxMode( nOldGfxMode );
} }

View File

@@ -600,6 +600,7 @@ public:
void DrawText( Point& rPosition, void DrawText( Point& rPosition,
OUString& rString, OUString& rString,
long* pDXArry = nullptr, long* pDXArry = nullptr,
long* pDYArry = nullptr,
bool bRecordPath = false, bool bRecordPath = false,
sal_Int32 nGraphicsMode = GM_COMPATIBLE); sal_Int32 nGraphicsMode = GM_COMPATIBLE);

View File

@@ -551,7 +551,7 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
// dxAry will not fit // dxAry will not fit
if ( nNewTextLen ) if ( nNewTextLen )
{ {
std::unique_ptr<long[]> pDXAry; std::unique_ptr<long[]> pDXAry, pDYAry;
sal_uInt32 nMaxStreamPos = nRecordPos + ( nRecordSize << 1 ); sal_uInt32 nMaxStreamPos = nRecordPos + ( nRecordSize << 1 );
sal_Int32 nDxArySize = nMaxStreamPos - pWMF->Tell(); sal_Int32 nDxArySize = nMaxStreamPos - pWMF->Tell();
sal_Int32 nDxAryEntries = nDxArySize >> 1; sal_Int32 nDxAryEntries = nDxArySize >> 1;
@@ -561,6 +561,10 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
{ {
sal_uInt16 i; // needed just outside the for sal_uInt16 i; // needed just outside the for
pDXAry.reset(new long[ nNewTextLen ]); pDXAry.reset(new long[ nNewTextLen ]);
if ( nOptions & ETO_PDY )
{
pDYAry.reset(new long[ nNewTextLen ]);
}
for (i = 0; i < nNewTextLen; i++ ) for (i = 0; i < nNewTextLen; i++ )
{ {
if ( pWMF->Tell() >= nMaxStreamPos ) if ( pWMF->Tell() >= nMaxStreamPos )
@@ -568,15 +572,15 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
sal_Int32 nDxCount = 1; sal_Int32 nDxCount = 1;
if ( nNewTextLen != nOriginalTextLen ) if ( nNewTextLen != nOriginalTextLen )
{ {
sal_Unicode nUniChar = aText[i]; sal_Unicode cUniChar = aText[i];
OString aTmp(&nUniChar, 1, pOut->GetCharSet()); OString aTmp(&cUniChar, 1, pOut->GetCharSet());
if ( aTmp.getLength() > 1 ) if ( aTmp.getLength() > 1 )
{ {
nDxCount = aTmp.getLength(); nDxCount = aTmp.getLength();
} }
} }
sal_Int16 nDx = 0; sal_Int16 nDx = 0, nDy = 0;
while ( nDxCount-- ) while ( nDxCount-- )
{ {
if ( ( pWMF->Tell() + 2 ) > nMaxStreamPos ) if ( ( pWMF->Tell() + 2 ) > nMaxStreamPos )
@@ -590,17 +594,21 @@ void WMFReader::ReadRecordParams( sal_uInt16 nFunc )
break; break;
sal_Int16 nDyTmp = 0; sal_Int16 nDyTmp = 0;
pWMF->ReadInt16(nDyTmp); pWMF->ReadInt16(nDyTmp);
// TODO: use Dy offset nDy += nDyTmp;
} }
} }
pDXAry[ i ] = nDx; pDXAry[ i ] = nDx;
if ( nOptions & ETO_PDY )
{
pDYAry[i] = nDy;
}
} }
if ( i == nNewTextLen ) if ( i == nNewTextLen )
bUseDXAry = true; bUseDXAry = true;
} }
if ( pDXAry && bUseDXAry ) if ( pDXAry && bUseDXAry )
pOut->DrawText( aPosition, aText, pDXAry.get() ); pOut->DrawText( aPosition, aText, pDXAry.get(), pDYAry.get() );
else else
pOut->DrawText( aPosition, aText ); pOut->DrawText( aPosition, aText );
} }