C# 如何将SPL文件转换成EMF文件

本文主要讲述如何将SPL文件转换成EMF文件。


目录

一、什么是SPL文件和EMF文件

(一)SPL文件

(二)EMF文件

二、文件解析

(一)SPL格式

(二)打开SPL文件

(三)解析SPL文件

 三、编程思路

(一)记录EMF文件的位置和大小

(二)找到EMF文件的开始位置和大小

(三)将SPL文件转换成EMF文件


一、什么是SPL文件和EMF文件

(一)SPL文件

如下图把标记处的复选框打钩,这样就可以在打印过程中把打印的文档保留下来。

保留的临时文件存储在⽬录 C:\Windows\System32\spool\PRINTERS下 。

每次打印保留两个⽂件分别以.SHD 和 .SPL 结尾,.SHD 为打印控制⽂件,.SPL 存储打印内容。

我们可以通过解析 SPL⽂件的⽅式来获得打印内容。


(二)EMF文件

EMF文件是一种图片格式,可以用画图软件打开。

EMF(增强元文件)是Windows操作系统用来打印缓存文件格式的术语。打印作业发送到打印机后,如果正在打印另一个文件,计算机将读取新文件并将其存储,通常存储在硬盘或存储器中,以便以后打印。


二、文件解析

(一)SPL格式

简单来说,SPL文件中封装了多个EMF文件,下图是一个SPL文件的二进制格式。


(二)打开SPL文件

可以用WinHex软件打开SPL文件读取其二进制数据(以十六进制显示),读取的数据如下图。

读取的二进制文件有几个要点需要注意:

(1)读取的数据是以双字为一个单位,即四个字节,对于十六进制来说,就是“00 00 01 00”为一个单位。

(2)读取的数据高位存储在低位位置,低位数据存储在高位位置,比如说第一个单位的数据“00 00 01 00”实际上“00 01 00 00”(反过来)。


(三)解析SPL文件

下图中标注部分是SPL头,封装了SPL文件的一些信息,包括EMF文件的位置,可以找到网上其他的一些资料,找到SPL头的具体内容。一些资料中说的SPL头的第二个单位的数据是EMF头的位置,可是对于一些文件来说,确实如此,而也有一些文件并非如此,具体原因不清楚,还需要找到SPL头的具体文档说明,才能知道。如下图中第二个单位是“00 00 00 7C”,指示的是数据第0000007C位置的数据“00 00 00 0C”正是EMF头的开始位置。

 EMF头的开始数据是“00 00 00 0C”,下一个单位是EMF文件的大小,即“00 14 0B FC”,即EMF文件正式开始的位置加上EMF文件的大小,就是EMF文件的结尾处,这里要注意的是,EMF文件是不包含EMF头的,即EMF文件正式开始的位置是下一个单位“00 00 00 01”。

EMF文件正式开始的位置“00 00 00 84”加上EMF文件的大小“00 14 0B FC”,再减1,就是EMF文件的结尾位置“00 14 0C 85”,即下图中的“00 00 00 14”处,这样就找到了一个EMF文件,从“00 00 00 01”到“00 00 00 14”,就是一个EMF文件。

 三、编程思路

我的想法比较简单,就是以字节形式遍历整个SPL文件,找到“0C 00 00 00 XX XX XX XX 01 00 00 00”,符合这个条件的一般都是EMF文件,然后从“01 00 00 00”开始,按照其中文件大小的数据,读取一整个EMF文件,然后以同样方式继续读取下一个EMF文件。

代码写的不好,比较复杂,如下:


(一)记录EMF文件的位置和大小

用两个全局变量记录EMF文件的位置和大小:

        //用一个列表记录一个SPL文件中所有EMF文件的起始位置
        private static List<int> _startIndexs;
        //用一个列表记录一个SPL文件中所有EMF文件的数据大小
        private static List<int> _emfDataCount;

(二)找到EMF文件的开始位置和大小

定义一个方法,用来找到EMF文件的开始位置和大小:

        /// <summary>
        /// 这个方法可以找到一个SPL文件中所有的EMF文件的开始位置和大小,并分别记录到两个列表中
        /// </summary>
        /// <param name="srcFilePath">需要提供SPL文件路径(包含文件名)作为参数</param>
        private static void FindEmf(string srcFilePath)
        {
            //初始化两个用来记录EMF文件开始位置和大小的列表
            _startIndexs = new List<int>();
            _emfDataCount = new List<int>();
            //读取SPL文件
            using (FileStream fsR = new FileStream(srcFilePath, FileMode.Open, FileAccess.Read))
            {
                //设置一个20M的字节数组作为缓存区来记录读取的数据
                byte[] buffer = new byte[20 * 1024 * 1024];
                int i = fsR.Read(buffer, 0, buffer.Length);
                //当SPL文件超过20M时,需要读取多遍来记录数据,whileCount用来记录读取的遍数
                int whileCount = -1;
                bool flag = false;
                bool flagB = false;
                //当i=0时,说明已经读取完SPL文件
                while (i > 0)
                {
                    whileCount++;
                    //遍历缓存区数组
                    for (int j = 0; j < i; j++)
                    {
                        if (!flag)
                        {
                            if (j <= 20 * 1024 * 1024-12)
                            {
                                if (j % 4 == 0)
                                {
                                    //找到“0C 00 00 00 XX XX XX XX 01 00 00 00”
                                    if (buffer[j] == 12 && buffer[j + 1] == 0 && buffer[j + 2] == 0 && buffer[j + 3] == 0 && buffer[j + 8] == 1 && buffer[j + 9] == 0 && buffer[j + 10] == 0 && buffer[j + 11] == 0)
                                    {
                                        //记录emf起始位置
                                        _startIndexs.Add(whileCount *( 20 * 1024 * 1024) + j + 8);
                                        //记录emf大小
                                        _emfDataCount.Add((buffer[j + 7]) * 256 * 256 * 256 + (buffer[j + 6]) * 256 * 256 + (buffer[j + 5]) * 256 + (buffer[j + 4]));

                                    }
                                }
                            }
                            else
                            {
                                //当“0C 00 00 00”在数组倒数第二个单位时,需要判断循环读取第二遍时数组第一个单位是否是“01 00 00 00”
                                if (j == 20 * 1024 * 1024-8)
                                {
                                    if (buffer[j] == 12 && buffer[j + 1] == 0 && buffer[j + 2] == 0 && buffer[j + 3] == 0)
                                    {
                                        //记录emf起始位置
                                        _startIndexs.Add(whileCount * (20 * 1024 * 1024) + j + 8);
                                        //记录emf大小
                                        _emfDataCount.Add((buffer[j + 7]) * 256 * 256 * 256 + (buffer[j + 6]) * 256 * 256 + (buffer[j + 5]) * 256 + (buffer[j + 4]));
                                        flag = true;
                                        flagB = true;
                                    }
                                }
                                //当“0C 00 00 00”在数组倒数第一个单位时,需要判断循环读取第二遍时数组第二个单位是否是“01 00 00 00”
                                if (j == 20 * 1024 * 1024-4)
                                {
                                    if (buffer[j] == 12 && buffer[j + 1] == 0 && buffer[j + 2] == 0 && buffer[j + 3] == 0)
                                    {
                                        //记录emf起始位置
                                        _startIndexs.Add(whileCount * (20 * 1024 * 1024) + j + 8);
                                        flag = true;
                                    }
                                }
                            }
                        }
                        //在上一次循环中,“0C 00 00 00”在数组倒数第二个单位或者倒数第一个单位时
                        //需要判断此次循环读取数组第一个单位或者第二个单位是否是“01 00 00 00”
                        else
                        {
                            flag = false;
                            if (_startIndexs.Count == _emfDataCount.Count)
                            {
                                if (!(buffer[j] == 1 && buffer[j + 1] == 0 && buffer[j + 2] == 0 && buffer[j + 3] == 0))
                                {
                                    _startIndexs.RemoveAt(_startIndexs.Count - 1);
                                    _emfDataCount.RemoveAt(_emfDataCount.Count - 1);
                                }
                            }
                            else
                            {
                                if (flagB)
                                {
                                    flagB = false;
                                    _startIndexs.RemoveAt(_startIndexs.Count - 2);
                                    _emfDataCount.RemoveAt(_emfDataCount.Count - 1);
                                }

                                if (!(buffer[j + 4] == 1 && buffer[j + 5] == 0 && buffer[j + 6] == 0 && buffer[j + 7] == 0))
                                {
                                    _startIndexs.RemoveAt(_startIndexs.Count - 1);
                                }
                                else
                                {
                                    _emfDataCount.Add((buffer[j + 3]) * 256 * 256 * 256 + (buffer[j + 2]) * 256 * 256 + (buffer[j + 1]) * 256 + (buffer[j]));
                                }


                            }
                        }

                    }
                    //如果SPL文件没读完,则继续读第二遍,直到读完
                    i = fsR.Read(buffer, 0, buffer.Length);
                }
            }
        }

(三)将SPL文件转换成EMF文件

代码主要部分,将SPL文件转换成EMF文件

        /// <summary>
        /// 将一个SPL文件转换成EMF文件,一个SPL文件中可能封装多个EMF文件
        /// </summary>
        /// <param name="srcFilePath">参数一是SPL文件路径(包含SPL文件名)</param>
        /// <param name="dstFilePath">参数二是EMF文件路径(不包含EMF文件名)</param>
        public static void SplToEmf(string srcFilePath, string dstFilePath)
        {
            //找到当前SPL文件中所有EMF文件的开始位置和数据大小
            FindEmf(srcFilePath);
            //遍历记录EMF文件开始位置的列表,从而读取所有EMF文件
            for (int j = 0; j < _startIndexs.Count; j++)
            {
                //读取SPL文件
                using (FileStream fsR = new FileStream(srcFilePath, FileMode.Open, FileAccess.Read))
                {
                    //用20M的字节数组作为缓存区读取SPL文件
                    byte[] buffer = new byte[20 * 1024 * 1024];
                    //将读取到的EMF文件数据记录下来
                    using (FileStream fsW = new FileStream(Path.Combine( dstFilePath,Path.GetFileNameWithoutExtension(srcFilePath)+"-"+ j.ToString()+".emf"), FileMode.Create, FileAccess.Write))
                    {
                        int i = fsR.Read(buffer, 0, buffer.Length);
                        int startIndex = _startIndexs[j];
                        int emfDataCount = _emfDataCount[j];
                        int X = startIndex;
                        //remnant记录剩余没读完的EMF数据
                        int remnant = emfDataCount;
                        //remnant剩余数据为0时读完一个EMF文件数据
                        while (remnant > 0)
                        {
                            //当EMF文件的起始位置在20M的缓存区内时,开始读取
                            //否则循环读取SPL文件,在下一个缓存中开始找开始位置
                            if (startIndex <= 20 * 1024 * 1024-1)
                            {
                                if (remnant > i - X)
                                    fsW.Write(buffer, X, i - X);
                                else
                                    fsW.Write(buffer, X, remnant);
                                remnant = remnant - (i - X);
                                i = fsR.Read(buffer, 0, buffer.Length);
                                X = 0;
                            }
                            else
                            {
                                i = fsR.Read(buffer, 0, buffer.Length);
                                startIndex = startIndex - 20 * 1024 * 1024;
                                X = 0;
                            }
                        }
                    }
                }
            }
        }

 代码仅供参考。


本文由本人编写,经验缺乏,尚望各位多多指正,以匡不逮。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
### 回答1: 要打开临时SPLSPLF)文件,可以按照以下步骤进行操作: 首先,将SPL文件从临时目录复制到一个可访问的位置。这可以通过以下步骤完成: 1. 打开文件资源管理器。 2. 输入"%TEMP%"(不包括引号)到资源管理器地址栏中并按下Enter键。 3. 在打开的临时文件夹中,找到并复制所需的SPL文件。 4. 将SPL文件粘贴到一个可以轻松访问的目录,例如桌面或文档文件夹。 然后,可以使用适当的应用程序打开SPL文件。此类应用程序通常是打印机相关的,并可以将SPL文件转换为可读的文本或图像格式。以下是一些常见的方法和应用程序: 1. 使用Adobe Acrobat:如果SPL文件是由打印机生成的PDF文件,可以使用Adobe Acrobat打开。在Acrobat中,选择"文件 > 打开",然后选择复制到的SPL文件。 2. 使用打印机驱动程序:某些打印机制造商提供可以打开SPL文件的打印机驱动程序。这些驱动程序通常被称为"打印转换器",允许将SPL文件转换为PDF、JPEG等格式。请查看您所使用打印机的制造商网站,了解是否提供此类驱动程序。 3. 使用专门的文件查看器:还有一些专门用于查看SPL文件的第三方应用程序,例如"SplViewer"等。可以从互联网上下载并安装这些工具,然后使用它们打开SPL文件。 需要注意的是,SPL文件的打开方式可能因操作系统和具体的SPL文件类型而有所不同。因此,建议在尝试以上步骤之前,先了解SPL文件的确切类型,并根据该类型查找适当的打开方法。 ### 回答2: 要打开临时SPL文件,可以按照以下步骤进行操作: 1. 确保你的计算机上安装了打印机驱动程序。如果没有安装,可以从打印机制造商的官方网站上下载并安装适合你打印机型号的驱动程序。 2. 在你的计算机上找到临时SPL文件。这些文件通常存储在Windows临时文件夹中,可以通过以下路径访问:C:\Users\你的用户名\AppData\Local\Temp。 3. 右键单击临时SPL文件,然后从弹出菜单中选择“打印”。 4. 一个打印对话框将出现在屏幕上。在这个对话框中,选择你要使用的打印机,并根据需要进行其他设置(例如打印份数、纸张大小等)。 5. 点击“打印”按钮,计算机将开始将临时SPL文件发送到打印机进行打印。 值得注意的是,临时SPL文件实际上是用于存储打印作业的临时文件,而不是真正的可编辑文档。所以无法通过常规的办法打开并编辑SPL文件。如果你需要编辑其中的文本内容,你可能需要使用专门的软件或工具来转换SPL文件为其他格式(例如PDF),然后再进行编辑。 ### 回答3: 要打开临时SPL文件,需要遵循以下步骤: 1. 首先,确定您的电脑具备打开并处理SPL文件的软件。常见的SPL文件打开软件包括Adobe Reader、Foxit Reader等PDF阅读器,以及一些打印机制造商提供的打印驱动程序。 2. 如果您的SPL文件实际上是由打印机生成的,可以尝试在打印机的设置菜单中查找打印队列或历史记录。点击打印队列或历史记录,通常可以找到您之前打印的文件列表。在文件列表中找到并选择您想要打开的SPL文件,并选择“打开”或“查看”。 3. 如果您的SPL文件是PDF格式的,您可以右键单击SPL文件,从弹出菜单中选择“打开方式”,然后从列表中选择适合的PDF阅读器。 4. 如果您的电脑上没有任何与SPL文件关联的程序,您可以尝试使用基于互联网的在线文件转换工具。这些工具可以将SPL文件转换为常见的文档格式,如PDF、DOC或TXT。在转换文件后,您就可以使用常用的软件打开并查看文件了。 总结来说,要打开临时SPL文件,你可以尝试使用适用的PDF阅读器、打印机的打印队列或历史记录、在线文件转换工具等方法。根据具体情况,选择最适合的方法来打开并查看SPL文件
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值