Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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 : */
9 :
10 : #include <vcl/embeddedfontshelper.hxx>
11 :
12 : #include <osl/file.hxx>
13 : #include <rtl/bootstrap.hxx>
14 : #include <sft.hxx>
15 : #include <vcl/outdev.hxx>
16 : #include <vcl/svapp.hxx>
17 :
18 : #include <boost/scoped_ptr.hpp>
19 : #include <fontsubset.hxx>
20 : #include <outdev.h>
21 : #include <outfont.hxx>
22 : #include <salgdi.hxx>
23 :
24 : using namespace com::sun::star;
25 : using namespace vcl;
26 :
27 490 : static void clearDir( const OUString& path )
28 : {
29 490 : osl::Directory dir( path );
30 490 : if( dir.reset() == osl::Directory::E_None )
31 : {
32 : for(;;)
33 : {
34 0 : osl::DirectoryItem item;
35 0 : if( dir.getNextItem( item ) != osl::Directory::E_None )
36 0 : break;
37 0 : osl::FileStatus status( osl_FileStatus_Mask_FileURL );
38 0 : if( item.getFileStatus( status ) == osl::File::E_None )
39 0 : osl::File::remove( status.getFileURL());
40 0 : }
41 490 : }
42 490 : }
43 :
44 245 : void EmbeddedFontsHelper::clearTemporaryFontFiles()
45 : {
46 245 : OUString path = "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
47 245 : rtl::Bootstrap::expandMacros( path );
48 245 : path += "/user/temp/embeddedfonts/";
49 245 : clearDir( path + "fromdocs/" );
50 245 : clearDir( path + "fromsystem/" );
51 245 : }
52 :
53 0 : bool EmbeddedFontsHelper::addEmbeddedFont( uno::Reference< io::XInputStream > stream, const OUString& fontName,
54 : const char* extra, std::vector< unsigned char > key )
55 : {
56 0 : OUString fileUrl = EmbeddedFontsHelper::fileUrlForTemporaryFont( fontName, extra );
57 0 : osl::File file( fileUrl );
58 0 : switch( file.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_Write ))
59 : {
60 : case osl::File::E_None:
61 0 : break; // ok
62 : case osl::File::E_EXIST:
63 0 : return true; // Assume it's already been added correctly.
64 : default:
65 : SAL_WARN( "vcl.fonts", "Cannot open file for temporary font" );
66 0 : return false;
67 : }
68 0 : size_t keyPos = 0;
69 0 : std::vector< char > fontData;
70 0 : fontData.reserve( 1000000 );
71 : for(;;)
72 : {
73 0 : uno::Sequence< sal_Int8 > buffer;
74 0 : sal_uInt64 read = stream->readBytes( buffer, 1024 );
75 0 : for( sal_uInt64 pos = 0;
76 0 : pos < read && keyPos < key.size();
77 : ++pos )
78 0 : buffer[ pos ] ^= key[ keyPos++ ];
79 0 : if( read > 0 )
80 : {
81 0 : sal_uInt64 writtenTotal = 0;
82 0 : while( writtenTotal < read )
83 : {
84 : sal_uInt64 written;
85 0 : file.write( buffer.getConstArray(), read, written );
86 0 : writtenTotal += written;
87 : }
88 : }
89 0 : fontData.insert( fontData.end(), buffer.getConstArray(), buffer.getConstArray() + read );
90 0 : if( read <= 0 )
91 0 : break;
92 0 : }
93 0 : if( file.close() != osl::File::E_None )
94 : {
95 : SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
96 0 : osl::File::remove( fileUrl );
97 0 : return false;
98 : }
99 0 : if( !sufficientFontRights( &fontData.front(), fontData.size(), EditingAllowed ))
100 : {
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 0 : osl::File::remove( fileUrl );
106 0 : return false;
107 : }
108 0 : EmbeddedFontsHelper::activateFont( fontName, fileUrl );
109 0 : return true;
110 : }
111 :
112 0 : OUString EmbeddedFontsHelper::fileUrlForTemporaryFont( const OUString& fontName, const char* extra )
113 : {
114 0 : OUString path = "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
115 0 : rtl::Bootstrap::expandMacros( path );
116 0 : path += "/user/temp/embeddedfonts/fromdocs/";
117 0 : osl::Directory::createPath( path );
118 0 : OUString filename = fontName;
119 : static int uniqueCounter = 0;
120 0 : if( strcmp( extra, "?" ) == 0 )
121 0 : filename += OUString::number( uniqueCounter++ );
122 : else
123 0 : filename += OStringToOUString( extra, RTL_TEXTENCODING_ASCII_US );
124 0 : filename += ".ttf"; // TODO is it always ttf?
125 0 : return path + filename;
126 : }
127 :
128 0 : void EmbeddedFontsHelper::activateFont( const OUString& fontName, const OUString& fileUrl )
129 : {
130 0 : OutputDevice *pDevice = Application::GetDefaultDevice();
131 0 : pDevice->AddTempDevFont( fileUrl, fontName );
132 0 : pDevice->ImplUpdateAllFontData( true );
133 0 : }
134 :
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 0 : bool EmbeddedFontsHelper::sufficientFontRights( const void* data, long size, FontRights rights )
141 : {
142 : TrueTypeFont* font;
143 0 : if( OpenTTFontBuffer( data, size, 0 /*TODO*/, &font ) == SF_OK )
144 : {
145 : TTGlobalFontInfo info;
146 0 : GetTTGlobalFontInfo( font, &info );
147 0 : CloseTTFont( font );
148 : // http://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc
149 0 : int copyright = info.typeFlags & TYPEFLAG_COPYRIGHT_MASK;
150 0 : switch( rights )
151 : {
152 : case ViewingAllowed:
153 : // Embedding not restricted completely.
154 0 : return ( copyright & 0x02 ) != 0x02;
155 : case EditingAllowed:
156 : // Font is installable or editable.
157 0 : return copyright == 0 || ( copyright & 0x08 );
158 : }
159 : }
160 0 : return true; // no known restriction
161 : }
162 :
163 0 : OUString EmbeddedFontsHelper::fontFileUrl( const OUString& familyName, FontFamily family, FontItalic italic,
164 : FontWeight weight, FontPitch pitch, rtl_TextEncoding, FontRights rights )
165 : {
166 0 : OUString path = "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
167 0 : rtl::Bootstrap::expandMacros( path );
168 0 : path += "/user/temp/embeddedfonts/fromsystem/";
169 0 : osl::Directory::createPath( path );
170 0 : OUString filename = familyName + "_" + OUString::number( family ) + "_" + OUString::number( italic )
171 0 : + "_" + OUString::number( weight ) + "_" + OUString::number( pitch );
172 0 : filename += ".ttf"; // TODO is it always ttf?
173 0 : OUString url = path + filename;
174 0 : if( osl::File( url ).open( osl_File_OpenFlag_Read ) == osl::File::E_None ) // = exists()
175 : {
176 : // File with contents of the font file already exists, assume it's been created by a previous call.
177 0 : return url;
178 : }
179 0 : bool ok = false;
180 0 : SalGraphics* graphics = Application::GetDefaultDevice()->ImplGetGraphics();
181 0 : ImplDevFontList fonts;
182 0 : graphics->GetDevFontList( &fonts );
183 0 : boost::scoped_ptr< ImplGetDevFontList > fontInfo( fonts.GetDevFontList());
184 0 : PhysicalFontFace* selected = NULL;
185 0 : for( int i = 0;
186 0 : i < fontInfo->Count();
187 : ++i )
188 : {
189 0 : PhysicalFontFace* f = fontInfo->Get( i );
190 0 : if( f->GetFamilyName() == familyName )
191 : {
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 0 : if(( family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
198 0 : && ( italic == ITALIC_DONTKNOW || f->GetSlant() == italic )
199 0 : && ( weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
200 0 : && ( pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
201 : { // Exact match, return it immediately.
202 0 : selected = f;
203 0 : break;
204 : }
205 0 : if(( f->GetFamilyType() == FAMILY_DONTKNOW || family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
206 0 : && ( f->GetSlant() == ITALIC_DONTKNOW || italic == ITALIC_DONTKNOW || f->GetSlant() == italic )
207 0 : && ( f->GetWeight() == WEIGHT_DONTKNOW || weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
208 0 : && ( 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 0 : selected = f;
211 : }
212 : }
213 : }
214 0 : if( selected != NULL )
215 : {
216 : sal_Ucs unicodes[ 256 ];
217 0 : for( int i = 0;
218 : i < 256;
219 : ++i )
220 0 : unicodes[ i ] = 'A'; // Just something, not needed, but GetEmbedFontData() needs it.
221 : sal_Int32 widths[ 256 ];
222 0 : FontSubsetInfo info;
223 : long size;
224 0 : if( const void* data = graphics->GetEmbedFontData( selected, unicodes, widths, info, &size ))
225 : {
226 0 : if( sufficientFontRights( data, size, rights ))
227 : {
228 0 : osl::File file( url );
229 0 : if( file.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) == osl::File::E_None )
230 : {
231 0 : sal_uInt64 written = 0;
232 0 : sal_uInt64 totalSize = size;
233 0 : bool error = false;
234 0 : while( written < totalSize && !error)
235 : {
236 : sal_uInt64 nowWritten;
237 0 : switch( file.write( static_cast< const char* >( data ) + written, size - written, nowWritten ))
238 : {
239 : case osl::File::E_None:
240 0 : written += nowWritten;
241 0 : break;
242 : case osl::File::E_AGAIN:
243 : case osl::File::E_INTR:
244 0 : break;
245 : default:
246 0 : error = true;
247 0 : break;
248 : }
249 : }
250 0 : file.close();
251 0 : if( error )
252 0 : osl::File::remove( url );
253 : else
254 0 : ok = true;
255 0 : }
256 : }
257 0 : graphics->FreeEmbedFontData( data, size );
258 0 : }
259 : }
260 0 : return ok ? url : "";
261 465 : }
262 :
263 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|