LibreOffice Module vcl (master)  1
embeddedfontshelper.cxx
Go to the documentation of this file.
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 <sal/config.h>
11 
12 #include <memory>
13 #include <config_folders.h>
14 #include <config_eot.h>
15 
16 #include <osl/file.hxx>
17 #include <rtl/bootstrap.hxx>
18 #include <sal/log.hxx>
19 #include <vcl/svapp.hxx>
21 #include <com/sun/star/io/XInputStream.hpp>
22 
23 #include <outdev.h>
26 #include <salgdi.hxx>
27 #include <sft.hxx>
28 
29 
30 #if ENABLE_EOT
31 extern "C"
32 {
33 namespace libeot
34 {
35 #include <libeot/libeot.h>
36 } // namespace libeot
37 } // extern "C"
38 #endif
39 
40 using namespace com::sun::star;
41 using namespace vcl;
42 
43 static void clearDir( const OUString& path )
44 {
45  osl::Directory dir( path );
46  if( dir.reset() == osl::Directory::E_None )
47  {
48  for(;;)
49  {
50  osl::DirectoryItem item;
51  if( dir.getNextItem( item ) != osl::Directory::E_None )
52  break;
53  osl::FileStatus status( osl_FileStatus_Mask_FileURL );
54  if( item.getFileStatus( status ) == osl::File::E_None )
55  osl::File::remove( status.getFileURL());
56  }
57  }
58 }
59 
61 {
62  OUString path = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
63  rtl::Bootstrap::expandMacros( path );
64  path += "/user/temp/embeddedfonts/";
65  clearDir( path + "fromdocs/" );
66  clearDir( path + "fromsystem/" );
67 }
68 
69 bool EmbeddedFontsHelper::addEmbeddedFont( const uno::Reference< io::XInputStream >& stream, const OUString& fontName,
70  const char* extra, std::vector< unsigned char > const & key, bool eot )
71 {
72  OUString fileUrl = EmbeddedFontsHelper::fileUrlForTemporaryFont( fontName, extra );
73  osl::File file( fileUrl );
74  switch( file.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_Write ))
75  {
76  case osl::File::E_None:
77  break; // ok
78  case osl::File::E_EXIST:
79  return true; // Assume it's already been added correctly.
80  default:
81  SAL_WARN( "vcl.fonts", "Cannot open file for temporary font" );
82  return false;
83  }
84  size_t keyPos = 0;
85  std::vector< char > fontData;
86  fontData.reserve( 1000000 );
87  for(;;)
88  {
89  uno::Sequence< sal_Int8 > buffer;
90  sal_uInt64 read = stream->readBytes( buffer, 1024 );
91  for( sal_uInt64 pos = 0;
92  pos < read && keyPos < key.size();
93  ++pos )
94  buffer[ pos ] ^= key[ keyPos++ ];
95  // if eot, don't write the file out yet, since we need to unpack it first.
96  if( !eot && read > 0 )
97  {
98  sal_uInt64 writtenTotal = 0;
99  while( writtenTotal < read )
100  {
101  sal_uInt64 written;
102  file.write( buffer.getConstArray(), read, written );
103  writtenTotal += written;
104  }
105  }
106  fontData.insert( fontData.end(), buffer.getConstArray(), buffer.getConstArray() + read );
107  if( read <= 0 )
108  break;
109  }
110  bool sufficientFontRights(false);
111 #if ENABLE_EOT
112  if( eot )
113  {
114  unsigned uncompressedFontSize = 0;
115  unsigned char *nakedPointerToUncompressedFont = nullptr;
116  libeot::EOTMetadata eotMetadata;
117  libeot::EOTError uncompressError =
118  libeot::EOT2ttf_buffer( reinterpret_cast<unsigned char *>(fontData.data()), fontData.size(), &eotMetadata, &nakedPointerToUncompressedFont, &uncompressedFontSize );
119  std::shared_ptr<unsigned char> uncompressedFont( nakedPointerToUncompressedFont, libeot::EOTfreeBuffer );
120  if( uncompressError != libeot::EOT_SUCCESS )
121  {
122  SAL_WARN( "vcl.fonts", "Failed to uncompress font" );
123  osl::File::remove( fileUrl );
124  return false;
125  }
126  sal_uInt64 writtenTotal = 0;
127  while( writtenTotal < uncompressedFontSize )
128  {
129  sal_uInt64 written;
130  if( file.write( uncompressedFont.get() + writtenTotal, uncompressedFontSize - writtenTotal, written ) != osl::File::E_None )
131  {
132  SAL_WARN( "vcl.fonts", "Error writing temporary font file" );
133  osl::File::remove( fileUrl );
134  return false;
135  }
136  writtenTotal += written;
137  }
138  sufficientFontRights = libeot::EOTcanLegallyEdit( &eotMetadata );
139  libeot::EOTfreeMetadata( &eotMetadata );
140  }
141 #endif
142 
143  if( file.close() != osl::File::E_None )
144  {
145  SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
146  osl::File::remove( fileUrl );
147  return false;
148  }
149  if( !eot )
150  {
151  sufficientFontRights = sufficientTTFRights(fontData.data(), fontData.size(), FontRights::EditingAllowed);
152  }
153  if( !sufficientFontRights )
154  {
155  // It would be actually better to open the document in read-only mode in this case,
156  // warn the user about this, and provide a button to drop the font(s) in order
157  // to switch to editing.
158  SAL_INFO( "vcl.fonts", "Ignoring embedded font that is not usable for editing" );
159  osl::File::remove( fileUrl );
160  return false;
161  }
162  m_aAccumulatedFonts.emplace_back(std::make_pair(fontName, fileUrl));
163  return true;
164 }
165 
166 namespace
167 {
168  struct UpdateFontsGuard
169  {
170  UpdateFontsGuard()
171  {
173  }
174 
175  ~UpdateFontsGuard()
176  {
178  }
179  };
180 }
181 
183 {
184  if (m_aAccumulatedFonts.empty())
185  return;
186  UpdateFontsGuard aUpdateFontsGuard;
187  for (const auto& rEntry : m_aAccumulatedFonts)
188  EmbeddedFontsHelper::activateFont(rEntry.first, rEntry.second);
189  m_aAccumulatedFonts.clear();
190 }
191 
192 OUString EmbeddedFontsHelper::fileUrlForTemporaryFont( const OUString& fontName, const char* extra )
193 {
194  OUString path = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
195  rtl::Bootstrap::expandMacros( path );
196  path += "/user/temp/embeddedfonts/fromdocs/";
197  osl::Directory::createPath( path );
198  OUString filename = fontName;
199  static int uniqueCounter = 0;
200  if( strcmp( extra, "?" ) == 0 )
201  filename += OUString::number( uniqueCounter++ );
202  else
203  filename += OStringToOUString( extra, RTL_TEXTENCODING_ASCII_US );
204  filename += ".ttf"; // TODO is it always ttf?
205  return path + filename;
206 }
207 
208 void EmbeddedFontsHelper::activateFont( const OUString& fontName, const OUString& fileUrl )
209 {
211  pDevice->AddTempDevFont(fileUrl, fontName);
212 }
213 
214 // Check if it's (legally) allowed to embed the font file into a document
215 // (ttf has a flag allowing this). PhysicalFontFace::IsEmbeddable() appears
216 // to have a different meaning (guessing from code, IsSubsettable() might
217 // possibly mean it's ttf, while IsEmbeddable() might mean it's type1).
218 // So just try to open the data as ttf and see.
220 {
221  TrueTypeFont* font;
222  if( OpenTTFontBuffer( data, size, 0 /*TODO*/, &font ) == SFErrCodes::Ok )
223  {
224  TTGlobalFontInfo info;
225  GetTTGlobalFontInfo( font, &info );
226  CloseTTFont( font );
227  // https://www.microsoft.com/typography/otspec/os2.htm#fst
228  int copyright = info.typeFlags;
229  switch( rights )
230  {
231  case FontRights::ViewingAllowed:
232  // Embedding not restricted completely.
233  return ( copyright & 0x02 ) != 0x02;
234  case FontRights::EditingAllowed:
235  // Font is installable or editable.
236  return copyright == 0 || ( copyright & 0x08 );
237  }
238  }
239  return true; // no known restriction
240 }
241 
242 OUString EmbeddedFontsHelper::fontFileUrl( std::u16string_view familyName, FontFamily family, FontItalic italic,
243  FontWeight weight, FontPitch pitch, FontRights rights )
244 {
245  OUString path = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
246  rtl::Bootstrap::expandMacros( path );
247  path += "/user/temp/embeddedfonts/fromsystem/";
248  osl::Directory::createPath( path );
249  OUString filename = OUString::Concat(familyName) + "_" + OUString::number( family ) + "_" + OUString::number( italic )
250  + "_" + OUString::number( weight ) + "_" + OUString::number( pitch )
251  + ".ttf"; // TODO is it always ttf?
252  OUString url = path + filename;
253  if( osl::File( url ).open( osl_File_OpenFlag_Read ) == osl::File::E_None ) // = exists()
254  {
255  // File with contents of the font file already exists, assume it's been created by a previous call.
256  return url;
257  }
258  bool ok = false;
261  graphics->GetDevFontList( &fonts );
262  std::unique_ptr< vcl::font::PhysicalFontFaceCollection > fontInfo( fonts.GetFontFaceCollection());
263  PhysicalFontFace* selected = nullptr;
264  for( int i = 0;
265  i < fontInfo->Count();
266  ++i )
267  {
268  PhysicalFontFace* f = fontInfo->Get( i );
269  if( f->GetFamilyName() == familyName )
270  {
271  // Ignore comparing text encodings, at least for now. They cannot be trivially compared
272  // (e.g. UCS2 and UTF8 are technically the same characters, just have different encoding,
273  // and just having a unicode font doesn't say what glyphs it actually contains).
274  // It is possible that it still may be needed to do at least some checks here
275  // for some encodings (can one font have more font files for more encodings?).
276  if(( family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
277  && ( italic == ITALIC_DONTKNOW || f->GetItalic() == italic )
278  && ( weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
279  && ( pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
280  { // Exact match, return it immediately.
281  selected = f;
282  break;
283  }
284  if(( f->GetFamilyType() == FAMILY_DONTKNOW || family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
285  && ( f->GetItalic() == ITALIC_DONTKNOW || italic == ITALIC_DONTKNOW || f->GetItalic() == italic )
286  && ( f->GetWeight() == WEIGHT_DONTKNOW || weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
287  && ( f->GetPitch() == PITCH_DONTKNOW || pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
288  { // Some fonts specify 'DONTKNOW' for some things, still a good match, if we don't find a better one.
289  selected = f;
290  }
291  }
292  }
293  if( selected != nullptr )
294  {
296  if (const void* data = graphics->GetEmbedFontData(selected, &size))
297  {
298  if( sufficientTTFRights( data, size, rights ))
299  {
300  osl::File file( url );
301  if( file.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) == osl::File::E_None )
302  {
303  sal_uInt64 written = 0;
304  sal_uInt64 totalSize = size;
305  bool error = false;
306  while( written < totalSize && !error)
307  {
308  sal_uInt64 nowWritten;
309  switch( file.write( static_cast< const char* >( data ) + written, size - written, nowWritten ))
310  {
311  case osl::File::E_None:
312  written += nowWritten;
313  break;
314  case osl::File::E_AGAIN:
315  case osl::File::E_INTR:
316  break;
317  default:
318  error = true;
319  break;
320  }
321  }
322  file.close();
323  if( error )
324  osl::File::remove( url );
325  else
326  ok = true;
327  }
328  }
329  graphics->FreeEmbedFontData( data, size );
330  }
331  }
332  return ok ? url : "";
333 }
334 
335 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
exports com.sun.star.frame. status
static void clearDir(const OUString &path)
void activateFonts()
Adds the accumulated fonts to the list of known fonts.
void CloseTTFont(TrueTypeFont *ttf)
TrueTypeFont destructor.
Definition: sft.cxx:1155
long Long
FAMILY_DONTKNOW
bool addEmbeddedFont(const css::uno::Reference< css::io::XInputStream > &stream, const OUString &fontName, const char *extra, std::vector< unsigned char > const &key, bool eot=false)
Reads a font from the input stream, saves it to a temporary font file and adds it to the list of font...
FontRights
Specification of what kind of operation is allowed when embedding a font.
virtual const void * GetEmbedFontData(const PhysicalFontFace *pFont, tools::Long *pDataLen)=0
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1069
size_t pos
virtual void FreeEmbedFontData(const void *pData, tools::Long nDataLen)=0
FontItalic GetItalic() const
void GetTTGlobalFontInfo(TrueTypeFont *ttf, TTGlobalFontInfo *info)
Returns global font information about the TrueType font.
Definition: sft.cxx:2038
bool AddTempDevFont(const OUString &rFileURL, const OUString &rFontName)
sal_uInt32 typeFlags
type flags (copyright bits)
Definition: sft.hxx:173
abstract base class for physical font faces
WEIGHT_DONTKNOW
FontFamily GetFamilyType() const
#define SAL_CONFIGFILE(name)
static SAL_DLLPRIVATE void ImplRefreshAllFontData(bool bNewFontLists)
static void activateFont(const OUString &fontName, const OUString &fileUrl)
Adds the given font to the list of known fonts.
int i
FontWeight GetWeight() const
std::unique_ptr< vcl::font::PhysicalFontFaceCollection > GetFontFaceCollection() const
static void clearTemporaryFontFiles()
Removes all temporary fonts in the path used by fileUrlForTemporaryFont().
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:168
static OUString fileUrlForTemporaryFont(const OUString &fontName, const char *extra)
Returns a URL for a file where to store contents of a given temporary font.
fonts
PITCH_DONTKNOW
size
static OUString fontFileUrl(std::u16string_view familyName, FontFamily family, FontItalic italic, FontWeight weight, FontPitch pitch, FontRights rights)
Returns URL for a font file for the given font, or empty if it does not exist.
FontPitch
exports com.sun.star.chart2. data
FontFamily
FontWeight
#define SAL_INFO(area, stream)
Sun Font Tools.
ITALIC_DONTKNOW
SalGraphics const * GetGraphics() const
Get the graphic context that the output device uses to draw on.
Definition: outdev.cxx:203
static bool sufficientTTFRights(const void *data, tools::Long size, FontRights rights)
Returns if the restrictions specified in the font (if present) allow embedding the font for a particu...
#define SAL_WARN(area, stream)
void(* f)(TrueTypeTable *)
Definition: ttcr.cxx:482
FontPitch GetPitch() const
static SAL_DLLPRIVATE void ImplClearAllFontData(bool bNewFontLists)
const OUString & GetFamilyName() const
Return value of GetTTGlobalFontInfo()
Definition: sft.hxx:147
FontItalic
virtual void GetDevFontList(PhysicalFontCollection *)=0
SFErrCodes OpenTTFontBuffer(const void *pBuffer, sal_uInt32 nLen, sal_uInt32 facenum, TrueTypeFont **ttf, const FontCharMapRef xCharMap)
TrueTypeFont constructor.
Definition: sft.cxx:1082