update credits
[LibreOffice.git] / vcl / source / gdi / embeddedfontshelper.cxx
bloba147ea3a419cbc8bff4eaad0b1ded01f91cc5c26
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <vcl/embeddedfontshelper.hxx>
12 #include <osl/file.hxx>
13 #include <rtl/bootstrap.hxx>
14 #include <sft.hxx>
15 #include <vcl/outdev.hxx>
16 #include <vcl/svapp.hxx>
18 #include <boost/scoped_ptr.hpp>
19 #include <fontsubset.hxx>
20 #include <outdev.h>
21 #include <outfont.hxx>
22 #include <salgdi.hxx>
24 using namespace com::sun::star;
25 using namespace vcl;
27 static void clearDir( const OUString& path )
29 osl::Directory dir( path );
30 if( dir.reset() == osl::Directory::E_None )
32 for(;;)
34 osl::DirectoryItem item;
35 if( dir.getNextItem( item ) != osl::Directory::E_None )
36 break;
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:
61 break; // ok
62 case osl::File::E_EXIST:
63 return true; // Assume it's already been added correctly.
64 default:
65 SAL_WARN( "vcl.fonts", "Cannot open file for temporary font" );
66 return false;
68 size_t keyPos = 0;
69 std::vector< char > fontData;
70 fontData.reserve( 1000000 );
71 for(;;)
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();
77 ++pos )
78 buffer[ pos ] ^= key[ keyPos++ ];
79 if( read > 0 )
81 sal_uInt64 writtenTotal = 0;
82 while( writtenTotal < read )
84 sal_uInt64 written;
85 file.write( buffer.getConstArray(), read, written );
86 writtenTotal += written;
89 fontData.insert( fontData.end(), buffer.getConstArray(), buffer.getConstArray() + read );
90 if( read <= 0 )
91 break;
93 if( file.close() != osl::File::E_None )
95 SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
96 osl::File::remove( fileUrl );
97 return false;
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 );
106 return false;
108 EmbeddedFontsHelper::activateFont( fontName, fileUrl );
109 return true;
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++ );
122 else
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 )
142 TrueTypeFont* font;
143 if( OpenTTFontBuffer( data, size, 0 /*TODO*/, &font ) == SF_OK )
145 TTGlobalFontInfo info;
146 GetTTGlobalFontInfo( font, &info );
147 CloseTTFont( font );
148 // http://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc
149 int copyright = info.typeFlags & TYPEFLAG_COPYRIGHT_MASK;
150 switch( rights )
152 case ViewingAllowed:
153 // Embedding not restricted completely.
154 return ( copyright & 0x02 ) != 0x02;
155 case EditingAllowed:
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.
177 return url;
179 bool ok = false;
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;
185 for( int i = 0;
186 i < fontInfo->Count();
187 ++i )
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.
202 selected = f;
203 break;
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.
210 selected = f;
214 if( selected != NULL )
216 sal_Ucs unicodes[ 256 ];
217 for( int i = 0;
218 i < 256;
219 ++i )
220 unicodes[ i ] = 'A'; // Just something, not needed, but GetEmbedFontData() needs it.
221 sal_Int32 widths[ 256 ];
222 FontSubsetInfo info;
223 long size;
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;
233 bool error = false;
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;
241 break;
242 case osl::File::E_AGAIN:
243 case osl::File::E_INTR:
244 break;
245 default:
246 error = true;
247 break;
250 file.close();
251 if( error )
252 osl::File::remove( url );
253 else
254 ok = true;
257 graphics->FreeEmbedFontData( data, size );
260 return ok ? url : "";
263 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */