在VS 2010安装项目中安装Type 1字体

时间:2011-09-27 03:12:38

标签: visual-studio-2010 fonts setup-project

我正在尝试使用Visual Studio 2010安装项目将一组Type 1字体打包到MSI文件中以便于安装。

我已将配置项目配置为将所有PFM和PFB文件放在Fonts文件夹中,将所有PFM文件设置为vsdrfFont并修复此处提到的命名问题:

http://cjwdev.wordpress.com/2011/03/14/installing-non-truetype-fonts-with-visual-studio-installer/

但是,这不适用于Type 1字体。

安装了Type 1字体文件,但字体仍然无法识别,并且未显示在Fonts窗口中。

如果手动安装,则在HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Type 1 Installer\Type 1 Fonts下注册Type 1字体并正常工作。

如何使用安装项目获得相同的结果?

2 个答案:

答案 0 :(得分:2)

要安装Type 1字体,您需要执行以下操作:

  1. 在“Type 1 Fonts”
  2. 下注册字体标题
  3. 将PFM和PFB复制到Windows字体目录
  4. 调用AddFontResource方法
  5. 在“Type 1 Fonts”

    下注册字体标题

    SOFTWARE\Microsoft\Windows NT\CurrentVersion\Type 1 Installer\Type 1 Fonts

    字体标题是必需的,而不是将其提供给安装程序,以下代码段将允许您从PFM读取字体标题。它基于从以下来源收集的信息:

    http://partners.adobe.com/public/developer/en/font/5178.PFM.pdf

        private static string GetType1FontName(string filename)
        {
            StringBuilder postscriptName = new StringBuilder();
    
            FileInfo fontFile = new FileInfo(filename);
            using (FileStream fs = fontFile.OpenRead())
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    using (BinaryReader inStream = new BinaryReader(fs))
                    {
                        // PFM Header is 117 bytes
                        inStream.ReadBytes(117); // skip 117
                        short size = inStream.ReadInt16();
                        int extMetricsOffset = inStream.ReadInt32();
                        int extentTableOffset = inStream.ReadInt32();
    
                        inStream.ReadBytes(4); // skip 4
                        int kernPairOffset = inStream.ReadInt32();
                        int kernTrackOffset = inStream.ReadInt32();
                        int driverInfoOffset = inStream.ReadInt32();
    
                        fs.Position = driverInfoOffset;
                        while (inStream.PeekChar() != 0)
                        {
                            postscriptName.Append(inStream.ReadChar());
                        }
                    }
                }
            }
    
            return postscriptName.ToString();
        }
    

    将PFM和PFB复制到Windows字体目录

    根据此博客http://www.atalasoft.com/cs/blogs/stevehawley/archive/2008/08/25/getting-the-fonts-folder.aspx,获取Windows字体文件夹的正确方法如下:

        [DllImport("shell32.dll")]
        private static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken,
                   uint dwFlags, [Out] StringBuilder pszPath);
    
        public static string GetFontFolderPath()
        {
            StringBuilder sb = new StringBuilder();
            SHGetFolderPath(IntPtr.Zero, 0x0014, IntPtr.Zero, 0x0000, sb);
    
            return sb.ToString();
        }
    

    调用AddFontResource方法

    最后,应调用方法AddFontResource,参数lpFilename应由由管道符“|”分隔的pfm和pfb文件组成。在我的情况下,我把完整的路径放到Windows字体文件夹,似乎工作。在调用AddFontResource之后,您需要使用WM.FONTCHANGE(0x001D)参数调用PostMessage,以通知其他窗口更改。

        [DllImport("gdi32.dll")]
        static extern int AddFontResource(string lpFilename);
    
        // build the name for the api "<pfm>|<pfb>"
        string apiValue = string.Format("{0}|{1}", PfmFileDestination, PfbFileDestination);
    
        // Call the api to register the font
        int retVal = AddFontResource(apiValue);
    
        // Inform other windows of change
        PostMessage(HWND_BROADCAST, WM.FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
    

答案 1 :(得分:1)

这是一个涉及MSI自定义操作的解决方案。我用C#写过,但任何其他能够调用DLL的语言都可以是用户。以下是C#的教程链接:Walkthrough: Creating a Custom Action

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Runtime.InteropServices;

namespace InstallType1Font
{
    [RunInstaller(true)]
    public partial class Installer1 : Installer
    {
        public Installer1()
        {
            InitializeComponent();
        }

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
        }

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Commit(IDictionary savedState)
        {
            base.Commit(savedState);

            // here, you'll have to determine the proper path
            string path = @"c:\Windows\Fonts\MyFont.pfm";
            if (File.Exists(path))
            {
                InstallFontFile(IntPtr.Zero, path, 0);
            }
        }

        [DllImport("fontext.dll", CharSet = CharSet.Auto)]
        private static extern void InstallFontFile(IntPtr hwnd, string filePath, int flags);

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Rollback(IDictionary savedState)
        {
            base.Rollback(savedState);
        }

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Uninstall(IDictionary savedState)
        {
            base.Uninstall(savedState);
        }
    }
}

据我所知,InstallFontFile没有记录,但允许永久安装字体。使用此风险需要您自担风险。

注意:您仍然需要修改.MSI以确保字体文件具有FontTitle,如您提供的链接中所述。