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/.
10 #include <vcl/embeddedfontshelper.hxx>
12 #include <osl/file.hxx>
13 #include <rtl/bootstrap.hxx>
15 #include <vcl/outdev.hxx>
16 #include <vcl/svapp.hxx>
18 #include <boost/scoped_ptr.hpp>
19 #include <fontsubset.hxx>
21 #include <outfont.hxx>
24 using namespace com::sun::star
;
27 static void clearDir( const OUString
& path
)
29 osl::Directory
dir( path
);
30 if( dir
.reset() == osl::Directory::E_None
)
34 osl::DirectoryItem item
;
35 if( dir
.getNextItem( item
) != osl::Directory::E_None
)
37 osl::FileStatus
status( osl_FileStatus_Mask_FileURL
);
38 if( item
.getFileStatus( status
) == osl::File::E_None
)
39 osl::File::remove( status
.getFileURL());
44 void EmbeddedFontsHelper::clearTemporaryFontFiles()
46 OUString path
= "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
47 rtl::Bootstrap::expandMacros( path
);
48 path
+= "/user/temp/embeddedfonts/";
49 clearDir( path
+ "fromdocs/" );
50 clearDir( path
+ "fromsystem/" );
53 bool EmbeddedFontsHelper::addEmbeddedFont( uno::Reference
< io::XInputStream
> stream
, const OUString
& fontName
,
54 const char* extra
, std::vector
< unsigned char > key
)
56 OUString fileUrl
= EmbeddedFontsHelper::fileUrlForTemporaryFont( fontName
, extra
);
57 osl::File
file( fileUrl
);
58 switch( file
.open( osl_File_OpenFlag_Create
| osl_File_OpenFlag_Write
))
60 case osl::File::E_None
:
62 case osl::File::E_EXIST
:
63 return true; // Assume it's already been added correctly.
65 SAL_WARN( "vcl.fonts", "Cannot open file for temporary font" );
69 std::vector
< char > fontData
;
70 fontData
.reserve( 1000000 );
73 uno::Sequence
< sal_Int8
> buffer
;
74 sal_uInt64 read
= stream
->readBytes( buffer
, 1024 );
75 for( sal_uInt64 pos
= 0;
76 pos
< read
&& keyPos
< key
.size();
78 buffer
[ pos
] ^= key
[ keyPos
++ ];
81 sal_uInt64 writtenTotal
= 0;
82 while( writtenTotal
< read
)
85 file
.write( buffer
.getConstArray(), read
, written
);
86 writtenTotal
+= written
;
89 fontData
.insert( fontData
.end(), buffer
.getConstArray(), buffer
.getConstArray() + read
);
93 if( file
.close() != osl::File::E_None
)
95 SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
96 osl::File::remove( fileUrl
);
99 if( !sufficientFontRights( &fontData
.front(), fontData
.size(), EditingAllowed
))
101 // It would be actually better to open the document in read-only mode in this case,
102 // warn the user about this, and provide a button to drop the font(s) in order
103 // to switch to editing.
104 SAL_INFO( "vcl.fonts", "Ignoring embedded font that is not usable for editing" );
105 osl::File::remove( fileUrl
);
108 EmbeddedFontsHelper::activateFont( fontName
, fileUrl
);
112 OUString
EmbeddedFontsHelper::fileUrlForTemporaryFont( const OUString
& fontName
, const char* extra
)
114 OUString path
= "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
115 rtl::Bootstrap::expandMacros( path
);
116 path
+= "/user/temp/embeddedfonts/fromdocs/";
117 osl::Directory::createPath( path
);
118 OUString filename
= fontName
;
119 static int uniqueCounter
= 0;
120 if( strcmp( extra
, "?" ) == 0 )
121 filename
+= OUString::number( uniqueCounter
++ );
123 filename
+= OStringToOUString( extra
, RTL_TEXTENCODING_ASCII_US
);
124 filename
+= ".ttf"; // TODO is it always ttf?
125 return path
+ filename
;
128 void EmbeddedFontsHelper::activateFont( const OUString
& fontName
, const OUString
& fileUrl
)
130 OutputDevice
*pDevice
= Application::GetDefaultDevice();
131 pDevice
->AddTempDevFont( fileUrl
, fontName
);
132 pDevice
->ImplUpdateAllFontData( true );
135 // Check if it's (legally) allowed to embed the font file into a document
136 // (ttf has a flag allowing this). PhysicalFontFace::IsEmbeddable() appears
137 // to have a different meaning (guessing from code, IsSubsettable() might
138 // possibly mean it's ttf, while IsEmbeddable() might mean it's type1).
139 // So just try to open the data as ttf and see.
140 bool EmbeddedFontsHelper::sufficientFontRights( const void* data
, long size
, FontRights rights
)
143 if( OpenTTFontBuffer( data
, size
, 0 /*TODO*/, &font
) == SF_OK
)
145 TTGlobalFontInfo info
;
146 GetTTGlobalFontInfo( font
, &info
);
148 // http://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc
149 int copyright
= info
.typeFlags
& TYPEFLAG_COPYRIGHT_MASK
;
153 // Embedding not restricted completely.
154 return ( copyright
& 0x02 ) != 0x02;
156 // Font is installable or editable.
157 return copyright
== 0 || ( copyright
& 0x08 );
160 return true; // no known restriction
163 OUString
EmbeddedFontsHelper::fontFileUrl( const OUString
& familyName
, FontFamily family
, FontItalic italic
,
164 FontWeight weight
, FontPitch pitch
, rtl_TextEncoding
, FontRights rights
)
166 OUString path
= "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
167 rtl::Bootstrap::expandMacros( path
);
168 path
+= "/user/temp/embeddedfonts/fromsystem/";
169 osl::Directory::createPath( path
);
170 OUString filename
= familyName
+ "_" + OUString::number( family
) + "_" + OUString::number( italic
)
171 + "_" + OUString::number( weight
) + "_" + OUString::number( pitch
);
172 filename
+= ".ttf"; // TODO is it always ttf?
173 OUString url
= path
+ filename
;
174 if( osl::File( url
).open( osl_File_OpenFlag_Read
) == osl::File::E_None
) // = exists()
176 // File with contents of the font file already exists, assume it's been created by a previous call.
180 SalGraphics
* graphics
= Application::GetDefaultDevice()->ImplGetGraphics();
181 ImplDevFontList fonts
;
182 graphics
->GetDevFontList( &fonts
);
183 boost::scoped_ptr
< ImplGetDevFontList
> fontInfo( fonts
.GetDevFontList());
184 PhysicalFontFace
* selected
= NULL
;
186 i
< fontInfo
->Count();
189 PhysicalFontFace
* f
= fontInfo
->Get( i
);
190 if( f
->GetFamilyName() == familyName
)
192 // Ignore comparing text encodings, at least for now. They cannot be trivially compared
193 // (e.g. UCS2 and UTF8 are technically the same characters, just have different encoding,
194 // and just having a unicode font doesn't say what glyphs it actually contains).
195 // It is possible that it still may be needed to do at least some checks here
196 // for some encodings (can one font have more font files for more encodings?).
197 if(( family
== FAMILY_DONTKNOW
|| f
->GetFamilyType() == family
)
198 && ( italic
== ITALIC_DONTKNOW
|| f
->GetSlant() == italic
)
199 && ( weight
== WEIGHT_DONTKNOW
|| f
->GetWeight() == weight
)
200 && ( pitch
== PITCH_DONTKNOW
|| f
->GetPitch() == pitch
))
201 { // Exact match, return it immediately.
205 if(( f
->GetFamilyType() == FAMILY_DONTKNOW
|| family
== FAMILY_DONTKNOW
|| f
->GetFamilyType() == family
)
206 && ( f
->GetSlant() == ITALIC_DONTKNOW
|| italic
== ITALIC_DONTKNOW
|| f
->GetSlant() == italic
)
207 && ( f
->GetWeight() == WEIGHT_DONTKNOW
|| weight
== WEIGHT_DONTKNOW
|| f
->GetWeight() == weight
)
208 && ( f
->GetPitch() == PITCH_DONTKNOW
|| pitch
== PITCH_DONTKNOW
|| f
->GetPitch() == pitch
))
209 { // Some fonts specify 'DONTKNOW' for some things, still a good match, if we don't find a better one.
214 if( selected
!= NULL
)
216 sal_Ucs unicodes
[ 256 ];
220 unicodes
[ i
] = 'A'; // Just something, not needed, but GetEmbedFontData() needs it.
221 sal_Int32 widths
[ 256 ];
224 if( const void* data
= graphics
->GetEmbedFontData( selected
, unicodes
, widths
, info
, &size
))
226 if( sufficientFontRights( data
, size
, rights
))
228 osl::File
file( url
);
229 if( file
.open( osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
) == osl::File::E_None
)
231 sal_uInt64 written
= 0;
232 sal_uInt64 totalSize
= size
;
234 while( written
< totalSize
&& !error
)
236 sal_uInt64 nowWritten
;
237 switch( file
.write( static_cast< const char* >( data
) + written
, size
- written
, nowWritten
))
239 case osl::File::E_None
:
240 written
+= nowWritten
;
242 case osl::File::E_AGAIN
:
243 case osl::File::E_INTR
:
252 osl::File::remove( url
);
257 graphics
->FreeEmbedFontData( data
, size
);
260 return ok
? url
: "";
263 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */