summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--emfio/inc/mtftools.hxx24
-rw-r--r--emfio/source/reader/emfreader.cxx4
-rw-r--r--emfio/source/reader/mtftools.cxx211
-rw-r--r--emfio/source/reader/wmfreader.cxx3
-rw-r--r--include/vcl/metaact.hxx8
-rw-r--r--vcl/inc/impfont.hxx6
-rw-r--r--vcl/source/filter/wmf/emfwr.cxx2
-rw-r--r--vcl/source/font/font.cxx64
8 files changed, 286 insertions, 36 deletions
diff --git a/emfio/inc/mtftools.hxx b/emfio/inc/mtftools.hxx
index 736b1da2fe74..914235288a1d 100644
--- a/emfio/inc/mtftools.hxx
+++ b/emfio/inc/mtftools.hxx
@@ -27,6 +27,7 @@
#include <vcl/lineinfo.hxx>
#include <vcl/outdevstate.hxx>
#include <vcl/FilterConfigItem.hxx>
+#include <rtl/ref.hxx>
#include "emfiodllapi.h"
@@ -259,6 +260,8 @@ namespace emfio
#define HUNDREDTH_MILLIMETERS_PER_MILLIINCH 2.54
#define MILLIINCH_PER_TWIPS 1.44
+class MetaFontAction;
+
//============================ WmfReader ==================================
namespace emfio
@@ -455,6 +458,24 @@ namespace emfio
{}
};
+ // tdf#127471 implement detection and correction of wrongly scaled
+ // fonts in own-written, old (before this fix) EMF/WMF files
+ class ScaledFontDetectCorrectHelper
+ {
+ private:
+ rtl::Reference<MetaFontAction> maCurrentMetaFontAction;
+ std::vector<double> maAlternativeFontScales;
+ std::vector<std::pair<rtl::Reference<MetaFontAction>, double>> maPositiveIdentifiedCases;
+ std::vector<std::pair<rtl::Reference<MetaFontAction>, double>> maNegativeIdentifiedCases;
+
+ public:
+ ScaledFontDetectCorrectHelper();
+ void endCurrentMetaFontAction();
+ void newCurrentMetaFontAction(rtl::Reference<MetaFontAction>& rNewMetaFontAction);
+ void evaluateAlternativeFontScale(OUString const & rText, long nImportedTextLength);
+ void applyAlternativeFontScale();
+ };
+
class EMFIO_DLLPUBLIC MtfTools
{
MtfTools(MtfTools const &) = delete;
@@ -518,6 +539,9 @@ namespace emfio
sal_uInt32 mnEndPos;
std::vector<std::unique_ptr<BSaveStruct>> maBmpSaveList;
+ // tdf#127471 always try to detect - only used with ScaledText
+ ScaledFontDetectCorrectHelper maScaledFontHelper;
+
bool mbNopMode : 1;
bool mbFillStyleSelected : 1;
bool mbClipNeedsUpdate : 1;
diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx
index 6a974085d887..d0e7eb31c696 100644
--- a/emfio/source/reader/emfreader.cxx
+++ b/emfio/source/reader/emfreader.cxx
@@ -1825,6 +1825,10 @@ namespace emfio
}
mpInputStream->Seek( nNextPos );
}
+
+ // tdf#127471
+ maScaledFontHelper.applyAlternativeFontScale();
+
if( !maBmpSaveList.empty() )
ResolveBitmapActions( maBmpSaveList );
diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx
index 3053d3878e13..42f965ea3f56 100644
--- a/emfio/source/reader/mtftools.cxx
+++ b/emfio/source/reader/mtftools.cxx
@@ -318,6 +318,184 @@ namespace emfio
#endif
};
+ // tdf#127471
+ ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper()
+ : maCurrentMetaFontAction(),
+ maAlternativeFontScales(),
+ maPositiveIdentifiedCases(),
+ maNegativeIdentifiedCases()
+ {
+ }
+
+ void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction()
+ {
+ if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty())
+ {
+ // create average corrected FontScale value and count
+ // positive/negative hits
+ sal_uInt32 nPositive(0);
+ sal_uInt32 nNegative(0);
+ double fAverage(0.0);
+
+ for(double fPart : maAlternativeFontScales)
+ {
+ if(fPart < 0.0)
+ {
+ nNegative++;
+ fAverage += -fPart;
+ }
+ else
+ {
+ nPositive++;
+ fAverage += fPart;
+ }
+ }
+
+ fAverage /= static_cast<double>(maAlternativeFontScales.size());
+
+ if(nPositive >= nNegative)
+ {
+ // correction intended, it is probably an old imported file
+ maPositiveIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage));
+ }
+ else
+ {
+ // correction not favorable in the majority of cases for this Font, still
+ // remember to have a weight in the last decision for correction
+ maNegativeIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage));
+ }
+ }
+
+ maCurrentMetaFontAction.clear();
+ maAlternativeFontScales.clear();
+ }
+
+ void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(rtl::Reference<MetaFontAction>& rNewMetaFontAction)
+ {
+ maCurrentMetaFontAction.clear();
+ maAlternativeFontScales.clear();
+
+ if(rNewMetaFontAction.is())
+ {
+ // check 1st criteria for FontScale active. We usually write this,
+ // so this will already sort out most situations
+ const vcl::Font& rCandidate(rNewMetaFontAction->GetFont());
+
+ if(0 != rCandidate.GetAverageFontWidth())
+ {
+ const long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth());
+
+ // check 2nd (system-dependent) criteria for FontScale
+ if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight())
+ {
+ // FontScale is active, remrmber and use as current
+ maCurrentMetaFontAction = rNewMetaFontAction;
+ }
+ }
+ }
+ }
+
+ void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, long nImportedTextLength)
+ {
+ if(maCurrentMetaFontAction.is())
+ {
+ SolarMutexGuard aGuard; // VirtualDevice is not thread-safe
+ ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice;
+
+ // calculate measured TextLength
+ const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont());
+ pTempVirtualDevice->SetFont(rFontCandidate);
+ const long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText));
+
+ // compare expected and imported TextLengths
+ if(nImportedTextLength != nMeasuredTextLength)
+ {
+ const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength));
+ const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0);
+
+ // if we assume that loaded file was written on old linux, we have to
+ // back-convert the scale value depending on which system we run
+#ifdef _WIN32
+ // When running on Windows the value was not adapted at font import (see WinMtfFontStyle
+ // constructor), so it is still NormedFontScaling and we need to convert to Windows-style
+ // scaling
+#else
+ // When running on unx (non-Windows) the value was already adapted at font import (see WinMtfFontStyle
+ // constructor). It was wrongly assumed to be Windows-style FontScaling, so we need to revert that
+ // to get back to the needed unx-style FontScale
+#endif
+ // Interestingly this leads to the *same* correction, so no need to make this
+ // system-dependent (!)
+ const long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth());
+ const long nScaledAverageFontWidth(rFontCandidate.GetAverageFontWidth());
+ const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight()));
+ const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor);
+ long nCorrectedTextLength(0);
+
+ { // do in own scope, only need nUnscaledAverageFontWidth
+ vcl::Font rFontCandidate2(rFontCandidate);
+ rFontCandidate2.SetAverageFontWidth(static_cast<long>(fCorrectedAverageFontWidth));
+ pTempVirtualDevice->SetFont(rFontCandidate2);
+ nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText);
+ }
+
+ const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength));
+ const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0);
+
+ // If FactorCorrectedText fits better than FactorText this is probably
+ // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system
+ // and should be corrected.
+ // Usually in tested cases this lies inside 5% of range, so detecting this just using
+ // fFactorTextPercent indside 5% -> no old file
+ // fFactorCorrectedTextPercent indside 5% -> is old file
+ // works not too bad, but there are some strange not so often used fonts where that
+ // values do deviate, so better just compare if old corrected would fit better than
+ // the uncorrected case, that is usually safe.
+ if(fFactorCorrectedTextPercent < fFactorTextPercent)
+ {
+ maAlternativeFontScales.push_back(fCorrectedAverageFontWidth);
+ }
+ else
+ {
+ // also push, but negative to remember non-fitting case
+ maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth);
+ }
+ }
+ }
+ }
+
+ void ScaledFontDetectCorrectHelper::applyAlternativeFontScale()
+ {
+ // make sure last evtl. detected current FontAction gets added to identified cases
+ endCurrentMetaFontAction();
+
+ // Take final decision to correct FontScaling for this imported Metafile or not.
+ // It is possible to weight positive against negative cases, so to only finally
+ // correct when more positive cases were detected.
+ // But that would be inconsequent and wrong. *If* the detected case is an old import
+ // the whole file was written with wrong FontScale values and all Font actions
+ // need to be corrected. Thus, for now, correct all when there are/is positive
+ // cases detected.
+ // On the other hand it *may* be that for some strange fonts there is a false-positive
+ // in the positive cases, so at least insist on positive cases being more than negative.
+ // Still, do then correct *all* cases.
+ if(!maPositiveIdentifiedCases.empty()
+ && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size())
+ {
+ for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases)
+ {
+ rCandidate.first->correctFontScale(static_cast<long>(rCandidate.second));
+ }
+ for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases)
+ {
+ rCandidate.first->correctFontScale(static_cast<long>(rCandidate.second));
+ }
+ }
+
+ maPositiveIdentifiedCases.clear();
+ maNegativeIdentifiedCases.clear();
+ }
+
Color MtfTools::ReadColor()
{
sal_uInt32 nColor;
@@ -906,6 +1084,7 @@ namespace emfio
mnStartPos(0),
mnEndPos(0),
maBmpSaveList(),
+ maScaledFontHelper(),
mbNopMode(false),
mbFillStyleSelected(false),
mbClipNeedsUpdate(true),
@@ -1597,14 +1776,29 @@ namespace emfio
maActPos = rPosition + aActPosDelta;
}
}
- if ( bChangeFont || ( maLatestFont != aTmp ) )
+
+ if(bChangeFont || (maLatestFont != aTmp))
{
maLatestFont = aTmp;
- mpGDIMetaFile->AddAction( new MetaFontAction( aTmp ) );
+ rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp));
+
+ // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector
+ maScaledFontHelper.endCurrentMetaFontAction();
+
+ // !bRecordPath: else no MetaTextArrayAction will be created
+ // nullptr != pDXArry: detection only possible when text size is given
+ // rText.getLength(): no useful check without text
+ if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength())
+ {
+ maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction);
+ }
+
+ mpGDIMetaFile->AddAction( rtl::Reference<MetaAction>( aNewMetaFontAction.get() ) );
mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) );
mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) );
mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) );
}
+
if ( bRecordPath )
{
// TODO
@@ -1625,7 +1819,18 @@ namespace emfio
/* because text without dx array is badly scaled, we
will create such an array if necessary */
long* pDX = pDXArry;
- if (!pDXArry)
+ if (pDXArry)
+ {
+ // only useful when we have an imported DXArray
+ if(!rText.isEmpty())
+ {
+ maScaledFontHelper.evaluateAlternativeFontScale(
+ rText,
+ pDXArry[rText.getLength() - 1] // extract imported TextLength
+ );
+ }
+ }
+ else
{
// #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
SolarMutexGuard aGuard;
diff --git a/emfio/source/reader/wmfreader.cxx b/emfio/source/reader/wmfreader.cxx
index 58c4d971cb49..74b399282787 100644
--- a/emfio/source/reader/wmfreader.cxx
+++ b/emfio/source/reader/wmfreader.cxx
@@ -1228,6 +1228,9 @@ namespace emfio
case W_META_ENDDOC:
break;
}
+
+ // tdf#127471
+ maScaledFontHelper.applyAlternativeFontScale();
}
static const long aMaxWidth = 1024;
diff --git a/include/vcl/metaact.hxx b/include/vcl/metaact.hxx
index 2666edb5f334..febbadb992a2 100644
--- a/include/vcl/metaact.hxx
+++ b/include/vcl/metaact.hxx
@@ -1399,12 +1399,20 @@ public:
const MapMode& GetMapMode() const { return maMapMode; }
};
+// tdf#127471 decl for friend below
+namespace emfio { class ScaledFontDetectCorrectHelper; }
+
class VCL_DLLPUBLIC MetaFontAction final : public MetaAction
{
private:
vcl::Font maFont;
+ // tdf#127471 for import correction of own wrong written EMF/WMF files allow correction
+ // of FontScale after import. Only from there, so use a friend here and keep the method private
+ friend class emfio::ScaledFontDetectCorrectHelper;
+ void correctFontScale(long nNewFontScale) { maFont.SetAverageFontWidth(nNewFontScale); }
+
public:
MetaFontAction();
MetaFontAction(MetaFontAction const &) = default;
diff --git a/vcl/inc/impfont.hxx b/vcl/inc/impfont.hxx
index 624707839f40..c96a6bab8c59 100644
--- a/vcl/inc/impfont.hxx
+++ b/vcl/inc/impfont.hxx
@@ -64,14 +64,12 @@ public:
void SetCharSet( const rtl_TextEncoding eCharSet ) { meCharSet = eCharSet; }
void SetFontSize( const Size& rSize )
{
-#ifndef _WIN32
if(rSize.Height() != maAverageFontSize.Height())
{
// reset evtl. buffered calculated AverageFontSize, it depends
// on Font::Height
mnCalculatedAverageFontWidth = 0;
}
-#endif
maAverageFontSize = rSize;
}
@@ -91,10 +89,8 @@ public:
void IncreaseQualityBy( int nQualityAmount ) { mnQuality += nQualityAmount; }
void DecreaseQualityBy( int nQualityAmount ) { mnQuality -= nQualityAmount; }
-#ifndef _WIN32
long GetCalculatedAverageFontWidth() const { return mnCalculatedAverageFontWidth; }
void SetCalculatedAverageFontWidth(long nNew) { mnCalculatedAverageFontWidth = nNew; }
-#endif
bool operator==( const ImplFont& ) const;
@@ -146,9 +142,7 @@ private:
int mnQuality;
-#ifndef _WIN32
long mnCalculatedAverageFontWidth;
-#endif
};
#endif // INCLUDED_VCL_INC_IMPFONT_HXX
diff --git a/vcl/source/filter/wmf/emfwr.cxx b/vcl/source/filter/wmf/emfwr.cxx
index 58f0e550a9ae..514ce8ef9e8a 100644
--- a/vcl/source/filter/wmf/emfwr.cxx
+++ b/vcl/source/filter/wmf/emfwr.cxx
@@ -474,7 +474,7 @@ void EMFWriter::ImplCheckTextAttr()
// specific formats.
const long nAverageFontWidth(rFont.GetOrCalculateAverageFontWidth());
- if(nAverageFontWidth > 0)
+ if(nFontHeight > 0)
{
const double fScaleFactor(static_cast<double>(nAverageFontWidth) / static_cast<double>(nFontHeight));
nFontWidth = static_cast<long>(static_cast<double>(nFontWidth) * fScaleFactor);
diff --git a/vcl/source/font/font.cxx b/vcl/source/font/font.cxx
index 2ac9a032bb30..64f16b08f8db 100644
--- a/vcl/source/font/font.cxx
+++ b/vcl/source/font/font.cxx
@@ -33,6 +33,7 @@
#include <rtl/instance.hxx>
#include <TypeSerializer.hxx>
+#include <vcl/svapp.hxx>
#ifdef _WIN32
#include <vcl/metric.hxx>
@@ -366,34 +367,49 @@ void Font::GetFontAttributes( FontAttributes& rAttrs ) const
// tdf#127471 for corrections on EMF/WMF we need the AvgFontWidth in Windows-specific notation
long Font::GetOrCalculateAverageFontWidth() const
{
-#ifdef _WIN32
- // on windows we just have it available
- return GetAverageFontWidth();
-#else
- // On non-Windows systems we need to calculate AvgFontWidth
- // as close as possible (discussion see documentation in task)
if(0 == mpImplFont->GetCalculatedAverageFontWidth())
{
- // calculate it. For discussion of method used, see task
- const std::size_t nSize(127 - 32);
- std::array<sal_Unicode, nSize> aArray;
+ // VirtualDevice is not thread safe
+ SolarMutexGuard aGuard;
+
+ // create unscaled copy of font (this), a VirtualDevice and set it there
+ vcl::Font aUnscaledFont(*this);
+ ScopedVclPtr<VirtualDevice> pTempVirtualDevice(VclPtr<VirtualDevice>::Create());
+ aUnscaledFont.SetAverageFontWidth(0);
+ pTempVirtualDevice->SetFont(aUnscaledFont);
- for(sal_Unicode a(0); a < nSize; a++)
+#ifdef _WIN32
+ // on Windows systems use FontMetric to get/create AverageFontWidth from system
+ const FontMetric aMetric(pTempVirtualDevice->GetFontMetric());
+ const_cast<Font*>(this)->mpImplFont->SetCalculatedAverageFontWidth(aMetric.GetAverageFontWidth());
+#else
+ // On non-Windows systems we need to calculate AvgFontWidth
+ // as close as possible (discussion see documentation in task),
+ // so calculate it. For discussion of method used, see task
+ // buffer measure string creation, will always use the same
+ static OUString aMeasureString;
+
+ if(!aMeasureString.getLength())
{
- aArray[a] = a + 32;
+ const std::size_t nSize(127 - 32);
+ std::array<sal_Unicode, nSize> aArray;
+
+ for(sal_Unicode a(0); a < nSize; a++)
+ {
+ aArray[a] = a + 32;
+ }
+
+ aMeasureString = OUString(aArray.data(), nSize);
}
- vcl::Font aUnscaledFont(*this);
- ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
- aUnscaledFont.SetAverageFontWidth(0);
- pVirDev->SetFont(aUnscaledFont);
const double fAverageFontWidth(
- pVirDev->GetTextWidth(OUString(aArray.data())) / static_cast<double>(nSize));
+ pTempVirtualDevice->GetTextWidth(aMeasureString, 0, aMeasureString.getLength()) /
+ static_cast<double>(aMeasureString.getLength()));
const_cast<Font*>(this)->mpImplFont->SetCalculatedAverageFontWidth(basegfx::fround(fAverageFontWidth));
+#endif
}
return mpImplFont->GetCalculatedAverageFontWidth();
-#endif
}
SvStream& ReadImplFont( SvStream& rIStm, ImplFont& rImplFont, long& rnNormedFontScaling )
@@ -517,7 +533,7 @@ SvStream& ReadFont( SvStream& rIStm, vcl::Font& rFont )
aUnscaledFont.SetAverageFontWidth(0);
const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont));
- if (aUnscaledFontMetric.GetAverageFontWidth() > 0)
+ if (nHeight > 0)
{
const double fScaleFactor(static_cast<double>(nNormedFontScaling) / static_cast<double>(nHeight));
nScaledWidth = basegfx::fround(static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()) * fScaleFactor);
@@ -881,10 +897,8 @@ ImplFont::ImplFont() :
maFillColor( COL_TRANSPARENT ),
mbWordLine( false ),
mnOrientation( 0 ),
- mnQuality( 0 )
-#ifndef _WIN32
- , mnCalculatedAverageFontWidth(0)
-#endif
+ mnQuality( 0 ),
+ mnCalculatedAverageFontWidth( 0 )
{}
ImplFont::ImplFont( const ImplFont& rImplFont ) :
@@ -916,10 +930,8 @@ ImplFont::ImplFont( const ImplFont& rImplFont ) :
maFillColor( rImplFont.maFillColor ),
mbWordLine( rImplFont.mbWordLine ),
mnOrientation( rImplFont.mnOrientation ),
- mnQuality( rImplFont.mnQuality )
-#ifndef _WIN32
- , mnCalculatedAverageFontWidth(rImplFont.mnCalculatedAverageFontWidth)
-#endif
+ mnQuality( rImplFont.mnQuality ),
+ mnCalculatedAverageFontWidth( rImplFont.mnCalculatedAverageFontWidth )
{}
bool ImplFont::operator==( const ImplFont& rOther ) const