From 989d0953a4d69bef3c8aba8e9dc7758194adcdc4 Mon Sep 17 00:00:00 2001 From: Luboš Luňák Date: Fri, 1 Feb 2013 14:26:36 +0100 Subject: basic support for embedded fonts in odt (fdo#42195) There are still places that should be improved a bit, but this works. Change-Id: Ieb7947a294ec95b6fd8cec2e8c4bc731e2594c42 --- xmloff/Library_xo.mk | 1 + xmloff/inc/xmloff/XMLFontAutoStylePool.hxx | 5 +- xmloff/inc/xmloff/XMLFontStylesContext.hxx | 39 +++++++++ xmloff/inc/xmloff/xmlimp.hxx | 2 + xmloff/inc/xmloff/xmltoken.hxx | 2 + xmloff/source/core/xmltoken.cxx | 2 + xmloff/source/style/XMLFontAutoStylePool.cxx | 111 +++++++++++++++++++++++- xmloff/source/style/XMLFontStylesContext.cxx | 124 +++++++++++++++++++++++++++ 8 files changed, 283 insertions(+), 3 deletions(-) (limited to 'xmloff') diff --git a/xmloff/Library_xo.mk b/xmloff/Library_xo.mk index a24b6fd27812..7ffa8ba4c459 100644 --- a/xmloff/Library_xo.mk +++ b/xmloff/Library_xo.mk @@ -50,6 +50,7 @@ $(eval $(call gb_Library_use_libraries,xo,\ svl \ tl \ utl \ + vcl \ $(gb_UWINAPI) \ )) diff --git a/xmloff/inc/xmloff/XMLFontAutoStylePool.hxx b/xmloff/inc/xmloff/XMLFontAutoStylePool.hxx index 4fd666b50efc..8ed243daa321 100644 --- a/xmloff/inc/xmloff/XMLFontAutoStylePool.hxx +++ b/xmloff/inc/xmloff/XMLFontAutoStylePool.hxx @@ -37,6 +37,9 @@ class XMLOFF_DLLPUBLIC XMLFontAutoStylePool : public UniRefBase XMLFontAutoStylePool_Impl *pPool; XMLFontAutoStylePoolNames_Impl m_aNames; sal_uInt32 nName; + bool tryToEmbedFonts; + + OUString embedFontFile( const OUString& fontUrl, const char* style ); protected: @@ -44,7 +47,7 @@ protected: public: - XMLFontAutoStylePool( SvXMLExport& rExport ); + XMLFontAutoStylePool( SvXMLExport& rExport, bool tryToEmbedFonts = false ); ~XMLFontAutoStylePool(); ::rtl::OUString Add( diff --git a/xmloff/inc/xmloff/XMLFontStylesContext.hxx b/xmloff/inc/xmloff/XMLFontStylesContext.hxx index e48ae0a60c48..7a50fc76ee2f 100644 --- a/xmloff/inc/xmloff/XMLFontStylesContext.hxx +++ b/xmloff/inc/xmloff/XMLFontStylesContext.hxx @@ -121,12 +121,51 @@ public: sal_Int32 nPitchIdx, sal_Int32 nCharsetIdx ) const; + OUString familyName() const; + SvXMLImportContext * CreateChildContext( sal_uInt16 nPrefix, const ::rtl::OUString& rLocalName, const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList ); }; +/// Handles +class XMLFontStyleContextFontFaceSrc : public SvXMLImportContext +{ + const XMLFontStyleContext_Impl& font; +public: + + TYPEINFO(); + + XMLFontStyleContextFontFaceSrc( SvXMLImport& rImport, sal_uInt16 nPrfx, + const ::rtl::OUString& rLName, + const XMLFontStyleContext_Impl& font ); + + virtual SvXMLImportContext * CreateChildContext( + sal_uInt16 nPrefix, + const ::rtl::OUString& rLocalName, + const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList ); +}; + +/// Handles +class XMLFontStyleContextFontFaceUri : public SvXMLStyleContext +{ + const XMLFontStyleContext_Impl& font; + void handleEmbeddedFont( const OUString& url ); +public: + + TYPEINFO(); + + XMLFontStyleContextFontFaceUri( SvXMLImport& rImport, sal_uInt16 nPrfx, + const ::rtl::OUString& rLName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::xml::sax::XAttributeList > & xAttrList, + const XMLFontStyleContext_Impl& font ); + + virtual void SetAttribute( sal_uInt16 nPrefixKey, const OUString& rLocalName, + const OUString& rValue ); +}; + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/inc/xmloff/xmlimp.hxx b/xmloff/inc/xmloff/xmlimp.hxx index d97fe847ceda..7e6381a867ad 100644 --- a/xmloff/inc/xmloff/xmlimp.hxx +++ b/xmloff/inc/xmloff/xmlimp.hxx @@ -453,6 +453,8 @@ public: @see mbIsGraphicLoadOnDemandSupported */ bool isGraphicLoadOnDemandSupported() const; + + virtual void NotifyEmbeddedFontRead() {}; }; inline UniReference< XMLTextImportHelper > SvXMLImport::GetTextImport() diff --git a/xmloff/inc/xmloff/xmltoken.hxx b/xmloff/inc/xmloff/xmltoken.hxx index c4dff1c69ae8..13b59fe083fb 100644 --- a/xmloff/inc/xmloff/xmltoken.hxx +++ b/xmloff/inc/xmloff/xmltoken.hxx @@ -2457,6 +2457,8 @@ namespace xmloff { namespace token { XML_SCRIPTS, XML_FONT_FACE_DECLS, XML_FONT_FACE, + XML_FONT_FACE_SRC, + XML_FONT_FACE_URI, XML_FONT_ADORNMENTS, XML_INCH, XML_SPACE_AFTER, diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 4011f4963c2e..03180526de8f 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -2459,6 +2459,8 @@ namespace xmloff { namespace token { TOKEN( "scripts", XML_SCRIPTS ), TOKEN( "font-face-decls", XML_FONT_FACE_DECLS ), TOKEN( "font-face", XML_FONT_FACE ), + TOKEN( "font-face-src", XML_FONT_FACE_SRC ), + TOKEN( "font-face-uri", XML_FONT_FACE_URI ), TOKEN( "font-adornments", XML_FONT_ADORNMENTS ), TOKEN( "inch", XML_INCH ), TOKEN( "space-after", XML_SPACE_AFTER ), diff --git a/xmloff/source/style/XMLFontAutoStylePool.cxx b/xmloff/source/style/XMLFontAutoStylePool.cxx index a496e79cbac9..0f92d66c41e9 100644 --- a/xmloff/source/style/XMLFontAutoStylePool.cxx +++ b/xmloff/source/style/XMLFontAutoStylePool.cxx @@ -25,11 +25,17 @@ #include "fonthdl.hxx" #include #include +#include +#include +#include +#include +#include using ::rtl::OUString; using ::rtl::OUStringBuffer; +using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::xmloff::token; @@ -128,9 +134,10 @@ public: ~XMLFontAutoStylePool_Impl() { DeleteAndDestroyAll(); } }; -XMLFontAutoStylePool::XMLFontAutoStylePool( SvXMLExport& rExp ) : +XMLFontAutoStylePool::XMLFontAutoStylePool( SvXMLExport& rExp, bool _tryToEmbedFonts ) : rExport( rExp ), - pPool( new XMLFontAutoStylePool_Impl ) + pPool( new XMLFontAutoStylePool_Impl ), + tryToEmbedFonts( _tryToEmbedFonts ) { } @@ -226,6 +233,7 @@ void XMLFontAutoStylePool::exportXML() XMLFontEncodingPropHdl aEncHdl; const SvXMLUnitConverter& rUnitConv = GetExport().GetMM100UnitConverter(); + std::map< OUString, OUString > fontFilesMap; // our url to document url sal_uInt32 nCount = pPool->size(); for( sal_uInt32 i=0; i fileUrls; + static const char* const styles[] = { "", "b", "i", "bi" }; + for( unsigned int j = 0; + j < SAL_N_ELEMENTS( styles ); + ++j ) + { + OUString fileUrl = TemporaryFonts::fileUrlForFont( pEntry->GetFamilyName(), styles[ j ] ); + if( !fontFilesMap.count( fileUrl )) + { + OUString docUrl = embedFontFile( fileUrl, styles[ j ] ); + if( !docUrl.isEmpty()) + fontFilesMap[ fileUrl ] = docUrl; + else + continue; // --> failed (most probably this font is not embedded) + } + fileUrls.push_back( fileUrl ); + } + if( !fileUrls.empty()) + { + SvXMLElementExport fontFaceSrc( GetExport(), XML_NAMESPACE_SVG, + XML_FONT_FACE_SRC, true, true ); + for( std::vector< OUString >::const_iterator it = fileUrls.begin(); + it != fileUrls.end(); + ++it ) + { + if( fontFilesMap.count( *it )) + { + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, fontFilesMap[ *it ] ); + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, "simple" ); + SvXMLElementExport fontFaceUri( GetExport(), XML_NAMESPACE_SVG, + XML_FONT_FACE_URI, true, true ); + } + } + } + } + } +} + +OUString XMLFontAutoStylePool::embedFontFile( const OUString& fileUrl, const char* style ) +{ + try + { + osl::File file( fileUrl ); + if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None ) + return OUString(); + uno::Reference< embed::XStorage > storage; + storage.set( GetExport().GetTargetStorage()->openStorageElement( OUString( "Fonts" ), + ::embed::ElementModes::WRITE ), uno::UNO_QUERY_THROW ); + int index = 0; + OUString name; + do + { + name = "font" + OUString::number( ++index ) + OUString::createFromAscii( style ) + ".ttf"; + } while( storage->hasByName( name ) ); + uno::Reference< io::XOutputStream > outputStream; + outputStream.set( storage->openStreamElement( name, ::embed::ElementModes::WRITE ), UNO_QUERY_THROW ); + uno::Reference < beans::XPropertySet > propertySet( outputStream, uno::UNO_QUERY ); + assert( propertySet.is()); + propertySet->setPropertyValue( "MediaType", uno::makeAny( OUString( "application/x-font-ttf" ))); // TODO + for(;;) + { + char buffer[ 4096 ]; + sal_uInt64 readSize; + sal_Bool eof; + if( file.isEndOfFile( &eof ) != osl::File::E_None ) + { + SAL_WARN( "xmloff", "Error reading font file " << fileUrl ); + outputStream->closeOutput(); + return OUString(); + } + if( eof ) + break; + if( file.read( buffer, 4096, readSize ) != osl::File::E_None ) + { + SAL_WARN( "xmloff", "Error reading font file " << fileUrl ); + outputStream->closeOutput(); + return OUString(); + } + if( readSize == 0 ) + break; + outputStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize )); + } + outputStream->closeOutput(); + if( storage.is() ) + { + Reference< embed::XTransactedObject > transaction( storage, UNO_QUERY ); + if( transaction.is()) + { + transaction->commit(); + return "Fonts/" + name; + } + } + } catch( const Exception& e ) + { + SAL_WARN( "xmloff", "Exception when embedding a font file:" << e.Message ); } + return OUString(); } diff --git a/xmloff/source/style/XMLFontStylesContext.cxx b/xmloff/source/style/XMLFontStylesContext.cxx index 6a6e290f53d8..8cdb5ed012a3 100644 --- a/xmloff/source/style/XMLFontStylesContext.cxx +++ b/xmloff/source/style/XMLFontStylesContext.cxx @@ -21,8 +21,11 @@ #include #include +#include +#include #include +#include #include #include "xmloff/xmlnmspe.hxx" @@ -172,6 +175,127 @@ void XMLFontStyleContext_Impl::FillProperties( } } +SvXMLImportContext * XMLFontStyleContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const ::rtl::OUString& rLocalName, + const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList ) +{ + if( nPrefix == XML_NAMESPACE_SVG && IsXMLToken( rLocalName, XML_FONT_FACE_SRC )) + return new XMLFontStyleContextFontFaceSrc( GetImport(), nPrefix, rLocalName, *this ); + return SvXMLStyleContext::CreateChildContext( nPrefix, rLocalName, xAttrList ); +} + +OUString XMLFontStyleContext_Impl::familyName() const +{ + OUString ret; + aFamilyName >>= ret; + return ret; +} + + +TYPEINIT1( XMLFontStyleContextFontFaceSrc, SvXMLImportContext ); + +XMLFontStyleContextFontFaceSrc::XMLFontStyleContextFontFaceSrc( SvXMLImport& rImport, + sal_uInt16 nPrfx, const OUString& rLName, + const XMLFontStyleContext_Impl& _font ) + : SvXMLImportContext( rImport, nPrfx, rLName ) + , font( _font ) +{ +} + +SvXMLImportContext * XMLFontStyleContextFontFaceSrc::CreateChildContext( + sal_uInt16 nPrefix, + const ::rtl::OUString& rLocalName, + const ::com::sun::star::uno::Reference< ::com::sun::star::xml::sax::XAttributeList > & xAttrList ) +{ + if( nPrefix == XML_NAMESPACE_SVG && IsXMLToken( rLocalName, XML_FONT_FACE_URI )) + return new XMLFontStyleContextFontFaceUri( GetImport(), nPrefix, rLocalName, xAttrList, font ); + return SvXMLImportContext::CreateChildContext( nPrefix, rLocalName, xAttrList ); +} + + +TYPEINIT1( XMLFontStyleContextFontFaceUri, SvXMLImportContext ); + +XMLFontStyleContextFontFaceUri::XMLFontStyleContextFontFaceUri( SvXMLImport& rImport, + sal_uInt16 nPrfx, const OUString& rLName, + const ::com::sun::star::uno::Reference< + ::com::sun::star::xml::sax::XAttributeList > & xAttrList, + const XMLFontStyleContext_Impl& _font ) + : SvXMLStyleContext( rImport, nPrfx, rLName, xAttrList ) + , font( _font ) +{ +} + +void XMLFontStyleContextFontFaceUri::SetAttribute( sal_uInt16 nPrefixKey, const OUString& rLocalName, + const OUString& rValue ) +{ + if( nPrefixKey == XML_NAMESPACE_XLINK && IsXMLToken( rLocalName, XML_HREF )) + handleEmbeddedFont( rValue ); + else + SvXMLStyleContext::SetAttribute( nPrefixKey, rLocalName, rValue ); +} + +void XMLFontStyleContextFontFaceUri::handleEmbeddedFont( const OUString& url ) +{ + OUString fontName = font.familyName(); + const char* style = ""; + // OOXML needs to know what kind of style the font is (regular, italic, bold, bold-italic), + // and the TemporaryFonts class is modelled after it. But ODF doesn't (need to) include + // this information, so try to guess from the name (LO encodes the style), otherwise + // go with regular and hope it works. + if( url.endsWithIgnoreAsciiCase( "bi.ttf" )) + style = "bi"; + else if( url.endsWithIgnoreAsciiCase( "b.ttf" )) + style = "b"; + else if( url.endsWithIgnoreAsciiCase( "i.ttf" )) + style = "i"; + // If there's any giveMeStreamForThisURL(), then it's well-hidden for me to find it. + if( GetImport().IsPackageURL( url )) + { + uno::Reference< embed::XStorage > storage; + storage.set( GetImport().GetSourceStorage(), UNO_QUERY_THROW ); + if( url.indexOf( '/' ) > -1 ) // TODO what if more levels? + storage.set( storage->openStorageElement( url.copy( 0, url.indexOf( '/' )), + ::embed::ElementModes::READ ), uno::UNO_QUERY_THROW ); + OUString fileUrl = TemporaryFonts::fileUrlForFont( fontName, style ); + osl::File file( fileUrl ); + switch( file.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_Write )) + { + case osl::File::E_None: + break; // ok + case osl::File::E_EXIST: + return; // Assume it's already been added correctly. + default: + SAL_WARN( "xmloff", "Cannot open file for temporary font" ); + return; + } + uno::Reference< io::XInputStream > inputStream; + inputStream.set( storage->openStreamElement( url.copy( url.indexOf( '/' ) + 1 ), ::embed::ElementModes::READ ), + UNO_QUERY_THROW ); + for(;;) + { + uno::Sequence< sal_Int8 > buffer; + int read = inputStream->readBytes( buffer, 1024 ); + sal_uInt64 dummy; + if( read > 0 ) + file.write( buffer.getConstArray(), read, dummy ); + if( read < 1024 ) + break; + } + inputStream->closeInput(); + if( file.close() != osl::File::E_None ) + { + SAL_WARN( "xmloff", "Writing temporary font file failed" ); + osl::File::remove( fileUrl ); + return; + } + TemporaryFonts::activateFont( fontName, fileUrl ); + GetImport().NotifyEmbeddedFontRead(); + } + else + SAL_WARN( "xmloff", "External URL for font file not handled." ); +} + SvXMLStyleContext *XMLFontStylesContext::CreateStyleChildContext( sal_uInt16 nPrefix, const ::rtl::OUString& rLocalName, -- cgit