LCOV - code coverage report
Current view: top level - vcl/source/gdi - embeddedfontshelper.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 70 135 51.9 %
Date: 2015-06-13 12:38:46 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          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 <config_folders.h>
      11             : #include <config_eot.h>
      12             : 
      13             : #include <osl/file.hxx>
      14             : #include <rtl/bootstrap.hxx>
      15             : #include <vcl/outdev.hxx>
      16             : #include <vcl/svapp.hxx>
      17             : 
      18             : #include "fontsubset.hxx"
      19             : #include "outdev.h"
      20             : #include "outfont.hxx"
      21             : #include "PhysicalFontCollection.hxx"
      22             : #include "salgdi.hxx"
      23             : #include "sft.hxx"
      24             : 
      25             : #include <vcl/embeddedfontshelper.hxx>
      26             : 
      27             : #if ENABLE_EOT
      28             : extern "C"
      29             : {
      30             : namespace libeot
      31             : {
      32             : #include <libeot/libeot.h>
      33             : } // namespace libeot
      34             : } // extern "C"
      35             : #endif
      36             : 
      37             : using namespace com::sun::star;
      38             : using namespace vcl;
      39             : 
      40         974 : static void clearDir( const OUString& path )
      41             : {
      42         974 :     osl::Directory dir( path );
      43         974 :     if( dir.reset() == osl::Directory::E_None )
      44             :     {
      45             :         for(;;)
      46             :         {
      47          77 :             osl::DirectoryItem item;
      48          77 :             if( dir.getNextItem( item ) != osl::Directory::E_None )
      49          64 :                 break;
      50          26 :             osl::FileStatus status( osl_FileStatus_Mask_FileURL );
      51          13 :             if( item.getFileStatus( status ) == osl::File::E_None )
      52          13 :                 osl::File::remove( status.getFileURL());
      53          13 :         }
      54         974 :     }
      55         974 : }
      56             : 
      57         487 : void EmbeddedFontsHelper::clearTemporaryFontFiles()
      58             : {
      59         487 :     OUString path = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
      60         487 :     rtl::Bootstrap::expandMacros( path );
      61         487 :     path += "/user/temp/embeddedfonts/";
      62         487 :     clearDir( path + "fromdocs/" );
      63         487 :     clearDir( path + "fromsystem/" );
      64         487 : }
      65             : 
      66          13 : bool EmbeddedFontsHelper::addEmbeddedFont( uno::Reference< io::XInputStream > stream, const OUString& fontName,
      67             :     const char* extra, std::vector< unsigned char > key, bool eot )
      68             : {
      69          13 :     OUString fileUrl = EmbeddedFontsHelper::fileUrlForTemporaryFont( fontName, extra );
      70          26 :     osl::File file( fileUrl );
      71          13 :     switch( file.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_Write ))
      72             :     {
      73             :         case osl::File::E_None:
      74          13 :             break; // ok
      75             :         case osl::File::E_EXIST:
      76           0 :             return true; // Assume it's already been added correctly.
      77             :         default:
      78             :             SAL_WARN( "vcl.fonts", "Cannot open file for temporary font" );
      79           0 :             return false;
      80             :     }
      81          13 :     size_t keyPos = 0;
      82          13 :     std::vector< char > fontData;
      83          13 :     fontData.reserve( 1000000 );
      84             :     for(;;)
      85             :     {
      86        4038 :         uno::Sequence< sal_Int8 > buffer;
      87        4038 :         sal_uInt64 read = stream->readBytes( buffer, 1024 );
      88        8076 :         for( sal_uInt64 pos = 0;
      89        4038 :              pos < read && keyPos < key.size();
      90             :              ++pos )
      91           0 :             buffer[ pos ] ^= key[ keyPos++ ];
      92             :         // if eot, don't write the file out yet, since we need to unpack it first.
      93        4038 :         if( !eot && read > 0 )
      94             :         {
      95        4025 :             sal_uInt64 writtenTotal = 0;
      96       12075 :             while( writtenTotal < read )
      97             :             {
      98             :                 sal_uInt64 written;
      99        4025 :                 file.write( buffer.getConstArray(), read, written );
     100        4025 :                 writtenTotal += written;
     101             :             }
     102             :         }
     103        4038 :         fontData.insert( fontData.end(), buffer.getConstArray(), buffer.getConstArray() + read );
     104        4038 :         if( read <= 0 )
     105          13 :             break;
     106        4025 :     }
     107          13 :     bool sufficientFontRights(false);
     108             : #if ENABLE_EOT
     109             :     if( eot )
     110             :     {
     111             :         unsigned uncompressedFontSize = 0;
     112             :         unsigned char *nakedPointerToUncompressedFont = NULL;
     113             :         libeot::EOTMetadata eotMetadata;
     114             :         libeot::EOTError uncompressError =
     115             :             libeot::EOT2ttf_buffer( reinterpret_cast<unsigned char *>(&fontData[0]), fontData.size(), &eotMetadata, &nakedPointerToUncompressedFont, &uncompressedFontSize );
     116             :         std::shared_ptr<unsigned char> uncompressedFont( nakedPointerToUncompressedFont, libeot::EOTfreeBuffer );
     117             :         if( uncompressError != libeot::EOT_SUCCESS )
     118             :         {
     119             :             SAL_WARN( "vcl.fonts", "Failed to uncompress font" );
     120             :             osl::File::remove( fileUrl );
     121             :             return false;
     122             :         }
     123             :         sal_uInt64 writtenTotal = 0;
     124             :         while( writtenTotal < uncompressedFontSize )
     125             :         {
     126             :             sal_uInt64 written;
     127             :             if( file.write( uncompressedFont.get() + writtenTotal, uncompressedFontSize - writtenTotal, written ) != osl::File::E_None )
     128             :             {
     129             :                 SAL_WARN( "vcl.fonts", "Error writing temporary font file" );
     130             :                 osl::File::remove( fileUrl );
     131             :                 return false;
     132             :             }
     133             :             writtenTotal += written;
     134             :         }
     135             :         sufficientFontRights = libeot::EOTcanLegallyEdit( &eotMetadata );
     136             :         libeot::EOTfreeMetadata( &eotMetadata );
     137             :     }
     138             : #endif
     139             : 
     140          13 :     if( file.close() != osl::File::E_None )
     141             :     {
     142             :         SAL_WARN( "vcl.fonts", "Writing temporary font file failed" );
     143           0 :         osl::File::remove( fileUrl );
     144           0 :         return false;
     145             :     }
     146          13 :     if( !eot )
     147             :     {
     148          13 :         sufficientFontRights = sufficientTTFRights( &fontData.front(), fontData.size(), EditingAllowed );
     149             :     }
     150          13 :     if( !sufficientFontRights )
     151             :     {
     152             :         // It would be actually better to open the document in read-only mode in this case,
     153             :         // warn the user about this, and provide a button to drop the font(s) in order
     154             :         // to switch to editing.
     155             :         SAL_INFO( "vcl.fonts", "Ignoring embedded font that is not usable for editing" );
     156           0 :         osl::File::remove( fileUrl );
     157           0 :         return false;
     158             :     }
     159          13 :     EmbeddedFontsHelper::activateFont( fontName, fileUrl );
     160          26 :     return true;
     161             : }
     162             : 
     163          13 : OUString EmbeddedFontsHelper::fileUrlForTemporaryFont( const OUString& fontName, const char* extra )
     164             : {
     165          13 :     OUString path = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
     166          13 :     rtl::Bootstrap::expandMacros( path );
     167          13 :     path += "/user/temp/embeddedfonts/fromdocs/";
     168          13 :     osl::Directory::createPath( path );
     169          26 :     OUString filename = fontName;
     170             :     static int uniqueCounter = 0;
     171          13 :     if( strcmp( extra, "?" ) == 0 )
     172          13 :         filename += OUString::number( uniqueCounter++ );
     173             :     else
     174           0 :         filename += OStringToOUString( extra, RTL_TEXTENCODING_ASCII_US );
     175          13 :     filename += ".ttf"; // TODO is it always ttf?
     176          26 :     return path + filename;
     177             : }
     178             : 
     179          13 : void EmbeddedFontsHelper::activateFont( const OUString& fontName, const OUString& fileUrl )
     180             : {
     181          13 :     OutputDevice *pDevice = Application::GetDefaultDevice();
     182          13 :     pDevice->AddTempDevFont(fileUrl, fontName);
     183          13 : }
     184             : 
     185             : // Check if it's (legally) allowed to embed the font file into a document
     186             : // (ttf has a flag allowing this). PhysicalFontFace::IsEmbeddable() appears
     187             : // to have a different meaning (guessing from code, IsSubsettable() might
     188             : // possibly mean it's ttf, while IsEmbeddable() might mean it's type1).
     189             : // So just try to open the data as ttf and see.
     190          13 : bool EmbeddedFontsHelper::sufficientTTFRights( const void* data, long size, FontRights rights )
     191             : {
     192             :     TrueTypeFont* font;
     193          13 :     if( OpenTTFontBuffer( data, size, 0 /*TODO*/, &font ) == SF_OK )
     194             :     {
     195             :         TTGlobalFontInfo info;
     196          13 :         GetTTGlobalFontInfo( font, &info );
     197          13 :         CloseTTFont( font );
     198             :         // http://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc
     199          13 :         int copyright = info.typeFlags & TYPEFLAG_COPYRIGHT_MASK;
     200          13 :         switch( rights )
     201             :         {
     202             :             case ViewingAllowed:
     203             :                 // Embedding not restricted completely.
     204          13 :                 return ( copyright & 0x02 ) != 0x02;
     205             :             case EditingAllowed:
     206             :                 // Font is installable or editable.
     207          13 :                 return copyright == 0 || ( copyright & 0x08 );
     208             :         }
     209             :     }
     210           0 :     return true; // no known restriction
     211             : }
     212             : 
     213           0 : OUString EmbeddedFontsHelper::fontFileUrl( const OUString& familyName, FontFamily family, FontItalic italic,
     214             :     FontWeight weight, FontPitch pitch, rtl_TextEncoding, FontRights rights )
     215             : {
     216           0 :     OUString path = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
     217           0 :     rtl::Bootstrap::expandMacros( path );
     218           0 :     path += "/user/temp/embeddedfonts/fromsystem/";
     219           0 :     osl::Directory::createPath( path );
     220           0 :     OUString filename = familyName + "_" + OUString::number( family ) + "_" + OUString::number( italic )
     221           0 :         + "_" + OUString::number( weight ) + "_" + OUString::number( pitch );
     222           0 :     filename += ".ttf"; // TODO is it always ttf?
     223           0 :     OUString url = path + filename;
     224           0 :     if( osl::File( url ).open( osl_File_OpenFlag_Read ) == osl::File::E_None ) // = exists()
     225             :     {
     226             :         // File with contents of the font file already exists, assume it's been created by a previous call.
     227           0 :         return url;
     228             :     }
     229           0 :     bool ok = false;
     230           0 :     SalGraphics* graphics = Application::GetDefaultDevice()->GetGraphics();
     231           0 :     PhysicalFontCollection fonts;
     232           0 :     graphics->GetDevFontList( &fonts );
     233           0 :     std::unique_ptr< ImplGetDevFontList > fontInfo( fonts.GetDevFontList());
     234           0 :     PhysicalFontFace* selected = NULL;
     235           0 :     for( int i = 0;
     236           0 :          i < fontInfo->Count();
     237             :          ++i )
     238             :      {
     239           0 :         PhysicalFontFace* f = fontInfo->Get( i );
     240           0 :         if( f->GetFamilyName() == familyName )
     241             :         {
     242             :             // Ignore comparing text encodings, at least for now. They cannot be trivially compared
     243             :             // (e.g. UCS2 and UTF8 are technically the same characters, just have different encoding,
     244             :             // and just having a unicode font doesn't say what glyphs it actually contains).
     245             :             // It is possible that it still may be needed to do at least some checks here
     246             :             // for some encodings (can one font have more font files for more encodings?).
     247           0 :             if(( family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
     248           0 :                 && ( italic == ITALIC_DONTKNOW || f->GetSlant() == italic )
     249           0 :                 && ( weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
     250           0 :                 && ( pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
     251             :             { // Exact match, return it immediately.
     252           0 :                 selected = f;
     253           0 :                 break;
     254             :             }
     255           0 :             if(( f->GetFamilyType() == FAMILY_DONTKNOW || family == FAMILY_DONTKNOW || f->GetFamilyType() == family )
     256           0 :                 && ( f->GetSlant() == ITALIC_DONTKNOW || italic == ITALIC_DONTKNOW || f->GetSlant() == italic )
     257           0 :                 && ( f->GetWeight() == WEIGHT_DONTKNOW || weight == WEIGHT_DONTKNOW || f->GetWeight() == weight )
     258           0 :                 && ( f->GetPitch() == PITCH_DONTKNOW || pitch == PITCH_DONTKNOW || f->GetPitch() == pitch ))
     259             :             { // Some fonts specify 'DONTKNOW' for some things, still a good match, if we don't find a better one.
     260           0 :                 selected = f;
     261             :             }
     262             :         }
     263             :     }
     264           0 :     if( selected != NULL )
     265             :     {
     266           0 :         FontSubsetInfo info;
     267             :         long size;
     268           0 :         if( const void* data = graphics->GetEmbedFontData( selected, NULL, NULL, 0, info, &size ))
     269             :         {
     270           0 :             if( sufficientTTFRights( data, size, rights ))
     271             :             {
     272           0 :                 osl::File file( url );
     273           0 :                 if( file.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) == osl::File::E_None )
     274             :                 {
     275           0 :                     sal_uInt64 written = 0;
     276           0 :                     sal_uInt64 totalSize = size;
     277           0 :                     bool error = false;
     278           0 :                     while( written < totalSize && !error)
     279             :                     {
     280             :                         sal_uInt64 nowWritten;
     281           0 :                         switch( file.write( static_cast< const char* >( data ) + written, size - written, nowWritten ))
     282             :                         {
     283             :                             case osl::File::E_None:
     284           0 :                                 written += nowWritten;
     285           0 :                                 break;
     286             :                             case osl::File::E_AGAIN:
     287             :                             case osl::File::E_INTR:
     288           0 :                                 break;
     289             :                             default:
     290           0 :                                 error = true;
     291           0 :                                 break;
     292             :                         }
     293             :                     }
     294           0 :                     file.close();
     295           0 :                     if( error )
     296           0 :                         osl::File::remove( url );
     297             :                     else
     298           0 :                         ok = true;
     299           0 :                 }
     300             :             }
     301           0 :             graphics->FreeEmbedFontData( data, size );
     302           0 :         }
     303             :     }
     304           0 :     return ok ? url : "";
     305         801 : }
     306             : 
     307             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11