1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "svgfontexport.hxx"
22 #include "svgfilter.hxx"
23 #include "svgwriter.hxx"
25 #include <vcl/unohelp.hxx>
26 #include <vcl/font.hxx>
27 #include <vcl/outdev.hxx>
28 #include <vcl/settings.hxx>
29 #include <i18nlangtag/languagetag.hxx>
31 static const sal_Int32 nFontEM
= 2048;
34 SVGFontExport::SVGFontExport( SVGExport
& rExport
, const ::std::vector
< ObjectRepresentation
>& rObjects
) :
36 maObjects( rObjects
),
42 SVGFontExport::~SVGFontExport()
47 SVGFontExport::GlyphSet
& SVGFontExport::implGetGlyphSet( const vcl::Font
& rFont
)
49 FontWeight
eWeight( WEIGHT_NORMAL
);
50 FontItalic
eItalic( ITALIC_NONE
);
51 const OUString
& aFontName( rFont
.GetFamilyName() );
53 switch( rFont
.GetWeight() )
56 case WEIGHT_ULTRABOLD
:
58 eWeight
= WEIGHT_BOLD
;
65 if( rFont
.GetItalic() != ITALIC_NONE
)
66 eItalic
= ITALIC_NORMAL
;
68 return( maGlyphTree
[ aFontName
.getToken( 0, ';' ) ][ eWeight
][ eItalic
] );
72 void SVGFontExport::implCollectGlyphs()
74 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
76 pVDev
->EnableOutput( false );
78 for (auto const& elem
: maObjects
)
80 if( elem
.HasRepresentation() )
82 const GDIMetaFile
& rMtf
= elem
.GetRepresentation();
86 for( size_t i
= 0, nCount
= rMtf
.GetActionSize(); i
< nCount
; ++i
)
89 MetaAction
* pAction
= rMtf
.GetAction( i
);
90 const MetaActionType nType
= pAction
->GetType();
94 case MetaActionType::TEXT
:
96 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
97 sal_Int32 aLength
=std::min( pA
->GetText().getLength(), pA
->GetLen() );
98 aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
102 case MetaActionType::TEXTRECT
:
104 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
105 aText
= pA
->GetText();
109 case MetaActionType::TEXTARRAY
:
111 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
112 sal_Int32 aLength
=std::min( pA
->GetText().getLength(), pA
->GetLen() );
113 aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
117 case MetaActionType::STRETCHTEXT
:
119 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
120 sal_Int32 aLength
=std::min( pA
->GetText().getLength(), pA
->GetLen() );
121 aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
126 pAction
->Execute( pVDev
);
130 if( !aText
.isEmpty() )
132 GlyphSet
& rGlyphSet
= implGetGlyphSet( pVDev
->GetFont() );
133 css::uno::Reference
< css::i18n::XBreakIterator
> xBI(
134 vcl::unohelper::CreateBreakIterator() );
138 const css::lang::Locale
& rLocale
= Application::GetSettings().GetLanguageTag().getLocale();
139 sal_Int32 nCurPos
= 0, nLastPos
= -1;
141 while( ( nCurPos
< aText
.getLength() ) && ( nCurPos
> nLastPos
) )
143 sal_Int32 nCount2
= 1;
146 nCurPos
= xBI
->nextCharacters( aText
, nCurPos
, rLocale
,
147 css::i18n::CharacterIteratorMode::SKIPCELL
,
150 rGlyphSet
.insert( aText
.copy( nLastPos
, nCurPos
- nLastPos
) );
155 const sal_Unicode
* pStr
= aText
.getStr();
157 for( sal_uInt32 k
= 0, nLen
= aText
.getLength(); k
< nLen
; ++k
)
158 rGlyphSet
.insert( OUString( pStr
[ k
] ) );
169 void SVGFontExport::implEmbedFont( const vcl::Font
& rFont
)
171 if( mrExport
.IsEmbedFonts() )
173 GlyphSet
& rGlyphSet
= implGetGlyphSet( rFont
);
175 if( !rGlyphSet
.empty() )
177 const OUString
aEmbeddedFontStr( "EmbeddedFont_" );
180 SvXMLElementExport
aExp( mrExport
, XML_NAMESPACE_NONE
, "defs", true, true );
181 OUString
aCurIdStr( aEmbeddedFontStr
);
182 OUString
aUnitsPerEM( OUString::number( nFontEM
) );
183 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
184 vcl::Font
aFont( rFont
);
186 aFont
.SetFontSize( Size( 0, nFontEM
) );
187 aFont
.SetAlignment( ALIGN_BASELINE
);
189 pVDev
->SetMapMode(MapMode(MapUnit::Map100thMM
));
190 pVDev
->SetFont( aFont
);
192 aCurIdStr
+= OUString::number( ++mnCurFontId
);
193 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", aCurIdStr
);
194 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "horiz-adv-x", aUnitsPerEM
);
197 SvXMLElementExport
aExp2( mrExport
, XML_NAMESPACE_NONE
, "font", true, true );
198 OUString aFontWeight
;
200 const Size
aSize( nFontEM
, nFontEM
);
203 if( aFont
.GetWeight() != WEIGHT_NORMAL
)
204 aFontWeight
= "bold";
206 aFontWeight
= "normal";
209 if( aFont
.GetItalic() != ITALIC_NONE
)
210 aFontStyle
= "italic";
212 aFontStyle
= "normal";
214 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "font-family", GetMappedFontName( rFont
.GetFamilyName() ) );
215 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "units-per-em", aUnitsPerEM
);
216 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "font-weight", aFontWeight
);
217 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "font-style", aFontStyle
);
218 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "ascent", OUString::number( pVDev
->GetFontMetric().GetAscent() ) );
219 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "descent", OUString::number( pVDev
->GetFontMetric().GetDescent() ) );
222 SvXMLElementExport
aExp3( mrExport
, XML_NAMESPACE_NONE
, "font-face", true, true );
225 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "horiz-adv-x", OUString::number( aSize
.Width() ) );
229 const tools::PolyPolygon
aMissingGlyphPolyPoly( tools::Rectangle( aPos
, aSize
) );
231 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "d", SVGActionWriter::GetPathString( aMissingGlyphPolyPoly
, false ) );
234 SvXMLElementExport
aExp4( mrExport
, XML_NAMESPACE_NONE
, "missing-glyph", true, true );
237 for (auto const& glyph
: rGlyphSet
)
239 implEmbedGlyph( *pVDev
, glyph
);
248 void SVGFontExport::implEmbedGlyph( OutputDevice
const & rOut
, const OUString
& rCellStr
)
250 tools::PolyPolygon aPolyPoly
;
251 const sal_Unicode nSpace
= ' ';
253 if( rOut
.GetTextOutline( aPolyPoly
, rCellStr
) )
255 tools::Rectangle aBoundRect
;
257 aPolyPoly
.Scale( 1.0, -1.0 );
259 if( !rOut
.GetTextBoundRect( aBoundRect
, rCellStr
) )
260 aBoundRect
= tools::Rectangle( Point( 0, 0 ), Size( rOut
.GetTextWidth( rCellStr
), 0 ) );
262 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "unicode", rCellStr
);
264 if( rCellStr
[ 0 ] == nSpace
&& rCellStr
.getLength() == 1 )
265 aBoundRect
= tools::Rectangle( Point( 0, 0 ), Size( rOut
.GetTextWidth( OUString(' ') ), 0 ) );
267 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "horiz-adv-x", OUString::number( aBoundRect
.GetWidth() ) );
269 const OUString
aPathString( SVGActionWriter::GetPathString( aPolyPoly
, false ) );
270 if( !aPathString
.isEmpty() )
272 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "d", aPathString
);
276 SvXMLElementExport
aExp( mrExport
, XML_NAMESPACE_NONE
, "glyph", true, true );
282 void SVGFontExport::EmbedFonts()
286 for (auto const& glyph
: maGlyphTree
)
288 const FontWeightMap
& rFontWeightMap
= glyph
.second
;
289 for (auto const& fontWeight
: rFontWeightMap
)
291 const FontItalicMap
& rFontItalicMap
= fontWeight
.second
;
292 for (auto const& fontItalic
: rFontItalicMap
)
296 aFont
.SetFamilyName( glyph
.first
);
297 aFont
.SetWeight( fontWeight
.first
);
298 aFont
.SetItalic( fontItalic
.first
);
300 implEmbedFont( aFont
);
307 OUString
SVGFontExport::GetMappedFontName( const OUString
& rFontName
) const
309 OUString
aRet( rFontName
.getToken( 0, ';' ) );
317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */