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/metric.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/settings.hxx>
30 #include <i18nlangtag/languagetag.hxx>
31 #include <xmloff/namespacemap.hxx>
32 #include <o3tl/string_view.hxx>
34 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
35 #include <com/sun/star/i18n/XBreakIterator.hpp>
37 const sal_Int32 nFontEM
= 2048;
40 SVGFontExport::SVGFontExport( SVGExport
& rExport
, ::std::vector
< ObjectRepresentation
>&& rObjects
) :
42 maObjects( std::move(rObjects
) ),
48 SVGFontExport::~SVGFontExport()
53 SVGFontExport::GlyphSet
& SVGFontExport::implGetGlyphSet( const vcl::Font
& rFont
)
55 FontWeight
eWeight( WEIGHT_NORMAL
);
56 FontItalic
eItalic( ITALIC_NONE
);
57 const OUString
& aFontName( rFont
.GetFamilyName() );
59 switch( rFont
.GetWeight() )
62 case WEIGHT_ULTRABOLD
:
64 eWeight
= WEIGHT_BOLD
;
71 if( rFont
.GetItalic() != ITALIC_NONE
)
72 eItalic
= ITALIC_NORMAL
;
74 return( maGlyphTree
[ aFontName
.getToken( 0, ';' ) ][ eWeight
][ eItalic
] );
78 void SVGFontExport::implCollectGlyphs()
80 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
82 pVDev
->EnableOutput( false );
84 for (auto const& elem
: maObjects
)
86 if( elem
.HasRepresentation() )
88 const GDIMetaFile
& rMtf
= elem
.GetRepresentation();
92 for( size_t i
= 0, nCount
= rMtf
.GetActionSize(); i
< nCount
; ++i
)
95 MetaAction
* pAction
= rMtf
.GetAction( i
);
96 const MetaActionType nType
= pAction
->GetType();
100 case MetaActionType::TEXT
:
102 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
103 sal_Int32 aLength
=std::min( pA
->GetText().getLength(), pA
->GetLen() );
104 aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
108 case MetaActionType::TEXTRECT
:
110 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
111 aText
= pA
->GetText();
115 case MetaActionType::TEXTARRAY
:
117 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
118 sal_Int32 aLength
=std::min( pA
->GetText().getLength(), pA
->GetLen() );
119 aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
123 case MetaActionType::STRETCHTEXT
:
125 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
126 sal_Int32 aLength
=std::min( pA
->GetText().getLength(), pA
->GetLen() );
127 aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
132 pAction
->Execute( pVDev
);
136 if( !aText
.isEmpty() )
138 GlyphSet
& rGlyphSet
= implGetGlyphSet( pVDev
->GetFont() );
139 css::uno::Reference
< css::i18n::XBreakIterator
> xBI(
140 vcl::unohelper::CreateBreakIterator() );
144 const css::lang::Locale
& rLocale
= Application::GetSettings().GetLanguageTag().getLocale();
145 sal_Int32 nCurPos
= 0, nLastPos
= -1;
147 while( ( nCurPos
< aText
.getLength() ) && ( nCurPos
> nLastPos
) )
149 sal_Int32 nCount2
= 1;
152 nCurPos
= xBI
->nextCharacters( aText
, nCurPos
, rLocale
,
153 css::i18n::CharacterIteratorMode::SKIPCELL
,
156 rGlyphSet
.insert( aText
.copy( nLastPos
, nCurPos
- nLastPos
) );
161 const sal_Unicode
* pStr
= aText
.getStr();
163 for( sal_uInt32 k
= 0, nLen
= aText
.getLength(); k
< nLen
; ++k
)
164 rGlyphSet
.insert( OUString( pStr
[ k
] ) );
175 void SVGFontExport::implEmbedFont( const vcl::Font
& rFont
)
177 if( !mrExport
.IsEmbedFonts() )
180 GlyphSet
& rGlyphSet
= implGetGlyphSet( rFont
);
182 if( rGlyphSet
.empty() )
186 SvXMLElementExport
aExp( mrExport
, XML_NAMESPACE_NONE
, u
"defs"_ustr
, true, true );
187 OUString
aCurIdStr( u
"EmbeddedFont_"_ustr
);
188 OUString
aUnitsPerEM( OUString::number( nFontEM
) );
189 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
190 vcl::Font
aFont( rFont
);
192 aFont
.SetFontSize( Size( 0, nFontEM
) );
193 aFont
.SetAlignment( ALIGN_BASELINE
);
195 pVDev
->SetMapMode(MapMode(MapUnit::Map100thMM
));
196 pVDev
->SetFont( aFont
);
198 aCurIdStr
+= OUString::number( ++mnCurFontId
);
199 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"id"_ustr
, aCurIdStr
);
200 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"horiz-adv-x"_ustr
, aUnitsPerEM
);
203 SvXMLElementExport
aExp2( mrExport
, XML_NAMESPACE_NONE
, u
"font"_ustr
, true, true );
204 OUString aFontWeight
;
206 const Size
aSize( nFontEM
, nFontEM
);
209 if( aFont
.GetWeight() != WEIGHT_NORMAL
)
210 aFontWeight
= "bold";
212 aFontWeight
= "normal";
215 if( aFont
.GetItalic() != ITALIC_NONE
)
216 aFontStyle
= "italic";
218 aFontStyle
= "normal";
220 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"font-family"_ustr
, GetMappedFontName( rFont
.GetFamilyName() ) );
221 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"units-per-em"_ustr
, aUnitsPerEM
);
222 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"font-weight"_ustr
, aFontWeight
);
223 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"font-style"_ustr
, aFontStyle
);
224 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"ascent"_ustr
, OUString::number( pVDev
->GetFontMetric().GetAscent() ) );
225 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"descent"_ustr
, OUString::number( pVDev
->GetFontMetric().GetDescent() ) );
228 SvXMLElementExport
aExp3( mrExport
, XML_NAMESPACE_NONE
, u
"font-face"_ustr
, true, true );
231 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"horiz-adv-x"_ustr
, OUString::number( aSize
.Width() ) );
235 const tools::PolyPolygon
aMissingGlyphPolyPoly( tools::Rectangle( aPos
, aSize
) );
237 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"d"_ustr
, SVGActionWriter::GetPathString( aMissingGlyphPolyPoly
, false ) );
240 SvXMLElementExport
aExp4( mrExport
, XML_NAMESPACE_NONE
, u
"missing-glyph"_ustr
, true, true );
243 for (auto const& glyph
: rGlyphSet
)
245 implEmbedGlyph( *pVDev
, glyph
);
252 void SVGFontExport::implEmbedGlyph( OutputDevice
const & rOut
, const OUString
& rCellStr
)
254 tools::PolyPolygon aPolyPoly
;
256 if( !rOut
.GetTextOutline( aPolyPoly
, rCellStr
) )
259 tools::Rectangle aBoundRect
;
261 aPolyPoly
.Scale( 1.0, -1.0 );
263 if (rCellStr
== " " || !rOut
.GetTextBoundRect(aBoundRect
, rCellStr
))
264 aBoundRect
= tools::Rectangle( Point( 0, 0 ), Size( rOut
.GetTextWidth( rCellStr
), 0 ) );
266 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"unicode"_ustr
, rCellStr
);
267 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"horiz-adv-x"_ustr
, OUString::number( aBoundRect
.GetWidth() ) );
269 const OUString
aPathString( SVGActionWriter::GetPathString( aPolyPoly
, false ) );
270 if( !aPathString
.isEmpty() )
272 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, u
"d"_ustr
, aPathString
);
276 SvXMLElementExport
aExp( mrExport
, XML_NAMESPACE_NONE
, u
"glyph"_ustr
, true, true );
281 void SVGFontExport::EmbedFonts()
285 for (auto const& glyph
: maGlyphTree
)
287 const FontWeightMap
& rFontWeightMap
= glyph
.second
;
288 for (auto const& fontWeight
: rFontWeightMap
)
290 const FontItalicMap
& rFontItalicMap
= fontWeight
.second
;
291 for (auto const& fontItalic
: rFontItalicMap
)
295 aFont
.SetFamilyName( glyph
.first
);
296 aFont
.SetWeight( fontWeight
.first
);
297 aFont
.SetItalic( fontItalic
.first
);
299 implEmbedFont( aFont
);
306 OUString
SVGFontExport::GetMappedFontName( std::u16string_view rFontName
) const
308 OUString
aRet( o3tl::getToken(rFontName
, 0, ';' ) );
316 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */