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

Generated by: LCOV version 1.10